game

package
v0.0.0-...-9131a41 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 3, 2017 License: MIT Imports: 12 Imported by: 0

Documentation

Index

Constants

View Source
const (
	ResultWin  = 1
	ResultTie  = 0
	ResultLoss = -1
)
View Source
const ConnTimeout = 10 * time.Second
View Source
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

func FindIdsAndSecrets() ([]string, []string, error)

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

func RunAuthenticatedServer(
	constructor func(ids, secrets []string) (websocket.Handler, error),
)

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

func (*AuthenticatedClientManager) Register

func (m *AuthenticatedClientManager) Register(
	connChan chan *websocket.Conn,
) *sync.WaitGroup

func (*AuthenticatedClientManager) Validate

func (m *AuthenticatedClientManager) Validate(
	conn *websocket.Conn,
) (string, error)

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()

func (*SimpleConnectionManager) Handler

func (m *SimpleConnectionManager) Handler(
	connChan chan *websocket.Conn,
	errChan chan error,
) websocket.Handler

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 (*SynchronizedGameClient) Error

func (c *SynchronizedGameClient) Error() chan ClientError

func (*SynchronizedGameClient) Id

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.

func NewWatchdog

func NewWatchdog(timeout time.Duration) *Watchdog

func (*Watchdog) Stop

func (w *Watchdog) Stop()

Stop the watchdog from sending an error when the timeout is reached.

func (*Watchdog) Watch

func (w *Watchdog) Watch() chan bool

Start the watchdog on a separate goroutine. Will call Done() on the given waitgroup when it times out unless it is stopped before the timer is done.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL