hexzmem

package
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Dec 22, 2024 License: MIT Imports: 16 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrGameNotExist = fmt.Errorf("game does not exist in store")
)

Functions

func GenerateGameID

func GenerateGameID() string

Generates a game ID consisting of 6 uppercase ASCII letters.

func GeneratePubsubID

func GeneratePubsubID() string

Types

type AlwaysAcceptTokenStore

type AlwaysAcceptTokenStore struct{}

AlwaysAcceptTokenStore is an implementation of TokenStore that accepts everything.

func (AlwaysAcceptTokenStore) ConsumeCSRFToken

func (s AlwaysAcceptTokenStore) ConsumeCSRFToken(ctx context.Context, playerId api.PlayerId, token string) (bool, error)

func (AlwaysAcceptTokenStore) NewCSRFToken

func (s AlwaysAcceptTokenStore) NewCSRFToken(ctx context.Context, playerId api.PlayerId, ttl time.Duration) (string, error)

func (AlwaysAcceptTokenStore) TokenBucketGet

func (s AlwaysAcceptTokenStore) TokenBucketGet(ctx context.Context, bucket string, tokens int, refillRate float64, capacity int) (bool, error)

type Clock

type Clock interface {
	Now() time.Time
}

type FlashMessage

type FlashMessage struct {
	Kind    string    `json:"kind"`
	Message string    `json:"message"`
	Created time.Time `json:"created"`
}

type FlashStore

type FlashStore interface {
	// AddMessage adds a flash message for the given flashID.
	// Given the short-lived nature of flash messages, implementations should
	// ensure that messages associated with a flashID have a short TTL
	// (1 minute should be enough).
	AddMessage(ctx context.Context, flashID string, msg FlashMessage) error
	// PopMessages retrieves all flash messages for the flashID and removes them from the store.
	PopMessages(ctx context.Context, flashID string) ([]FlashMessage, error)
}

type GameInfo

type GameInfo struct {
	Id       string
	Host     string
	Started  time.Time
	GameType api.GameType
}

GameInfo contains essential summary info about a game. It is stored in serialized form for open and active games.

type GameStore

type GameStore interface {
	// StoreNewGame stores a new game under a randomly chosen new game ID.
	// The game ID will be returned, and also stored in state.GameInfo.Id.
	// If that field is already set in the input, an error is returned
	// and the game is not stored.
	StoreNewGame(ctx context.Context, state *pb.GameState) error
	// LookupGame looks up the current game state for the given gameId.
	LookupGame(ctx context.Context, gameId string) (*pb.GameState, error)
	// UpdateGame updates the game state for game ID s.GameInfo.Id.
	// Existing states are overwritten unconditionally.
	UpdateGame(ctx context.Context, state *pb.GameState) error
	// ListOpenGames lists games that are waiting for more players to join.
	ListOpenGames(ctx context.Context, limit int) ([]*GameInfo, error)
	// ListActiveGames lists the limit most recently played games.
	ListActiveGames(ctx context.Context, limit int) ([]*GameInfo, error)

	// Publish publishes the given game event on the GameStore's pubsub topic.
	Publish(ctx context.Context, pubsubId string, event *pb.GameStorePubsubEvent) error
	// Subscribe subscribes to game events for the given game ID. Events will be
	// sent to the returned channel. This method internaly spawns a goroutine
	// and returns immediately. The goroutine will close the returned channel
	// and terminate when the provided context ctx is cancelled.
	Subscribe(ctx context.Context, pubsubId string) <-chan *pb.GameStorePubsubEvent
}

GameStore is an interface for local or remote game stores, e.g. Redis.

type InMemFlashStore

type InMemFlashStore struct {
	// contains filtered or unexported fields
}

InMemFlashStore is an in-memory implementation of a FlashStore. Its methods can safely be called by multiple goroutines.

func NewInMemFlashStore

func NewInMemFlashStore() *InMemFlashStore

func (*InMemFlashStore) AddMessage

func (s *InMemFlashStore) AddMessage(ctx context.Context, flashID string, msg FlashMessage) error

func (*InMemFlashStore) PopMessages

func (s *InMemFlashStore) PopMessages(ctx context.Context, flashID string) ([]FlashMessage, error)

