@rustwirebot/rustplus.js 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/README.md +351 -0
- package/camera.js +425 -0
- package/cli/index.js +308 -0
- package/cli/pair.html +49 -0
- package/docs/API.md +101 -0
- package/docs/DownloadBundle.md +7 -0
- package/docs/PairingFlow.md +19 -0
- package/donate.md +10 -0
- package/examples/10_shoot_autoturret.js +100 -0
- package/examples/1_send_team_chat.js +16 -0
- package/examples/2_turn_smart_switch_on.js +21 -0
- package/examples/3_turn_smart_switch_off.js +21 -0
- package/examples/4_entity_changed_broadcasts.js +58 -0
- package/examples/5_download_map_jpeg.js +22 -0
- package/examples/6_async_requests.js +35 -0
- package/examples/7_render_camera.js +34 -0
- package/examples/8_move_ptz_camera.js +56 -0
- package/examples/9_zoom_ptz_camera.js +37 -0
- package/package.json +48 -0
- package/rustplus.js +380 -0
- package/rustplus.js.svg +34 -0
- package/rustplus.proto +431 -0
- package/vendor/push-receiver/LICENSE +21 -0
- package/vendor/push-receiver/NOTICE.md +32 -0
- package/vendor/push-receiver/android/fcm.js +134 -0
- package/vendor/push-receiver/client.js +216 -0
- package/vendor/push-receiver/constants.js +53 -0
- package/vendor/push-receiver/fcm/index.js +52 -0
- package/vendor/push-receiver/fcm/server-key/index.js +67 -0
- package/vendor/push-receiver/gcm/android_checkin.proto +96 -0
- package/vendor/push-receiver/gcm/checkin.proto +155 -0
- package/vendor/push-receiver/gcm/index.js +128 -0
- package/vendor/push-receiver/index.js +17 -0
- package/vendor/push-receiver/mcs.proto +328 -0
- package/vendor/push-receiver/parser.js +276 -0
- package/vendor/push-receiver/register/index.js +18 -0
- package/vendor/push-receiver/utils/base64/index.js +15 -0
- package/vendor/push-receiver/utils/decrypt/index.js +23 -0
- package/vendor/push-receiver/utils/http-request/index.js +43 -0
- package/vendor/push-receiver/utils/request/index.js +27 -0
- package/vendor/push-receiver/utils/timeout/index.js +7 -0
package/camera.js
ADDED
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const { EventEmitter } = require("events");
|
|
4
|
+
const Jimp = require("jimp");
|
|
5
|
+
|
|
6
|
+
class Camera extends EventEmitter {
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* These represent the possible buttons that can be sent to the server.
|
|
10
|
+
*/
|
|
11
|
+
static Buttons = {
|
|
12
|
+
NONE: 0,
|
|
13
|
+
FORWARD: 2,
|
|
14
|
+
BACKWARD: 4,
|
|
15
|
+
LEFT: 8,
|
|
16
|
+
RIGHT: 16,
|
|
17
|
+
JUMP: 32,
|
|
18
|
+
DUCK: 64,
|
|
19
|
+
SPRINT: 128,
|
|
20
|
+
USE: 256,
|
|
21
|
+
FIRE_PRIMARY: 1024,
|
|
22
|
+
FIRE_SECONDARY: 2048,
|
|
23
|
+
RELOAD: 8192,
|
|
24
|
+
FIRE_THIRD: 134217728,
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* These represent the possible control flags that can be sent to the server.
|
|
29
|
+
* For example, Static CCTV cameras will not support movement.
|
|
30
|
+
*/
|
|
31
|
+
static ControlFlags = {
|
|
32
|
+
NONE: 0,
|
|
33
|
+
MOVEMENT: 1,
|
|
34
|
+
MOUSE: 2,
|
|
35
|
+
SPRINT_AND_DUCK: 4,
|
|
36
|
+
FIRE: 8,
|
|
37
|
+
RELOAD: 16,
|
|
38
|
+
CROSSHAIR: 32,
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* @param rustplus An existing RustPlus instance
|
|
43
|
+
* @param identifier Camera Identifier, such as OILRIG1 (or custom name)
|
|
44
|
+
*
|
|
45
|
+
* Events emitted by the Camera class instance
|
|
46
|
+
* - subscribing: When we are subscribing to the Camera.
|
|
47
|
+
* - subscribed: When we are subscribed to the Camera.
|
|
48
|
+
* - unsubscribing: When we are unsubscribing from the Camera.
|
|
49
|
+
* - unsubscribed: When we are unsubscribed from the Camera.
|
|
50
|
+
* - render: When a camera frame has been rendered. A png image buffer will be provided.
|
|
51
|
+
*/
|
|
52
|
+
constructor(rustplus, identifier) {
|
|
53
|
+
|
|
54
|
+
super();
|
|
55
|
+
|
|
56
|
+
this.rustplus = rustplus;
|
|
57
|
+
this.identifier = identifier;
|
|
58
|
+
this.isSubscribed = false;
|
|
59
|
+
|
|
60
|
+
this.cameraRays = [];
|
|
61
|
+
this.cameraSubscribeInfo = null;
|
|
62
|
+
this.subscribeInterval = null;
|
|
63
|
+
|
|
64
|
+
// listen to camera message broadcasts
|
|
65
|
+
this.rustplus.on('message', async (message) => {
|
|
66
|
+
await this._onMessage(message);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// unsubscribe when rustplus is disconnected (to prevent hanging due to intervals still running)
|
|
70
|
+
this.rustplus.on('disconnected', async () => {
|
|
71
|
+
if(this.isSubscribed){
|
|
72
|
+
await this.unsubscribe();
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async _onMessage(message) {
|
|
79
|
+
|
|
80
|
+
// do nothing if not subscribed
|
|
81
|
+
if(!this.isSubscribed){
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if(message.broadcast && message.broadcast.cameraRays){
|
|
86
|
+
await this._onCameraRays(message.broadcast.cameraRays);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async _onCameraRays(cameraRays) {
|
|
92
|
+
|
|
93
|
+
// do nothing if not subscribed
|
|
94
|
+
if(!this.isSubscribed){
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// add new camera rays to cache
|
|
99
|
+
this.cameraRays.push(cameraRays);
|
|
100
|
+
|
|
101
|
+
// wait until we have enough camera rays to render an image
|
|
102
|
+
if(this.cameraRays.length > 10){
|
|
103
|
+
|
|
104
|
+
// remove first oldest rayData
|
|
105
|
+
this.cameraRays.shift();
|
|
106
|
+
|
|
107
|
+
// render to png
|
|
108
|
+
const frame = await this._renderCameraFrame(this.cameraRays, this.cameraSubscribeInfo.width, this.cameraSubscribeInfo.height);
|
|
109
|
+
|
|
110
|
+
// fire callback
|
|
111
|
+
await this._onRender(frame);
|
|
112
|
+
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async _onRender(image) {
|
|
118
|
+
|
|
119
|
+
// do nothing if not subscribed
|
|
120
|
+
if(!this.isSubscribed){
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
this.emit('render', image);
|
|
125
|
+
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Render a camera frame to a PNG image buffer
|
|
130
|
+
* @param frames the frame data to render. This will be an array of camera rays from the server.
|
|
131
|
+
* @param width the width of the frame
|
|
132
|
+
* @param height the height of the frame
|
|
133
|
+
*/
|
|
134
|
+
async _renderCameraFrame(frames, width, height) {
|
|
135
|
+
|
|
136
|
+
// First we populate the samplePositionBuffer with the positions of each sample
|
|
137
|
+
const samplePositionBuffer = new Int16Array(width * height * 2);
|
|
138
|
+
for (let w = 0, _ = 0; _ < height; _++)
|
|
139
|
+
for (let g = 0; g < width; g++) {
|
|
140
|
+
samplePositionBuffer[w] = g;
|
|
141
|
+
samplePositionBuffer[++w] = _;
|
|
142
|
+
w++;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
for (let B = new IndexGenerator(1337), R = width * height - 1; R >= 1; R--) {
|
|
146
|
+
let C = 2 * R,
|
|
147
|
+
I = 2 * B.nextInt(R + 1),
|
|
148
|
+
P = samplePositionBuffer[C],
|
|
149
|
+
k = samplePositionBuffer[C + 1],
|
|
150
|
+
A = samplePositionBuffer[I],
|
|
151
|
+
F = samplePositionBuffer[I + 1];
|
|
152
|
+
samplePositionBuffer[I] = P;
|
|
153
|
+
samplePositionBuffer[I + 1] = k;
|
|
154
|
+
samplePositionBuffer[C] = A;
|
|
155
|
+
samplePositionBuffer[C + 1] = F;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Create the output buffer
|
|
159
|
+
const output = new Array(width * height);
|
|
160
|
+
// Loop through each frame
|
|
161
|
+
for (let frame of frames) {
|
|
162
|
+
|
|
163
|
+
// Reset some look back and pointer variables
|
|
164
|
+
let sampleOffset = 2 * frame.sampleOffset;
|
|
165
|
+
let dataPointer = 0;
|
|
166
|
+
let rayLookback = new Array(64);
|
|
167
|
+
for (let r = 0; r < 64; r++) rayLookback[r] = [0, 0, 0];
|
|
168
|
+
|
|
169
|
+
const rayData = frame.rayData;
|
|
170
|
+
|
|
171
|
+
// Loop through the ray data
|
|
172
|
+
while (true) {
|
|
173
|
+
if (dataPointer >= rayData.length - 1)
|
|
174
|
+
break;
|
|
175
|
+
|
|
176
|
+
// Get the first byte and set some variables
|
|
177
|
+
let t, r, i, n = rayData[dataPointer++];
|
|
178
|
+
|
|
179
|
+
// Ray Decoding Logic
|
|
180
|
+
if (255 === n) {
|
|
181
|
+
let l = rayData[dataPointer++],
|
|
182
|
+
o = rayData[dataPointer++],
|
|
183
|
+
s = rayData[dataPointer++],
|
|
184
|
+
u = (3 * (((t = (l << 2) | (o >> 6)) / 128) | 0) + 5 * (((r = 63 & o) / 16) | 0) + 7 * (i = s)) & 63,
|
|
185
|
+
f = rayLookback[u];
|
|
186
|
+
f[0] = t;
|
|
187
|
+
f[1] = r;
|
|
188
|
+
f[2] = i;
|
|
189
|
+
} else {
|
|
190
|
+
let c = 192 & n;
|
|
191
|
+
|
|
192
|
+
if (0 === c) {
|
|
193
|
+
let h = 63 & n, y = rayLookback[h];
|
|
194
|
+
t = y[0];
|
|
195
|
+
r = y[1];
|
|
196
|
+
i = y[2];
|
|
197
|
+
} else if (64 === c) {
|
|
198
|
+
let p = 63 & n,
|
|
199
|
+
v = rayLookback[p],
|
|
200
|
+
b = v[0],
|
|
201
|
+
w = v[1],
|
|
202
|
+
_ = v[2],
|
|
203
|
+
g = rayData[dataPointer++];
|
|
204
|
+
t = b + ((g >> 3) - 15);
|
|
205
|
+
r = w + ((7 & g) - 3);
|
|
206
|
+
i = _;
|
|
207
|
+
} else if (128 === c) {
|
|
208
|
+
let R = 63 & n,
|
|
209
|
+
C = rayLookback[R],
|
|
210
|
+
I = C[0],
|
|
211
|
+
P = C[1],
|
|
212
|
+
k = C[2];
|
|
213
|
+
t = I + (rayData[dataPointer++] - 127);
|
|
214
|
+
r = P;
|
|
215
|
+
i = k;
|
|
216
|
+
} else {
|
|
217
|
+
let A = rayData[dataPointer++],
|
|
218
|
+
F = rayData[dataPointer++],
|
|
219
|
+
D = (3 * (((t = (A << 2) | (F >> 6)) / 128) | 0) + 5 * (((r = 63 & F) / 16) | 0) + 7 * (i = 63 & n)) & 63,
|
|
220
|
+
E = rayLookback[D];
|
|
221
|
+
E[0] = t;
|
|
222
|
+
E[1] = r;
|
|
223
|
+
E[2] = i;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
sampleOffset %= 2 * width * height;
|
|
228
|
+
const index = samplePositionBuffer[sampleOffset++] + samplePositionBuffer[sampleOffset++] * width;
|
|
229
|
+
output[index] = [t / 1023, r / 63, i];
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const colours = [
|
|
234
|
+
[0.5, 0.5, 0.5], [0.8, 0.7, 0.7], [0.3, 0.7, 1], [0.6, 0.6, 0.6],
|
|
235
|
+
[0.7, 0.7, 0.7], [0.8, 0.6, 0.4], [1, 0.4, 0.4], [1, 0.1, 0.1],
|
|
236
|
+
];
|
|
237
|
+
|
|
238
|
+
const image = new Jimp(width, height);
|
|
239
|
+
|
|
240
|
+
for (let i = 0; i < output.length; i++) {
|
|
241
|
+
let ray = output[i];
|
|
242
|
+
if (!ray) {
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
let distance = ray[0]
|
|
247
|
+
let alignment = ray[1]
|
|
248
|
+
let material = ray[2]
|
|
249
|
+
|
|
250
|
+
let target_colour;
|
|
251
|
+
|
|
252
|
+
if (distance === 1 && alignment === 0 && material === 0) {
|
|
253
|
+
target_colour = [208, 230, 252];
|
|
254
|
+
} else {
|
|
255
|
+
let colour = colours[material];
|
|
256
|
+
target_colour = [(alignment * colour[0] * 255), (alignment * colour[1] * 255), (alignment * colour[2] * 255)]
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
let x = i % width;
|
|
260
|
+
let y = height - 1 - Math.floor(i / width);
|
|
261
|
+
image.setPixelColor(Jimp.rgbaToInt(target_colour[0], target_colour[1], target_colour[2], 255), x, y);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// return png buffer
|
|
265
|
+
return image.getBufferAsync(Jimp.MIME_PNG);
|
|
266
|
+
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
async _subscribe() {
|
|
270
|
+
|
|
271
|
+
// subscribe to camera
|
|
272
|
+
const response = await this.rustplus.sendRequestAsync({
|
|
273
|
+
cameraSubscribe: {
|
|
274
|
+
cameraId: this.identifier,
|
|
275
|
+
},
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
// update camera subscribe info
|
|
279
|
+
this.cameraSubscribeInfo = response.cameraSubscribeInfo;
|
|
280
|
+
this.isSubscribed = true;
|
|
281
|
+
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
async subscribe() {
|
|
285
|
+
|
|
286
|
+
this.emit('subscribing');
|
|
287
|
+
|
|
288
|
+
// subscribe to camera
|
|
289
|
+
await this._subscribe();
|
|
290
|
+
|
|
291
|
+
this.emit('subscribed');
|
|
292
|
+
|
|
293
|
+
// automatically resubscribe to the camera every 10 seconds
|
|
294
|
+
this.subscribeInterval = setInterval(async () => {
|
|
295
|
+
if(this.isSubscribed){
|
|
296
|
+
await this._subscribe();
|
|
297
|
+
}
|
|
298
|
+
}, 10_000);
|
|
299
|
+
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
async unsubscribe() {
|
|
303
|
+
|
|
304
|
+
this.emit('unsubscribing');
|
|
305
|
+
|
|
306
|
+
this.isSubscribed = false;
|
|
307
|
+
|
|
308
|
+
// stop automatically resubscribing
|
|
309
|
+
clearInterval(this.subscribeInterval);
|
|
310
|
+
|
|
311
|
+
// release memory
|
|
312
|
+
this.cameraRays = [];
|
|
313
|
+
this.cameraSubscribeInfo = null;
|
|
314
|
+
this.subscribeInterval = null;
|
|
315
|
+
|
|
316
|
+
// unsubscribe from camera on server (if connected)
|
|
317
|
+
if(this.rustplus.isConnected()){
|
|
318
|
+
try {
|
|
319
|
+
await this.rustplus.sendRequestAsync({
|
|
320
|
+
cameraUnsubscribe: {
|
|
321
|
+
|
|
322
|
+
},
|
|
323
|
+
});
|
|
324
|
+
} catch (error) {
|
|
325
|
+
// ignore errors unsubscribing from camera
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
this.emit('unsubscribed');
|
|
330
|
+
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Sends camera movement to the server (mouse movement)
|
|
335
|
+
* @param buttons The buttons that are currently pressed
|
|
336
|
+
* @param x The x delta of the mouse movement
|
|
337
|
+
* @param y The y delta of the mouse movement
|
|
338
|
+
*/
|
|
339
|
+
async move(buttons, x, y) {
|
|
340
|
+
return await this.rustplus.sendRequestAsync({
|
|
341
|
+
cameraInput: {
|
|
342
|
+
buttons: buttons,
|
|
343
|
+
mouseDelta: {
|
|
344
|
+
x: x,
|
|
345
|
+
y: y,
|
|
346
|
+
}
|
|
347
|
+
},
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Zooms a PTZ camera in by 1 level.
|
|
353
|
+
* PTZ cameras have 4 zoom levels.
|
|
354
|
+
* If the PTZ camera is already at max zoom (level 4), it zooms out as far as it can (level 1).
|
|
355
|
+
*/
|
|
356
|
+
async zoom() {
|
|
357
|
+
|
|
358
|
+
// press left mouse button to zoom in
|
|
359
|
+
await this.move(Camera.Buttons.FIRE_PRIMARY, 0, 0);
|
|
360
|
+
|
|
361
|
+
// release all mouse buttons
|
|
362
|
+
await this.move(Camera.Buttons.NONE, 0, 0);
|
|
363
|
+
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Shoots a PTZ controllable Auto Turret.
|
|
368
|
+
*/
|
|
369
|
+
async shoot() {
|
|
370
|
+
|
|
371
|
+
// press left mouse button to shoot
|
|
372
|
+
await this.move(Camera.Buttons.FIRE_PRIMARY, 0, 0);
|
|
373
|
+
|
|
374
|
+
// release all mouse buttons
|
|
375
|
+
await this.move(Camera.Buttons.NONE, 0, 0);
|
|
376
|
+
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Reloads a PTZ controllable Auto Turret
|
|
381
|
+
*/
|
|
382
|
+
async reload() {
|
|
383
|
+
|
|
384
|
+
// press reload button to reload turret
|
|
385
|
+
await this.move(Camera.Buttons.RELOAD, 0, 0);
|
|
386
|
+
|
|
387
|
+
// release all mouse buttons
|
|
388
|
+
await this.move(Camera.Buttons.NONE, 0, 0);
|
|
389
|
+
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* Check if camera is an auto turret
|
|
394
|
+
* @returns {boolean}
|
|
395
|
+
*/
|
|
396
|
+
isAutoTurret() {
|
|
397
|
+
const crosshairControlFlag = Camera.ControlFlags.CROSSHAIR;
|
|
398
|
+
return (this.cameraSubscribeInfo?.controlFlags & crosshairControlFlag) === crosshairControlFlag;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
class IndexGenerator {
|
|
404
|
+
|
|
405
|
+
constructor(e) {
|
|
406
|
+
this.state = 0 | e;
|
|
407
|
+
this.nextState();
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
nextInt(e) {
|
|
411
|
+
let t = ((this.nextState() * (0 | e)) / 4294967295) | 0;
|
|
412
|
+
if (t < 0) t = e + t - 1;
|
|
413
|
+
return 0 | t;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
nextState() {
|
|
417
|
+
let e = this.state, t = e;
|
|
418
|
+
e = ((e = ((e = (e ^ ((e << 13) | 0)) | 0) ^ ((e >>> 17) | 0)) | 0) ^ ((e << 5) | 0)) | 0;
|
|
419
|
+
this.state = e;
|
|
420
|
+
return t >= 0 ? t : 4294967295 + t - 1;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
module.exports = Camera;
|