server.js
What server.js exports: game or shared, never both. A server.js at the
top of the app's folder turns rooms on. Tinylib runs it in each room, where
no page can read it, and keeps the room's state: every function gets the
state and a ServerContext. Functions are synchronous.
Math.random(), Date.now() and crypto.randomUUID() come from the
room's secret seed and the time the call arrived, so a room's run repeats
exactly. Only the state lasts from one call to the next; tinylib dev
loads server.js afresh for every call. Every room runs the latest published
server.js, and a room ends when the latest exports the other one.
Example
export const game = {
players: { min: 2, max: 2 },
setup: ({ players }) => ({ moves: [], white: players[0].id, winner: null }),
turn: (state, { players }) => (state.winner ? [] : [players[state.moves.length % 2].id]),
actions: {
move(state, move, { player, refuse }) {
if (!legal(state, move)) refuse("That move isn't legal.")
state.moves.push(move)
if (mate(state)) state.winner = player.id
},
},
left(state, { player, players }) {
state.winner = players.find((other) => other.id !== player.id).id
},
outcome: (state) => (state.winner ? { winners: [state.winner] } : null),
}
Properties
Game
People join until players.max are in, or until a player calls
room.start() once players.min are. Then setup runs with them, and
nobody joins after.
Type Parameters
| Type Parameter | Default type |
|---|---|
State | unknown |
Properties
| Property | Type | Description |
|---|---|---|
players | object | How many play: min from 1, max from 2 to limits.rooms.maxMembers. A game keeps the numbers of the publish it was created under, as its state keeps its shape. |
players.min | number | - |
players.max | number | - |
actions | Record<string, (state, args, ctx) => void> | Each runs on room.act(name, args) once the game has started, changes state in place, and may call ctx.refuse or ctx.remove. Tinylib runs it on a copy of the state and keeps the copy only if it returns. Any throw other than ctx.refuse rejects the page's call with failed, and the developer sees the stack in the console. |
anytime? | Record<string, (state, args, ctx) => void> | Actions any player still in may call while the game runs, whatever turn says: resigning, offering a draw, claiming a win on time. They run as actions do. A name in both actions and anytime fails every call when the room first runs server.js. |
setup()
setup(ctx): State;
Runs once, when the game starts, and returns the first state.
ctx.players are the players, in the order of room.members, so someone
who left and came back keeps their first place, and ctx.player
is null. If it throws, the call that started the game rejects with
failed and the game waits.
Parameters
| Parameter | Type |
|---|---|
ctx | ServerContext |
Returns
State
view()?
optional view(state, ctx): unknown;
Runs after every change, once per player, and returns what that person sees. Nothing else reaches their device. Without it, everyone sees the whole state.
Parameters
| Parameter | Type |
|---|---|
state | State |
ctx | ServerContext |
Returns
unknown
turn()?
optional turn(state, ctx): string[];
Runs after every change and returns the ids of the players who may call
actions now. Tinylib drops anyone who has left, refuses everyone else
with not_your_turn, and sends "Your turn" to someone newly named who
isn't looking at the game. anytime actions skip it. When it names
nobody and outcome returns null, the game ends. Without it, every
player still in may act, and no "Your turn" is sent.
Parameters
| Parameter | Type |
|---|---|
state | State |
ctx | ServerContext |
Returns
string[]
left()?
optional left(state, ctx): void;
Runs when a player leaves a game that has started, blocks another
player, is removed by ctx.remove, or is dealt out by the idle rule,
with ctx.player as who left. Resigning or dealing them out is
the game's rule. It can't refuse. If it throws, the leave still happens
and the state is unchanged. When no outcome follows and fewer than
players.min are left, the game ends with no outcome. When Tinylib takes
a player out, as a suspension does, and fewer than players.min would be
left, the game ends as if it never started and left doesn't run.
Parameters
| Parameter | Type |
|---|---|
state | State |
ctx | ServerContext |
Returns
void
outcome()
outcome(state, ctx):
| {
winners: string[];
}
| null;
Runs after every change, and returns null while the game goes on. The
first { winners } it returns ends the game, and each player who isn't
looking gets "Game over". An empty winners is a game nobody won.
Parameters
| Parameter | Type |
|---|---|
state | State |
ctx | ServerContext |
Returns
| {
winners: string[];
}
| null
Shared
People join at any time until the room is full, and it ends when nobody is still in it.
Type Parameters
| Type Parameter | Default type |
|---|---|
State | unknown |
Properties
| Property | Type | Description |
|---|---|---|
size | number | The most people in the room at once, 2 to limits.rooms.maxMembers. |
actions | Record<string, (state, args, ctx) => void> | As in a Game, from the moment the room is made. |
setup()
setup(ctx): State;
Runs once, in host.createRoom, and returns the first state. If it
throws, createRoom rejects with failed and no room is made.
Parameters
| Parameter | Type |
|---|---|
ctx | ServerContext |
Returns
State
view()?
optional view(state, ctx): unknown;
As in a Game.
Parameters
| Parameter | Type |
|---|---|
state | State |
ctx | ServerContext |
Returns
unknown
joinable()?
optional joinable(state, ctx): boolean;
Runs when someone accepts an invite, and ctx.player is the person
joining. false turns them away with not_joinable. Without it, people
can join until the room is full.
Parameters
| Parameter | Type |
|---|---|
state | State |
ctx | ServerContext |
Returns
boolean
left()?
optional left(state, ctx): void;
Runs when someone leaves, is removed or blocks another member, with
ctx.player as who left. It can't refuse. If it throws, the leave still
happens and the state is unchanged.
Parameters
| Parameter | Type |
|---|---|
state | State |
ctx | ServerContext |
Returns
void
ServerContext
What each function of a Game or Shared gets besides the state.
Properties
refuse()
refuse(sentence): never;
Stops the action and rejects the page's call with refused, whose message is sentence. Actions only.
Parameters
| Parameter | Type |
|---|---|
sentence | string |
Returns
never
remove()
remove(id): void;
Takes a member out of the room once the action returns, as if they left.
The action is refused with not_removable when id is the caller's own,
a member who left, or someone never in the room. Actions only.
Parameters
| Parameter | Type |
|---|---|
id | string |
Returns
void