Skip to content

Commit

Permalink
Redact Log Events (#268)
Browse files Browse the repository at this point in the history
  • Loading branch information
Stefan-Hanke authored and nicolodavis committed Dec 6, 2018
1 parent ed165a8 commit 58cbd1e
Show file tree
Hide file tree
Showing 13 changed files with 373 additions and 7 deletions.
6 changes: 4 additions & 2 deletions docs/api/Game.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,10 @@ game state and the moves. The moves are converted to a
Code to run at the end of a move.
* `flow.movesPerTurn` (_number_): Ends the turn automatically if a certain number
of moves have been made.
* `flow.undoableMoves` (_array_): Enables undo and redo of listed moves.
Leave `undefined` if all moves should be undoable.
* `flow.undoableMoves` (_array_): Enables undo and redo of listed moves. Leave `undefined` if all moves should be undoable.
* `flow.redactedMoves` (_array_): List of moves to redact from the log.
* `flow.optimisticUpdate` (_function_): _(G, ctx, move) => boolean_
Return `false` to disable optimistic execution of a particular move on the client.
* `flow.phases` (_object_): Optional spec of game phases. See
[Phases](/phases) for more information.

Expand Down
14 changes: 14 additions & 0 deletions examples/react/redacted-move/board.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* Copyright 2018 The boardgame.io Authors
*
* Use of this source code is governed by a MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
*/

.secret-state section {
text-align: left;
padding: 10px;
margin-bottom: 20px;
background: #eee;
}
42 changes: 42 additions & 0 deletions examples/react/redacted-move/board.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright 2017 The boardgame.io Authors.
*
* Use of this source code is governed by a MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
*/

import React from 'react';
import PropTypes from 'prop-types';
import './board.css';

const Board = ({ G, ctx, moves, playerID, log }) => (
<div className="secret-state">
<section>
<strong>G</strong>
<pre>{JSON.stringify(G, null, 2)}</pre>

<strong>log</strong>
<pre>{JSON.stringify(log, null, 2)}</pre>
{playerID && (
<button
onClick={() =>
moves.clickCell({ secret: G.players[ctx.currentPlayer] })
}
>
Click Cell
</button>
)}
</section>
</div>
);

Board.propTypes = {
G: PropTypes.any.isRequired,
ctx: PropTypes.any.isRequired,
moves: PropTypes.any.isRequired,
playerID: PropTypes.any,
log: PropTypes.any,
};

export default Board;
38 changes: 38 additions & 0 deletions examples/react/redacted-move/game.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright 2018 The boardgame.io Authors
*
* Use of this source code is governed by a MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
*/

import { Game, PlayerView, TurnOrder } from 'boardgame.io/core';

const RedactedMoves = Game({
name: 'secret-state',

setup: () => ({
other: {},
players: {
0: 'player 0 state',
1: 'player 1 state',
},
}),

moves: {
/* eslint-disable no-unused-vars */
clickCell(G, ctx, secretstuff) {
return { ...G };
},
/* eslint-enable no-unused-vars */
},

flow: {
redactedMoves: ['clickCell'],
turnOrder: TurnOrder.ANY,
},

playerView: PlayerView.STRIP_SECRETS,
});

export default RedactedMoves;
19 changes: 19 additions & 0 deletions examples/react/redacted-move/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
* Copyright 2018 The boardgame.io Authors.
*
* Use of this source code is governed by a MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
*/

import Multiview from './multiview';

const routes = [
{
path: '/redacted_move',
text: 'Example',
component: Multiview,
},
];

export default { routes };
51 changes: 51 additions & 0 deletions examples/react/redacted-move/multiview.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* Copyright 2018 The boardgame.io Authors.
*
* Use of this source code is governed by a MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT.
*/

import React from 'react';
import { Client } from 'boardgame.io/react';
import Game from './game';
import Board from './board';

const App = Client({
game: Game,
numPlayers: 2,
board: Board,
debug: false,
multiplayer: { local: true },
});

const Multiview = () => (
<div style={{ padding: 50 }}>
<h1>Redacted Moves</h1>
<p>
This examples demonstrates the use of redacted moves. Using redacted moves
allows for secret information to be stripped from the log for other
players.
</p>
<p>
Clicking the button on one of the players, you should see complete log
event for that player but a redacted one for everyone else.
</p>
<div className="runner">
<div className="run">
<App gameID="redacted-move" playerID="0" />
&lt;App playerID=&quot;0&quot;/&gt;
</div>
<div className="run">
<App gameID="redacted-move" playerID="1" />
&lt;App playerID=&quot;1&quot;/&gt;
</div>
<div className="run">
<App gameID="redacted-move" />
&lt;App/&gt;
</div>
</div>
</div>
);

export default Multiview;
5 changes: 5 additions & 0 deletions examples/react/routes.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import random from './random';
import turnorder from './turnorder';
import ui from './ui';
import threejs from './threejs';
import redacted_move from './redacted-move';

const routes = [
{
Expand All @@ -40,6 +41,10 @@ const routes = [
name: 'Secret State',
routes: secret_state.routes,
},
{
name: 'Redacted Move',
routes: redacted_move.routes,
},
{
name: 'UI',
routes: ui.routes,
Expand Down
14 changes: 13 additions & 1 deletion examples/react/secret-state/board.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,28 @@ import React from 'react';
import PropTypes from 'prop-types';
import './board.css';

const Board = ({ G }) => (
const Board = ({ G, ctx, moves, playerID }) => (
<div className="secret-state">
<section>
<pre>{JSON.stringify(G, null, 2)}</pre>
{playerID && (
<button
onClick={() =>
moves.clickCell({ secret: G.players[ctx.currentPlayer] })
}
>
Click Cell
</button>
)}
</section>
</div>
);

Board.propTypes = {
G: PropTypes.any.isRequired,
ctx: PropTypes.any.isRequired,
moves: PropTypes.any.isRequired,
playerID: PropTypes.any,
};

export default Board;
6 changes: 6 additions & 0 deletions examples/react/secret-state/game.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ const SecretState = Game({
},
}),

moves: {
clickCell(G) {
return { ...G };
},
},

playerView: PlayerView.STRIP_SECRETS,
});

Expand Down
2 changes: 1 addition & 1 deletion examples/react/secret-state/multiview.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const App = Client({
numPlayers: 3,
board: Board,
debug: false,
multiplayer: true,
multiplayer: { local: true },
});

const Multiview = () => (
Expand Down
9 changes: 9 additions & 0 deletions src/core/flow.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ 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,
Expand All @@ -62,6 +65,7 @@ export function Flow({
optimisticUpdate,
canMakeMove,
canUndoMove,
redactedMoves,
}) {
if (!ctx) ctx = () => ({});
if (!events) events = {};
Expand Down Expand Up @@ -92,6 +96,7 @@ export function Flow({
ctx,
init,
canUndoMove,
redactedMoves,

eventNames: Object.getOwnPropertyNames(events),
enabledEventNames: Object.getOwnPropertyNames(enabledEvents),
Expand Down Expand Up @@ -181,6 +186,8 @@ 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
Expand Down Expand Up @@ -256,6 +263,7 @@ export function FlowWithPhases({
setActionPlayers,
undoableMoves,
allowedMoves,
redactedMoves,
optimisticUpdate,
}) {
// Attach defaults.
Expand Down Expand Up @@ -722,5 +730,6 @@ export function FlowWithPhases({
processMove,
canMakeMove,
canUndoMove,
redactedMoves,
});
}
54 changes: 52 additions & 2 deletions src/master/master.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,48 @@ import { MAKE_MOVE, GAME_EVENT } from '../core/action-types';
import { createStore } from 'redux';
import * as logging from '../core/logger';

/**
* 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) {
return log;
}

return log.map(logEvent => {
// filter for all other players and a spectator
if (playerID !== null && +playerID === +logEvent.action.payload.playerID) {
return logEvent;
}

// only filter moves
if (logEvent.action.type !== 'MAKE_MOVE') {
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 },
};
}

return filteredEvent;
});
}

/**
* Master
*
Expand Down Expand Up @@ -101,9 +143,15 @@ export class Master {
deltalog: undefined,
};

const log = redactLog(
this.game.flow.redactedMoves,
state.deltalog,
playerID
);

return {
type: 'update',
args: [gameID, filteredState, state.deltalog],
args: [gameID, filteredState, log],
};
});

Expand Down Expand Up @@ -138,10 +186,12 @@ export class Master {
deltalog: undefined,
};

const log = redactLog(this.game.flow.redactedMoves, state.log, playerID);

this.transportAPI.send({
playerID,
type: 'sync',
args: [gameID, filteredState, state.log],
args: [gameID, filteredState, log],
});

return;
Expand Down

0 comments on commit 58cbd1e

Please sign in to comment.