From 03e3aa866d6dbb1c7f56795bccea2f7653c278b7 Mon Sep 17 00:00:00 2001 From: vilmibm Date: Thu, 29 Dec 2022 23:01:12 -0500 Subject: [PATCH] handle SIGINT, some cleanup --- client/cmd/main.go | 35 +++++++++++++++-- server/cmd/main.go | 90 ++++++++++++++++++++++++++++++------------- server/db/db.go | 6 +-- server/witch/witch.go | 1 + 4 files changed, 98 insertions(+), 34 deletions(-) diff --git a/client/cmd/main.go b/client/cmd/main.go index 631a223..88f36b5 100644 --- a/client/cmd/main.go +++ b/client/cmd/main.go @@ -7,6 +7,8 @@ import ( "fmt" "io" "log" + "os" + "os/signal" "strings" "time" @@ -56,6 +58,25 @@ func (cs *ClientState) Messages() error { return nil } +func (cs *ClientState) HandleSIGINT(sigC chan os.Signal) { + for range sigC { + cm := &proto.Command{ + SessionInfo: cs.SessionInfo, + Verb: "quit", + } + err := cs.cmdStream.Send(cm) + if err != nil { + fmt.Printf("failed to send quit verb to server: %s\n", err.Error()) + } + _, err = cs.cmdStream.Recv() + if err != nil { + fmt.Printf("failed to receive an ACK from server: %s\n", err.Error()) + } + + cs.App.Stop() + } +} + func (cs *ClientState) HandleInput(input string) error { var verb string rest := input @@ -74,6 +95,10 @@ func (cs *ClientState) HandleInput(input string) error { if err != nil { return err } + _, err = cs.cmdStream.Recv() + if err != nil { + fmt.Printf("failed to receive an ACK from server: %s\n", err.Error()) + } if verb == "quit" || verb == "q" { cs.App.Stop() } @@ -158,13 +183,10 @@ func _main() error { ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) defer cancel() - pong, err := cs.Client.Ping(ctx, cs.SessionInfo) - if err != nil { + if _, err = cs.Client.Ping(ctx, cs.SessionInfo); err != nil { log.Fatalf("%v.Ping -> %v", cs.Client, err) } - log.Printf("%#v", pong) - pages := tview.NewPages() pages.AddPage("splash", @@ -202,6 +224,9 @@ func _main() error { pages.AddPage("main", mainPage, true, false) + sigC := make(chan os.Signal, 1) + signal.Notify(sigC, os.Interrupt) + lunfi := tview.NewInputField().SetLabel("account name") lpwfi := tview.NewInputField().SetLabel("password").SetMaskCharacter('~') @@ -225,6 +250,7 @@ func _main() error { pages.SwitchToPage("game") app.SetFocus(commandInput) + go cs.HandleSIGINT(sigC) // TODO error handle go cs.Messages() } @@ -260,6 +286,7 @@ func _main() error { pages.SwitchToPage("game") app.SetFocus(commandInput) + go cs.HandleSIGINT(sigC) // TODO error handle go cs.Messages() } diff --git a/server/cmd/main.go b/server/cmd/main.go index 92d55d4..ce78466 100644 --- a/server/cmd/main.go +++ b/server/cmd/main.go @@ -5,7 +5,6 @@ import ( "errors" "flag" "fmt" - "io" "log" "net" "sync" @@ -37,7 +36,6 @@ func _main() (err error) { if err != nil { return err } - fmt.Printf("DBG %#v\n", l) var opts []grpc.ServerOption if *tls { @@ -146,7 +144,7 @@ func (s *gameWorldServer) verbHandler(verb, rest string, sender, target db.Objec } } - if !ok { + if !ok || sc == nil { sc, err = witch.NewScriptContext(serverAPI) if err != nil { return err @@ -173,42 +171,83 @@ func (s *gameWorldServer) HandleCmd(verb, rest string, sender *db.Object) { // TODO } +func (s *gameWorldServer) endSession(sid string) error { + log.Printf("ending session %s", sid) + + errors := []string{} + err := s.db.EndSession(sid) + if err != nil { + errors = append(errors, err.Error()) + } + + s.msgRouter[sid] = nil + + avatar, err := s.db.AvatarBySessionID(sid) + if err != nil { + errors = append(errors, err.Error()) + } else { + s.scriptsMutex.Lock() + s.scripts[avatar.ID] = nil + s.scriptsMutex.Unlock() + } + + if len(errors) > 0 { + return fmt.Errorf("error(s) encountered trying to end session %s: %v", sid, errors) + } + + return nil +} + func (s *gameWorldServer) Commands(stream proto.GameWorld_CommandsServer) error { var sid string + var cmd *proto.Command + var err error + var avatar *db.Object + var send func(*proto.ClientMessage) error + var affected []db.Object + var o db.Object for { - cmd, err := stream.Recv() - if err == io.EOF { - // TODO this doesn't really do anything. if a client - // disconnects without warning there's no EOF. - return s.db.EndSession(sid) - } - if err != nil { + if cmd, err = stream.Recv(); err != nil { + log.Printf("commands stream closed with error: %s", err.Error()) return err } - sid = cmd.SessionInfo.SessionID - - log.Printf("verb %s in session %s", cmd.Verb, sid) - - if cmd.Verb == "quit" || cmd.Verb == "q" { - s.msgRouter[sid] = nil - log.Printf("ending session %s", sid) - return s.db.EndSession(sid) + if sid == "" { + sid = cmd.SessionInfo.SessionID + defer s.endSession(sid) } - send := s.msgRouter[sid] - // TODO what is the implication of returning an error from this function? + err = stream.Send(&proto.CommandAck{ + Acked: true, + }) - avatar, err := s.db.AvatarBySessionID(sid) if err != nil { + log.Printf("unable to ack command in session %s", sid) + return err + } + + if send == nil { + send = s.msgRouter[sid] + } + + if avatar, err = s.db.AvatarBySessionID(sid); err != nil { return s.HandleError(send, err) } - log.Printf("found avatar %#v", avatar) - affected, err := s.db.Earshot(*avatar) + log.Printf("verb %s from avatar %d in session %s", cmd.Verb, avatar.ID, sid) - for _, o := range affected { - err = s.verbHandler(cmd.Verb, cmd.Rest, *avatar, o) + if cmd.Verb == "quit" || cmd.Verb == "q" { + return nil + } + + if affected, err = s.db.Earshot(*avatar); err != nil { + return s.HandleError(send, err) + } + + for _, o = range affected { + if err = s.verbHandler(cmd.Verb, cmd.Rest, *avatar, o); err != nil { + log.Printf("error handling verb %s for object %d: %s", cmd.Verb, o.ID, err) + } } //s.HandleCmd(cmd.Verb, cmd.Rest, avatar) @@ -387,7 +426,6 @@ func (s *gameWorldServer) HandleError(send func(*proto.ClientMessage) error, err // TODO other server functions func main() { - // TODO at some point during startup clear out sessions err := _main() if err != nil { log.Fatal(err.Error()) diff --git a/server/db/db.go b/server/db/db.go index fcb0973..1ad4cc1 100644 --- a/server/db/db.go +++ b/server/db/db.go @@ -297,12 +297,11 @@ func (db *pgDB) SessionIDForAvatar(obj Object) (string, error) { if !obj.Avatar { return "", nil } - fmt.Printf("%#v", obj) ctx := context.Background() stmt := `SELECT id FROM sessions WHERE account = $1` var sid *string - err := db.pool.QueryRow(ctx, stmt, obj.OwnerID).Scan(&sid) - if err != nil { + var err error + if err = db.pool.QueryRow(ctx, stmt, obj.OwnerID).Scan(&sid); err != nil { return "", err } @@ -315,7 +314,6 @@ func (db *pgDB) SessionIDForAvatar(obj Object) (string, error) { func (db *pgDB) AvatarBySessionID(sid string) (avatar *Object, err error) { avatar = &Object{} - // TODO subquery stmt := ` SELECT id, avatar, data, owner, script diff --git a/server/witch/witch.go b/server/witch/witch.go index ea4d26e..c95a6fe 100644 --- a/server/witch/witch.go +++ b/server/witch/witch.go @@ -114,5 +114,6 @@ func NewScriptContext(sAPI ServerAPI) (*ScriptContext, error) { } func (sc *ScriptContext) Handle(vc VerbContext) { + log.Printf("%#v", sc) sc.incoming <- vc }