@mnbroatch/boardgame.io 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +102 -0
- package/ai/package.json +7 -0
- package/client/package.json +7 -0
- package/core/package.json +7 -0
- package/debug/package.json +7 -0
- package/dist/boardgameio.es.js +14238 -0
- package/dist/boardgameio.js +14277 -0
- package/dist/boardgameio.min.js +16 -0
- package/dist/cjs/Debug-9d141c06.js +9586 -0
- package/dist/cjs/ai-e0e8a768.js +377 -0
- package/dist/cjs/ai.js +20 -0
- package/dist/cjs/client-76dec77b.js +258 -0
- package/dist/cjs/client-a22d7500.js +524 -0
- package/dist/cjs/client.js +26 -0
- package/dist/cjs/core.js +52 -0
- package/dist/cjs/debug.js +18 -0
- package/dist/cjs/filter-player-view-bb02e2f6.js +89 -0
- package/dist/cjs/initialize-267fcd69.js +61 -0
- package/dist/cjs/internal.js +25 -0
- package/dist/cjs/master-2904879d.js +320 -0
- package/dist/cjs/master.js +18 -0
- package/dist/cjs/multiplayer.js +23 -0
- package/dist/cjs/plugin-random-7425844d.js +229 -0
- package/dist/cjs/plugins.js +59 -0
- package/dist/cjs/react-native.js +182 -0
- package/dist/cjs/react.js +727 -0
- package/dist/cjs/reducer-16eec232.js +1203 -0
- package/dist/cjs/server.js +4087 -0
- package/dist/cjs/socketio-7a0837eb.js +478 -0
- package/dist/cjs/testing.js +30 -0
- package/dist/cjs/transport-b1874dfa.js +37 -0
- package/dist/cjs/turn-order-b2ff8740.js +1136 -0
- package/dist/cjs/util-fcfd8fb8.js +140 -0
- package/dist/esm/Debug-0141fe2d.js +9577 -0
- package/dist/esm/ai-5c06e761.js +371 -0
- package/dist/esm/ai.js +8 -0
- package/dist/esm/client-2e653027.js +522 -0
- package/dist/esm/client-5f57c3f2.js +255 -0
- package/dist/esm/client.js +16 -0
- package/dist/esm/core.js +40 -0
- package/dist/esm/debug.js +10 -0
- package/dist/esm/filter-player-view-2c6cc96f.js +87 -0
- package/dist/esm/initialize-11d626ca.js +59 -0
- package/dist/esm/internal.js +10 -0
- package/dist/esm/master-fa8f2e43.js +318 -0
- package/dist/esm/master.js +10 -0
- package/dist/esm/multiplayer.js +14 -0
- package/dist/esm/plugin-random-087f861e.js +226 -0
- package/dist/esm/plugins.js +55 -0
- package/dist/esm/react-native.js +173 -0
- package/dist/esm/react.js +716 -0
- package/dist/esm/reducer-c46da7e5.js +1198 -0
- package/dist/esm/socketio-c22ffa65.js +455 -0
- package/dist/esm/testing.js +26 -0
- package/dist/esm/transport-ce07b771.js +35 -0
- package/dist/esm/turn-order-376d315e.js +1091 -0
- package/dist/esm/util-b6147cef.js +135 -0
- package/dist/types/packages/ai.d.ts +5 -0
- package/dist/types/packages/client.d.ts +3 -0
- package/dist/types/packages/core.d.ts +5 -0
- package/dist/types/packages/debug.d.ts +2 -0
- package/dist/types/packages/internal.d.ts +8 -0
- package/dist/types/packages/master.d.ts +2 -0
- package/dist/types/packages/multiplayer.d.ts +3 -0
- package/dist/types/packages/plugins.d.ts +3 -0
- package/dist/types/packages/react-native.d.ts +2 -0
- package/dist/types/packages/react.d.ts +3 -0
- package/dist/types/packages/server.d.ts +6 -0
- package/dist/types/packages/testing.d.ts +1 -0
- package/dist/types/src/ai/ai.d.ts +53 -0
- package/dist/types/src/ai/ai.test.d.ts +1 -0
- package/dist/types/src/ai/bot.d.ts +40 -0
- package/dist/types/src/ai/mcts-bot.d.ts +60 -0
- package/dist/types/src/ai/random-bot.d.ts +27 -0
- package/dist/types/src/client/client.d.ts +104 -0
- package/dist/types/src/client/client.test.d.ts +1 -0
- package/dist/types/src/client/debug/tests/debug.test.d.ts +1 -0
- package/dist/types/src/client/manager.d.ts +61 -0
- package/dist/types/src/client/react.d.ts +75 -0
- package/dist/types/src/client/react.ssr.test.d.ts +4 -0
- package/dist/types/src/client/react.test.d.ts +1 -0
- package/dist/types/src/client/transport/dummy.d.ts +18 -0
- package/dist/types/src/client/transport/local.d.ts +59 -0
- package/dist/types/src/client/transport/local.test.d.ts +1 -0
- package/dist/types/src/client/transport/socketio.d.ts +45 -0
- package/dist/types/src/client/transport/socketio.test.d.ts +1 -0
- package/dist/types/src/client/transport/transport.d.ts +50 -0
- package/dist/types/src/client/transport/transport.test.d.ts +1 -0
- package/dist/types/src/core/action-creators.d.ts +144 -0
- package/dist/types/src/core/action-types.d.ts +10 -0
- package/dist/types/src/core/backwards-compatibility.d.ts +12 -0
- package/dist/types/src/core/constants.d.ts +6 -0
- package/dist/types/src/core/errors.d.ts +15 -0
- package/dist/types/src/core/flow.d.ts +28 -0
- package/dist/types/src/core/flow.test.d.ts +1 -0
- package/dist/types/src/core/game-methods.d.ts +9 -0
- package/dist/types/src/core/game.d.ts +26 -0
- package/dist/types/src/core/game.test.d.ts +1 -0
- package/dist/types/src/core/initialize.d.ts +9 -0
- package/dist/types/src/core/logger.d.ts +2 -0
- package/dist/types/src/core/player-view.d.ts +7 -0
- package/dist/types/src/core/player-view.test.d.ts +1 -0
- package/dist/types/src/core/reducer.d.ts +155 -0
- package/dist/types/src/core/reducer.test.d.ts +1 -0
- package/dist/types/src/core/turn-order.d.ts +179 -0
- package/dist/types/src/core/turn-order.test.d.ts +8 -0
- package/dist/types/src/lobby/client.d.ts +194 -0
- package/dist/types/src/lobby/client.test.d.ts +1 -0
- package/dist/types/src/lobby/connection.d.ts +44 -0
- package/dist/types/src/lobby/connection.test.d.ts +1 -0
- package/dist/types/src/lobby/create-match-form.d.ts +26 -0
- package/dist/types/src/lobby/login-form.d.ts +23 -0
- package/dist/types/src/lobby/match-instance.d.ts +31 -0
- package/dist/types/src/lobby/react.d.ts +113 -0
- package/dist/types/src/lobby/react.ssr.test.d.ts +4 -0
- package/dist/types/src/lobby/react.test.d.ts +1 -0
- package/dist/types/src/master/filter-player-view.d.ts +96 -0
- package/dist/types/src/master/filter-player-view.test.d.ts +1 -0
- package/dist/types/src/master/master.d.ts +94 -0
- package/dist/types/src/master/master.test.d.ts +1 -0
- package/dist/types/src/plugins/events/events.d.ts +54 -0
- package/dist/types/src/plugins/events/events.test.d.ts +1 -0
- package/dist/types/src/plugins/main.d.ts +75 -0
- package/dist/types/src/plugins/main.test.d.ts +1 -0
- package/dist/types/src/plugins/plugin-events.d.ts +5 -0
- package/dist/types/src/plugins/plugin-immer.d.ts +7 -0
- package/dist/types/src/plugins/plugin-immer.test.d.ts +1 -0
- package/dist/types/src/plugins/plugin-log.d.ts +14 -0
- package/dist/types/src/plugins/plugin-log.test.d.ts +1 -0
- package/dist/types/src/plugins/plugin-player.d.ts +29 -0
- package/dist/types/src/plugins/plugin-player.test.d.ts +1 -0
- package/dist/types/src/plugins/plugin-random.d.ts +4 -0
- package/dist/types/src/plugins/plugin-serializable.d.ts +7 -0
- package/dist/types/src/plugins/plugin-serializable.test.d.ts +1 -0
- package/dist/types/src/plugins/random/random.alea.d.ts +19 -0
- package/dist/types/src/plugins/random/random.d.ts +54 -0
- package/dist/types/src/plugins/random/random.test.d.ts +1 -0
- package/dist/types/src/server/api.d.ts +13 -0
- package/dist/types/src/server/api.test.d.ts +1 -0
- package/dist/types/src/server/auth.d.ts +38 -0
- package/dist/types/src/server/auth.test.d.ts +1 -0
- package/dist/types/src/server/cors.d.ts +4 -0
- package/dist/types/src/server/cors.test.d.ts +1 -0
- package/dist/types/src/server/db/base.d.ts +192 -0
- package/dist/types/src/server/db/flatfile.d.ts +44 -0
- package/dist/types/src/server/db/flatfile.test.d.ts +1 -0
- package/dist/types/src/server/db/index.d.ts +4 -0
- package/dist/types/src/server/db/index.test.d.ts +1 -0
- package/dist/types/src/server/db/inmemory.d.ts +43 -0
- package/dist/types/src/server/db/inmemory.test.d.ts +1 -0
- package/dist/types/src/server/db/localstorage.d.ts +7 -0
- package/dist/types/src/server/db/localstorage.test.d.ts +1 -0
- package/dist/types/src/server/index.d.ts +68 -0
- package/dist/types/src/server/index.test.d.ts +1 -0
- package/dist/types/src/server/transport/pubsub/generic-pub-sub.d.ts +6 -0
- package/dist/types/src/server/transport/pubsub/in-memory-pub-sub.d.ts +7 -0
- package/dist/types/src/server/transport/pubsub/in-memory-pub-sub.test.d.ts +1 -0
- package/dist/types/src/server/transport/socketio-simultaneous.test.d.ts +1 -0
- package/dist/types/src/server/transport/socketio.d.ts +65 -0
- package/dist/types/src/server/transport/socketio.test.d.ts +1 -0
- package/dist/types/src/server/util.d.ts +35 -0
- package/dist/types/src/testing/mock-random.d.ts +15 -0
- package/dist/types/src/testing/mock-random.test.d.ts +1 -0
- package/dist/types/src/types.d.ts +387 -0
- package/internal/package.json +7 -0
- package/master/package.json +7 -0
- package/multiplayer/package.json +7 -0
- package/package.json +211 -0
- package/plugins/package.json +7 -0
- package/react/package.json +7 -0
- package/react-native/package.json +7 -0
- package/server/package.json +6 -0
- package/src/ai/ai.test.ts +433 -0
- package/src/ai/ai.ts +84 -0
- package/src/ai/bot.ts +122 -0
- package/src/ai/mcts-bot.ts +331 -0
- package/src/ai/random-bot.ts +20 -0
- package/src/client/client.test.ts +993 -0
- package/src/client/client.ts +588 -0
- package/src/client/debug/Debug.svelte +239 -0
- package/src/client/debug/Menu.svelte +65 -0
- package/src/client/debug/ai/AI.svelte +215 -0
- package/src/client/debug/ai/Options.svelte +48 -0
- package/src/client/debug/info/Info.svelte +22 -0
- package/src/client/debug/info/Item.svelte +24 -0
- package/src/client/debug/log/Log.svelte +157 -0
- package/src/client/debug/log/LogEvent.svelte +149 -0
- package/src/client/debug/log/LogMetadata.svelte +7 -0
- package/src/client/debug/log/PhaseMarker.svelte +27 -0
- package/src/client/debug/log/TurnMarker.svelte +23 -0
- package/src/client/debug/main/ClientSwitcher.svelte +59 -0
- package/src/client/debug/main/Controls.svelte +58 -0
- package/src/client/debug/main/Hotkey.svelte +84 -0
- package/src/client/debug/main/InteractiveFunction.svelte +85 -0
- package/src/client/debug/main/Main.svelte +121 -0
- package/src/client/debug/main/Move.svelte +68 -0
- package/src/client/debug/main/PlayerInfo.svelte +70 -0
- package/src/client/debug/mcts/Action.svelte +22 -0
- package/src/client/debug/mcts/MCTS.svelte +78 -0
- package/src/client/debug/mcts/Table.svelte +98 -0
- package/src/client/debug/tests/JSONTree.mock.svelte +3 -0
- package/src/client/debug/tests/debug.test.ts +183 -0
- package/src/client/debug/utils/shortcuts.js +50 -0
- package/src/client/debug/utils/shortcuts.test.js +49 -0
- package/src/client/manager.ts +177 -0
- package/src/client/react-native.js +136 -0
- package/src/client/react-native.test.js +229 -0
- package/src/client/react.ssr.test.tsx +24 -0
- package/src/client/react.test.tsx +213 -0
- package/src/client/react.tsx +192 -0
- package/src/client/transport/dummy.ts +19 -0
- package/src/client/transport/local.test.ts +353 -0
- package/src/client/transport/local.ts +230 -0
- package/src/client/transport/socketio.test.ts +328 -0
- package/src/client/transport/socketio.ts +210 -0
- package/src/client/transport/transport.test.ts +27 -0
- package/src/client/transport/transport.ts +95 -0
- package/src/core/action-creators.ts +159 -0
- package/src/core/action-types.ts +18 -0
- package/src/core/backwards-compatibility.ts +23 -0
- package/src/core/constants.ts +6 -0
- package/src/core/errors.ts +35 -0
- package/src/core/flow.test.ts +2433 -0
- package/src/core/flow.ts +897 -0
- package/src/core/game-methods.ts +9 -0
- package/src/core/game.test.ts +286 -0
- package/src/core/game.ts +114 -0
- package/src/core/initialize.ts +77 -0
- package/src/core/logger.test.js +90 -0
- package/src/core/logger.ts +18 -0
- package/src/core/player-view.test.ts +50 -0
- package/src/core/player-view.ts +39 -0
- package/src/core/reducer.test.ts +991 -0
- package/src/core/reducer.ts +532 -0
- package/src/core/turn-order.test.ts +1123 -0
- package/src/core/turn-order.ts +473 -0
- package/src/lobby/client.test.ts +385 -0
- package/src/lobby/client.ts +358 -0
- package/src/lobby/connection.test.ts +207 -0
- package/src/lobby/connection.ts +162 -0
- package/src/lobby/create-match-form.tsx +122 -0
- package/src/lobby/login-form.tsx +75 -0
- package/src/lobby/match-instance.tsx +135 -0
- package/src/lobby/react.ssr.test.tsx +22 -0
- package/src/lobby/react.test.tsx +594 -0
- package/src/lobby/react.tsx +402 -0
- package/src/master/filter-player-view.test.ts +381 -0
- package/src/master/filter-player-view.ts +102 -0
- package/src/master/master.test.ts +1068 -0
- package/src/master/master.ts +492 -0
- package/src/plugins/events/events.test.ts +108 -0
- package/src/plugins/events/events.ts +209 -0
- package/src/plugins/main.test.ts +411 -0
- package/src/plugins/main.ts +314 -0
- package/src/plugins/plugin-events.ts +40 -0
- package/src/plugins/plugin-immer.test.ts +86 -0
- package/src/plugins/plugin-immer.ts +37 -0
- package/src/plugins/plugin-log.test.ts +37 -0
- package/src/plugins/plugin-log.ts +40 -0
- package/src/plugins/plugin-player.test.ts +172 -0
- package/src/plugins/plugin-player.ts +100 -0
- package/src/plugins/plugin-random.ts +40 -0
- package/src/plugins/plugin-serializable.test.ts +40 -0
- package/src/plugins/plugin-serializable.ts +55 -0
- package/src/plugins/random/random.alea.ts +109 -0
- package/src/plugins/random/random.test.ts +167 -0
- package/src/plugins/random/random.ts +198 -0
- package/src/server/api.test.ts +1699 -0
- package/src/server/api.ts +527 -0
- package/src/server/auth.test.ts +275 -0
- package/src/server/auth.ts +89 -0
- package/src/server/cors.test.ts +121 -0
- package/src/server/cors.ts +7 -0
- package/src/server/db/base.ts +296 -0
- package/src/server/db/flatfile.test.ts +221 -0
- package/src/server/db/flatfile.ts +228 -0
- package/src/server/db/index.test.ts +8 -0
- package/src/server/db/index.ts +12 -0
- package/src/server/db/inmemory.test.ts +143 -0
- package/src/server/db/inmemory.ts +143 -0
- package/src/server/db/localstorage.test.ts +73 -0
- package/src/server/db/localstorage.ts +44 -0
- package/src/server/index.test.ts +265 -0
- package/src/server/index.ts +175 -0
- package/src/server/transport/pubsub/generic-pub-sub.ts +11 -0
- package/src/server/transport/pubsub/in-memory-pub-sub.test.ts +47 -0
- package/src/server/transport/pubsub/in-memory-pub-sub.ts +28 -0
- package/src/server/transport/socketio-simultaneous.test.ts +603 -0
- package/src/server/transport/socketio.test.ts +303 -0
- package/src/server/transport/socketio.ts +279 -0
- package/src/server/util.ts +85 -0
- package/src/testing/mock-random.test.ts +45 -0
- package/src/testing/mock-random.ts +27 -0
- package/src/types.ts +511 -0
- package/testing/package.json +7 -0
|
@@ -0,0 +1,1091 @@
|
|
|
1
|
+
import produce from 'immer';
|
|
2
|
+
import { R as RandomPlugin } from './plugin-random-087f861e.js';
|
|
3
|
+
import isPlainObject from 'lodash.isplainobject';
|
|
4
|
+
|
|
5
|
+
/*
|
|
6
|
+
* Copyright 2017 The boardgame.io Authors
|
|
7
|
+
*
|
|
8
|
+
* Use of this source code is governed by a MIT-style
|
|
9
|
+
* license that can be found in the LICENSE file or at
|
|
10
|
+
* https://opensource.org/licenses/MIT.
|
|
11
|
+
*/
|
|
12
|
+
const MAKE_MOVE = 'MAKE_MOVE';
|
|
13
|
+
const GAME_EVENT = 'GAME_EVENT';
|
|
14
|
+
const REDO = 'REDO';
|
|
15
|
+
const RESET = 'RESET';
|
|
16
|
+
const SYNC = 'SYNC';
|
|
17
|
+
const UNDO = 'UNDO';
|
|
18
|
+
const UPDATE = 'UPDATE';
|
|
19
|
+
const PATCH = 'PATCH';
|
|
20
|
+
const PLUGIN = 'PLUGIN';
|
|
21
|
+
const STRIP_TRANSIENTS = 'STRIP_TRANSIENTS';
|
|
22
|
+
|
|
23
|
+
/*
|
|
24
|
+
* Copyright 2017 The boardgame.io Authors
|
|
25
|
+
*
|
|
26
|
+
* Use of this source code is governed by a MIT-style
|
|
27
|
+
* license that can be found in the LICENSE file or at
|
|
28
|
+
* https://opensource.org/licenses/MIT.
|
|
29
|
+
*/
|
|
30
|
+
/**
|
|
31
|
+
* Generate a move to be dispatched to the game move reducer.
|
|
32
|
+
*
|
|
33
|
+
* @param {string} type - The move type.
|
|
34
|
+
* @param {Array} args - Additional arguments.
|
|
35
|
+
* @param {string} playerID - The ID of the player making this action.
|
|
36
|
+
* @param {string} credentials - (optional) The credentials for the player making this action.
|
|
37
|
+
*/
|
|
38
|
+
const makeMove = (type, args, playerID, credentials) => ({
|
|
39
|
+
type: MAKE_MOVE,
|
|
40
|
+
payload: { type, args, playerID, credentials },
|
|
41
|
+
});
|
|
42
|
+
/**
|
|
43
|
+
* Generate a game event to be dispatched to the flow reducer.
|
|
44
|
+
*
|
|
45
|
+
* @param {string} type - The event type.
|
|
46
|
+
* @param {Array} args - Additional arguments.
|
|
47
|
+
* @param {string} playerID - The ID of the player making this action.
|
|
48
|
+
* @param {string} credentials - (optional) The credentials for the player making this action.
|
|
49
|
+
*/
|
|
50
|
+
const gameEvent = (type, args, playerID, credentials) => ({
|
|
51
|
+
type: GAME_EVENT,
|
|
52
|
+
payload: { type, args, playerID, credentials },
|
|
53
|
+
});
|
|
54
|
+
/**
|
|
55
|
+
* Generate an automatic game event that is a side-effect of a move.
|
|
56
|
+
* @param {string} type - The event type.
|
|
57
|
+
* @param {Array} args - Additional arguments.
|
|
58
|
+
* @param {string} playerID - The ID of the player making this action.
|
|
59
|
+
* @param {string} credentials - (optional) The credentials for the player making this action.
|
|
60
|
+
*/
|
|
61
|
+
const automaticGameEvent = (type, args, playerID, credentials) => ({
|
|
62
|
+
type: GAME_EVENT,
|
|
63
|
+
payload: { type, args, playerID, credentials },
|
|
64
|
+
automatic: true,
|
|
65
|
+
});
|
|
66
|
+
const sync = (info) => ({
|
|
67
|
+
type: SYNC,
|
|
68
|
+
state: info.state,
|
|
69
|
+
log: info.log,
|
|
70
|
+
initialState: info.initialState,
|
|
71
|
+
clientOnly: true,
|
|
72
|
+
});
|
|
73
|
+
/**
|
|
74
|
+
* Used to update the Redux store's state with patch in response to
|
|
75
|
+
* an action coming from another player.
|
|
76
|
+
* @param prevStateID previous stateID
|
|
77
|
+
* @param stateID stateID after this patch
|
|
78
|
+
* @param {Operation[]} patch - The patch to apply.
|
|
79
|
+
* @param {LogEntry[]} deltalog - A log delta.
|
|
80
|
+
*/
|
|
81
|
+
const patch = (prevStateID, stateID, patch, deltalog) => ({
|
|
82
|
+
type: PATCH,
|
|
83
|
+
prevStateID,
|
|
84
|
+
stateID,
|
|
85
|
+
patch,
|
|
86
|
+
deltalog,
|
|
87
|
+
clientOnly: true,
|
|
88
|
+
});
|
|
89
|
+
/**
|
|
90
|
+
* Used to update the Redux store's state in response to
|
|
91
|
+
* an action coming from another player.
|
|
92
|
+
* @param {object} state - The state to restore.
|
|
93
|
+
* @param {Array} deltalog - A log delta.
|
|
94
|
+
*/
|
|
95
|
+
const update = (state, deltalog) => ({
|
|
96
|
+
type: UPDATE,
|
|
97
|
+
state,
|
|
98
|
+
deltalog,
|
|
99
|
+
clientOnly: true,
|
|
100
|
+
});
|
|
101
|
+
/**
|
|
102
|
+
* Used to reset the game state.
|
|
103
|
+
* @param {object} state - The initial state.
|
|
104
|
+
*/
|
|
105
|
+
const reset = (state) => ({
|
|
106
|
+
type: RESET,
|
|
107
|
+
state,
|
|
108
|
+
clientOnly: true,
|
|
109
|
+
});
|
|
110
|
+
/**
|
|
111
|
+
* Used to undo the last move.
|
|
112
|
+
* @param {string} playerID - The ID of the player making this action.
|
|
113
|
+
* @param {string} credentials - (optional) The credentials for the player making this action.
|
|
114
|
+
*/
|
|
115
|
+
const undo = (playerID, credentials) => ({
|
|
116
|
+
type: UNDO,
|
|
117
|
+
payload: { type: null, args: null, playerID, credentials },
|
|
118
|
+
});
|
|
119
|
+
/**
|
|
120
|
+
* Used to redo the last undone move.
|
|
121
|
+
* @param {string} playerID - The ID of the player making this action.
|
|
122
|
+
* @param {string} credentials - (optional) The credentials for the player making this action.
|
|
123
|
+
*/
|
|
124
|
+
const redo = (playerID, credentials) => ({
|
|
125
|
+
type: REDO,
|
|
126
|
+
payload: { type: null, args: null, playerID, credentials },
|
|
127
|
+
});
|
|
128
|
+
/**
|
|
129
|
+
* Allows plugins to define their own actions and intercept them.
|
|
130
|
+
*/
|
|
131
|
+
const plugin = (type, args, playerID, credentials) => ({
|
|
132
|
+
type: PLUGIN,
|
|
133
|
+
payload: { type, args, playerID, credentials },
|
|
134
|
+
});
|
|
135
|
+
/**
|
|
136
|
+
* Private action used to strip transient metadata (e.g. errors) from the game
|
|
137
|
+
* state.
|
|
138
|
+
*/
|
|
139
|
+
const stripTransients = () => ({
|
|
140
|
+
type: STRIP_TRANSIENTS,
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
var ActionCreators = /*#__PURE__*/Object.freeze({
|
|
144
|
+
__proto__: null,
|
|
145
|
+
makeMove: makeMove,
|
|
146
|
+
gameEvent: gameEvent,
|
|
147
|
+
automaticGameEvent: automaticGameEvent,
|
|
148
|
+
sync: sync,
|
|
149
|
+
patch: patch,
|
|
150
|
+
update: update,
|
|
151
|
+
reset: reset,
|
|
152
|
+
undo: undo,
|
|
153
|
+
redo: redo,
|
|
154
|
+
plugin: plugin,
|
|
155
|
+
stripTransients: stripTransients
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Moves can return this when they want to indicate
|
|
160
|
+
* that the combination of arguments is illegal and
|
|
161
|
+
* the move ought to be discarded.
|
|
162
|
+
*/
|
|
163
|
+
const INVALID_MOVE = 'INVALID_MOVE';
|
|
164
|
+
|
|
165
|
+
/*
|
|
166
|
+
* Copyright 2018 The boardgame.io Authors
|
|
167
|
+
*
|
|
168
|
+
* Use of this source code is governed by a MIT-style
|
|
169
|
+
* license that can be found in the LICENSE file or at
|
|
170
|
+
* https://opensource.org/licenses/MIT.
|
|
171
|
+
*/
|
|
172
|
+
/**
|
|
173
|
+
* Plugin that allows using Immer to make immutable changes
|
|
174
|
+
* to G by just mutating it.
|
|
175
|
+
*/
|
|
176
|
+
const ImmerPlugin = {
|
|
177
|
+
name: 'plugin-immer',
|
|
178
|
+
fnWrap: (move) => (context, ...args) => {
|
|
179
|
+
let isInvalid = false;
|
|
180
|
+
const newG = produce(context.G, (G) => {
|
|
181
|
+
const result = move({ ...context, G }, ...args);
|
|
182
|
+
if (result === INVALID_MOVE) {
|
|
183
|
+
isInvalid = true;
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
return result;
|
|
187
|
+
});
|
|
188
|
+
if (isInvalid)
|
|
189
|
+
return INVALID_MOVE;
|
|
190
|
+
return newG;
|
|
191
|
+
},
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
var GameMethod;
|
|
195
|
+
(function (GameMethod) {
|
|
196
|
+
GameMethod["MOVE"] = "MOVE";
|
|
197
|
+
GameMethod["GAME_ON_END"] = "GAME_ON_END";
|
|
198
|
+
GameMethod["PHASE_ON_BEGIN"] = "PHASE_ON_BEGIN";
|
|
199
|
+
GameMethod["PHASE_ON_END"] = "PHASE_ON_END";
|
|
200
|
+
GameMethod["TURN_ON_BEGIN"] = "TURN_ON_BEGIN";
|
|
201
|
+
GameMethod["TURN_ON_MOVE"] = "TURN_ON_MOVE";
|
|
202
|
+
GameMethod["TURN_ON_END"] = "TURN_ON_END";
|
|
203
|
+
})(GameMethod || (GameMethod = {}));
|
|
204
|
+
|
|
205
|
+
/*
|
|
206
|
+
* Copyright 2018 The boardgame.io Authors
|
|
207
|
+
*
|
|
208
|
+
* Use of this source code is governed by a MIT-style
|
|
209
|
+
* license that can be found in the LICENSE file or at
|
|
210
|
+
* https://opensource.org/licenses/MIT.
|
|
211
|
+
*/
|
|
212
|
+
var Errors;
|
|
213
|
+
(function (Errors) {
|
|
214
|
+
Errors["CalledOutsideHook"] = "Events must be called from moves or the `onBegin`, `onEnd`, and `onMove` hooks.\nThis error probably means you called an event from other game code, like an `endIf` trigger or one of the `turn.order` methods.";
|
|
215
|
+
Errors["EndTurnInOnEnd"] = "`endTurn` is disallowed in `onEnd` hooks \u2014 the turn is already ending.";
|
|
216
|
+
Errors["MaxTurnEndings"] = "Maximum number of turn endings exceeded for this update.\nThis likely means game code is triggering an infinite loop.";
|
|
217
|
+
Errors["PhaseEventInOnEnd"] = "`setPhase` & `endPhase` are disallowed in a phase\u2019s `onEnd` hook \u2014 the phase is already ending.\nIf you\u2019re trying to dynamically choose the next phase when a phase ends, use the phase\u2019s `next` trigger.";
|
|
218
|
+
Errors["StageEventInOnEnd"] = "`setStage`, `endStage` & `setActivePlayers` are disallowed in `onEnd` hooks.";
|
|
219
|
+
Errors["StageEventInPhaseBegin"] = "`setStage`, `endStage` & `setActivePlayers` are disallowed in a phase\u2019s `onBegin` hook.\nUse `setActivePlayers` in a `turn.onBegin` hook or declare stages with `turn.activePlayers` instead.";
|
|
220
|
+
Errors["StageEventInTurnBegin"] = "`setStage` & `endStage` are disallowed in `turn.onBegin`.\nUse `setActivePlayers` or declare stages with `turn.activePlayers` instead.";
|
|
221
|
+
})(Errors || (Errors = {}));
|
|
222
|
+
/**
|
|
223
|
+
* Events
|
|
224
|
+
*/
|
|
225
|
+
class Events {
|
|
226
|
+
constructor(flow, ctx, playerID) {
|
|
227
|
+
this.flow = flow;
|
|
228
|
+
this.playerID = playerID;
|
|
229
|
+
this.dispatch = [];
|
|
230
|
+
this.initialTurn = ctx.turn;
|
|
231
|
+
this.updateTurnContext(ctx, undefined);
|
|
232
|
+
// This is an arbitrarily large upper threshold, which could be made
|
|
233
|
+
// configurable via a game option if the need arises.
|
|
234
|
+
this.maxEndedTurnsPerAction = ctx.numPlayers * 100;
|
|
235
|
+
}
|
|
236
|
+
api() {
|
|
237
|
+
const events = {
|
|
238
|
+
_private: this,
|
|
239
|
+
};
|
|
240
|
+
for (const type of this.flow.eventNames) {
|
|
241
|
+
events[type] = (...args) => {
|
|
242
|
+
this.dispatch.push({
|
|
243
|
+
type,
|
|
244
|
+
args,
|
|
245
|
+
phase: this.currentPhase,
|
|
246
|
+
turn: this.currentTurn,
|
|
247
|
+
calledFrom: this.currentMethod,
|
|
248
|
+
// Used to capture a stack trace in case it is needed later.
|
|
249
|
+
error: new Error('Events Plugin Error'),
|
|
250
|
+
});
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
return events;
|
|
254
|
+
}
|
|
255
|
+
isUsed() {
|
|
256
|
+
return this.dispatch.length > 0;
|
|
257
|
+
}
|
|
258
|
+
updateTurnContext(ctx, methodType) {
|
|
259
|
+
this.currentPhase = ctx.phase;
|
|
260
|
+
this.currentTurn = ctx.turn;
|
|
261
|
+
this.currentMethod = methodType;
|
|
262
|
+
}
|
|
263
|
+
unsetCurrentMethod() {
|
|
264
|
+
this.currentMethod = undefined;
|
|
265
|
+
}
|
|
266
|
+
/**
|
|
267
|
+
* Updates ctx with the triggered events.
|
|
268
|
+
* @param {object} state - The state object { G, ctx }.
|
|
269
|
+
*/
|
|
270
|
+
update(state) {
|
|
271
|
+
const initialState = state;
|
|
272
|
+
const stateWithError = ({ stack }, message) => ({
|
|
273
|
+
...initialState,
|
|
274
|
+
plugins: {
|
|
275
|
+
...initialState.plugins,
|
|
276
|
+
events: {
|
|
277
|
+
...initialState.plugins.events,
|
|
278
|
+
data: { error: message + '\n' + stack },
|
|
279
|
+
},
|
|
280
|
+
},
|
|
281
|
+
});
|
|
282
|
+
EventQueue: for (let i = 0; i < this.dispatch.length; i++) {
|
|
283
|
+
const event = this.dispatch[i];
|
|
284
|
+
const turnHasEnded = event.turn !== state.ctx.turn;
|
|
285
|
+
// This protects against potential infinite loops if specific events are called on hooks.
|
|
286
|
+
// The moment we exceed the defined threshold, we just bail out of all phases.
|
|
287
|
+
const endedTurns = this.currentTurn - this.initialTurn;
|
|
288
|
+
if (endedTurns >= this.maxEndedTurnsPerAction) {
|
|
289
|
+
return stateWithError(event.error, Errors.MaxTurnEndings);
|
|
290
|
+
}
|
|
291
|
+
if (event.calledFrom === undefined) {
|
|
292
|
+
return stateWithError(event.error, Errors.CalledOutsideHook);
|
|
293
|
+
}
|
|
294
|
+
// Stop processing events once the game has finished.
|
|
295
|
+
if (state.ctx.gameover)
|
|
296
|
+
break EventQueue;
|
|
297
|
+
switch (event.type) {
|
|
298
|
+
case 'endStage':
|
|
299
|
+
case 'setStage':
|
|
300
|
+
case 'setActivePlayers': {
|
|
301
|
+
switch (event.calledFrom) {
|
|
302
|
+
// Disallow all stage events in onEnd and phase.onBegin hooks.
|
|
303
|
+
case GameMethod.TURN_ON_END:
|
|
304
|
+
case GameMethod.PHASE_ON_END:
|
|
305
|
+
return stateWithError(event.error, Errors.StageEventInOnEnd);
|
|
306
|
+
case GameMethod.PHASE_ON_BEGIN:
|
|
307
|
+
return stateWithError(event.error, Errors.StageEventInPhaseBegin);
|
|
308
|
+
// Disallow setStage & endStage in turn.onBegin hooks.
|
|
309
|
+
case GameMethod.TURN_ON_BEGIN:
|
|
310
|
+
if (event.type === 'setActivePlayers')
|
|
311
|
+
break;
|
|
312
|
+
return stateWithError(event.error, Errors.StageEventInTurnBegin);
|
|
313
|
+
}
|
|
314
|
+
// If the turn already ended, don't try to process stage events.
|
|
315
|
+
if (turnHasEnded)
|
|
316
|
+
continue EventQueue;
|
|
317
|
+
break;
|
|
318
|
+
}
|
|
319
|
+
case 'endTurn': {
|
|
320
|
+
if (event.calledFrom === GameMethod.TURN_ON_END ||
|
|
321
|
+
event.calledFrom === GameMethod.PHASE_ON_END) {
|
|
322
|
+
return stateWithError(event.error, Errors.EndTurnInOnEnd);
|
|
323
|
+
}
|
|
324
|
+
// If the turn already ended some other way,
|
|
325
|
+
// don't try to end the turn again.
|
|
326
|
+
if (turnHasEnded)
|
|
327
|
+
continue EventQueue;
|
|
328
|
+
break;
|
|
329
|
+
}
|
|
330
|
+
case 'endPhase':
|
|
331
|
+
case 'setPhase': {
|
|
332
|
+
if (event.calledFrom === GameMethod.PHASE_ON_END) {
|
|
333
|
+
return stateWithError(event.error, Errors.PhaseEventInOnEnd);
|
|
334
|
+
}
|
|
335
|
+
// If the phase already ended some other way,
|
|
336
|
+
// don't try to end the phase again.
|
|
337
|
+
if (event.phase !== state.ctx.phase)
|
|
338
|
+
continue EventQueue;
|
|
339
|
+
break;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
const action = automaticGameEvent(event.type, event.args, this.playerID);
|
|
343
|
+
state = this.flow.processEvent(state, action);
|
|
344
|
+
}
|
|
345
|
+
return state;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/*
|
|
350
|
+
* Copyright 2020 The boardgame.io Authors
|
|
351
|
+
*
|
|
352
|
+
* Use of this source code is governed by a MIT-style
|
|
353
|
+
* license that can be found in the LICENSE file or at
|
|
354
|
+
* https://opensource.org/licenses/MIT.
|
|
355
|
+
*/
|
|
356
|
+
const EventsPlugin = {
|
|
357
|
+
name: 'events',
|
|
358
|
+
noClient: ({ api }) => api._private.isUsed(),
|
|
359
|
+
isInvalid: ({ data }) => data.error || false,
|
|
360
|
+
// Update the events plugin’s internal turn context each time a move
|
|
361
|
+
// or hook is called. This allows events called after turn or phase
|
|
362
|
+
// endings to dispatch the current turn and phase correctly.
|
|
363
|
+
fnWrap: (method, methodType) => (context, ...args) => {
|
|
364
|
+
const api = context.events;
|
|
365
|
+
if (api)
|
|
366
|
+
api._private.updateTurnContext(context.ctx, methodType);
|
|
367
|
+
const G = method(context, ...args);
|
|
368
|
+
if (api)
|
|
369
|
+
api._private.unsetCurrentMethod();
|
|
370
|
+
return G;
|
|
371
|
+
},
|
|
372
|
+
dangerouslyFlushRawState: ({ state, api }) => api._private.update(state),
|
|
373
|
+
api: ({ game, ctx, playerID }) => new Events(game.flow, ctx, playerID).api(),
|
|
374
|
+
};
|
|
375
|
+
|
|
376
|
+
/*
|
|
377
|
+
* Copyright 2018 The boardgame.io Authors
|
|
378
|
+
*
|
|
379
|
+
* Use of this source code is governed by a MIT-style
|
|
380
|
+
* license that can be found in the LICENSE file or at
|
|
381
|
+
* https://opensource.org/licenses/MIT.
|
|
382
|
+
*/
|
|
383
|
+
/**
|
|
384
|
+
* Plugin that makes it possible to add metadata to log entries.
|
|
385
|
+
* During a move, you can set metadata using ctx.log.setMetadata and it will be
|
|
386
|
+
* available on the log entry for that move.
|
|
387
|
+
*/
|
|
388
|
+
const LogPlugin = {
|
|
389
|
+
name: 'log',
|
|
390
|
+
flush: () => ({}),
|
|
391
|
+
api: ({ data }) => {
|
|
392
|
+
return {
|
|
393
|
+
setMetadata: (metadata) => {
|
|
394
|
+
data.metadata = metadata;
|
|
395
|
+
},
|
|
396
|
+
};
|
|
397
|
+
},
|
|
398
|
+
setup: () => ({}),
|
|
399
|
+
};
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Check if a value can be serialized (e.g. using `JSON.stringify`).
|
|
403
|
+
* Adapted from: https://stackoverflow.com/a/30712764/3829557
|
|
404
|
+
*/
|
|
405
|
+
function isSerializable(value) {
|
|
406
|
+
// Primitives are OK.
|
|
407
|
+
if (value === undefined ||
|
|
408
|
+
value === null ||
|
|
409
|
+
typeof value === 'boolean' ||
|
|
410
|
+
typeof value === 'number' ||
|
|
411
|
+
typeof value === 'string') {
|
|
412
|
+
return true;
|
|
413
|
+
}
|
|
414
|
+
// A non-primitive value that is neither a POJO or an array cannot be serialized.
|
|
415
|
+
if (!isPlainObject(value) && !Array.isArray(value)) {
|
|
416
|
+
return false;
|
|
417
|
+
}
|
|
418
|
+
// Recurse entries if the value is an object or array.
|
|
419
|
+
for (const key in value) {
|
|
420
|
+
if (!isSerializable(value[key]))
|
|
421
|
+
return false;
|
|
422
|
+
}
|
|
423
|
+
return true;
|
|
424
|
+
}
|
|
425
|
+
/**
|
|
426
|
+
* Plugin that checks whether state is serializable, in order to avoid
|
|
427
|
+
* network serialization bugs.
|
|
428
|
+
*/
|
|
429
|
+
const SerializablePlugin = {
|
|
430
|
+
name: 'plugin-serializable',
|
|
431
|
+
fnWrap: (move) => (context, ...args) => {
|
|
432
|
+
const result = move(context, ...args);
|
|
433
|
+
// Check state in non-production environments.
|
|
434
|
+
if (process.env.NODE_ENV !== 'production' && !isSerializable(result)) {
|
|
435
|
+
throw new Error('Move state is not JSON-serialiazable.\n' +
|
|
436
|
+
'See https://boardgame.io/documentation/#/?id=state for more information.');
|
|
437
|
+
}
|
|
438
|
+
return result;
|
|
439
|
+
},
|
|
440
|
+
};
|
|
441
|
+
|
|
442
|
+
/*
|
|
443
|
+
* Copyright 2018 The boardgame.io Authors
|
|
444
|
+
*
|
|
445
|
+
* Use of this source code is governed by a MIT-style
|
|
446
|
+
* license that can be found in the LICENSE file or at
|
|
447
|
+
* https://opensource.org/licenses/MIT.
|
|
448
|
+
*/
|
|
449
|
+
const production = process.env.NODE_ENV === 'production';
|
|
450
|
+
const logfn = production ? () => { } : (...msg) => console.log(...msg);
|
|
451
|
+
const errorfn = (...msg) => console.error(...msg);
|
|
452
|
+
function info(msg) {
|
|
453
|
+
logfn(`INFO: ${msg}`);
|
|
454
|
+
}
|
|
455
|
+
function error(error) {
|
|
456
|
+
errorfn('ERROR:', error);
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
/*
|
|
460
|
+
* Copyright 2018 The boardgame.io Authors
|
|
461
|
+
*
|
|
462
|
+
* Use of this source code is governed by a MIT-style
|
|
463
|
+
* license that can be found in the LICENSE file or at
|
|
464
|
+
* https://opensource.org/licenses/MIT.
|
|
465
|
+
*/
|
|
466
|
+
/**
|
|
467
|
+
* List of plugins that are always added.
|
|
468
|
+
*/
|
|
469
|
+
const CORE_PLUGINS = [ImmerPlugin, RandomPlugin, LogPlugin, SerializablePlugin];
|
|
470
|
+
const DEFAULT_PLUGINS = [...CORE_PLUGINS, EventsPlugin];
|
|
471
|
+
/**
|
|
472
|
+
* Allow plugins to intercept actions and process them.
|
|
473
|
+
*/
|
|
474
|
+
const ProcessAction = (state, action, opts) => {
|
|
475
|
+
// TODO(#723): Extend error handling to plugins.
|
|
476
|
+
opts.game.plugins
|
|
477
|
+
.filter((plugin) => plugin.action !== undefined)
|
|
478
|
+
.filter((plugin) => plugin.name === action.payload.type)
|
|
479
|
+
.forEach((plugin) => {
|
|
480
|
+
const name = plugin.name;
|
|
481
|
+
const pluginState = state.plugins[name] || { data: {} };
|
|
482
|
+
const data = plugin.action(pluginState.data, action.payload);
|
|
483
|
+
state = {
|
|
484
|
+
...state,
|
|
485
|
+
plugins: {
|
|
486
|
+
...state.plugins,
|
|
487
|
+
[name]: { ...pluginState, data },
|
|
488
|
+
},
|
|
489
|
+
};
|
|
490
|
+
});
|
|
491
|
+
return state;
|
|
492
|
+
};
|
|
493
|
+
/**
|
|
494
|
+
* The APIs created by various plugins are stored in the plugins
|
|
495
|
+
* section of the state object:
|
|
496
|
+
*
|
|
497
|
+
* {
|
|
498
|
+
* G: {},
|
|
499
|
+
* ctx: {},
|
|
500
|
+
* plugins: {
|
|
501
|
+
* plugin-a: {
|
|
502
|
+
* data: {}, // this is generated by the plugin at Setup / Flush.
|
|
503
|
+
* api: {}, // this is ephemeral and generated by Enhance.
|
|
504
|
+
* }
|
|
505
|
+
* }
|
|
506
|
+
* }
|
|
507
|
+
*
|
|
508
|
+
* This function retrieves plugin APIs and returns them as an object
|
|
509
|
+
* for consumption as used by move contexts.
|
|
510
|
+
*/
|
|
511
|
+
const GetAPIs = ({ plugins }) => Object.entries(plugins || {}).reduce((apis, [name, { api }]) => {
|
|
512
|
+
apis[name] = api;
|
|
513
|
+
return apis;
|
|
514
|
+
}, {});
|
|
515
|
+
/**
|
|
516
|
+
* Applies the provided plugins to the given move / flow function.
|
|
517
|
+
*
|
|
518
|
+
* @param methodToWrap - The move function or hook to apply the plugins to.
|
|
519
|
+
* @param methodType - The type of the move or hook being wrapped.
|
|
520
|
+
* @param plugins - The list of plugins.
|
|
521
|
+
*/
|
|
522
|
+
const FnWrap = (methodToWrap, methodType, plugins) => {
|
|
523
|
+
return [...CORE_PLUGINS, ...plugins, EventsPlugin]
|
|
524
|
+
.filter((plugin) => plugin.fnWrap !== undefined)
|
|
525
|
+
.reduce((method, { fnWrap }) => fnWrap(method, methodType), methodToWrap);
|
|
526
|
+
};
|
|
527
|
+
/**
|
|
528
|
+
* Allows the plugin to generate its initial state.
|
|
529
|
+
*/
|
|
530
|
+
const Setup = (state, opts) => {
|
|
531
|
+
[...DEFAULT_PLUGINS, ...opts.game.plugins]
|
|
532
|
+
.filter((plugin) => plugin.setup !== undefined)
|
|
533
|
+
.forEach((plugin) => {
|
|
534
|
+
const name = plugin.name;
|
|
535
|
+
const data = plugin.setup({
|
|
536
|
+
G: state.G,
|
|
537
|
+
ctx: state.ctx,
|
|
538
|
+
game: opts.game,
|
|
539
|
+
});
|
|
540
|
+
state = {
|
|
541
|
+
...state,
|
|
542
|
+
plugins: {
|
|
543
|
+
...state.plugins,
|
|
544
|
+
[name]: { data },
|
|
545
|
+
},
|
|
546
|
+
};
|
|
547
|
+
});
|
|
548
|
+
return state;
|
|
549
|
+
};
|
|
550
|
+
/**
|
|
551
|
+
* Invokes the plugin before a move or event.
|
|
552
|
+
* The API that the plugin generates is stored inside
|
|
553
|
+
* the `plugins` section of the state (which is subsequently
|
|
554
|
+
* merged into ctx).
|
|
555
|
+
*/
|
|
556
|
+
const Enhance = (state, opts) => {
|
|
557
|
+
[...DEFAULT_PLUGINS, ...opts.game.plugins]
|
|
558
|
+
.filter((plugin) => plugin.api !== undefined)
|
|
559
|
+
.forEach((plugin) => {
|
|
560
|
+
const name = plugin.name;
|
|
561
|
+
const pluginState = state.plugins[name] || { data: {} };
|
|
562
|
+
const api = plugin.api({
|
|
563
|
+
G: state.G,
|
|
564
|
+
ctx: state.ctx,
|
|
565
|
+
data: pluginState.data,
|
|
566
|
+
game: opts.game,
|
|
567
|
+
playerID: opts.playerID,
|
|
568
|
+
});
|
|
569
|
+
state = {
|
|
570
|
+
...state,
|
|
571
|
+
plugins: {
|
|
572
|
+
...state.plugins,
|
|
573
|
+
[name]: { ...pluginState, api },
|
|
574
|
+
},
|
|
575
|
+
};
|
|
576
|
+
});
|
|
577
|
+
return state;
|
|
578
|
+
};
|
|
579
|
+
/**
|
|
580
|
+
* Allows plugins to update their state after a move / event.
|
|
581
|
+
*/
|
|
582
|
+
const Flush = (state, opts) => {
|
|
583
|
+
// We flush the events plugin first, then custom plugins and the core plugins.
|
|
584
|
+
// This means custom plugins cannot use the events API but will be available in event hooks.
|
|
585
|
+
// Note that plugins are flushed in reverse, to allow custom plugins calling each other.
|
|
586
|
+
[...CORE_PLUGINS, ...opts.game.plugins, EventsPlugin]
|
|
587
|
+
.reverse()
|
|
588
|
+
.forEach((plugin) => {
|
|
589
|
+
const name = plugin.name;
|
|
590
|
+
const pluginState = state.plugins[name] || { data: {} };
|
|
591
|
+
if (plugin.flush) {
|
|
592
|
+
const newData = plugin.flush({
|
|
593
|
+
G: state.G,
|
|
594
|
+
ctx: state.ctx,
|
|
595
|
+
game: opts.game,
|
|
596
|
+
api: pluginState.api,
|
|
597
|
+
data: pluginState.data,
|
|
598
|
+
});
|
|
599
|
+
state = {
|
|
600
|
+
...state,
|
|
601
|
+
plugins: {
|
|
602
|
+
...state.plugins,
|
|
603
|
+
[plugin.name]: { data: newData },
|
|
604
|
+
},
|
|
605
|
+
};
|
|
606
|
+
}
|
|
607
|
+
else if (plugin.dangerouslyFlushRawState) {
|
|
608
|
+
state = plugin.dangerouslyFlushRawState({
|
|
609
|
+
state,
|
|
610
|
+
game: opts.game,
|
|
611
|
+
api: pluginState.api,
|
|
612
|
+
data: pluginState.data,
|
|
613
|
+
});
|
|
614
|
+
// Remove everything other than data.
|
|
615
|
+
const data = state.plugins[name].data;
|
|
616
|
+
state = {
|
|
617
|
+
...state,
|
|
618
|
+
plugins: {
|
|
619
|
+
...state.plugins,
|
|
620
|
+
[plugin.name]: { data },
|
|
621
|
+
},
|
|
622
|
+
};
|
|
623
|
+
}
|
|
624
|
+
});
|
|
625
|
+
return state;
|
|
626
|
+
};
|
|
627
|
+
/**
|
|
628
|
+
* Allows plugins to indicate if they should not be materialized on the client.
|
|
629
|
+
* This will cause the client to discard the state update and wait for the
|
|
630
|
+
* master instead.
|
|
631
|
+
*/
|
|
632
|
+
const NoClient = (state, opts) => {
|
|
633
|
+
return [...DEFAULT_PLUGINS, ...opts.game.plugins]
|
|
634
|
+
.filter((plugin) => plugin.noClient !== undefined)
|
|
635
|
+
.map((plugin) => {
|
|
636
|
+
const name = plugin.name;
|
|
637
|
+
const pluginState = state.plugins[name];
|
|
638
|
+
if (pluginState) {
|
|
639
|
+
return plugin.noClient({
|
|
640
|
+
G: state.G,
|
|
641
|
+
ctx: state.ctx,
|
|
642
|
+
game: opts.game,
|
|
643
|
+
api: pluginState.api,
|
|
644
|
+
data: pluginState.data,
|
|
645
|
+
});
|
|
646
|
+
}
|
|
647
|
+
return false;
|
|
648
|
+
})
|
|
649
|
+
.includes(true);
|
|
650
|
+
};
|
|
651
|
+
/**
|
|
652
|
+
* Allows plugins to indicate if the entire action should be thrown out
|
|
653
|
+
* as invalid. This will cancel the entire state update.
|
|
654
|
+
*/
|
|
655
|
+
const IsInvalid = (state, opts) => {
|
|
656
|
+
const firstInvalidReturn = [...DEFAULT_PLUGINS, ...opts.game.plugins]
|
|
657
|
+
.filter((plugin) => plugin.isInvalid !== undefined)
|
|
658
|
+
.map((plugin) => {
|
|
659
|
+
const { name } = plugin;
|
|
660
|
+
const pluginState = state.plugins[name];
|
|
661
|
+
const message = plugin.isInvalid({
|
|
662
|
+
G: state.G,
|
|
663
|
+
ctx: state.ctx,
|
|
664
|
+
game: opts.game,
|
|
665
|
+
data: pluginState && pluginState.data,
|
|
666
|
+
});
|
|
667
|
+
return message ? { plugin: name, message } : false;
|
|
668
|
+
})
|
|
669
|
+
.find((value) => value);
|
|
670
|
+
return firstInvalidReturn || false;
|
|
671
|
+
};
|
|
672
|
+
/**
|
|
673
|
+
* Update plugin state after move/event & check if plugins consider the update to be valid.
|
|
674
|
+
* @returns Tuple of `[updatedState]` or `[originalState, invalidError]`.
|
|
675
|
+
*/
|
|
676
|
+
const FlushAndValidate = (state, opts) => {
|
|
677
|
+
const updatedState = Flush(state, opts);
|
|
678
|
+
const isInvalid = IsInvalid(updatedState, opts);
|
|
679
|
+
if (!isInvalid)
|
|
680
|
+
return [updatedState];
|
|
681
|
+
const { plugin, message } = isInvalid;
|
|
682
|
+
error(`${plugin} plugin declared action invalid:\n${message}`);
|
|
683
|
+
return [state, isInvalid];
|
|
684
|
+
};
|
|
685
|
+
/**
|
|
686
|
+
* Allows plugins to customize their data for specific players.
|
|
687
|
+
* For example, a plugin may want to share no data with the client, or
|
|
688
|
+
* want to keep some player data secret from opponents.
|
|
689
|
+
*/
|
|
690
|
+
const PlayerView = ({ G, ctx, plugins = {} }, { game, playerID }) => {
|
|
691
|
+
[...DEFAULT_PLUGINS, ...game.plugins].forEach(({ name, playerView }) => {
|
|
692
|
+
if (!playerView)
|
|
693
|
+
return;
|
|
694
|
+
const { data } = plugins[name] || { data: {} };
|
|
695
|
+
const newData = playerView({ G, ctx, game, data, playerID });
|
|
696
|
+
plugins = {
|
|
697
|
+
...plugins,
|
|
698
|
+
[name]: { data: newData },
|
|
699
|
+
};
|
|
700
|
+
});
|
|
701
|
+
return plugins;
|
|
702
|
+
};
|
|
703
|
+
|
|
704
|
+
/**
|
|
705
|
+
* Adjust the given options to use the new minMoves/maxMoves if a legacy moveLimit was given
|
|
706
|
+
* @param options The options object to apply backwards compatibility to
|
|
707
|
+
* @param enforceMinMoves Use moveLimit to set both minMoves and maxMoves
|
|
708
|
+
*/
|
|
709
|
+
function supportDeprecatedMoveLimit(options, enforceMinMoves = false) {
|
|
710
|
+
if (options.moveLimit) {
|
|
711
|
+
if (enforceMinMoves) {
|
|
712
|
+
options.minMoves = options.moveLimit;
|
|
713
|
+
}
|
|
714
|
+
options.maxMoves = options.moveLimit;
|
|
715
|
+
delete options.moveLimit;
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
/*
|
|
720
|
+
* Copyright 2017 The boardgame.io Authors
|
|
721
|
+
*
|
|
722
|
+
* Use of this source code is governed by a MIT-style
|
|
723
|
+
* license that can be found in the LICENSE file or at
|
|
724
|
+
* https://opensource.org/licenses/MIT.
|
|
725
|
+
*/
|
|
726
|
+
function SetActivePlayers(ctx, arg) {
|
|
727
|
+
let activePlayers = {};
|
|
728
|
+
let _prevActivePlayers = [];
|
|
729
|
+
let _nextActivePlayers = null;
|
|
730
|
+
let _activePlayersMinMoves = {};
|
|
731
|
+
let _activePlayersMaxMoves = {};
|
|
732
|
+
if (Array.isArray(arg)) {
|
|
733
|
+
// support a simple array of player IDs as active players
|
|
734
|
+
const value = {};
|
|
735
|
+
arg.forEach((v) => (value[v] = Stage.NULL));
|
|
736
|
+
activePlayers = value;
|
|
737
|
+
}
|
|
738
|
+
else {
|
|
739
|
+
// process active players argument object
|
|
740
|
+
// stages previously did not enforce minMoves, this behaviour is kept intentionally
|
|
741
|
+
supportDeprecatedMoveLimit(arg);
|
|
742
|
+
if (arg.next) {
|
|
743
|
+
_nextActivePlayers = arg.next;
|
|
744
|
+
}
|
|
745
|
+
if (arg.revert) {
|
|
746
|
+
_prevActivePlayers = [
|
|
747
|
+
...ctx._prevActivePlayers,
|
|
748
|
+
{
|
|
749
|
+
activePlayers: ctx.activePlayers,
|
|
750
|
+
_activePlayersMinMoves: ctx._activePlayersMinMoves,
|
|
751
|
+
_activePlayersMaxMoves: ctx._activePlayersMaxMoves,
|
|
752
|
+
_activePlayersNumMoves: ctx._activePlayersNumMoves,
|
|
753
|
+
},
|
|
754
|
+
];
|
|
755
|
+
}
|
|
756
|
+
if (arg.currentPlayer !== undefined) {
|
|
757
|
+
ApplyActivePlayerArgument(activePlayers, _activePlayersMinMoves, _activePlayersMaxMoves, ctx.currentPlayer, arg.currentPlayer);
|
|
758
|
+
}
|
|
759
|
+
if (arg.others !== undefined) {
|
|
760
|
+
for (let i = 0; i < ctx.playOrder.length; i++) {
|
|
761
|
+
const id = ctx.playOrder[i];
|
|
762
|
+
if (id !== ctx.currentPlayer) {
|
|
763
|
+
ApplyActivePlayerArgument(activePlayers, _activePlayersMinMoves, _activePlayersMaxMoves, id, arg.others);
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
if (arg.all !== undefined) {
|
|
768
|
+
for (let i = 0; i < ctx.playOrder.length; i++) {
|
|
769
|
+
const id = ctx.playOrder[i];
|
|
770
|
+
ApplyActivePlayerArgument(activePlayers, _activePlayersMinMoves, _activePlayersMaxMoves, id, arg.all);
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
if (arg.value) {
|
|
774
|
+
for (const id in arg.value) {
|
|
775
|
+
ApplyActivePlayerArgument(activePlayers, _activePlayersMinMoves, _activePlayersMaxMoves, id, arg.value[id]);
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
if (arg.minMoves) {
|
|
779
|
+
for (const id in activePlayers) {
|
|
780
|
+
if (_activePlayersMinMoves[id] === undefined) {
|
|
781
|
+
_activePlayersMinMoves[id] = arg.minMoves;
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
if (arg.maxMoves) {
|
|
786
|
+
for (const id in activePlayers) {
|
|
787
|
+
if (_activePlayersMaxMoves[id] === undefined) {
|
|
788
|
+
_activePlayersMaxMoves[id] = arg.maxMoves;
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
if (Object.keys(activePlayers).length === 0) {
|
|
794
|
+
activePlayers = null;
|
|
795
|
+
}
|
|
796
|
+
if (Object.keys(_activePlayersMinMoves).length === 0) {
|
|
797
|
+
_activePlayersMinMoves = null;
|
|
798
|
+
}
|
|
799
|
+
if (Object.keys(_activePlayersMaxMoves).length === 0) {
|
|
800
|
+
_activePlayersMaxMoves = null;
|
|
801
|
+
}
|
|
802
|
+
const _activePlayersNumMoves = {};
|
|
803
|
+
for (const id in activePlayers) {
|
|
804
|
+
_activePlayersNumMoves[id] = 0;
|
|
805
|
+
}
|
|
806
|
+
return {
|
|
807
|
+
...ctx,
|
|
808
|
+
activePlayers,
|
|
809
|
+
_activePlayersMinMoves,
|
|
810
|
+
_activePlayersMaxMoves,
|
|
811
|
+
_activePlayersNumMoves,
|
|
812
|
+
_prevActivePlayers,
|
|
813
|
+
_nextActivePlayers,
|
|
814
|
+
};
|
|
815
|
+
}
|
|
816
|
+
/**
|
|
817
|
+
* Update activePlayers, setting it to previous, next or null values
|
|
818
|
+
* when it becomes empty.
|
|
819
|
+
* @param ctx
|
|
820
|
+
*/
|
|
821
|
+
function UpdateActivePlayersOnceEmpty(ctx) {
|
|
822
|
+
let { activePlayers, _activePlayersMinMoves, _activePlayersMaxMoves, _activePlayersNumMoves, _prevActivePlayers, _nextActivePlayers, } = ctx;
|
|
823
|
+
if (activePlayers && Object.keys(activePlayers).length === 0) {
|
|
824
|
+
if (_nextActivePlayers) {
|
|
825
|
+
ctx = SetActivePlayers(ctx, _nextActivePlayers);
|
|
826
|
+
({
|
|
827
|
+
activePlayers,
|
|
828
|
+
_activePlayersMinMoves,
|
|
829
|
+
_activePlayersMaxMoves,
|
|
830
|
+
_activePlayersNumMoves,
|
|
831
|
+
_prevActivePlayers,
|
|
832
|
+
} = ctx);
|
|
833
|
+
}
|
|
834
|
+
else if (_prevActivePlayers.length > 0) {
|
|
835
|
+
const lastIndex = _prevActivePlayers.length - 1;
|
|
836
|
+
({
|
|
837
|
+
activePlayers,
|
|
838
|
+
_activePlayersMinMoves,
|
|
839
|
+
_activePlayersMaxMoves,
|
|
840
|
+
_activePlayersNumMoves,
|
|
841
|
+
} = _prevActivePlayers[lastIndex]);
|
|
842
|
+
_prevActivePlayers = _prevActivePlayers.slice(0, lastIndex);
|
|
843
|
+
}
|
|
844
|
+
else {
|
|
845
|
+
activePlayers = null;
|
|
846
|
+
_activePlayersMinMoves = null;
|
|
847
|
+
_activePlayersMaxMoves = null;
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
return {
|
|
851
|
+
...ctx,
|
|
852
|
+
activePlayers,
|
|
853
|
+
_activePlayersMinMoves,
|
|
854
|
+
_activePlayersMaxMoves,
|
|
855
|
+
_activePlayersNumMoves,
|
|
856
|
+
_prevActivePlayers,
|
|
857
|
+
};
|
|
858
|
+
}
|
|
859
|
+
/**
|
|
860
|
+
* Apply an active player argument to the given player ID
|
|
861
|
+
* @param {Object} activePlayers
|
|
862
|
+
* @param {Object} _activePlayersMinMoves
|
|
863
|
+
* @param {Object} _activePlayersMaxMoves
|
|
864
|
+
* @param {String} playerID The player to apply the parameter to
|
|
865
|
+
* @param {(String|Object)} arg An active player argument
|
|
866
|
+
*/
|
|
867
|
+
function ApplyActivePlayerArgument(activePlayers, _activePlayersMinMoves, _activePlayersMaxMoves, playerID, arg) {
|
|
868
|
+
if (typeof arg !== 'object' || arg === Stage.NULL) {
|
|
869
|
+
arg = { stage: arg };
|
|
870
|
+
}
|
|
871
|
+
if (arg.stage !== undefined) {
|
|
872
|
+
// stages previously did not enforce minMoves, this behaviour is kept intentionally
|
|
873
|
+
supportDeprecatedMoveLimit(arg);
|
|
874
|
+
activePlayers[playerID] = arg.stage;
|
|
875
|
+
if (arg.minMoves)
|
|
876
|
+
_activePlayersMinMoves[playerID] = arg.minMoves;
|
|
877
|
+
if (arg.maxMoves)
|
|
878
|
+
_activePlayersMaxMoves[playerID] = arg.maxMoves;
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
/**
|
|
882
|
+
* Converts a playOrderPos index into its value in playOrder.
|
|
883
|
+
* @param {Array} playOrder - An array of player ID's.
|
|
884
|
+
* @param {number} playOrderPos - An index into the above.
|
|
885
|
+
*/
|
|
886
|
+
function getCurrentPlayer(playOrder, playOrderPos) {
|
|
887
|
+
// convert to string in case playOrder is set to number[]
|
|
888
|
+
return playOrder[playOrderPos] + '';
|
|
889
|
+
}
|
|
890
|
+
/**
|
|
891
|
+
* Called at the start of a turn to initialize turn order state.
|
|
892
|
+
*
|
|
893
|
+
* TODO: This is called inside StartTurn, which is called from
|
|
894
|
+
* both UpdateTurn and StartPhase (so it's called at the beginning
|
|
895
|
+
* of a new phase as well as between turns). We should probably
|
|
896
|
+
* split it into two.
|
|
897
|
+
*/
|
|
898
|
+
function InitTurnOrderState(state, turn) {
|
|
899
|
+
let { G, ctx } = state;
|
|
900
|
+
const { numPlayers } = ctx;
|
|
901
|
+
const pluginAPIs = GetAPIs(state);
|
|
902
|
+
const context = { ...pluginAPIs, G, ctx };
|
|
903
|
+
const order = turn.order;
|
|
904
|
+
let playOrder = [...Array.from({ length: numPlayers })].map((_, i) => i + '');
|
|
905
|
+
if (order.playOrder !== undefined) {
|
|
906
|
+
playOrder = order.playOrder(context);
|
|
907
|
+
}
|
|
908
|
+
const playOrderPos = order.first(context);
|
|
909
|
+
const posType = typeof playOrderPos;
|
|
910
|
+
if (posType !== 'number') {
|
|
911
|
+
error(`invalid value returned by turn.order.first — expected number got ${posType} “${playOrderPos}”.`);
|
|
912
|
+
}
|
|
913
|
+
const currentPlayer = getCurrentPlayer(playOrder, playOrderPos);
|
|
914
|
+
ctx = { ...ctx, currentPlayer, playOrderPos, playOrder };
|
|
915
|
+
ctx = SetActivePlayers(ctx, turn.activePlayers || {});
|
|
916
|
+
return ctx;
|
|
917
|
+
}
|
|
918
|
+
/**
|
|
919
|
+
* Called at the end of each turn to update the turn order state.
|
|
920
|
+
* @param {object} G - The game object G.
|
|
921
|
+
* @param {object} ctx - The game object ctx.
|
|
922
|
+
* @param {object} turn - A turn object for this phase.
|
|
923
|
+
* @param {string} endTurnArg - An optional argument to endTurn that
|
|
924
|
+
may specify the next player.
|
|
925
|
+
*/
|
|
926
|
+
function UpdateTurnOrderState(state, currentPlayer, turn, endTurnArg) {
|
|
927
|
+
const order = turn.order;
|
|
928
|
+
let { G, ctx } = state;
|
|
929
|
+
let playOrderPos = ctx.playOrderPos;
|
|
930
|
+
let endPhase = false;
|
|
931
|
+
if (endTurnArg && endTurnArg !== true) {
|
|
932
|
+
if (typeof endTurnArg !== 'object') {
|
|
933
|
+
error(`invalid argument to endTurn: ${endTurnArg}`);
|
|
934
|
+
}
|
|
935
|
+
Object.keys(endTurnArg).forEach((arg) => {
|
|
936
|
+
switch (arg) {
|
|
937
|
+
case 'remove':
|
|
938
|
+
currentPlayer = getCurrentPlayer(ctx.playOrder, playOrderPos);
|
|
939
|
+
break;
|
|
940
|
+
case 'next':
|
|
941
|
+
playOrderPos = ctx.playOrder.indexOf(endTurnArg.next);
|
|
942
|
+
currentPlayer = endTurnArg.next;
|
|
943
|
+
break;
|
|
944
|
+
default:
|
|
945
|
+
error(`invalid argument to endTurn: ${arg}`);
|
|
946
|
+
}
|
|
947
|
+
});
|
|
948
|
+
}
|
|
949
|
+
else {
|
|
950
|
+
const pluginAPIs = GetAPIs(state);
|
|
951
|
+
const context = { ...pluginAPIs, G, ctx };
|
|
952
|
+
const t = order.next(context);
|
|
953
|
+
const type = typeof t;
|
|
954
|
+
if (t !== undefined && type !== 'number') {
|
|
955
|
+
error(`invalid value returned by turn.order.next — expected number or undefined got ${type} “${t}”.`);
|
|
956
|
+
}
|
|
957
|
+
if (t === undefined) {
|
|
958
|
+
endPhase = true;
|
|
959
|
+
}
|
|
960
|
+
else {
|
|
961
|
+
playOrderPos = t;
|
|
962
|
+
currentPlayer = getCurrentPlayer(ctx.playOrder, playOrderPos);
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
ctx = {
|
|
966
|
+
...ctx,
|
|
967
|
+
playOrderPos,
|
|
968
|
+
currentPlayer,
|
|
969
|
+
};
|
|
970
|
+
return { endPhase, ctx };
|
|
971
|
+
}
|
|
972
|
+
/**
|
|
973
|
+
* Set of different turn orders possible in a phase.
|
|
974
|
+
* These are meant to be passed to the `turn` setting
|
|
975
|
+
* in the flow objects.
|
|
976
|
+
*
|
|
977
|
+
* Each object defines the first player when the phase / game
|
|
978
|
+
* begins, and also a function `next` to determine who the
|
|
979
|
+
* next player is when the turn ends.
|
|
980
|
+
*
|
|
981
|
+
* The phase ends if next() returns undefined.
|
|
982
|
+
*/
|
|
983
|
+
const TurnOrder = {
|
|
984
|
+
/**
|
|
985
|
+
* DEFAULT
|
|
986
|
+
*
|
|
987
|
+
* The default round-robin turn order.
|
|
988
|
+
*/
|
|
989
|
+
DEFAULT: {
|
|
990
|
+
first: ({ ctx }) => ctx.turn === 0
|
|
991
|
+
? ctx.playOrderPos
|
|
992
|
+
: (ctx.playOrderPos + 1) % ctx.playOrder.length,
|
|
993
|
+
next: ({ ctx }) => (ctx.playOrderPos + 1) % ctx.playOrder.length,
|
|
994
|
+
},
|
|
995
|
+
/**
|
|
996
|
+
* RESET
|
|
997
|
+
*
|
|
998
|
+
* Similar to DEFAULT, but starts from 0 each time.
|
|
999
|
+
*/
|
|
1000
|
+
RESET: {
|
|
1001
|
+
first: () => 0,
|
|
1002
|
+
next: ({ ctx }) => (ctx.playOrderPos + 1) % ctx.playOrder.length,
|
|
1003
|
+
},
|
|
1004
|
+
/**
|
|
1005
|
+
* CONTINUE
|
|
1006
|
+
*
|
|
1007
|
+
* Similar to DEFAULT, but starts with the player who ended the last phase.
|
|
1008
|
+
*/
|
|
1009
|
+
CONTINUE: {
|
|
1010
|
+
first: ({ ctx }) => ctx.playOrderPos,
|
|
1011
|
+
next: ({ ctx }) => (ctx.playOrderPos + 1) % ctx.playOrder.length,
|
|
1012
|
+
},
|
|
1013
|
+
/**
|
|
1014
|
+
* ONCE
|
|
1015
|
+
*
|
|
1016
|
+
* Another round-robin turn order, but goes around just once.
|
|
1017
|
+
* The phase ends after all players have played.
|
|
1018
|
+
*/
|
|
1019
|
+
ONCE: {
|
|
1020
|
+
first: () => 0,
|
|
1021
|
+
next: ({ ctx }) => {
|
|
1022
|
+
if (ctx.playOrderPos < ctx.playOrder.length - 1) {
|
|
1023
|
+
return ctx.playOrderPos + 1;
|
|
1024
|
+
}
|
|
1025
|
+
},
|
|
1026
|
+
},
|
|
1027
|
+
/**
|
|
1028
|
+
* CUSTOM
|
|
1029
|
+
*
|
|
1030
|
+
* Identical to DEFAULT, but also sets playOrder at the
|
|
1031
|
+
* beginning of the phase.
|
|
1032
|
+
*
|
|
1033
|
+
* @param {Array} playOrder - The play order.
|
|
1034
|
+
*/
|
|
1035
|
+
CUSTOM: (playOrder) => ({
|
|
1036
|
+
playOrder: () => playOrder,
|
|
1037
|
+
first: () => 0,
|
|
1038
|
+
next: ({ ctx }) => (ctx.playOrderPos + 1) % ctx.playOrder.length,
|
|
1039
|
+
}),
|
|
1040
|
+
/**
|
|
1041
|
+
* CUSTOM_FROM
|
|
1042
|
+
*
|
|
1043
|
+
* Identical to DEFAULT, but also sets playOrder at the
|
|
1044
|
+
* beginning of the phase to a value specified by a field
|
|
1045
|
+
* in G.
|
|
1046
|
+
*
|
|
1047
|
+
* @param {string} playOrderField - Field in G.
|
|
1048
|
+
*/
|
|
1049
|
+
CUSTOM_FROM: (playOrderField) => ({
|
|
1050
|
+
playOrder: ({ G }) => G[playOrderField],
|
|
1051
|
+
first: () => 0,
|
|
1052
|
+
next: ({ ctx }) => (ctx.playOrderPos + 1) % ctx.playOrder.length,
|
|
1053
|
+
}),
|
|
1054
|
+
};
|
|
1055
|
+
const Stage = {
|
|
1056
|
+
NULL: null,
|
|
1057
|
+
};
|
|
1058
|
+
const ActivePlayers = {
|
|
1059
|
+
/**
|
|
1060
|
+
* ALL
|
|
1061
|
+
*
|
|
1062
|
+
* The turn stays with one player, but any player can play (in any order)
|
|
1063
|
+
* until the phase ends.
|
|
1064
|
+
*/
|
|
1065
|
+
ALL: { all: Stage.NULL },
|
|
1066
|
+
/**
|
|
1067
|
+
* ALL_ONCE
|
|
1068
|
+
*
|
|
1069
|
+
* The turn stays with one player, but any player can play (once, and in any order).
|
|
1070
|
+
* This is typically used in a phase where you want to elicit a response
|
|
1071
|
+
* from every player in the game.
|
|
1072
|
+
*/
|
|
1073
|
+
ALL_ONCE: { all: Stage.NULL, minMoves: 1, maxMoves: 1 },
|
|
1074
|
+
/**
|
|
1075
|
+
* OTHERS
|
|
1076
|
+
*
|
|
1077
|
+
* The turn stays with one player, and every *other* player can play (in any order)
|
|
1078
|
+
* until the phase ends.
|
|
1079
|
+
*/
|
|
1080
|
+
OTHERS: { others: Stage.NULL },
|
|
1081
|
+
/**
|
|
1082
|
+
* OTHERS_ONCE
|
|
1083
|
+
*
|
|
1084
|
+
* The turn stays with one player, and every *other* player can play (once, and in any order).
|
|
1085
|
+
* This is typically used in a phase where you want to elicit a response
|
|
1086
|
+
* from every *other* player in the game.
|
|
1087
|
+
*/
|
|
1088
|
+
OTHERS_ONCE: { others: Stage.NULL, minMoves: 1, maxMoves: 1 },
|
|
1089
|
+
};
|
|
1090
|
+
|
|
1091
|
+
export { ActionCreators as A, makeMove as B, ActivePlayers as C, Enhance as E, FnWrap as F, GAME_EVENT as G, InitTurnOrderState as I, MAKE_MOVE as M, NoClient as N, PlayerView as P, RESET as R, SYNC as S, TurnOrder as T, UPDATE as U, redo as a, PATCH as b, REDO as c, UNDO as d, STRIP_TRANSIENTS as e, update as f, error as g, GameMethod as h, supportDeprecatedMoveLimit as i, GetAPIs as j, Stage as k, SetActivePlayers as l, info as m, UpdateTurnOrderState as n, UpdateActivePlayersOnceEmpty as o, patch as p, gameEvent as q, reset as r, sync as s, PLUGIN as t, undo as u, ProcessAction as v, INVALID_MOVE as w, FlushAndValidate as x, stripTransients as y, Setup as z };
|