diff --git a/src/core/flow.js b/src/core/flow.js index bdc82653a..6961cba03 100644 --- a/src/core/flow.js +++ b/src/core/flow.js @@ -36,6 +36,9 @@ import { TurnOrder } from './turn-order'; * the client while waiting for * the result of execution from * the server. + * @param {...object} canMakeMove - (G, ctx, playerID) => boolean + * Predicate to determine whether the player + * identified by playerID is allowed to make a move. */ export function Flow({ ctx, @@ -44,12 +47,16 @@ export function Flow({ validator, processMove, optimisticUpdate, + canMakeMove, }) { if (!ctx) ctx = () => ({}); if (!events) events = {}; if (!init) init = state => state; if (!validator) validator = () => true; if (!processMove) processMove = state => state; + if (!canMakeMove) + canMakeMove = (G, ctx, playerID) => + ctx.currentPlayer === 'any' || playerID === ctx.currentPlayer; if (optimisticUpdate === undefined) { optimisticUpdate = () => true; @@ -89,6 +96,8 @@ export function Flow({ }, optimisticUpdate, + + canMakeMove, }; } @@ -198,6 +207,7 @@ export function FlowWithPhases({ endPhase, undo, optimisticUpdate, + canMakeMove, }) { // Attach defaults. if (endPhase === undefined && phases) { @@ -501,5 +511,6 @@ export function FlowWithPhases({ events: enabledEvents, validator, processMove, + canMakeMove, }); } diff --git a/src/core/flow.test.js b/src/core/flow.test.js index 705cfe7e3..ebd7a08a5 100644 --- a/src/core/flow.test.js +++ b/src/core/flow.test.js @@ -521,3 +521,23 @@ test('undo / redo', () => { state = reducer(state, gameEvent('undo')); expect(state.G).toEqual({ A: true }); }); + +test('canMakeMove', () => { + // default behaviour + let flow = Flow({}); + expect(flow.canMakeMove({}, {}, 0)).toBe(false); + expect(flow.canMakeMove({}, { currentPlayer: 0 }, 0)).toBe(true); + expect(flow.canMakeMove({}, { currentPlayer: 'any' }, 0)).toBe(true); + + // no one can make a move + flow = Flow({ canMakeMove: () => false }); + expect(flow.canMakeMove({}, {}, 0)).toBe(false); + expect(flow.canMakeMove({}, { currentPlayer: 0 }, 0)).toBe(false); + expect(flow.canMakeMove({}, {}, 'any')).toBe(false); + + // flow with phases passes canMakeMove + flow = FlowWithPhases({ canMakeMove: () => false }); + expect(flow.canMakeMove({}, {}, 0)).toBe(false); + expect(flow.canMakeMove({}, { currentPlayer: 0 }, 0)).toBe(false); + expect(flow.canMakeMove({}, {}, 'any')).toBe(false); +}); diff --git a/src/server/index.js b/src/server/index.js index d931bf32a..aec28ccc7 100644 --- a/src/server/index.js +++ b/src/server/index.js @@ -58,12 +58,8 @@ export function Server({ games, db, _clientInfo, _roomInfo }) { return; } - // Bail out if the player making the move is not - // the current player. - if ( - state.ctx.currentPlayer != 'any' && - playerID != state.ctx.currentPlayer - ) { + // Check whether the player is allowed to make the move + if (!game.flow.canMakeMove(game, state.ctx, playerID)) { return; }