Skip to content

Commit 003fe46

Browse files
committed
MongoDB connector
1 parent e3f7e91 commit 003fe46

9 files changed

Lines changed: 277 additions & 21 deletions

File tree

package-lock.json

Lines changed: 104 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@
8787
"koa-helmet": "^3.2.0",
8888
"koa-webpack": "^1.0.0",
8989
"lint-staged": "^6.0.1",
90+
"mongo-mock": "^3.1.0",
9091
"open-browser-webpack-plugin": "0.0.5",
9192
"prettier": "^1.10.2",
9293
"react": "^16.0.0",
@@ -113,6 +114,8 @@
113114
"koa-router": "^7.2.1",
114115
"koa-socket": "^4.4.0",
115116
"koa-static": "^4.0.1",
117+
"lru-native": "^0.4.0",
118+
"mongodb": "^3.0.3",
116119
"mousetrap": "^1.6.1",
117120
"prop-types": "^15.5.10",
118121
"react-json-view": "^1.13.0",

src/client/multiplayer/multiplayer.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ export class Multiplayer {
5858
this.socket.emit(
5959
'action',
6060
action,
61-
state._id,
61+
state._stateID,
6262
this.gameID,
6363
this.playerID
6464
);

src/core/reducer.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export function createGameReducer({ game, numPlayers, multiplayer }) {
3636
// A monotonically non-decreasing ID to ensure that
3737
// state updates are only allowed from clients that
3838
// are at the same version that the server.
39-
_id: 0,
39+
_stateID: 0,
4040

4141
// A snapshot of this object so that actions can be
4242
// replayed over it to view old snapshots.
@@ -84,7 +84,7 @@ export function createGameReducer({ game, numPlayers, multiplayer }) {
8484
ctx = { ...ctx, _random: PRNGState.get() };
8585

8686
const log = [...state.log, action];
87-
return { ...state, G, ctx, log, _id: state._id + 1 };
87+
return { ...state, G, ctx, log, _stateID: state._stateID + 1 };
8888
}
8989

9090
case Actions.MAKE_MOVE: {
@@ -110,7 +110,7 @@ export function createGameReducer({ game, numPlayers, multiplayer }) {
110110
}
111111

112112
const log = [...state.log, action];
113-
state = { ...state, G, ctx, log, _id: state._id + 1 };
113+
state = { ...state, G, ctx, log, _stateID: state._stateID + 1 };
114114

115115
// If we're on the client, just process the move
116116
// and no triggers in multiplayer mode.

src/core/reducer.test.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,15 @@ const game = Game({
2323

2424
const endTurn = () => gameEvent('endTurn');
2525

26-
test('_id is incremented', () => {
26+
test('_stateID is incremented', () => {
2727
const reducer = createGameReducer({ game });
2828

2929
let state = undefined;
3030

3131
state = reducer(state, makeMove('unknown'));
32-
expect(state._id).toBe(1);
32+
expect(state._stateID).toBe(1);
3333
state = reducer(state, endTurn());
34-
expect(state._id).toBe(2);
34+
expect(state._stateID).toBe(2);
3535
});
3636

3737
test('makeMove', () => {

src/server/db.js

Lines changed: 96 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
* https://opensource.org/licenses/MIT.
77
*/
88

9+
const MongoClient = require('mongodb').MongoClient;
10+
const LRUCache = require('lru-native');
11+
912
/**
1013
* InMemory data storage.
1114
*/
@@ -17,6 +20,14 @@ export class InMemory {
1720
this.games = new Map();
1821
}
1922

23+
/**
24+
* Connect.
25+
* No-op for the InMemory instance.
26+
*/
27+
async connect() {
28+
return;
29+
}
30+
2031
/**
2132
* Write the game state to the in-memory object.
2233
* @param {string} id - The game id.
@@ -35,12 +46,96 @@ export class InMemory {
3546
async get(id) {
3647
return await this.games.get(id);
3748
}
49+
3850
/**
39-
* Read the game state from the in-memory object.
51+
* Check if a particular game id exists.
4052
* @param {string} id - The game id.
4153
* @returns {boolean} - True if a game with this id exists.
4254
*/
4355
async has(id) {
4456
return await this.games.has(id);
4557
}
4658
}
59+
60+
/**
61+
* MongoDB connector.
62+
*/
63+
export class Mongo {
64+
/**
65+
* Creates a new Mongo connector object.
66+
*/
67+
constructor({ url, dbname, cacheSize, mockClient }) {
68+
if (cacheSize === undefined) cacheSize = 1000;
69+
70+
this.client = mockClient || MongoClient;
71+
this.url = url;
72+
this.dbname = dbname;
73+
this.cache = new LRUCache({ maxElements: cacheSize });
74+
}
75+
76+
/**
77+
* Connect to the instance.
78+
*/
79+
async connect() {
80+
const c = await this.client.connect(this.url);
81+
this.db = c.db(this.dbname);
82+
return;
83+
}
84+
85+
/**
86+
* Write the game state.
87+
* @param {string} id - The game id.
88+
* @param {object} store - A game state to persist.
89+
*/
90+
async set(id, state) {
91+
this.cache.set(id, state);
92+
93+
const col = this.db.collection(id);
94+
delete state._id;
95+
await col.insert(state);
96+
97+
return;
98+
}
99+
100+
/**
101+
* Read the game state.
102+
* @param {string} id - The game id.
103+
* @returns {object} - A game state, or undefined
104+
* if no game is found with this id.
105+
*/
106+
async get(id) {
107+
const item = this.cache.get(id);
108+
if (item !== undefined) {
109+
return item;
110+
}
111+
112+
const col = this.db.collection(id);
113+
const docs = await col
114+
.find()
115+
.sort({ _id: -1 })
116+
.limit(1)
117+
.toArray();
118+
119+
this.cache.set(id, docs[0]);
120+
return docs[0];
121+
}
122+
123+
/**
124+
* Check if a particular game exists.
125+
* @param {string} id - The game id.
126+
* @returns {boolean} - True if a game with this id exists.
127+
*/
128+
async has(id) {
129+
const item = this.cache.get(id);
130+
if (item !== undefined) {
131+
return true;
132+
}
133+
134+
const col = this.db.collection(id);
135+
const docs = await col
136+
.find()
137+
.limit(1)
138+
.toArray();
139+
return docs.length > 0;
140+
}
141+
}

0 commit comments

Comments
 (0)