messy WIP; default objects + witch stuff

trunk
vilmibm 2022-12-22 22:03:37 -08:00
parent 89e4c4b095
commit 1664d678cb
4 changed files with 156 additions and 37 deletions

View File

@ -84,6 +84,10 @@ func newServer() (*gameWorldServer, error) {
return nil, err return nil, err
} }
if err = db.Ensure(); err != nil {
return nil, fmt.Errorf("failed to ensure default entities: %w", err)
}
if err = db.ClearSessions(); err != nil { if err = db.ClearSessions(); err != nil {
return nil, fmt.Errorf("could not clear sessions: %w", err) return nil, fmt.Errorf("could not clear sessions: %w", err)
} }
@ -91,12 +95,17 @@ func newServer() (*gameWorldServer, error) {
s := &gameWorldServer{ s := &gameWorldServer{
msgRouter: make(map[string]func(*proto.ClientMessage) error), msgRouter: make(map[string]func(*proto.ClientMessage) error),
db: db, db: db,
Gateway: witch.NewGateway(),
} }
gw := witch.NewGateway(s.HandleCmd)
s.Gateway = gw
return s, nil return s, nil
} }
func (s *gameWorldServer) HandleCmd(verb, rest string, sender *db.Object) {
// TODO
}
func (s *gameWorldServer) Commands(stream proto.GameWorld_CommandsServer) error { func (s *gameWorldServer) Commands(stream proto.GameWorld_CommandsServer) error {
var sid string var sid string
for { for {
@ -129,20 +138,26 @@ func (s *gameWorldServer) Commands(stream proto.GameWorld_CommandsServer) error
} }
log.Printf("found avatar %#v", avatar) log.Printf("found avatar %#v", avatar)
switch cmd.Verb { s.HandleCmd(cmd.Verb, cmd.Rest, avatar)
case "say":
if err = s.HandleSay(avatar, cmd.Rest); err != nil { /*
s.HandleError(func(_ *proto.ClientMessage) error { return nil }, err)
switch cmd.Verb {
case "say":
if err = s.HandleSay(avatar, cmd.Rest); err != nil {
s.HandleError(func(_ *proto.ClientMessage) error { return nil }, err)
}
default:
msg := &proto.ClientMessage{
Type: proto.ClientMessage_WHISPER,
Text: fmt.Sprintf("unknown verb: %s", cmd.Verb),
}
if err = send(msg); err != nil {
s.HandleError(send, err)
}
} }
default:
msg := &proto.ClientMessage{ */
Type: proto.ClientMessage_WHISPER,
Text: fmt.Sprintf("unknown verb: %s", cmd.Verb),
}
if err = send(msg); err != nil {
s.HandleError(send, err)
}
}
/* /*
@ -202,14 +217,25 @@ func (s *gameWorldServer) Register(ctx context.Context, auth *proto.AuthInfo) (s
return nil, fmt.Errorf("failed to find avatar for %s: %w", sessionID, err) return nil, fmt.Errorf("failed to find avatar for %s: %w", sessionID, err)
} }
bedroom, err := s.db.BedroomBySessionID(sessionID) /*
bedroom, err := s.db.BedroomBySessionID(sessionID)
if err != nil {
return nil, fmt.Errorf("failed to find bedroom for %s: %w", sessionID, err)
}
err = s.db.MoveInto(*av, *bedroom)
if err != nil {
return nil, fmt.Errorf("failed to move %d into %d: %w", av.ID, bedroom.ID, err)
}
*/
foyer, err := s.db.GetObject("system", "foyer")
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to find bedroom for %s: %w", sessionID, err) return nil, fmt.Errorf("failed to find foyer: %w", err)
} }
err = s.db.MoveInto(*av, *bedroom) if err = s.db.MoveInto(*av, *foyer); err != nil {
if err != nil { return nil, fmt.Errorf("failed to move %d into %d: %w", av.ID, foyer.ID, err)
return nil, fmt.Errorf("failed to move %d into %d: %w", av.ID, bedroom.ID, err)
} }
// TODO send room info, avatar info to client (need to figure this out and update proto) // TODO send room info, avatar info to client (need to figure this out and update proto)

View File

