Skip to content

Commit

Permalink
refactor(server): Use the term "match" in the API and Lobby (#704)
Browse files Browse the repository at this point in the history
Closes #703

* GameMetadata -> MatchMetadata

* rooms -> matches

* API exposes `matchID`, make Lobby use that instead of `gameID` or `roomID`.

* CreateGame -> CreateMatch

* gameID, roomID -> matchID

* Document Lobby API endpoints.

* nextRoomID -> nextMatchID

* Update error messages.

* gameList -> matchList

* Use "match" wherever it makes sense in lobby code.

* Replace "room" with "match" in documentation.

* Rename 'game' to 'match' in comments and test names.

Co-authored-by: Chris Swithinbank <swithinbank@gmail.com>

* doesGameRequireAuthentication -> doesMatchRequireAuthentication

* Rename gameID to matchID in database interface functions.

Co-authored-by: Chris Swithinbank <swithinbank@gmail.com>
  • Loading branch information
adngdb and delucis authored May 21, 2020
1 parent f32dc76 commit adb251d
Show file tree
Hide file tree
Showing 18 changed files with 444 additions and 363 deletions.
4 changes: 2 additions & 2 deletions docs/documentation/api/Client.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,8 @@ The `Board` component will receive the following as `props`:

11. `playerID`: The player ID associated with the client.

12. `gameMetadata`: An object containing the players that have joined
the game from a [room](/api/Lobby.md).
12. `matchMetadata`: An object containing the players that have joined
the game from a [match](/api/Lobby.md).

Example:

Expand Down
44 changes: 22 additions & 22 deletions docs/documentation/api/Lobby.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,15 @@ import { Lobby } from 'boardgame.io/react';

### Server-side API

The [Server](/api/Server) hosts the Lobby REST API that can be used to create and join rooms. It is particularly useful when you want to
The [Server](/api/Server) hosts the Lobby REST API that can be used to create and join matches. It is particularly useful when you want to
authenticate clients to prove that they have the right to send
actions on behalf of a player.

Authenticated games are created with server-side tokens for each player. You can create a room with the `create` API call, and join a player to a room with the `join` API call.
Authenticated games are created with server-side tokens for each player. You can create a match with the `create` API call, and join a player to a match with the `join` API call.

A game that is authenticated will not accept moves from a client on behalf of a player without the appropriate credential token.

Use the `create` API call to create a room that requires credential tokens. When you call the `join` API, you can retrieve the credential token for a particular player.
Use the `create` API call to create a match that requires credential tokens. When you call the `join` API, you can retrieve the credential token for a particular player.

#### Configuration

Expand All @@ -45,27 +45,27 @@ Options are:
- `apiPort`: If specified, it runs the Lobby API in a separate Koa server on this port. Otherwise, it shares the same Koa server runnning on the default boardgame.io `port`.
- `apiCallback`: Called when the Koa server is ready. Only applicable if `apiPort` is specified.

#### Creating a room
#### Creating a match

##### POST `/games/{name}/create`

Creates a new authenticated room for a game named `name`.
Creates a new authenticated match for a game named `name`.

Accepts three parameters:

`numPlayers` (required): the number of players.

`setupData` (optional): custom object that is passed to the game `setup` function.

`unlisted` (optional): if set to `true`, the room will be excluded from the public list of room instances.
`unlisted` (optional): if set to `true`, the match will be excluded from the public list of match instances.

Returns `roomID`, which is the ID of the newly created game instance.
Returns `matchID`, which is the ID of the newly created game instance.

#### Joining a game

##### POST `/games/{name}/{id}/join`

Allows a player to join a particular room instance `id` of a game named `name`.
Allows a player to join a particular match instance `id` of a game named `name`.

Accepts three JSON body parameters:

Expand All @@ -81,7 +81,7 @@ Returns `playerCredentials` which is the token this player will require to authe

##### POST `/games/{name}/{id}/update`

Rename and/or update additional information of a user in the room instance `id` of a game named `name` previously joined by the player.
Rename and/or update additional information of a user in the match instance `id` of a game named `name` previously joined by the player.

Accepts four parameters, requires at least one of the two optional parameters:

Expand All @@ -93,41 +93,41 @@ Accepts four parameters, requires at least one of the two optional parameters:

`data` (optional): additional information associated to the player.

#### Leaving a room
#### Leaving a match

##### POST `/games/{name}/{id}/leave`

Leave the room instance `id` of a game named `name` previously joined by the player.
Leave the match instance `id` of a game named `name` previously joined by the player.

Accepts two parameters, all required:

`playerID`: the ID used by the player in the game (0, 1...).

`credentials`: the authentication token of the player.

#### Listing all room instances of a given game
#### Listing all match instances of a given game

##### GET `/games/{name}`

Returns all room instances of the game named `name`.
Returns all match instances of the game named `name`.

Returns an array of `rooms`. Each instance has fields:
Returns an array of `matches`. Each instance has fields:

`roomID`: the ID of the room instance.
`matchID`: the ID of the match instance.

`players`: the list of seats and players that have joined the game, if any.

`setupData` (optional): custom object that was passed to the game `setup` function.

#### Getting specific instance of a room by its ID
#### Getting specific instance of a match by its ID

##### GET `/games/{name}/{id}`

Returns a room instance given its roomID.
Returns a match instance given its matchID.

Returns a room instance. Each instance has fields:
Returns a match instance. Each instance has fields:

`roomID`: the ID of the room instance.
`matchID`: the ID of the match instance.

`players`: the list of seats and players that have joined the game, if any.

Expand All @@ -143,14 +143,14 @@ All actions for an authenticated game require an additional payload field `crede

`{name}` (required): the name of the game being played again.

`{id}` (required): the ID of the previous finished room.
`{id}` (required): the ID of the previous finished match.

Given a previous room, generates a room ID where users should go if they want to play again. Creates this new room if it didn't exist before.
Given a previous match, generates a match ID where users should go if they want to play again. Creates this new match if it didn't exist before.

Accepts these parameters:

`playerID` (required): the player ID of the player on the previous game.

`credentials` (required): player's credentials.

Returns `nextRoomID`, which is the ID of the newly created room that the user should go to play again.
Returns `nextMatchID`, which is the ID of the newly created match that the user should go to play again.
48 changes: 24 additions & 24 deletions src/lobby/connection.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class _LobbyConnectionImpl {
this.playerName = playerName || 'Visitor';
this.playerCredentials = playerCredentials;
this.server = server;
this.rooms = [];
this.matches = [];
}

_baseUrl() {
Expand All @@ -21,7 +21,7 @@ class _LobbyConnectionImpl {

async refresh() {
try {
this.rooms.length = 0;
this.matches.length = 0;
const resp = await fetch(this._baseUrl());
if (resp.status !== 200) {
throw new Error('HTTP status ' + resp.status);
Expand All @@ -31,19 +31,19 @@ class _LobbyConnectionImpl {
if (!this._getGameComponents(gameName)) continue;
const gameResp = await fetch(this._baseUrl() + '/' + gameName);
const gameJson = await gameResp.json();
for (let inst of gameJson.rooms) {
for (let inst of gameJson.matches) {
inst.gameName = gameName;
}
this.rooms = this.rooms.concat(gameJson.rooms);
this.matches = this.matches.concat(gameJson.matches);
}
} catch (error) {
throw new Error('failed to retrieve list of games (' + error + ')');
throw new Error('failed to retrieve list of matches (' + error + ')');
}
}

_getGameInstance(gameID) {
for (let inst of this.rooms) {
if (inst['gameID'] === gameID) return inst;
_getMatchInstance(matchID) {
for (let inst of this.matches) {
if (inst['matchID'] === matchID) return inst;
}
}

Expand All @@ -54,23 +54,23 @@ class _LobbyConnectionImpl {
}

_findPlayer(playerName) {
for (let inst of this.rooms) {
for (let inst of this.matches) {
if (inst.players.some(player => player.name === playerName)) return inst;
}
}

async join(gameName, gameID, playerID) {
async join(gameName, matchID, playerID) {
try {
let inst = this._findPlayer(this.playerName);
if (inst) {
throw new Error('player has already joined ' + inst.gameID);
throw new Error('player has already joined ' + inst.matchID);
}
inst = this._getGameInstance(gameID);
inst = this._getMatchInstance(matchID);
if (!inst) {
throw new Error('game instance ' + gameID + ' not found');
throw new Error('game instance ' + matchID + ' not found');
}
const resp = await fetch(
this._baseUrl() + '/' + gameName + '/' + gameID + '/join',
this._baseUrl() + '/' + gameName + '/' + matchID + '/join',
{
method: 'POST',
body: JSON.stringify({
Expand All @@ -85,18 +85,18 @@ class _LobbyConnectionImpl {
inst.players[Number.parseInt(playerID)].name = this.playerName;
this.playerCredentials = json.playerCredentials;
} catch (error) {
throw new Error('failed to join room ' + gameID + ' (' + error + ')');
throw new Error('failed to join match ' + matchID + ' (' + error + ')');
}
}

async leave(gameName, gameID) {
async leave(gameName, matchID) {
try {
let inst = this._getGameInstance(gameID);
if (!inst) throw new Error('game instance not found');
let inst = this._getMatchInstance(matchID);
if (!inst) throw new Error('match instance not found');
for (let player of inst.players) {
if (player.name === this.playerName) {
const resp = await fetch(
this._baseUrl() + '/' + gameName + '/' + gameID + '/leave',
this._baseUrl() + '/' + gameName + '/' + matchID + '/leave',
{
method: 'POST',
body: JSON.stringify({
Expand All @@ -114,18 +114,18 @@ class _LobbyConnectionImpl {
return;
}
}
throw new Error('player not found in room');
throw new Error('player not found in match');
} catch (error) {
throw new Error('failed to leave room ' + gameID + ' (' + error + ')');
throw new Error('failed to leave match ' + matchID + ' (' + error + ')');
}
}

async disconnect() {
let inst = this._findPlayer(this.playerName);
if (inst) {
await this.leave(inst.gameName, inst.gameID);
await this.leave(inst.gameName, inst.matchID);
}
this.rooms = [];
this.matches = [];
this.playerName = 'Visitor';
}

Expand All @@ -148,7 +148,7 @@ class _LobbyConnectionImpl {
if (resp.status !== 200) throw new Error('HTTP status ' + resp.status);
} catch (error) {
throw new Error(
'failed to create room for ' + gameName + ' (' + error + ')'
'failed to create match for ' + gameName + ' (' + error + ')'
);
}
}
Expand Down
Loading

0 comments on commit adb251d

Please sign in to comment.