X-Git-Url: https://git.auder.net/?a=blobdiff_plain;f=server%2Fmodels%2FGame.js;h=f05d2fec82d8f8955feb93ba7503e39bfded4fdd;hb=81b71035639a4204bd7834c9185ea961755af1cf;hp=5f332b758db5cd185c2d8407a9f5654a8ac5625f;hpb=8be8238cbd8bd1eeeb4c101648d6902cae425f7b;p=vchess.git diff --git a/server/models/Game.js b/server/models/Game.js index 5f332b75..f05d2fec 100644 --- a/server/models/Game.js +++ b/server/models/Game.js @@ -15,9 +15,11 @@ const UserModel = require("./User"); * created: datetime * drawOffer: char ('w','b' or '' for none) * rematchOffer: char (similar to drawOffer) - * randomness: integer + * options: varchar * deletedByWhite: boolean * deletedByBlack: boolean + * chatReadWhite: datetime + * chatReadBlack: datetime * * Structure table Moves: * gid: ref game id @@ -32,35 +34,44 @@ const UserModel = require("./User"); * added: datetime */ -const GameModel = -{ +const GameModel = { + checkGameInfo: function(g) { return ( g.vid.toString().match(/^[0-9]+$/) && g.cadence.match(/^[0-9dhms +]+$/) && - g.randomness.toString().match(/^[0-2]$/) && g.fen.match(/^[a-zA-Z0-9, /-]*$/) && g.players.length == 2 && g.players.every(p => p.id.toString().match(/^[0-9]+$/)) ); }, - create: function(vid, fen, randomness, cadence, players, cb) { + incrementCounter: function(vid, cb) { + db.serialize(function() { + let query = + "UPDATE GameStat " + + "SET total = total + 1 " + + "WHERE vid = " + vid; + db.run(query, cb); + }); + }, + + create: function(vid, fen, options, cadence, players, cb) { db.serialize(function() { let query = "INSERT INTO Games " + "(" + - "vid, fenStart, fen, randomness, " + + "vid, fenStart, fen, options, " + "white, black, " + "cadence, created" + ") " + "VALUES " + "(" + - vid + ",'" + fen + "','" + fen + "'," + randomness + "," + + vid + ",'" + fen + "','" + fen + "',?," + players[0].id + "," + players[1].id + "," + "'" + cadence + "'," + Date.now() + ")"; - db.run(query, function(err) { + db.run(query, options, function(err) { cb(err, { id: this.lastID }); }); }); @@ -72,13 +83,11 @@ const GameModel = db.serialize(function() { let query = "SELECT " + - "g.id, g.fen, g.fenStart, g.cadence, g.created, " + - "g.white, g.black, g.score, g.scoreMsg, " + - "g.drawOffer, g.rematchOffer, v.name AS vname " + - "FROM Games g " + - "JOIN Variants v " + - " ON g.vid = v.id " + - "WHERE g.id = " + id; + "id, vid, fen, fenStart, cadence, created, " + + "white, black, options, score, scoreMsg, " + + "chatReadWhite, chatReadBlack, drawOffer, rematchOffer " + + "FROM Games " + + "WHERE id = " + id; db.get(query, (err, gameInfo) => { if (!gameInfo) { cb(err || { errmsg: "Game not found" }, undefined); @@ -104,13 +113,12 @@ const GameModel = "WHERE gid = " + id; db.all(query, (err4, chats) => { const game = Object.assign( - {}, - gameInfo, { players: players, moves: moves, chats: chats - } + }, + gameInfo ); cb(null, game); }); @@ -124,17 +132,19 @@ const GameModel = getObserved: function(uid, cursor, cb) { db.serialize(function() { let query = - "SELECT id, vid, cadence, created, score, white, black " + - "FROM Games "; - if (uid > 0) query += - "WHERE " + - " created < " + cursor + " AND " + - " white <> " + uid + " AND " + - " black <> " + uid + " "; + "SELECT id, vid, cadence, options, created, score, white, black " + + "FROM Games " + + "WHERE created < " + cursor + " "; + if (uid > 0) { + query += + " AND white <> " + uid + " " + + " AND black <> " + uid + " "; + } query += "ORDER BY created DESC " + "LIMIT 20"; //TODO: 20 hard-coded... db.all(query, (err, games) => { + games = games || []; // Query players names let pids = {}; games.forEach(g => { @@ -142,6 +152,7 @@ const GameModel = if (!pids[g.black]) pids[g.black] = true; }); UserModel.getByIds(Object.keys(pids), (err2, users) => { + users = users || []; let names = {}; users.forEach(u => { names[u.id] = u.name; }); cb( @@ -152,6 +163,7 @@ const GameModel = id: g.id, vid: g.vid, cadence: g.cadence, + options: g.options, created: g.created, score: g.score, players: [ @@ -171,13 +183,11 @@ const GameModel = getRunning: function(uid, cb) { db.serialize(function() { let query = - "SELECT g.id, g.cadence, g.created, " + - "g.white, g.black, v.name AS vname " + - "FROM Games g " + - "JOIN Variants v " + - " ON g.vid = v.id " + + "SELECT id, vid, cadence, options, created, white, black " + + "FROM Games " + "WHERE score = '*' AND (white = " + uid + " OR black = " + uid + ")"; db.all(query, (err, games) => { + games = games || []; // Get movesCount (could be done in // with next query) query = "SELECT gid, COUNT(*) AS nbMoves " + @@ -194,6 +204,7 @@ const GameModel = if (!pids[g.black]) pids[g.black] = true; }); UserModel.getByIds(Object.keys(pids), (err2, users) => { + users = users || []; let names = {}; users.forEach(u => { names[u.id] = u.name; }); cb( @@ -202,8 +213,9 @@ const GameModel = g => { return { id: g.id, - vname: g.vname, + vid: g.vid, cadence: g.cadence, + options: g.options, created: g.created, score: g.score, movesCount: movesCounts[g.id] || 0, @@ -225,12 +237,9 @@ const GameModel = getCompleted: function(uid, cursor, cb) { db.serialize(function() { let query = - "SELECT g.id, g.cadence, g.created, g.score, g.scoreMsg, " + - "g.white, g.black, g.deletedByWhite, g.deletedByBlack, " + - "v.name AS vname " + - "FROM Games g " + - "JOIN Variants v " + - " ON g.vid = v.id " + + "SELECT id, vid, cadence, options, created, score, scoreMsg, " + + "white, black, deletedByWhite, deletedByBlack " + + "FROM Games " + "WHERE " + " score <> '*' AND" + " created < " + cursor + " AND" + @@ -249,6 +258,7 @@ const GameModel = "ORDER BY created DESC " + "LIMIT 20"; db.all(query, (err, games) => { + games = games || []; // Query player names let pids = {}; games.forEach(g => { @@ -256,6 +266,7 @@ const GameModel = if (!pids[g.black]) pids[g.black] = true; }); UserModel.getByIds(Object.keys(pids), (err2, users) => { + users = users || []; let names = {}; users.forEach(u => { names[u.id] = u.name; }); cb( @@ -264,8 +275,9 @@ const GameModel = g => { return { id: g.id, - vname: g.vname, + vid: g.vid, cadence: g.cadence, + options: g.options, created: g.created, score: g.score, scoreMsg: g.scoreMsg, @@ -306,9 +318,11 @@ const GameModel = ) && ( !obj.rematchOffer || !!(obj.rematchOffer.match(/^[wbn]$/)) ) && ( - !obj.fen || !!(obj.fen.match(/^[a-zA-Z0-9, /-]*$/)) + !obj.fen || !!(obj.fen.match(/^[a-zA-Z0-9,.:{}\[\]" /-]*$/)) ) && ( !obj.score || !!(obj.score.match(/^[012?*\/-]+$/)) + ) && ( + !obj.chatRead || ['w','b'].includes(obj.chatRead) ) && ( !obj.scoreMsg || !!(obj.scoreMsg.match(/^[a-zA-Z ]+$/)) ) && ( @@ -343,6 +357,10 @@ const GameModel = const myColor = obj.deletedBy == 'w' ? "White" : "Black"; modifs += "deletedBy" + myColor + " = true,"; } + if (!!obj.chatRead) { + const myColor = obj.chatRead == 'w' ? "White" : "Black"; + modifs += "chatRead" + myColor + " = " + Date.now() + ","; + } if (!!obj.score) { modifs += "score = '" + obj.score + "'," + "scoreMsg = '" + obj.scoreMsg + "',"; @@ -370,8 +388,10 @@ const GameModel = "(" + id + ",?," + Date.now() + "," + obj.move.idx + ")"; db.run(query, JSON.stringify(obj.move.squares)); finishAndSendQuery(); - } else cb({ errmsg: "Wrong move index" }); - } else { + } + else cb({ errmsg: "Wrong move index" }); + } + else { if (ret.maxIdx < 2) cb({ errmsg: "Time not over" }); else { // We also need the game cadence @@ -380,7 +400,7 @@ const GameModel = "FROM Games " + "WHERE id = " + id; db.get(query, (err2, ret2) => { - const daysTc = parseInt(ret2.cadence.match(/\(^[0-9]+\)/)[0]); + const daysTc = parseInt(ret2.cadence.match(/^[0-9]+/)[0]); if (Date.now() - ret.lastPlayed > daysTc * 24 * 3600 * 1000) finishAndSendQuery(); else cb({ errmsg: "Time not over" }); @@ -388,14 +408,16 @@ const GameModel = } } }); - } else finishAndSendQuery(); + } + else finishAndSendQuery(); // NOTE: chat and delchat are mutually exclusive if (!!obj.chat) { const query = "INSERT INTO Chats (gid, msg, name, added) VALUES (" + id + ",?,'" + obj.chat.name + "'," + Date.now() + ")"; db.run(query, obj.chat.msg); - } else if (obj.delchat) { + } + else if (obj.delchat) { const query = "DELETE " + "FROM Chats " + @@ -447,14 +469,14 @@ const GameModel = const day = 86400000; db.serialize(function() { let query = - "SELECT id, created " + + "SELECT id, created, cadence, score " + "FROM Games"; db.all(query, (err, games) => { query = "SELECT gid, count(*) AS nbMoves, MAX(played) AS lastMaj " + "FROM Moves " + "GROUP BY gid"; - db.get(query, (err2, mstats) => { + db.all(query, (err2, mstats) => { // Reorganize moves data to avoid too many array lookups: let movesGroups = {}; mstats.forEach(ms => { @@ -464,28 +486,56 @@ const GameModel = }; }); // Remove games still not really started, - // with no action in the last 3 months: + // with no action in the last 2 weeks, or result != '*': let toRemove = []; + let lostOnTime = [ [], [] ]; games.forEach(g => { if ( ( !movesGroups[g.id] && - tsNow - g.created > 91*day + (g.score != '*' || tsNow - g.created > 14*day) ) || ( + !!movesGroups[g.id] && movesGroups[g.id].nbMoves == 1 && - tsNow - movesGroups[g.id].lastMaj > 91*day + (g.score != '*' || tsNow - movesGroups[g.id].lastMaj > 14*day) ) ) { toRemove.push(g.id); } + // Set score if lost on time and >= 2 moves: + else if ( + g.score == '*' && + !!movesGroups[g.id] && + movesGroups[g.id].nbMoves >= 2 && + tsNow - movesGroups[g.id].lastMaj > + // cadence in days * nb seconds per day: + parseInt(g.cadence.slice(0, -1), 10) * day + ) { + lostOnTime[movesGroups[g.id].nbMoves % 2].push(g.id); + } }); if (toRemove.length > 0) GameModel.remove(toRemove); + if (lostOnTime.some(l => l.length > 0)) { + db.parallelize(function() { + for (let i of [0, 1]) { + if (lostOnTime[i].length > 0) { + const score = (i == 0 ? "0-1" : "1-0"); + const query = + "UPDATE Games " + + "SET score = '" + score + "', scoreMsg = 'Time' " + + "WHERE id IN (" + lostOnTime[i].join(',') + ")"; + db.run(query); + } + } + }); + } }); }); }); } -} + +}; module.exports = GameModel;