'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
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
BA
72 }
73 // Poll all players except me (if I'm playing) to know online status.
74 // --> Send ping to server (answer pong if players[s] are connected)
75 if (!!this.gameRef.id)
76 {
77 this.players.forEach(p => {
78 if (p.sid != this.st.user.sid)
79 this.st.conn.send(JSON.stringify({code:"ping", oppid:p.sid}));
80 });
a6088c90 81 }
f7121527
BA
82 // TODO: how to know who is observing ? Send message to everyone with game ID ?
83 // and then just listen to (dis)connect events
84
a6088c90
BA
85 // TODO: also handle "draw accepted" (use opponents array?)
86 // --> must give this info also when sending lastState...
87 // and, if all players agree then OK draw (end game ...etc)
88 const socketMessageListener = msg => {
89 const data = JSON.parse(msg.data);
90 let L = undefined;
91 switch (data.code)
92 {
f7121527
BA
93 case "newmove":
94 // TODO: observer on dark games must see all board ? Or alternate ? (seems better)
95 // ...or just see nothing as on buho21
96 this.$refs["baseGame"].play(
46284a2f 97 data.move, this.vname!="Dark" ? "animate" : null);
a6088c90
BA
98 break;
99 case "pong": //received if we sent a ping (game still alive on our side)
100 if (this.gameRef.id != data.gameId)
f7121527 101 break; //games IDs don't match: the game is definitely over...
a6088c90
BA
102 this.oppConnected = true;
103 // Send our "last state" informations to opponent(s)
104 L = this.vr.moves.length;
105 Object.keys(this.opponents).forEach(oid => {
106 this.conn.send(JSON.stringify({
107 code: "lastate",
108 oppid: oid,
109 gameId: this.gameRef.id,
110 lastMove: (L>0?this.vr.moves[L-1]:undefined),
111 movesCount: L,
112 }));
113 });
114 break;
f7121527 115 // TODO: refactor this, because at 3 or 4 players we may have missed 2 or 3 moves
a6088c90
BA
116 case "lastate": //got opponent infos about last move
117 L = this.vr.moves.length;
118 if (this.gameRef.id != data.gameId)
119 break; //games IDs don't match: nothing we can do...
120 // OK, opponent still in game (which might be over)
121 if (this.score != "*")
122 {
123 // We finished the game (any result possible)
124 this.conn.send(JSON.stringify({
125 code: "lastate",
126 oppid: data.oppid,
127 gameId: this.gameRef.id,
128 score: this.score,
129 }));
130 }
131 else if (!!data.score) //opponent finished the game
132 this.endGame(data.score);
133 else if (data.movesCount < L)
134 {
135 // We must tell last move to opponent
136 this.conn.send(JSON.stringify({
137 code: "lastate",
138 oppid: this.opponent.id,
139 gameId: this.gameRef.id,
140 lastMove: this.vr.moves[L-1],
141 movesCount: L,
142 }));
143 }
144 else if (data.movesCount > L) //just got last move from him
145 this.play(data.lastMove, "animate");
146 break;
147 case "resign": //..you won!
148 this.endGame(this.mycolor=="w"?"1-0":"0-1");
149 break;
150 // TODO: also use (dis)connect info to count online players?
151 case "gameconnect":
152 case "gamedisconnect":
153 if (this.mode=="human")
154 {
155 const online = (data.code == "connect");
156 // If this is an opponent ?
157 if (!!this.opponents[data.id])
158 this.opponents[data.id].online = online;
159 else
160 {
161 // Or an observer ?
162 if (!online)
163 delete this.people[data.id];
164 else
165 this.people[data.id] = data.name;
166 }
167 }
168 break;
169 }
170 };
171 const socketCloseListener = () => {
172 this.conn.addEventListener('message', socketMessageListener);
173 this.conn.addEventListener('close', socketCloseListener);
174 };
175 if (!!this.conn)
176 {
177 this.conn.onmessage = socketMessageListener;
178 this.conn.onclose = socketCloseListener;
179 }
180 },
181 // dans variant.js (plutôt room.js) conn gère aussi les challenges
182 // et les chats dans chat.js. Puis en webRTC, repenser tout ça.
183 methods: {
184 offerDraw: function() {
185 if (!confirm("Offer draw?"))
186 return;
187 // Stay in "draw offer sent" state until next move is played
188 this.drawOfferSent = true;
189 if (this.subMode == "corr")
190 {
191 // TODO: set drawOffer on in game (how ?)
192 }
193 else //live game
194 {
195 this.opponents.forEach(o => {
196 if (!!o.online)
197 {
198 try {
199 this.conn.send(JSON.stringify({code: "draw", oppid: o.id}));
200 } catch (INVALID_STATE_ERR) {
201 return;
202 }
203 }
204 });
205 }
206 },
207 // + conn handling: "draw" message ==> agree for draw (if we have "drawOffered" at true)
208 receiveDrawOffer: function() {
209 //if (...)
210 // TODO: ignore if preventDrawOffer is set; otherwise show modal box with option "prevent future offers"
211 // if accept: send message "draw"
212 },
213 abortGame: function() {
214 if (!confirm("Abort the game?"))
215 return;
216 //+ bouton "abort" avec score == "?" + demander confirmation pour toutes ces actions,
217 //send message: "gameOver" avec score "?"
218 },
219 resign: function(e) {
220 if (!confirm("Resign the game?"))
221 return;
222 if (this.mode == "human" && this.oppConnected(this.oppid))
223 {
224 try {
225 this.conn.send(JSON.stringify({code: "resign", oppid: this.oppid}));
226 } catch (INVALID_STATE_ERR) {
227 return;
228 }
229 }
230 this.endGame(this.mycolor=="w"?"0-1":"1-0");
231 },
b196f8ea
BA
232 // 4 cases for loading a game:
233 // - from localStorage (one running game I play)
234 // - from indexedDB (one completed live game)
235 // - from server (one correspondance game I play[ed] or not)
236 // - from remote peer (one live game I don't play, finished or not)
237 loadGame: async function() {
d2634386
BA
238 const game = GameStorage.get(this.gameRef);
239 this.game = game;
a6088c90 240 this.cursor = game.moves.length-1;
d2634386 241 // TODO: lastMove must be in BaseGame, not here
a6088c90 242 this.lastMove = (game.moves.length > 0 ? game.moves[this.cursor] : null);
b196f8ea
BA
243 const vModule = await import("@/variants/" + game.vname + ".js");
244 window.V = vModule.VariantRules;
a6088c90
BA
245 },
246 oppConnected: function(uid) {
b196f8ea 247 return this.opponents.some(o => o.id == uid && o.online);
a6088c90
BA
248 },
249 },
250};
251</script>