-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathws-relay.js
168 lines (154 loc) · 5.83 KB
/
ws-relay.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
const WebSocket = require('ws');
const prompt = require('prompt');
const { success, error, warn, info, log, indent } = require('cli-msg');
const atob = require('atob');
let argv = require('minimist')(process.argv);
let promptsPassed = false;
function addPromptOverrideProperty(key, val) {
if (!prompt.override) {
prompt.override = {};
}
prompt.override[key] = val;
}
if (argv.hasOwnProperty('delay')) {
addPromptOverrideProperty('delay', argv.delay);
}
if (argv.hasOwnProperty('port')) {
addPromptOverrideProperty('port',argv.port);
}
if (argv.hasOwnProperty('rocketLeagueHost')) {
addPromptOverrideProperty('rocketLeagueHost',argv.rocketLeagueHost);
}
//Add timeout, in case of programmatic usage where a user may not be fully present
if (argv.timeout) {
let timeoutMs = parseInt(argv.timeout, 10);
if (timeoutMs > 0) {
setTimeout(() => {
if (!promptsPassed) {
console.error(`\n\nPrompts not completed within timeout limit (${timeoutMs}ms). Exiting`);
process.exit(100);
}
}, timeoutMs);
}
}
prompt.get([
{
description: 'Relay delay in milliseconds (used in cloud productions)',
pattern: /^\d+$/,
message: 'Must be a number',
name: 'delay',
required: true,
default: "0",
},
{
description: "Port number for this websocket server",
pattern: /^\d+$/,
message: 'Must be a number',
name: 'port',
required: true,
default: "49322",
},
{
description: "Hostname:Port that Rocket League is running on",
name: 'rocketLeagueHost',
required: true,
default: "localhost:49122"
}
], function (e, r) {
promptsPassed = true;
/**
* Rocket League WebSocket client
* @type {WebSocket}
*/
let wsClient;
let relayMsDelay = parseInt(r.delay, 10);
const wss = new WebSocket.Server({ port: r.port });
let connections = {};
info.wb("Opened WebSocket server on port " + r.port);
wss.on('connection', function connection(ws) {
let id = (+ new Date()).toString();
success.wb("Received connection: " + id);
connections[id] = {
connection: ws,
registeredFunctions: []
};
ws.send(JSON.stringify({
event: "wsRelay:info",
data: "Connected!"
}));
ws.on('message', function incoming(message) {
sendRelayMessage(id, message);
});
ws.on('close', function close() {
// Might run into race conditions with accessing connections for sending, but cant be arsed to account for this.
// If a connection closes something will be fucked anyway
delete connections[id];
});
});
initRocketLeagueWebsocket(r.rocketLeagueHost);
setInterval(function () {
if (wsClient.readyState === WebSocket.CLOSED) {
warn.wb("Rocket League WebSocket Server Closed. Attempting to reconnect");
initRocketLeagueWebsocket(r.rocketLeagueHost);
}
}, 10000);
function sendRelayMessage(senderConnectionId, message) {
let json = JSON.parse(message);
log.wb(senderConnectionId + "> Sent " + json.event);
let channelEvent = (json['event']).split(':');
if (channelEvent[0] === 'wsRelay') {
if (channelEvent[1] === 'register') {
if (connections[senderConnectionId].registeredFunctions.indexOf(json['data']) < 0) {
connections[senderConnectionId].registeredFunctions.push(json['data']);
info.wb(senderConnectionId + "> Registered to receive: "+json['data']);
} else {
warn.wb(senderConnectionId + "> Attempted to register an already registered function: "+json['data']);
}
} else if (channelEvent[1] === 'unregister') {
let idx = connections[senderConnectionId].registeredFunctions.indexOf(json['data']);
if (idx > -1) {
connections[senderConnectionId].registeredFunctions.splice(idx, 1);
info.wb(senderConnectionId + "> Unregistered: "+json['data']);
} else {
warn.wb(senderConnectionId + "> Attempted to unregister a non-registered function: "+json['data']);
}
}
return;
}
for (let k in connections) {
if (senderConnectionId === k) {
continue;
}
if (!connections.hasOwnProperty(k)) {
continue;
}
if (connections[k].registeredFunctions.indexOf(json['event']) > -1) {
setTimeout(() => {
try {
connections[k].connection.send(message);
} catch (e) {
//The connection can close between the exist check, and sending, so we catch it here and ignore
}
}, 0);
}
}
}
function initRocketLeagueWebsocket(rocketLeagueHost) {
wsClient = new WebSocket("ws://"+rocketLeagueHost);
wsClient.onopen = function open() {
success.wb("Connected to Rocket League on "+rocketLeagueHost);
};
wsClient.onmessage = function(message) {
let sendMessage = message.data;
if (sendMessage.substr(0, 1) !== '{') {
sendMessage = atob(message.data);
}
setTimeout(() => {
sendRelayMessage(0, sendMessage);
}, relayMsDelay);
};
wsClient.onerror = function (err) {
error.wb(`Error connecting to Rocket League on host "${rocketLeagueHost}"\nIs the plugin loaded into Rocket League? Run the command "plugin load sos" from the BakkesMod console to make sure`);
};
}
});