-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.js
184 lines (164 loc) · 5.73 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
var WebSocketServer = require("ws").Server;
var wss = new WebSocketServer({ port: 6503 });
const util = require("util");
const dateStr = () =>
new Date().toISOString().replace(/T/, " ").replace(/\..+/, "");
function systemLog(logInput) {
console.log(
`\x1b[90m[\x1b[31m${dateStr()}\x1b[90m][\x1b[91mSystem\x1b[90m]\x1b[0m: ${logInput}`
);
}
if (wss) {
systemLog("Server online.");
}
var rooms = [];
/* Rooms
RoomID is native id for each
Each room has connections, which all will lead back to UI.
Room can only have ONE of each type of connection (protocol).
*/
function Room(roomID) {
this.roomID = roomID;
this.UI = null;
this.connections = [];
this.offers = [];
}
function heartbeat() {
this.isAlive = true;
}
wss.on("connection", (ws, req) => {
ws.isAlive = true;
ws.on("pong", heartbeat);
const roomID = new URLSearchParams(req.url.substr(1)).get("roomID");
function connCommandLog(logInput) {
console.log(
`\x1b[90m[\x1b[31m${dateStr()}\x1b[90m][\x1b[92m${roomID.substr(
0,
8
)}\x1b[90m][\x1b[93m${ws.protocol}\x1b[90m]\x1b[0m: ${logInput}`
);
}
function roomCommandLog(logInput) {
console.log(
`\x1b[90m[\x1b[31m${dateStr()}\x1b[90m][\x1b[92m${roomID.substr(
0,
8
)}\x1b[90m]\x1b[0m: ${logInput}`
);
}
systemLog("Connection request recieved.");
// If no roomID or protocol provided, terminate connection
if (!roomID || !ws.protocol) {
systemLog(
`RoomID or protocol not provided, terminating new connection. (room: ${roomID}, protocol: ${ws.protocol})`
);
ws.send({ error: true, errorMessage: "Protocol already in use!" });
ws.terminate();
}
const currentRoom = () => rooms.find((x) => x.roomID === roomID);
const getConnection = (protocol) =>
currentRoom().connections.find((x) => x.protocol === protocol);
const getOffer = (protocol) =>
currentRoom().offers.find((x) => x.protocol === protocol);
const matchingUI = () => currentRoom().UI;
const sendAsJSON = (sendto, obj) =>
sendto === null ? undefined : sendto.send(JSON.stringify(obj));
// Check if room exists and create/add current connection OR kick user since procotol exists in room
if (
currentRoom(roomID) &&
currentRoom(roomID).connections.find((x) => x.protocol === ws.protocol) !=
undefined
) {
roomCommandLog(
`Connection already exists for ${ws.protocol}, terminating new connection.`
);
sendAsJSON(ws, { error: true, errorMessage: "Protocol already in use!" });
ws.terminate();
} else if (!currentRoom(roomID)) {
roomCommandLog("Fresh roomID, creating room object.");
rooms.push(new Room(roomID));
}
if (ws.protocol === "UI") {
currentRoom().UI = ws;
} else {
currentRoom().connections.push(ws);
}
// Send offer if UI room already exists + added offer
const offerToSend = getOffer(ws.protocol);
if (matchingUI() && offerToSend && ws.protocol !== "UI") {
connCommandLog("Found matching UI offer, awaiting response.");
sendAsJSON(ws, offerToSend.offer);
} else if (matchingUI() && ws.protocol !== "UI") {
connCommandLog("No matching offer was found.");
sendAsJSON(ws, {
error: true,
errorMessage: "There was no available offer for this protocol.",
});
}
ws.on("message", (data) => {
let msgObj = JSON.parse(data.toString());
if (msgObj.reset) {
roomCommandLog(
"Reset request recieved, forcing offer regeneration from all connections."
);
for (connection of currentRoom().connections) {
sendAsJSON(connection, { closed: true });
sendAsJSON(ws, { closed: true, protocol: ws.protocol });
}
}
if (ws.protocol === "UI") {
// If is UI, either send offer to connection, or store offer in offers
connCommandLog(
`Recieved generated offer for ${msgObj.protocol}, sending to match or adding to store.`
);
const connWanted = getConnection(msgObj.protocol);
connWanted
? sendAsJSON(connWanted, msgObj.offer)
: currentRoom().offers.push(msgObj);
} else {
// If protocol isn't UI, send offer to UI
connCommandLog("Recieved generated offer, sending to UI.");
sendAsJSON(matchingUI(), msgObj);
}
});
// Heartbeat interval for checking connection
ws.interval = setInterval(function ping() {
// If pong hasn't returned after 70 seconds, terminate connection and remove from room
if (!ws.isAlive) {
connCommandLog("Heartbeat not recieved.");
return ws.terminate();
}
ws.isAlive = false;
ws.ping();
}, 30000);
ws.on("close", (code, reason) => {
clearInterval(ws.interval);
ws.protocol === "UI"
? closeUIConnection(reason.toString())
: closeConnection(reason.toString());
});
// Function for closing connections. Logs, notifies UI, and removes connection from room.
function closeConnection(errMsg = "") {
connCommandLog(
`Connection closed, notifying UI and removing. (msg: ${errMsg})`
);
// If on connection close, WS is known connection, notify UI and remove connection from connections
sendAsJSON(matchingUI(), { closed: true, protocol: ws.protocol });
currentRoom().connections = currentRoom().connections.filter(
(x) => x !== ws
);
}
// Function for closing UI connection. Logs, notifies room connections, and clears room.
function closeUIConnection(errMsg = "") {
connCommandLog(
`Connection closed, notifying connections and clearing room. (msg: ${errMsg})`
);
// If connection close is UI, inform all connections of disconnect
for (connection of currentRoom().connections) {
sendAsJSON(connection, { closed: true });
}
// Clear current room of UI and offers
currentRoom().UI = null;
currentRoom().offers = [];
}
});