Skip to content

Commit 62f97e5

Browse files
committed
fix: Allow plugins to use events in fnWrap
Allow custom plugins to call events in their `fnWrap` method. Care needs taking here as `fnWrap` may be run multiple times for hooks etc. This also stops processing the event dispatch queue once the game is over to protect against `endGame` infinite loops (via `onEnd` hooks).
1 parent f4380ac commit 62f97e5

3 files changed

Lines changed: 44 additions & 1 deletion

File tree

src/plugins/events/events.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,9 @@ export class Events {
149149
return stateWithError(event.error, Errors.CalledOutsideHook);
150150
}
151151

152+
// Stop processing events once the game has finished.
153+
if (state.ctx.gameover) break EventQueue;
154+
152155
switch (event.type) {
153156
case 'endStage':
154157
case 'setStage':

src/plugins/main.test.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import { Client } from '../client/client';
1010
import { Local } from '../client/transport/local';
1111
import type { Game } from '../types';
12+
import { GameMethod } from '../../packages/core';
1213

1314
describe('basic', () => {
1415
let client: ReturnType<typeof Client>;
@@ -358,3 +359,42 @@ describe('plugins are accessible in events triggered from moves', () => {
358359
});
359360
});
360361
});
362+
363+
describe('plugins can use events in fnWrap', () => {
364+
const game: Game = {
365+
plugins: [
366+
{
367+
name: 'test',
368+
fnWrap:
369+
(fn, type) =>
370+
(G, ctx, ...args) => {
371+
G = fn(G, ctx, ...args);
372+
if (G.endTurn && type === GameMethod.MOVE) {
373+
ctx.events.endTurn();
374+
}
375+
if (G.endGame) {
376+
ctx.events.endGame(G.endGame);
377+
}
378+
return G;
379+
},
380+
},
381+
],
382+
moves: {
383+
endGame: () => ({ endGame: true }),
384+
endTurn: () => ({ endTurn: true }),
385+
},
386+
};
387+
388+
test('plugin can end turn', () => {
389+
const client = Client({ game });
390+
client.moves.endTurn();
391+
expect(client.getState().ctx.turn).toBe(2);
392+
expect(client.getState().ctx.currentPlayer).toBe('1');
393+
});
394+
395+
test('plugin can end game', () => {
396+
const client = Client({ game });
397+
client.moves.endGame();
398+
expect(client.getState().ctx.gameover).toBe(true);
399+
});
400+
});

src/plugins/main.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ export const FnWrap = (
102102
methodType: GameMethod,
103103
plugins: Plugin[]
104104
) => {
105-
return [...DEFAULT_PLUGINS, ...plugins]
105+
return [...CORE_PLUGINS, ...plugins, PluginEvents]
106106
.filter((plugin) => plugin.fnWrap !== undefined)
107107
.reduce(
108108
(method: AnyFn, { fnWrap }: Plugin) => fnWrap(method, methodType),

0 commit comments

Comments
 (0)