fb249a7c8d6a085e5e133ef2b915e06d5c8b02e3
1 const db
= require("../utils/database");
2 const UserModel
= require("./User");
5 * Structure table Games:
7 * vid: integer (variant id)
8 * fenStart: varchar (initial position)
9 * fen: varchar (current position)
11 * score: varchar (result)
12 * scoreMsg: varchar ("Time", "Mutual agreement"...)
14 * drawOffer: char ('w','b' or '' for none)
16 * Structure table Players:
21 * Structure table Moves:
23 * squares: varchar (description)
27 * Structure table Chats:
36 checkGameInfo: function(g
) {
37 if (!g
.vid
.toString().match(/^[0-9]+$/))
38 return "Wrong variant ID";
39 if (!g
.cadence
.match(/^[0-9dhms
+]+$/))
40 return "Wrong characters in time control";
41 if (!g
.fen
.match(/^[a-zA-Z0-9, /-]*$/))
42 return "Bad FEN string";
43 if (g
.players
.length
!= 2)
44 return "Need exactly 2 players";
45 if (g
.players
.some(p
=> !p
.id
.toString().match(/^[0-9]+$/)))
46 return "Wrong characters in player ID";
50 create: function(vid
, fen
, cadence
, players
, cb
)
52 db
.serialize(function() {
54 "INSERT INTO Games " +
55 "(vid, fenStart, fen, score, cadence, created, drawOffer) " +
57 "(" + vid
+ ",'" + fen
+ "','" + fen
+ "','*','" + cadence
+ "'," + Date
.now() + ",'')";
58 db
.run(query
, function(err
) {
61 players
.forEach((p
,idx
) => {
62 const color
= (idx
==0 ? "w" : "b");
64 "INSERT INTO Players VALUES " +
65 "(" + this.lastID
+ "," + p
.id
+ ",'" + color
+ "')";
68 cb(null, {gid: this.lastID
});
73 // TODO: some queries here could be async
74 getOne: function(id
, light
, cb
)
76 db
.serialize(function() {
78 // NOTE: g.scoreMsg can be NULL
79 // (in this case score = "*" and no reason to look at it)
80 "SELECT g.id, g.vid, g.fen, g.fenStart, g.cadence, g.created, g.score, " +
81 "g.scoreMsg, g.drawOffer, v.name AS vname " +
86 db
.get(query
, (err
,gameInfo
) => {
90 "SELECT p.uid, p.color, u.name " +
94 "WHERE p.gid = " + id
;
95 db
.all(query
, (err2
,players
) => {
100 const game
= Object
.assign({},
104 return cb(null, game
);
107 "SELECT squares, played, idx " +
110 db
.all(query
, (err3
,moves
) => {
114 "SELECT msg, name, added " +
117 db
.all(query
, (err4
,chats
) => {
120 const game
= Object
.assign({},
128 return cb(null, game
);
136 // For display on MyGames or Hall: no need for moves or chats
137 getByUser: function(uid
, excluded
, cb
)
139 db
.serialize(function() {
143 // Special case anonymous user: show all games
145 "SELECT id AS gid " +
155 "HAVING COUNT(uid = " + uid
+ " OR NULL) " +
156 (excluded
? " = 0" : " > 0");
158 db
.all(query
, (err
,gameIds
) => {
159 if (!!err
|| gameIds
.length
== 0)
163 for (let i
=0; i
<gameIds
.length
; i
++)
165 GameModel
.getOne(gameIds
[i
]["gid"], true, (err2
,game
) => {
168 gameArray
.push(game
);
169 kounter
++; //TODO: let's hope this is atomic?!
170 // Call callback function only when gameArray is complete:
171 if (kounter
== gameIds
.length
)
172 return cb(null, gameArray
);
179 getPlayers: function(id
, cb
)
181 db
.serialize(function() {
186 db
.all(query
, (err
,players
) => {
187 return cb(err
, players
);
192 checkGameUpdate: function(obj
)
194 // Check all that is possible (required) in obj:
197 if (!obj
.move.played
.toString().match(/^[0-9]+$/))
198 return "Wrong move played time";
199 if (!obj
.move.idx
.toString().match(/^[0-9]+$/))
200 return "Wrong move index";
202 if (!!obj
.drawOffer
&& !obj
.drawOffer
.match(/^[wbtn]$/))
203 return "Wrong draw offer format";
204 if (!!obj
.fen
&& !obj
.fen
.match(/^[a-zA-Z0-9, /-]*$/))
205 return "Wrong FEN string";
206 if (!!obj
.score
&& !obj
.score
.match(/^[012?*\/-]+$/))
207 return "Wrong characters in score";
208 if (!!obj
.scoreMsg
&& !obj
.scoreMsg
.match(/^[a
-zA
-Z
]+$/))
209 return "Wrong characters in score message";
211 return UserModel
.checkNameEmail({name: obj
.chat
.name
});
215 // obj can have fields move, chat, fen, drawOffer and/or score
216 update: function(id
, obj
)
218 db
.parallelize(function() {
224 modifs
+= "message = message || ' ' || '" + obj
.message
+ "',";
225 // NOTE: if drawOffer is set, we should check that it's player's turn
226 // A bit overcomplicated. Let's trust the client on that for now...
229 if (obj
.drawOffer
== "n") //Special "None" update
231 modifs
+= "drawOffer = '" + obj
.drawOffer
+ "',";
234 modifs
+= "fen = '" + obj
.fen
+ "',";
236 modifs
+= "score = '" + obj
.score
+ "',";
238 modifs
+= "scoreMsg = '" + obj
.scoreMsg
+ "',";
239 modifs
= modifs
.slice(0,-1); //remove last comma
240 if (modifs
.length
> 0)
242 query
+= modifs
+ " WHERE id = " + id
;
249 "INSERT INTO Moves (gid, squares, played, idx) VALUES " +
250 "(" + id
+ ",?," + m
.played
+ "," + m
.idx
+ ")";
251 db
.run(query
, JSON
.stringify(m
.squares
));
256 "INSERT INTO Chats (gid, msg, name, added) VALUES ("
257 + id
+ ",?,'" + obj
.chat
.name
+ "'," + Date
.now() + ")";
258 db
.run(query
, obj
.chat
.msg
);
265 db
.parallelize(function() {
267 "DELETE FROM Games " +
271 "DELETE FROM Players " +
275 "DELETE FROM Moves " +
279 "DELETE FROM Chats " +
285 cleanGamesDb: function()
287 const tsNow
= Date
.now();
288 // 86400000 = 24 hours in milliseconds
289 const day
= 86400000;
290 db
.serialize(function() {
292 "SELECT id, created " +
294 db
.all(query
, (err
,games
) => {
297 "SELECT count(*) as nbMoves, max(played) AS lastMaj " +
299 "WHERE gid = " + g
.id
;
300 db
.get(query
, (err2
,mstats
) => {
301 // Remove games still not really started,
302 // with no action in the last 3 months:
303 if ((mstats
.nbMoves
== 0 && tsNow
- g
.created
> 91*day
) ||
304 (mstats
.nbMoves
== 1 && tsNow
- mstats
.lastMaj
> 91*day
))
306 GameModel
.remove(g
.id
);
315 module
.exports
= GameModel
;