Finish corr implementation for draw offers (untested)
[vchess.git] / server / models / Game.js
CommitLineData
00f2759e
BA
1var db = require("../utils/database");
2
3/*
4 * Structure table Games:
5 * id: game id (int)
6 * vid: integer (variant id)
ab4f4bf2
BA
7 * fenStart: varchar (initial position)
8 * fen: varchar (current position)
a9b131f1 9 * timeControl: string
00f2759e 10 * score: varchar (result)
83494c7f 11 * created: datetime
00f2759e
BA
12 *
13 * Structure table Players:
14 * gid: ref game id
15 * uid: ref user id
16 * color: character
17 *
18 * Structure table Moves:
ab4f4bf2 19 * gid: ref game id
f41ce580 20 * squares: varchar (description)
ab4f4bf2 21 * message: text
00f2759e
BA
22 * played: datetime
23 * idx: integer
00f2759e
BA
24 */
25
ab4f4bf2 26const GameModel =
00f2759e 27{
a9b131f1 28 create: function(vid, fen, timeControl, players, cb)
ab4f4bf2 29 {
88667455 30 db.serialize(function() {
ab4f4bf2 31 let query =
b7cbbda1
BA
32 "INSERT INTO Games"
33 + " (vid, fenStart, fen, score, timeControl, created, drawOffer)"
d431028c 34 + " VALUES (" + vid + ",'" + fen + "','" + fen + "','*','"
b7cbbda1 35 + timeControl + "'," + Date.now() + "," + false + ")";
8c564f46 36 db.run(query, function(err) {
cf742aaf
BA
37 if (!!err)
38 return cb(err);
8c564f46
BA
39 players.forEach((p,idx) => {
40 const color = (idx==0 ? "w" : "b");
2be5d614
BA
41 query =
42 "INSERT INTO Players VALUES " +
f41ce580 43 "(" + this.lastID + "," + p.id + ",'" + color + "')";
2be5d614
BA
44 db.run(query);
45 });
46 cb(null, {gid: this.lastID});
00f2759e
BA
47 });
48 });
ab4f4bf2 49 },
00f2759e 50
ab4f4bf2
BA
51 // TODO: queries here could be async, and wait for all to complete
52 getOne: function(id, cb)
53 {
54 db.serialize(function() {
f41ce580 55 // TODO: optimize queries?
ab4f4bf2 56 let query =
f41ce580
BA
57 "SELECT g.id, g.vid, g.fen, g.fenStart, g.timeControl, g.score, " +
58 "v.name AS vname " +
59 "FROM Games g " +
60 "JOIN Variants v " +
61 " ON g.vid = v.id " +
62 "WHERE g.id = " + id;
ab4f4bf2
BA
63 db.get(query, (err,gameInfo) => {
64 if (!!err)
65 return cb(err);
00f2759e 66 query =
f41ce580
BA
67 "SELECT p.uid, p.color, u.name " +
68 "FROM Players p " +
69 "JOIN Users u " +
70 " ON p.uid = u.id " +
71 "WHERE p.gid = " + id;
fd7aea36 72 db.all(query, (err2,players) => {
ab4f4bf2
BA
73 if (!!err2)
74 return cb(err2);
75 query =
92b82def 76 "SELECT squares, message, played, idx " +
ab4f4bf2
BA
77 "FROM Moves " +
78 "WHERE gid = " + id;
fd7aea36 79 db.all(query, (err3,moves) => {
ab4f4bf2
BA
80 if (!!err3)
81 return cb(err3);
fd7aea36
BA
82 const game = Object.assign({},
83 gameInfo,
84 {
85 players: players,
86 moves: moves
87 }
88 );
ab4f4bf2
BA
89 return cb(null, game);
90 });
00f2759e
BA
91 });
92 });
93 });
ab4f4bf2 94 },
00f2759e 95
5d04793e 96 getByUser: function(uid, excluded, cb)
ab4f4bf2
BA
97 {
98 db.serialize(function() {
99 // Next query is fine because a player appear at most once in a game
100 const query =
101 "SELECT gid " +
102 "FROM Players " +
5d04793e 103 "WHERE uid " + (excluded ? "<>" : "=") + " " + uid;
afd3240d 104 db.all(query, (err,gameIds) => {
ab4f4bf2
BA
105 if (!!err)
106 return cb(err);
098cd7f1 107 gameIds = gameIds || []; //might be empty
ab4f4bf2 108 let gameArray = [];
afd3240d
BA
109 for (let i=0; i<gameIds.length; i++)
110 {
111 GameModel.getOne(gameIds[i]["gid"], (err2,game) => {
ab4f4bf2
BA
112 if (!!err2)
113 return cb(err2);
114 gameArray.push(game);
afd3240d
BA
115 // Call callback function only when gameArray is complete:
116 if (i == gameIds.length - 1)
117 return cb(null, gameArray);
ab4f4bf2 118 });
afd3240d 119 }
ab4f4bf2
BA
120 });
121 });
122 },
123
411d23cd
BA
124 getPlayers: function(id, cb)
125 {
126 db.serialize(function() {
127 const query =
128 "SELECT id " +
129 "FROM Players " +
130 "WHERE gid = " + id;
131 db.all(query, (err,players) => {
132 return cb(err, players);
133 });
134 });
135 },
136
b7cbbda1 137 // obj can have fields move, fen, drawOffer and/or score
f41ce580 138 update: function(id, obj)
3d55deea 139 {
f41ce580 140 db.parallelize(function() {
3d55deea
BA
141 let query =
142 "UPDATE Games " +
143 "SET ";
b7cbbda1
BA
144 if (!!obj.drawOffer)
145 query += "drawOffer = " + obj.drawOffer + ",";
3d55deea 146 if (!!obj.fen)
afc42601 147 query += "fen = '" + obj.fen + "',";
3d55deea 148 if (!!obj.score)
afc42601 149 query += "score = '" + obj.score + "',";
3d55deea 150 query = query.slice(0,-1); //remove last comma
afc42601 151 query += " WHERE id = " + id;
f41ce580
BA
152 db.run(query);
153 if (!!obj.move)
154 {
afc42601 155 const m = obj.move;
f41ce580 156 query =
92b82def 157 "INSERT INTO Moves (gid, squares, message, played, idx) VALUES " +
f41ce580 158 "(" + id + ",'" + JSON.stringify(m.squares) + "','" + m.message +
92b82def 159 "'," + m.played + "," + m.idx + ")";
f41ce580
BA
160 db.run(query);
161 }
3d55deea
BA
162 });
163 },
164
ab4f4bf2
BA
165 remove: function(id)
166 {
167 db.parallelize(function() {
168 let query =
169 "DELETE FROM Games " +
170 "WHERE id = " + id;
171 db.run(query);
172 query =
173 "DELETE FROM Players " +
174 "WHERE gid = " + id;
175 db.run(query);
176 query =
177 "DELETE FROM Moves " +
178 "WHERE gid = " + id;
179 db.run(query);
180 });
181 },
d431028c
BA
182
183 cleanGamesDb: function()
184 {
185 const tsNow = Date.now();
186 // 86400000 = 24 hours in milliseconds
187 const day = 86400000;
188 db.serialize(function() {
189 let query =
190 "SELECT id,score " +
191 "FROM Games ";
192 db.all(query, (err,games) => {
193 games.forEach(g => {
194 query =
195 "SELECT max(played) AS lastMaj " +
196 "FROM Moves " +
197 "WHERE gid = " + g.id;
f21cd6d9 198 db.get(query, (err2,updated) => {
d431028c
BA
199 if (!updated && tsNow - g.created > 7*day)
200 return GameModel.remove(g.id);
201 const lastMaj = updated.lastMaj;
202 if (g.score != "*" && tsNow - lastMaj > 7*day ||
203 g.score == "*" && tsNow - lastMaj > 91*day)
204 {
205 GameModel.remove(g.id);
206 }
207 });
208 });
209 });
210 });
211 },
00f2759e 212}
ab4f4bf2
BA
213
214module.exports = GameModel;