1 const url
= require('url');
3 // Node version in Ubuntu 16.04 does not know about URL class
4 // NOTE: url is already transformed, without ?xxx=yyy... parts
5 function getJsonFromUrl(url
)
7 const query
= url
.substr(2); //starts with "/?"
9 query
.split("&").forEach((part
) => {
10 const item
= part
.split("=");
11 result
[item
[0]] = decodeURIComponent(item
[1]);
16 module
.exports = function(wss
) {
17 // Associative array page --> sid --> tmpId --> socket
18 // "page" is either "/" for hall or "/game/some_gid" for Game,
19 // tmpId is required if a same user (browser) has different tabs
21 wss
.on("connection", (socket
, req
) => {
22 const query
= getJsonFromUrl(req
.url
);
23 const sid
= query
["sid"];
24 const tmpId
= query
["tmpId"];
25 const page
= query
["page"];
26 const notifyRoom
= (page
,code
,obj
={}) => {
29 Object
.keys(clients
[page
]).forEach(k
=> {
30 Object
.keys(clients
[page
][k
]).forEach(x
=> {
31 if (k
== sid
&& x
== tmpId
)
33 clients
[page
][k
][x
].send(JSON
.stringify(Object
.assign(
34 {code:code
, from:sid
}, obj
)));
38 const deleteConnexion
= () => {
39 if (!clients
[page
] || !clients
[page
][sid
] || !clients
[page
][sid
][tmpId
])
40 return; //job already done
41 delete clients
[page
][sid
][tmpId
];
42 if (Object
.keys(clients
[page
][sid
]).length
== 0)
44 delete clients
[page
][sid
];
45 if (Object
.keys(clients
[page
]) == 0)
49 const messageListener
= (objtxt
) => {
50 let obj
= JSON
.parse(objtxt
);
53 // Check if receiver is connected, because there may be some lag
54 // between a client disconnects and another notice.
55 if (Array
.isArray(obj
.target
))
57 if (!clients
[page
][obj
.target
[0]] ||
58 !clients
[page
][obj
.target
[0]][obj
.target
[1]])
63 else if (!clients
[page
][obj
.target
])
68 // Wait for "connect" message to notify connection to the room,
69 // because if game loading is slow the message listener might
70 // not be ready too early.
73 notifyRoom(page
, "connect");
74 if (page
.indexOf("/game/") >= 0)
75 notifyRoom("/", "gconnect", {page:page
});
81 if (!clients
[page
] || !clients
[page
][sid
])
83 // I effectively disconnected from this page:
84 notifyRoom(page
, "disconnect");
85 if (page
.indexOf("/game/") >= 0)
86 notifyRoom("/", "gdisconnect", {page:page
});
91 // Self multi-connect: manual removal + disconnect
92 const doKill
= (pg
) => {
93 Object
.keys(clients
[pg
][obj
.sid
]).forEach(x
=> {
94 clients
[pg
][obj
.sid
][x
].send(JSON
.stringify({code: "killed"}));
96 delete clients
[pg
][obj
.sid
];
98 const disconnectFromOtherConnexion
= (pg
,code
,o
={}) => {
99 Object
.keys(clients
[pg
]).forEach(k
=> {
102 Object
.keys(clients
[pg
][k
]).forEach(x
=> {
103 clients
[pg
][k
][x
].send(JSON
.stringify(Object
.assign(
104 {code:code
, from:obj
.sid
}, o
)));
109 Object
.keys(clients
).forEach(pg
=> {
110 if (!!clients
[pg
][obj
.sid
])
113 disconnectFromOtherConnexion(pg
, "disconnect");
114 if (pg
.indexOf("/game/") >= 0)
115 disconnectFromOtherConnexion("/", "gdisconnect", {page:pg
});
120 case "pollclients": //from Hall or Game
123 Object
.keys(clients
[page
]).forEach(k
=> {
124 // Avoid polling myself: no new information to get
128 socket
.send(JSON
.stringify({code:"pollclients", sockIds:sockIds
}));
131 case "pollclientsandgamers": //from Hall
134 Object
.keys(clients
["/"]).forEach(k
=> {
135 // Avoid polling myself: no new information to get
137 sockIds
.push({sid:k
});
139 // NOTE: a "gamer" could also just be an observer
140 Object
.keys(clients
).forEach(p
=> {
143 Object
.keys(clients
[p
]).forEach(k
=> {
145 sockIds
.push({sid:k
, page:p
}); //page needed for gamers
149 socket
.send(JSON
.stringify({code:"pollclientsandgamers", sockIds:sockIds
}));
153 // Asking something: from is fully identified,
154 // but the requested resource can be from any tmpId (except current!)
161 const tmpIds
= Object
.keys(clients
[page
][obj
.target
]);
162 if (obj
.target
== sid
) //targetting myself
164 const idx_myTmpid
= tmpIds
.findIndex(x
=> x
== tmpId
);
165 if (idx_myTmpid
>= 0)
166 tmpIds
.splice(idx_myTmpid
, 1);
168 const tmpId_idx
= Math
.floor(Math
.random() * tmpIds
.length
);
169 clients
[page
][obj
.target
][tmpIds
[tmpId_idx
]].send(
170 JSON
.stringify({code:obj
.code
, from:[sid
,tmpId
]}));
174 // Some Hall events: target all tmpId's (except mine),
175 case "refusechallenge":
177 Object
.keys(clients
[page
][obj
.target
]).forEach(x
=> {
178 if (obj
.target
!= sid
|| x
!= tmpId
)
180 clients
[page
][obj
.target
][x
].send(JSON
.stringify(
181 {code:obj
.code
, data:obj
.data
}));
186 // Notify all room: mostly game events
190 case "deletechallenge":
196 notifyRoom(page
, obj
.code
, {data:obj
.data
});
199 // Passing, relaying something: from isn't needed,
200 // but target is fully identified (sid + tmpId)
206 clients
[page
][obj
.target
[0]][obj
.target
[1]].send(JSON
.stringify(
207 {code:obj
.code
, data:obj
.data
}));
211 const closeListener
= () => {
212 // For tab or browser closing:
215 // Update clients object: add new connexion
217 clients
[page
] = {[sid
]: {[tmpId
]: socket
}};
218 else if (!clients
[page
][sid
])
219 clients
[page
][sid
] = {[tmpId
]: socket
};
221 clients
[page
][sid
][tmpId
] = socket
;
222 socket
.on("message", messageListener
);
223 socket
.on("close", closeListener
);