.row
.col-sm-12.col-md-10.col-md-offset-1.col-lg-8.col-lg-offset-2
footer
- a(href="https://github.com/yagu0/vchess") {{ st.tr["Source code"] }}
+ router-link.menuitem(to="/about") {{ st.tr["About"] }}
p.clickable(onClick="doClick('modalContact')")
- | {{ st.tr["Contact form"] }}
- //my-game(:game-ref="gameRef" :mode="mode" :settings="settings" @game-over="archiveGame")
- //// TODO: add only the necessary icons to mini-css custom build
+ | {{ st.tr["Contact"] }}
+ // TODO: add only the necessary icons to mini-css custom build
//script(src="//unpkg.com/feather-icons")
</template>
st: store.state,
};
},
-// // TODO: $route: ...
-// gameRef: function() {
-// this.loadGame();
-// },
computed: {
flagImage: function() {
return `/images/flags/${this.st.lang}.svg`;
display: inline-flex
align-items: center
justify-content: center
- & > a
+ & > .menuitem
display: inline-block
margin: 0 10px 0 0
&:link
localStorage["myname"] = res.name;
localStorage["myid"] = res.id;
}
- next();
+ next("/");
}
);
},
name: "game",
component: loadView("Game"),
},
-// {
-// path: "/about",
-// name: "about",
-// // route level code-splitting
-// // this generates a separate chunk (about.[hash].js) for this route
-// // which is lazy-loaded when the route is visited.
-// component: loadView('About'),
-// //function() {
-// // return import(/* webpackChunkName: "about" */ "./views/About.vue");
-// //}
-// },
- // TODO: gameRef, problemId: https://router.vuejs.org/guide/essentials/dynamic-matching.html
+ {
+ path: "/about",
+ name: "about",
+ component: loadView("About"),
+ },
+ // TODO: myGames, problemId: https://router.vuejs.org/guide/essentials/dynamic-matching.html
]
});
router.beforeEach((to, from, next) => {
- window.scrollTo(0, 0); //TODO: check if a live game is running; if yes, call next('/game')
- //https://router.vuejs.org/guide/advanced/navigation-guards.html#global-before-guards
+ window.scrollTo(0, 0);
+ if (!!store.state.conn) //uninitialized at first page
+ {
+ // Notify WebSockets server (TODO: path or fullPath?)
+ store.state.conn.send(JSON.stringify({code: "pagechange", page: to.path}));
+ }
next();
- //TODO: si une partie en cours dans storage, rediriger vers cette partie
- //(à condition que l'URL n'y corresponde pas déjà !)
- // (l'identifiant de l'utilisateur si connecté)
-// if (!!localStorage["variant"])
-// location.hash = "#game?id=" + localStorage["gameId"];
+ // TODO?: redirect to current game (through GameStorage.getCurrent()) if any?
+ // (and if the URL doesn't already match it) (use next("/game/GID"))
+ //https://router.vuejs.org/guide/advanced/navigation-guards.html#global-before-guards
});
export default router;
--- /dev/null
+<template lang="pug">
+.row
+ .col-sm-12.col-md-10.col-md-offset-1.col-lg-8.col-lg-offset-2
+ p TODO: give github URL, tell website story...
+ a(href="https://github.com/yagu0/vchess") contribute...
+</template>
+
+<style lang="sass">
+.warn
+ padding: 3px
+ color: red
+ background-color: lightgrey
+ font-weight: bold
+
+p.boxed
+ background-color: #FFCC66
+ padding: 5px
+
+.stageDelimiter
+ color: purple
+
+.section-title
+ padding: 0
+
+.section-title > h4
+ padding: 5px
+
+ol, ul:not(.browser-default)
+ padding-left: 20px
+
+ul:not(.browser-default)
+ margin-top: 5px
+
+ul:not(.browser-default) > li
+ list-style-type: disc
+</style>
// ==> après, implémenter/vérifier les passages de challenges + parties en cours
// observer,
// + problèmes, habiller et publier. (+ corr...)
- // TODO: how to know who is observing ? Send message to everyone with game ID ?
- // and then just listen to (dis)connect events
- // server always send "connect on " + URL ; then add to observers if game...
-// router when access a game page tell to server I joined + game ID (no need rid)
-// and ask server for current joined (= observers)
// when send to chat (or a move), reach only this group (send gid along)
-// -> doivent être enregistrés comme observers au niveau du serveur...
- // non: poll users + events startObserving / stopObserving
- // (à faire au niveau du routeur ?)
-->
<script>
id: "",
rid: ""
},
- game: { }, //passed to BaseGame
- oppConnected: false, //TODO: use for styling
+ game: {}, //passed to BaseGame
corrMsg: "", //to send offline messages in corr games
virtualClocks: [0, 0], //initialized with true game.clocks
vr: null, //"variant rules" object initialized from FEN
drawOffer: "", //TODO: use for button style
- people: [ ], //potential observers (TODO)
+ people: [], //players + observers
};
},
watch: {
this.game.vname = variantArray.filter(v => v.id == this.game.vid)[0].name;
},
},
+ computed: {
+ oppConnected: function() {
+ return this.people.indexOf(p => p.id == this.game.oppid) >= 0;
+ },
+ },
created: function() {
if (!!this.$route.params["id"])
{
if (!game.fen)
game.fen = game.fenStart; //game wasn't started
const gtype = (game.timeControl.indexOf('d') >= 0 ? "corr" : "live");
+ const tc = extractTime(game.timeControl);
if (gtype == "corr")
{
// corr game: needs to compute the clocks + initime
- //if (game.players[i].rtime < 0) initime = Date.now(), else compute,
- //also using move.played fields
- game.clocks = [-1, -1];
+ game.clocks = [tc.mainTime, tc.mainTime];
game.initime = [0, 0];
- // TODO: compute clocks + initime
+ let addTime = [0, 0];
+ for (let i=2; i<game.moves.length; i++)
+ {
+ addTime[i%2] += tc.increment -
+ (game.moves[i].played - game.moves[i-1].played);
+ }
+ for (let i=0; i<=1; i++)
+ game.clocks[i] += addTime[i];
+ const L = game.moves.length;
+ game.initime[L%2] = game.moves[L-1].played;
}
- const tc = extractTime(game.timeControl);
// TODO: this is not really beautiful (uid on corr players...)
if (gtype == "corr" && game.players[0].color == "b")
[ game.players[0], game.players[1] ] = [ game.players[1], game.players[0] ];
const myIdx = game.players.findIndex(p => {
return p.sid == this.st.user.sid || p.uid == this.st.user.id;
});
- if (game.clocks[0] < 0) //game unstarted
+ if (gtype == "live" && game.clocks[0] < 0) //game unstarted
{
game.clocks = [tc.mainTime, tc.mainTime];
game.initime[0] = Date.now();
- if (myIdx >= 0 && gtype == "live")
+ if (myIdx >= 0)
{
// I play in this live game; corr games don't have clocks+initime
GameStorage.update(game.id,
gdisplay: "live",
games: [],
challenges: [],
- people: [], //(all) online players
+ people: [], //people in main hall
infoMessage: "",
newchallenge: {
fen: "",
const game = Object.assign({}, gameInfo, {
// (other) Game infos: constant
fenStart: gameInfo.fen,
- created: Date.now(),
+ added: Date.now(),
// Game state (including FEN): will be updated
moves: [],
clocks: [-1, -1], //-1 = unstarted
padding-top: 5px
font-size: 0.8em
- p.boxed
- background-color: #FFCC66
- padding: 5px
+p.boxed
+ background-color: #FFCC66
+ padding: 5px
- .stageDelimiter
- color: purple
+.stageDelimiter
+ color: purple
- // To show (new) pieces, and/or there values...
- figure.showPieces > img
- width: 50px
+// To show (new) pieces, and/or there values...
+figure.showPieces > img
+ width: 50px
- figure.showPieces > figcaption
- color: #6C6C6C
+figure.showPieces > figcaption
+ color: #6C6C6C
- .section-title
- padding: 0
+.section-title
+ padding: 0
- .section-title > h4
- padding: 5px
+.section-title > h4
+ padding: 5px
- ol, ul:not(.browser-default)
- padding-left: 20px
+ol, ul:not(.browser-default)
+ padding-left: 20px
- ul:not(.browser-default)
- margin-top: 5px
+ul:not(.browser-default)
+ margin-top: 5px
- ul:not(.browser-default) > li
- list-style-type: disc
+ul:not(.browser-default) > li
+ list-style-type: disc
.light-square-diag
background-color: #e5e5ca
foreign key (vid) references Variants(id)
);
--- NOTE: no need for a "created" field, it's deduce from first move playing time
+-- NOTE: no need for a "created" field, it's deduced from first move playing time
create table Games (
id integer primary key,
vid integer,
gid integer,
uid integer,
color character,
- rtime integer, --remaining time in milliseconds
foreign key (gid) references Games(id),
foreign key (uid) references Users(id)
);
// TODO: later, allow duplicate connections (shouldn't be much more complicated)
if (!!clients[sid])
return socket.send(JSON.stringify({code:"duplicate"}));
- clients[sid] = socket;
- // Notify room:
- Object.keys(clients).forEach(k => {
- if (k != sid)
- clients[k].send(JSON.stringify({code:"connect",sid:sid}));
- });
+ clients[sid] = {sock: socket, page: query["page"]};
+ const notifyRoom = (page,code) => {
+ Object.keys(clients).forEach(k => {
+ if (k != sid && clients[k].page == page)
+ clients[k].sock.send(JSON.stringify({code:code,sid:sid}));
+ });
+ };
+ notifyRoom(query["page"],"connect");
socket.on("message", objtxt => {
let obj = JSON.parse(objtxt);
if (!!obj.target && !clients[obj.target])
{
case "pollclients":
socket.send(JSON.stringify({code:"pollclients",
- sockIds:Object.keys(clients).filter(k => k != sid)}));
+ sockIds: Object.keys(clients).filter(k =>
+ k != sid && clients[k].page == obj.page)}));
+ break;
+ case "pagechange":
+ notifyRoom(clients[sid].page, "disconnect");
+ clients[sid].page = obj.page;
+ notifyRoom(obj.page, "connect");
break;
case "askidentity":
- clients[obj.target].send(
+ clients[obj.target].sock.send(
JSON.stringify({code:"askidentity",from:sid}));
break;
case "askchallenge":
- clients[obj.target].send(
+ clients[obj.target].sock.send(
JSON.stringify({code:"askchallenge",from:sid}));
break;
case "askgame":
- clients[obj.target].send(
+ clients[obj.target].sock.send(
JSON.stringify({code:"askgame",from:sid}));
break;
case "identity":
- clients[obj.target].send(
+ clients[obj.target].sock.send(
JSON.stringify({code:"identity",user:obj.user}));
break;
case "refusechallenge":
- clients[obj.target].send(
+ clients[obj.target].sock.send(
JSON.stringify({code:"refusechallenge", cid:obj.cid, from:sid}));
break;
case "deletechallenge":
- clients[obj.target].send(
+ clients[obj.target].sock.send(
JSON.stringify({code:"deletechallenge", cid:obj.cid, from:sid}));
break;
case "newgame":
- clients[obj.target].send(JSON.stringify(
+ clients[obj.target].sock.send(JSON.stringify(
{code:"newgame", gameInfo:obj.gameInfo, cid:obj.cid}));
break;
case "challenge":
- clients[obj.target].send(JSON.stringify(
+ clients[obj.target].sock.send(JSON.stringify(
{code:"challenge", chall:obj.chall, from:sid}));
break;
case "game":
- clients[obj.target].send(JSON.stringify(
+ clients[obj.target].sock.send(JSON.stringify(
{code:"game", game:obj.game, from:sid}));
break;
case "newchat":
- clients[obj.target].send(JSON.stringify({code:"newchat",msg:obj.msg}));
+ clients[obj.target].sock.send(JSON.stringify({code:"newchat",msg:obj.msg}));
break;
// TODO: WebRTC instead in this case (most demanding?)
case "newmove":
- clients[obj.target].send(JSON.stringify({code:"newmove",move:obj.move}));
+ clients[obj.target].sock.send(JSON.stringify({code:"newmove",move:obj.move}));
break;
case "ping":
// If this code is reached, then obj.target is connected
break;
case "lastate":
const oppId = obj.target;
- obj.oppid = sid; //I'm the opponent of my opponent(s)
- clients[oppId].send(JSON.stringify(obj));
+ obj.oppid = sid; //I'm the opponent of my opponent
+ clients[oppId].sock.send(JSON.stringify(obj));
break;
case "resign":
- clients[obj.target].send(JSON.stringify({code:"resign"}));
+ clients[obj.target].sock.send(JSON.stringify({code:"resign"}));
break;
case "abort":
- clients[obj.target].send(JSON.stringify({code:"abort",msg:obj.msg}));
+ clients[obj.target].sock.send(JSON.stringify({code:"abort",msg:obj.msg}));
break;
case "drawoffer":
- clients[obj.target].send(JSON.stringify({code:"drawoffer"}));
+ clients[obj.target].sock.send(JSON.stringify({code:"drawoffer"}));
break;
case "draw":
- clients[obj.target].send(JSON.stringify({code:"draw"}));
+ clients[obj.target].sock.send(JSON.stringify({code:"draw"}));
break;
}
});
socket.on("close", () => {
+ const page = clients[sid].page;
delete clients[sid];
- // Notify every other connected client
- Object.keys(clients).forEach( k => {
- clients[k].send(JSON.stringify({code:"disconnect",sid:sid}));
- });
+ notifyRoom(page, "disconnect");
});
});
}