@ -12,7 +12,7 @@ import (
"github.com/jackc/pgx/v4/pgxpool" "github.com/jackc/pgx/v4/pgxpool"
) )
// go:embed schema.sql //go:embed schema.sql
var schema string var schema string
// TODO I have a suspicion that I'm going to want to move to an ORM like model where the object struct has a DB member and various methods on it. For now I'm just going to keep adding shit to the DB interface because if it doesn't get too long in the end then it's fine. // TODO I have a suspicion that I'm going to want to move to an ORM like model where the object struct has a DB member and various methods on it. For now I'm just going to keep adding shit to the DB interface because if it doesn't get too long in the end then it's fine.
@ -26,6 +26,12 @@ type DB interface {
EndSession(string) error EndSession(string) error
ClearSessions() error ClearSessions() error
// General
GetObject(owner, name string) (*Object, error)
// Defaults
Ensure() error
// Presence // Presence
ActiveSessions() ([]Session, error) ActiveSessions() ([]Session, error)
AvatarBySessionID(string) (*Object, error) AvatarBySessionID(string) (*Object, error)
@ -38,6 +44,7 @@ type Account struct {
ID int ID int
Name string Name string
Pwhash string Pwhash string
God bool
} }
type Session struct { type Session struct {
@ -70,7 +77,59 @@ func NewDB(connURL string) (DB, error) {
return pgdb, nil return pgdb, nil
} }
// Ensure checks for and then creates default resources if they do not exist (like the Foyer)
func (db *pgDB) Ensure() error {
// TODO this is sloppy but shrug
_, err := db.pool.Exec(context.Background(), schema)
//log.Println(err)
sysAcc, err := db.GetAccount("system")
if err != nil {
// TODO actually check error. for now assuming it means does not exist
sysAcc, err = db.CreateGod("system", "")
if err != nil {
return fmt.Errorf("failed to create system account: %w", err)
}
}
log.Printf("%#v", sysAcc)
if _, err := db.GetObject("system", "foyer"); err != nil {
data := map[string]string{}
data["name"] = "foyer"
data["description"] = "a big room. the ceiling is painted with constellations."
foyer := &Object{
Data: data,
Script: "",
// TODO default room script
}
if err = db.CreateObject(sysAcc, foyer); err != nil {
return err
}
}
return nil
}
func (db *pgDB) CreateGod(name, password string) (account *Account, err error) {
account = &Account{
Name: name,
Pwhash: password,
God: true,
}
return account, db.createAccount(account)
}
func (db *pgDB) CreateAccount(name, password string) (account *Account, err error) { func (db *pgDB) CreateAccount(name, password string) (account *Account, err error) {
account = &Account{
Name: name,
Pwhash: password,
}
return account, db.createAccount(account)
}
func (db *pgDB) createAccount(account *Account) (err error) {
ctx := context.Background() ctx := context.Background()
tx, err := db.pool.Begin(ctx) tx, err := db.pool.Begin(ctx)
if err != nil { if err != nil {
@ -79,13 +138,8 @@ func (db *pgDB) CreateAccount(name, password string) (account *Account, err erro
defer tx.Rollback(ctx) defer tx.Rollback(ctx)
account = &Account{ stmt := "INSERT INTO accounts (name, pwhash, god) VALUES ( $1, $2, $3 ) RETURNING id"
Name: name, err = tx.QueryRow(ctx, stmt, account.Name, account.Pwhash, account.God).Scan(&account.ID)
Pwhash: password,
}
stmt := "INSERT INTO accounts (name, pwhash) VALUES ( $1, $2 ) RETURNING id"
err = tx.QueryRow(ctx, stmt, name, password).Scan(&account.ID)
// TODO handle and cleanup unqiue violations // TODO handle and cleanup unqiue violations
if err != nil { if err != nil {
return return
@ -97,10 +151,11 @@ func (db *pgDB) CreateAccount(name, password string) (account *Account, err erro
av := &Object{ av := &Object{
Avatar: true, Avatar: true,
Data: data, Data: data,
Script: "",
} }
stmt = "INSERT INTO objects ( avatar, data, owner ) VALUES ( $1, $2, $3 ) RETURNING id" stmt = "INSERT INTO objects ( avatar, data, owner, script ) VALUES ( $1, $2, $3, $4 ) RETURNING id"
err = tx.QueryRow(ctx, stmt, av.Avatar, av.Data, account.ID).Scan(&av.ID) err = tx.QueryRow(ctx, stmt, av.Avatar, av.Data, account.ID, av.Script).Scan(&av.ID)
if err != nil { if err != nil {
return return
} }
@ -117,10 +172,11 @@ func (db *pgDB) CreateAccount(name, password string) (account *Account, err erro
bedroom := &Object{ bedroom := &Object{
Bedroom: true, Bedroom: true,
Data: data, Data: data,
Script: "",
} }
stmt = "INSERT INTO objects ( bedroom, data, owner ) VALUES ( $1, $2, $3 ) RETURNING id" stmt = "INSERT INTO objects ( bedroom, data, owner, script ) VALUES ( $1, $2, $3, $4 ) RETURNING id"
err = tx.QueryRow(ctx, stmt, bedroom.Bedroom, bedroom.Data, account.ID).Scan(&bedroom.ID) err = tx.QueryRow(ctx, stmt, bedroom.Bedroom, bedroom.Data, account.ID, bedroom.Script).Scan(&bedroom.ID)
if err != nil { if err != nil {
return return
} }
@ -131,9 +187,7 @@ func (db *pgDB) CreateAccount(name, password string) (account *Account, err erro
return return
} }
err = tx.Commit(ctx) return tx.Commit(ctx)
return
} }
func (db *pgDB) ValidateCredentials(name, password string) (*Account, error) { func (db *pgDB) ValidateCredentials(name, password string) (*Account, error) {
@ -142,6 +196,10 @@ func (db *pgDB) ValidateCredentials(name, password string) (*Account, error) {
return nil, err return nil, err
} }
if a.Pwhash == "" {
return nil, errors.New("this account cannot be logged into")
}
// TODO hashing lol // TODO hashing lol
if a.Pwhash != password { if a.Pwhash != password {
@ -276,6 +334,20 @@ func (db *pgDB) Earshot(obj Object) ([]Object, error) {
return out, nil return out, nil
} }
func (db *pgDB) GetObject(owner, name string) (obj *Object, err error) {
ctx := context.Background()
obj = &Object{}
stmt := `
SELECT id, avatar, data, owner, script
FROM objects
WHERE owner = $1 AND data['name'] = $2`
err = db.pool.QueryRow(ctx, stmt, owner, fmt.Sprintf(`"%s"`, name)).Scan(
&obj.ID, &obj.Avatar, &obj.Data, &obj.OwnerID, &obj.Script)
// TODO i think the escaping here is going to create a sadness ^
return
}
func (db *pgDB) ActiveSessions() (out []Session, err error) { func (db *pgDB) ActiveSessions() (out []Session, err error) {
stmt := `SELECT id, account FROM sessions` stmt := `SELECT id, account FROM sessions`
rows, err := db.pool.Query(context.Background(), stmt) rows, err := db.pool.Query(context.Background(), stmt)
@ -299,6 +371,24 @@ func (db *pgDB) ClearSessions() (err error) {
return return
} }
func (db *pgDB) CreateObject(owner *Account, obj *Object) error {
ctx := context.Background()
stmt := `
INSERT INTO objects (avatar, bedroom, data, script, owner)
VALUES ( $1, $2, $3, $4, $5)
RETURNING id
`
err := db.pool.QueryRow(ctx, stmt,
obj.Avatar, obj.Bedroom, obj.Data, obj.Script, owner.ID).Scan(
&obj.ID)
if err != nil {
return err
}
return nil
}
func randSmell() string { func randSmell() string {
// TODO seeding // TODO seeding
smells := []string{ smells := []string{

View File

@ -17,7 +17,7 @@ CREATE TABLE objects (
avatar boolean NOT NULL DEFAULT FALSE, avatar boolean NOT NULL DEFAULT FALSE,
bedroom boolean NOT NULL DEFAULT FALSE, bedroom boolean NOT NULL DEFAULT FALSE,
data jsonb NOT NULL, data jsonb NOT NULL,
script text NOT NULL, script text NOT NULL,
owner integer REFERENCES accounts ON DELETE RESTRICT owner integer REFERENCES accounts ON DELETE RESTRICT
); );

View File

@ -72,7 +72,7 @@ func newScriptContext(obj db.Object) (*scriptContext, error) {
return &scriptContext{}, nil return &scriptContext{}, nil
} }
func (sc *scriptContext) Handle(ver, rest string, sender, target *db.Object) error { func (sc *scriptContext) Handle(verb, rest string, sender, target *db.Object) error {
// TODO call _handle function from the Lstate // TODO call _handle function from the Lstate
return nil return nil
@ -82,12 +82,15 @@ type Gateway struct {
// maps game object IDs to script contexts // maps game object IDs to script contexts
m map[int]*scriptContext m map[int]*scriptContext
mu sync.RWMutex mu sync.RWMutex
cb func(string, string, *db.Object)
} }
func NewGateway() *Gateway { func NewGateway(cb func(string, string, *db.Object)) *Gateway {
return &Gateway{ return &Gateway{
m: map[int]*scriptContext{}, m: map[int]*scriptContext{},
mu: sync.RWMutex{}, mu: sync.RWMutex{},
// TODO use cb from scriptContext
cb: cb,
} }
} }