-// TODO: general methods to access/retrieve from storage, to be generalized
-// https://developer.mozilla.org/fr/docs/Web/API/API_IndexedDB
-// https://dexie.org/
-
import { extractTime } from "@/utils/timeControl";
-import { shuffle } from "@/utils/alea";
+
+// TODO: show game structure
+//const newItem = [
+// { gameId: "", players: [], timeControl: "", clocks: [] }
+//];
+
+function dbOperation(callback)
+{
+ let db = null;
+ let DBOpenRequest = window.indexedDB.open("vchess", 4);
+
+ DBOpenRequest.onerror = function(event) {
+ alert("Database error: " + event.target.errorCode);
+ };
+
+ DBOpenRequest.onsuccess = function(event) {
+ db = DBOpenRequest.result;
+ callback(db);
+ db.close();
+ };
+
+ DBOpenRequest.onupgradeneeded = function(event) {
+ let db = event.target.result;
+ db.onerror = function(event) {
+ alert("Error while loading database: " + event.target.errorCode);
+ };
+ // Create objectStore for vchess->games
+ db.createObjectStore("games", { keyPath: "gameId" });
+ }
+}
+
+// Optional callback to get error status
+function addGame(game, callback)
+{
+ dbOperation((db) => {
+ let transaction = db.transaction(["games"], "readwrite");
+ if (callback)
+ {
+ transaction.oncomplete = function() {
+ callback({}); //everything's fine
+ }
+ transaction.onerror = function() {
+ callback({errmsg: "addGame failed: " + transaction.error});
+ };
+ }
+ let objectStore = transaction.objectStore("games");
+ objectStore.add(game);
+ });
+}
+
+// Clear current live game from localStorage
+function clear() {
+ localStorage.deleteItem("gameInfo");
+ localStorage.deleteItem("gameState");
+}
+
+// Current live game:
+function getCurrent()
+{
+ return Object.assign({},
+ JSON.parse(localStorage.getItem("gameInfo")),
+ JSON.parse(localStorage.getItem("gameState")));
+}
+
+// Only called internally after a score update
+function transferToDb()
+{
+ addGame(getCurrent(), (err) => {
+ if (!!err.errmsg)
+ return err;
+ clear();
+ });
+}
export const GameStorage =
{
+ // localStorage:
init: function(o)
{
- localStorage.setItem("gameId", o.gameId);
- localStorage.setItem("vname", o.vname);
- // NOTE: when >= 3 players, better use an array + shuffle for mycolor
- const mycolor = (Math.random() < 0.5 ? "w" : "b");
- localStorage.setItem("mycolor", mycolor);
- localStorage.setItem("fenStart", o.fenStart);
- localStorage.setItem("fen", o.fenStart);
- localStorage.setItem("moves", JSON.stringify([]));
- // Shuffle players order (white then black then other colors).
- localStorage.setItem("players", JSON.stringify(shuffle(o.players)));
// Extract times (in [milli]seconds), set clocks, store in localStorage
const tc = extractTime(o.timeControl);
- localStorage.setItem("timeControl", o.timeControl);
- localStorage.setItem("clocks", JSON.stringify(
- [...Array(o.players.length)].fill(tc.mainTime)));
- localStorage.setItem("increment", tc.increment);
- localStorage.setItem("started", JSON.stringify(
- [...Array(o.players.length)].fill(false)));
- localStorage.setItem("score", "*");
- localStorage.setItem("started", JSON.stringify(
- [...Array(o.players.length)].fill(false)));
- localStorage.setItem("clocks", JSON.stringify(
- [...Array(o.players.length)].fill(0)));
- localStorage.setItem("mode", "live"); //function for live games only
+
+ // game infos: constant
+ const gameInfo =
+ {
+ gameId: o.gameId,
+ vname: o.vname,
+ fenStart: o.fenStart,
+ players: o.players,
+ timeControl: o.timeControl,
+ increment: tc.increment,
+ mode: "live", //function for live games only
+ };
+
+ // game state: will be updated
+ const gameState =
+ {
+ fen: o.fenStart,
+ moves: [],
+ clocks: [...Array(o.players.length)].fill(tc.mainTime),
+ initime: undefined,
+ score: "*",
+ };
+
+ localStorage.setItem("gameInfo", JSON.stringify(gameInfo));
+ localStorage.setItem("gameState", JSON.stringify(gameState));
},
+ getInitime: function()
+ {
+ const gameState = JSON.parse(localStorage.getItem("gameState"));
+ return gameState.initime;
+ },
+
+ // localStorage:
// TODO: also option to takeback a move ?
// NOTE: for live games only (all on server for corr)
- update: function(move, score) //game ID is not required
+ update: function(o) //colorIdx, move, fen, elapsed, increment, initime, score
{
- if (!!move)
+ let gameState = JSON.parse(localStorage.getItem("gameState"));
+ if (!!o.move)
{
- let moves = JSON.parse(localStorage.getItem("moves"));
- moves.push(move);
- localStorage.setItem("moves", JSON.stringify(moves));
+ gameState.moves.push(o.move);
+ gameState.fen = o.fen;
+ if (!!o.elapsed) //NaN if first move in game
+ gameState.clocks[o.colorIdx] += (o.increment - o.elapsed);
}
- if (!!score)
- localStorage.setItem("score", score);
+ if (!!o.initime) //just a flag (true)
+ gameState.initime = Date.now();
+ if (!!o.score)
+ gameState.score = o.score;
+ localStorage.setItem("gameState", JSON.stringify(gameState));
+ if (!!o.score && o.score != "*")
+ transferToDb(); //game is over
},
- transferToDb: function()
+ // indexedDB:
+ // Since DB requests are asynchronous, require a callback using the result
+ // TODO: option for remote retrieval (third arg, or just "gameRef")
+ getLocal: function(gameId, callback)
{
- // TODO: take finished game on localStorage and transfer it to indexedDB
+ let games = [];
+ dbOperation((db) => {
+ // TODO: if gameId is provided, limit search to gameId (just .get(gameId). ...)
+ let objectStore = db.transaction('games').objectStore('games');
+ objectStore.openCursor().onsuccess = function(event) {
+ var cursor = event.target.result;
+ // if there is still another cursor to go, keep running this code
+ if (cursor)
+ {
+ games.push(cursor.value);
+ cursor.continue();
+ }
+ else
+ callback(games);
+ }
+ });
},
- // "computer mode" clearing is done through the menu
- clear: function()
+ // Delete a game in indexedDB
+ remove: function(gameId, callback)
{
- localStorage.setItem("gameId", o.gameId);
- localStorage.setItem("vname", o.vname);
- // NOTE: when >= 3 players, better use an array + shuffle for mycolor
- const mycolor = (Math.random() < 0.5 ? "w" : "b");
- localStorage.setItem("mycolor", mycolor);
- localStorage.setItem("fenStart", o.fenStart);
- localStorage.setItem("fen", o.fenStart);
- localStorage.setItem("moves", JSON.stringify([]));
- // Shuffle players order (white then black then other colors).
- localStorage.setItem("players", JSON.stringify(shuffle(o.players)));
- // Extract times (in [milli]seconds), set clocks, store in localStorage
- const tc = extractTime(o.timeControl);
- localStorage.setItem("timeControl", o.timeControl);
- localStorage.setItem("clocks", JSON.stringify(
- [...Array(o.players.length)].fill(tc.mainTime)));
- localStorage.setItem("increment", tc.increment);
- localStorage.setItem("started", JSON.stringify(
- [...Array(o.players.length)].fill(false)));
- localStorage.setItem("score", "*");
- localStorage.setItem("started", JSON.stringify(
- [...Array(o.players.length)].fill(false)));
- localStorage.setItem("clocks", JSON.stringify(
- [...Array(o.players.length)].fill(0)));
- localStorage.setItem("mode", "live"); //function for live games only
-
-
- // TODO: refresh, and implement "transfert" function (to indexedDB)
- localStorage["myid"];
- localStorage["oppid"];
- delete localStorage["gameId"];
- delete localStorage["variant"];
- delete localStorage["mycolor"];
- delete localStorage["fenStart"];
- delete localStorage["moves"];
+ dbOperation((db) => {
+ let transaction = db.transaction(["games"], "readwrite");
+ if (callback)
+ {
+ transaction.oncomplete = function() {
+ callback({}); //everything's fine
+ }
+ transaction.onerror = function() {
+ callback({errmsg: "game removal failed: " + transaction.error});
+ };
+ }
+ transaction.objectStore("games").delete(gameId);
+ });
},
- // TODO: game or gameInfo ?! --> when moves are played, it's a game, otherwise info
- get: function(gameRef)
+ // Retrieve any live game from its identifiers (remote or not, running or not)
+ // NOTE: need callback because result might be obtained asynchronously
+ get: function(gameRef, callback)
{
const gid = gameRef.id;
const rid = gameRef.rid; //may be blank
- let game = {};
- if (localStorage.getItem("gameId") === gid)
+ if (!!rid)
{
- // Retrieve running game from localStorage
- game.score = localStorage.getItem("score");
- game.mycolor = localStorage.getItem("mycolor");
- game.fenStart = localStorage.getItem("fenStart");
- game.fen = localStorage.getItem("fen");
- game.moves = JSON.parse(localStorage.getItem("moves"));
- game.players = JSON.parse(localStorage.getItem("players"));
- game.started = JSON.parse(localStorage.getItem("started"));
- game.clocks = JSON.parse(localStorage.getItem("clocks"));
- game.timeControl = localStorage.getItem("timeControl");
- game.increment = localStorage.getItem("increment");
- game.vname = localStorage.getItem("vname");
- game.mode = "live";
+ // TODO: send request to server which forward to user sid == rid,
+ // need to listen to "remote game" event in main hall ?
+ return callback({}); //means "the game will arrive later" (TODO...)
}
- else
+
+ const gameInfoStr = localStorage.getItem("gameInfo");
+ if (gameInfoStr)
{
- // Find the game in indexedDB, on server or remotely: TODO
+ const gameInfo = JSON.parse(gameInfoStr);
+ if (gameInfo.gameId == gid)
+ {
+ const gameState = JSON.parse(localStorage.getItem("gameState"));
+ return callback(Object.assign({}, gameInfo, gameState));
+ }
}
- return game;
+
+ // Game is local and not running
+ GameStorage.getLocal(gid, callback);
},
};