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