Step toward a one-page application
[vchess.git] / server / sockets.js
1 const url = require('url');
2 const VariantModel = require("./models/Variant");
3
4 // Node version in Ubuntu 16.04 does not know about URL class
5 function getJsonFromUrl(url) {
6 var query = url.substr(2); //starts with "/?"
7 var result = {};
8 query.split("&").forEach(function(part) {
9 var item = part.split("=");
10 result[item[0]] = decodeURIComponent(item[1]);
11 });
12 return result;
13 }
14
15 // TODO: empêcher multi-log du même user (envoyer le user ID + secret en même temps que name et...)
16 // --> si secret ne matche pas celui trouvé en DB, stop
17
18 // TODO: this file in the end will be much simpler, just tracking connect/disconnect
19 // (everything else using WebRTC)
20
21 module.exports = function(wss) {
22 VariantModel.getAll((err,variants) => {
23 let clients = { "index": {} };
24 let games = {}; //pending games (player sid)
25 for (const v of variants)
26 clients[v.id] = {};
27 // No-op function as a callback when sending messages
28 const noop = () => { };
29 wss.on("connection", (socket, req) => {
30 // const params = new URL("http://localhost" + req.url).searchParams;
31 // const sid = params.get("sid");
32 // const page = params.get("page");
33 var query = getJsonFromUrl(req.url);
34 const sid = query["sid"];
35 const page = query["page"];
36 // Ignore duplicate connections:
37 if (!!clients[page][sid])
38 {
39 socket.send(JSON.stringify({code:"duplicate"}));
40 return;
41 }
42 clients[page][sid] = socket;
43 if (page == "index")
44 {
45 // Send counting info
46 const countings = {};
47 for (const v of variants)
48 countings[v.id] = Object.keys(clients[v.id]).length;
49 socket.send(JSON.stringify({code:"counts",counts:countings}));
50 }
51 else
52 {
53 // Send to every client connected on index an update message for counts
54 Object.keys(clients["index"]).forEach( k => {
55 clients["index"][k].send(
56 JSON.stringify({code:"increase",vid:page}), noop);
57 });
58 // Also notify potential opponents:
59 // hit all clients which check if sid corresponds
60 Object.keys(clients[page]).forEach( k => {
61 clients[page][k].send(JSON.stringify({code:"connect",id:sid}), noop);
62 });
63 socket.on("message", objtxt => {
64 let obj = JSON.parse(objtxt);
65 switch (obj.code)
66 {
67 case "newchat":
68 if (!!clients[page][obj.oppid])
69 {
70 clients[page][obj.oppid].send(
71 JSON.stringify({code:"newchat",msg:obj.msg}), noop);
72 }
73 break;
74 case "newmove":
75 if (!!clients[page][obj.oppid])
76 {
77 clients[page][obj.oppid].send(
78 JSON.stringify({code:"newmove",move:obj.move}), noop);
79 }
80 break;
81 case "ping":
82 if (!!clients[page][obj.oppid])
83 socket.send(JSON.stringify({code:"pong",gameId:obj.gameId}));
84 break;
85 case "myname":
86 // Reveal my username to opponent
87 if (!!clients[page][obj.oppid])
88 {
89 clients[page][obj.oppid].send(JSON.stringify({
90 code:"oppname", name:obj.name}));
91 }
92 break;
93 case "lastate":
94 if (!!clients[page][obj.oppid])
95 {
96 const oppId = obj.oppid;
97 obj.oppid = sid; //I'm oppid for my opponent
98 clients[page][oppId].send(JSON.stringify(obj), noop);
99 }
100 break;
101 case "newgame":
102 if (!!games[page])
103 {
104 // Start a new game
105 const oppId = games[page]["id"];
106 const fen = games[page]["fen"];
107 const gameId = games[page]["gameid"];
108 delete games[page];
109 const mycolor = (Math.random() < 0.5 ? 'w' : 'b');
110 socket.send(JSON.stringify(
111 {code:"newgame",fen:fen,oppid:oppId,color:mycolor,gameid:gameId}));
112 if (!!clients[page][oppId])
113 {
114 clients[page][oppId].send(
115 JSON.stringify(
116 {code:"newgame",fen:fen,oppid:sid,color:mycolor=="w"?"b":"w",gameid:gameId}),
117 noop);
118 }
119 }
120 else
121 games[page] = {id:sid, fen:obj.fen, gameid:obj.gameid}; //wait for opponent
122 break;
123 case "cancelnewgame": //if a user cancel his seek
124 delete games[page];
125 break;
126 case "resign":
127 if (!!clients[page][obj.oppid])
128 clients[page][obj.oppid].send(JSON.stringify({code:"resign"}), noop);
129 break;
130 // TODO: case "challenge" (get ID) --> send to all, "acceptchallenge" (with ID) --> send to all, "cancelchallenge" --> send to all
131 // also, "sendgame" (give current game info, if any) --> to new connections, "sendchallenges" (same for challenges) --> to new connections
132 }
133 });
134 }
135 socket.on("close", () => {
136 delete clients[page][sid];
137 // Remove potential pending game
138 if (!!games[page] && games[page]["id"] == sid)
139 delete games[page];
140 if (page != "index")
141 {
142 // Send to every client connected on index an update message for counts
143 Object.keys(clients["index"]).forEach( k => {
144 clients["index"][k].send(
145 JSON.stringify({code:"decrease",vid:page}), noop);
146 });
147 }
148 // Also notify potential opponents:
149 // hit all clients which check if sid corresponds
150 Object.keys(clients[page]).forEach( k => {
151 clients[page][k].send(JSON.stringify({code:"disconnect",id:sid}), noop);
152 });
153 });
154 });
155 });
156 }