apply game actions from connections

This commit is contained in:
Branden J Brown 2024-01-29 13:12:25 -06:00
parent 6077316a0d
commit 1099e0b618
1 changed files with 70 additions and 6 deletions

74
game.go
View File

@ -16,16 +16,48 @@ import (
// An action is a request to change the game state.
type action struct {
Action string `json:"action"`
Param string `json:"param"`
Player player.ID `json:"-"`
}
func applyAction(g *game.Match, a action) error {
switch a.Action {
case "quit":
return g.Concede(a.Player)
case "across", "self":
if err := g.Shoot(a.Player, a.Action == "self"); err != nil {
return err
}
if g.RoundWinner() != nil {
return errRoundEnded
}
return nil
case "0":
return g.Apply(a.Player, 0)
case "1":
return g.Apply(a.Player, 1)
case "2":
return g.Apply(a.Player, 2)
case "3":
return g.Apply(a.Player, 3)
case "4":
return g.Apply(a.Player, 4)
case "5":
return g.Apply(a.Player, 5)
case "6":
return g.Apply(a.Player, 6)
case "7":
return g.Apply(a.Player, 7)
default:
return errWeirdAction
}
}
// gameActor is an actor that updates a game's state and relays changes to all
// observers.
func gameActor(ctx context.Context, g *game.Match, dealer, chall person, join <-chan person) {
// Games should generally be on the order of minutes. A four hour game is
// definitely expired.
ctx, stop := context.WithTimeoutCause(ctx, 4*time.Hour, errGameExpired)
ctx, stop := context.WithTimeoutCause(ctx, 4*time.Hour, errMatchExpired)
defer stop()
var obs []person
actions := make(chan action, 2)
@ -51,9 +83,27 @@ func gameActor(ctx context.Context, g *game.Match, dealer, chall person, join <-
// observer when they disconnect
p.conn.CloseRead(ctx)
case a := <-actions:
// TODO(zeph): transform the action into a game state change
slog.DebugContext(ctx, "got action", "from", a.Player, "action", a)
slog.DebugContext(ctx, "got action", "from", a.Player, "action", a.Action)
err := applyAction(g, a)
switch err {
case nil:
broadcast(ctx, g, dealer, chall, obs)
case errRoundEnded:
broadcast(ctx, g, dealer, chall, obs)
if g.MatchWinner() != nil {
gameOver(ctx, dealer, chall, obs)
// TODO(zeph): server needs to know the players are gone
return
}
g.NextRound()
broadcast(ctx, g, dealer, chall, obs)
// TODO(zeph): broadcast shell counts?
case game.ErrWrongTurn: // do nothing
case errWeirdAction:
slog.WarnContext(ctx, "nonsense action", "from", a.Player, "action", a.Action)
default:
slog.ErrorContext(ctx, "action caused a mystery error", "from", a.Player, "action", a.Action, "err", err.Error())
}
}
}
}
@ -98,4 +148,18 @@ func broadcast(ctx context.Context, g *game.Match, dealer, chall person, obs []p
}
}
var errGameExpired = errors.New("there is a time limit on games please")
func gameOver(ctx context.Context, dealer, chall person, obs []person) {
// TODO(zeph): need to communicate to the server that these have gone away
go dealer.conn.Close(websocket.StatusNormalClosure, "match ended")
go chall.conn.Close(websocket.StatusNormalClosure, "match ended")
for _, p := range obs {
c := p.conn
go c.Close(websocket.StatusNormalClosure, "match ended")
}
}
var (
errRoundEnded = errors.New("someone h*ckin died")
errWeirdAction = errors.New("unknown action")
errMatchExpired = errors.New("there is a time limit on matches please")
)