Add initial JSON events

This commit is contained in:
Trevor Slocum 2023-08-26 19:47:12 -07:00
parent 138a91e57d
commit 1c60039ed9
6 changed files with 137 additions and 32 deletions

View file

@ -10,12 +10,17 @@ Log in to bgammon. A random username is assigned when none is provided.
This must be the first command sent when a client connects to bgammon.
### `json [on/off]`
### `loginjson [username] [password]`
Log in to bgammon and enable JSON formatted responses.
All client applications should use the `loginjson` command to log in, as JSON
formatted responses are more easily parsed by computers.
### `json <on/off>`
Turn JSON formatted messages on or off. JSON messages are not sent by default.
Graphical clients should send the `json on` command immediately after sending `login`.
### `help [command]`
Request help for all commands, or optionally a specific command.
@ -24,11 +29,11 @@ Request help for all commands, or optionally a specific command.
List all games.
### `create [public/private] [password]`
### `create <public/private> [password]`
List all games.
### `join [id] [password]`
### `join <id> [password]`
Join game.

View file

@ -1,5 +0,0 @@
package main
func main() {
}

View file

@ -94,6 +94,17 @@ func (g *serverGame) sendBoard(client *serverClient) {
}
}
func (g *serverGame) playerCount() int {
c := 0
if g.client1 != nil {
c++
}
if g.client2 != nil {
c++
}
return c
}
func (g *serverGame) eachClient(f func(client *serverClient)) {
if g.client1 != nil {
f(g.client1)

View file

@ -3,6 +3,7 @@ package main
import (
"bufio"
"bytes"
"encoding/json"
"fmt"
"log"
"math/rand"
@ -184,7 +185,7 @@ COMMANDS:
// Require users to send login command first.
if cmd.client.account == -1 {
if keyword == bgammon.CommandLogin || keyword == "l" {
if keyword == bgammon.CommandLogin || keyword == bgammon.CommandLoginJSON || keyword == "l" || keyword == "lj" {
var username []byte
var password []byte
switch len(params) {
@ -204,6 +205,10 @@ COMMANDS:
}
cmd.client.name = username
if keyword == bgammon.CommandLoginJSON || keyword == "lj" {
cmd.client.json = true
}
s.sendWelcome(cmd.client)
log.Printf("login as %s - %s", username, password)
@ -215,6 +220,13 @@ COMMANDS:
}
clientGame := s.gameByClient(cmd.client)
clientNumber := 0
if clientGame != nil {
clientNumber = 1
if clientGame.client2 == cmd.client {
clientNumber = 2
}
}
switch keyword {
case bgammon.CommandHelp, "h":
@ -256,6 +268,23 @@ COMMANDS:
}
opponent.events <- []byte(fmt.Sprintf("say %s %s", cmd.client.name, bytes.Join(params, []byte(" "))))
case bgammon.CommandList, "ls":
if cmd.client.json {
ev := bgammon.EventList{}
for _, g := range s.games {
ev.Games = append(ev.Games, bgammon.GameListing{
ID: g.id,
Password: len(g.password) != 0,
Players: g.playerCount(),
Name: string(g.name),
})
}
buf, err := json.Marshal(ev)
if err != nil {
panic(err)
}
cmd.client.events <- buf
continue
}
cmd.client.events <- []byte("liststart Games list:")
players := 0
password := 0
@ -357,18 +386,14 @@ COMMANDS:
continue
}
playerNumber := 1
if clientGame.client2 == cmd.client {
playerNumber = 2
}
if !clientGame.roll(playerNumber) {
if !clientGame.roll(clientNumber) {
cmd.client.events <- []byte("notice It is not your turn to roll.")
continue
}
clientGame.eachClient(func(client *serverClient) {
roll1 := 0
roll2 := 0
if playerNumber == 1 {
if clientNumber == 1 {
roll1 = clientGame.Roll1
} else {
roll2 = clientGame.Roll2
@ -391,11 +416,7 @@ COMMANDS:
continue
}
playerNumber := 1
if clientGame.client2 == cmd.client {
playerNumber = 2
}
if clientGame.Turn != playerNumber {
if clientGame.Turn != clientNumber {
cmd.client.events <- []byte("failedmove It is not your turn to move.")
continue
}
@ -432,7 +453,7 @@ COMMANDS:
}
originalFrom, originalTo := from, to
from, to = bgammon.FlipSpace(from, playerNumber), bgammon.FlipSpace(to, playerNumber)
from, to = bgammon.FlipSpace(from, clientNumber), bgammon.FlipSpace(to, clientNumber)
legalMoves := gameCopy.LegalMoves()
var found bool
@ -443,7 +464,7 @@ COMMANDS:
}
}
if !found {
log.Printf("available legal moves: %s", bgammon.FormatMoves(legalMoves, playerNumber))
log.Printf("available legal moves: %s", bgammon.FormatMoves(legalMoves, clientNumber))
cmd.client.events <- []byte(fmt.Sprintf("failedmove %d/%d Illegal move.", originalFrom, originalTo))
continue COMMANDS
}
@ -487,12 +508,7 @@ COMMANDS:
continue
}
playerNumber := 1
if clientGame.client2 == cmd.client {
playerNumber = 2
}
scanner := bufio.NewScanner(bytes.NewReader(clientGame.BoardState(playerNumber)))
scanner := bufio.NewScanner(bytes.NewReader(clientGame.BoardState(clientNumber)))
for scanner.Scan() {
cmd.client.events <- append([]byte("notice "), scanner.Bytes()...)
}

View file

@ -6,6 +6,7 @@ type Command string
const (
CommandLogin = "login" // Log in with username and password, or as a guest.
CommandLoginJSON = "loginjson" // Log in with username and password, or as a guest, and enable JSON messages.
CommandHelp = "help" // Print help information.
CommandJSON = "json" // Enable or disable JSON formatted messages.
CommandSay = "say" // Send chat message.
@ -20,3 +21,10 @@ const (
CommandBoard = "board" // Print current board state in human-readable form.
CommandDisconnect = "disconnect" // Disconnect from server.
)
type EventType string
const (
EventTypeWelcome = "welcome"
EventTypeJoined = "joined"
)

View file

@ -1,8 +1,78 @@
package bgammon
import (
"encoding/json"
"fmt"
)
// events are always received FROM the server
type Event struct {
Player int
Command
Type string
Player string
}
type EventWelcome struct {
PlayerName string
Clients int
Games int
}
type EventJoined struct {
GameID int
PlayerName string
}
type GameListing struct {
ID int
Password bool
Players int
Name string
}
type EventList struct {
Games []GameListing
}
type EventSay struct {
Message string
}
type EventBoard struct {
GameState
}
type EventRoll struct {
Roll1 int
Roll2 int
}
type EventMove struct {
Moves [][]int
}
func DecodeEvent(message []byte) (interface{}, error) {
e := &Event{}
err := json.Unmarshal(message, e)
if err != nil {
return nil, err
}
switch e.Type {
case EventTypeWelcome:
ev := &EventWelcome{}
err = json.Unmarshal(message, ev)
if err != nil {
return nil, err
}
return ev, nil
case EventTypeJoined:
ev := &EventJoined{}
err = json.Unmarshal(message, ev)
if err != nil {
return nil, err
}
return ev, nil
default:
return nil, fmt.Errorf("failed to decode event: unknown event type: %s", e.Type)
}
}