type InMemoryGameStore

type InMemoryGameStore struct {
	// contains filtered or unexported fields
}

An in-memory replacement for a RedisClient, to enable purely local, single-server gameplay.

func NewInMemoryGameStore

func NewInMemoryGameStore(gameTTL time.Duration) *InMemoryGameStore

func (*InMemoryGameStore) ListActiveGames

func (s *InMemoryGameStore) ListActiveGames(ctx context.Context, limit int) ([]*GameInfo, error)

func (*InMemoryGameStore) ListOpenGames

func (s *InMemoryGameStore) ListOpenGames(ctx context.Context, limit int) ([]*GameInfo, error)

func (*InMemoryGameStore) LookupGame

func (s *InMemoryGameStore) LookupGame(ctx context.Context, gameId string) (*pb.GameState, error)

func (*InMemoryGameStore) Publish

func (s *InMemoryGameStore) Publish(ctx context.Context, pubsubId string, event *pb.GameStorePubsubEvent) error

func (*InMemoryGameStore) StoreNewGame

func (s *InMemoryGameStore) StoreNewGame(ctx context.Context, state *pb.GameState) error

func (*InMemoryGameStore) Subscribe

func (s *InMemoryGameStore) Subscribe(ctx context.Context, pubsubId string) <-chan *pb.GameStorePubsubEvent

func (*InMemoryGameStore) UpdateGame

func (s *InMemoryGameStore) UpdateGame(ctx context.Context, state *pb.GameState) error

type InMemoryPlayerStore

type InMemoryPlayerStore struct {
	// contains filtered or unexported fields
}

func NewInMemoryPlayerStore

func NewInMemoryPlayerStore(loginTTL time.Duration) (*InMemoryPlayerStore, error)

Creates a new in-memory player store and loads the player DB from the given file. If dbPath is empty, no persistent storage is used.

func (*InMemoryPlayerStore) Login

func (s *InMemoryPlayerStore) Login(ctx context.Context, player Player) error

func (*InMemoryPlayerStore) Logout

func (s *InMemoryPlayerStore) Logout(ctx context.Context, playerId api.PlayerId) error

func (*InMemoryPlayerStore) Lookup

func (s *InMemoryPlayerStore) Lookup(ctx context.Context, playerId api.PlayerId) (Player, error)

func (*InMemoryPlayerStore) NumPlayers

func (s *InMemoryPlayerStore) NumPlayers() int

type Player

type Player struct {
	// A randomly generated unique ID.
	// This ID may be used in cookies.
	Id         api.PlayerId `json:"id"`
	Name       string       `json:"name"`
	LastActive time.Time    `json:"lastActive"`
	// Only set for registered users. For guests, this field is empty.
	UserID string `json:"userId"`
}

Player contains the data that is stored in the PlayerStore for a given player. It has JSON annotations for serialization (to disk or in memory storage).

type PlayerStore

type PlayerStore interface {
	// Lookup looks up the given player by ID.
	Lookup(ctx context.Context, playerId api.PlayerId) (Player, error)
	// Login logs in the given player. If the player is already logged in,
	// the existing data will be overwritten with the new data.
	// LastActive will be updated internally and does not need to be set by the caller.
	Login(ctx context.Context, player Player) error
	// Deletes the player from the store.
	Logout(ctx context.Context, playerId api.PlayerId) error
}

type RealClock

type RealClock struct{}

func (*RealClock) Now

func (c *RealClock) Now() time.Time

type RedisClient

type RedisClient struct {
	// contains filtered or unexported fields
}

func NewRedisClient

func NewRedisClient(config *RedisClientConfig) (*RedisClient, error)

func (*RedisClient) AddMessage

func (c *RedisClient) AddMessage(ctx context.Context, flashID string, msg FlashMessage) error

func (*RedisClient) ConsumeCSRFToken

func (c *RedisClient) ConsumeCSRFToken(ctx context.Context, playerId api.PlayerId, token string) (bool, error)

func (*RedisClient) DeleteGame

func (c *RedisClient) DeleteGame(ctx context.Context, gameId string) error

func (*RedisClient) ListActiveGames

