Toward game info simplification
[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'")
a6088c90
BA
25 textarea(v-show="score=='*' && vr.turn==mycolor" v-model="corrMsg")
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,
d2634386 45 gameRef: {id: "", rid: ""}, //given in URL (rid = remote ID)
d6c1bf37 46 game: {}, //passed to BaseGame
6dd02928 47 vr: null, //"variant rules" object initialized from FEN
d2634386
BA
48 drawOfferSent: false, //did I just ask for draw? (TODO: draw variables?)
49 people: [], //potential observers (TODO)
a6088c90
BA
50 };
51 },
52 watch: {
f7121527 53 '$route' (to, from) {
4fe5664d
BA
54 if (!!to.params["id"])
55 {
56 this.gameRef.id = to.params["id"];
57 this.gameRef.rid = to.query["rid"];
58 this.loadGame();
59 }
a6088c90
BA
60 },
61 },
a6088c90 62 created: function() {
f7121527 63 if (!!this.$route.params["id"])
a6088c90 64 {
f7121527
BA
65 this.gameRef.id = this.$route.params["id"];
66 this.gameRef.rid = this.$route.query["rid"];
b196f8ea 67 this.loadGame();
f7121527 68 }
f7121527
BA
69 // TODO: how to know who is observing ? Send message to everyone with game ID ?
70 // and then just listen to (dis)connect events
71
3b450453
BA
72
73 // server always send "connect on " + URL ; then add to observers if game...
74 // detect multiple tabs connected (when connect ask server if my SID is already in use)
75// router when access a game page tell to server I joined + game ID (no need rid)
76// and ask server for current joined (= observers)
77// when send to chat (or a move), reach only this group (send gid along)
78
79 // --> doivent être enregistrés comme observers au niveau du serveur...
80 // non: poll users + events startObserving / stopObserving
81
82
a6088c90
BA
83 // TODO: also handle "draw accepted" (use opponents array?)
84 // --> must give this info also when sending lastState...
85 // and, if all players agree then OK draw (end game ...etc)
86 const socketMessageListener = msg => {
87 const data = JSON.parse(msg.data);
88 let L = undefined;
89 switch (data.code)
90 {
f7121527
BA
91 case "newmove":
92 // TODO: observer on dark games must see all board ? Or alternate ? (seems better)
93 // ...or just see nothing as on buho21
4fe5664d 94 this.$refs["basegame"].play(
d6c1bf37 95 data.move, this.game.vname!="Dark" ? "animate" : null);
a6088c90
BA
96 break;
97 case "pong": //received if we sent a ping (game still alive on our side)
98 if (this.gameRef.id != data.gameId)
f7121527 99 break; //games IDs don't match: the game is definitely over...
a6088c90
BA
100 this.oppConnected = true;
101 // Send our "last state" informations to opponent(s)
102 L = this.vr.moves.length;
103 Object.keys(this.opponents).forEach(oid => {
104 this.conn.send(JSON.stringify({
105 code: "lastate",
106 oppid: oid,
107 gameId: this.gameRef.id,
108 lastMove: (L>0?this.vr.moves[L-1]:undefined),
109 movesCount: L,
110 }));
111 });
112 break;
f7121527 113 // TODO: refactor this, because at 3 or 4 players we may have missed 2 or 3 moves
a6088c90
BA
114 case "lastate": //got opponent infos about last move
115 L = this.vr.moves.length;
116 if (this.gameRef.id != data.gameId)
117 break; //games IDs don't match: nothing we can do...
118 // OK, opponent still in game (which might be over)
119 if (this.score != "*")
120 {
121 // We finished the game (any result possible)
122 this.conn.send(JSON.stringify({
123 code: "lastate",
124 oppid: data.oppid,
125 gameId: this.gameRef.id,
126 score: this.score,
127 }));
128 }
129 else if (!!data.score) //opponent finished the game
130 this.endGame(data.score);
131 else if (data.movesCount < L)
132 {
133 // We must tell last move to opponent
134 this.conn.send(JSON.stringify({
135 code: "lastate",
136 oppid: this.opponent.id,
137 gameId: this.gameRef.id,
138 lastMove: this.vr.moves[L-1],
139 movesCount: L,
140 }));
141 }
142 else if (data.movesCount > L) //just got last move from him
143 this.play(data.lastMove, "animate");
144 break;
145 case "resign": //..you won!
146 this.endGame(this.mycolor=="w"?"1-0":"0-1");
147 break;
148 // TODO: also use (dis)connect info to count online players?
149 case "gameconnect":
150 case "gamedisconnect":
151 if (this.mode=="human")
152 {
153 const online = (data.code == "connect");
154 // If this is an opponent ?
155 if (!!this.opponents[data.id])
156 this.opponents[data.id].online = online;
157 else
158 {
159 // Or an observer ?
160 if (!online)
161 delete this.people[data.id];
162 else
163 this.people[data.id] = data.name;
164 }
165 }
166 break;
167 }
168 };
169 const socketCloseListener = () => {
170 this.conn.addEventListener('message', socketMessageListener);
171 this.conn.addEventListener('close', socketCloseListener);
172 };
173 if (!!this.conn)
174 {
175 this.conn.onmessage = socketMessageListener;
176 this.conn.onclose = socketCloseListener;
177 }
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 {
197 this.conn.send(JSON.stringify({code: "draw", oppid: o.id}));
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 "?"
216 },
217 resign: function(e) {
218 if (!confirm("Resign the game?"))
219 return;
220 if (this.mode == "human" && this.oppConnected(this.oppid))
221 {
222 try {
223 this.conn.send(JSON.stringify({code: "resign", oppid: this.oppid}));
224 } catch (INVALID_STATE_ERR) {
225 return;
226 }
227 }
228 this.endGame(this.mycolor=="w"?"0-1":"1-0");
229 },
b196f8ea
BA
230 // 4 cases for loading a game:
231 // - from localStorage (one running game I play)
232 // - from indexedDB (one completed live game)
233 // - from server (one correspondance game I play[ed] or not)
234 // - from remote peer (one live game I don't play, finished or not)
6dd02928
BA
235 loadGame: function() {
236 GameStorage.get(this.gameRef, async (game) => {
237 this.game = game;
238 const vModule = await import("@/variants/" + game.vname + ".js");
d6c1bf37 239 window.V = vModule.VariantRules;
6dd02928 240 this.vr = new V(game.fen);
d6c1bf37 241 });
4fe5664d
BA
242// // Poll all players except me (if I'm playing) to know online status.
243// // --> Send ping to server (answer pong if players[s] are connected)
244// if (this.gameInfo.players.some(p => p.sid == this.st.user.sid))
245// {
246// this.game.players.forEach(p => {
247// if (p.sid != this.st.user.sid)
248// this.st.conn.send(JSON.stringify({code:"ping", oppid:p.sid}));
249// });
250// }
a6088c90
BA
251 },
252 oppConnected: function(uid) {
b196f8ea 253 return this.opponents.some(o => o.id == uid && o.online);
a6088c90 254 },
ce87ac6a
BA
255 processMove: function(move) {
256 // TODO: process some opponent's move
257 },
a6088c90
BA
258 },
259};
260</script>