bonktools 2.3.0 → 3.1.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/src/packet.js ADDED
@@ -0,0 +1,374 @@
1
+ /**
2
+ * Packet parser for BonkBot
3
+ */
4
+ const {
5
+ SERVER_MESSAGE_TYPES,
6
+ CLIENT_MESSAGE_TYPES,
7
+ } = require("./utils/constants");
8
+ const {
9
+ createLogger
10
+ } = require("./utils/logger");
11
+ // Create logger
12
+ const logger = createLogger("PacketParser");
13
+
14
+ /**
15
+ * Parse a packet array into a normalized object
16
+ * @param {Array} packet - Packet array from socket
17
+ * @returns {Object} Normalized packet object
18
+ */
19
+ function parsePacket(packet) {
20
+ if (!Array.isArray(packet) || packet.length < 1) {
21
+ return {
22
+ type: "unknown",
23
+ raw: packet
24
+ };
25
+ }
26
+
27
+ const messageType = packet[0];
28
+
29
+ // Handle server-to-client packets (incoming)
30
+ switch (messageType) {
31
+ case SERVER_MESSAGE_TYPES.PLAYER_PINGS:
32
+ return {
33
+ type: "PLAYER_PINGS",
34
+ pings: packet[1],
35
+ pingId: packet[2],
36
+ };
37
+
38
+ case SERVER_MESSAGE_TYPES.ROOM_ADDRESS:
39
+ return {
40
+ type: "ROOM_ADDRESS",
41
+ address: packet[1],
42
+ };
43
+
44
+ case SERVER_MESSAGE_TYPES.JOIN_ROOM:
45
+ return {
46
+ type: "JOIN_ROOM",
47
+ myid: packet[1],
48
+ hostid: packet[2],
49
+ playerdata: packet[3],
50
+ timestamp: packet[4],
51
+ teamslocked: packet[5],
52
+ roomid: packet[6],
53
+ roombypass: packet[7],
54
+ };
55
+
56
+ case SERVER_MESSAGE_TYPES.PLAYER_JOIN:
57
+ const isGuest = packet[4] === true;
58
+ return {
59
+ type: "PLAYER_JOIN",
60
+ id: packet[1],
61
+ peerID: packet[2],
62
+ username: packet[3],
63
+ guest: isGuest,
64
+ level: isGuest ? "0" : packet[5],
65
+ team: packet[6] || 1, // Default to FFA if not provided
66
+ avatar: packet[packet.length - 1], // Avatar is always the last item
67
+ };
68
+
69
+ case SERVER_MESSAGE_TYPES.PLAYER_LEAVE:
70
+ return {
71
+ type: "PLAYER_LEAVE",
72
+ id: packet[1],
73
+ tick: packet[2],
74
+ };
75
+
76
+ case SERVER_MESSAGE_TYPES.HOST_LEAVE:
77
+ if (packet[2] === -1) {
78
+ return {
79
+ type: "HOST_LEAVE",
80
+ gameclose: true,
81
+ oldid: packet[1],
82
+ };
83
+ } else {
84
+ return {
85
+ type: "HOST_LEAVE",
86
+ gameclose: false,
87
+ oldid: packet[1],
88
+ newid: packet[2],
89
+ timestamp: packet[3],
90
+ };
91
+ }
92
+
93
+ case SERVER_MESSAGE_TYPES.PLAYER_INPUT:
94
+ let invalid = false
95
+ if (
96
+ !packet[2] ||
97
+ !packet[2].hasOwnProperty("f") ||
98
+ !packet[2].hasOwnProperty("c") ||
99
+ !packet[2].hasOwnProperty("i")
100
+ ) {
101
+ invalid = true
102
+ }
103
+
104
+ return {
105
+ type: "PLAYER_INPUT",
106
+ id: packet[1],
107
+ input: packet[2].i,
108
+ frame: packet[2].f,
109
+ sequence: packet[2].c,
110
+ invalid: invalid,
111
+ };
112
+
113
+ case SERVER_MESSAGE_TYPES.READY_CHANGE:
114
+ return {
115
+ type: "READY_CHANGE",
116
+ id: packet[1],
117
+ ready: packet[2],
118
+ };
119
+
120
+ case SERVER_MESSAGE_TYPES.GAME_END:
121
+ return {
122
+ type: "GAME_END"
123
+ };
124
+
125
+ case SERVER_MESSAGE_TYPES.GAME_START:
126
+ return {
127
+ type: "GAME_START",
128
+ timestamp: packet[1],
129
+ mapData: packet[2],
130
+ state: packet[3],
131
+ };
132
+
133
+ case SERVER_MESSAGE_TYPES.STATUS_MESSAGE:
134
+ return {
135
+ type: "STATUS_MESSAGE",
136
+ status: packet[1],
137
+ };
138
+
139
+ case SERVER_MESSAGE_TYPES.TEAM_CHANGE:
140
+ return {
141
+ type: "TEAM_CHANGE",
142
+ id: packet[1],
143
+ team: packet[2],
144
+ };
145
+
146
+ case SERVER_MESSAGE_TYPES.TEAMLOCK_TOGGLE:
147
+ return {
148
+ type: "TEAMLOCK_TOGGLE",
149
+ locked: packet[1],
150
+ };
151
+
152
+ case SERVER_MESSAGE_TYPES.CHAT_MESSAGE:
153
+ return {
154
+ type: "CHAT_MESSAGE",
155
+ id: packet[1],
156
+ message: packet[2],
157
+ };
158
+
159
+ case SERVER_MESSAGE_TYPES.INITIAL_DATA:
160
+ return {
161
+ type: "INITIAL_DATA",
162
+ ...packet[1],
163
+ };
164
+
165
+ case SERVER_MESSAGE_TYPES.TIMESYNC:
166
+ return {
167
+ type: "TIMESYNC",
168
+ time: packet[1].result,
169
+ id: packet[1].id,
170
+ };
171
+
172
+ case SERVER_MESSAGE_TYPES.PLAYER_KICK:
173
+ return {
174
+ type: "PLAYER_KICK",
175
+ id: packet[1],
176
+ };
177
+
178
+ case SERVER_MESSAGE_TYPES.MAP_REORDER:
179
+ return {
180
+ type: "MAP_REORDER",
181
+ start: packet[1],
182
+ end: packet[2],
183
+ };
184
+
185
+ case SERVER_MESSAGE_TYPES.GAMEMODE_CHANGE:
186
+ return {
187
+ type: "GAMEMODE_CHANGE",
188
+ engine: packet[1],
189
+ mode: packet[2],
190
+ };
191
+
192
+ case SERVER_MESSAGE_TYPES.CHANGE_ROUNDS:
193
+ return {
194
+ type: "CHANGE_ROUNDS",
195
+ rounds: packet[1],
196
+ };
197
+
198
+ case SERVER_MESSAGE_TYPES.MAP_SWITCH:
199
+ return {
200
+ type: "MAP_SWITCH",
201
+ mapdata: packet[1],
202
+ };
203
+
204
+ case SERVER_MESSAGE_TYPES.TYPING:
205
+ return {
206
+ type: "TYPING",
207
+ id: packet[1],
208
+ };
209
+
210
+ case SERVER_MESSAGE_TYPES.AFK_WARNING:
211
+ return {
212
+ type: "AFK_WARNING",
213
+ };
214
+
215
+ case SERVER_MESSAGE_TYPES.MAP_SUGGEST:
216
+ return {
217
+ type: "MAP_SUGGEST",
218
+ mapdata: packet[1],
219
+ id: packet[2],
220
+ };
221
+
222
+ case SERVER_MESSAGE_TYPES.MAP_SUGGEST:
223
+ return {
224
+ type: "MAP_SUGGEST",
225
+ maptitle: packet[1],
226
+ mapauthor: packet[2],
227
+ id: packet[3],
228
+ };
229
+
230
+ case SERVER_MESSAGE_TYPES.BALANCE_SET:
231
+ return {
232
+ type: "BALANCE_SET",
233
+ id: packet[1],
234
+ balance: packet[2],
235
+ };
236
+
237
+ case SERVER_MESSAGE_TYPES.DEBUG_WINNER:
238
+ return {
239
+ type: "DEBUG_WINNER",
240
+ id: packet[1],
241
+ data: packet[2],
242
+ };
243
+
244
+ case SERVER_MESSAGE_TYPES.SAVE_REPLAY:
245
+ return {
246
+ type: "SAVE_REPLAY",
247
+ id: packet[1],
248
+ };
249
+
250
+ case SERVER_MESSAGE_TYPES.HOST_TRANSFER:
251
+ return {
252
+ type: "HOST_TRANSFER",
253
+ oldHost: packet[1].oldHost || packet[1],
254
+ newHost: packet[1].newHost || packet[2],
255
+ };
256
+
257
+ case SERVER_MESSAGE_TYPES.FRIEND_REQUEST:
258
+ return {
259
+ type: "FRIEND_REQUEST",
260
+ id: packet[1],
261
+ };
262
+
263
+ case SERVER_MESSAGE_TYPES.COUNTDOWN:
264
+ return {
265
+ type: "COUNTDOWN",
266
+ countdown: packet[1],
267
+ };
268
+
269
+ case SERVER_MESSAGE_TYPES.ABORT_COUNTDOWN:
270
+ return {
271
+ type: "ABORT_COUNTDOWN",
272
+ };
273
+
274
+ case SERVER_MESSAGE_TYPES.PLAYER_LEVEL_UP:
275
+ return {
276
+ type: "PLAYER_LEVEL_UP",
277
+ sid: packet[1].sid,
278
+ level: packet[1].lv,
279
+ };
280
+
281
+ case SERVER_MESSAGE_TYPES.LOCAL_GAINED_XP:
282
+ const xpData = packet[1];
283
+ if (xpData.newLevel) {
284
+ return {
285
+ type: "LOCAL_GAINED_XP",
286
+ xp: xpData.newXP,
287
+ level: xpData.newLevel,
288
+ token: xpData.newToken,
289
+ };
290
+ } else {
291
+ return {
292
+ type: "LOCAL_GAINED_XP",
293
+ xp: xpData.newXP,
294
+ };
295
+ }
296
+
297
+ case SERVER_MESSAGE_TYPES.STATE:
298
+ return parseStatePacket(packet[1]);
299
+
300
+ case SERVER_MESSAGE_TYPES.ROOM_SHARE_LINK:
301
+ return {
302
+ type: "ROOM_SHARE_LINK",
303
+ roomId: packet[1],
304
+ roomBypass: packet[2],
305
+ };
306
+
307
+ case SERVER_MESSAGE_TYPES.PLAYER_TABBED:
308
+ return {
309
+ type: "PLAYER_TABBED",
310
+ id: packet[1],
311
+ tabbed: packet[2],
312
+ };
313
+
314
+ case SERVER_MESSAGE_TYPES.CURATE_RESULT:
315
+ return {
316
+ type: "CURATE_RESULT",
317
+ success: packet[1],
318
+ message: packet[2],
319
+ };
320
+
321
+ case SERVER_MESSAGE_TYPES.ROOM_NAME_UPDATE:
322
+ return {
323
+ type: "ROOM_NAME_UPDATE",
324
+ name: packet[1],
325
+ };
326
+
327
+ case SERVER_MESSAGE_TYPES.ROOM_PASSWORD_UPDATE:
328
+ return {
329
+ type: "ROOM_PASSWORD_UPDATE",
330
+ passwordSet: packet[1] === 1,
331
+ };
332
+
333
+ default:
334
+ logger.debug("Unknown message type", {
335
+ type: messageType,
336
+ packet
337
+ });
338
+ return {
339
+ type: "unknown",
340
+ messageType,
341
+ data: packet.slice(1),
342
+ };
343
+ }
344
+ }
345
+
346
+ /**
347
+ * Parse a state packet into a normalized object
348
+ * @param {Object} data - State packet data
349
+ * @returns {Object} Normalized state packet
350
+ */
351
+ function parseStatePacket(data) {
352
+ return {
353
+ type: "STATE",
354
+ gt: data.gt,
355
+ rounds: data.wl,
356
+ quickplay: data.q,
357
+ teamsLocked: data.tl,
358
+ teams: data.tea,
359
+ engine: data.ga,
360
+ mode: data.mo,
361
+ balance: data.bal,
362
+ inputs: data.inputs,
363
+ framecount: data.fc,
364
+ stateID: data.stateID,
365
+ admin: data.admin,
366
+ map: data.gs ? data.gs.map : null,
367
+ state: data.state,
368
+ random: data.random,
369
+ };
370
+ }
371
+
372
+ module.exports = {
373
+ parsePacket
374
+ };
@@ -0,0 +1,153 @@
1
+ /**
2
+ * Constants used throughout the BonkBot library
3
+ */
4
+ // Default skin for bots
5
+ const DEFAULT_AVATAR = {
6
+ layers: [],
7
+ bc: 16774557,
8
+ };
9
+ // Default server
10
+ const DEFAULT_SERVER = "b2ny1";
11
+ // Socket message types - Server to Client (Incoming)
12
+ const SERVER_MESSAGE_TYPES = {
13
+ PLAYER_PINGS: 1,
14
+ ROOM_ADDRESS: 2,
15
+ JOIN_ROOM: 3,
16
+ PLAYER_JOIN: 4,
17
+ PLAYER_LEAVE: 5,
18
+ HOST_LEAVE: 6,
19
+ PLAYER_INPUT: 7,
20
+ READY_CHANGE: 8,
21
+ GAME_END: 13,
22
+ GAME_START: 15,
23
+ STATUS_MESSAGE: 16,
24
+ TEAM_CHANGE: 18,
25
+ TEAMLOCK_TOGGLE: 19,
26
+ CHAT_MESSAGE: 20,
27
+ INITIAL_DATA: 21,
28
+ TIMESYNC: 23,
29
+ PLAYER_KICK: 24,
30
+ MAP_REORDER: 25,
31
+ GAMEMODE_CHANGE: 26,
32
+ CHANGE_ROUNDS: 27,
33
+ MAP_SWITCH: 29,
34
+ TYPING: 30,
35
+ AFK_WARNING: 32,
36
+ MAP_SUGGEST: 33,
37
+ MAP_SUGGEST: 34,
38
+ BALANCE_SET: 36,
39
+ DEBUG_WINNER: 38,
40
+ SAVE_REPLAY: 40,
41
+ HOST_TRANSFER: 41,
42
+ FRIEND_REQUEST: 42,
43
+ COUNTDOWN: 43,
44
+ ABORT_COUNTDOWN: 44,
45
+ PLAYER_LEVEL_UP: 45,
46
+ LOCAL_GAINED_XP: 46,
47
+ STATE: 48,
48
+ ROOM_SHARE_LINK: 49,
49
+ PLAYER_TABBED: 52,
50
+ CURATE_RESULT: 57,
51
+ ROOM_NAME_UPDATE: 58,
52
+ ROOM_PASSWORD_UPDATE: 59,
53
+ };
54
+ // Socket message types - Client to Server (Outgoing)
55
+ const CLIENT_MESSAGE_TYPES = {
56
+ PING_RESPONSE: 1,
57
+ TEST_PING: 2,
58
+ GET_DEBUG: 3,
59
+ SEND_INPUTS: 4,
60
+ TRIGGER_START: 5,
61
+ CHANGE_OWN_TEAM: 6,
62
+ TEAM_LOCK: 7,
63
+ SILENCE_PLAYER: 8,
64
+ KICK_BAN_PLAYER: 9,
65
+ CHAT_MESSAGE: 10,
66
+ INFORM_IN_LOBBY: 11,
67
+ CREATE_ROOM: 12,
68
+ JOIN_ROOM: 13,
69
+ RETURN_TO_LOBBY: 14,
70
+ SET_READY: 16,
71
+ ALL_READY_RESET: 17,
72
+ TIMESYNC: 18,
73
+ SEND_MAP_REORDER: 19,
74
+ SEND_MODE: 20,
75
+ SEND_ROUNDS: 21,
76
+ SEND_MAP_DELETE: 22,
77
+ SEND_MAP_ADD: 23,
78
+ SEND_TYPING: 24,
79
+ CHANGE_OTHER_TEAM: 26,
80
+ SEND_MAP_SUGGEST: 27,
81
+ SEND_BALANCE: 29,
82
+ VERSION_CHECK: 30,
83
+ SEND_DEBUG_WINNER: 31,
84
+ SEND_TEAM_SETTINGS: 32,
85
+ SEND_ARM_RECORD: 33,
86
+ SEND_HOST_CHANGE: 34,
87
+ SEND_FRIENDED: 35,
88
+ SEND_START_COUNTDOWN: 36,
89
+ SEND_ABORT_COUNTDOWN: 37,
90
+ SEND_REQUEST_XP: 38,
91
+ SEND_MAP_VOTE: 39,
92
+ INFORM_IN_GAME: 40,
93
+ GET_PRE_VOTE: 41,
94
+ TABBED: 44,
95
+ DESYNC_TEST: 45,
96
+ SEND_DESYNC_RESPONSE: 46,
97
+ SEND_NO_HOST_SWAP: 50,
98
+ SEND_CURATE: 51,
99
+ };
100
+ // For backward compatibility, keep the old MESSAGE_TYPES object
101
+ // but use the server message types as the default
102
+ const MESSAGE_TYPES = {
103
+ ...SERVER_MESSAGE_TYPES,
104
+ ...CLIENT_MESSAGE_TYPES,
105
+ };
106
+ // Team mappings
107
+ const TEAM_NAMES = {
108
+ 0: "spectator",
109
+ 1: "ffa",
110
+ 2: "red",
111
+ 3: "blue",
112
+ 4: "green",
113
+ 5: "yellow",
114
+ };
115
+ // Gamemode names
116
+ const GAMEMODE_NAMES = {
117
+ "b": "classic",
118
+ "ar": "arrows",
119
+ "ard": "death arrrows",
120
+ "sp": "grapple",
121
+ "v": "vtol",
122
+ "f": "football"
123
+ }
124
+ // Engine names
125
+ const ENGINE_NAMES = {
126
+ "b": "bonk",
127
+ "f": "football"
128
+ }
129
+ // API endpoints
130
+ const API = {
131
+ LOGIN: "https://bonk2.io/scripts/login_legacy.php",
132
+ GET_ROOMS: "https://bonk2.io/scripts/getrooms.php",
133
+ GET_ROOM_ADDRESS: "https://bonk2.io/scripts/getroomaddress.php",
134
+ AUTOJOIN: "https://bonk2.io/scripts/autojoin.php"
135
+ };
136
+ // Socket connection settings
137
+ const SOCKET = {
138
+ KEEP_ALIVE_INTERVAL: 5000,
139
+ CONNECTION_TIMEOUT: 10000,
140
+ };
141
+ // Export all constants
142
+ module.exports = {
143
+ DEFAULT_AVATAR,
144
+ DEFAULT_SERVER,
145
+ MESSAGE_TYPES,
146
+ GAMEMODE_NAMES,
147
+ ENGINE_NAMES,
148
+ SERVER_MESSAGE_TYPES,
149
+ CLIENT_MESSAGE_TYPES,
150
+ TEAM_NAMES,
151
+ API,
152
+ SOCKET,
153
+ };
@@ -0,0 +1,137 @@
1
+ /**
2
+ * Custom error classes for BonkBot
3
+ */
4
+
5
+ /**
6
+ * Base error class for BonkBot errors
7
+ */
8
+ class BonkBotError extends Error {
9
+ /**
10
+ * Create a new BonkBotError
11
+ * @param {string} message - Error message
12
+ * @param {Object} [options] - Additional error options
13
+ */
14
+ constructor(message, options = {}) {
15
+ super(message);
16
+ this.name = this.constructor.name;
17
+ this.code = options.code || "UNKNOWN_ERROR";
18
+ this.details = options.details || {};
19
+
20
+ // Capture stack trace
21
+ if (Error.captureStackTrace) {
22
+ Error.captureStackTrace(this, this.constructor);
23
+ }
24
+ }
25
+ }
26
+
27
+ /**
28
+ * Error thrown when there's a connection issue
29
+ */
30
+ class ConnectionError extends BonkBotError {
31
+ /**
32
+ * Create a new ConnectionError
33
+ * @param {string} message - Error message
34
+ * @param {Object} [options] - Additional error options
35
+ */
36
+ constructor(message, options = {}) {
37
+ super(message, {
38
+ code: options.code || "CONNECTION_ERROR",
39
+ details: options.details,
40
+ });
41
+ }
42
+ }
43
+
44
+ /**
45
+ * Error thrown when there's an authentication issue
46
+ */
47
+ class AuthenticationError extends BonkBotError {
48
+ /**
49
+ * Create a new AuthenticationError
50
+ * @param {string} message - Error message
51
+ * @param {Object} [options] - Additional error options
52
+ */
53
+ constructor(message, options = {}) {
54
+ super(message, {
55
+ code: options.code || "AUTHENTICATION_ERROR",
56
+ details: options.details,
57
+ });
58
+ }
59
+ }
60
+
61
+ /**
62
+ * Error thrown when there's an issue with room operations
63
+ */
64
+ class RoomError extends BonkBotError {
65
+ /**
66
+ * Create a new RoomError
67
+ * @param {string} message - Error message
68
+ * @param {Object} [options] - Additional error options
69
+ */
70
+ constructor(message, options = {}) {
71
+ super(message, {
72
+ code: options.code || "ROOM_ERROR",
73
+ details: options.details,
74
+ });
75
+ }
76
+ }
77
+
78
+ /**
79
+ * Error thrown when there's an issue with the game state
80
+ */
81
+ class GameStateError extends BonkBotError {
82
+ /**
83
+ * Create a new GameStateError
84
+ * @param {string} message - Error message
85
+ * @param {Object} [options] - Additional error options
86
+ */
87
+ constructor(message, options = {}) {
88
+ super(message, {
89
+ code: options.code || "GAME_STATE_ERROR",
90
+ details: options.details,
91
+ });
92
+ }
93
+ }
94
+
95
+ /**
96
+ * Error thrown when there's a validation issue
97
+ */
98
+ class ValidationError extends BonkBotError {
99
+ /**
100
+ * Create a new ValidationError
101
+ * @param {string} message - Error message
102
+ * @param {Object} [options] - Additional error options
103
+ */
104
+ constructor(message, options = {}) {
105
+ super(message, {
106
+ code: options.code || "VALIDATION_ERROR",
107
+ details: options.details,
108
+ });
109
+ }
110
+ }
111
+
112
+ /**
113
+ * Error thrown when a rate limit is hit
114
+ */
115
+ class RateLimitError extends BonkBotError {
116
+ /**
117
+ * Create a new RateLimitError
118
+ * @param {string} message - Error message
119
+ * @param {Object} [options] - Additional error options
120
+ */
121
+ constructor(message, options = {}) {
122
+ super(message, {
123
+ code: options.code || "RATE_LIMIT_ERROR",
124
+ details: options.details,
125
+ });
126
+ }
127
+ }
128
+
129
+ module.exports = {
130
+ BonkBotError,
131
+ ConnectionError,
132
+ AuthenticationError,
133
+ RoomError,
134
+ GameStateError,
135
+ ValidationError,
136
+ RateLimitError,
137
+ };