func (c *RedisClient) ListActiveGames(ctx context.Context, limit int) ([]*GameInfo, error)

func (*RedisClient) ListOpenGames

func (c *RedisClient) ListOpenGames(ctx context.Context, limit int) ([]*GameInfo, error)

func (*RedisClient) LoginPlayer

func (c *RedisClient) LoginPlayer(ctx context.Context, player Player) error

func (*RedisClient) LogoutPlayer

func (c *RedisClient) LogoutPlayer(ctx context.Context, playerId api.PlayerId) error

func (*RedisClient) LookupGame

func (c *RedisClient) LookupGame(ctx context.Context, gameId string) (*pb.GameState, error)

func (*RedisClient) LookupPlayer

func (c *RedisClient) LookupPlayer(ctx context.Context, playerId api.PlayerId) (Player, error)

func (*RedisClient) NewCSRFToken

func (c *RedisClient) NewCSRFToken(ctx context.Context, playerId api.PlayerId, ttl time.Duration) (string, error)

func (*RedisClient) Ping

func (c *RedisClient) Ping() error

func (*RedisClient) PopMessages

func (c *RedisClient) PopMessages(ctx context.Context, flashID string) ([]FlashMessage, error)

func (*RedisClient) Publish

func (c *RedisClient) Publish(ctx context.Context, pubsubId string, event *pb.GameStorePubsubEvent) error

Sends a message to the channel for the given game. Returns the number of subscribers that received the message.

func (*RedisClient) StoreNewGame

func (c *RedisClient) StoreNewGame(ctx context.Context, state *pb.GameState) error

Stores the given game state in Redis. Game ID and pubsub ID are taken from the game state's GameInfo. If any of them is not set, it is assigned an appropriate random value. This method also updates the state's Modified field.

func (*RedisClient) Subscribe

func (c *RedisClient) Subscribe(ctx context.Context, pubsubId string) <-chan *pb.GameStorePubsubEvent

func (*RedisClient) TokenBucketGet

func (c *RedisClient) TokenBucketGet(ctx context.Context, bucket string, tokens int, refillRate float64, capacity int) (bool, error)

func (*RedisClient) UpdateGame

func (c *RedisClient) UpdateGame(ctx context.Context, s *pb.GameState) error

Stores the given game state in Redis, overwriting any existing game with the same ID. This method updates the Modified field of the game state.

type RedisClientConfig

type RedisClientConfig struct {
	Addr     string
	LoginTTL time.Duration
	GameTTL  time.Duration
	DB       int // Production should always use 0, 1 is for testing.
}

type RemotePlayerStore

type RemotePlayerStore struct {
	*RedisClient
}

RemotePlayerStore is an interface adapter that lets RedisClient implement PlayerStore.

func (*RemotePlayerStore) Login

func (s *RemotePlayerStore) Login(ctx context.Context, player Player) error

func (*RemotePlayerStore) Logout

func (s *RemotePlayerStore) Logout(ctx context.Context, playerId api.PlayerId) error

func (*RemotePlayerStore) Lookup

func (s *RemotePlayerStore) Lookup(ctx context.Context, playerId api.PlayerId) (Player, error)

type TokenStore

type TokenStore interface {
	// NewCSRFToken creates and returns a new token with the specified TTL.
	NewCSRFToken(ctx context.Context, playerId api.PlayerId, ttl time.Duration) (string, error)
	// ConsumeCSRFToken checks if the given CSRF token is known, and consumes it.
	ConsumeCSRFToken(ctx context.Context, playerId api.PlayerId, token string) (bool, error)

	// TokenBucketGet atomically gets the requested number of tokens from bucket.
	// Before removing any tokens, it refills the bucket according to the
	// given refillRate (in tokens per second) and the most recent refill time.
	// On the first call (ever or after the max TTL for any bucket), the bucket
	// is assumed to contain capacity tokens. The bucket will never contain
	// more than capacity tokens.
	TokenBucketGet(ctx context.Context, bucket string, tokens int, refillRate float64, capacity int) (bool, error)
}

TokenStore is the interface for a store of tokens. Tokens can be used for CSRF prevention and rate limiting.

Jump to

Keyboard shortcuts

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