115 lines
3.5 KiB
TypeScript
115 lines
3.5 KiB
TypeScript
import { CreateLobby } from "./action/initial/create-lobby";
|
|
import * as serverConfig from "./config";
|
|
import * as lobby from "./lobby";
|
|
import { Lobby } from "./lobby";
|
|
import { GameCode } from "./lobby/game-code";
|
|
import * as timeout from "./timeout";
|
|
import { Token } from "./user/token";
|
|
|
|
/**
|
|
* Represents a chunk of data that should be written as a single transaction,
|
|
* so if the server was restarted either all are applied or none are.
|
|
*/
|
|
export interface Transaction {
|
|
/**
|
|
* New lobby data that should replace what is currently in the store.
|
|
*/
|
|
lobby?: Lobby;
|
|
/**
|
|
* If given, timeouts that should be added to the store.
|
|
*/
|
|
timeouts?: Iterable<timeout.TimeoutAfter>;
|
|
/**
|
|
* If given, the id of the timeout that this resolves, and so should be
|
|
* removed from the store.
|
|
*/
|
|
executedTimeout?: timeout.Id;
|
|
}
|
|
|
|
export type ReadOnlyTransaction = Transaction & {
|
|
lobby?: undefined;
|
|
timeouts?: Iterable<timeout.TimeoutAfter & { after: Exclude<number, 0> }>;
|
|
};
|
|
|
|
export abstract class Store {
|
|
/**
|
|
* The configuration for this store.
|
|
*/
|
|
public abstract readonly config: serverConfig.Storage;
|
|
|
|
/**
|
|
* A unique id for the store - this *must* be unique to the store, otherwise
|
|
* there is a chance reused lobby codes could be incorrectly accessed by users
|
|
* using old tokens. When this changes, it makes all previously issued tokens
|
|
* invalid, but if there is any security concern, change the application
|
|
* secret, not this. That will have the same effect securely.
|
|
*/
|
|
public abstract async id(): Promise<string>;
|
|
|
|
/**
|
|
* Returns if the given lobby exists.
|
|
*/
|
|
public abstract async exists(gameCode: GameCode): Promise<boolean>;
|
|
|
|
/** Create a new lobby.
|
|
* @return The game code for the new lobby and the user id for the owner.
|
|
*/
|
|
public abstract async newLobby(
|
|
creation: CreateLobby,
|
|
secret: string
|
|
): Promise<{ gameCode: GameCode; token: Token }>;
|
|
|
|
/**
|
|
* An operation that doesn't make any changes to the lobby.
|
|
* @param gameCode The game code for the lobby to modify.
|
|
* @param read The operation to perform.
|
|
*/
|
|
public async read<T>(
|
|
gameCode: GameCode,
|
|
read: (lobby: Lobby) => { transaction: ReadOnlyTransaction; result: T }
|
|
): Promise<T> {
|
|
return this.writeAndReturn(gameCode, read);
|
|
}
|
|
|
|
/**
|
|
* Perform a write operation on a lobby.
|
|
* If you make changes to the object as given, you must return it.
|
|
* @param gameCode The game code for the lobby to modify.
|
|
* @param write The operation to perform.
|
|
*/
|
|
public async write(
|
|
gameCode: GameCode,
|
|
write: (lobby: Lobby) => Transaction
|
|
): Promise<void> {
|
|
await this.writeAndReturn(gameCode, (lobby: Lobby) => ({
|
|
transaction: write(lobby),
|
|
result: undefined
|
|
}));
|
|
}
|
|
|
|
public abstract async writeAndReturn<T>(
|
|
gameCode: GameCode,
|
|
write: (lobby: Lobby) => { transaction: Transaction; result: T }
|
|
): Promise<T>;
|
|
|
|
/** Get a list of summaries for all the public lobbies in the store.*/
|
|
public abstract lobbySummaries(): AsyncIterableIterator<lobby.Summary>;
|
|
|
|
/**
|
|
* Get all timed out timeouts from the store.
|
|
*/
|
|
public abstract timedOut(): AsyncIterableIterator<timeout.TimedOut>;
|
|
|
|
/**
|
|
* Delete the given lobby and all associated timeouts.
|
|
*/
|
|
public abstract async delete(gameCode: GameCode): Promise<void>;
|
|
|
|
/**
|
|
* Remove lobbies where the game is finished or everyone has been
|
|
* disconnected for some time.
|
|
* This should also clean up the cache as appropriate.
|
|
*/
|
|
public abstract async garbageCollect(): Promise<number>;
|
|
}
|