@rustwirebot/rustplus.js 2.5.2 → 2.6.1

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/README.md CHANGED
@@ -1,4 +1,4 @@
1
- > **Note:** This is a maintained fork of [liamcottle/rustplus.js](https://github.com/liamcottle/rustplus.js), republished under the `@rustwire` npm scope by BotForge Studios so it can be installed/run as `npx @rustwirebot/rustplus.js <command>`. All credit for the original library and protobuf reverse-engineering goes to Liam Cottle. This fork is distributed under the same MIT license — see [LICENSE](./LICENSE). The `@liamcottle/push-receiver` dependency used by the `fcm-register`/`fcm-listen` CLI commands has also been vendored locally under [`vendor/push-receiver`](./vendor/push-receiver) (with its own attribution/NOTICE) so this package keeps working even if that upstream npm package is ever removed.
1
+ > **Note:** This is a maintained fork of [liamcottle/rustplus.js](https://github.com/liamcottle/rustplus.js), republished under the `@rustwire` npm scope by BotForge Studios so it can be installed/run as `npx @rustwirebot/rustplus.js <command>`. All credit for the original library and protobuf reverse-engineering goes to Liam Cottle. This fork is distributed under the same MIT license — see [LICENSE](./LICENSE). The `@liamcottle/push-receiver` dependency used by the `fcm-register`/`fcm-listen` CLI commands has also been vendored locally under [`vendor/push-receiver`](./vendor/push-receiver) (with its own attribution/NOTICE) so this package keeps working even if that upstream npm package is ever removed. As of v2.6.0, every `required` field in `rustplus.proto` has been relaxed to `optional` — real-world Rust servers frequently omit fields (queue counts, blueprint flags, online status, etc.) that the original schema marked as mandatory, which crashed decoding entirely instead of just defaulting to a sensible zero-value. Verified by round-tripping all 56 message types with fully empty payloads.
2
2
 
3
3
  <p align="center">
4
4
  <img src="./rustplus.js.svg" width="400"></a>
package/cli/index.js CHANGED
@@ -205,6 +205,20 @@ async function fcmListen(options) {
205
205
  const androidId = config.fcm_credentials.gcm.androidId;
206
206
  const securityToken = config.fcm_credentials.gcm.securityToken;
207
207
  const client = new PushReceiverClient(androidId, securityToken, []);
208
+
209
+ // FIX: previously only ON_DATA_RECEIVED was logged — there was no way to
210
+ // tell whether the connection ever actually succeeded, failed, or was
211
+ // silently retrying. Now all of this is visible.
212
+ client.on('connect', () => {
213
+ console.log('\x1b[32m%s\x1b[0m', `[${new Date().toLocaleString()}] Connected to FCM — waiting for notifications...`);
214
+ });
215
+ client.on('disconnect', () => {
216
+ console.log('\x1b[33m%s\x1b[0m', `[${new Date().toLocaleString()}] Disconnected — retrying...`);
217
+ });
218
+ client.on('error', (err) => {
219
+ console.log('\x1b[31m%s\x1b[0m', `[${new Date().toLocaleString()}] Connection error: ${err?.message || err}`);
220
+ });
221
+
208
222
  client.on('ON_DATA_RECEIVED', (data) => {
209
223
 
210
224
  // generate timestamp
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rustwirebot/rustplus.js",
3
- "version": "2.5.2",
3
+ "version": "2.6.1",
4
4
  "description": "NodeJS library for controlling Smart Switches and interacting with the Rust+ Companion API. Maintained fork of @liamcottle/rustplus.js, republished by BotForge Studios / RustWire.",
5
5
  "main": "rustplus.js",
6
6
  "bin": {
package/rustplus.proto CHANGED
@@ -38,17 +38,17 @@ message Ray {
38
38
  }
39
39
 
40
40
  message ClanActionResult {
41
- required int32 requestId = 1;
42
- required int32 result = 2;
43
- required bool hasClanInfo = 3;
41
+ optional int32 requestId = 1;
42
+ optional int32 result = 2;
43
+ optional bool hasClanInfo = 3;
44
44
  optional ClanInfo clanInfo = 4;
45
45
  }
46
46
 
47
47
  message ClanInfo {
48
- required int64 clanId = 1;
49
- required string name = 2;
50
- required int64 created = 3;
51
- required uint64 creator = 4;
48
+ optional int64 clanId = 1;
49
+ optional string name = 2;
50
+ optional int64 created = 3;
51
+ optional uint64 creator = 4;
52
52
  optional string motd = 5;
53
53
  optional int64 motdTimestamp = 6;
54
54
  optional uint64 motdAuthor = 7;
@@ -60,42 +60,42 @@ message ClanInfo {
60
60
  optional int32 maxMemberCount = 13;
61
61
 
62
62
  message Role {
63
- required int32 roleId = 1;
64
- required int32 rank = 2;
65
- required string name = 3;
66
- required bool canSetMotd = 4;
67
- required bool canSetLogo = 5;
68
- required bool canInvite = 6;
69
- required bool canKick = 7;
70
- required bool canPromote = 8;
71
- required bool canDemote = 9;
72
- required bool canSetPlayerNotes = 10;
73
- required bool canAccessLogs = 11;
63
+ optional int32 roleId = 1;
64
+ optional int32 rank = 2;
65
+ optional string name = 3;
66
+ optional bool canSetMotd = 4;
67
+ optional bool canSetLogo = 5;
68
+ optional bool canInvite = 6;
69
+ optional bool canKick = 7;
70
+ optional bool canPromote = 8;
71
+ optional bool canDemote = 9;
72
+ optional bool canSetPlayerNotes = 10;
73
+ optional bool canAccessLogs = 11;
74
74
  }
75
75
 
76
76
  message Member {
77
- required uint64 steamId = 1;
78
- required int32 roleId = 2;
79
- required int64 joined = 3;
80
- required int64 lastSeen = 4;
77
+ optional uint64 steamId = 1;
78
+ optional int32 roleId = 2;
79
+ optional int64 joined = 3;
80
+ optional int64 lastSeen = 4;
81
81
  optional string notes = 5;
82
82
  optional bool online = 6;
83
83
  }
84
84
 
85
85
  message Invite {
86
- required uint64 steamId = 1;
87
- required uint64 recruiter = 2;
88
- required int64 timestamp = 3;
86
+ optional uint64 steamId = 1;
87
+ optional uint64 recruiter = 2;
88
+ optional int64 timestamp = 3;
89
89
  }
90
90
  }
91
91
 
92
92
  message ClanLog {
93
- required int64 clanId = 1;
93
+ optional int64 clanId = 1;
94
94
  repeated ClanLog.Entry logEntries = 2;
95
95
 
96
96
  message Entry {
97
- required int64 timestamp = 1;
98
- required string eventKey = 2;
97
+ optional int64 timestamp = 1;
98
+ optional string eventKey = 2;
99
99
  optional string arg1 = 3;
100
100
  optional string arg2 = 4;
101
101
  optional string arg3 = 5;
@@ -107,9 +107,9 @@ message ClanInvitations {
107
107
  repeated ClanInvitations.Invitation invitations = 1;
108
108
 
109
109
  message Invitation {
110
- required int64 clanId = 1;
111
- required uint64 recruiter = 2;
112
- required int64 timestamp = 3;
110
+ optional int64 clanId = 1;
111
+ optional uint64 recruiter = 2;
112
+ optional int64 timestamp = 3;
113
113
  }
114
114
  }
115
115
 
@@ -132,9 +132,9 @@ enum AppMarkerType {
132
132
  }
133
133
 
134
134
  message AppRequest {
135
- required uint32 seq = 1;
136
- required uint64 playerId = 2;
137
- required int32 playerToken = 3;
135
+ optional uint32 seq = 1;
136
+ optional uint64 playerId = 2;
137
+ optional int32 playerToken = 3;
138
138
  optional uint32 entityId = 4;
139
139
  optional AppEmpty getInfo = 8;
140
140
  optional AppEmpty getTime = 9;
@@ -164,7 +164,7 @@ message AppMessage {
164
164
  }
165
165
 
166
166
  message AppResponse {
167
- required uint32 seq = 1;
167
+ optional uint32 seq = 1;
168
168
  optional AppSuccess success = 4;
169
169
  optional AppError error = 5;
170
170
  optional AppInfo info = 6;
@@ -194,41 +194,41 @@ message AppEmpty {
194
194
  }
195
195
 
196
196
  message AppSendMessage {
197
- required string message = 1;
197
+ optional string message = 1;
198
198
  }
199
199
 
200
200
  message AppSetEntityValue {
201
- required bool value = 1;
201
+ optional bool value = 1;
202
202
  }
203
203
 
204
204
  message AppPromoteToLeader {
205
- required uint64 steamId = 1;
205
+ optional uint64 steamId = 1;
206
206
  }
207
207
 
208
208
  message AppGetNexusAuth {
209
- required string appKey = 1;
209
+ optional string appKey = 1;
210
210
  }
211
211
 
212
212
  message AppSuccess {
213
213
  }
214
214
 
215
215
  message AppError {
216
- required string error = 1;
216
+ optional string error = 1;
217
217
  }
218
218
 
219
219
  message AppFlag {
220
- required bool value = 1;
220
+ optional bool value = 1;
221
221
  }
222
222
 
223
223
  message AppInfo {
224
- required string name = 1;
225
- required string headerImage = 2;
226
- required string url = 3;
227
- required string map = 4;
228
- required uint32 mapSize = 5;
229
- required uint32 wipeTime = 6;
230
- required uint32 players = 7;
231
- required uint32 maxPlayers = 8;
224
+ optional string name = 1;
225
+ optional string headerImage = 2;
226
+ optional string url = 3;
227
+ optional string map = 4;
228
+ optional uint32 mapSize = 5;
229
+ optional uint32 wipeTime = 6;
230
+ optional uint32 players = 7;
231
+ optional uint32 maxPlayers = 8;
232
232
  optional uint32 queuedPlayers = 9; // FIX: some real Rust servers omit this field entirely; was `required`, which crashed decoding with a ProtocolError instead of just defaulting to 0.
233
233
  optional uint32 seed = 10;
234
234
  optional uint32 salt = 11;
@@ -239,31 +239,31 @@ message AppInfo {
239
239
  }
240
240
 
241
241
  message AppTime {
242
- required float dayLengthMinutes = 1;
243
- required float timeScale = 2;
244
- required float sunrise = 3;
245
- required float sunset = 4;
246
- required float time = 5;
242
+ optional float dayLengthMinutes = 1;
243
+ optional float timeScale = 2;
244
+ optional float sunrise = 3;
245
+ optional float sunset = 4;
246
+ optional float time = 5;
247
247
  }
248
248
 
249
249
  message AppMap {
250
- required uint32 width = 1;
251
- required uint32 height = 2;
252
- required bytes jpgImage = 3;
253
- required int32 oceanMargin = 4;
250
+ optional uint32 width = 1;
251
+ optional uint32 height = 2;
252
+ optional bytes jpgImage = 3;
253
+ optional int32 oceanMargin = 4;
254
254
  repeated AppMap.Monument monuments = 5;
255
255
  optional string background = 6;
256
256
 
257
257
  message Monument {
258
- required string token = 1;
259
- required float x = 2;
260
- required float y = 3;
258
+ optional string token = 1;
259
+ optional float x = 2;
260
+ optional float y = 3;
261
261
  }
262
262
  }
263
263
 
264
264
  message AppEntityInfo {
265
- required AppEntityType type = 1;
266
- required AppEntityPayload payload = 3;
265
+ optional AppEntityType type = 1;
266
+ optional AppEntityPayload payload = 3;
267
267
  }
268
268
 
269
269
  message AppEntityPayload {
@@ -274,42 +274,42 @@ message AppEntityPayload {
274
274
  optional uint32 protectionExpiry = 5;
275
275
 
276
276
  message Item {
277
- required int32 itemId = 1;
278
- required int32 quantity = 2;
277
+ optional int32 itemId = 1;
278
+ optional int32 quantity = 2;
279
279
  optional bool itemIsBlueprint = 3; // FIX: some servers omit this for non-blueprint items; was `required`
280
280
  }
281
281
  }
282
282
 
283
283
  message AppTeamInfo {
284
- required uint64 leaderSteamId = 1;
284
+ optional uint64 leaderSteamId = 1;
285
285
  repeated AppTeamInfo.Member members = 2;
286
286
  repeated AppTeamInfo.Note mapNotes = 3;
287
287
  repeated AppTeamInfo.Note leaderMapNotes = 4;
288
288
 
289
289
  message Member {
290
- required uint64 steamId = 1;
291
- required string name = 2;
292
- required float x = 3;
293
- required float y = 4;
294
- required bool isOnline = 5;
295
- required uint32 spawnTime = 6;
296
- required bool isAlive = 7;
297
- required uint32 deathTime = 8;
290
+ optional uint64 steamId = 1;
291
+ optional string name = 2;
292
+ optional float x = 3;
293
+ optional float y = 4;
294
+ optional bool isOnline = 5;
295
+ optional uint32 spawnTime = 6;
296
+ optional bool isAlive = 7;
297
+ optional uint32 deathTime = 8;
298
298
  }
299
299
 
300
300
  message Note {
301
- required int32 type = 2;
302
- required float x = 3;
303
- required float y = 4;
301
+ optional int32 type = 2;
302
+ optional float x = 3;
303
+ optional float y = 4;
304
304
  }
305
305
  }
306
306
 
307
307
  message AppTeamMessage {
308
- required uint64 steamId = 1;
309
- required string name = 2;
310
- required string message = 3;
311
- required string color = 4;
312
- required uint32 time = 5;
308
+ optional uint64 steamId = 1;
309
+ optional string name = 2;
310
+ optional string message = 3;
311
+ optional string color = 4;
312
+ optional uint32 time = 5;
313
313
  }
314
314
 
315
315
  message AppTeamChat {
@@ -317,10 +317,10 @@ message AppTeamChat {
317
317
  }
318
318
 
319
319
  message AppMarker {
320
- required uint32 id = 1;
321
- required AppMarkerType type = 2;
322
- required float x = 3;
323
- required float y = 4;
320
+ optional uint32 id = 1;
321
+ optional AppMarkerType type = 2;
322
+ optional float x = 3;
323
+ optional float y = 4;
324
324
  optional uint64 steamId = 5;
325
325
  optional float rotation = 6;
326
326
  optional float radius = 7;
@@ -332,11 +332,11 @@ message AppMarker {
332
332
  repeated AppMarker.SellOrder sellOrders = 13;
333
333
 
334
334
  message SellOrder {
335
- required int32 itemId = 1;
336
- required int32 quantity = 2;
337
- required int32 currencyId = 3;
338
- required int32 costPerItem = 4;
339
- required int32 amountInStock = 5;
335
+ optional int32 itemId = 1;
336
+ optional int32 quantity = 2;
337
+ optional int32 currencyId = 3;
338
+ optional int32 costPerItem = 4;
339
+ optional int32 amountInStock = 5;
340
340
  optional bool itemIsBlueprint = 6; // FIX: some servers omit this for non-blueprint items; was `required`
341
341
  optional bool currencyIsBlueprint = 7; // FIX: same as above; was `required`
342
342
  optional float itemCondition = 8;
@@ -353,10 +353,10 @@ message AppClanInfo {
353
353
  }
354
354
 
355
355
  message AppClanMessage {
356
- required uint64 steamId = 1;
357
- required string name = 2;
358
- required string message = 3;
359
- required int64 time = 4;
356
+ optional uint64 steamId = 1;
357
+ optional string name = 2;
358
+ optional string message = 3;
359
+ optional int64 time = 4;
360
360
  }
361
361
 
362
362
  message AppClanChat {
@@ -364,22 +364,22 @@ message AppClanChat {
364
364
  }
365
365
 
366
366
  message AppNexusAuth {
367
- required string serverId = 1;
368
- required int32 playerToken = 2;
367
+ optional string serverId = 1;
368
+ optional int32 playerToken = 2;
369
369
  }
370
370
 
371
371
  message AppTeamChanged {
372
- required uint64 playerId = 1;
373
- required AppTeamInfo teamInfo = 2;
372
+ optional uint64 playerId = 1;
373
+ optional AppTeamInfo teamInfo = 2;
374
374
  }
375
375
 
376
376
  message AppNewTeamMessage {
377
- required AppTeamMessage message = 1;
377
+ optional AppTeamMessage message = 1;
378
378
  }
379
379
 
380
380
  message AppEntityChanged {
381
- required uint32 entityId = 1;
382
- required AppEntityPayload payload = 2;
381
+ optional uint32 entityId = 1;
382
+ optional AppEntityPayload payload = 2;
383
383
  }
384
384
 
385
385
  message AppClanChanged {
@@ -387,32 +387,32 @@ message AppClanChanged {
387
387
  }
388
388
 
389
389
  message AppNewClanMessage {
390
- required int64 clanId = 1;
391
- required AppClanMessage message = 2;
390
+ optional int64 clanId = 1;
391
+ optional AppClanMessage message = 2;
392
392
  }
393
393
 
394
394
  message AppCameraSubscribe {
395
- required string cameraId = 1;
395
+ optional string cameraId = 1;
396
396
  }
397
397
 
398
398
  message AppCameraInput {
399
- required int32 buttons = 1;
400
- required Vector2 mouseDelta = 2;
399
+ optional int32 buttons = 1;
400
+ optional Vector2 mouseDelta = 2;
401
401
  }
402
402
 
403
403
  message AppCameraInfo {
404
- required int32 width = 1;
405
- required int32 height = 2;
406
- required float nearPlane = 3;
407
- required float farPlane = 4;
408
- required int32 controlFlags = 5;
404
+ optional int32 width = 1;
405
+ optional int32 height = 2;
406
+ optional float nearPlane = 3;
407
+ optional float farPlane = 4;
408
+ optional int32 controlFlags = 5;
409
409
  }
410
410
 
411
411
  message AppCameraRays {
412
- required float verticalFov = 1;
413
- required int32 sampleOffset = 2;
414
- required bytes rayData = 3;
415
- required float distance = 4;
412
+ optional float verticalFov = 1;
413
+ optional int32 sampleOffset = 2;
414
+ optional bytes rayData = 3;
415
+ optional float distance = 4;
416
416
  repeated AppCameraRays.Entity entities = 5;
417
417
 
418
418
  enum EntityType {
@@ -421,11 +421,11 @@ message AppCameraRays {
421
421
  }
422
422
 
423
423
  message Entity {
424
- required uint32 entityId = 1;
425
- required EntityType type = 2;
426
- required Vector3 position = 3;
427
- required Vector3 rotation = 4;
428
- required Vector3 size = 5;
424
+ optional uint32 entityId = 1;
425
+ optional EntityType type = 2;
426
+ optional Vector3 position = 3;
427
+ optional Vector3 rotation = 4;
428
+ optional Vector3 size = 5;
429
429
  optional string name = 6;
430
430
  }
431
431
  }
@@ -142,10 +142,15 @@ module.exports = class Client extends EventEmitter {
142
142
  }
143
143
 
144
144
  _onSocketError(error) {
145
- // ignore, the close handler takes care of retry
145
+ // FIX: was completely silent ("ignore, the close handler takes care of
146
+ // retry") — this made it impossible to tell whether the connection was
147
+ // failing, retrying, or actually working. Surface it as a proper event
148
+ // so callers (like the CLI) can log what's actually happening.
149
+ this.emit('error', error);
146
150
  }
147
151
 
148
152
  _onParserError(error) {
153
+ this.emit('error', error);
149
154
  this._retry();
150
155
  }
151
156