Commit | Line | Data |
---|---|---|
a29d9d6b | 1 | const url = require('url'); |
1d184b4c | 2 | |
2807f530 | 3 | // Node version in Ubuntu 16.04 does not know about URL class |
a0c41e7e | 4 | // NOTE: url is already transformed, without ?xxx=yyy... parts |
98db2082 BA |
5 | function getJsonFromUrl(url) |
6 | { | |
80ee5d5a BA |
7 | const query = url.substr(2); //starts with "/?" |
8 | let result = {}; | |
9 | query.split("&").forEach((part) => { | |
5bd05dba BA |
10 | const item = part.split("="); |
11 | result[item[0]] = decodeURIComponent(item[1]); | |
12 | }); | |
13 | return result; | |
2807f530 BA |
14 | } |
15 | ||
1d184b4c | 16 | module.exports = function(wss) { |
8418f0d7 BA |
17 | // Associative array sid --> tmpId --> {socket, page}, |
18 | // "page" is either "/" for hall or "/game/some_gid" for Game, | |
19 | // tmpId is required if a same user (browser) has different tabs | |
20 | let clients = {}; | |
5bd05dba BA |
21 | wss.on("connection", (socket, req) => { |
22 | const query = getJsonFromUrl(req.url); | |
23 | const sid = query["sid"]; | |
8418f0d7 | 24 | const tmpId = query["tmpId"]; |
c6788ecf | 25 | const notifyRoom = (page,code,obj={},excluded=[]) => { |
92a523d1 | 26 | Object.keys(clients).forEach(k => { |
c6788ecf BA |
27 | if (k in excluded) |
28 | return; | |
92a523d1 | 29 | if (k != sid && clients[k].page == page) |
5c8e044f BA |
30 | { |
31 | clients[k].sock.send(JSON.stringify(Object.assign( | |
8418f0d7 | 32 | {code:code, from:[sid,tmpId]}, obj))); |
5c8e044f | 33 | } |
92a523d1 BA |
34 | }); |
35 | }; | |
a3ac374b | 36 | const messageListener = (objtxt) => { |
5bd05dba | 37 | let obj = JSON.parse(objtxt); |
5a3da968 | 38 | if (!!obj.target && !clients[obj.target]) |
b4d619d1 | 39 | return; //receiver not connected, nothing we can do |
5bd05dba BA |
40 | switch (obj.code) |
41 | { | |
a3ac374b BA |
42 | // Wait for "connect" message to notify connection to the room, |
43 | // because if game loading is slow the message listener might | |
44 | // not be ready too early. | |
41c80bb6 | 45 | case "connect": |
120fe373 | 46 | { |
8418f0d7 | 47 | const curPage = clients[sid][tmpId].page; |
120fe373 BA |
48 | notifyRoom(curPage, "connect"); //Hall or Game |
49 | if (curPage.indexOf("/game/") >= 0) | |
ac8f441c | 50 | notifyRoom("/", "gconnect"); //notify main hall |
41c80bb6 | 51 | break; |
120fe373 | 52 | } |
8418f0d7 BA |
53 | case "disconnect": |
54 | { | |
55 | const oldPage = obj.page; | |
56 | notifyRoom(oldPage, "disconnect"); //Hall or Game | |
57 | if (oldPage.indexOf("/game/") >= 0) | |
58 | notifyRoom("/", "gdisconnect"); //notify main hall | |
59 | break; | |
60 | } | |
81d9ce72 | 61 | case "pollclients": |
ac8f441c | 62 | { |
8418f0d7 BA |
63 | const curPage = clients[sid][tmpId].page; |
64 | let sockIds = {}; //result, object sid ==> [tmpIds] | |
65 | Object.keys(clients).forEach(k => { | |
66 | Object.keys(clients[k]).forEach(x => { | |
67 | if ((k != sid || x != tmpId) | |
68 | && clients[k][x].page == curPage) | |
69 | { | |
70 | if (!sockIds[k]) | |
71 | sockIds[k] = [x]; | |
72 | else | |
73 | sockIds[k].push(x); | |
74 | } | |
75 | }); | |
76 | }); | |
77 | socket.send(JSON.stringify({code:"pollclients", sockIds:sockIds})); | |
92a523d1 | 78 | break; |
ac8f441c BA |
79 | } |
80 | case "pollgamers": | |
8418f0d7 BA |
81 | { |
82 | let sockIds = {}; | |
83 | Object.keys(clients).forEach(k => { | |
84 | Object.keys(clients[k]).forEach(x => { | |
85 | if ((k != sid || x != tmpId) | |
86 | && clients[k][x].page.indexOf("/game/") >= 0) | |
87 | { | |
88 | if (!sockIds[k]) | |
89 | sockIds[k] = [x]; | |
90 | else | |
91 | sockIds[k].push(x); | |
92 | } | |
93 | }); | |
94 | }); | |
95 | socket.send(JSON.stringify({code:"pollgamers", sockIds:sockIds})); | |
5a3da968 | 96 | break; |
8418f0d7 | 97 | } |
5a3da968 | 98 | case "askidentity": |
8418f0d7 BA |
99 | { |
100 | // Identity only depends on sid, so select a tmpId at random | |
101 | const tmpIds = Object.keys(clients[obj.target]); | |
102 | const tmpId_idx = Math.floor(Math.random() * tmpIds.length); | |
103 | clients[obj.target][tmpIds[tmpId_idx]].sock.send(JSON.stringify( | |
104 | {code:"askidentity",from:[sid,tmpId]})); | |
81d9ce72 | 105 | break; |
8418f0d7 | 106 | } |
a0c41e7e | 107 | case "asklastate": |
8418f0d7 BA |
108 | clients[obj.target[0]][obj.target[1]].sock.send(JSON.stringify( |
109 | {code:"asklastate",from:[sid,tmpId]})); | |
a0c41e7e | 110 | break; |
dd75774d | 111 | case "askchallenge": |
8418f0d7 BA |
112 | clients[obj.target[0]][obj.target[1]].sock.send(JSON.stringify( |
113 | {code:"askchallenge",from:[sid,tmpId]})); | |
81d9ce72 | 114 | break; |
cd0d7743 | 115 | case "askgames": |
dc284d90 | 116 | { |
c6788ecf | 117 | // Check all clients playing, and send them a "askgame" message |
8418f0d7 BA |
118 | // game ID --> [ sid1 --> array of tmpIds, sid2 --> array of tmpIds] |
119 | let gameSids = {}; | |
dc284d90 | 120 | const regexpGid = /\/[a-zA-Z0-9]+$/; |
c6788ecf | 121 | Object.keys(clients).forEach(k => { |
8418f0d7 BA |
122 | Object.keys(clients[k]).forEach(x => { |
123 | if ((k != sid || x != tmpId) | |
124 | && clients[k][x].page.indexOf("/game/") >= 0) | |
c6788ecf | 125 | { |
8418f0d7 | 126 | const gid = clients[k][x].page.match(regexpGid)[0]; |
dc284d90 | 127 | if (!gameSids[gid]) |
8418f0d7 BA |
128 | gameSids[gid] = [{k: [x]}]; |
129 | else if (k == Object.keys(gameSids[gid][0])[0]) | |
130 | gameSids[gid][0][k].push(x); | |
131 | else if (gameSids[gid].length == 1) | |
132 | gameSids[gid].push({k: [x]}); | |
dc284d90 | 133 | else |
8418f0d7 | 134 | Object.values(gameSids[gid][1]).push(x); |
c6788ecf BA |
135 | } |
136 | }); | |
dc284d90 BA |
137 | // Request only one client out of 2 (TODO: this is a bit heavy) |
138 | // Alt: ask game to all, and filter later? | |
139 | Object.keys(gameSids).forEach(gid => { | |
140 | const L = gameSids[gid].length; | |
8418f0d7 | 141 | const sidIdx = L > 1 |
dc284d90 BA |
142 | ? Math.floor(Math.random() * Math.floor(L)) |
143 | : 0; | |
8418f0d7 BA |
144 | const tmpIdx = Object.values(gameSids[gid][sidIdx] |
145 | const rid = gameSids[gid][sidIdx][tmpIdx]; | |
146 | clients[sidIdx][tmpIdx].sock.send(JSON.stringify( | |
147 | {code:"askgame", from: [sid,tmpId]})); | |
dc284d90 BA |
148 | }); |
149 | break; | |
150 | } | |
26f3a887 BA |
151 | case "askgame": |
152 | clients[obj.target].sock.send(JSON.stringify( | |
153 | {code:"askgame", from:sid})); | |
154 | break; | |
dc284d90 BA |
155 | case "askfullgame": |
156 | clients[obj.target].sock.send(JSON.stringify( | |
157 | {code:"askfullgame", from:sid})); | |
158 | break; | |
159 | case "fullgame": | |
160 | clients[obj.target].sock.send(JSON.stringify( | |
161 | {code:"fullgame", game:obj.game})); | |
5a3da968 BA |
162 | break; |
163 | case "identity": | |
f41ce580 BA |
164 | clients[obj.target].sock.send(JSON.stringify( |
165 | {code:"identity",user:obj.user})); | |
4d64881e | 166 | break; |
5bd05dba | 167 | case "refusechallenge": |
f41ce580 BA |
168 | clients[obj.target].sock.send(JSON.stringify( |
169 | {code:"refusechallenge", cid:obj.cid, from:sid})); | |
5bd05dba | 170 | break; |
98f48579 | 171 | case "deletechallenge": |
f41ce580 BA |
172 | clients[obj.target].sock.send(JSON.stringify( |
173 | {code:"deletechallenge", cid:obj.cid, from:sid})); | |
98f48579 | 174 | break; |
a6bddfc6 | 175 | case "newgame": |
92a523d1 | 176 | clients[obj.target].sock.send(JSON.stringify( |
5bd05dba | 177 | {code:"newgame", gameInfo:obj.gameInfo, cid:obj.cid})); |
a6bddfc6 | 178 | break; |
42c15a75 | 179 | case "challenge": |
92a523d1 | 180 | clients[obj.target].sock.send(JSON.stringify( |
42c15a75 BA |
181 | {code:"challenge", chall:obj.chall, from:sid})); |
182 | break; | |
dd75774d | 183 | case "game": |
c6788ecf BA |
184 | if (!!obj.target) |
185 | { | |
186 | clients[obj.target].sock.send(JSON.stringify( | |
80ee5d5a | 187 | {code:"game", game:obj.game, from:sid})); |
c6788ecf BA |
188 | } |
189 | else | |
190 | { | |
191 | // Notify all room except opponent and me: | |
80ee5d5a | 192 | notifyRoom("/", "game", {game:obj.game}, [obj.oppsid]); |
c6788ecf | 193 | } |
4d64881e | 194 | break; |
5bd05dba | 195 | case "newchat": |
ac8f441c | 196 | notifyRoom(clients[sid].page, "newchat", {chat:obj.chat}); |
5bd05dba | 197 | break; |
5bd05dba | 198 | // TODO: WebRTC instead in this case (most demanding?) |
26f3a887 | 199 | // --> Or else: at least do a "notifyRoom" (also for draw, resign...) |
5bd05dba | 200 | case "newmove": |
f41ce580 | 201 | clients[obj.target].sock.send(JSON.stringify( |
c6788ecf | 202 | {code:"newmove", move:obj.move})); |
5bd05dba BA |
203 | break; |
204 | case "lastate": | |
f41ce580 BA |
205 | clients[obj.target].sock.send(JSON.stringify( |
206 | {code:"lastate", state:obj.state})); | |
5bd05dba | 207 | break; |
5bd05dba | 208 | case "resign": |
f41ce580 | 209 | clients[obj.target].sock.send(JSON.stringify( |
3837d4f7 | 210 | {code:"resign", side:obj.side})); |
5bd05dba | 211 | break; |
b988c726 | 212 | case "abort": |
f41ce580 | 213 | clients[obj.target].sock.send(JSON.stringify( |
3837d4f7 | 214 | {code:"abort"})); |
b988c726 | 215 | break; |
2cc10cdb | 216 | case "drawoffer": |
f41ce580 BA |
217 | clients[obj.target].sock.send(JSON.stringify( |
218 | {code:"drawoffer"})); | |
2cc10cdb BA |
219 | break; |
220 | case "draw": | |
f41ce580 | 221 | clients[obj.target].sock.send(JSON.stringify( |
dcd68c41 | 222 | {code:"draw", message:obj.message})); |
2cc10cdb | 223 | break; |
5bd05dba | 224 | } |
a3ac374b BA |
225 | }; |
226 | const closeListener = () => { | |
5bd05dba | 227 | delete clients[sid]; |
a3ac374b BA |
228 | }; |
229 | if (!!clients[sid]) | |
230 | { | |
231 | // Turn off old sock through current client: | |
232 | clients[sid].sock.send(JSON.stringify({code:"duplicate"})); | |
233 | } | |
234 | // Potentially replace current connection: | |
235 | clients[sid] = {sock: socket, page: query["page"]}; | |
236 | socket.on("message", messageListener); | |
237 | socket.on("close", closeListener); | |
5bd05dba | 238 | }); |
1d184b4c | 239 | } |