From 8924e848e0657ed130258d622a680940bab7b6fe Mon Sep 17 00:00:00 2001 From: Nicolo Davis Date: Tue, 21 May 2019 22:58:08 +0800 Subject: [PATCH] move redactedMoves into a boolean option in the long form move syntax --- src/core/flow.js | 10 --------- src/core/game.js | 26 +++++++++++++--------- src/core/reducer.js | 8 ++++++- src/master/master.js | 43 +++++++++++++++---------------------- src/master/master.test.js | 45 +++++++++++++++++++++------------------ 5 files changed, 64 insertions(+), 68 deletions(-) diff --git a/src/core/flow.js b/src/core/flow.js index f72fe47a8..d6ef5c0e9 100644 --- a/src/core/flow.js +++ b/src/core/flow.js @@ -53,9 +53,6 @@ import * as logging from './logger'; * Predicate to determine whether a * particular move is undoable at this * time. - * - * @param {Array} redactedMoves - List of moves to be redacted - * from the log. */ export function Flow({ ctx, @@ -66,7 +63,6 @@ export function Flow({ optimisticUpdate, canMakeMove, canUndoMove, - redactedMoves, moveMap, }) { if (!ctx) ctx = () => ({}); @@ -103,7 +99,6 @@ export function Flow({ ctx, init, canUndoMove, - redactedMoves, moveMap, eventNames: Object.getOwnPropertyNames(events), @@ -191,9 +186,6 @@ export function Flow({ * @param {...object} undoableMoves - List of moves that are undoable, * (default: null, i.e. all moves are undoable). * - * @param {Array} redactedMoves - List of moves to be redacted - * from the log. - * * @param {...object} optimisticUpdate - (G, ctx, move) => boolean * Control whether a move should * be executed optimistically on @@ -249,7 +241,6 @@ export function FlowWithPhases({ endGame, setActionPlayers, undoableMoves, - redactedMoves, optimisticUpdate, game, }) { @@ -728,7 +719,6 @@ export function FlowWithPhases({ processMove, canMakeMove, canUndoMove, - redactedMoves, moveMap, }); } diff --git a/src/core/game.js b/src/core/game.js index 531fbcb10..af358f836 100644 --- a/src/core/game.js +++ b/src/core/game.js @@ -102,30 +102,36 @@ function Game(game) { game.flow = FlowWithPhases({ game, ...game.flow }); } + const getMove = name => { + if (name in game.moves) { + return game.moves[name]; + } + + if (name in game.flow.moveMap) { + return game.flow.moveMap[name]; + } + + return null; + }; + return { ...game, + getMove, + moveNames: [ ...Object.getOwnPropertyNames(game.moves), ...Object.keys(game.flow.moveMap || []), ], processMove: (G, action, ctx) => { - let moveFn = null; - - if (game.moves.hasOwnProperty(action.type)) { - moveFn = game.moves[action.type]; - } - - if (action.type in game.flow.moveMap) { - moveFn = game.flow.moveMap[action.type]; - } + let moveFn = getMove(action.type); if (moveFn instanceof Object && moveFn.impl) { moveFn = moveFn.impl; } - if (moveFn !== null) { + if (moveFn instanceof Function) { const ctxWithPlayerID = { ...ctx, playerID: action.playerID }; const args = [G, ctxWithPlayerID].concat(action.args); const fn = FnWrap(moveFn, game); diff --git a/src/core/reducer.js b/src/core/reducer.js index da65c684f..e85dc04ee 100644 --- a/src/core/reducer.js +++ b/src/core/reducer.js @@ -273,14 +273,20 @@ export function CreateGameReducer({ game, multiplayer }) { return state; } + const move = game.getMove(action.payload.type); + // Create a log entry for this move. - const logEntry = { + let logEntry = { action, _stateID: state._stateID, turn: state.ctx.turn, phase: state.ctx.phase, }; + if (move.redact === true) { + logEntry.redact = true; + } + // don't call into events here const newState = apiCtx.updateAndDetach( { ...state, deltalog: [logEntry] }, diff --git a/src/master/master.js b/src/master/master.js index 912582047..5effc54ff 100644 --- a/src/master/master.js +++ b/src/master/master.js @@ -16,42 +16,37 @@ const GameMetadataKey = gameID => `${gameID}:metadata`; /** * Redact the log. * - * @param {Array} redactedMoves - List of moves to redact. * @param {Array} log - The game log (or deltalog). * @param {String} playerID - The playerID that this log is * to be sent to. */ -export function redactLog(redactedMoves, log, playerID) { - if (redactedMoves === undefined || log === undefined) { +export function redactLog(log, playerID) { + if (log === undefined) { return log; } return log.map(logEvent => { - // filter for all other players and a spectator + // filter for all other players and spectators. if (playerID !== null && +playerID === +logEvent.action.payload.playerID) { return logEvent; } - // only filter moves - if (logEvent.action.type !== 'MAKE_MOVE') { + if (logEvent.redact !== true) { return logEvent; } - const moveName = logEvent.action.payload.type; - let filteredEvent = logEvent; - if (redactedMoves.includes(moveName)) { - const newPayload = { - ...filteredEvent.action.payload, - args: undefined, - argsRedacted: true, - }; - filteredEvent = { - ...filteredEvent, - action: { ...filteredEvent.action, payload: newPayload }, - }; - } + const payload = { + ...logEvent.action.payload, + args: null, + }; + const filteredEvent = { + ...logEvent, + action: { ...logEvent.action, payload }, + }; - return filteredEvent; + /* eslint-disable-next-line no-unused-vars */ + const { redact, ...remaining } = filteredEvent; + return remaining; }); } @@ -227,11 +222,7 @@ export class Master { }, }; - const log = redactLog( - this.game.flow.redactedMoves, - state.deltalog, - playerID - ); + const log = redactLog(state.deltalog, playerID); return { type: 'update', @@ -302,7 +293,7 @@ export class Master { }, }; - const log = redactLog(this.game.flow.redactedMoves, state.log, playerID); + const log = redactLog(state.log, playerID); this.transportAPI.send({ playerID, diff --git a/src/master/master.test.js b/src/master/master.test.js index 9495d8e8d..8316e88d8 100644 --- a/src/master/master.test.js +++ b/src/master/master.test.js @@ -312,29 +312,30 @@ describe('authentication', () => { describe('redactLog', () => { test('no redactedMoves', () => { - const logEvents = [ActionCreators.gameEvent('endTurn')]; - const result = redactLog(undefined, logEvents, '0'); + const logEvents = [{ action: ActionCreators.gameEvent('endTurn') }]; + const result = redactLog(logEvents, '0'); expect(result).toMatchObject(logEvents); }); test('redacted move is only shown with args to the player that made the move', () => { - const rm = ['clickCell']; const logEvents = [ - { action: ActionCreators.makeMove('clickCell', [1, 2, 3], '0') }, + { + action: ActionCreators.makeMove('clickCell', [1, 2, 3], '0'), + redact: true, + }, ]; // player that made the move - let result = redactLog(rm, logEvents, '0'); + let result = redactLog(logEvents, '0'); expect(result).toMatchObject(logEvents); // other player - result = redactLog(rm, logEvents, '1'); + result = redactLog(logEvents, '1'); expect(result).toMatchObject([ { action: { type: 'MAKE_MOVE', payload: { - argsRedacted: true, credentials: undefined, playerID: '0', type: 'clickCell', @@ -345,53 +346,55 @@ describe('redactLog', () => { }); test('not redacted move is shown to all', () => { - const rm = ['clickCell']; const logEvents = [ { action: ActionCreators.makeMove('unclickCell', [1, 2, 3], '0') }, ]; // player that made the move - let result = redactLog(rm, logEvents, '0'); + let result = redactLog(logEvents, '0'); expect(result).toMatchObject(logEvents); // other player - result = redactLog(rm, logEvents, '1'); + result = redactLog(logEvents, '1'); expect(result).toMatchObject(logEvents); }); test('can explicitly set showing args to true', () => { - const rm = []; const logEvents = [ { action: ActionCreators.makeMove('unclickCell', [1, 2, 3], '0') }, ]; // player that made the move - let result = redactLog(rm, logEvents, '0'); + let result = redactLog(logEvents, '0'); expect(result).toMatchObject(logEvents); // other player - result = redactLog(rm, logEvents, '1'); + result = redactLog(logEvents, '1'); expect(result).toMatchObject(logEvents); }); test('events are not redacted', () => { - const rm = ['clickCell']; const logEvents = [{ action: ActionCreators.gameEvent('endTurn') }]; // player that made the move - let result = redactLog(rm, logEvents, '0'); + let result = redactLog(logEvents, '0'); expect(result).toMatchObject(logEvents); // other player - result = redactLog(rm, logEvents, '1'); + result = redactLog(logEvents, '1'); expect(result).toMatchObject(logEvents); }); test('make sure sync redacts the log', async () => { - const game2 = Game({ - moves: { A: G => G, B: G => G }, - flow: { redactedMoves: ['B'] }, + const game = Game({ + moves: { + A: G => G, + B: { + impl: G => G, + redact: true, + }, + }, }); const send = jest.fn(); - const master = new Master(game2, new InMemory(), TransportAPI(send)); + const master = new Master(game, new InMemory(), TransportAPI(send)); const actionA = ActionCreators.makeMove('A', ['not redacted']); const actionB = ActionCreators.makeMove('B', ['redacted']); @@ -419,7 +422,7 @@ describe('redactLog', () => { type: 'MAKE_MOVE', payload: { type: 'B', - argsRedacted: true, + args: null, }, }, _stateID: 1,