handle SIGINT, some cleanup

trunk
vilmibm 2022-12-29 23:01:12 -05:00
parent 8a56e79f5d
commit 03e3aa866d
4 changed files with 98 additions and 34 deletions

View File

@ -7,6 +7,8 @@ import (
"fmt" "fmt"
"io" "io"
"log" "log"
"os"
"os/signal"
"strings" "strings"
"time" "time"
@ -56,6 +58,25 @@ func (cs *ClientState) Messages() error {
return nil 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 { func (cs *ClientState) HandleInput(input string) error {
var verb string var verb string
rest := input rest := input
@ -74,6 +95,10 @@ func (cs *ClientState) HandleInput(input string) error {
if err != nil { if err != nil {
return err 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" { if verb == "quit" || verb == "q" {
cs.App.Stop() cs.App.Stop()
} }
@ -158,13 +183,10 @@ func _main() error {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel() defer cancel()
pong, err := cs.Client.Ping(ctx, cs.SessionInfo) if _, err = cs.Client.Ping(ctx, cs.SessionInfo); err != nil {
if err != nil {
log.Fatalf("%v.Ping -> %v", cs.Client, err) log.Fatalf("%v.Ping -> %v", cs.Client, err)
} }
log.Printf("%#v", pong)
pages := tview.NewPages() pages := tview.NewPages()
pages.AddPage("splash", pages.AddPage("splash",
@ -202,6 +224,9 @@ func _main() error {
pages.AddPage("main", mainPage, true, false) pages.AddPage("main", mainPage, true, false)
sigC := make(chan os.Signal, 1)
signal.Notify(sigC, os.Interrupt)
lunfi := tview.NewInputField().SetLabel("account name") lunfi := tview.NewInputField().SetLabel("account name")
lpwfi := tview.NewInputField().SetLabel("password").SetMaskCharacter('~') lpwfi := tview.NewInputField().SetLabel("password").SetMaskCharacter('~')
@ -225,6 +250,7 @@ func _main() error {
pages.SwitchToPage("game") pages.SwitchToPage("game")
app.SetFocus(commandInput) app.SetFocus(commandInput)
go cs.HandleSIGINT(sigC)
// TODO error handle // TODO error handle
go cs.Messages() go cs.Messages()
} }
@ -260,6 +286,7 @@ func _main() error {
pages.SwitchToPage("game") pages.SwitchToPage("game")
app.SetFocus(commandInput) app.SetFocus(commandInput)
go cs.HandleSIGINT(sigC)
// TODO error handle // TODO error handle
go cs.Messages() go cs.Messages()
} }

View File

@ -5,7 +5,6 @@ import (
"errors" "errors"
"flag" "flag"
"fmt" "fmt"
"io"
"log" "log"
"net" "net"
"sync" "sync"
@ -37,7 +36,6 @@ func _main() (err error) {
if err != nil { if err != nil {
return err return err
} }
fmt.Printf("DBG %#v\n", l)
var opts []grpc.ServerOption var opts []grpc.ServerOption
if *tls { 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) sc, err = witch.NewScriptContext(serverAPI)
if err != nil { if err != nil {
return err return err
@ -173,42 +171,83 @@ func (s *gameWorldServer) HandleCmd(verb, rest string, sender *db.Object) {
// TODO // 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 { func (s *gameWorldServer) Commands(stream proto.GameWorld_CommandsServer) error {
var sid string 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 { for {
cmd, err := stream.Recv() if cmd, err = stream.Recv(); err != nil {
if err == io.EOF { log.Printf("commands stream closed with error: %s", err.Error())
// 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 {
return err return err
} }
sid = cmd.SessionInfo.SessionID if sid == "" {
sid = cmd.SessionInfo.SessionID
log.Printf("verb %s in session %s", cmd.Verb, sid) defer s.endSession(sid)
if cmd.Verb == "quit" || cmd.Verb == "q" {
s.msgRouter[sid] = nil
log.Printf("ending session %s", sid)
return s.db.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 { 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) 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 { if cmd.Verb == "quit" || cmd.Verb == "q" {
err = s.verbHandler(cmd.Verb, cmd.Rest, *avatar, o) 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) //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 // TODO other server functions
func main() { func main() {
// TODO at some point during startup clear out sessions
err := _main() err := _main()
if err != nil { if err != nil {
log.Fatal(err.Error()) log.Fatal(err.Error())

View File

@ -297,12 +297,11 @@ func (db *pgDB) SessionIDForAvatar(obj Object) (string, error) {
if !obj.Avatar { if !obj.Avatar {
return "", nil return "", nil
} }
fmt.Printf("%#v", obj)
ctx := context.Background() ctx := context.Background()
stmt := `SELECT id FROM sessions WHERE account = $1` stmt := `SELECT id FROM sessions WHERE account = $1`
var sid *string var sid *string
err := db.pool.QueryRow(ctx, stmt, obj.OwnerID).Scan(&sid) var err error
if err != nil { if err = db.pool.QueryRow(ctx, stmt, obj.OwnerID).Scan(&sid); err != nil {
return "", err return "", err
} }
@ -315,7 +314,6 @@ func (db *pgDB) SessionIDForAvatar(obj Object) (string, error) {
func (db *pgDB) AvatarBySessionID(sid string) (avatar *Object, err error) { func (db *pgDB) AvatarBySessionID(sid string) (avatar *Object, err error) {
avatar = &Object{} avatar = &Object{}
// TODO subquery // TODO subquery
stmt := ` stmt := `
SELECT id, avatar, data, owner, script SELECT id, avatar, data, owner, script

View File

@ -114,5 +114,6 @@ func NewScriptContext(sAPI ServerAPI) (*ScriptContext, error) {
} }
func (sc *ScriptContext) Handle(vc VerbContext) { func (sc *ScriptContext) Handle(vc VerbContext) {
log.Printf("%#v", sc)
sc.incoming <- vc sc.incoming <- vc
} }