out/cmd/server/usergameserver.go

170 lines
4.3 KiB
Go

package main
import (
"context"
"encoding/json"
"fmt"
"log"
"slices"
"time"
)
type UserGameServer struct {
ID string
Session Session
games Games
lastPoll time.Time
}
func NewUserGameServer(ctx context.Context, session Session, games Games) (*UserGameServer, error) {
ids, err := games.GamesForUser(ctx, session.ID)
if err != nil {
return nil, err
}
if len(ids) == 0 {
return nil, fmt.Errorf("user %s is in zero games", session.ID)
}
return &UserGameServer{
ID: ids[0],
Session: session,
games: games,
}, nil
}
func (ugs *UserGameServer) More(ctx context.Context) error {
defer func() {
ugs.lastPoll = time.Now()
}()
for {
select {
case <-ctx.Done():
return ctx.Err()
case <-time.After(time.Second * 1):
}
if events, err := ugs.games.GameEvents(ctx, ugs.ID, ugs.lastPoll); err != nil {
return err
} else if len(events) == 0 {
continue
}
return nil
}
}
func (ugs *UserGameServer) Listen(ctx context.Context, can context.CancelFunc, reader func(context.Context) ([]byte, error)) {
defer can()
if err := ugs.listen(ctx, reader); err != nil && ctx.Err() == nil {
log.Println(err)
}
}
func (ugs *UserGameServer) listen(ctx context.Context, reader func(context.Context) ([]byte, error)) error {
for ctx.Err() == nil {
b, err := reader(ctx)
if err != nil {
return err
}
var m map[string]string
if err := json.Unmarshal(b, &m); err != nil {
return err
}
if startGame := m["party"] == "start"; startGame {
if gameState, err := ugs.games.GameState(ctx, ugs.ID); err != nil {
return err
} else if gameState.Started {
} else if err := ugs.games.CreateEventAssignmentRotation(ctx, ugs.ID, "", "", "", 0); err != nil {
return err
}
} else if killOccurred := m["k"] != ""; killOccurred {
victimName := m["name"]
word := m["k"]
if word == "" {
return fmt.Errorf("expected .k")
}
killer := ugs.Session.ID
victim, err := ugs.games.UserByName(ctx, ugs.ID, victimName)
if err != nil {
return err
}
var points int
if gameState, err := ugs.games.GameState(ctx, ugs.ID); err != nil {
return err
} else if global := gameState.Players[killer].KillWords.Global; global.Word == word {
points = global.Points
} else if matches := slices.DeleteFunc(gameState.Players[victim].KillWords.Publics(), func(kw KillWord) bool { return kw.Word != word }); len(matches) > 0 {
points = matches[0].Points
} else if matches := slices.DeleteFunc(gameState.Players[victim].KillWords.Privates(), func(kw KillWord) bool { return kw.Word != word }); len(matches) > 0 {
points = matches[0].Points
} else {
return fmt.Errorf("refusing unexpected .k")
}
if err := ugs.games.CreateEventAssignmentRotation(ctx, ugs.ID, killer, victim, word, points); err != nil {
return err
}
} else if isRename := m["name"] != ""; isRename {
if err := ugs.games.UpdateUserName(ctx, ugs.Session.ID, m["name"]); err != nil {
return err
}
} else if isRestart := m["again"] == "true"; isRestart {
if gameState, err := ugs.games.GameState(ctx, ugs.ID); err != nil {
return err
} else if gameState.Completed.IsZero() {
} else {
return fmt.Errorf("not impl: new game: %+v", m)
}
} else {
return fmt.Errorf("UNKNOWN: %+v", m)
}
}
return ctx.Err()
}
type UserGameState GameState
func (ugs *UserGameServer) State(ctx context.Context) (UserGameState, error) {
gameState, err := ugs.games.GameState(ctx, ugs.ID)
if err != nil {
return UserGameState{}, err
}
if complete := !gameState.Completed.IsZero(); complete {
return UserGameState(gameState), nil
}
self := gameState.Players[ugs.Session.ID]
for i := range self.Kills {
self.Kills[i].KillWord.Points = 0
}
self.KillWords.Assignment.Public = nil
self.KillWords.Assignment.Private = nil
gameState.Players[ugs.Session.ID] = self
for k, v := range gameState.Players {
if isSelf := k == ugs.Session.ID; isSelf {
v.KillWords.Assignment = Assignment{}
} else {
v.KillWords.Global = KillWord{}
v.KillWords.Assignee = ""
for i := range v.Kills {
v.Kills[i].Victim = ""
v.Kills[i].KillWord.Word = ""
}
}
if assignedToSomeoneElse := self.KillWords.Assignee != k; assignedToSomeoneElse {
v.KillWords.Assignment.Private = v.KillWords.Assignment.Private[:0]
}
gameState.Players[k] = v
}
return UserGameState(gameState), nil
}