Add protocol specification
This commit is contained in:
parent
b0ffa930b7
commit
cfbdd632a9
5 changed files with 174 additions and 11 deletions
82
PROTOCOL.md
Normal file
82
PROTOCOL.md
Normal file
|
@ -0,0 +1,82 @@
|
|||
# Secification of bgammon protocol
|
||||
|
||||
## User commands
|
||||
|
||||
Format: `command <required argument> [optional argument]`
|
||||
|
||||
### `login [username] [password]`
|
||||
|
||||
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.
|
||||
|
||||
### `help [command]`
|
||||
|
||||
Request help for all commands, or optionally a specific command.
|
||||
|
||||
### `list`
|
||||
|
||||
List all games.
|
||||
|
||||
### `create [public/private] [password]`
|
||||
|
||||
List all games.
|
||||
|
||||
### `join [ID] [password]`
|
||||
|
||||
Join game.
|
||||
|
||||
### `roll`
|
||||
|
||||
Roll dice.
|
||||
|
||||
### `move <from-to> [from-to]...`
|
||||
|
||||
Move checkers.
|
||||
|
||||
### `reset`
|
||||
|
||||
Reset pending checker movement.
|
||||
|
||||
### `ok`
|
||||
|
||||
Confirm checker movement and pass turn to next player.
|
||||
|
||||
### `disconnect`
|
||||
|
||||
Disconnect from the server.
|
||||
|
||||
## Server responses
|
||||
|
||||
Data types:
|
||||
|
||||
- `integer` a whole number
|
||||
- `boolean` - `0` (representing false) or `1` (representing true)
|
||||
- `text` - alphanumeric without spaces
|
||||
- `line` - alphanumeric with spaces
|
||||
|
||||
### `hello <message:line>`
|
||||
|
||||
Initial welcome message sent by the server. It provides instructions on how to log in.
|
||||
|
||||
This message does not normally need to be displayed when using a graphical client.
|
||||
|
||||
### `welcome <name:text> there are <clients:integer> clients playing <games:integer> games.`
|
||||
|
||||
Initial message sent by the server.
|
||||
|
||||
### `notice <message:line>`
|
||||
|
||||
Server message. This should always be displayed to the user.
|
||||
|
||||
### `liststart Games list:`
|
||||
|
||||
Start of games list.
|
||||
|
||||
### `game <ID:integer> <password:boolean> <players:integer> <name:line>`
|
||||
|
||||
Game description.
|
||||
|
||||
### `listend End of games list.`
|
||||
|
||||
End of games list.
|
|
@ -6,6 +6,7 @@ import (
|
|||
"log"
|
||||
"math/rand"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
@ -124,8 +125,18 @@ func (s *server) sendWelcome(c *serverClient) {
|
|||
c.events <- []byte(fmt.Sprintf("welcome %s there are %d clients playing %d games.", c.name, len(s.clients), len(s.games)))
|
||||
}
|
||||
|
||||
func (s *server) gameByClient(c *serverClient) *serverGame {
|
||||
for _, g := range s.games {
|
||||
if g.client1 == c || g.client2 == c {
|
||||
return g
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *server) handleCommands() {
|
||||
var cmd serverCommand
|
||||
COMMANDS:
|
||||
for cmd = range s.commands {
|
||||
if cmd.client == nil {
|
||||
log.Panicf("nil client with command %s", cmd.command)
|
||||
|
@ -186,6 +197,11 @@ func (s *server) handleCommands() {
|
|||
}
|
||||
|
||||
switch keyword {
|
||||
case bgammon.CommandHelp:
|
||||
// TODO get extended help by specifying a command after help
|
||||
cmd.client.events <- []byte("helpstart Help text:")
|
||||
cmd.client.events <- []byte("help Test help text")
|
||||
cmd.client.events <- []byte("helpend End of help text.")
|
||||
case bgammon.CommandSay:
|
||||
if len(params) == 0 {
|
||||
continue
|
||||
|
@ -196,13 +212,12 @@ func (s *server) handleCommands() {
|
|||
password := 0
|
||||
name := "game name"
|
||||
for _, g := range s.games {
|
||||
cmd.client.events <- []byte(fmt.Sprintf("game %d %d %d %s", g.id, players, password, name))
|
||||
cmd.client.events <- []byte(fmt.Sprintf("game %d %d %d %s", g.id, password, players, name))
|
||||
}
|
||||
cmd.client.events <- []byte("listend End of games list.")
|
||||
case bgammon.CommandCreate:
|
||||
sendUsage := func() {
|
||||
cmd.client.events <- []byte("notice To create a public game specify whether it is public or private.")
|
||||
cmd.client.events <- []byte("notice When creating a private game, a password must be set.")
|
||||
cmd.client.events <- []byte("notice To create a public game specify whether it is public or private. When creating a private game, a password must also be provided.")
|
||||
}
|
||||
if len(params) == 0 {
|
||||
sendUsage()
|
||||
|
@ -232,10 +247,63 @@ func (s *server) handleCommands() {
|
|||
g.name = gameName
|
||||
g.password = gamePassword
|
||||
|
||||
if rand.Intn(2) == 0 { // Start as black.
|
||||
g.client1 = cmd.client
|
||||
g.Player1.Name = string(cmd.client.name)
|
||||
} else { // Start as white.
|
||||
g.client2 = cmd.client
|
||||
g.Player2.Name = string(cmd.client.name)
|
||||
}
|
||||
|
||||
s.games = append(s.games, g)
|
||||
|
||||
players := 1
|
||||
password := 0
|
||||
if len(gamePassword) != 0 {
|
||||
password = 1
|
||||
}
|
||||
cmd.client.events <- []byte(fmt.Sprintf("joined %d %d %d %s", g.id, password, players, gameName))
|
||||
case bgammon.CommandJoin:
|
||||
if s.gameByClient(cmd.client) != nil {
|
||||
cmd.client.events <- []byte("failedjoin Please leave the game you are in before joining another game.")
|
||||
continue
|
||||
}
|
||||
|
||||
sendUsage := func() {
|
||||
cmd.client.events <- []byte("notice To join a public game specify its game ID. To join a private game, a password must also be specified.")
|
||||
}
|
||||
|
||||
if len(params) == 0 {
|
||||
sendUsage()
|
||||
continue
|
||||
}
|
||||
gameID, err := strconv.Atoi(string(params[1]))
|
||||
if err != nil || gameID < 1 {
|
||||
sendUsage()
|
||||
continue
|
||||
}
|
||||
|
||||
for _, g := range s.games {
|
||||
if g.id == gameID {
|
||||
if len(g.password) != 0 && (len(params) < 2 || !bytes.Equal(g.password, bytes.Join(params[2:], []byte(" ")))) {
|
||||
cmd.client.events <- []byte("failedjoin Invalid password.")
|
||||
continue COMMANDS
|
||||
}
|
||||
|
||||
log.Printf("join existing game %+v", g)
|
||||
// cmd.client.events <- []byte(fmt.Sprintf("joined %d %d %d %s", g.id, players, password, gameName))
|
||||
continue COMMANDS
|
||||
}
|
||||
}
|
||||
case bgammon.CommandLeave:
|
||||
case bgammon.CommandRoll:
|
||||
case bgammon.CommandMove:
|
||||
case bgammon.CommandDisconnect:
|
||||
g := s.gameByClient(cmd.client)
|
||||
if g != nil {
|
||||
// todo leave game
|
||||
}
|
||||
cmd.client.Terminate("Client disconnected")
|
||||
default:
|
||||
log.Printf("unknown command %s", keyword)
|
||||
}
|
||||
|
|
19
command.go
19
command.go
|
@ -5,11 +5,16 @@ package bgammon
|
|||
type Command string
|
||||
|
||||
const (
|
||||
CommandLogin = "login" // Log in with username and password, or as a guest.
|
||||
CommandSay = "say" // Send chat message.
|
||||
CommandList = "list" // List available games.
|
||||
CommandCreate = "create" // Create game.
|
||||
CommandJoin = "join" // Join game.
|
||||
CommandRoll = "roll" // Roll dice.
|
||||
CommandMove = "move" // Move checkers.
|
||||
CommandLogin = "login" // Log in with username and password, or as a guest.
|
||||
CommandHelp = "help" // Print help information.
|
||||
CommandSay = "say" // Send chat message.
|
||||
CommandList = "list" // List available games.
|
||||
CommandCreate = "create" // Create game.
|
||||
CommandJoin = "join" // Join game.
|
||||
CommandLeave = "leave" // Leave game.
|
||||
CommandRoll = "roll" // Roll dice.
|
||||
CommandMove = "move" // Move checkers.
|
||||
CommandReset = "reset" // Reset checker movement.
|
||||
CommandOk = "ok" // Confirm checker movement and pass turn to next player.
|
||||
CommandDisconnect = "disconnect" // Disconnect from server.
|
||||
)
|
||||
|
|
4
game.go
4
game.go
|
@ -13,7 +13,9 @@ type Game struct {
|
|||
|
||||
func NewGame() *Game {
|
||||
return &Game{
|
||||
Board: NewBoard(),
|
||||
Board: NewBoard(),
|
||||
Player1: NewPlayer(1),
|
||||
Player2: NewPlayer(2),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,3 +4,9 @@ type Player struct {
|
|||
Number int // 1 black, 2 white
|
||||
Name string
|
||||
}
|
||||
|
||||
func NewPlayer(number int) *Player {
|
||||
return &Player{
|
||||
Number: number,
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue