Tiny changes in 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
ba82879c
BA
19 BaseGame(:vname="variant.name" :analyze="analyze"
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";
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,
ba82879c
BA
47 // variables passed to BaseGame:
48 fenStart: "",
49 vr: null,
50 players: ["Myself","Computer"], //always playing white for now
51 mycolor: "w",
52 ////////////
f7121527 53 gameRef: {id: "", rid: ""}, //given in URL (rid = remote ID, if applicable)
ba82879c 54 mode: "live", //or "corr"
46284a2f 55 vname: "", //filled when game is retrieved
a6088c90 56 drawOfferSent: false, //did I just ask for draw?
f7121527
BA
57 players: [], //filled later (2 to 4 players)
58 people: [], //potential observers
a6088c90
BA
59 };
60 },
ba82879c
BA
61 computed: {
62 analyze: function() {
63 return this.mode == "analyze";
64 },
65 },
a6088c90 66 watch: {
f7121527
BA
67 '$route' (to, from) {
68 this.gameRef.id = to.params["id"];
69 this.gameRef.rid = to.query["rid"];
a6088c90
BA
70 this.launchGame();
71 },
72 },
a6088c90 73 created: function() {
f7121527 74 if (!!this.$route.params["id"])
a6088c90 75 {
f7121527
BA
76 this.gameRef.id = this.$route.params["id"];
77 this.gameRef.rid = this.$route.query["rid"];
78 this.launchGame();
79 }
80 // Poll all players except me (if I'm playing) to know online status.
81 // --> Send ping to server (answer pong if players[s] are connected)
82 if (!!this.gameRef.id)
83 {
84 this.players.forEach(p => {
85 if (p.sid != this.st.user.sid)
86 this.st.conn.send(JSON.stringify({code:"ping", oppid:p.sid}));
87 });
a6088c90 88 }
f7121527
BA
89 // TODO: how to know who is observing ? Send message to everyone with game ID ?
90 // and then just listen to (dis)connect events
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
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 },
46284a2f 239 // TODO: vname is unknown before game is loaded (vname is a field in memory)
a6088c90 240 launchGame: async function() {
46284a2f 241 const vModule = await import("@/variants/" + this.vname + ".js");
a6088c90
BA
242 window.V = vModule.VariantRules;
243 this.loadGame(this.gid);
244 },
245 loadGame: function(gid) {
246 // TODO: ask game to remote peer if this.remoteId is set
247 // (or just if game not found locally)
248 // NOTE: if it's a corr game, ask it from server
249 const game = getGameFromStorage(gid); //, this.gameRef.uid); //uid may be blank
250 this.opponent.id = game.oppid; //opponent ID in case of running HH game
251 this.opponent.name = game.oppname; //maye be blank (if anonymous)
252 this.score = game.score;
253 this.mycolor = game.mycolor;
254 this.fenStart = game.fenStart;
255 this.moves = game.moves;
256 this.cursor = game.moves.length-1;
257 this.lastMove = (game.moves.length > 0 ? game.moves[this.cursor] : null);
f7121527 258 // TODO: fill players array
a6088c90
BA
259 },
260 oppConnected: function(uid) {
261 return this.opponents.any(o => o.id == uidi && o.online);
262 },
263 },
264};
265</script>