Commit | Line | Data |
---|---|---|
6d01bb17 BA |
1 | import { extractTime } from "@/utils/timeControl"; |
2 | import { shuffle } from "@/utils/alea"; | |
ce87ac6a | 3 | |
8f5780d8 BA |
4 | // TODO: show game structure |
5 | //const newItem = [ | |
6 | // { gameId: "", players: [], timeControl: "", clocks: [] } | |
7 | //]; | |
8 | ||
9 | function 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 | |
35 | function 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 | |
54 | function clear() { | |
55 | localStorage.deleteItem("gameInfo"); | |
56 | localStorage.deleteItem("gameState"); | |
57 | } | |
58 | ||
59 | // Current live game: | |
60 | function 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 | |
68 | function transferToDb() | |
69 | { | |
70 | addGame(getCurrent(), (err) => { | |
71 | if (!!err.errmsg) | |
72 | return err; | |
73 | clear(); | |
74 | }); | |
75 | } | |
76 | ||
d2634386 | 77 | export 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 | }; |