Skip to content

Commit e4fc7bd

Browse files
delucisnicolodavis
andauthored
feat(master): Update metadata with gameover value on game end (#645)
* feat(master): Update game metadata with gameover value on game end Closes #634 * test(master): Add tests for gameover metadata updates * refactor(master): Tidy up some typings Co-authored-by: Nicolo John Davis <[email protected]>
1 parent 78113aa commit e4fc7bd

3 files changed

Lines changed: 71 additions & 9 deletions

File tree

src/master/master.test.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,44 @@ describe('update', () => {
208208
await master.onUpdate(event, 3, 'gameID', '0');
209209
expect(error).toHaveBeenCalledWith(`game over - gameID=[gameID]`);
210210
});
211+
212+
test('writes gameover to metadata', async () => {
213+
const id = 'gameWithMetadata';
214+
const db = new InMemory();
215+
const dbMetadata = {
216+
gameName: 'tic-tac-toe',
217+
setupData: {},
218+
players: { '0': { id: 0 }, '1': { id: 1 } },
219+
};
220+
db.setMetadata(id, dbMetadata);
221+
const masterWithMetadata = new Master(game, db, TransportAPI(send));
222+
await masterWithMetadata.onSync(id, '0', 2);
223+
224+
const gameOverArg = 'gameOverArg';
225+
const event = ActionCreators.gameEvent('endGame', gameOverArg);
226+
await masterWithMetadata.onUpdate(event, 0, id, '0');
227+
const { metadata } = db.fetch(id, { metadata: true });
228+
expect(metadata.gameover).toEqual(gameOverArg);
229+
});
230+
231+
test('writes gameover to metadata with async storage API', async () => {
232+
const id = 'gameWithMetadata';
233+
const db = new InMemoryAsync();
234+
const dbMetadata = {
235+
gameName: 'tic-tac-toe',
236+
setupData: {},
237+
players: { '0': { id: 0 }, '1': { id: 1 } },
238+
};
239+
db.setMetadata(id, dbMetadata);
240+
const masterWithMetadata = new Master(game, db, TransportAPI(send));
241+
await masterWithMetadata.onSync(id, '0', 2);
242+
243+
const gameOverArg = 'gameOverArg';
244+
const event = ActionCreators.gameEvent('endGame', gameOverArg);
245+
await masterWithMetadata.onUpdate(event, 0, id, '0');
246+
const { metadata } = db.fetch(id, { metadata: true });
247+
expect(metadata.gameover).toEqual(gameOverArg);
248+
});
211249
});
212250

213251
describe('playerView', () => {

src/master/master.ts

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,12 @@ export class Master {
138138
auth: null | AuthFn;
139139
shouldAuth: typeof doesGameRequireAuthentication;
140140

141-
constructor(game: Game, storageAPI, transportAPI, auth?: AuthFn | boolean) {
141+
constructor(
142+
game: Game,
143+
storageAPI: StorageAPI.Sync | StorageAPI.Async,
144+
transportAPI,
145+
auth?: AuthFn | boolean
146+
) {
142147
this.game = ProcessGameConfig(game);
143148
this.storageAPI = storageAPI;
144149
this.transportAPI = transportAPI;
@@ -171,17 +176,18 @@ export class Master {
171176
playerID: string
172177
) {
173178
let isActionAuthentic;
179+
let metadata: Server.GameMetadata | undefined;
174180
const credentials = credAction.payload.credentials;
175181
if (IsSynchronous(this.storageAPI)) {
176-
const { metadata } = this.storageAPI.fetch(gameID, { metadata: true });
182+
({ metadata } = this.storageAPI.fetch(gameID, { metadata: true }));
177183
const playerMetadata = getPlayerMetadata(metadata, playerID);
178184
isActionAuthentic = this.shouldAuth(metadata)
179185
? this.auth(credentials, playerMetadata)
180186
: true;
181187
} else {
182-
const { metadata } = await this.storageAPI.fetch(gameID, {
188+
({ metadata } = await this.storageAPI.fetch(gameID, {
183189
metadata: true,
184-
});
190+
}));
185191
const playerMetadata = getPlayerMetadata(metadata, playerID);
186192
isActionAuthentic = this.shouldAuth(metadata)
187193
? await this.auth(credentials, playerMetadata)
@@ -284,10 +290,29 @@ export class Master {
284290

285291
const { deltalog, ...stateWithoutDeltalog } = state;
286292

293+
let newMetadata: Server.GameMetadata | undefined;
294+
if (
295+
metadata &&
296+
!('gameover' in metadata) &&
297+
state.ctx.gameover !== undefined
298+
) {
299+
newMetadata = {
300+
...metadata,
301+
gameover: state.ctx.gameover,
302+
};
303+
}
304+
287305
if (IsSynchronous(this.storageAPI)) {
288306
this.storageAPI.setState(key, stateWithoutDeltalog, deltalog);
307+
if (newMetadata) this.storageAPI.setMetadata(key, newMetadata);
289308
} else {
290-
await this.storageAPI.setState(key, stateWithoutDeltalog, deltalog);
309+
const writes = [
310+
this.storageAPI.setState(key, stateWithoutDeltalog, deltalog),
311+
];
312+
if (newMetadata) {
313+
writes.push(this.storageAPI.setMetadata(key, newMetadata));
314+
}
315+
await Promise.all(writes);
291316
}
292317
}
293318

@@ -311,8 +336,7 @@ export class Master {
311336
}>;
312337

313338
if (IsSynchronous(this.storageAPI)) {
314-
const api = this.storageAPI as StorageAPI.Sync;
315-
result = api.fetch(key, {
339+
result = this.storageAPI.fetch(key, {
316340
state: true,
317341
metadata: true,
318342
log: true,
@@ -350,8 +374,7 @@ export class Master {
350374
});
351375

352376
if (IsSynchronous(this.storageAPI)) {
353-
const api = this.storageAPI as StorageAPI.Sync;
354-
api.setState(key, state);
377+
this.storageAPI.setState(key, state);
355378
} else {
356379
await this.storageAPI.setState(key, state);
357380
}

src/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,7 @@ export namespace Server {
275275
gameName: string;
276276
players: { [id: number]: PlayerMetadata };
277277
setupData: any;
278+
gameover?: any;
278279
nextRoomID?: string;
279280
}
280281

0 commit comments

Comments
 (0)