Skip to content

Commit a0d5f36

Browse files
jasonharrisonnicolodavis
authored andcommitted
Surface game metadata and player nicknames in client / react props (#436)
* Surface game metadata and player nicknames in client / react props * Add gameMetadata to Client documentation * Do not send credentials to clients in metadata, update documentation, add tests * remove unnecessary code from client.js * Address review
1 parent ca3378c commit a0d5f36

9 files changed

Lines changed: 90 additions & 9 deletions

File tree

docs/documentation/api/Client.md

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,28 @@ The `Board` component will receive the following as `props`:
108108

109109
10. `playerID`: The player ID associated with the client.
110110

111-
11. `isActive`: `true` if the client is able to currently make
111+
11. `gameMetadata`: An object containing the players that have joined the game from a [room](/api/Lobby.md).
112+
113+
Example:
114+
115+
````json
116+
{
117+
"players": {
118+
"0": {
119+
"id": 0,
120+
"name": "Alice"
121+
},
122+
"1": {
123+
"id": 1,
124+
"name": "Bob"
125+
}
126+
}
127+
}```
128+
129+
12. `isActive`: `true` if the client is able to currently make
112130
a move or interact with the game.
113131

114-
12. `isMultiplayer`: `true` if it is a multiplayer game.
132+
13. `isMultiplayer`: `true` if it is a multiplayer game.
115133

116-
13. `isConnected`: `true` if connection to the server is active.
134+
14. `isConnected`: `true` if connection to the server is active.
135+
````

src/client/client.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,7 @@ class _ClientImpl {
239239
isConnected: true,
240240
onAction: () => {},
241241
subscribe: () => {},
242+
subscribeGameMetadata: _metadata => {}, // eslint-disable-line no-unused-vars
242243
connect: () => {},
243244
updateGameID: () => {},
244245
updatePlayerID: () => {},
@@ -286,6 +287,10 @@ class _ClientImpl {
286287
}
287288

288289
this.createDispatchers();
290+
291+
this.transport.subscribeGameMetadata(metadata => {
292+
this.gameMetadata = metadata;
293+
});
289294
}
290295

291296
subscribe(fn) {

src/client/client.test.js

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,13 @@ describe('multiplayer', () => {
195195

196196
describe('custom transport', () => {
197197
class CustomTransport {
198-
custom = true;
198+
constructor() {
199+
this.callback = null;
200+
}
201+
202+
subscribeGameMetadata(fn) {
203+
this.callback = fn;
204+
}
199205
}
200206

201207
let client;
@@ -209,7 +215,12 @@ describe('multiplayer', () => {
209215

210216
test('correct transport used', () => {
211217
expect(client.transport).toBeInstanceOf(CustomTransport);
212-
expect(client.transport.custom).toBe(true);
218+
});
219+
220+
test('metadata callback', () => {
221+
const metadata = { m: true };
222+
client.transport.callback(metadata);
223+
expect(client.gameMetadata).toEqual(metadata);
213224
});
214225
});
215226

src/client/react-native.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ export function Client(opts) {
113113
reset: this.client.reset,
114114
undo: this.client.undo,
115115
redo: this.client.redo,
116+
gameMetadata: this.client.gameMetadata,
116117
});
117118
}
118119

src/client/react.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ export function Client(opts) {
170170
reset: this.client.reset,
171171
undo: this.client.undo,
172172
redo: this.client.redo,
173+
gameMetadata: this.client.gameMetadata,
173174
});
174175
}
175176

src/client/transport/local.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,8 @@ export class Local {
121121
*/
122122
subscribe() {}
123123

124+
subscribeGameMetadata(_metadata) {} // eslint-disable-line no-unused-vars
125+
124126
/**
125127
* Updates the game id.
126128
* @param {string} id - The new game id.

src/client/transport/socketio.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ export class SocketIO {
4646
this.gameID = this.gameName + ':' + this.gameID;
4747
this.isConnected = false;
4848
this.callback = () => {};
49+
this.gameMetadataCallback = () => {};
4950
}
5051

5152
/**
@@ -96,10 +97,11 @@ export class SocketIO {
9697

9798
// Called when the client first connects to the master
9899
// and requests the current game state.
99-
this.socket.on('sync', (gameID, state, log) => {
100+
this.socket.on('sync', (gameID, state, log, gameMetadata) => {
100101
if (gameID == this.gameID) {
101102
const action = ActionCreators.sync(state, log);
102103
this.store.dispatch(action);
104+
this.gameMetadataCallback(gameMetadata);
103105
}
104106
});
105107

@@ -124,6 +126,10 @@ export class SocketIO {
124126
this.callback = fn;
125127
}
126128

129+
subscribeGameMetadata(fn) {
130+
this.gameMetadataCallback = fn;
131+
}
132+
127133
/**
128134
* Updates the game id.
129135
* @param {string} id - The new game id.

src/master/master.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -259,14 +259,20 @@ export class Master {
259259
async onSync(gameID, playerID, numPlayers) {
260260
const key = gameID;
261261

262-
let state;
262+
let state, gameMetadata, filteredGameMetadata;
263263

264264
if (this.executeSynchronously) {
265265
state = this.storageAPI.get(key);
266+
gameMetadata = this.storageAPI.get(GameMetadataKey(gameID));
266267
} else {
267268
state = await this.storageAPI.get(key);
269+
gameMetadata = await this.storageAPI.get(GameMetadataKey(gameID));
270+
}
271+
if (gameMetadata) {
272+
filteredGameMetadata = Object.values(gameMetadata.players).map(player => {
273+
return { id: player.id, name: player.name };
274+
});
268275
}
269-
270276
// If the game doesn't exist, then create one on demand.
271277
// TODO: Move this out of the sync call.
272278
if (state === undefined) {
@@ -301,7 +307,7 @@ export class Master {
301307
this.transportAPI.send({
302308
playerID,
303309
type: 'sync',
304-
args: [gameID, filteredState, log],
310+
args: [gameID, filteredState, log, filteredGameMetadata],
305311
});
306312

307313
return;

src/master/master.test.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,36 @@ describe('sync', () => {
4444
await master.onSync('gameID', '0', 2);
4545
expect(send).toHaveBeenCalled();
4646
});
47+
48+
test('should not have metadata', async () => {
49+
await master.onSync('gameID', '0', 2);
50+
// [0][0] = first call, first argument
51+
expect(send.mock.calls[0][0].args[3]).toBeUndefined();
52+
});
53+
54+
test('should have metadata', async () => {
55+
const db = new InMemory();
56+
const dbMetadata = {
57+
players: {
58+
'0': {
59+
id: 0,
60+
credentials: 'qS2m4Ujb_',
61+
name: 'Alice',
62+
},
63+
'1': {
64+
id: 1,
65+
credentials: 'nIQtXFybDD',
66+
name: 'Bob',
67+
},
68+
},
69+
};
70+
db.set('gameID:metadata', dbMetadata);
71+
const masterWithMetadata = new Master(game, db, TransportAPI(send));
72+
await masterWithMetadata.onSync('gameID', '0', 2);
73+
74+
const expectedMetadata = [{ id: 0, name: 'Alice' }, { id: 1, name: 'Bob' }];
75+
expect(send.mock.calls[0][0].args[3]).toMatchObject(expectedMetadata);
76+
});
4777
});
4878

4979
describe('update', () => {

0 commit comments

Comments
 (0)