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