Add replay command

This commit is contained in:
Trevor Slocum 2023-12-15 23:27:24 -08:00
parent 5191ecb83d
commit 764c491c5b
8 changed files with 69 additions and 5 deletions

View file

@ -51,6 +51,9 @@ formatted responses are more easily parsed by computers.
- Change account setting.
- Available settings: `highlight`, `pips` and `moves`.
- `replay <id>`
- Retrieve replay of the specified game.
- `json <on/off>`
- Turn JSON formatted messages on or off. JSON messages are not sent by default.

View file

@ -22,9 +22,9 @@ The index is always eight digits in length with leading zeroes.
#### Metadata
The first line of the game is the metadata.
The first line of the game is the metadata. The timestamp specifies when the game started.
`i <player1> <player2> <total> <score1> <score2> <winner> <points> <acey>`
`i <timestamp> <player1> <player2> <total> <score1> <score2> <winner> <points> <acey>`
#### Events

View file

@ -10,6 +10,7 @@ const (
CommandResetPassword = "resetpassword" // Request password reset link via email.
CommandPassword = "password" // Change password.
CommandSet = "set" // Change account setting.
CommandReplay = "replay" // Retrieve replay.
CommandHelp = "help" // Print help information.
CommandJSON = "json" // Enable or disable JSON formatted messages.
CommandSay = "say" // Send chat message.
@ -50,4 +51,5 @@ const (
EventTypeFailedOk = "failedok"
EventTypeWin = "win"
EventTypeSettings = "settings"
EventTypeReplay = "replay"
)

View file

@ -117,6 +117,12 @@ type EventSettings struct {
Moves bool
}
type EventReplay struct {
Event
ID int
Content []byte
}
func DecodeEvent(message []byte) (interface{}, error) {
e := &Event{}
err := json.Unmarshal(message, e)
@ -162,6 +168,8 @@ func DecodeEvent(message []byte) (interface{}, error) {
ev = &EventWin{}
case EventTypeSettings:
ev = &EventSettings{}
case EventTypeReplay:
ev = &EventReplay{}
default:
return nil, fmt.Errorf("failed to decode event: unknown event type: %s", e.Type)
}

View file

@ -65,6 +65,8 @@ func (c *serverClient) sendEvent(e interface{}) {
ev.Type = bgammon.EventTypeWin
case *bgammon.EventSettings:
ev.Type = bgammon.EventTypeSettings
case *bgammon.EventReplay:
ev.Type = bgammon.EventTypeReplay
default:
log.Panicf("unknown event type %+v", ev)
}
@ -128,7 +130,7 @@ func (c *serverClient) sendEvent(e interface{}) {
c.Write([]byte(fmt.Sprintf("win %s wins!", ev.Player)))
}
default:
log.Panicf("unknown event type %+v", ev)
log.Printf("warning: skipped sending unknown event to non-json client: %+v", ev)
}
}

View file

@ -412,6 +412,27 @@ func recordGameResult(g *bgammon.Game, winType int, account1 int, account2 int,
return err
}
func replayByID(id int) ([]byte, error) {
if db == nil {
return nil, nil
} else if id <= 0 {
return nil, fmt.Errorf("please specify an id")
}
tx, err := begin()
if err != nil {
return nil, err
}
defer tx.Commit(context.Background())
var replay []byte
err = tx.QueryRow(context.Background(), "SELECT replay FROM game WHERE id = $1", id).Scan(&replay)
if err != nil {
return nil, nil
}
return replay, nil
}
func dailyStats(tz *time.Location) (*serverStatsResult, error) {
tx, err := begin()
if err != nil {

View file

@ -43,6 +43,10 @@ func setAccountSetting(id int, name string, value int) error {
return nil
}
func replayByID(id int) ([]byte, error) {
return nil, nil
}
func recordGameResult(g *bgammon.Game, winType int, account1 int, account2 int, replay [][]byte) error {
return nil
}

View file

@ -996,7 +996,7 @@ COMMANDS:
if clientGame.Acey {
acey = 1
}
clientGame.replay = append([][]byte{[]byte(fmt.Sprintf("i %s %s %d %d %d %d %d %d", clientGame.Player1.Name, clientGame.Player2.Name, clientGame.Points, clientGame.Player1.Points, clientGame.Player2.Points, clientGame.Winner, clientGame.DoubleValue, acey))}, clientGame.replay...)
clientGame.replay = append([][]byte{[]byte(fmt.Sprintf("i %d %s %s %d %d %d %d %d %d", clientGame.Started.Unix(), clientGame.Player1.Name, clientGame.Player2.Name, clientGame.Points, clientGame.Player1.Points, clientGame.Player2.Points, clientGame.Winner, clientGame.DoubleValue, acey))}, clientGame.replay...)
clientGame.replay = append(clientGame.replay, []byte(fmt.Sprintf("%d d %d 0", clientGame.Turn, clientGame.DoubleValue*2)))
@ -1292,7 +1292,7 @@ COMMANDS:
if clientGame.Acey {
acey = 1
}
clientGame.replay = append([][]byte{[]byte(fmt.Sprintf("i %s %s %d %d %d %d %d %d", clientGame.Player1.Name, clientGame.Player2.Name, clientGame.Points, clientGame.Player1.Points, clientGame.Player2.Points, clientGame.Winner, winPoints, acey))}, clientGame.replay...)
clientGame.replay = append([][]byte{[]byte(fmt.Sprintf("i %d %s %s %d %d %d %d %d %d", clientGame.Started.Unix(), clientGame.Player1.Name, clientGame.Player2.Name, clientGame.Points, clientGame.Player1.Points, clientGame.Player2.Points, clientGame.Winner, winPoints, acey))}, clientGame.replay...)
clientGame.replay = append(clientGame.replay, []byte(fmt.Sprintf("%d r %d-%d %s", clientGame.Turn, clientGame.Roll1, clientGame.Roll2, bgammon.FormatMoves(clientGame.Moves))))
@ -1645,6 +1645,30 @@ COMMANDS:
continue
}
_ = setAccountSetting(cmd.client.account, name, value)
case bgammon.CommandReplay:
if len(params) == 0 {
cmd.client.sendNotice("Please specify the game as follows: replay <id>")
continue
}
id, err := strconv.Atoi(string(params[0]))
if err != nil || id < 0 {
cmd.client.sendNotice("Invalid replay ID provided.")
continue
}
replay, err := replayByID(id)
if err != nil {
cmd.client.sendNotice("Invalid replay ID provided.")
continue
} else if len(replay) == 0 {
cmd.client.sendNotice("No replay was recorded for that game.")
continue
}
cmd.client.sendEvent(&bgammon.EventReplay{
ID: id,
Content: replay,
})
case bgammon.CommandDisconnect:
if clientGame != nil {
clientGame.removeClient(cmd.client)