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), | |
102 | started: [...Array(o.players.length)].fill(false), | |
103 | score: "*", | |
104 | }; | |
105 | ||
106 | localStorage.setItem("gameInfo", JSON.stringify(gameInfo)); | |
107 | localStorage.setItem("gameState", JSON.stringify(gameState)); | |
d2634386 | 108 | }, |
59d58d7d | 109 | |
8f5780d8 | 110 | // localStorage: |
6274a545 | 111 | // TODO: also option to takeback a move ? |
4c177b7f | 112 | // NOTE: for live games only (all on server for corr) |
6274a545 | 113 | update: function(o) //move, clock, initime, score, colorIdx |
4c177b7f | 114 | { |
6274a545 | 115 | // TODO: finish this --> colorIdx must be computed before entering the function |
8f5780d8 | 116 | let gameState = JSON.parse(localStorage.getItem("gameState")); |
6274a545 | 117 | if (!!o.move) |
4c177b7f | 118 | { |
6274a545 BA |
119 | // https://stackoverflow.com/a/38750895 |
120 | const allowed = ['appear', 'vanish', 'start', 'end']; | |
121 | const filtered_move = Object.keys(o.move) | |
122 | .filter(key => allowed.includes(key)) | |
123 | .reduce((obj, key) => { | |
124 | obj[key] = raw[key]; | |
125 | return obj; | |
126 | }, {}); | |
127 | gameState.moves.push(filtered_move); | |
128 | gameState.fen = o.move.fen; | |
129 | const colorIdx = ["w","b","g","r"][o.move.color]; | |
130 | gameState.clocks[colorIdx] = o.move.clock; | |
4c177b7f | 131 | } |
6274a545 BA |
132 | if (!!o.initime) //just a flag (true) |
133 | gameState.initime = Date.now(); | |
134 | if (!!o.score) | |
135 | gameState.score = o.score; | |
8f5780d8 | 136 | localStorage.setItem("gameState", JSON.stringify(gameState)); |
6274a545 | 137 | if (!!o.score && o.score != "*") |
8f5780d8 | 138 | transferToDb(); //game is over |
4c177b7f BA |
139 | }, |
140 | ||
8f5780d8 BA |
141 | // indexedDB: |
142 | // Since DB requests are asynchronous, require a callback using the result | |
143 | // TODO: option for remote retrieval (third arg, or just "gameRef") | |
d6c1bf37 | 144 | getLocal: function(gameId, callback) |
d2634386 | 145 | { |
8f5780d8 BA |
146 | let games = []; |
147 | dbOperation((db) => { | |
148 | // TODO: if gameId is provided, limit search to gameId (just .get(gameId). ...) | |
149 | let objectStore = db.transaction('games').objectStore('games'); | |
150 | objectStore.openCursor().onsuccess = function(event) { | |
151 | var cursor = event.target.result; | |
152 | // if there is still another cursor to go, keep runing this code | |
153 | if (cursor) | |
154 | { | |
155 | games.push(cursor.value); | |
156 | cursor.continue(); | |
157 | } | |
158 | else | |
159 | callback(games); | |
160 | } | |
161 | }); | |
d2634386 | 162 | }, |
59d58d7d | 163 | |
8f5780d8 BA |
164 | // Delete a game in indexedDB |
165 | remove: function(gameId, callback) | |
d2634386 | 166 | { |
8f5780d8 BA |
167 | dbOperation((db) => { |
168 | let transaction = db.transaction(["games"], "readwrite"); | |
169 | if (callback) | |
170 | { | |
171 | transaction.oncomplete = function() { | |
172 | callback({}); //everything's fine | |
173 | } | |
174 | transaction.onerror = function() { | |
175 | callback({errmsg: "deleteGame failed: " + transaction.error}); | |
176 | }; | |
177 | } | |
178 | transaction.objectStore("games").delete(gameId); | |
179 | }); | |
d2634386 | 180 | }, |
59d58d7d | 181 | |
8f5780d8 BA |
182 | // Retrieve any live game from its identifiers (remote or not, running or not) |
183 | // NOTE: need callback because result might be obtained asynchronously | |
184 | get: function(gameRef, callback) | |
d2634386 BA |
185 | { |
186 | const gid = gameRef.id; | |
187 | const rid = gameRef.rid; //may be blank | |
8f5780d8 | 188 | if (!!rid) |
d2634386 | 189 | { |
8f5780d8 BA |
190 | // TODO: send request to server which forward to user sid == rid, |
191 | // need to listen to "remote game" event in main hall ? | |
192 | return callback({}); //means "the game will arrive later" (TODO...) | |
d2634386 | 193 | } |
8f5780d8 BA |
194 | |
195 | const gameInfoStr = localStorage.getItem("gameInfo"); | |
196 | if (gameInfoStr) | |
d2634386 | 197 | { |
8f5780d8 BA |
198 | const gameInfo = JSON.parse(gameInfoStr); |
199 | if (gameInfo.gameId == gid) | |
200 | { | |
201 | const gameState = JSON.parse(localStorage.getItem("gameState")); | |
202 | return callback(Object.assign({}, gameInfo, gameState)); | |
203 | } | |
d2634386 | 204 | } |
8f5780d8 BA |
205 | |
206 | // Game is local and not running | |
d6c1bf37 | 207 | GameStorage.getLocal(gid, callback); |
d2634386 BA |
208 | }, |
209 | }; |