On the way to simplify : gameState + gameInfo everywhere = game
[vchess.git] / client / src / utils / storage.js
CommitLineData
6d01bb17
BA
1import { extractTime } from "@/utils/timeControl";
2import { shuffle } from "@/utils/alea";
ce87ac6a 3
8f5780d8
BA
4// TODO: show game structure
5//const newItem = [
6// { gameId: "", players: [], timeControl: "", clocks: [] }
7//];
8
9function dbOperation(callback)
10{
11 let db = null;
12 let DBOpenRequest = window.indexedDB.open("vchess", 4);
13
14 DBOpenRequest.onerror = function(event) {
15 alert("Database error: " + event.target.errorCode);
16 };
17
18 DBOpenRequest.onsuccess = function(event) {
19 db = DBOpenRequest.result;
20 callback(db);
21 db.close();
22 };
23
24 DBOpenRequest.onupgradeneeded = function(event) {
25 let db = event.target.result;
26 db.onerror = function(event) {
27 alert("Error while loading database: " + event.target.errorCode);
28 };
29 // Create objectStore for vchess->games
30 db.createObjectStore("games", { keyPath: "gameId" });
31 }
32}
33
34// Optional callback to get error status
35function addGame(game, callback)
36{
37 dbOperation((db) => {
38 let transaction = db.transaction(["games"], "readwrite");
39 if (callback)
40 {
41 transaction.oncomplete = function() {
42 callback({}); //everything's fine
43 }
44 transaction.onerror = function() {
45 callback({errmsg: "addGame failed: " + transaction.error});
46 };
47 }
48 let objectStore = transaction.objectStore("games");
49 objectStore.add(game);
50 });
51}
52
53// Clear current live game from localStorage
54function clear() {
55 localStorage.deleteItem("gameInfo");
56 localStorage.deleteItem("gameState");
57}
58
59// Current live game:
60function getCurrent()
61{
62 return Object.assign({},
63 JSON.parse(localStorage.getItem("gameInfo")),
64 JSON.parse(localStorage.getItem("gameState")));
65}
66
67// Only called internally after a score update
68function transferToDb()
69{
70 addGame(getCurrent(), (err) => {
71 if (!!err.errmsg)
72 return err;
73 clear();
74 });
75}
76
d2634386 77export const GameStorage =
59d58d7d 78{
8f5780d8 79 // localStorage:
6d01bb17 80 init: function(o)
d2634386 81 {
4c177b7f
BA
82 // NOTE: when >= 3 players, better use an array + shuffle for mycolor
83 const mycolor = (Math.random() < 0.5 ? "w" : "b");
6d01bb17 84 // Shuffle players order (white then black then other colors).
8f5780d8 85 const players = shuffle(o.players);
6d01bb17
BA
86 // Extract times (in [milli]seconds), set clocks, store in localStorage
87 const tc = extractTime(o.timeControl);
8f5780d8
BA
88
89 // game infos: constant
90 const gameInfo =
91 {
92 gameId: o.gameId,
93 vname: o.vname,
94 mycolor: mycolor,
95 fenStart: o.fenStart,
96 players: players,
97 timeControl: o.timeControl,
98 increment: tc.increment,
99 mode: "live", //function for live games only
100 };
101
102 // game state: will be updated
103 const gameState =
104 {
105 fen: o.fenStart,
106 moves: [],
107 clocks: [...Array(o.players.length)].fill(tc.mainTime),
108 started: [...Array(o.players.length)].fill(false),
109 score: "*",
110 };
111
112 localStorage.setItem("gameInfo", JSON.stringify(gameInfo));
113 localStorage.setItem("gameState", JSON.stringify(gameState));
d2634386 114 },
59d58d7d 115
8f5780d8
BA
116 // localStorage:
117 // TODO: also option to takeback a move ? Is fen included in move ?
4c177b7f 118 // NOTE: for live games only (all on server for corr)
8f5780d8 119 update: function(fen, moves, clocks, started, score)
4c177b7f 120 {
8f5780d8
BA
121 let gameState = JSON.parse(localStorage.getItem("gameState"));
122 if (!!fen)
4c177b7f 123 {
8f5780d8
BA
124 gameState.moves = moves;
125 gameState.fen = fen;
126 gameState.clocks = clocks;
4c177b7f 127 }
8f5780d8
BA
128 if (!!started)
129 gameState.started = started;
4c177b7f 130 if (!!score)
8f5780d8
BA
131 gameState.score = score;
132 localStorage.setItem("gameState", JSON.stringify(gameState));
133 if (!!score && score != "*")
134 transferToDb(); //game is over
4c177b7f
BA
135 },
136
8f5780d8
BA
137 // indexedDB:
138 // Since DB requests are asynchronous, require a callback using the result
139 // TODO: option for remote retrieval (third arg, or just "gameRef")
d6c1bf37 140 getLocal: function(gameId, callback)
d2634386 141 {
8f5780d8
BA
142 let games = [];
143 dbOperation((db) => {
144 // TODO: if gameId is provided, limit search to gameId (just .get(gameId). ...)
145 let objectStore = db.transaction('games').objectStore('games');
146 objectStore.openCursor().onsuccess = function(event) {
147 var cursor = event.target.result;
148 // if there is still another cursor to go, keep runing this code
149 if (cursor)
150 {
151 games.push(cursor.value);
152 cursor.continue();
153 }
154 else
155 callback(games);
156 }
157 });
d2634386 158 },
59d58d7d 159
8f5780d8
BA
160 // Delete a game in indexedDB
161 remove: function(gameId, callback)
d2634386 162 {
8f5780d8
BA
163 dbOperation((db) => {
164 let transaction = db.transaction(["games"], "readwrite");
165 if (callback)
166 {
167 transaction.oncomplete = function() {
168 callback({}); //everything's fine
169 }
170 transaction.onerror = function() {
171 callback({errmsg: "deleteGame failed: " + transaction.error});
172 };
173 }
174 transaction.objectStore("games").delete(gameId);
175 });
d2634386 176 },
59d58d7d 177
8f5780d8
BA
178 // Retrieve any live game from its identifiers (remote or not, running or not)
179 // NOTE: need callback because result might be obtained asynchronously
180 get: function(gameRef, callback)
d2634386
BA
181 {
182 const gid = gameRef.id;
183 const rid = gameRef.rid; //may be blank
8f5780d8 184 if (!!rid)
d2634386 185 {
8f5780d8
BA
186 // TODO: send request to server which forward to user sid == rid,
187 // need to listen to "remote game" event in main hall ?
188 return callback({}); //means "the game will arrive later" (TODO...)
d2634386 189 }
8f5780d8
BA
190
191 const gameInfoStr = localStorage.getItem("gameInfo");
192 if (gameInfoStr)
d2634386 193 {
8f5780d8
BA
194 const gameInfo = JSON.parse(gameInfoStr);
195 if (gameInfo.gameId == gid)
196 {
197 const gameState = JSON.parse(localStorage.getItem("gameState"));
198 return callback(Object.assign({}, gameInfo, gameState));
199 }
d2634386 200 }
8f5780d8
BA
201
202 // Game is local and not running
d6c1bf37 203 GameStorage.getLocal(gid, callback);
d2634386
BA
204 },
205};