1 | import { REMOTE_ACTION, hashState, Keys } from "@cardcore/util";
|
2 |
|
3 | export default function createGameMiddleware(gameActions, clientActions) {
|
4 | const { clientFetch, clientPoll, clientNext } = clientActions;
|
5 |
|
6 | return function gameMiddleware(store) {
|
7 | return next => {
|
8 | return async action => {
|
9 |
|
10 |
|
11 |
|
12 | if (typeof action === "function") {
|
13 | return action(store.dispatch, store.getState);
|
14 | }
|
15 |
|
16 |
|
17 | if (!gameActions[action.type]) {
|
18 | return Promise.resolve(next(action));
|
19 | }
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 | const prevState = store.getState();
|
27 | const me = prevState.client.keys.id;
|
28 | let prevHash = null;
|
29 |
|
30 | if (action.type !== gameActions.CREATE_GAME) {
|
31 | prevHash = hashState(prevState);
|
32 | }
|
33 | if (!action[REMOTE_ACTION]) {
|
34 | action = {
|
35 | ...action,
|
36 | agent: me,
|
37 | prev: prevHash
|
38 | };
|
39 | } else {
|
40 | if (action.prev !== prevHash) {
|
41 | throw new Error(`hash mismatch, ${action.next} !== ${nextHash}`);
|
42 | }
|
43 | }
|
44 | next(action);
|
45 | const nextState = store.getState();
|
46 | const nextHash = hashState(nextState);
|
47 |
|
48 | if (action[REMOTE_ACTION]) {
|
49 | if (action.next !== nextHash) {
|
50 | throw new Error(`hash mismatch, ${action.next} !== ${nextHash}`);
|
51 | }
|
52 | } else {
|
53 | action = {
|
54 | ...action,
|
55 | next: nextHash
|
56 | };
|
57 | }
|
58 |
|
59 |
|
60 | if (!action[REMOTE_ACTION]) {
|
61 | const signedAction = Keys.signAction(prevState, action);
|
62 | let res;
|
63 | try {
|
64 | res = await store.dispatch(
|
65 | clientFetch(`/${encodeURIComponent(nextHash)}`, {
|
66 | method: "POST",
|
67 | body: signedAction,
|
68 | headers: {
|
69 | "content-type": "application/json"
|
70 | }
|
71 | })
|
72 | );
|
73 | } catch (e) {
|
74 | throw new Error(
|
75 | "Failed to create an action. We should probably handle this error."
|
76 | );
|
77 | }
|
78 | if (!res.ok) {
|
79 | throw new Error(await res.text());
|
80 | }
|
81 | }
|
82 |
|
83 |
|
84 | else {
|
85 | if (!Keys.verifyAction(prevState, action)) {
|
86 | throw new Error(
|
87 | `Remote action failed to verify: ${JSON.stringify(action)}`
|
88 | );
|
89 | }
|
90 | }
|
91 |
|
92 | await store.dispatch(clientNext());
|
93 | store.dispatch(clientPoll());
|
94 | };
|
95 | };
|
96 | };
|
97 | }
|