@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,314 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2018 The boardgame.io Authors
|
|
3
|
+
*
|
|
4
|
+
* Use of this source code is governed by a MIT-style
|
|
5
|
+
* license that can be found in the LICENSE file or at
|
|
6
|
+
* https://opensource.org/licenses/MIT.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import PluginImmer from './plugin-immer';
|
|
10
|
+
import PluginRandom from './plugin-random';
|
|
11
|
+
import PluginEvents from './plugin-events';
|
|
12
|
+
import PluginLog from './plugin-log';
|
|
13
|
+
import PluginSerializable from './plugin-serializable';
|
|
14
|
+
import type {
|
|
15
|
+
AnyFn,
|
|
16
|
+
DefaultPluginAPIs,
|
|
17
|
+
PartialGameState,
|
|
18
|
+
State,
|
|
19
|
+
Game,
|
|
20
|
+
Plugin,
|
|
21
|
+
ActionShape,
|
|
22
|
+
PlayerID,
|
|
23
|
+
} from '../types';
|
|
24
|
+
import { error } from '../core/logger';
|
|
25
|
+
import type { GameMethod } from '../core/game-methods';
|
|
26
|
+
|
|
27
|
+
interface PluginOpts {
|
|
28
|
+
game: Game;
|
|
29
|
+
isClient?: boolean;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* List of plugins that are always added.
|
|
34
|
+
*/
|
|
35
|
+
const CORE_PLUGINS = [PluginImmer, PluginRandom, PluginLog, PluginSerializable];
|
|
36
|
+
const DEFAULT_PLUGINS = [...CORE_PLUGINS, PluginEvents];
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Allow plugins to intercept actions and process them.
|
|
40
|
+
*/
|
|
41
|
+
export const ProcessAction = (
|
|
42
|
+
state: State,
|
|
43
|
+
action: ActionShape.Plugin,
|
|
44
|
+
opts: PluginOpts
|
|
45
|
+
): State => {
|
|
46
|
+
// TODO(#723): Extend error handling to plugins.
|
|
47
|
+
opts.game.plugins
|
|
48
|
+
.filter((plugin) => plugin.action !== undefined)
|
|
49
|
+
.filter((plugin) => plugin.name === action.payload.type)
|
|
50
|
+
.forEach((plugin) => {
|
|
51
|
+
const name = plugin.name;
|
|
52
|
+
const pluginState = state.plugins[name] || { data: {} };
|
|
53
|
+
const data = plugin.action(pluginState.data, action.payload);
|
|
54
|
+
|
|
55
|
+
state = {
|
|
56
|
+
...state,
|
|
57
|
+
plugins: {
|
|
58
|
+
...state.plugins,
|
|
59
|
+
[name]: { ...pluginState, data },
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
});
|
|
63
|
+
return state;
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* The APIs created by various plugins are stored in the plugins
|
|
68
|
+
* section of the state object:
|
|
69
|
+
*
|
|
70
|
+
* {
|
|
71
|
+
* G: {},
|
|
72
|
+
* ctx: {},
|
|
73
|
+
* plugins: {
|
|
74
|
+
* plugin-a: {
|
|
75
|
+
* data: {}, // this is generated by the plugin at Setup / Flush.
|
|
76
|
+
* api: {}, // this is ephemeral and generated by Enhance.
|
|
77
|
+
* }
|
|
78
|
+
* }
|
|
79
|
+
* }
|
|
80
|
+
*
|
|
81
|
+
* This function retrieves plugin APIs and returns them as an object
|
|
82
|
+
* for consumption as used by move contexts.
|
|
83
|
+
*/
|
|
84
|
+
export const GetAPIs = ({ plugins }: PartialGameState) =>
|
|
85
|
+
Object.entries(plugins || {}).reduce((apis, [name, { api }]) => {
|
|
86
|
+
apis[name] = api;
|
|
87
|
+
return apis;
|
|
88
|
+
}, {} as DefaultPluginAPIs);
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Applies the provided plugins to the given move / flow function.
|
|
92
|
+
*
|
|
93
|
+
* @param methodToWrap - The move function or hook to apply the plugins to.
|
|
94
|
+
* @param methodType - The type of the move or hook being wrapped.
|
|
95
|
+
* @param plugins - The list of plugins.
|
|
96
|
+
*/
|
|
97
|
+
export const FnWrap = (
|
|
98
|
+
methodToWrap: AnyFn,
|
|
99
|
+
methodType: GameMethod,
|
|
100
|
+
plugins: Plugin[]
|
|
101
|
+
) => {
|
|
102
|
+
return [...CORE_PLUGINS, ...plugins, PluginEvents]
|
|
103
|
+
.filter((plugin) => plugin.fnWrap !== undefined)
|
|
104
|
+
.reduce(
|
|
105
|
+
(method: AnyFn, { fnWrap }: Plugin) => fnWrap(method, methodType),
|
|
106
|
+
methodToWrap
|
|
107
|
+
);
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Allows the plugin to generate its initial state.
|
|
112
|
+
*/
|
|
113
|
+
export const Setup = (
|
|
114
|
+
state: PartialGameState,
|
|
115
|
+
opts: PluginOpts
|
|
116
|
+
): PartialGameState => {
|
|
117
|
+
[...DEFAULT_PLUGINS, ...opts.game.plugins]
|
|
118
|
+
.filter((plugin) => plugin.setup !== undefined)
|
|
119
|
+
.forEach((plugin) => {
|
|
120
|
+
const name = plugin.name;
|
|
121
|
+
const data = plugin.setup({
|
|
122
|
+
G: state.G,
|
|
123
|
+
ctx: state.ctx,
|
|
124
|
+
game: opts.game,
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
state = {
|
|
128
|
+
...state,
|
|
129
|
+
plugins: {
|
|
130
|
+
...state.plugins,
|
|
131
|
+
[name]: { data },
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
});
|
|
135
|
+
return state;
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Invokes the plugin before a move or event.
|
|
140
|
+
* The API that the plugin generates is stored inside
|
|
141
|
+
* the `plugins` section of the state (which is subsequently
|
|
142
|
+
* merged into ctx).
|
|
143
|
+
*/
|
|
144
|
+
export const Enhance = <S extends State | PartialGameState>(
|
|
145
|
+
state: S,
|
|
146
|
+
opts: PluginOpts & { playerID: PlayerID }
|
|
147
|
+
): S => {
|
|
148
|
+
[...DEFAULT_PLUGINS, ...opts.game.plugins]
|
|
149
|
+
.filter((plugin) => plugin.api !== undefined)
|
|
150
|
+
.forEach((plugin) => {
|
|
151
|
+
const name = plugin.name;
|
|
152
|
+
const pluginState = state.plugins[name] || { data: {} };
|
|
153
|
+
|
|
154
|
+
const api = plugin.api({
|
|
155
|
+
G: state.G,
|
|
156
|
+
ctx: state.ctx,
|
|
157
|
+
data: pluginState.data,
|
|
158
|
+
game: opts.game,
|
|
159
|
+
playerID: opts.playerID,
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
state = {
|
|
163
|
+
...state,
|
|
164
|
+
plugins: {
|
|
165
|
+
...state.plugins,
|
|
166
|
+
[name]: { ...pluginState, api },
|
|
167
|
+
},
|
|
168
|
+
};
|
|
169
|
+
});
|
|
170
|
+
return state;
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Allows plugins to update their state after a move / event.
|
|
175
|
+
*/
|
|
176
|
+
const Flush = (state: State, opts: PluginOpts): State => {
|
|
177
|
+
// We flush the events plugin first, then custom plugins and the core plugins.
|
|
178
|
+
// This means custom plugins cannot use the events API but will be available in event hooks.
|
|
179
|
+
// Note that plugins are flushed in reverse, to allow custom plugins calling each other.
|
|
180
|
+
[...CORE_PLUGINS, ...opts.game.plugins, PluginEvents]
|
|
181
|
+
.reverse()
|
|
182
|
+
.forEach((plugin) => {
|
|
183
|
+
const name = plugin.name;
|
|
184
|
+
const pluginState = state.plugins[name] || { data: {} };
|
|
185
|
+
|
|
186
|
+
if (plugin.flush) {
|
|
187
|
+
const newData = plugin.flush({
|
|
188
|
+
G: state.G,
|
|
189
|
+
ctx: state.ctx,
|
|
190
|
+
game: opts.game,
|
|
191
|
+
api: pluginState.api,
|
|
192
|
+
data: pluginState.data,
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
state = {
|
|
196
|
+
...state,
|
|
197
|
+
plugins: {
|
|
198
|
+
...state.plugins,
|
|
199
|
+
[plugin.name]: { data: newData },
|
|
200
|
+
},
|
|
201
|
+
};
|
|
202
|
+
} else if (plugin.dangerouslyFlushRawState) {
|
|
203
|
+
state = plugin.dangerouslyFlushRawState({
|
|
204
|
+
state,
|
|
205
|
+
game: opts.game,
|
|
206
|
+
api: pluginState.api,
|
|
207
|
+
data: pluginState.data,
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
// Remove everything other than data.
|
|
211
|
+
const data = state.plugins[name].data;
|
|
212
|
+
state = {
|
|
213
|
+
...state,
|
|
214
|
+
plugins: {
|
|
215
|
+
...state.plugins,
|
|
216
|
+
[plugin.name]: { data },
|
|
217
|
+
},
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
return state;
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Allows plugins to indicate if they should not be materialized on the client.
|
|
227
|
+
* This will cause the client to discard the state update and wait for the
|
|
228
|
+
* master instead.
|
|
229
|
+
*/
|
|
230
|
+
export const NoClient = (state: State, opts: PluginOpts): boolean => {
|
|
231
|
+
return [...DEFAULT_PLUGINS, ...opts.game.plugins]
|
|
232
|
+
.filter((plugin) => plugin.noClient !== undefined)
|
|
233
|
+
.map((plugin) => {
|
|
234
|
+
const name = plugin.name;
|
|
235
|
+
const pluginState = state.plugins[name];
|
|
236
|
+
|
|
237
|
+
if (pluginState) {
|
|
238
|
+
return plugin.noClient({
|
|
239
|
+
G: state.G,
|
|
240
|
+
ctx: state.ctx,
|
|
241
|
+
game: opts.game,
|
|
242
|
+
api: pluginState.api,
|
|
243
|
+
data: pluginState.data,
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
return false;
|
|
248
|
+
})
|
|
249
|
+
.includes(true);
|
|
250
|
+
};
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Allows plugins to indicate if the entire action should be thrown out
|
|
254
|
+
* as invalid. This will cancel the entire state update.
|
|
255
|
+
*/
|
|
256
|
+
const IsInvalid = (
|
|
257
|
+
state: State,
|
|
258
|
+
opts: PluginOpts
|
|
259
|
+
): false | { plugin: string; message: string } => {
|
|
260
|
+
const firstInvalidReturn = [...DEFAULT_PLUGINS, ...opts.game.plugins]
|
|
261
|
+
.filter((plugin) => plugin.isInvalid !== undefined)
|
|
262
|
+
.map((plugin) => {
|
|
263
|
+
const { name } = plugin;
|
|
264
|
+
const pluginState = state.plugins[name];
|
|
265
|
+
|
|
266
|
+
const message = plugin.isInvalid({
|
|
267
|
+
G: state.G,
|
|
268
|
+
ctx: state.ctx,
|
|
269
|
+
game: opts.game,
|
|
270
|
+
data: pluginState && pluginState.data,
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
return message ? { plugin: name, message } : false;
|
|
274
|
+
})
|
|
275
|
+
.find((value) => value);
|
|
276
|
+
return firstInvalidReturn || false;
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Update plugin state after move/event & check if plugins consider the update to be valid.
|
|
281
|
+
* @returns Tuple of `[updatedState]` or `[originalState, invalidError]`.
|
|
282
|
+
*/
|
|
283
|
+
export const FlushAndValidate = (state: State, opts: PluginOpts) => {
|
|
284
|
+
const updatedState = Flush(state, opts);
|
|
285
|
+
const isInvalid = IsInvalid(updatedState, opts);
|
|
286
|
+
if (!isInvalid) return [updatedState] as const;
|
|
287
|
+
const { plugin, message } = isInvalid;
|
|
288
|
+
error(`${plugin} plugin declared action invalid:\n${message}`);
|
|
289
|
+
return [state, isInvalid] as const;
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Allows plugins to customize their data for specific players.
|
|
294
|
+
* For example, a plugin may want to share no data with the client, or
|
|
295
|
+
* want to keep some player data secret from opponents.
|
|
296
|
+
*/
|
|
297
|
+
export const PlayerView = (
|
|
298
|
+
{ G, ctx, plugins = {} }: State,
|
|
299
|
+
{ game, playerID }: PluginOpts & { playerID: PlayerID }
|
|
300
|
+
) => {
|
|
301
|
+
[...DEFAULT_PLUGINS, ...game.plugins].forEach(({ name, playerView }) => {
|
|
302
|
+
if (!playerView) return;
|
|
303
|
+
|
|
304
|
+
const { data } = plugins[name] || { data: {} };
|
|
305
|
+
const newData = playerView({ G, ctx, game, data, playerID });
|
|
306
|
+
|
|
307
|
+
plugins = {
|
|
308
|
+
...plugins,
|
|
309
|
+
[name]: { data: newData },
|
|
310
|
+
};
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
return plugins;
|
|
314
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2020 The boardgame.io Authors
|
|
3
|
+
*
|
|
4
|
+
* Use of this source code is governed by a MIT-style
|
|
5
|
+
* license that can be found in the LICENSE file or at
|
|
6
|
+
* https://opensource.org/licenses/MIT.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { Plugin } from '../types';
|
|
10
|
+
import type { PrivateEventsAPI, EventsAPI } from './events/events';
|
|
11
|
+
import { Events } from './events/events';
|
|
12
|
+
|
|
13
|
+
export type { EventsAPI };
|
|
14
|
+
|
|
15
|
+
const EventsPlugin: Plugin<EventsAPI & PrivateEventsAPI> = {
|
|
16
|
+
name: 'events',
|
|
17
|
+
|
|
18
|
+
noClient: ({ api }) => api._private.isUsed(),
|
|
19
|
+
|
|
20
|
+
isInvalid: ({ data }) => data.error || false,
|
|
21
|
+
|
|
22
|
+
// Update the events plugin’s internal turn context each time a move
|
|
23
|
+
// or hook is called. This allows events called after turn or phase
|
|
24
|
+
// endings to dispatch the current turn and phase correctly.
|
|
25
|
+
fnWrap:
|
|
26
|
+
(method, methodType) =>
|
|
27
|
+
(context, ...args) => {
|
|
28
|
+
const api = context.events as EventsAPI & PrivateEventsAPI;
|
|
29
|
+
if (api) api._private.updateTurnContext(context.ctx, methodType);
|
|
30
|
+
const G = method(context, ...args);
|
|
31
|
+
if (api) api._private.unsetCurrentMethod();
|
|
32
|
+
return G;
|
|
33
|
+
},
|
|
34
|
+
|
|
35
|
+
dangerouslyFlushRawState: ({ state, api }) => api._private.update(state),
|
|
36
|
+
|
|
37
|
+
api: ({ game, ctx, playerID }) => new Events(game.flow, ctx, playerID).api(),
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export default EventsPlugin;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2019 The boardgame.io Authors
|
|
3
|
+
*
|
|
4
|
+
* Use of this source code is governed by a MIT-style
|
|
5
|
+
* license that can be found in the LICENSE file or at
|
|
6
|
+
* https://opensource.org/licenses/MIT.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { Client } from '../client/client';
|
|
10
|
+
import type { _ClientImpl } from '../client/client';
|
|
11
|
+
import { INVALID_MOVE } from '../core/constants';
|
|
12
|
+
|
|
13
|
+
// Surpress invalid move error logging
|
|
14
|
+
jest.mock('../core/logger');
|
|
15
|
+
|
|
16
|
+
describe('immer', () => {
|
|
17
|
+
let client: _ClientImpl;
|
|
18
|
+
|
|
19
|
+
beforeAll(() => {
|
|
20
|
+
client = Client({
|
|
21
|
+
game: {
|
|
22
|
+
moves: {
|
|
23
|
+
A: ({ G }) => {
|
|
24
|
+
G.moveBody = true;
|
|
25
|
+
},
|
|
26
|
+
invalid: ({ G }) => {
|
|
27
|
+
G.madeInvalidMove = true;
|
|
28
|
+
return INVALID_MOVE;
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
phases: {
|
|
33
|
+
A: {
|
|
34
|
+
start: true,
|
|
35
|
+
onBegin: ({ G }) => {
|
|
36
|
+
G.onPhaseBegin = true;
|
|
37
|
+
},
|
|
38
|
+
onEnd: ({ G }) => {
|
|
39
|
+
G.onPhaseEnd = true;
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
turn: {
|
|
45
|
+
onBegin: ({ G }) => {
|
|
46
|
+
G.onTurnBegin = true;
|
|
47
|
+
},
|
|
48
|
+
onEnd: ({ G }) => {
|
|
49
|
+
G.onTurnEnd = true;
|
|
50
|
+
},
|
|
51
|
+
onMove: ({ G }) => {
|
|
52
|
+
G.onMove = true;
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test('begin', () => {
|
|
60
|
+
expect(client.getState().G.onPhaseBegin).toBe(true);
|
|
61
|
+
expect(client.getState().G.onTurnBegin).toBe(true);
|
|
62
|
+
expect(client.getState().G.onPhaseEnd).not.toBe(true);
|
|
63
|
+
expect(client.getState().G.onTurnEnd).not.toBe(true);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test('end turn', () => {
|
|
67
|
+
client.events.endTurn();
|
|
68
|
+
expect(client.getState().G.onTurnEnd).toBe(true);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test('end phase', () => {
|
|
72
|
+
client.events.endPhase();
|
|
73
|
+
expect(client.getState().G.onPhaseEnd).toBe(true);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test('move', () => {
|
|
77
|
+
client.moves.A();
|
|
78
|
+
expect(client.getState().G.moveBody).toBe(true);
|
|
79
|
+
expect(client.getState().G.onMove).toBe(true);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test('invalid move', () => {
|
|
83
|
+
client.moves.invalid();
|
|
84
|
+
expect(client.getState().G.madeInvalidMove).toBeUndefined();
|
|
85
|
+
});
|
|
86
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2018 The boardgame.io Authors
|
|
3
|
+
*
|
|
4
|
+
* Use of this source code is governed by a MIT-style
|
|
5
|
+
* license that can be found in the LICENSE file or at
|
|
6
|
+
* https://opensource.org/licenses/MIT.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import produce from 'immer';
|
|
10
|
+
import type { Plugin } from '../types';
|
|
11
|
+
import { INVALID_MOVE } from '../core/constants';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Plugin that allows using Immer to make immutable changes
|
|
15
|
+
* to G by just mutating it.
|
|
16
|
+
*/
|
|
17
|
+
const ImmerPlugin: Plugin = {
|
|
18
|
+
name: 'plugin-immer',
|
|
19
|
+
|
|
20
|
+
fnWrap:
|
|
21
|
+
(move) =>
|
|
22
|
+
(context, ...args) => {
|
|
23
|
+
let isInvalid = false;
|
|
24
|
+
const newG = produce(context.G, (G) => {
|
|
25
|
+
const result = move({ ...context, G }, ...args);
|
|
26
|
+
if (result === INVALID_MOVE) {
|
|
27
|
+
isInvalid = true;
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
return result;
|
|
31
|
+
});
|
|
32
|
+
if (isInvalid) return INVALID_MOVE;
|
|
33
|
+
return newG;
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export default ImmerPlugin;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2019 The boardgame.io Authors
|
|
3
|
+
*
|
|
4
|
+
* Use of this source code is governed by a MIT-style
|
|
5
|
+
* license that can be found in the LICENSE file or at
|
|
6
|
+
* https://opensource.org/licenses/MIT.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { Client } from '../client/client';
|
|
10
|
+
import type { Game } from '../types';
|
|
11
|
+
|
|
12
|
+
describe('log-metadata', () => {
|
|
13
|
+
test('It sets metadata in a move and then clears the metadata', () => {
|
|
14
|
+
const game: Game = {
|
|
15
|
+
moves: {
|
|
16
|
+
setMetadataMove: ({ log }) => {
|
|
17
|
+
log.setMetadata({
|
|
18
|
+
message: 'test',
|
|
19
|
+
});
|
|
20
|
+
},
|
|
21
|
+
doNothing: ({ G }) => G,
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
const client = Client({ game });
|
|
25
|
+
client.moves.setMetadataMove();
|
|
26
|
+
|
|
27
|
+
expect(client.getState().plugins.log.data).toEqual({});
|
|
28
|
+
expect(client.getState().log[0].metadata).toEqual({
|
|
29
|
+
message: 'test',
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
client.moves.doNothing();
|
|
33
|
+
|
|
34
|
+
expect(client.getState().plugins.log.data).toEqual({});
|
|
35
|
+
expect(client.getState().log[1].metadata).toEqual(undefined);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright 2018 The boardgame.io Authors
|
|
3
|
+
*
|
|
4
|
+
* Use of this source code is governed by a MIT-style
|
|
5
|
+
* license that can be found in the LICENSE file or at
|
|
6
|
+
* https://opensource.org/licenses/MIT.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { Plugin } from '../types';
|
|
10
|
+
|
|
11
|
+
interface LogData {
|
|
12
|
+
metadata?: any;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface LogAPI {
|
|
16
|
+
setMetadata(metadata: any): void;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Plugin that makes it possible to add metadata to log entries.
|
|
21
|
+
* During a move, you can set metadata using ctx.log.setMetadata and it will be
|
|
22
|
+
* available on the log entry for that move.
|
|
23
|
+
*/
|
|
24
|
+
const LogPlugin: Plugin<LogAPI, LogData> = {
|
|
25
|
+
name: 'log',
|
|
26
|
+
|
|
27
|
+
flush: () => ({}),
|
|
28
|
+
|
|
29
|
+
api: ({ data }) => {
|
|
30
|
+
return {
|
|
31
|
+
setMetadata: (metadata) => {
|
|
32
|
+
data.metadata = metadata;
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
setup: () => ({}),
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export default LogPlugin;
|