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) { |
5bd05dba BA |
17 | let clients = {}; //associative array sid --> socket |
18 | wss.on("connection", (socket, req) => { | |
19 | const query = getJsonFromUrl(req.url); | |
bc093771 BA |
20 | if (query["page"] != "/" && query["page"].indexOf("/game/") < 0) |
21 | return; //other tabs don't need to be connected | |
5bd05dba | 22 | const sid = query["sid"]; |
c6788ecf | 23 | const notifyRoom = (page,code,obj={},excluded=[]) => { |
92a523d1 | 24 | Object.keys(clients).forEach(k => { |
c6788ecf BA |
25 | if (k in excluded) |
26 | return; | |
92a523d1 | 27 | if (k != sid && clients[k].page == page) |
5c8e044f BA |
28 | { |
29 | clients[k].sock.send(JSON.stringify(Object.assign( | |
c6788ecf | 30 | {code:code, from:sid}, obj))); |
5c8e044f | 31 | } |
92a523d1 BA |
32 | }); |
33 | }; | |
a3ac374b | 34 | const messageListener = (objtxt) => { |
5bd05dba | 35 | let obj = JSON.parse(objtxt); |
5a3da968 | 36 | if (!!obj.target && !clients[obj.target]) |
b4d619d1 | 37 | return; //receiver not connected, nothing we can do |
5bd05dba BA |
38 | switch (obj.code) |
39 | { | |
a3ac374b BA |
40 | case "duplicate": |
41 | // Turn off message listening, and send disconnect if needed: | |
42 | socket.removeListener("message", messageListener); | |
43 | socket.removeListener("close", closeListener); | |
d641bec1 | 44 | // From obj.page to clients[sid].page (TODO: unclear) |
a3ac374b BA |
45 | if (clients[sid].page != obj.page) |
46 | { | |
d641bec1 BA |
47 | notifyRoom(obj.page, "disconnect"); |
48 | if (obj.page.indexOf("/game/") >= 0) | |
a3ac374b BA |
49 | notifyRoom("/", "gdisconnect"); |
50 | } | |
51 | break; | |
52 | // Wait for "connect" message to notify connection to the room, | |
53 | // because if game loading is slow the message listener might | |
54 | // not be ready too early. | |
41c80bb6 | 55 | case "connect": |
120fe373 BA |
56 | { |
57 | const curPage = clients[sid].page; | |
58 | notifyRoom(curPage, "connect"); //Hall or Game | |
59 | if (curPage.indexOf("/game/") >= 0) | |
ac8f441c | 60 | notifyRoom("/", "gconnect"); //notify main hall |
41c80bb6 | 61 | break; |
120fe373 | 62 | } |
81d9ce72 | 63 | case "pollclients": |
ac8f441c | 64 | { |
f41ce580 | 65 | const curPage = clients[sid].page; |
81d9ce72 | 66 | socket.send(JSON.stringify({code:"pollclients", |
ac8f441c BA |
67 | sockIds: Object.keys(clients).filter(k => |
68 | k != sid && clients[k].page == curPage | |
c6788ecf | 69 | )})); |
92a523d1 | 70 | break; |
ac8f441c BA |
71 | } |
72 | case "pollgamers": | |
ac8f441c BA |
73 | socket.send(JSON.stringify({code:"pollgamers", |
74 | sockIds: Object.keys(clients).filter(k => | |
75 | k != sid && clients[k].page.indexOf("/game/") >= 0 | |
76 | )})); | |
77 | break; | |
92a523d1 | 78 | case "pagechange": |
9335d45b | 79 | // page change clients[sid].page --> obj.page |
a0c41e7e | 80 | // TODO: some offline rooms don't need to receive disconnect event |
c6788ecf | 81 | notifyRoom(clients[sid].page, "disconnect"); |
bcaa8c00 | 82 | if (clients[sid].page.indexOf("/game/") >= 0) |
ac8f441c | 83 | notifyRoom("/", "gdisconnect"); |
92a523d1 | 84 | clients[sid].page = obj.page; |
a0c41e7e BA |
85 | // No need to notify connection: it's self-sent in .vue file |
86 | //notifyRoom(obj.page, "connect"); | |
bcaa8c00 | 87 | if (obj.page.indexOf("/game/") >= 0) |
ac8f441c | 88 | notifyRoom("/", "gconnect"); |
5a3da968 BA |
89 | break; |
90 | case "askidentity": | |
f41ce580 BA |
91 | clients[obj.target].sock.send(JSON.stringify( |
92 | {code:"askidentity",from:sid})); | |
81d9ce72 | 93 | break; |
a0c41e7e BA |
94 | case "asklastate": |
95 | clients[obj.target].sock.send(JSON.stringify( | |
96 | {code:"asklastate",from:sid})); | |
97 | break; | |
dd75774d | 98 | case "askchallenge": |
f41ce580 BA |
99 | clients[obj.target].sock.send(JSON.stringify( |
100 | {code:"askchallenge",from:sid})); | |
81d9ce72 | 101 | break; |
cd0d7743 | 102 | case "askgames": |
dc284d90 | 103 | { |
c6788ecf | 104 | // Check all clients playing, and send them a "askgame" message |
dc284d90 BA |
105 | let gameSids = {}; //game ID --> [sid1, sid2] |
106 | const regexpGid = /\/[a-zA-Z0-9]+$/; | |
c6788ecf BA |
107 | Object.keys(clients).forEach(k => { |
108 | if (k != sid && clients[k].page.indexOf("/game/") >= 0) | |
109 | { | |
dc284d90 BA |
110 | const gid = clients[k].page.match(regexpGid)[0]; |
111 | if (!gameSids[gid]) | |
112 | gameSids[gid] = [k]; | |
113 | else | |
114 | gameSids[gid].push(k); | |
c6788ecf BA |
115 | } |
116 | }); | |
dc284d90 BA |
117 | // Request only one client out of 2 (TODO: this is a bit heavy) |
118 | // Alt: ask game to all, and filter later? | |
119 | Object.keys(gameSids).forEach(gid => { | |
120 | const L = gameSids[gid].length; | |
121 | const idx = L > 1 | |
122 | ? Math.floor(Math.random() * Math.floor(L)) | |
123 | : 0; | |
124 | const rid = gameSids[gid][idx]; | |
125 | clients[rid].sock.send(JSON.stringify( | |
ab6f48ea | 126 | {code:"askgame", from: sid})); |
dc284d90 BA |
127 | }); |
128 | break; | |
129 | } | |
26f3a887 BA |
130 | case "askgame": |
131 | clients[obj.target].sock.send(JSON.stringify( | |
132 | {code:"askgame", from:sid})); | |
133 | break; | |
dc284d90 BA |
134 | case "askfullgame": |
135 | clients[obj.target].sock.send(JSON.stringify( | |
136 | {code:"askfullgame", from:sid})); | |
137 | break; | |
138 | case "fullgame": | |
139 | clients[obj.target].sock.send(JSON.stringify( | |
140 | {code:"fullgame", game:obj.game})); | |
5a3da968 BA |
141 | break; |
142 | case "identity": | |
f41ce580 BA |
143 | clients[obj.target].sock.send(JSON.stringify( |
144 | {code:"identity",user:obj.user})); | |
4d64881e | 145 | break; |
5bd05dba | 146 | case "refusechallenge": |
f41ce580 BA |
147 | clients[obj.target].sock.send(JSON.stringify( |
148 | {code:"refusechallenge", cid:obj.cid, from:sid})); | |
5bd05dba | 149 | break; |
98f48579 | 150 | case "deletechallenge": |
f41ce580 BA |
151 | clients[obj.target].sock.send(JSON.stringify( |
152 | {code:"deletechallenge", cid:obj.cid, from:sid})); | |
98f48579 | 153 | break; |
a6bddfc6 | 154 | case "newgame": |
92a523d1 | 155 | clients[obj.target].sock.send(JSON.stringify( |
5bd05dba | 156 | {code:"newgame", gameInfo:obj.gameInfo, cid:obj.cid})); |
a6bddfc6 | 157 | break; |
42c15a75 | 158 | case "challenge": |
92a523d1 | 159 | clients[obj.target].sock.send(JSON.stringify( |
42c15a75 BA |
160 | {code:"challenge", chall:obj.chall, from:sid})); |
161 | break; | |
dd75774d | 162 | case "game": |
c6788ecf BA |
163 | if (!!obj.target) |
164 | { | |
165 | clients[obj.target].sock.send(JSON.stringify( | |
80ee5d5a | 166 | {code:"game", game:obj.game, from:sid})); |
c6788ecf BA |
167 | } |
168 | else | |
169 | { | |
170 | // Notify all room except opponent and me: | |
80ee5d5a | 171 | notifyRoom("/", "game", {game:obj.game}, [obj.oppsid]); |
c6788ecf | 172 | } |
4d64881e | 173 | break; |
5bd05dba | 174 | case "newchat": |
ac8f441c | 175 | notifyRoom(clients[sid].page, "newchat", {chat:obj.chat}); |
5bd05dba | 176 | break; |
5bd05dba | 177 | // TODO: WebRTC instead in this case (most demanding?) |
26f3a887 | 178 | // --> Or else: at least do a "notifyRoom" (also for draw, resign...) |
5bd05dba | 179 | case "newmove": |
f41ce580 | 180 | clients[obj.target].sock.send(JSON.stringify( |
c6788ecf | 181 | {code:"newmove", move:obj.move})); |
5bd05dba BA |
182 | break; |
183 | case "lastate": | |
f41ce580 BA |
184 | clients[obj.target].sock.send(JSON.stringify( |
185 | {code:"lastate", state:obj.state})); | |
5bd05dba | 186 | break; |
5bd05dba | 187 | case "resign": |
f41ce580 | 188 | clients[obj.target].sock.send(JSON.stringify( |
3837d4f7 | 189 | {code:"resign", side:obj.side})); |
5bd05dba | 190 | break; |
b988c726 | 191 | case "abort": |
f41ce580 | 192 | clients[obj.target].sock.send(JSON.stringify( |
3837d4f7 | 193 | {code:"abort"})); |
b988c726 | 194 | break; |
2cc10cdb | 195 | case "drawoffer": |
f41ce580 BA |
196 | clients[obj.target].sock.send(JSON.stringify( |
197 | {code:"drawoffer"})); | |
2cc10cdb BA |
198 | break; |
199 | case "draw": | |
f41ce580 | 200 | clients[obj.target].sock.send(JSON.stringify( |
dcd68c41 | 201 | {code:"draw", message:obj.message})); |
2cc10cdb | 202 | break; |
5bd05dba | 203 | } |
a3ac374b BA |
204 | }; |
205 | const closeListener = () => { | |
92a523d1 | 206 | const page = clients[sid].page; |
5bd05dba | 207 | delete clients[sid]; |
92a523d1 | 208 | notifyRoom(page, "disconnect"); |
bcaa8c00 | 209 | if (page.indexOf("/game/") >= 0) |
ac8f441c | 210 | notifyRoom("/", "gdisconnect"); //notify main hall |
a3ac374b BA |
211 | }; |
212 | if (!!clients[sid]) | |
213 | { | |
214 | // Turn off old sock through current client: | |
215 | clients[sid].sock.send(JSON.stringify({code:"duplicate"})); | |
216 | } | |
217 | // Potentially replace current connection: | |
218 | clients[sid] = {sock: socket, page: query["page"]}; | |
219 | socket.on("message", messageListener); | |
220 | socket.on("close", closeListener); | |
5bd05dba | 221 | }); |
1d184b4c | 222 | } |