basic SAY verb handling
This commit is contained in:
		
							parent
							
								
									3db69fd8fb
								
							
						
					
					
						commit
						f021ba0133
					
				@ -178,6 +178,8 @@ func _main() error {
 | 
				
			|||||||
	commandInput := tview.NewInputField().SetLabel("> ")
 | 
						commandInput := tview.NewInputField().SetLabel("> ")
 | 
				
			||||||
	handleInput := func(_ tcell.Key) {
 | 
						handleInput := func(_ tcell.Key) {
 | 
				
			||||||
		input := commandInput.GetText()
 | 
							input := commandInput.GetText()
 | 
				
			||||||
 | 
							// TODO command history
 | 
				
			||||||
 | 
							commandInput.SetText("")
 | 
				
			||||||
		// TODO do i need to clear the input's text?
 | 
							// TODO do i need to clear the input's text?
 | 
				
			||||||
		go cs.HandleInput(input)
 | 
							go cs.HandleInput(input)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
				
			|||||||
@ -2,6 +2,7 @@ package main
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"context"
 | 
						"context"
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
	"flag"
 | 
						"flag"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"io"
 | 
						"io"
 | 
				
			||||||
@ -73,6 +74,10 @@ func newServer() (*gameWorldServer, error) {
 | 
				
			|||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err = db.ClearSessions(); err != nil {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("could not clear sessions: %w", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	s := &gameWorldServer{
 | 
						s := &gameWorldServer{
 | 
				
			||||||
		msgRouter: make(map[string]func(*proto.ClientMessage) error),
 | 
							msgRouter: make(map[string]func(*proto.ClientMessage) error),
 | 
				
			||||||
		db:        db,
 | 
							db:        db,
 | 
				
			||||||
@ -105,6 +110,31 @@ func (s *gameWorldServer) Commands(stream proto.GameWorld_CommandsServer) error
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		send := s.msgRouter[sid]
 | 
							send := s.msgRouter[sid]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// TODO what is the implication of returning an error from this function?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							avatar, err := s.db.AvatarBySessionID(sid)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return s.HandleError(send, err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							log.Printf("found avatar %#v", avatar)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							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)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/*
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			msg := &proto.ClientMessage{
 | 
								msg := &proto.ClientMessage{
 | 
				
			||||||
				Type: proto.ClientMessage_OVERHEARD,
 | 
									Type: proto.ClientMessage_OVERHEARD,
 | 
				
			||||||
				Text: fmt.Sprintf("%s sent command %s with args %s",
 | 
									Text: fmt.Sprintf("%s sent command %s with args %s",
 | 
				
			||||||
@ -118,10 +148,7 @@ func (s *gameWorldServer) Commands(stream proto.GameWorld_CommandsServer) error
 | 
				
			|||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				log.Printf("failed to send %v to %s: %s", msg, sid, err)
 | 
									log.Printf("failed to send %v to %s: %s", msg, sid, err)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
							*/
 | 
				
			||||||
		// TODO find the user who ran action via SessionInfo
 | 
					 | 
				
			||||||
		// TODO get area of effect, which should include the sender
 | 
					 | 
				
			||||||
		// TODO dispatch the command to each affected object
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -194,11 +221,94 @@ func (s *gameWorldServer) Login(ctx context.Context, auth *proto.AuthInfo) (si *
 | 
				
			|||||||
		return
 | 
							return
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						av, err := s.db.AvatarBySessionID(sessionID)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, fmt.Errorf("failed to find avatar for %s: %w", sessionID, err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						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)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	si = &proto.SessionInfo{SessionID: sessionID}
 | 
						si = &proto.SessionInfo{SessionID: sessionID}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// TODO actually put them in world
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return
 | 
						return
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *gameWorldServer) HandleSay(avatar *db.Object, msg string) error {
 | 
				
			||||||
 | 
						name := avatar.Data["name"]
 | 
				
			||||||
 | 
						if name == "" {
 | 
				
			||||||
 | 
							// TODO determine this based on a hash or something
 | 
				
			||||||
 | 
							name = "a mysterious figure"
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						heard, err := s.db.Earshot(*avatar)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Println(err.Error())
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						log.Printf("found %#v in earshot of %#v\n", heard, avatar)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						as, err := s.db.ActiveSessions()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sendErrs := []error{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, h := range heard {
 | 
				
			||||||
 | 
							// TODO once we have a script engine, deliver the HEARS event
 | 
				
			||||||
 | 
							for _, sess := range as {
 | 
				
			||||||
 | 
								if sess.AccountID == h.OwnerID {
 | 
				
			||||||
 | 
									cm := proto.ClientMessage{
 | 
				
			||||||
 | 
										Type:    proto.ClientMessage_OVERHEARD,
 | 
				
			||||||
 | 
										Text:    msg,
 | 
				
			||||||
 | 
										Speaker: &name,
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									err = s.msgRouter[sess.ID](&cm)
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										sendErrs = append(sendErrs, err)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(sendErrs) > 0 {
 | 
				
			||||||
 | 
							errMsg := "send errors: "
 | 
				
			||||||
 | 
							for i, err := range sendErrs {
 | 
				
			||||||
 | 
								errMsg += err.Error()
 | 
				
			||||||
 | 
								if i < len(sendErrs)-1 {
 | 
				
			||||||
 | 
									errMsg += ", "
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return errors.New(errMsg)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (s *gameWorldServer) HandleError(send func(*proto.ClientMessage) error, err error) error {
 | 
				
			||||||
 | 
						log.Printf("error: %s", err.Error())
 | 
				
			||||||
 | 
						msg := &proto.ClientMessage{
 | 
				
			||||||
 | 
							Type: proto.ClientMessage_WHISPER,
 | 
				
			||||||
 | 
							Text: "server error :(",
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						err = send(msg)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							log.Printf("error sending to client: %s", err.Error())
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TODO other server functions
 | 
					// TODO other server functions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func main() {
 | 
					func main() {
 | 
				
			||||||
 | 
				
			|||||||
@ -24,11 +24,14 @@ type DB interface {
 | 
				
			|||||||
	GetAccount(string) (*Account, error)
 | 
						GetAccount(string) (*Account, error)
 | 
				
			||||||
	StartSession(Account) (string, error)
 | 
						StartSession(Account) (string, error)
 | 
				
			||||||
	EndSession(string) error
 | 
						EndSession(string) error
 | 
				
			||||||
 | 
						ClearSessions() error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Presence
 | 
						// Presence
 | 
				
			||||||
 | 
						ActiveSessions() ([]Session, error)
 | 
				
			||||||
	AvatarBySessionID(string) (*Object, error)
 | 
						AvatarBySessionID(string) (*Object, error)
 | 
				
			||||||
	BedroomBySessionID(string) (*Object, error)
 | 
						BedroomBySessionID(string) (*Object, error)
 | 
				
			||||||
	MoveInto(toMove Object, container Object) error
 | 
						MoveInto(toMove Object, container Object) error
 | 
				
			||||||
 | 
						Earshot(Object) ([]Object, error)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Account struct {
 | 
					type Account struct {
 | 
				
			||||||
@ -37,11 +40,17 @@ type Account struct {
 | 
				
			|||||||
	Pwhash string
 | 
						Pwhash string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Session struct {
 | 
				
			||||||
 | 
						ID        string
 | 
				
			||||||
 | 
						AccountID int
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Object struct {
 | 
					type Object struct {
 | 
				
			||||||
	ID      int
 | 
						ID      int
 | 
				
			||||||
	Avatar  bool
 | 
						Avatar  bool
 | 
				
			||||||
	Bedroom bool
 | 
						Bedroom bool
 | 
				
			||||||
	Data    map[string]string
 | 
						Data    map[string]string
 | 
				
			||||||
 | 
						OwnerID int
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type pgDB struct {
 | 
					type pgDB struct {
 | 
				
			||||||
@ -194,11 +203,11 @@ func (db *pgDB) AvatarBySessionID(sid string) (avatar *Object, err error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	// TODO subquery
 | 
						// TODO subquery
 | 
				
			||||||
	stmt := `
 | 
						stmt := `
 | 
				
			||||||
	SELECT id, avatar, data
 | 
						SELECT id, avatar, data, owner
 | 
				
			||||||
	FROM objects WHERE avatar = true AND owner = (
 | 
						FROM objects WHERE avatar = true AND owner = (
 | 
				
			||||||
		SELECT a.id FROM sessions s JOIN accounts a ON s.account = a.id WHERE s.id = $1)`
 | 
							SELECT a.id FROM sessions s JOIN accounts a ON s.account = a.id WHERE s.id = $1)`
 | 
				
			||||||
	err = db.pool.QueryRow(context.Background(), stmt, sid).Scan(
 | 
						err = db.pool.QueryRow(context.Background(), stmt, sid).Scan(
 | 
				
			||||||
		&avatar.ID, &avatar.Avatar, &avatar.Data)
 | 
							&avatar.ID, &avatar.Avatar, &avatar.Data, &avatar.OwnerID)
 | 
				
			||||||
	return
 | 
						return
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -238,6 +247,54 @@ func (db *pgDB) MoveInto(toMove Object, container Object) error {
 | 
				
			|||||||
	return tx.Commit(ctx)
 | 
						return tx.Commit(ctx)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (db *pgDB) Earshot(obj Object) ([]Object, error) {
 | 
				
			||||||
 | 
						stmt := `
 | 
				
			||||||
 | 
						SELECT id, avatar, bedroom, data, owner FROM objects
 | 
				
			||||||
 | 
						WHERE id IN (
 | 
				
			||||||
 | 
							SELECT contained FROM contains
 | 
				
			||||||
 | 
							WHERE container = (
 | 
				
			||||||
 | 
								SELECT container FROM contains WHERE contained = $1 LIMIT 1))`
 | 
				
			||||||
 | 
						rows, err := db.pool.Query(context.Background(), stmt, obj.ID)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						out := []Object{}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for rows.Next() {
 | 
				
			||||||
 | 
							heard := Object{}
 | 
				
			||||||
 | 
							if err = rows.Scan(&heard.ID, &heard.Avatar, &heard.Bedroom, &heard.Data, &heard.OwnerID); err != nil {
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							out = append(out, heard)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return out, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (db *pgDB) ActiveSessions() (out []Session, err error) {
 | 
				
			||||||
 | 
						stmt := `SELECT id, account FROM sessions`
 | 
				
			||||||
 | 
						rows, err := db.pool.Query(context.Background(), stmt)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for rows.Next() {
 | 
				
			||||||
 | 
							s := Session{}
 | 
				
			||||||
 | 
							if err = rows.Scan(&s.ID, &s.AccountID); err != nil {
 | 
				
			||||||
 | 
								return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							out = append(out, s)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (db *pgDB) ClearSessions() (err error) {
 | 
				
			||||||
 | 
						_, err = db.pool.Exec(context.Background(), "DELETE FROM sessions")
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func randSmell() string {
 | 
					func randSmell() string {
 | 
				
			||||||
	// TODO seeding
 | 
						// TODO seeding
 | 
				
			||||||
	smells := []string{
 | 
						smells := []string{
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user