TODO: game storage init + get
[vchess.git] / client / src / views / Game.vue
CommitLineData
a6bddfc6
BA
1<!-- TODO: component Game, + handle players + observers connect/disconnect
2 event = "gameconnect" ...etc
3 connect/disconnect with sid+name (ID not required); name slightly redundant but easier
4ce15fd9
BA
4quand on arrive dans la partie, on poll les sids pour savoir qui est en ligne (ping)
5(éventuel échange lastate avec les connectés, pong ...etc)
6ensuite quand qqun se deco il suffit d'écouter "disconnect"
7pareil quand quelqu'un reco.
8(c'est assez rudimentaire et écoute trop de messages, mais dans un premier temps...)
7b626bdd 9 // TODO: [in game] send move + elapsed time (in milliseconds); in case of "lastate" message too
ba82879c
BA
10// TODO: if I'm an observer and player(s) disconnect/reconnect, how to find me ?
11// onClick :: ask full game to remote player, and register as an observer in game
12// (use gameId to communicate)
13// on landing on game :: if gameId not found locally, check remotely
14// ==> il manque un param dans game : "remoteId"
a6bddfc6 15-->
a6088c90
BA
16<template lang="pug">
17.row
18 .col-sm-12.col-md-10.col-md-offset-1.col-lg-8.col-lg-offset-2
4fe5664d 19 BaseGame(:vname="vname" :game-info="gameInfo" :analyze="analyze" :vr="vr"
ba82879c 20 ref="basegame" @newmove="processMove")
a6088c90
BA
21 .button-group(v-if="mode!='analyze'")
22 button(@click="offerDraw") Draw
23 button(@click="abortGame") Abort
24 button(@click="resign") Resign
25 div(v-if="mode=='corr'")
26 textarea(v-show="score=='*' && vr.turn==mycolor" v-model="corrMsg")
27 div(v-show="cursor>=0") {{ moves[cursor].message }}
28</template>
29
30<script>
46284a2f 31import BaseGame from "@/components/BaseGame.vue";
a6088c90
BA
32//import Chat from "@/components/Chat.vue";
33//import MoveList from "@/components/MoveList.vue";
34import { store } from "@/store";
d2634386 35import { GameStorage } from "@/utils/storage";
a6088c90
BA
36
37export default {
38 name: 'my-game',
39 components: {
40 BaseGame,
41 },
f7121527 42 // gameRef: to find the game in (potentially remote) storage
a6088c90 43 // mode: "live" or "corr" (correspondance game), or "analyze"
a6088c90
BA
44 data: function() {
45 return {
46 st: store.state,
d2634386 47 gameRef: {id: "", rid: ""}, //given in URL (rid = remote ID)
4fe5664d
BA
48 gameInfo: {}, //passed to BaseGame
49 vr: null, //TODO
50 vname: "", //obtained from gameInfo (slightly redundant..)
ce87ac6a 51 mode: "analyze", //mutable
d2634386
BA
52 drawOfferSent: false, //did I just ask for draw? (TODO: draw variables?)
53 people: [], //potential observers (TODO)
a6088c90
BA
54 };
55 },
ba82879c
BA
56 computed: {
57 analyze: function() {
58 return this.mode == "analyze";
59 },
60 },
a6088c90 61 watch: {
f7121527 62 '$route' (to, from) {
4fe5664d
BA
63 if (!!to.params["id"])
64 {
65 this.gameRef.id = to.params["id"];
66 this.gameRef.rid = to.query["rid"];
67 this.loadGame();
68 }
a6088c90
BA
69 },
70 },
a6088c90 71 created: function() {
f7121527 72 if (!!this.$route.params["id"])
a6088c90 73 {
f7121527
BA
74 this.gameRef.id = this.$route.params["id"];
75 this.gameRef.rid = this.$route.query["rid"];
b196f8ea 76 this.loadGame();
f7121527 77 }
f7121527
BA
78 // TODO: how to know who is observing ? Send message to everyone with game ID ?
79 // and then just listen to (dis)connect events
80
3b450453
BA
81
82 // server always send "connect on " + URL ; then add to observers if game...
83 // detect multiple tabs connected (when connect ask server if my SID is already in use)
84// router when access a game page tell to server I joined + game ID (no need rid)
85// and ask server for current joined (= observers)
86// when send to chat (or a move), reach only this group (send gid along)
87
88 // --> doivent être enregistrés comme observers au niveau du serveur...
89 // non: poll users + events startObserving / stopObserving
90
91
a6088c90
BA
92 // TODO: also handle "draw accepted" (use opponents array?)
93 // --> must give this info also when sending lastState...
94 // and, if all players agree then OK draw (end game ...etc)
95 const socketMessageListener = msg => {
96 const data = JSON.parse(msg.data);
97 let L = undefined;
98 switch (data.code)
99 {
f7121527
BA
100 case "newmove":
101 // TODO: observer on dark games must see all board ? Or alternate ? (seems better)
102 // ...or just see nothing as on buho21
4fe5664d 103 this.$refs["basegame"].play(
46284a2f 104 data.move, this.vname!="Dark" ? "animate" : null);
a6088c90
BA
105 break;
106 case "pong": //received if we sent a ping (game still alive on our side)
107 if (this.gameRef.id != data.gameId)
f7121527 108 break; //games IDs don't match: the game is definitely over...
a6088c90
BA
109 this.oppConnected = true;
110 // Send our "last state" informations to opponent(s)
111 L = this.vr.moves.length;
112 Object.keys(this.opponents).forEach(oid => {
113 this.conn.send(JSON.stringify({
114 code: "lastate",
115 oppid: oid,
116 gameId: this.gameRef.id,
117 lastMove: (L>0?this.vr.moves[L-1]:undefined),
118 movesCount: L,
119 }));
120 });
121 break;
f7121527 122 // TODO: refactor this, because at 3 or 4 players we may have missed 2 or 3 moves
a6088c90
BA
123 case "lastate": //got opponent infos about last move
124 L = this.vr.moves.length;
125 if (this.gameRef.id != data.gameId)
126 break; //games IDs don't match: nothing we can do...
127 // OK, opponent still in game (which might be over)
128 if (this.score != "*")
129 {
130 // We finished the game (any result possible)
131 this.conn.send(JSON.stringify({
132 code: "lastate",
133 oppid: data.oppid,
134 gameId: this.gameRef.id,
135 score: this.score,
136 }));
137 }
138 else if (!!data.score) //opponent finished the game
139 this.endGame(data.score);
140 else if (data.movesCount < L)
141 {
142 // We must tell last move to opponent
143 this.conn.send(JSON.stringify({
144 code: "lastate",
145 oppid: this.opponent.id,
146 gameId: this.gameRef.id,
147 lastMove: this.vr.moves[L-1],
148 movesCount: L,
149 }));
150 }
151 else if (data.movesCount > L) //just got last move from him
152 this.play(data.lastMove, "animate");
153 break;
154 case "resign": //..you won!
155 this.endGame(this.mycolor=="w"?"1-0":"0-1");
156 break;
157 // TODO: also use (dis)connect info to count online players?
158 case "gameconnect":
159 case "gamedisconnect":
160 if (this.mode=="human")
161 {
162 const online = (data.code == "connect");
163 // If this is an opponent ?
164 if (!!this.opponents[data.id])
165 this.opponents[data.id].online = online;
166 else
167 {
168 // Or an observer ?
169 if (!online)
170 delete this.people[data.id];
171 else
172 this.people[data.id] = data.name;
173 }
174 }
175 break;
176 }
177 };
178 const socketCloseListener = () => {
179 this.conn.addEventListener('message', socketMessageListener);
180 this.conn.addEventListener('close', socketCloseListener);
181 };
182 if (!!this.conn)
183 {
184 this.conn.onmessage = socketMessageListener;
185 this.conn.onclose = socketCloseListener;
186 }
187 },
188 // dans variant.js (plutôt room.js) conn gère aussi les challenges
189 // et les chats dans chat.js. Puis en webRTC, repenser tout ça.
190 methods: {
191 offerDraw: function() {
192 if (!confirm("Offer draw?"))
193 return;
194 // Stay in "draw offer sent" state until next move is played
195 this.drawOfferSent = true;
196 if (this.subMode == "corr")
197 {
198 // TODO: set drawOffer on in game (how ?)
199 }
200 else //live game
201 {
202 this.opponents.forEach(o => {
203 if (!!o.online)
204 {
205 try {
206 this.conn.send(JSON.stringify({code: "draw", oppid: o.id}));
207 } catch (INVALID_STATE_ERR) {
208 return;
209 }
210 }
211 });
212 }
213 },
214 // + conn handling: "draw" message ==> agree for draw (if we have "drawOffered" at true)
215 receiveDrawOffer: function() {
216 //if (...)
217 // TODO: ignore if preventDrawOffer is set; otherwise show modal box with option "prevent future offers"
218 // if accept: send message "draw"
219 },
220 abortGame: function() {
221 if (!confirm("Abort the game?"))
222 return;
223 //+ bouton "abort" avec score == "?" + demander confirmation pour toutes ces actions,
224 //send message: "gameOver" avec score "?"
225 },
226 resign: function(e) {
227 if (!confirm("Resign the game?"))
228 return;
229 if (this.mode == "human" && this.oppConnected(this.oppid))
230 {
231 try {
232 this.conn.send(JSON.stringify({code: "resign", oppid: this.oppid}));
233 } catch (INVALID_STATE_ERR) {
234 return;
235 }
236 }
237 this.endGame(this.mycolor=="w"?"0-1":"1-0");
238 },
b196f8ea
BA
239 // 4 cases for loading a game:
240 // - from localStorage (one running game I play)
241 // - from indexedDB (one completed live game)
242 // - from server (one correspondance game I play[ed] or not)
243 // - from remote peer (one live game I don't play, finished or not)
244 loadGame: async function() {
4fe5664d 245 this.gameInfo = GameStorage.get(this.gameRef);
ce87ac6a
BA
246
247console.log(GameStorage.get(this.gameRef));
248
4fe5664d 249 this.vname = this.gameInfo.vname;
ce87ac6a 250 this.mode = this.gameInfo.mode;
4fe5664d 251 const vModule = await import("@/variants/" + this.vname + ".js");
b196f8ea 252 window.V = vModule.VariantRules;
4fe5664d 253 this.vr = new V(this.gameInfo.fen);
3b450453 254
4fe5664d
BA
255// // Poll all players except me (if I'm playing) to know online status.
256// // --> Send ping to server (answer pong if players[s] are connected)
257// if (this.gameInfo.players.some(p => p.sid == this.st.user.sid))
258// {
259// this.game.players.forEach(p => {
260// if (p.sid != this.st.user.sid)
261// this.st.conn.send(JSON.stringify({code:"ping", oppid:p.sid}));
262// });
263// }
a6088c90
BA
264 },
265 oppConnected: function(uid) {
b196f8ea 266 return this.opponents.some(o => o.id == uid && o.online);
a6088c90 267 },
ce87ac6a
BA
268 processMove: function(move) {
269 // TODO: process some opponent's move
270 },
a6088c90
BA
271 },
272};
273</script>