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