171 lines
4.4 KiB
Go
171 lines
4.4 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 codename := gameState.Players[killer].KillWords.Codename; codename.Word == word {
|
|
points = codename.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 if err := ugs.games.Reset(ctx, ugs.ID); err != nil {
|
|
return err
|
|
}
|
|
} 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
|
|
}
|
|
ugs.ID = gameState.ID
|
|
|
|
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.Codename = 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
|
|
}
|