Skip to content

Commit e515573

Browse files
authored
fix(random): handle redacted state on multiplayer clients (#885)
Fixes #870 Prior to the current plugin system, the randomness API set a default seed '0' if it didn’t receive one from game state (i.e. when it was run on a multiplayer client and the randomness state was redacted). When the new plugin system was built, this fallback value was removed, but that didn’t cause errors because the PRNG seed and state were no longer redacted. After #857 (released in v0.42.2) reintroduced redacted state for plugins, the plugin broke for multiplayer clients that no longer had access to plugin state. This commit reinstates the default seed (the result of which is discarded in any case) and adds a test for running the plugin with redacted state to catch a similar error in any future refactoring. See 4b1c135 for details of the randomness API before the current implementation.
1 parent 8f85fb0 commit e515573

2 files changed

Lines changed: 21 additions & 2 deletions

File tree

src/plugins/random/random.test.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import { makeMove } from '../../core/action-creators';
1111
import { CreateGameReducer } from '../../core/reducer';
1212
import { InitializeGame } from '../../core/initialize';
1313
import { Client } from '../../client/client';
14+
import { PlayerView } from '../main';
1415

1516
function Init(seed) {
1617
return new Random({ seed });
@@ -123,6 +124,24 @@ test('Random API is not executed optimisitically', () => {
123124
}
124125
});
125126

127+
test('Random API works when its state is redacted by playerView', () => {
128+
const game = {
129+
seed: 0,
130+
moves: {
131+
rollDie: (G, ctx) => ({ ...G, die: ctx.random.D6() }),
132+
},
133+
};
134+
135+
const opts = { game, isClient: true };
136+
const reducer = CreateGameReducer(opts);
137+
let state = InitializeGame({ game });
138+
state.plugins = PlayerView(state, { ...opts, playerID: '0' });
139+
expect(state.plugins.random.data).not.toBeDefined();
140+
expect(state.G.die).not.toBeDefined();
141+
state = reducer(state, makeMove('rollDie'));
142+
expect(state.G.die).not.toBeDefined();
143+
});
144+
126145
test('turn.onBegin has ctx APIs at the beginning of the game', () => {
127146
let random = null;
128147
let events = null;

src/plugins/random/random.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,12 +60,12 @@ export class Random {
6060
* constructor
6161
* @param {object} ctx - The ctx object to initialize from.
6262
*/
63-
constructor(state: RandomState) {
63+
constructor(state?: RandomState) {
6464
// If we are on the client, the seed is not present.
6565
// Just use a temporary seed to execute the move without
6666
// crashing it. The move state itself is discarded,
6767
// so the actual value doesn't matter.
68-
this.state = state;
68+
this.state = state || { seed: '0' };
6969
this.used = false;
7070
}
7171

0 commit comments

Comments
 (0)