Add ten minute read/write timeouts
This commit is contained in:
parent
6acce4f905
commit
ce59fe7598
8 changed files with 220 additions and 61 deletions
|
@ -1,5 +1,7 @@
|
|||
package bgammon
|
||||
|
||||
type Client interface {
|
||||
Terminate(reason string) error
|
||||
Write(message []byte)
|
||||
Terminate(reason string)
|
||||
Terminated() bool
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package main
|
|||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"code.rocket9labs.com/tslocum/bgammon"
|
||||
)
|
||||
|
@ -16,7 +17,6 @@ type serverClient struct {
|
|||
lastActive int64
|
||||
lastPing int64
|
||||
commands <-chan []byte
|
||||
events chan<- []byte
|
||||
playerNumber int
|
||||
bgammon.Client
|
||||
}
|
||||
|
@ -25,42 +25,48 @@ func (c *serverClient) sendEvent(e interface{}) {
|
|||
if c.json {
|
||||
switch ev := e.(type) {
|
||||
case *bgammon.EventWelcome:
|
||||
ev.Type = "welcome"
|
||||
ev.Type = bgammon.EventTypeWelcome
|
||||
case *bgammon.EventPing:
|
||||
ev.Type = bgammon.EventTypePing
|
||||
case *bgammon.EventNotice:
|
||||
ev.Type = "notice"
|
||||
ev.Type = bgammon.EventTypeNotice
|
||||
case *bgammon.EventSay:
|
||||
ev.Type = "say"
|
||||
ev.Type = bgammon.EventTypeSay
|
||||
case *bgammon.EventList:
|
||||
ev.Type = "list"
|
||||
ev.Type = bgammon.EventTypeList
|
||||
case *bgammon.EventJoined:
|
||||
ev.Type = "joined"
|
||||
ev.Type = bgammon.EventTypeJoined
|
||||
case *bgammon.EventFailedJoin:
|
||||
ev.Type = "failedjoin"
|
||||
ev.Type = bgammon.EventTypeFailedJoin
|
||||
case *bgammon.EventBoard:
|
||||
ev.Type = "board"
|
||||
ev.Type = bgammon.EventTypeBoard
|
||||
case *bgammon.EventRolled:
|
||||
ev.Type = "rolled"
|
||||
ev.Type = bgammon.EventTypeRolled
|
||||
case *bgammon.EventMoved:
|
||||
ev.Type = "moved"
|
||||
ev.Type = bgammon.EventTypeMoved
|
||||
default:
|
||||
log.Panicf("unknown event type %+v", ev)
|
||||
}
|
||||
|
||||
buf, err := json.Marshal(e)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
c.events <- buf
|
||||
c.Write(buf)
|
||||
return
|
||||
}
|
||||
|
||||
switch ev := e.(type) {
|
||||
case *bgammon.EventWelcome:
|
||||
c.events <- []byte(fmt.Sprintf("welcome %s there are %d clients playing %d games.", ev.PlayerName, ev.Clients, ev.Games))
|
||||
c.Write([]byte(fmt.Sprintf("welcome %s there are %d clients playing %d games.", ev.PlayerName, ev.Clients, ev.Games)))
|
||||
case *bgammon.EventPing:
|
||||
c.Write([]byte(fmt.Sprintf("ping %s", ev.Message)))
|
||||
case *bgammon.EventNotice:
|
||||
c.events <- []byte(fmt.Sprintf("notice %s", ev.Message))
|
||||
c.Write([]byte(fmt.Sprintf("notice %s", ev.Message)))
|
||||
case *bgammon.EventSay:
|
||||
c.events <- []byte(fmt.Sprintf("say %s %s", ev.Player, ev.Message))
|
||||
c.Write([]byte(fmt.Sprintf("say %s %s", ev.Player, ev.Message)))
|
||||
case *bgammon.EventList:
|
||||
c.events <- []byte("liststart Games list:")
|
||||
c.Write([]byte("liststart Games list:"))
|
||||
for _, g := range ev.Games {
|
||||
password := 0
|
||||
if g.Password {
|
||||
|
@ -70,17 +76,19 @@ func (c *serverClient) sendEvent(e interface{}) {
|
|||
if g.Name != "" {
|
||||
name = g.Name
|
||||
}
|
||||
c.events <- []byte(fmt.Sprintf("game %d %d %d %s", g.ID, password, g.Players, name))
|
||||
c.Write([]byte(fmt.Sprintf("game %d %d %d %s", g.ID, password, g.Players, name)))
|
||||
}
|
||||
c.events <- []byte("listend End of games list.")
|
||||
c.Write([]byte("listend End of games list."))
|
||||
case *bgammon.EventJoined:
|
||||
c.events <- []byte(fmt.Sprintf("joined %d %s", ev.GameID, ev.Player))
|
||||
c.Write([]byte(fmt.Sprintf("joined %d %s", ev.GameID, ev.Player)))
|
||||
case *bgammon.EventFailedJoin:
|
||||
c.events <- []byte(fmt.Sprintf("failedjoin %s", ev.Reason))
|
||||
c.Write([]byte(fmt.Sprintf("failedjoin %s", ev.Reason)))
|
||||
case *bgammon.EventRolled:
|
||||
c.events <- []byte(fmt.Sprintf("rolled %s %d %d", ev.Player, ev.Roll1, ev.Roll2))
|
||||
c.Write([]byte(fmt.Sprintf("rolled %s %d %d", ev.Player, ev.Roll1, ev.Roll2)))
|
||||
case *bgammon.EventMoved:
|
||||
c.events <- []byte(fmt.Sprintf("moved %s %s", ev.Player, bgammon.FormatMoves(ev.Moves, c.playerNumber)))
|
||||
c.Write([]byte(fmt.Sprintf("moved %s %s", ev.Player, bgammon.FormatMoves(ev.Moves, c.playerNumber))))
|
||||
default:
|
||||
log.Panicf("unknown event type %+v", ev)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,8 @@ import (
|
|||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"code.rocket9labs.com/tslocum/bgammon"
|
||||
)
|
||||
|
@ -12,12 +14,14 @@ import (
|
|||
var _ bgammon.Client = &socketClient{}
|
||||
|
||||
type socketClient struct {
|
||||
conn net.Conn
|
||||
events <-chan []byte
|
||||
commands chan<- []byte
|
||||
conn net.Conn
|
||||
events chan []byte
|
||||
commands chan<- []byte
|
||||
terminated bool
|
||||
wgEvents sync.WaitGroup
|
||||
}
|
||||
|
||||
func newSocketClient(conn net.Conn, commands chan<- []byte, events <-chan []byte) *socketClient {
|
||||
func newSocketClient(conn net.Conn, commands chan<- []byte, events chan []byte) *socketClient {
|
||||
c := &socketClient{
|
||||
conn: conn,
|
||||
events: events,
|
||||
|
@ -28,29 +32,90 @@ func newSocketClient(conn net.Conn, commands chan<- []byte, events <-chan []byte
|
|||
return c
|
||||
}
|
||||
|
||||
func (c *socketClient) Write(message []byte) {
|
||||
if c.terminated {
|
||||
return
|
||||
}
|
||||
|
||||
c.wgEvents.Add(1)
|
||||
c.events <- message
|
||||
}
|
||||
|
||||
func (c *socketClient) readCommands() {
|
||||
setTimeout := func() {
|
||||
err := c.conn.SetReadDeadline(time.Now().Add(clientTimeout))
|
||||
if err != nil {
|
||||
c.Terminate(err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
setTimeout()
|
||||
var scanner = bufio.NewScanner(c.conn)
|
||||
for scanner.Scan() {
|
||||
if c.terminated {
|
||||
continue // TODO wait group
|
||||
}
|
||||
|
||||
if scanner.Err() != nil {
|
||||
c.Terminate(scanner.Err().Error())
|
||||
return
|
||||
}
|
||||
|
||||
buf := make([]byte, len(scanner.Bytes()))
|
||||
copy(buf, scanner.Bytes())
|
||||
c.commands <- buf
|
||||
|
||||
log.Printf("<- %s", scanner.Bytes())
|
||||
setTimeout()
|
||||
}
|
||||
}
|
||||
|
||||
func (c *socketClient) writeEvents() {
|
||||
setTimeout := func() {
|
||||
err := c.conn.SetWriteDeadline(time.Now().Add(clientTimeout))
|
||||
if err != nil {
|
||||
c.Terminate(err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var event []byte
|
||||
for event = range c.events {
|
||||
c.conn.Write(event)
|
||||
c.conn.Write([]byte("\n"))
|
||||
if c.terminated {
|
||||
c.wgEvents.Done()
|
||||
continue
|
||||
}
|
||||
setTimeout()
|
||||
|
||||
_, err := c.conn.Write(append(event, '\n'))
|
||||
if err != nil {
|
||||
c.Terminate(err.Error())
|
||||
c.wgEvents.Done()
|
||||
continue
|
||||
}
|
||||
|
||||
log.Printf("-> %s", event)
|
||||
c.wgEvents.Done()
|
||||
}
|
||||
}
|
||||
|
||||
func (c *socketClient) Terminate(reason string) error {
|
||||
func (c *socketClient) Terminate(reason string) {
|
||||
if c.terminated {
|
||||
return
|
||||
}
|
||||
c.terminated = true
|
||||
c.conn.Write([]byte(fmt.Sprintf("Connection closed: %s\n", reason)))
|
||||
c.conn.Close()
|
||||
return nil
|
||||
go func() {
|
||||
time.Sleep(5 * time.Second)
|
||||
c.wgEvents.Wait()
|
||||
close(c.events)
|
||||
close(c.commands)
|
||||
log.Println("FINISHED CLEANUP")
|
||||
}()
|
||||
}
|
||||
|
||||
func (c *socketClient) Terminated() bool {
|
||||
return c.terminated
|
||||
}
|
||||
|
|
|
@ -84,7 +84,7 @@ func (g *serverGame) sendBoard(client *serverClient) {
|
|||
|
||||
scanner := bufio.NewScanner(bytes.NewReader(g.BoardState(playerNumber)))
|
||||
for scanner.Scan() {
|
||||
client.events <- append([]byte("notice "), scanner.Bytes()...)
|
||||
client.Write(append([]byte("notice "), scanner.Bytes()...))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -172,7 +172,7 @@ func (g *serverGame) removeClient(client *serverClient) {
|
|||
if opponent == nil {
|
||||
return
|
||||
}
|
||||
opponent.events <- []byte(fmt.Sprintf("left %d %s %s", g.id, client.name, g.name))
|
||||
opponent.Write([]byte(fmt.Sprintf("left %d %s %s", g.id, client.name, g.name)))
|
||||
if !opponent.json {
|
||||
g.sendBoard(opponent)
|
||||
}
|
||||
|
|
|
@ -14,6 +14,8 @@ import (
|
|||
"code.rocket9labs.com/tslocum/bgammon"
|
||||
)
|
||||
|
||||
const clientTimeout = 10 * time.Minute
|
||||
|
||||
type serverCommand struct {
|
||||
client *serverClient
|
||||
command []byte
|
||||
|
@ -76,7 +78,6 @@ func (s *server) handleConnection(conn net.Conn) {
|
|||
connected: now,
|
||||
lastActive: now,
|
||||
commands: commands,
|
||||
events: events,
|
||||
Client: newSocketClient(conn, commands, events),
|
||||
}
|
||||
log.Println("socket client", c)
|
||||
|
@ -89,17 +90,25 @@ func (s *server) handleConnection(conn net.Conn) {
|
|||
|
||||
func (s *server) handlePingClient(c *serverClient) {
|
||||
// TODO only ping when there is no recent activity
|
||||
t := time.NewTicker(time.Minute * 2)
|
||||
t := time.NewTicker(time.Minute * 4)
|
||||
for {
|
||||
<-t.C
|
||||
|
||||
if c.Terminated() {
|
||||
t.Stop()
|
||||
return
|
||||
}
|
||||
|
||||
if len(c.name) == 0 {
|
||||
c.Terminate("User did not send login command within 2 minutes.")
|
||||
t.Stop()
|
||||
return
|
||||
}
|
||||
|
||||
c.lastPing = time.Now().Unix()
|
||||
c.events <- []byte(fmt.Sprintf("ping %d", c.lastPing))
|
||||
c.sendEvent(&bgammon.EventPing{
|
||||
Message: fmt.Sprintf("%d", c.lastPing),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -136,7 +145,7 @@ func (s *server) randomUsername() []byte {
|
|||
}
|
||||
|
||||
func (s *server) sendHello(c *serverClient) {
|
||||
c.events <- []byte("hello Welcome to bgammon.org! Please log in by sending the 'login' command. You may specify a username, otherwise you will be assigned a random username. If you specify a username, you may also specify a password. Have fun!")
|
||||
c.Write([]byte("hello Welcome to bgammon.org! Please log in by sending the 'login' command. You may specify a username, otherwise you will be assigned a random username. If you specify a username, you may also specify a password. Have fun!"))
|
||||
}
|
||||
|
||||
func (s *server) sendWelcome(c *serverClient) {
|
||||
|
@ -233,12 +242,13 @@ COMMANDS:
|
|||
switch keyword {
|
||||
case bgammon.CommandHelp, "h":
|
||||
// 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.")
|
||||
cmd.client.Write([]byte("helpstart Help text:"))
|
||||
cmd.client.Write([]byte("help Test help text"))
|
||||
cmd.client.Write([]byte("helpend End of help text."))
|
||||
// TODO JSON format
|
||||
case bgammon.CommandJSON:
|
||||
sendUsage := func() {
|
||||
cmd.client.events <- []byte("notice To enable JSON formatted messages, send 'json on'. To disable JSON formatted messages, send 'json off'.")
|
||||
cmd.client.Write([]byte("notice To enable JSON formatted messages, send 'json on'. To disable JSON formatted messages, send 'json off'."))
|
||||
}
|
||||
if len(params) != 1 {
|
||||
sendUsage()
|
||||
|
@ -248,10 +258,10 @@ COMMANDS:
|
|||
switch paramLower {
|
||||
case "on":
|
||||
cmd.client.json = true
|
||||
cmd.client.events <- []byte("json JSON formatted messages enabled.")
|
||||
cmd.client.Write([]byte("json JSON formatted messages enabled.")) // TODO send in JSON format
|
||||
case "off":
|
||||
cmd.client.json = false
|
||||
cmd.client.events <- []byte("json JSON formatted messages disabled.")
|
||||
cmd.client.Write([]byte("json JSON formatted messages disabled."))
|
||||
default:
|
||||
sendUsage()
|
||||
}
|
||||
|
@ -260,15 +270,15 @@ COMMANDS:
|
|||
continue
|
||||
}
|
||||
if clientGame == nil {
|
||||
cmd.client.events <- []byte("notice Message not sent. You are not currently in a game.")
|
||||
cmd.client.Write([]byte("notice Message not sent. You are not currently in a game."))
|
||||
continue
|
||||
}
|
||||
opponent := clientGame.opponent(cmd.client)
|
||||
if opponent == nil {
|
||||
cmd.client.events <- []byte("notice Message not sent. There is no one else in the game.")
|
||||
cmd.client.Write([]byte("notice Message not sent. There is no one else in the game."))
|
||||
continue
|
||||
}
|
||||
opponent.events <- []byte(fmt.Sprintf("say %s %s", cmd.client.name, bytes.Join(params, []byte(" "))))
|
||||
opponent.Write([]byte(fmt.Sprintf("say %s %s", cmd.client.name, bytes.Join(params, []byte(" ")))))
|
||||
case bgammon.CommandList, "ls":
|
||||
ev := &bgammon.EventList{}
|
||||
for _, g := range s.games {
|
||||
|
@ -282,7 +292,7 @@ COMMANDS:
|
|||
cmd.client.sendEvent(ev)
|
||||
case bgammon.CommandCreate, "c":
|
||||
sendUsage := func() {
|
||||
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.")
|
||||
cmd.client.Write([]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()
|
||||
|
@ -354,7 +364,7 @@ COMMANDS:
|
|||
}
|
||||
case bgammon.CommandLeave, "l":
|
||||
if clientGame == nil {
|
||||
cmd.client.events <- []byte("failedleave You are not currently in a game.")
|
||||
cmd.client.Write([]byte("failedleave You are not currently in a game."))
|
||||
continue
|
||||
}
|
||||
if clientGame.client1 == cmd.client {
|
||||
|
@ -366,19 +376,19 @@ COMMANDS:
|
|||
// TODO move to .removeClient
|
||||
|
||||
leftMessage := []byte(fmt.Sprintf("left %s", cmd.client.name))
|
||||
cmd.client.events <- leftMessage
|
||||
cmd.client.Write(leftMessage)
|
||||
opponent := clientGame.opponent(cmd.client)
|
||||
if opponent != nil {
|
||||
opponent.events <- leftMessage
|
||||
opponent.Write(leftMessage)
|
||||
}
|
||||
case bgammon.CommandRoll, "r":
|
||||
if clientGame == nil {
|
||||
cmd.client.events <- []byte("notice You are not currently in a game.")
|
||||
cmd.client.Write([]byte("notice You are not currently in a game."))
|
||||
continue
|
||||
}
|
||||
|
||||
if !clientGame.roll(clientNumber) {
|
||||
cmd.client.events <- []byte("notice It is not your turn to roll.")
|
||||
cmd.client.Write([]byte("notice It is not your turn to roll."))
|
||||
continue
|
||||
}
|
||||
ev := &bgammon.EventRolled{
|
||||
|
@ -404,17 +414,17 @@ COMMANDS:
|
|||
}
|
||||
case bgammon.CommandMove, "m", "mv":
|
||||
if clientGame == nil {
|
||||
cmd.client.events <- []byte("failedmove You are not currently in a game.")
|
||||
cmd.client.Write([]byte("failedmove You are not currently in a game."))
|
||||
continue
|
||||
}
|
||||
|
||||
if clientGame.Turn != clientNumber {
|
||||
cmd.client.events <- []byte("failedmove It is not your turn to move.")
|
||||
cmd.client.Write([]byte("failedmove It is not your turn to move."))
|
||||
continue
|
||||
}
|
||||
|
||||
sendUsage := func() {
|
||||
cmd.client.events <- []byte("failedmove Specify one or more moves in the form FROM/TO. For example: 8/4 6/4")
|
||||
cmd.client.Write([]byte("failedmove Specify one or more moves in the form FROM/TO. For example: 8/4 6/4"))
|
||||
}
|
||||
|
||||
if len(params) == 0 {
|
||||
|
@ -457,7 +467,7 @@ COMMANDS:
|
|||
}
|
||||
if !found {
|
||||
log.Printf("available legal moves: %s", bgammon.FormatMoves(legalMoves, clientNumber))
|
||||
cmd.client.events <- []byte(fmt.Sprintf("failedmove %d/%d Illegal move.", originalFrom, originalTo))
|
||||
cmd.client.Write([]byte(fmt.Sprintf("failedmove %d/%d Illegal move.", originalFrom, originalTo)))
|
||||
continue COMMANDS
|
||||
}
|
||||
|
||||
|
@ -479,7 +489,7 @@ COMMANDS:
|
|||
})
|
||||
case bgammon.CommandOk, "k":
|
||||
if clientGame == nil {
|
||||
cmd.client.events <- []byte("notice You are not currently in a game.")
|
||||
cmd.client.Write([]byte("notice You are not currently in a game."))
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -489,26 +499,28 @@ COMMANDS:
|
|||
if clientGame.client2 == cmd.client {
|
||||
playerNumber = 2
|
||||
}
|
||||
cmd.client.events <- []byte(fmt.Sprintf("failedok You still have the following legal moves available: %s", bgammon.FormatMoves(legalMoves, playerNumber)))
|
||||
cmd.client.Write([]byte(fmt.Sprintf("failedok You still have the following legal moves available: %s", bgammon.FormatMoves(legalMoves, playerNumber))))
|
||||
continue
|
||||
}
|
||||
|
||||
log.Println("legal to pass turn")
|
||||
case bgammon.CommandBoard, "b":
|
||||
if clientGame == nil {
|
||||
cmd.client.events <- []byte("notice You are not currently in a game.")
|
||||
cmd.client.Write([]byte("notice You are not currently in a game."))
|
||||
continue
|
||||
}
|
||||
|
||||
scanner := bufio.NewScanner(bytes.NewReader(clientGame.BoardState(clientNumber)))
|
||||
for scanner.Scan() {
|
||||
cmd.client.events <- append([]byte("notice "), scanner.Bytes()...)
|
||||
cmd.client.Write(append([]byte("notice "), scanner.Bytes()...))
|
||||
}
|
||||
case bgammon.CommandDisconnect:
|
||||
if clientGame != nil {
|
||||
clientGame.removeClient(cmd.client)
|
||||
}
|
||||
cmd.client.Terminate("Client disconnected")
|
||||
case bgammon.CommandPong:
|
||||
// Do nothing.
|
||||
default:
|
||||
log.Printf("unknown command %s", keyword)
|
||||
}
|
||||
|
|
13
command.go
13
command.go
|
@ -19,12 +19,21 @@ const (
|
|||
CommandReset = "reset" // Reset checker movement.
|
||||
CommandOk = "ok" // Confirm checker movement and pass turn to next player.
|
||||
CommandBoard = "board" // Print current board state in human-readable form.
|
||||
CommandPong = "pong" // Response to server ping.
|
||||
CommandDisconnect = "disconnect" // Disconnect from server.
|
||||
)
|
||||
|
||||
type EventType string
|
||||
|
||||
const (
|
||||
EventTypeWelcome = "welcome"
|
||||
EventTypeJoined = "joined"
|
||||
EventTypeWelcome = "welcome"
|
||||
EventTypePing = "ping"
|
||||
EventTypeNotice = "notice"
|
||||
EventTypeSay = "say"
|
||||
EventTypeList = "list"
|
||||
EventTypeJoined = "joined"
|
||||
EventTypeFailedJoin = "failedjoin"
|
||||
EventTypeBoard = "board"
|
||||
EventTypeRolled = "rolled"
|
||||
EventTypeMoved = "moved"
|
||||
)
|
||||
|
|
61
event.go
61
event.go
|
@ -19,6 +19,11 @@ type EventWelcome struct {
|
|||
Games int
|
||||
}
|
||||
|
||||
type EventPing struct {
|
||||
Event
|
||||
Message string
|
||||
}
|
||||
|
||||
type EventNotice struct {
|
||||
Event
|
||||
Message string
|
||||
|
@ -82,6 +87,34 @@ func DecodeEvent(message []byte) (interface{}, error) {
|
|||
return nil, err
|
||||
}
|
||||
return ev, nil
|
||||
case EventTypePing:
|
||||
ev := &EventPing{}
|
||||
err = json.Unmarshal(message, ev)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ev, nil
|
||||
case EventTypeNotice:
|
||||
ev := &EventNotice{}
|
||||
err = json.Unmarshal(message, ev)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ev, nil
|
||||
case EventTypeSay:
|
||||
ev := &EventSay{}
|
||||
err = json.Unmarshal(message, ev)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ev, nil
|
||||
case EventTypeList:
|
||||
ev := &EventList{}
|
||||
err = json.Unmarshal(message, ev)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ev, nil
|
||||
case EventTypeJoined:
|
||||
ev := &EventJoined{}
|
||||
err = json.Unmarshal(message, ev)
|
||||
|
@ -89,6 +122,34 @@ func DecodeEvent(message []byte) (interface{}, error) {
|
|||
return nil, err
|
||||
}
|
||||
return ev, nil
|
||||
case EventTypeFailedJoin:
|
||||
ev := &EventFailedJoin{}
|
||||
err = json.Unmarshal(message, ev)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ev, nil
|
||||
case EventTypeBoard:
|
||||
ev := &EventBoard{}
|
||||
err = json.Unmarshal(message, ev)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ev, nil
|
||||
case EventTypeRolled:
|
||||
ev := &EventRolled{}
|
||||
err = json.Unmarshal(message, ev)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ev, nil
|
||||
case EventTypeMoved:
|
||||
ev := &EventMoved{}
|
||||
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)
|
||||
}
|
||||
|
|
2
game.go
2
game.go
|
@ -33,11 +33,13 @@ func NewGame() *Game {
|
|||
|
||||
func (g *Game) Copy() *Game {
|
||||
newGame := &Game{
|
||||
Board: make([]int, len(g.Board)),
|
||||
Player1: g.Player1,
|
||||
Player2: g.Player2,
|
||||
Turn: g.Turn,
|
||||
Roll1: g.Roll1,
|
||||
Roll2: g.Roll2,
|
||||
Moves: make([][]int, len(g.Moves)),
|
||||
}
|
||||
copy(newGame.Board, g.Board)
|
||||
copy(newGame.Moves, g.Moves)
|
||||
|
|
Loading…
Reference in a new issue