Documentation ¶
Index ¶
- Constants
- func AuthenticateHandler(constructor func(ids, secrets []string) (websocket.Handler, error)) (websocket.Handler, error)
- func FindIdsAndSecrets() ([]string, []string, error)
- func GameHandler(exitChan chan bool, connMan ConnectionManager, clientMan ClientManager, ...) websocket.Handler
- func Listen(c GameClient)
- func RunAuthenticatedServer(constructor func(ids, secrets []string) (websocket.Handler, error))
- func SetupFlags()
- type AuthenticatedClientManager
- type ClientError
- type ClientManager
- type ClientMessage
- type ConnectionManager
- type GameClient
- type GameRecorder
- type GameState
- type ServerMessage
- type SimpleConnectionManager
- type SimpleGameRecorder
- type StateManager
- type SynchronizedGameClient
- func (c *SynchronizedGameClient) Conn() *websocket.Conn
- func (c *SynchronizedGameClient) Error() chan ClientError
- func (c *SynchronizedGameClient) Id() string
- func (c *SynchronizedGameClient) Receive() chan ClientMessage
- func (c *SynchronizedGameClient) Send() chan ServerMessage
- func (c *SynchronizedGameClient) Watchdog() *Watchdog
- type SynchronizedStateManager
- type Watchdog
Constants ¶
const ( ResultWin = 1 ResultTie = 0 ResultLoss = -1 )
const ConnTimeout = 10 * time.Second
const MoveTimeout = 10 * time.Second
Variables ¶
This section is empty.
Functions ¶
func AuthenticateHandler ¶
func AuthenticateHandler( constructor func(ids, secrets []string) (websocket.Handler, error), ) (websocket.Handler, error)
Given a constructor that creates a websocket handler, wrap it with FindIdsAndSecrets() to authenticate from the command line or environment variables.
func FindIdsAndSecrets ¶
Searches first for command line arguments "ids" and "secrets", and then checks environment variables. Returns an error if they were not found or they were not the same length.
func GameHandler ¶
func GameHandler( exitChan chan bool, connMan ConnectionManager, clientMan ClientManager, stateMan StateManager, record GameRecorder, ) websocket.Handler
Start the game components and return a websocket handler that can be used to start or mock and HTTP server and receive requests. Must be given a connection manager, client manager, state manager, and game recorder. The first channel parameter will be passed a value when the game is over, that can be used to stop the HTTP server.
func Listen ¶
func Listen(c GameClient)
Listen for messages send from and received by this client in separate non-blocking goroutines.
func RunAuthenticatedServer ¶
Setup a server to listen to clients. To start the server you must provide a list of ids and secrets. When a client connects with a valid secret, it will be automatically assigned the corresponding id. To give a list like this via the command line, call go run main.go --ids "1 2" --secrets "s1 s2" Otherwise, in a Docker sandbox you can set the environment variables BOTBOX_IDS and BOTBOX_SECRETS as space-separated lists of ids and secrets. The secrets are necessary to prevent malicious scripts from trying to connect as two separate agents. Pass in a constructor function that will build the GameHandler from a list of client ids and secrets to expect.
func SetupFlags ¶
func SetupFlags()
Types ¶
type AuthenticatedClientManager ¶
type AuthenticatedClientManager struct {
// contains filtered or unexported fields
}
An authenticated client manager will require secret keys passed in for each client id. If a client does not pass in a valid key, or passes in a duplicate key, then it will be rejected.
func NewAuthenticatedClientManager ¶
func NewAuthenticatedClientManager( constructor func(id string, conn *websocket.Conn) GameClient, clientIds []string, clientSecrets []string, timeout time.Duration, ) *AuthenticatedClientManager
Create a new simple client manager. Give it a constructor to create clients from connections and also a list of client ids and secrets to expect.
func (*AuthenticatedClientManager) Clients ¶
func (m *AuthenticatedClientManager) Clients() []GameClient
func (*AuthenticatedClientManager) Ready ¶
func (m *AuthenticatedClientManager) Ready() bool
type ClientError ¶
type ClientError struct {
// contains filtered or unexported fields
}
This is an error that is associated with a client so that we can adequately punish clients who do not have good behavior.
func (ClientError) Error ¶
func (e ClientError) Error() string
type ClientManager ¶
type ClientManager interface { // Listen to a channel that receives websocket connections and authenticate // connections. If they are valid, allow them to remain connected other wise // close them immediately. Return a waitgroup that finished when all clients // are connected or a timeout occurs. Register(chan *websocket.Conn) *sync.WaitGroup // Return whether or not the client manager has all of the expected clients // connected. Ready() bool // Get a list of the connected clients. Clients() []GameClient }
This component handles authenticating and managing clients.
type ClientMessage ¶
type ClientMessage struct {
Action string `json:"action"`
}
type ConnectionManager ¶
type ConnectionManager interface { // Create a websocket handler that will send connections along the given // channel. This channel can be given to a listener which will do something // with the connections. E.g., a client manager. The last channel is // the exit channel, which should terminate the handler when it recieves a // value. Handler(chan *websocket.Conn, chan error) websocket.Handler // Close all of the active connections. Close() }
type GameClient ¶
type GameClient interface { // Get a unique identifier for this client. Id() string // Get a websocket connection for this client Conn() *websocket.Conn // Return a watchdog for this client with a timeout on how long it can take // to make a move. Keeps clients from blocking forever. Watchdog() *Watchdog // Send and receive channels for communicating with the client Send() chan ServerMessage Receive() chan ClientMessage Error() chan ClientError }
type GameRecorder ¶
type GameRecorder interface { LogState(GameState) error LogResult(GameState) error LogConnection(GameClient) error LogDisconnection(GameClient) error Close() error }
type GameState ¶
type GameState interface { // Return a JSON-serializable representation of actions that the player p can // make in the current state. Actions(p int) interface{} // Commit the given action a (in JSON) for player p. Do(p int, a string) // Get a JSON-serializable state of the game, as seen by player p View(p int) interface{} // Check if the game is over yet or not. Finished() bool // Decide the result of the match. Each player gets a score +1, 0, -1 Result() []int }
type ServerMessage ¶
type ServerMessage struct { Player int `json:"player"` Actions interface{} `json:"actions"` State interface{} `json:"state"` }
type SimpleConnectionManager ¶
type SimpleConnectionManager struct { Connections []*websocket.Conn ExitChans map[*websocket.Conn]chan bool }
A simple connection manager that forwards all connections along the connection channel in its handler.
func NewSimpleConnectionManager ¶
func NewSimpleConnectionManager() *SimpleConnectionManager
func (*SimpleConnectionManager) Close ¶
func (m *SimpleConnectionManager) Close()
type SimpleGameRecorder ¶
type SimpleGameRecorder struct { StateLog *os.File ResultLog *os.File ConnectLog *os.File DisconnectLog *os.File }
The game recorder records the game state every time it changes and whether a client connects as expected or disconnects unexpectedly. This allows the sandbox service to adequately punish clients which are not well-behaved, and send game results to the scoreboard service.
func NewSimpleGameRecorder ¶
func NewSimpleGameRecorder(dir string) (*SimpleGameRecorder, error)
func (*SimpleGameRecorder) Close ¶
func (r *SimpleGameRecorder) Close() error
func (*SimpleGameRecorder) LogConnection ¶
func (r *SimpleGameRecorder) LogConnection(c GameClient) error
func (*SimpleGameRecorder) LogDisconnection ¶
func (r *SimpleGameRecorder) LogDisconnection(c GameClient) error
func (*SimpleGameRecorder) LogResult ¶
func (r *SimpleGameRecorder) LogResult(s GameState) error
func (*SimpleGameRecorder) LogState ¶
func (r *SimpleGameRecorder) LogState(s GameState) error
type StateManager ¶
type StateManager interface { // Given a list of game clients, spawn a goroutine and play the game by // sending/receiving messages according to how the game should progress. // Return a channel which will receive the game state every time it changes. // Return a waitgroup that finished when the game is over or a timeout // occurs. Play([]GameClient, chan GameState, chan error) *sync.WaitGroup }
A manager that handles how actions should be received from clients. I.e., real-time, synchronous, turn-based, etc.
type SynchronizedGameClient ¶
type SynchronizedGameClient struct {
// contains filtered or unexported fields
}
func (*SynchronizedGameClient) Conn ¶
func (c *SynchronizedGameClient) Conn() *websocket.Conn
func (*SynchronizedGameClient) Error ¶
func (c *SynchronizedGameClient) Error() chan ClientError
func (*SynchronizedGameClient) Id ¶
func (c *SynchronizedGameClient) Id() string
func (*SynchronizedGameClient) Receive ¶
func (c *SynchronizedGameClient) Receive() chan ClientMessage
func (*SynchronizedGameClient) Send ¶
func (c *SynchronizedGameClient) Send() chan ServerMessage
func (*SynchronizedGameClient) Watchdog ¶
func (c *SynchronizedGameClient) Watchdog() *Watchdog
type SynchronizedStateManager ¶
type SynchronizedStateManager struct {
// contains filtered or unexported fields
}
func NewSynchronizedStateManager ¶
func NewSynchronizedStateManager( game GameState, timeout time.Duration, ) *SynchronizedStateManager
func (*SynchronizedStateManager) NewClient ¶
func (m *SynchronizedStateManager) NewClient( id string, conn *websocket.Conn, ) GameClient
func (*SynchronizedStateManager) Play ¶
func (m *SynchronizedStateManager) Play( clients []GameClient, stateChan chan GameState, errChan chan error, ) *sync.WaitGroup
Synchronizes gameplay so both players make moves at the same time. If a player does not make a move in the allotted timeframe, then it its turn is skipped and a timeout error is sent along the error channel. Game states may punish a client by doing something if the action received is the empty string.
type Watchdog ¶
type Watchdog struct {
// contains filtered or unexported fields
}
A watchdog is a simple tool that will return an error if it is not reset by the time the timeout is up.