1 const url
= require('url');
3 // Node version in Ubuntu 16.04 does not know about URL class
4 function getJsonFromUrl(url
)
6 const query
= url
.substr(2); //starts with "/?"
8 query
.split("&").forEach((part
) => {
9 const item
= part
.split("=");
10 result
[item
[0]] = decodeURIComponent(item
[1]);
15 // Removal in array of strings (socket IDs)
16 function remInArray(arr
, item
)
18 const idx
= arr
.indexOf(item
);
23 // TODO: empêcher multi-log du même user (envoyer le user ID + secret en même temps que name et...)
24 // --> si secret ne matche pas celui trouvé en DB, stop
25 // TODO: this file "in the end" would be much simpler, essentially just tracking connect/disconnect
26 // (everything else using WebRTC)
27 // TODO: lorsque challenge accepté, seul le dernier joueur à accepter envoi message "please start game"
28 // avec les coordonnées des participants. Le serveur renvoit alors les détails de la partie (couleurs, position)
29 //TODO: programmatic re-navigation on current game if we receive a move and are not there
31 module
.exports = function(wss
) {
32 let clients
= {}; //associative array client sid --> {socket, curPath}
33 let pages
= {}; //associative array path --> array of client sid
34 // No-op function as a callback when sending messages
35 const noop
= () => { };
36 wss
.on("connection", (socket
, req
) => {
37 const query
= getJsonFromUrl(req
.url
);
38 const sid
= query
["sid"];
39 // Ignore duplicate connections (on the same live game that we play):
41 return socket
.send(JSON
.stringify({code:"duplicate"}));
42 // We don't know yet on which page the user will be
43 clients
[sid
] = {socket: socket
, path: ""};
45 socket
.on("message", objtxt
=> {
46 let obj
= JSON
.parse(objtxt
);
50 if (clients
[sid
].path
.length
> 0)
51 remInArray(pages
[clients
[sid
].path
], sid
);
52 clients
[sid
].path
= obj
.path
;
53 pages
[obj
.path
].push(sid
);
54 // TODO also: notify "old" sub-room that I left (if it was not index)
59 Object
.keys(pages
).forEach(
60 path
=> { countings
[path
] = pages
[path
].length
; });
61 socket
.send(JSON
.stringify({code:"counts",counts:countings
}));
65 // Send to every client connected on index an update message for counts
66 pages
["/"].forEach((id
) => {
67 clients
[id
].socket
.send(
68 JSON
.stringify({code:"increase",path:obj
.path
}), noop
);
70 // TODO: do not notify anything in rules and problems sections (no socket required)
71 // --> in fact only /Atomic (main hall) and inside a game: /Atomic/392f3ju
72 // Also notify the (sub-)room (including potential opponents):
73 Object
.keys(clients
[page
]).forEach( k
=> {
74 clients
[page
][k
].send(JSON
.stringify({code:"connect",id:sid
}), noop
);
76 // Finally, receive (sub-)room composition
79 // NOTE: no "leave" counterpart (because it's always to enter somewhere else)
82 // Transmit chats and moves to current room
83 // TODO: WebRTC instead in this case (most demanding?)
85 if (!!clients
[page
][obj
.oppid
])
87 clients
[page
][obj
.oppid
].send(
88 JSON
.stringify({code:"newchat",msg:obj
.msg
}), noop
);
92 if (!!clients
[page
][obj
.oppid
])
94 clients
[page
][obj
.oppid
].send(
95 JSON
.stringify({code:"newmove",move:obj
.move}), noop
);
100 // TODO: generalize that for several opponents
102 if (!!clients
[page
][obj
.oppid
])
103 socket
.send(JSON
.stringify({code:"pong",gameId:obj
.gameId
}));
106 if (!!clients
[page
][obj
.oppid
])
108 const oppId
= obj
.oppid
;
109 obj
.oppid
= sid
; //I'm oppid for my opponent
110 clients
[page
][oppId
].send(JSON
.stringify(obj
), noop
);
113 // TODO: moreover, here, game info should be sent (through challenge; not stored here)
118 const oppId
= games
[page
]["id"];
119 const fen
= games
[page
]["fen"];
120 const gameId
= games
[page
]["gameid"];
122 const mycolor
= (Math
.random() < 0.5 ? 'w' : 'b');
123 socket
.send(JSON
.stringify(
124 {code:"newgame",fen:fen
,oppid:oppId
,color:mycolor
,gameid:gameId
}));
125 if (!!clients
[page
][oppId
])
127 clients
[page
][oppId
].send(
129 {code:"newgame",fen:fen
,oppid:sid
,color:mycolor
=="w"?"b":"w",gameid:gameId
}),
134 games
[page
] = {id:sid
, fen:obj
.fen
, gameid:obj
.gameid
}; //wait for opponent
136 case "cancelnewgame": //if a user cancel his seek
137 // TODO: just transmit event
138 //delete games[page];
140 // TODO: also other challenge events
142 if (!!clients
[page
][obj
.oppid
])
143 clients
[page
][obj
.oppid
].send(JSON
.stringify({code:"resign"}), noop
);
145 // TODO: case "challenge" (get ID) --> send to all, "acceptchallenge" (with ID) --> send to all, "cancelchallenge" --> send to all
146 // also, "sendgame" (give current game info, if any) --> to new connections, "sendchallenges" (same for challenges) --> to new connections
149 socket
.on("close", () => {
151 // TODO: carefully delete pages[.........]
155 // Send to every client connected on index an update message for counts
156 Object
.keys(clients
["index"]).forEach( k
=> {
157 clients
["index"][k
].send(
158 JSON
.stringify({code:"decrease",vid:page
}), noop
);
161 // Also notify potential opponents:
162 // hit all clients which check if sid corresponds
163 Object
.keys(clients
[page
]).forEach( k
=> {
164 clients
[page
][k
].send(JSON
.stringify({code:"disconnect",id:sid
}), noop
);