yolkbot 1.5.7 → 1.5.9

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.
Files changed (85) hide show
  1. package/browser/build/global.js +1 -1
  2. package/browser/build/module.js +1 -1
  3. package/dist/api.js +1 -244
  4. package/dist/bot/GamePlayer.js +1 -84
  5. package/dist/bot.js +1 -1226
  6. package/dist/comm/CommIn.js +1 -76
  7. package/dist/comm/CommOut.js +1 -60
  8. package/dist/constants/CloseCode.js +1 -27
  9. package/dist/constants/CommCode.js +1 -53
  10. package/dist/constants/challenges.js +1 -1821
  11. package/dist/constants/findItemById.js +1 -3
  12. package/dist/constants/guns.js +1 -155
  13. package/dist/constants/index.js +1 -141
  14. package/dist/constants/items.js +1 -45979
  15. package/dist/constants/maps.js +1 -1289
  16. package/dist/constants/regions.js +1 -30
  17. package/dist/dispatches/BanPlayerDispatch.js +1 -32
  18. package/dist/dispatches/BootPlayerDispatch.js +1 -21
  19. package/dist/dispatches/ChatDispatch.js +1 -32
  20. package/dist/dispatches/FireDispatch.js +1 -15
  21. package/dist/dispatches/GameOptionsDispatch.js +1 -66
  22. package/dist/dispatches/GoToAmmoDispatch.js +1 -37
  23. package/dist/dispatches/GoToCoopDispatch.js +1 -37
  24. package/dist/dispatches/GoToGrenadeDispatch.js +1 -37
  25. package/dist/dispatches/GoToPlayerDispatch.js +1 -34
  26. package/dist/dispatches/GoToSpatulaDispatch.js +1 -23
  27. package/dist/dispatches/LookAtDispatch.js +1 -37
  28. package/dist/dispatches/LookAtPosDispatch.js +1 -29
  29. package/dist/dispatches/MeleeDispatch.js +1 -26
  30. package/dist/dispatches/MovementDispatch.js +1 -26
  31. package/dist/dispatches/PauseDispatch.js +1 -18
  32. package/dist/dispatches/ReloadDispatch.js +1 -35
  33. package/dist/dispatches/ReportPlayerDispatch.js +1 -44
  34. package/dist/dispatches/ResetGameDispatch.js +1 -19
  35. package/dist/dispatches/SaveLoadoutDispatch.js +1 -106
  36. package/dist/dispatches/SpawnDispatch.js +1 -26
  37. package/dist/dispatches/SwapWeaponDispatch.js +1 -25
  38. package/dist/dispatches/SwitchTeamDispatch.js +1 -34
  39. package/dist/dispatches/ThrowGrenadeDispatch.js +1 -21
  40. package/dist/dispatches/index.js +1 -73
  41. package/dist/enums.js +1 -114
  42. package/dist/env/fetch.js +8 -129
  43. package/dist/env/globals.js +1 -17
  44. package/dist/index.js +1 -22
  45. package/dist/packets/addPlayer.js +1 -63
  46. package/dist/packets/beginShellStreak.js +1 -44
  47. package/dist/packets/challengeCompleted.js +1 -16
  48. package/dist/packets/changeCharacter.js +1 -46
  49. package/dist/packets/chat.js +1 -10
  50. package/dist/packets/collectItem.js +1 -26
  51. package/dist/packets/die.js +1 -40
  52. package/dist/packets/endShellStreak.js +1 -21
  53. package/dist/packets/eventModifier.js +1 -8
  54. package/dist/packets/explode.js +1 -19
  55. package/dist/packets/fire.js +1 -21
  56. package/dist/packets/gameAction.js +1 -33
  57. package/dist/packets/gameJoined.js +1 -64
  58. package/dist/packets/gameOptions.js +1 -27
  59. package/dist/packets/hitMe.js +1 -12
  60. package/dist/packets/hitMeHardBoiled.js +1 -18
  61. package/dist/packets/hitThem.js +1 -12
  62. package/dist/packets/melee.js +1 -8
  63. package/dist/packets/metaGameState.js +1 -62
  64. package/dist/packets/pause.js +1 -17
  65. package/dist/packets/ping.js +1 -19
  66. package/dist/packets/playerInfo.js +1 -15
  67. package/dist/packets/reload.js +1 -17
  68. package/dist/packets/removePlayer.js +1 -10
  69. package/dist/packets/respawn.js +1 -31
  70. package/dist/packets/socketReady.js +1 -16
  71. package/dist/packets/spawnItem.js +1 -11
  72. package/dist/packets/swapWeapon.js +1 -11
  73. package/dist/packets/switchTeam.js +1 -13
  74. package/dist/packets/syncMe.js +1 -25
  75. package/dist/packets/syncThem.js +1 -63
  76. package/dist/packets/throwGrenade.js +1 -17
  77. package/dist/packets/updateBalance.js +1 -8
  78. package/dist/pathing/astar.js +1 -68
  79. package/dist/pathing/mapnode.js +1 -284
  80. package/dist/socket.js +1 -107
  81. package/dist/util.js +1 -128
  82. package/dist/wasm/bytes.js +1 -8
  83. package/dist/wasm/direct.js +1 -84
  84. package/dist/wasm/legacy.js +1 -176
  85. package/package.json +2 -7
package/dist/bot.js CHANGED
@@ -1,1226 +1 @@
1
- import API from "./api.js";
2
- import CommIn from "./comm/CommIn.js";
3
- import CommOut from "./comm/CommOut.js";
4
- import CloseCode from "./constants/CloseCode.js";
5
- import CommCode from "./constants/CommCode.js";
6
- import GamePlayer from "./bot/GamePlayer.js";
7
- import yolkws from "./socket.js";
8
- import {
9
- ChiknWinnerDailyLimit,
10
- CoopState,
11
- findItemById,
12
- FramesBetweenSyncs,
13
- GameMode,
14
- Movement,
15
- PlayType,
16
- ShellStreak,
17
- StateBufferSize
18
- } from "./constants/index.js";
19
- import LookAtPosDispatch from "./dispatches/LookAtPosDispatch.js";
20
- import MovementDispatch from "./dispatches/MovementDispatch.js";
21
- import { DispatchIndex } from "./dispatches/index.js";
22
- import { coords, validate } from "./wasm/direct.js";
23
- import { createError } from "./util.js";
24
- import { Challenges } from "./constants/challenges.js";
25
- import { Maps } from "./constants/maps.js";
26
- import { Regions } from "./constants/regions.js";
27
- import {
28
- BuyItemError,
29
- ChallengeClaimError,
30
- ChallengeRerollError,
31
- ChicknWinnerError,
32
- ClaimSocialError,
33
- ClaimURLError,
34
- CleanupLevel,
35
- GameFindError,
36
- GameJoinError,
37
- Intents,
38
- LoginError,
39
- MatchmakerError,
40
- RedeemCodeError
41
- } from "./enums.js";
42
- import processAddPlayerPacket from "./packets/addPlayer.js";
43
- import processBeginShellStreakPacket from "./packets/beginShellStreak.js";
44
- import processChallengeCompletedPacket from "./packets/challengeCompleted.js";
45
- import processChangeCharacterPacket from "./packets/changeCharacter.js";
46
- import processChatPacket from "./packets/chat.js";
47
- import processCollectItemPacket from "./packets/collectItem.js";
48
- import processDiePacket from "./packets/die.js";
49
- import processEndShellStreakPacket from "./packets/endShellStreak.js";
50
- import processEventModifierPacket from "./packets/eventModifier.js";
51
- import processExplodePacket from "./packets/explode.js";
52
- import processFirePacket from "./packets/fire.js";
53
- import processGameActionPacket from "./packets/gameAction.js";
54
- import processGameJoinedPacket from "./packets/gameJoined.js";
55
- import processGameOptionsPacket from "./packets/gameOptions.js";
56
- import processHitMeHardBoiledPacket from "./packets/hitMeHardBoiled.js";
57
- import processHitMePacket from "./packets/hitMe.js";
58
- import processHitThemPacket from "./packets/hitThem.js";
59
- import processMeleePacket from "./packets/melee.js";
60
- import processMetaGameStatePacket from "./packets/metaGameState.js";
61
- import processPausePacket from "./packets/pause.js";
62
- import processPingPacket from "./packets/ping.js";
63
- import processPlayerInfoPacket from "./packets/playerInfo.js";
64
- import processReloadPacket from "./packets/reload.js";
65
- import processRemovePlayerPacket from "./packets/removePlayer.js";
66
- import processRespawnPacket from "./packets/respawn.js";
67
- import processSocketReadyPacket from "./packets/socketReady.js";
68
- import processSpawnItemPacket from "./packets/spawnItem.js";
69
- import processSwitchTeamPacket from "./packets/switchTeam.js";
70
- import processSyncMePacket from "./packets/syncMe.js";
71
- import processSyncThemPacket from "./packets/syncThem.js";
72
- import processThrowGrenadePacket from "./packets/throwGrenade.js";
73
- import processUpdateBalancePacket from "./packets/updateBalance.js";
74
- import processSwapWeaponPacket from "./packets/swapWeapon.js";
75
- const mod = (n, m) => (n % m + m) % m;
76
-
77
- export class Bot {
78
- intents = [];
79
- regionList = [];
80
- matchmakerListeners = [];
81
- #dispatches = [];
82
- #hooks = {};
83
- #globalHooks = [];
84
- #initialAccount;
85
- #initialGame;
86
- errorLogger = (...args) => console.error(...args);
87
- constructor(params = {}) {
88
- if (params.proxy && typeof process === "undefined")
89
- throw new Error("proxies do not work in this environment");
90
- this.intents = params.intents || [];
91
- if (this.intents.includes(Intents.COSMETIC_DATA)) {
92
- const ballCap = findItemById(1001);
93
- if (!ballCap)
94
- throw new Error("you cannot use the COSMETIC_DATA intent inside of the browser bundles");
95
- }
96
- if (this.intents.includes(Intents.SIMULATION) && !this.intents.includes(Intents.PATHFINDING))
97
- throw new Error("the SIMULATION intent requires the PATHFINDING intent");
98
- this.instance = params.instance || "shellshock.io";
99
- this.protocol = params.protocol || "wss";
100
- this.proxy = params.proxy || "";
101
- this.connectionTimeout = params.connectionTimeout || 5000;
102
- if (typeof params.errorLogger === "function")
103
- this.errorLogger = params.errorLogger;
104
- this.state = {
105
- name: "yolkbot",
106
- weaponIdx: 0,
107
- useAltGameURL: false,
108
- inGame: false,
109
- chatLines: 0,
110
- yaw: 0,
111
- pitch: 0,
112
- controlKeys: 0,
113
- onGround: 4,
114
- dx: 0,
115
- dy: 0,
116
- dz: 0,
117
- reloading: false,
118
- swappingGun: false,
119
- usingMelee: false,
120
- stateIdx: 0,
121
- serverStateIdx: 0,
122
- shotsFired: 0,
123
- buffer: []
124
- };
125
- this.players = {};
126
- this.me = new GamePlayer({});
127
- this.game = {
128
- raw: {},
129
- code: "",
130
- region: "",
131
- socket: null,
132
- gameModeId: 0,
133
- gameMode: "ffa",
134
- mapIdx: 0,
135
- map: {
136
- filename: "",
137
- hash: "",
138
- name: "",
139
- modes: {
140
- FFA: false,
141
- Teams: false,
142
- Spatula: false,
143
- King: false
144
- },
145
- availability: "both",
146
- numPlayers: "18",
147
- raw: {},
148
- zones: []
149
- },
150
- playerLimit: 0,
151
- isGameOwner: false,
152
- isPrivate: true,
153
- options: {
154
- gravity: 1,
155
- damage: 1,
156
- healthRegen: 1,
157
- locked: false,
158
- noTeamChange: false,
159
- noTeamShuffle: false,
160
- weaponsDisabled: Array(7).fill(false),
161
- mustUseSecondary: false
162
- },
163
- collectibles: [[], []],
164
- teamScore: [0, 0, 0],
165
- spatula: {
166
- coords: { x: 0, y: 0, z: 0 },
167
- controlledBy: 0,
168
- controlledByTeam: 0
169
- },
170
- kotc: {
171
- stage: CoopState.Capturing,
172
- zoneIdx: 0,
173
- activeZone: [],
174
- teamCapturing: 0,
175
- captureProgress: 0,
176
- numCapturing: 0,
177
- capturePercent: 0
178
- }
179
- };
180
- this.#initialGame = JSON.parse(JSON.stringify(this.game));
181
- this.account = {
182
- id: 0,
183
- firebase: {
184
- idToken: "",
185
- refreshToken: "",
186
- expiresIn: "3600",
187
- localId: ""
188
- },
189
- firebaseId: "",
190
- sessionId: "",
191
- session: 0,
192
- email: "",
193
- password: "",
194
- cw: {
195
- atLimit: false,
196
- limit: 0,
197
- secondsUntilPlay: 0,
198
- canPlayAgain: Date.now()
199
- },
200
- loadout: {
201
- hatId: null,
202
- meleeId: 0,
203
- stampId: null,
204
- classIdx: 0,
205
- colorIdx: 0,
206
- grenadeId: 0,
207
- primaryId: [3100, 3600, 3400, 3800, 4000, 4200, 4500],
208
- secondaryId: new Array(7).fill(3000),
209
- stampPositionX: 0,
210
- stampPositionY: 0
211
- },
212
- ownedItemIds: [],
213
- vip: false,
214
- emailVerified: false,
215
- isAged: false,
216
- isCG: false,
217
- adminRoles: 0,
218
- rawLoginData: {},
219
- eggBalance: 0
220
- };
221
- this.#initialAccount = JSON.parse(JSON.stringify(this.account));
222
- this.api = new API({
223
- proxy: this.proxy,
224
- protocol: this.protocol,
225
- instance: this.instance,
226
- connectionTimeout: this.connectionTimeout
227
- });
228
- this.ping = 0;
229
- this.lastPingTime = -1;
230
- this.lastDeathTime = -1;
231
- this.pathing = {
232
- nodeList: null,
233
- activePath: null,
234
- activeNode: null,
235
- activeNodeIdx: 0
236
- };
237
- this.hasQuit = false;
238
- if (this.intents.includes(Intents.NO_AFK_KICK))
239
- this.afkKickInterval = 0;
240
- if (this.intents.includes(Intents.RENEW_SESSION))
241
- this.renewSessionInterval = 0;
242
- }
243
- dispatch(dispatch, isEmit) {
244
- if (dispatch.validate(this)) {
245
- if (dispatch.check(this))
246
- dispatch.execute(this);
247
- else
248
- this.#dispatches.push(dispatch);
249
- return true;
250
- }
251
- this.errorLogger(`${isEmit ? "emit" : "dispatch"}: validation failed for dispatch ${dispatch.constructor.name}`);
252
- this.errorLogger("this means the dispatch will NEVER RUN!");
253
- this.errorLogger("make sure all parameters are valid and that any player IDs are in the game");
254
- return false;
255
- }
256
- async createAccount(email, pass) {
257
- this.account = this.#initialAccount;
258
- this.account.email = email;
259
- this.account.password = pass;
260
- const loginData = await this.api.createAccount(email, pass);
261
- return this.processLoginData(loginData);
262
- }
263
- async login(email, pass) {
264
- this.account = this.#initialAccount;
265
- this.account.email = email;
266
- this.account.password = pass;
267
- const loginData = await this.api.loginWithCredentials(email, pass);
268
- return this.processLoginData(loginData);
269
- }
270
- async loginWithRefreshToken(refreshToken) {
271
- this.account = this.#initialAccount;
272
- const loginData = await this.api.loginWithRefreshToken(refreshToken);
273
- return this.processLoginData(loginData);
274
- }
275
- async loginAnonymously() {
276
- this.account = this.#initialAccount;
277
- const loginData = await this.api.loginAnonymously();
278
- return this.processLoginData(loginData);
279
- }
280
- processLoginData(loginData) {
281
- if (!loginData.ok || !loginData.playerOutput) {
282
- this.$emit("authFail", loginData);
283
- if (loginData.ok && !loginData.playerOutput)
284
- this.errorLogger("processLoginData: missing playerOutput but is ok", loginData);
285
- return { ...loginData, error: LoginError.InternalError, ok: false };
286
- }
287
- if (loginData.banRemaining) {
288
- this.$emit("banned", loginData.banRemaining);
289
- return createError(LoginError.AccountBanned);
290
- }
291
- this.account.firebase = loginData.firebase;
292
- const output = loginData.playerOutput;
293
- this.account.rawLoginData = output;
294
- this.account.adminRoles = output.adminRoles || 0;
295
- this.account.eggBalance = output.currentBalance;
296
- this.account.emailVerified = output.emailVerified;
297
- this.account.firebaseId = output.firebaseId;
298
- this.account.id = output.id;
299
- this.account.isAged = new Date(output.dateCreated).getTime() < 1714546800000;
300
- this.account.isCG = output.cgAccountStatus?.hasAccount;
301
- this.account.loadout = output.loadout;
302
- this.account.ownedItemIds = output.ownedItemIds;
303
- this.account.session = output.session;
304
- this.account.sessionId = output.sessionId;
305
- this.account.vip = output.active_sub === "IsVIP";
306
- if (this.intents.includes(Intents.BOT_STATS))
307
- this.account.stats = {
308
- lifetime: output.statsLifetime,
309
- monthly: output.statsCurrent
310
- };
311
- if (this.intents.includes(Intents.CHALLENGES))
312
- this.#importChallenges(output.challenges);
313
- this.$emit("authSuccess", this.account);
314
- if (this.intents.includes(Intents.RENEW_SESSION)) {
315
- this.renewSessionInterval = setInterval(async () => {
316
- if (!this.account?.sessionId)
317
- return clearInterval(this.renewSessionInterval);
318
- const res = await this.api.queryServices({ cmd: "renewSession", sessionId: this.account.sessionId });
319
- if (res.data !== "renewed")
320
- this.$emit("sessionExpired");
321
- }, 10 * 60 * 1000);
322
- }
323
- return { ok: true, account: this.account };
324
- }
325
- #importChallenges(challengeArray) {
326
- this.account.challenges = [];
327
- for (const challengeData of challengeArray) {
328
- const challengeInfo = Challenges.find((c) => c.id === challengeData.challengeId);
329
- if (!challengeInfo)
330
- continue;
331
- delete challengeData.playerId;
332
- this.account.challenges.push({
333
- raw: { challengeInfo, challengeData },
334
- id: challengeData.challengeId,
335
- name: challengeInfo.loc.title,
336
- desc: challengeInfo.loc.desc,
337
- rewardEggs: challengeInfo.reward,
338
- isRerolled: !!challengeData.reset,
339
- isClaimed: !!challengeData.claimed,
340
- isCompleted: !!challengeData.completed,
341
- progressNum: challengeData.progress,
342
- goalNum: challengeInfo.goal
343
- });
344
- }
345
- }
346
- async createMatchmaker() {
347
- const matchmaker = new yolkws(`${this.protocol}://${this.instance}/matchmaker/`, { proxy: this.proxy, errorLogger: this.errorLogger });
348
- matchmaker.autoReconnect = true;
349
- const didConnect = await matchmaker.tryConnect();
350
- if (!didConnect)
351
- return createError(MatchmakerError.WebSocketConnectFail);
352
- this.matchmaker = matchmaker;
353
- const uuidTimeouts = [];
354
- this.matchmaker.onmessage = async (e) => {
355
- const data = JSON.parse(e.data);
356
- if (data.command === "validateUUID") {
357
- const timeout = setTimeout(() => {
358
- console.error("createMatchmaker: the matchmaker did not respond to our validateUUID");
359
- console.error("createMatchmaker: this means yolkbot is broken, please report this on Github");
360
- console.error("createMatchmaker: https://github.com/yolkop/yolkbot (or join the Discord)");
361
- }, 5000);
362
- uuidTimeouts.push(timeout);
363
- return this.matchmaker.send(JSON.stringify({ command: "validateUUID", hash: await validate(data.uuid) }));
364
- }
365
- if (uuidTimeouts.length) {
366
- uuidTimeouts.forEach((t) => clearTimeout(t));
367
- uuidTimeouts.length = 0;
368
- }
369
- this.matchmakerListeners.forEach((listener) => listener(data));
370
- };
371
- return { ok: true };
372
- }
373
- async getRegions() {
374
- if (!this.matchmaker) {
375
- const mmConnection = await this.createMatchmaker();
376
- if (!mmConnection.ok)
377
- return mmConnection;
378
- }
379
- return new Promise((res) => {
380
- const listener = (data) => {
381
- if (data.command === "regionList") {
382
- this.matchmakerListeners.splice(this.matchmakerListeners.indexOf(listener), 1);
383
- this.regionList = data.regionList;
384
- res({ ok: true, regionList: this.regionList });
385
- }
386
- };
387
- this.matchmakerListeners.push(listener);
388
- this.matchmaker.send(JSON.stringify({ command: "regionList" }));
389
- });
390
- }
391
- async initSession() {
392
- if (!this.account.sessionId && !this.intents.includes(Intents.SKIP_LOGIN)) {
393
- const anonLogin = await this.loginAnonymously();
394
- if (!anonLogin.ok)
395
- return anonLogin;
396
- }
397
- if (!this.matchmaker) {
398
- const mmConnection = await this.createMatchmaker();
399
- if (!mmConnection.ok)
400
- return mmConnection;
401
- }
402
- return { ok: true };
403
- }
404
- async findPublicGame(region, mode) {
405
- if (typeof region !== "string")
406
- return createError(GameFindError.MissingParams);
407
- const regions = this.regionList.length ? this.regionList : Regions;
408
- if (!regions.find((r) => r.id === region))
409
- return createError(GameFindError.InvalidRegion);
410
- let computedModeId;
411
- if (typeof mode === "number") {
412
- if (Object.values(GameMode).indexOf(mode) > -1)
413
- computedModeId = mode;
414
- else
415
- return createError(GameFindError.InvalidMode);
416
- } else if (typeof mode === "string") {
417
- const modeEntry = Object.keys(GameMode).find((key) => key.toLowerCase() === mode.toLowerCase());
418
- if (modeEntry)
419
- computedModeId = GameMode[modeEntry];
420
- else
421
- return createError(GameFindError.InvalidMode);
422
- } else
423
- return createError(GameFindError.InvalidMode);
424
- const initInfo = await this.initSession();
425
- if (!initInfo.ok)
426
- return initInfo;
427
- const game = await new Promise((resolve) => {
428
- const listener = (msg) => {
429
- if (msg.command === "notice")
430
- return;
431
- this.matchmakerListeners.splice(this.matchmakerListeners.indexOf(listener), 1);
432
- if (msg.command === "gameFound") {
433
- if (msg.useAltGameURL)
434
- this.state.useAltGameURL = true;
435
- return resolve(msg);
436
- }
437
- if (msg.error === "sessionNotFound")
438
- return resolve(createError(GameFindError.SessionExpired));
439
- this.errorLogger("findPublicGame: unknown matchmaker response", JSON.stringify(msg));
440
- resolve(createError(GameFindError.InternalError));
441
- };
442
- this.matchmakerListeners.push(listener);
443
- this.matchmaker.send(JSON.stringify({
444
- command: "findGame",
445
- region,
446
- playType: PlayType.JoinPublic,
447
- gameType: computedModeId,
448
- sessionId: this.account.sessionId
449
- }));
450
- });
451
- if (!game.id)
452
- return game;
453
- return {
454
- ok: true,
455
- raw: game,
456
- id: game.id,
457
- uuid: game.uuid,
458
- region: game.region,
459
- private: game.private,
460
- subdomain: game.subdomain
461
- };
462
- }
463
- async createPrivateGame(region, mode, map) {
464
- if (typeof region !== "string")
465
- return createError(GameFindError.MissingParams);
466
- const regions = this.regionList.length ? this.regionList : Regions;
467
- if (!regions.find((r) => r.id === region))
468
- return createError(GameFindError.InvalidRegion);
469
- let computedModeId;
470
- if (typeof mode === "number") {
471
- if (Object.values(GameMode).indexOf(mode) > -1)
472
- computedModeId = mode;
473
- else
474
- return createError(GameFindError.InvalidMode);
475
- } else if (typeof mode === "string") {
476
- const modeEntry = Object.keys(GameMode).find((key) => key.toLowerCase() === mode.toLowerCase());
477
- if (modeEntry)
478
- computedModeId = GameMode[modeEntry];
479
- else
480
- return createError(GameFindError.InvalidMode);
481
- } else
482
- return createError(GameFindError.InvalidMode);
483
- const mapIdx = Maps.findIndex((m) => m.name.toLowerCase() === map.toLowerCase());
484
- if (mapIdx === -1)
485
- return createError(GameFindError.InvalidMap);
486
- const initInfo = await this.initSession();
487
- if (!initInfo.ok)
488
- return initInfo;
489
- const game = await new Promise((resolve) => {
490
- const listener = (msg) => {
491
- if (msg.command === "notice")
492
- return;
493
- this.matchmakerListeners.splice(this.matchmakerListeners.indexOf(listener), 1);
494
- if (msg.command === "gameFound") {
495
- if (msg.useAltGameURL)
496
- this.state.useAltGameURL = true;
497
- return resolve(msg);
498
- }
499
- if (msg.error === "sessionNotFound")
500
- return resolve(createError(GameFindError.SessionExpired));
501
- this.errorLogger("createPrivateGame: unknown matchmaker response", JSON.stringify(msg));
502
- resolve(createError(GameFindError.InternalError));
503
- };
504
- this.matchmakerListeners.push(listener);
505
- this.matchmaker.send(JSON.stringify({
506
- command: "findGame",
507
- region,
508
- playType: PlayType.CreatePrivate,
509
- gameType: computedModeId,
510
- sessionId: this.account.sessionId,
511
- noobLobby: false,
512
- map: mapIdx
513
- }));
514
- });
515
- if (!game.id)
516
- return game;
517
- return {
518
- ok: true,
519
- raw: game,
520
- id: game.id,
521
- uuid: game.uuid,
522
- region: game.region,
523
- private: game.private,
524
- subdomain: game.subdomain
525
- };
526
- }
527
- async join(name, data) {
528
- this.state.name = name || "yolkbot";
529
- if (typeof data === "string") {
530
- if (data.includes("#"))
531
- data = data.split("#")[1];
532
- const initInfo = await this.initSession();
533
- if (!initInfo.ok)
534
- return initInfo;
535
- const joinResult = await new Promise((resolve) => {
536
- const listener = (message) => {
537
- if (message.command === "gameFound") {
538
- this.matchmakerListeners.splice(this.matchmakerListeners.indexOf(listener), 1);
539
- this.game.raw = message;
540
- this.game.code = message.id;
541
- this.game.region = message.region;
542
- resolve(message.id);
543
- }
544
- if (message.error && message.error === "gameNotFound") {
545
- this.matchmakerListeners.splice(this.matchmakerListeners.indexOf(listener), 1);
546
- resolve("gameNotFound");
547
- }
548
- };
549
- this.matchmakerListeners.push(listener);
550
- this.matchmaker.send(JSON.stringify({
551
- command: "joinGame",
552
- id: data,
553
- observe: false,
554
- sessionId: this.account.sessionId
555
- }));
556
- });
557
- if (joinResult === "gameNotFound")
558
- return createError(GameJoinError.GameNotFound);
559
- if (!this.game.raw.id) {
560
- this.errorLogger("join: invalid game data received from matchmaker:", this.game.raw);
561
- return createError(GameJoinError.InternalError);
562
- }
563
- } else if (typeof data === "object") {
564
- if (!data.id || !data.subdomain || !data.uuid || !data.region)
565
- return createError(GameJoinError.InvalidObject);
566
- if (this.account.id === 0) {
567
- const anonAttempt = await this.loginAnonymously();
568
- if (!anonAttempt.ok)
569
- return anonAttempt;
570
- }
571
- this.game.raw = data;
572
- this.game.code = this.game.raw.id;
573
- this.game.region = this.game.raw.region;
574
- } else
575
- return createError(GameJoinError.MissingParams);
576
- const host = this.state.useAltGameURL || this.instance === "proxy.yolkbot.xyz" ? `${this.instance}/servers/${this.game.raw.subdomain}` : `${this.game.raw.subdomain}.${this.instance}`;
577
- this.game.socket = new yolkws(`${this.protocol}://${host}/game/${this.game.raw.id}`, { proxy: this.proxy, errorLogger: this.errorLogger });
578
- this.game.socket.binaryType = "arraybuffer";
579
- this.game.socket.connectionTimeout = this.connectionTimeout;
580
- this.game.socket.onBeforeConnect = () => {
581
- this.game.socket.onmessage = (msg) => this.processPacket(msg.data);
582
- this.game.socket.onclose = (e) => {
583
- if (this.state?.inGame) {
584
- this.$emit("close", e.code);
585
- this.leave(-1);
586
- }
587
- };
588
- };
589
- const didConnect = await this.game.socket.tryConnect();
590
- if (!didConnect)
591
- return createError(GameJoinError.WebSocketConnectFail);
592
- return { ok: true };
593
- }
594
- #processMovement(ndx, ndy, ndz) {
595
- this.state.onGround = Math.max(--this.state.onGround, 0);
596
- this.me.position.x += ndx;
597
- this.me.position.y += ndy;
598
- this.me.position.z += ndz;
599
- const thisBlockX = Math.floor(this.me.position.x);
600
- const thisBlockY = Math.floor(this.me.position.y);
601
- const thisBlockZ = Math.floor(this.me.position.z);
602
- const blockInMyHitbox = this.pathing.nodeList.at(thisBlockX, thisBlockY, thisBlockZ);
603
- if (blockInMyHitbox && !blockInMyHitbox.isAir()) {
604
- if (!this.climbing && blockInMyHitbox.isLadder()) {}
605
- }
606
- if (this.me.climbing) {} else if (blockInMyHitbox && blockInMyHitbox.isJumpPad()) {
607
- this.me.position.y += 0.26;
608
- this.me.jumping = true;
609
- this.state.onGround = 0;
610
- }
611
- }
612
- #processPathfinding() {
613
- const pathLen = this.pathing.activePath.length;
614
- const lastNode = this.pathing.activePath[pathLen - 1];
615
- const myPos = this.me.position;
616
- const myFloorY = Math.floor(myPos.y);
617
- const distToEndCenter = Math.hypot(myPos.x - lastNode.flatCenter.x, myPos.z - lastNode.flatCenter.z);
618
- if (distToEndCenter < 0.3) {
619
- this.pathing.activePath = null;
620
- this.pathing.activeNode = null;
621
- this.pathing.activeNodeIdx = 0;
622
- this.dispatch(new MovementDispatch(0));
623
- this.$emit("pathfindComplete");
624
- } else {
625
- let shouldJump = false;
626
- if (this.pathing.activeNodeIdx < pathLen - 1) {
627
- const currentNode = this.pathing.activePath[this.pathing.activeNodeIdx];
628
- const nextNode = this.pathing.activePath[this.pathing.activeNodeIdx + 1];
629
- const dx = Math.abs(currentNode.x - nextNode.x);
630
- const dz = Math.abs(currentNode.z - nextNode.z);
631
- const isParkourJump = dx === 2 && dz === 0 || dx === 0 && dz === 2 || dx === 2 && dz === 1 || dx === 1 && dz === 2 || dx === 2 && dz === 2;
632
- if (isParkourJump) {
633
- const localX = myPos.x - Math.floor(myPos.x);
634
- const localZ = myPos.z - Math.floor(myPos.z);
635
- const headingX = nextNode.x > currentNode.x ? 1 : nextNode.x < currentNode.x ? -1 : 0;
636
- const headingZ = nextNode.z > currentNode.z ? 1 : nextNode.z < currentNode.z ? -1 : 0;
637
- let distToEdge = 0;
638
- if (headingX > 0)
639
- distToEdge = 1 - localX;
640
- else if (headingX < 0)
641
- distToEdge = localX;
642
- else if (headingZ > 0)
643
- distToEdge = 1 - localZ;
644
- else if (headingZ < 0)
645
- distToEdge = localZ;
646
- if (distToEdge <= 0.04)
647
- shouldJump = true;
648
- }
649
- }
650
- if (this.pathing.activeNodeIdx < pathLen) {
651
- const positionTarget = this.pathing.activePath[this.pathing.activeNodeIdx].flatCenter;
652
- if (this.state.onGround)
653
- this.dispatch(new LookAtPosDispatch(positionTarget));
654
- }
655
- for (let i = this.pathing.activeNodeIdx;i < pathLen; i++) {
656
- const node = this.pathing.activePath[i];
657
- if (node.flatRadialDistance(myPos) < 0.1 && node.y === myFloorY) {
658
- this.pathing.activeNodeIdx = i + 1;
659
- this.pathing.activeNode = this.pathing.activePath[this.pathing.activeNodeIdx];
660
- break;
661
- }
662
- }
663
- const movementKeys = Movement.Forward | (shouldJump ? Movement.Jump : 0);
664
- this.dispatch(new MovementDispatch(movementKeys));
665
- }
666
- }
667
- update() {
668
- if (this.hasQuit)
669
- return;
670
- if (this.pathing.activePath && this.intents.includes(Intents.PATHFINDING))
671
- this.#processPathfinding();
672
- for (let i = this.#dispatches.length - 1;i >= 0; i--) {
673
- const disp = this.#dispatches[i];
674
- if (disp.check(this)) {
675
- disp.execute(this);
676
- this.#dispatches.splice(i, 1);
677
- }
678
- }
679
- this.state.chatLines = Math.max(0, this.state.chatLines - 1 / (30 * 4));
680
- if (this.me.playing) {
681
- const currentIdx = this.state.stateIdx;
682
- const isBufferDebug = this.intents.includes(Intents.DEBUG_BUFFER);
683
- if (isBufferDebug)
684
- console.log("setting buffer for idx", currentIdx);
685
- this.me.jumping = !!(this.state.controlKeys & Movement.Jump);
686
- this.state.buffer[currentIdx] = {
687
- controlKeys: this.state.controlKeys,
688
- yaw: this.state.yaw,
689
- pitch: this.state.pitch,
690
- shotsFired: this.state.shotsFired,
691
- position: isBufferDebug ? { ...this.me.position } : {}
692
- };
693
- this.state.shotsFired = 0;
694
- if (this.intents.includes(Intents.SIMULATION)) {
695
- let dx = 0;
696
- let dy = 0;
697
- let dz = 0;
698
- const state = this.state.buffer[currentIdx];
699
- if (state.controlKeys & Movement.Left) {
700
- dx -= Math.cos(state.yaw);
701
- dz += Math.sin(state.yaw);
702
- }
703
- if (state.controlKeys & Movement.Right) {
704
- dx += Math.cos(state.yaw);
705
- dz -= Math.sin(state.yaw);
706
- }
707
- if (state.controlKeys & Movement.Forward) {
708
- if (this.me.climbing)
709
- dy += 1;
710
- else {
711
- dx -= Math.sin(state.yaw);
712
- dz -= Math.cos(state.yaw);
713
- }
714
- }
715
- if (state.controlKeys & Movement.Backward) {
716
- if (this.me.climbing)
717
- dy -= 1;
718
- else {
719
- dx += Math.sin(state.yaw);
720
- dz += Math.cos(state.yaw);
721
- }
722
- }
723
- if (state.controlKeys & Movement.Jump)
724
- this.state.controlKeys ^= this.state.controlKeys & Movement.Jump;
725
- if (this.me.climbing) {
726
- this.me.jumping = false;
727
- dy += dy * 0.028;
728
- this.me.position.y += dy;
729
- dy *= 0.5;
730
- this.#processMovement(0, dy, 0);
731
- } else {
732
- const mag = Math.sqrt(dx * dx + dy * dy + dz * dz);
733
- if (mag > 0) {
734
- const normDx = dx / mag;
735
- const normDz = dz / mag;
736
- dx = this.state.dx + normDx * 0.025;
737
- dz = this.state.dz + normDz * 0.025;
738
- } else {
739
- dx = this.state.dx;
740
- dz = this.state.dz;
741
- }
742
- dy = this.state.dy - this.game.options.gravity * 0.012;
743
- dy = Math.max(-0.29, dy);
744
- const oldX = this.me.position.x;
745
- const oldY = this.me.position.y;
746
- const oldZ = this.me.position.z;
747
- this.#processMovement(dx, dy, dz);
748
- dx = this.me.position.x - oldX;
749
- dy = this.me.position.y - oldY;
750
- dz = this.me.position.z - oldZ;
751
- if (this.state.onGround && dy > 0) {
752
- const normY = mag === 0 ? 0 : dy / mag;
753
- const n = 1 - normY * 0.5;
754
- dx *= n;
755
- dz *= n;
756
- }
757
- }
758
- dx *= 0.64;
759
- dz *= 0.64;
760
- if (this.me.climbing)
761
- dy *= 0.64;
762
- this.state.dx = dx;
763
- this.state.dy = dy;
764
- this.state.dz = dz;
765
- }
766
- if (this.state.stateIdx % FramesBetweenSyncs === 0) {
767
- this.$emit("tick");
768
- const out = new CommOut;
769
- out.packInt8(CommCode.syncMe);
770
- out.packInt8(this.state.stateIdx);
771
- out.packInt8(this.state.serverStateIdx);
772
- const startIdx = mod(this.state.stateIdx - FramesBetweenSyncs + 1, StateBufferSize);
773
- if (isBufferDebug)
774
- console.log("--- START THIS SYNC LOGGING ---");
775
- for (let i = 0;i < FramesBetweenSyncs; i++) {
776
- const idx = mod(startIdx + i, StateBufferSize);
777
- const frame = this.state.buffer[idx] || {};
778
- const keys = frame.controlKeys || 0;
779
- const shots = frame.shotsFired || 0;
780
- const yaw = frame.yaw ?? this.state.yaw;
781
- const pitch = frame.pitch ?? this.state.pitch;
782
- if (isBufferDebug)
783
- console.log("adding", this.state.stateIdx, startIdx, idx, frame);
784
- out.packInt8(keys);
785
- out.packInt8(shots);
786
- out.packString(coords(yaw, pitch));
787
- out.packInt8(100);
788
- }
789
- out.send(this.game.socket);
790
- if (isBufferDebug)
791
- console.log("--- END SYNC LOGGING ---");
792
- this.state.buffer = [];
793
- }
794
- this.state.stateIdx = mod(this.state.stateIdx + 1, StateBufferSize);
795
- }
796
- if (!this.intents.includes(Intents.PLAYER_HEALTH))
797
- return;
798
- const regen = 0.1 * (this.game.isPrivate ? this.game.options.healthRegen : 1);
799
- const players = Object.values(this.players);
800
- for (let i = 0;i < players.length; i++) {
801
- const player = players[i];
802
- if (player.playing && player.hp > 0) {
803
- const hasOverHeal = player.streakRewards.includes(ShellStreak.OverHeal);
804
- player.hp += hasOverHeal ? -regen : regen;
805
- player.hp = hasOverHeal ? Math.max(100, player.hp) : Math.min(100, player.hp);
806
- }
807
- if (player.spawnShield > 0)
808
- player.spawnShield -= 6;
809
- }
810
- }
811
- on(event, cb) {
812
- if (Object.keys(this.#hooks).includes(event))
813
- this.#hooks[event].push(cb);
814
- else
815
- this.#hooks[event] = [cb];
816
- }
817
- onAny(cb) {
818
- this.#globalHooks.push(cb);
819
- }
820
- off(event, cb) {
821
- if (cb)
822
- this.#hooks[event] = this.#hooks[event].filter((hook) => hook !== cb);
823
- else
824
- this.#hooks[event] = [];
825
- }
826
- $emit(event, ...args) {
827
- if (this.hasQuit)
828
- return;
829
- if (this.#hooks[event])
830
- for (const cb of this.#hooks[event])
831
- cb(...args);
832
- for (const cb of this.#globalHooks)
833
- cb(event, ...args);
834
- }
835
- emit(event, ...args) {
836
- const dispatch = DispatchIndex[event];
837
- if (dispatch)
838
- this.dispatch(new dispatch(...args), true);
839
- else
840
- throw new Error(`no event found for "${event}"`);
841
- }
842
- updateGameOptions() {
843
- const out = new CommOut;
844
- out.packInt8(CommCode.gameOptions);
845
- out.packInt8(this.game.options.gravity * 4);
846
- out.packInt8(this.game.options.damage * 4);
847
- out.packInt8(this.game.options.healthRegen * 4);
848
- const flags = (this.game.options.locked ? 1 : 0) | (this.game.options.noTeamChange ? 2 : 0) | (this.game.options.noTeamShuffle ? 4 : 0);
849
- out.packInt8(flags);
850
- this.game.options.weaponsDisabled.forEach((v) => out.packInt8(v ? 1 : 0));
851
- out.send(this.game.socket);
852
- }
853
- packetHandlers = {
854
- [CommCode.syncThem]: processSyncThemPacket.bind(null, this),
855
- [CommCode.fire]: processFirePacket.bind(null, this),
856
- [CommCode.hitThem]: processHitThemPacket.bind(null, this),
857
- [CommCode.syncMe]: processSyncMePacket.bind(null, this),
858
- [CommCode.hitMe]: processHitMePacket.bind(null, this),
859
- [CommCode.swapWeapon]: processSwapWeaponPacket.bind(null, this),
860
- [CommCode.collectItem]: processCollectItemPacket.bind(null, this),
861
- [CommCode.respawn]: processRespawnPacket.bind(null, this),
862
- [CommCode.die]: processDiePacket.bind(null, this),
863
- [CommCode.pause]: processPausePacket.bind(null, this),
864
- [CommCode.chat]: processChatPacket.bind(null, this),
865
- [CommCode.addPlayer]: processAddPlayerPacket.bind(null, this),
866
- [CommCode.removePlayer]: processRemovePlayerPacket.bind(null, this),
867
- [CommCode.eventModifier]: processEventModifierPacket.bind(null, this),
868
- [CommCode.metaGameState]: processMetaGameStatePacket.bind(null, this),
869
- [CommCode.beginShellStreak]: processBeginShellStreakPacket.bind(null, this),
870
- [CommCode.endShellStreak]: processEndShellStreakPacket.bind(null, this),
871
- [CommCode.hitMeHardBoiled]: processHitMeHardBoiledPacket.bind(null, this),
872
- [CommCode.gameOptions]: processGameOptionsPacket.bind(null, this),
873
- [CommCode.ping]: processPingPacket.bind(null, this),
874
- [CommCode.switchTeam]: processSwitchTeamPacket.bind(null, this),
875
- [CommCode.changeCharacter]: processChangeCharacterPacket.bind(null, this),
876
- [CommCode.reload]: processReloadPacket.bind(null, this),
877
- [CommCode.explode]: processExplodePacket.bind(null, this),
878
- [CommCode.throwGrenade]: processThrowGrenadePacket.bind(null, this),
879
- [CommCode.spawnItem]: processSpawnItemPacket.bind(null, this),
880
- [CommCode.melee]: processMeleePacket.bind(null, this),
881
- [CommCode.updateBalance]: processUpdateBalancePacket.bind(null, this),
882
- [CommCode.challengeCompleted]: processChallengeCompletedPacket.bind(null, this),
883
- [CommCode.gameAction]: processGameActionPacket.bind(null, this),
884
- [CommCode.socketReady]: processSocketReadyPacket.bind(null, this),
885
- [CommCode.gameJoined]: processGameJoinedPacket.bind(null, this),
886
- [CommCode.playerInfo]: processPlayerInfoPacket.bind(null, this),
887
- [CommCode.respawnDenied]: () => {
888
- this.me.playing = false;
889
- this.$emit("respawnDenied");
890
- },
891
- [CommCode.requestGameOptions]: () => {
892
- this.game.isPrivate = true;
893
- this.updateGameOptions();
894
- },
895
- [CommCode.expireUpgrade]: () => {},
896
- [CommCode.clientReady]: () => {},
897
- [CommCode.musicInfo]: () => CommIn.unPackLongString()
898
- };
899
- processPacket(packet) {
900
- if (this.hasQuit)
901
- return;
902
- CommIn.init(packet);
903
- if (this.intents.includes(Intents.PACKET_HOOK))
904
- this.$emit("packet", packet);
905
- let lastCommand = 0;
906
- let lastCode = 0;
907
- while (CommIn.isMoreDataAvailable()) {
908
- const cmd = CommIn.unPackInt8U();
909
- const handler = this.packetHandlers[cmd];
910
- if (handler)
911
- handler();
912
- else {
913
- console.error(`processPacket: got but couldn't identify: ${Object.keys(CommCode).find((k) => CommCode[k] === cmd)} ${cmd}`);
914
- if (lastCommand)
915
- console.error(`processPacket: it may be a result of the ${lastCommand} command (${lastCode}).`);
916
- break;
917
- }
918
- lastCommand = Object.keys(CommCode).find((k) => CommCode[k] === cmd);
919
- lastCode = cmd;
920
- if (this.intents.includes(Intents.LOG_PACKETS))
921
- console.log(`[LOG_PACKETS] packet ${lastCommand}: ${lastCode}`);
922
- }
923
- }
924
- async checkChiknWinner() {
925
- const response = await this.api.queryServices({
926
- cmd: "chicknWinnerReady",
927
- id: this.account.id,
928
- sessionId: this.account.sessionId
929
- });
930
- if (!response.ok)
931
- return response;
932
- this.account.cw.limit = response.limit;
933
- this.account.cw.atLimit = response.limit >= 4;
934
- this.account.cw.secondsUntilPlay = (this.account.cw.atLimit ? response.period : response.span) || 0;
935
- this.account.cw.canPlayAgain = Date.now() + this.account.cw.secondsUntilPlay * 1000;
936
- return { ok: true, cw: this.account.cw };
937
- }
938
- async playChiknWinner(doPrematureCooldownCheck = true) {
939
- if (this.account.cw.atLimit || this.account.cw.limit > ChiknWinnerDailyLimit)
940
- return createError(ChicknWinnerError.HitDailyLimit);
941
- if (this.account.cw.canPlayAgain > Date.now() && doPrematureCooldownCheck)
942
- return createError(ChicknWinnerError.OnCooldown);
943
- const response = await this.api.queryServices({
944
- cmd: "incentivizedVideoReward",
945
- firebaseId: this.account.firebaseId,
946
- id: this.account.id,
947
- sessionId: this.account.sessionId,
948
- token: null
949
- });
950
- if (!response.ok)
951
- return response;
952
- if (response.error) {
953
- if (response.error === "RATELIMITED" || response.error === "RATELMITED") {
954
- await this.checkChiknWinner();
955
- return createError(ChicknWinnerError.OnCooldown);
956
- } else if (response.error === "SESSION_EXPIRED") {
957
- this.$emit("sessionExpired");
958
- return createError(ChicknWinnerError.SessionExpired);
959
- }
960
- this.errorLogger("unknown Chikn Winner response, report this on Github:", response);
961
- return createError(ChicknWinnerError.InternalError);
962
- }
963
- if (response.reward) {
964
- this.account.eggBalance += response.reward.eggsGiven;
965
- response.reward.itemIds.forEach((id) => this.account.ownedItemIds.push(id));
966
- await this.checkChiknWinner();
967
- return { ok: true, ...response.reward };
968
- }
969
- this.errorLogger("unknown Chikn Winner response, report this on Github:", response);
970
- return createError(ChicknWinnerError.InternalError);
971
- }
972
- async resetChiknWinner() {
973
- if (this.account.eggBalance < 200)
974
- return createError(ChicknWinnerError.NotEnoughResetEggs);
975
- if (!this.account.cw.atLimit)
976
- return createError(ChicknWinnerError.NotAtDailyLimit);
977
- const response = await this.api.queryServices({
978
- cmd: "chwReset",
979
- sessionId: this.account.sessionId
980
- });
981
- if (!response.ok)
982
- return response;
983
- if (response.result !== "SUCCESS") {
984
- this.errorLogger("unknown Chikn Winner reset response, report this on Github:", response);
985
- return createError(ChicknWinnerError.InternalError);
986
- }
987
- this.account.eggBalance -= 200;
988
- await this.checkChiknWinner();
989
- return { ok: true, cw: this.account.cw };
990
- }
991
- canSee(target) {
992
- if (!this.intents.includes(Intents.PATHFINDING))
993
- throw new Error("canSee: you need enable the PATHFINDING intent");
994
- return this.pathing.nodeList.hasLineOfSight(this.me.position, target.position);
995
- }
996
- getBestTarget() {
997
- const options = Object.values(this.players).filter((player) => player?.playing).filter((player) => player.hp > 0).filter((player) => player.id !== this.me.id).filter((player) => this.me.team === 0 || player.team !== this.me.team);
998
- const distancePlayers = options.map((player) => ({
999
- player,
1000
- distance: (player.position.x - this.me.position.x) ** 2 + 5 * (player.position.y - this.me.position.y) ** 2 + (player.position.z - this.me.position.z) ** 2
1001
- })).sort((a, b) => a.distance - b.distance);
1002
- if (distancePlayers.length)
1003
- return distancePlayers[0].player;
1004
- else
1005
- return null;
1006
- }
1007
- async refreshChallenges() {
1008
- const result = await this.api.queryServices({
1009
- cmd: "challengeGetDaily",
1010
- sessionId: this.account.sessionId,
1011
- playerId: this.account.id
1012
- });
1013
- if (!result.ok)
1014
- return result;
1015
- this.#importChallenges(result);
1016
- return { ok: true, challenges: this.account.challenges };
1017
- }
1018
- async rerollChallenge(challengeId) {
1019
- const result = await this.api.queryServices({
1020
- cmd: "challengeRerollSlot",
1021
- sessionId: this.account.sessionId,
1022
- slotId: challengeId
1023
- });
1024
- if (!result.ok)
1025
- return result;
1026
- if (result["0"]) {
1027
- this.#importChallenges(Object.values(result));
1028
- return { ok: true, challenges: this.account.challenges };
1029
- }
1030
- const isInEnum = Object.values(ChallengeRerollError).includes(result.error);
1031
- if (isInEnum)
1032
- return { ok: false, error: result.error };
1033
- this.errorLogger("rerollChallenge: unknown error response", result);
1034
- return { ok: false, error: ChallengeRerollError.InternalError };
1035
- }
1036
- async claimChallenge(challengeId) {
1037
- const result = await this.api.queryServices({
1038
- cmd: "challengeClaimReward",
1039
- sessionId: this.account.sessionId,
1040
- slotId: challengeId
1041
- });
1042
- if (!result.ok)
1043
- return result;
1044
- if (result.reward) {
1045
- this.#importChallenges(result.challenges);
1046
- if (result.reward > 0)
1047
- this.account.eggBalance += result.reward;
1048
- return { ok: true, eggReward: result.reward, updatedChallenges: this.account.challenges };
1049
- }
1050
- const isInEnum = Object.values(ChallengeClaimError).includes(result.error);
1051
- if (isInEnum)
1052
- return { ok: false, error: result.error };
1053
- this.errorLogger("claimChallenge: unknown error response", result);
1054
- return { ok: false, error: ChallengeClaimError.InternalError };
1055
- }
1056
- async refreshBalance() {
1057
- const result = await this.api.queryServices({
1058
- cmd: "checkBalance",
1059
- firebaseId: this.account.firebaseId,
1060
- sessionId: this.account.sessionId
1061
- });
1062
- if (!result.ok)
1063
- return result;
1064
- this.account.eggBalance = result.currentBalance;
1065
- return { ok: true, currentBalance: result.currentBalance };
1066
- }
1067
- async redeemCode(code) {
1068
- const result = await this.api.queryServices({
1069
- cmd: "redeem",
1070
- firebaseId: this.account.firebaseId,
1071
- sessionId: this.account.sessionId,
1072
- id: this.account.id,
1073
- code
1074
- });
1075
- if (!result.ok)
1076
- return result;
1077
- if (result.result === "SUCCESS") {
1078
- this.account.eggBalance += result.eggs_given;
1079
- result.item_ids.forEach((id) => this.account.ownedItemIds.push(id));
1080
- return { ok: true, eggsGiven: result.eggs_given, itemIds: result.item_ids };
1081
- }
1082
- const isInEnum = Object.values(RedeemCodeError).includes(result.result);
1083
- if (isInEnum)
1084
- return { ok: false, error: result.result };
1085
- this.errorLogger("redeemCode: unknown error response", result);
1086
- return { ok: false, error: RedeemCodeError.InternalError };
1087
- }
1088
- async claimURLReward(reward) {
1089
- const result = await this.api.queryServices({
1090
- cmd: "urlRewardParams",
1091
- firebaseId: this.account.firebaseId,
1092
- sessionId: this.account.sessionId,
1093
- reward
1094
- });
1095
- if (!result.ok)
1096
- return result;
1097
- if (result.result === "SUCCESS") {
1098
- this.account.eggBalance += result.eggsGiven;
1099
- result.itemIds.forEach((id) => this.account.ownedItemIds.push(id));
1100
- return { ok: true, eggsGiven: result.eggsGiven, itemIds: result.itemIds };
1101
- }
1102
- const isInEnum = Object.values(ClaimURLError).includes(result.result);
1103
- if (isInEnum)
1104
- return { ok: false, error: result.result };
1105
- this.errorLogger("claimURLReward: unknown error response", result);
1106
- return { ok: false, error: ClaimURLError.InternalError };
1107
- }
1108
- async claimSocialReward(rewardTag) {
1109
- const result = await this.api.queryServices({
1110
- cmd: "reward",
1111
- firebaseId: this.account.firebaseId,
1112
- sessionId: this.account.sessionId,
1113
- rewardTag
1114
- });
1115
- if (!result.ok)
1116
- return result;
1117
- if (result.result === "SUCCESS") {
1118
- this.account.eggBalance += result.eggsGiven;
1119
- result.itemIds.forEach((id) => this.account.ownedItemIds.push(id));
1120
- return { ok: true, eggsGiven: result.eggsGiven, itemIds: result.itemIds };
1121
- }
1122
- const isInEnum = Object.values(ClaimSocialError).includes(result.result);
1123
- if (isInEnum)
1124
- return { ok: false, error: result.result };
1125
- this.errorLogger("claimSocialReward: unknown error response", result);
1126
- return { ok: false, error: ClaimSocialError.InternalError };
1127
- }
1128
- async buyItem(itemId) {
1129
- const result = await this.api.queryServices({
1130
- cmd: "buy",
1131
- firebaseId: this.account.firebaseId,
1132
- sessionId: this.account.sessionId,
1133
- itemId,
1134
- save: true
1135
- });
1136
- if (!result.ok)
1137
- return result;
1138
- if (result.result === "SUCCESS") {
1139
- this.account.eggBalance = result.currentBalance;
1140
- this.account.ownedItemIds.push(result.itemId);
1141
- return { ok: true, itemId: result.itemId, currentBalance: result.currentBalance };
1142
- }
1143
- const isInEnum = Object.values(BuyItemError).includes(result.result);
1144
- if (isInEnum)
1145
- return { ok: false, error: result.result };
1146
- this.errorLogger("buyItem: unknown error response", result);
1147
- return { ok: false, error: BuyItemError.InternalError };
1148
- }
1149
- leave(code = CloseCode.mainMenu) {
1150
- if (this.hasQuit)
1151
- return;
1152
- this.state.inGame = false;
1153
- if (code > -1) {
1154
- this.game?.socket?.close(code);
1155
- this.$emit("leave", code);
1156
- }
1157
- if (this.updateIntervalId)
1158
- clearInterval(this.updateIntervalId);
1159
- if (this.pingTimeoutId)
1160
- clearTimeout(this.pingTimeoutId);
1161
- this.#dispatches = [];
1162
- this.state.chatLines = 0;
1163
- this.state.reloading = false;
1164
- this.state.swappingGun = false;
1165
- this.state.usingMelee = false;
1166
- this.state.stateIdx = 0;
1167
- this.state.serverStateIdx = 0;
1168
- this.state.shotsFired = 0;
1169
- this.state.buffer = [];
1170
- this.players = {};
1171
- this.me = new GamePlayer({});
1172
- this.game = this.#initialGame;
1173
- this.ping = 0;
1174
- this.lastPingTime = -1;
1175
- this.lastDeathTime = -1;
1176
- this.pathing = {
1177
- nodeList: null,
1178
- activePath: null,
1179
- activeNode: null,
1180
- activeNodeIdx: 0
1181
- };
1182
- }
1183
- logout() {
1184
- this.account = this.#initialAccount;
1185
- if (this.intents.includes(Intents.RENEW_SESSION))
1186
- clearInterval(this.renewSessionInterval);
1187
- }
1188
- quit(cleanupLevel = CleanupLevel.Partial) {
1189
- if (this.hasQuit)
1190
- return;
1191
- this.hasQuit = true;
1192
- this.leave();
1193
- if (this.matchmaker) {
1194
- try {
1195
- this.matchmaker.close();
1196
- } catch {}
1197
- if (cleanupLevel >= CleanupLevel.Partial)
1198
- this.matchmaker = null;
1199
- }
1200
- if (this.intents.includes(Intents.NO_AFK_KICK))
1201
- clearInterval(this.afkKickInterval);
1202
- if (this.intents.includes(Intents.RENEW_SESSION))
1203
- clearInterval(this.renewSessionInterval);
1204
- if (this.pingTimeoutId)
1205
- clearTimeout(this.pingTimeoutId);
1206
- if (cleanupLevel >= CleanupLevel.Partial) {
1207
- this.api = null;
1208
- this.packetHandlers = null;
1209
- this.pathing = null;
1210
- this.#initialAccount = {};
1211
- this.#initialGame = {};
1212
- this.#hooks = {};
1213
- this.#globalHooks = [];
1214
- this.#dispatches = [];
1215
- this.matchmakerListeners = [];
1216
- if (cleanupLevel >= CleanupLevel.Full) {
1217
- this.account = null;
1218
- this.game = null;
1219
- this.me = null;
1220
- this.players = null;
1221
- this.state = null;
1222
- }
1223
- }
1224
- }
1225
- }
1226
- export default Bot;
1
+ import API from"./api.js";import CommIn from"./comm/CommIn.js";import CommOut from"./comm/CommOut.js";import CloseCode from"./constants/CloseCode.js";import CommCode from"./constants/CommCode.js";import GamePlayer from"./bot/GamePlayer.js";import yolkws from"./socket.js";import{ChiknWinnerDailyLimit,CoopState,findItemById,FramesBetweenSyncs,GameMode,Movement,PlayType,ShellStreak,StateBufferSize}from"./constants/index.js";import LookAtPosDispatch from"./dispatches/LookAtPosDispatch.js";import MovementDispatch from"./dispatches/MovementDispatch.js";import{DispatchIndex}from"./dispatches/index.js";import{coords,validate}from"./wasm/direct.js";import{createError}from"./util.js";import{Challenges}from"./constants/challenges.js";import{Maps}from"./constants/maps.js";import{Regions}from"./constants/regions.js";import{BuyItemError,ChallengeClaimError,ChallengeRerollError,ChicknWinnerError,ClaimSocialError,ClaimURLError,CleanupLevel,GameFindError,GameJoinError,Intents,LoginError,MatchmakerError,RedeemCodeError}from"./enums.js";import processAddPlayerPacket from"./packets/addPlayer.js";import processBeginShellStreakPacket from"./packets/beginShellStreak.js";import processChallengeCompletedPacket from"./packets/challengeCompleted.js";import processChangeCharacterPacket from"./packets/changeCharacter.js";import processChatPacket from"./packets/chat.js";import processCollectItemPacket from"./packets/collectItem.js";import processDiePacket from"./packets/die.js";import processEndShellStreakPacket from"./packets/endShellStreak.js";import processEventModifierPacket from"./packets/eventModifier.js";import processExplodePacket from"./packets/explode.js";import processFirePacket from"./packets/fire.js";import processGameActionPacket from"./packets/gameAction.js";import processGameJoinedPacket from"./packets/gameJoined.js";import processGameOptionsPacket from"./packets/gameOptions.js";import processHitMeHardBoiledPacket from"./packets/hitMeHardBoiled.js";import processHitMePacket from"./packets/hitMe.js";import processHitThemPacket from"./packets/hitThem.js";import processMeleePacket from"./packets/melee.js";import processMetaGameStatePacket from"./packets/metaGameState.js";import processPausePacket from"./packets/pause.js";import processPingPacket from"./packets/ping.js";import processPlayerInfoPacket from"./packets/playerInfo.js";import processReloadPacket from"./packets/reload.js";import processRemovePlayerPacket from"./packets/removePlayer.js";import processRespawnPacket from"./packets/respawn.js";import processSocketReadyPacket from"./packets/socketReady.js";import processSpawnItemPacket from"./packets/spawnItem.js";import processSwitchTeamPacket from"./packets/switchTeam.js";import processSyncMePacket from"./packets/syncMe.js";import processSyncThemPacket from"./packets/syncThem.js";import processThrowGrenadePacket from"./packets/throwGrenade.js";import processUpdateBalancePacket from"./packets/updateBalance.js";import processSwapWeaponPacket from"./packets/swapWeapon.js";const mod=(n,m)=>(n%m+m)%m;export class Bot{intents=[];regionList=[];matchmakerListeners=[];#dispatches=[];#hooks={};#globalHooks=[];#initialAccount;#initialGame;errorLogger=(...args)=>console.error(...args);constructor(params={}){if(params.proxy&&typeof process==="undefined")throw new Error("proxies do not work in this environment");this.intents=params.intents||[];if(this.intents.includes(Intents.COSMETIC_DATA)){const ballCap=findItemById(1001);if(!ballCap)throw new Error("you cannot use the COSMETIC_DATA intent inside of the browser bundles")}if(this.intents.includes(Intents.SIMULATION)&&!this.intents.includes(Intents.PATHFINDING))throw new Error("the SIMULATION intent requires the PATHFINDING intent");this.instance=params.instance||"shellshock.io";this.protocol=params.protocol||"wss";this.proxy=params.proxy||"";this.connectionTimeout=params.connectionTimeout||5000;if(typeof params.errorLogger==="function")this.errorLogger=params.errorLogger;this.state={name:"yolkbot",weaponIdx:0,useAltGameURL:false,inGame:false,chatLines:0,yaw:0,pitch:0,controlKeys:0,onGround:4,dx:0,dy:0,dz:0,reloading:false,swappingGun:false,usingMelee:false,stateIdx:0,serverStateIdx:0,shotsFired:0,buffer:[]};this.players={};this.me=new GamePlayer({});this.game={raw:{},code:"",region:"",socket:null,gameModeId:0,gameMode:"ffa",mapIdx:0,map:{filename:"",hash:"",name:"",modes:{FFA:false,Teams:false,Spatula:false,King:false},availability:"both",numPlayers:"18",raw:{},zones:[]},playerLimit:0,isGameOwner:false,isPrivate:true,options:{gravity:1,damage:1,healthRegen:1,locked:false,noTeamChange:false,noTeamShuffle:false,weaponsDisabled:Array(7).fill(false),mustUseSecondary:false},collectibles:[[],[]],teamScore:[0,0,0],spatula:{coords:{x:0,y:0,z:0},controlledBy:0,controlledByTeam:0},kotc:{stage:CoopState.Capturing,zoneIdx:0,activeZone:[],teamCapturing:0,captureProgress:0,numCapturing:0,capturePercent:0}};this.#initialGame=JSON.parse(JSON.stringify(this.game));this.account={id:0,firebase:{idToken:"",refreshToken:"",expiresIn:"3600",localId:""},firebaseId:"",sessionId:"",session:0,email:"",password:"",cw:{atLimit:false,limit:0,secondsUntilPlay:0,canPlayAgain:Date.now()},loadout:{hatId:null,meleeId:0,stampId:null,classIdx:0,colorIdx:0,grenadeId:0,primaryId:[3100,3600,3400,3800,4000,4200,4500],secondaryId:new Array(7).fill(3000),stampPositionX:0,stampPositionY:0},ownedItemIds:[],vip:false,emailVerified:false,isAged:false,isCG:false,adminRoles:0,rawLoginData:{},eggBalance:0};this.#initialAccount=JSON.parse(JSON.stringify(this.account));this.api=new API({proxy:this.proxy,protocol:this.protocol,instance:this.instance,connectionTimeout:this.connectionTimeout});this.ping=0;this.lastPingTime=-1;this.lastDeathTime=-1;this.pathing={nodeList:null,activePath:null,activeNode:null,activeNodeIdx:0};this.hasQuit=false;if(this.intents.includes(Intents.NO_AFK_KICK))this.afkKickInterval=0;if(this.intents.includes(Intents.RENEW_SESSION))this.renewSessionInterval=0}dispatch(dispatch,isEmit){if(dispatch.validate(this)){if(dispatch.check(this))dispatch.execute(this);else this.#dispatches.push(dispatch);return true}this.errorLogger(`${isEmit?"emit":"dispatch"}: validation failed for dispatch ${dispatch.constructor.name}`);this.errorLogger("this means the dispatch will NEVER RUN!");this.errorLogger("make sure all parameters are valid and that any player IDs are in the game");return false}async createAccount(email,pass){this.account=this.#initialAccount;this.account.email=email;this.account.password=pass;const loginData=await this.api.createAccount(email,pass);return this.processLoginData(loginData)}async login(email,pass){this.account=this.#initialAccount;this.account.email=email;this.account.password=pass;const loginData=await this.api.loginWithCredentials(email,pass);return this.processLoginData(loginData)}async loginWithRefreshToken(refreshToken){this.account=this.#initialAccount;const loginData=await this.api.loginWithRefreshToken(refreshToken);return this.processLoginData(loginData)}async loginAnonymously(){this.account=this.#initialAccount;const loginData=await this.api.loginAnonymously();return this.processLoginData(loginData)}processLoginData(loginData){if(!loginData.ok||!loginData.playerOutput){this.$emit("authFail",loginData);if(loginData.ok&&!loginData.playerOutput)this.errorLogger("processLoginData: missing playerOutput but is ok",loginData);return{...loginData,error:LoginError.InternalError,ok:false}}if(loginData.banRemaining){this.$emit("banned",loginData.banRemaining);return createError(LoginError.AccountBanned)}this.account.firebase=loginData.firebase;const output=loginData.playerOutput;this.account.rawLoginData=output;this.account.adminRoles=output.adminRoles||0;this.account.eggBalance=output.currentBalance;this.account.emailVerified=output.emailVerified;this.account.firebaseId=output.firebaseId;this.account.id=output.id;this.account.isAged=new Date(output.dateCreated).getTime()<1714546800000;this.account.isCG=output.cgAccountStatus?.hasAccount;this.account.loadout=output.loadout;this.account.ownedItemIds=output.ownedItemIds;this.account.session=output.session;this.account.sessionId=output.sessionId;this.account.vip=output.active_sub==="IsVIP";if(this.intents.includes(Intents.BOT_STATS))this.account.stats={lifetime:output.statsLifetime,monthly:output.statsCurrent};if(this.intents.includes(Intents.CHALLENGES))this.#importChallenges(output.challenges);this.$emit("authSuccess",this.account);if(this.intents.includes(Intents.RENEW_SESSION)){this.renewSessionInterval=setInterval(async()=>{if(!this.account?.sessionId)return clearInterval(this.renewSessionInterval);const res=await this.api.queryServices({cmd:"renewSession",sessionId:this.account.sessionId});if(res.data!=="renewed")this.$emit("sessionExpired")},10*60*1000)}return{ok:true,account:this.account}}#importChallenges(challengeArray){this.account.challenges=[];for(const challengeData of challengeArray){const challengeInfo=Challenges.find((c)=>c.id===challengeData.challengeId);if(!challengeInfo)continue;delete challengeData.playerId;this.account.challenges.push({raw:{challengeInfo,challengeData},id:challengeData.challengeId,name:challengeInfo.loc.title,desc:challengeInfo.loc.desc,rewardEggs:challengeInfo.reward,isRerolled:!!challengeData.reset,isClaimed:!!challengeData.claimed,isCompleted:!!challengeData.completed,progressNum:challengeData.progress,goalNum:challengeInfo.goal})}}async createMatchmaker(){const matchmaker=new yolkws(`${this.protocol}://${this.instance}/matchmaker/`,{proxy:this.proxy,errorLogger:this.errorLogger});matchmaker.autoReconnect=true;const didConnect=await matchmaker.tryConnect();if(!didConnect)return createError(MatchmakerError.WebSocketConnectFail);this.matchmaker=matchmaker;const uuidTimeouts=[];this.matchmaker.onmessage=async(e)=>{const data=JSON.parse(e.data);if(data.command==="validateUUID"){const timeout=setTimeout(()=>{console.error("createMatchmaker: the matchmaker did not respond to our validateUUID");console.error("createMatchmaker: report this issue at https://yolkbot.xyz/community")},5000);uuidTimeouts.push(timeout);return this.matchmaker.send(JSON.stringify({command:"validateUUID",hash:await validate(data.uuid)}))}if(uuidTimeouts.length){uuidTimeouts.forEach((t)=>clearTimeout(t));uuidTimeouts.length=0}this.matchmakerListeners.forEach((listener)=>listener(data))};return{ok:true}}async getRegions(){if(!this.matchmaker){const mmConnection=await this.createMatchmaker();if(!mmConnection.ok)return mmConnection}return new Promise((res)=>{const listener=(data)=>{if(data.command==="regionList"){this.matchmakerListeners.splice(this.matchmakerListeners.indexOf(listener),1);this.regionList=data.regionList;res({ok:true,regionList:this.regionList})}};this.matchmakerListeners.push(listener);this.matchmaker.send(JSON.stringify({command:"regionList"}))})}async initSession(){if(!this.account.sessionId&&!this.intents.includes(Intents.SKIP_LOGIN)){const anonLogin=await this.loginAnonymously();if(!anonLogin.ok)return anonLogin}if(!this.matchmaker){const mmConnection=await this.createMatchmaker();if(!mmConnection.ok)return mmConnection}return{ok:true}}async findPublicGame(region,mode){if(typeof region!=="string")return createError(GameFindError.MissingParams);const regions=this.regionList.length?this.regionList:Regions;if(!regions.find((r)=>r.id===region))return createError(GameFindError.InvalidRegion);let computedModeId;if(typeof mode==="number"){if(Object.values(GameMode).indexOf(mode)>-1)computedModeId=mode;else return createError(GameFindError.InvalidMode)}else if(typeof mode==="string"){const modeEntry=Object.keys(GameMode).find((key)=>key.toLowerCase()===mode.toLowerCase());if(modeEntry)computedModeId=GameMode[modeEntry];else return createError(GameFindError.InvalidMode)}else return createError(GameFindError.InvalidMode);const initInfo=await this.initSession();if(!initInfo.ok)return initInfo;const game=await new Promise((resolve)=>{const listener=(msg)=>{if(msg.command==="notice")return;this.matchmakerListeners.splice(this.matchmakerListeners.indexOf(listener),1);if(msg.command==="gameFound"){if(msg.useAltGameURL)this.state.useAltGameURL=true;return resolve(msg)}if(msg.error==="sessionNotFound")return resolve(createError(GameFindError.SessionExpired));this.errorLogger("findPublicGame: unknown matchmaker response",JSON.stringify(msg));resolve(createError(GameFindError.InternalError))};this.matchmakerListeners.push(listener);this.matchmaker.send(JSON.stringify({command:"findGame",region,playType:PlayType.JoinPublic,gameType:computedModeId,sessionId:this.account.sessionId}))});if(!game.id)return game;return{ok:true,raw:game,id:game.id,uuid:game.uuid,region:game.region,private:game.private,subdomain:game.subdomain}}async createPrivateGame(region,mode,map){if(typeof region!=="string")return createError(GameFindError.MissingParams);const regions=this.regionList.length?this.regionList:Regions;if(!regions.find((r)=>r.id===region))return createError(GameFindError.InvalidRegion);let computedModeId;if(typeof mode==="number"){if(Object.values(GameMode).indexOf(mode)>-1)computedModeId=mode;else return createError(GameFindError.InvalidMode)}else if(typeof mode==="string"){const modeEntry=Object.keys(GameMode).find((key)=>key.toLowerCase()===mode.toLowerCase());if(modeEntry)computedModeId=GameMode[modeEntry];else return createError(GameFindError.InvalidMode)}else return createError(GameFindError.InvalidMode);const mapIdx=Maps.findIndex((m)=>m.name.toLowerCase()===map.toLowerCase());if(mapIdx===-1)return createError(GameFindError.InvalidMap);const initInfo=await this.initSession();if(!initInfo.ok)return initInfo;const game=await new Promise((resolve)=>{const listener=(msg)=>{if(msg.command==="notice")return;this.matchmakerListeners.splice(this.matchmakerListeners.indexOf(listener),1);if(msg.command==="gameFound"){if(msg.useAltGameURL)this.state.useAltGameURL=true;return resolve(msg)}if(msg.error==="sessionNotFound")return resolve(createError(GameFindError.SessionExpired));this.errorLogger("createPrivateGame: unknown matchmaker response",JSON.stringify(msg));resolve(createError(GameFindError.InternalError))};this.matchmakerListeners.push(listener);this.matchmaker.send(JSON.stringify({command:"findGame",region,playType:PlayType.CreatePrivate,gameType:computedModeId,sessionId:this.account.sessionId,noobLobby:false,map:mapIdx}))});if(!game.id)return game;return{ok:true,raw:game,id:game.id,uuid:game.uuid,region:game.region,private:game.private,subdomain:game.subdomain}}async join(name,data){this.state.name=name||"yolkbot";if(typeof data==="string"){if(data.includes("#"))data=data.split("#")[1];const initInfo=await this.initSession();if(!initInfo.ok)return initInfo;const joinResult=await new Promise((resolve)=>{const listener=(message)=>{if(message.command==="gameFound"){this.matchmakerListeners.splice(this.matchmakerListeners.indexOf(listener),1);this.game.raw=message;this.game.code=message.id;this.game.region=message.region;resolve(message.id)}if(message.error&&message.error==="gameNotFound"){this.matchmakerListeners.splice(this.matchmakerListeners.indexOf(listener),1);resolve("gameNotFound")}};this.matchmakerListeners.push(listener);this.matchmaker.send(JSON.stringify({command:"joinGame",id:data,observe:false,sessionId:this.account.sessionId}))});if(joinResult==="gameNotFound")return createError(GameJoinError.GameNotFound);if(!this.game.raw.id){this.errorLogger("join: invalid game data received from matchmaker:",this.game.raw);return createError(GameJoinError.InternalError)}}else if(typeof data==="object"){if(!data.id||!data.subdomain||!data.uuid||!data.region)return createError(GameJoinError.InvalidObject);if(this.account.id===0){const anonAttempt=await this.loginAnonymously();if(!anonAttempt.ok)return anonAttempt}this.game.raw=data;this.game.code=this.game.raw.id;this.game.region=this.game.raw.region}else return createError(GameJoinError.MissingParams);const host=this.state.useAltGameURL||this.instance==="proxy.yolkbot.xyz"?`${this.instance}/servers/${this.game.raw.subdomain}`:`${this.game.raw.subdomain}.${this.instance}`;this.game.socket=new yolkws(`${this.protocol}://${host}/game/${this.game.raw.id}`,{proxy:this.proxy,errorLogger:this.errorLogger});this.game.socket.binaryType="arraybuffer";this.game.socket.connectionTimeout=this.connectionTimeout;this.game.socket.onBeforeConnect=()=>{this.game.socket.onmessage=(msg)=>this.processPacket(msg.data);this.game.socket.onclose=(e)=>{if(this.state?.inGame){this.$emit("close",e.code);this.leave(-1)}}};const didConnect=await this.game.socket.tryConnect();if(!didConnect)return createError(GameJoinError.WebSocketConnectFail);return{ok:true}}#processMovement(ndx,ndy,ndz){this.state.onGround=Math.max(--this.state.onGround,0);this.me.position.x+=ndx;this.me.position.y+=ndy;this.me.position.z+=ndz;const thisBlockX=Math.floor(this.me.position.x);const thisBlockY=Math.floor(this.me.position.y);const thisBlockZ=Math.floor(this.me.position.z);const blockInMyHitbox=this.pathing.nodeList.at(thisBlockX,thisBlockY,thisBlockZ);if(blockInMyHitbox&&!blockInMyHitbox.isAir()){if(!this.climbing&&blockInMyHitbox.isLadder()){}}if(this.me.climbing){}else if(blockInMyHitbox&&blockInMyHitbox.isJumpPad()){this.me.position.y+=0.26;this.me.jumping=true;this.state.onGround=0}}#processPathfinding(){const pathLen=this.pathing.activePath.length;const lastNode=this.pathing.activePath[pathLen-1];const myPos=this.me.position;const myFloorY=Math.floor(myPos.y);const distToEndCenter=Math.hypot(myPos.x-lastNode.flatCenter.x,myPos.z-lastNode.flatCenter.z);if(distToEndCenter<0.3){this.pathing.activePath=null;this.pathing.activeNode=null;this.pathing.activeNodeIdx=0;this.dispatch(new MovementDispatch(0));this.$emit("pathfindComplete")}else{let shouldJump=false;if(this.pathing.activeNodeIdx<pathLen-1){const currentNode=this.pathing.activePath[this.pathing.activeNodeIdx];const nextNode=this.pathing.activePath[this.pathing.activeNodeIdx+1];const dx=Math.abs(currentNode.x-nextNode.x);const dz=Math.abs(currentNode.z-nextNode.z);const isParkourJump=dx===2&&dz===0||dx===0&&dz===2||dx===2&&dz===1||dx===1&&dz===2||dx===2&&dz===2;if(isParkourJump){const localX=myPos.x-Math.floor(myPos.x);const localZ=myPos.z-Math.floor(myPos.z);const headingX=nextNode.x>currentNode.x?1:nextNode.x<currentNode.x?-1:0;const headingZ=nextNode.z>currentNode.z?1:nextNode.z<currentNode.z?-1:0;let distToEdge=0;if(headingX>0)distToEdge=1-localX;else if(headingX<0)distToEdge=localX;else if(headingZ>0)distToEdge=1-localZ;else if(headingZ<0)distToEdge=localZ;if(distToEdge<=0.04)shouldJump=true}}if(this.pathing.activeNodeIdx<pathLen){const positionTarget=this.pathing.activePath[this.pathing.activeNodeIdx].flatCenter;if(this.state.onGround)this.dispatch(new LookAtPosDispatch(positionTarget))}for(let i=this.pathing.activeNodeIdx;i<pathLen;i++){const node=this.pathing.activePath[i];if(node.flatRadialDistance(myPos)<0.1&&node.y===myFloorY){this.pathing.activeNodeIdx=i+1;this.pathing.activeNode=this.pathing.activePath[this.pathing.activeNodeIdx];break}}const movementKeys=Movement.Forward|(shouldJump?Movement.Jump:0);this.dispatch(new MovementDispatch(movementKeys))}}update(){if(this.hasQuit)return;if(this.pathing.activePath&&this.intents.includes(Intents.PATHFINDING))this.#processPathfinding();for(let i=this.#dispatches.length-1;i>=0;i--){const disp=this.#dispatches[i];if(disp.check(this)){disp.execute(this);this.#dispatches.splice(i,1)}}this.state.chatLines=Math.max(0,this.state.chatLines-1/(30*4));if(this.me.playing){const currentIdx=this.state.stateIdx;const isBufferDebug=this.intents.includes(Intents.DEBUG_BUFFER);if(isBufferDebug)console.log("setting buffer for idx",currentIdx);this.me.jumping=!!(this.state.controlKeys&Movement.Jump);this.state.buffer[currentIdx]={controlKeys:this.state.controlKeys,yaw:this.state.yaw,pitch:this.state.pitch,shotsFired:this.state.shotsFired,position:isBufferDebug?{...this.me.position}:{}};this.state.shotsFired=0;if(this.intents.includes(Intents.SIMULATION)){let dx=0;let dy=0;let dz=0;const state=this.state.buffer[currentIdx];if(state.controlKeys&Movement.Left){dx-=Math.cos(state.yaw);dz+=Math.sin(state.yaw)}if(state.controlKeys&Movement.Right){dx+=Math.cos(state.yaw);dz-=Math.sin(state.yaw)}if(state.controlKeys&Movement.Forward){if(this.me.climbing)dy+=1;else{dx-=Math.sin(state.yaw);dz-=Math.cos(state.yaw)}}if(state.controlKeys&Movement.Backward){if(this.me.climbing)dy-=1;else{dx+=Math.sin(state.yaw);dz+=Math.cos(state.yaw)}}if(state.controlKeys&Movement.Jump)this.state.controlKeys^=this.state.controlKeys&Movement.Jump;if(this.me.climbing){this.me.jumping=false;dy+=dy*0.028;this.me.position.y+=dy;dy*=0.5;this.#processMovement(0,dy,0)}else{const mag=Math.sqrt(dx*dx+dy*dy+dz*dz);if(mag>0){const normDx=dx/mag;const normDz=dz/mag;dx=this.state.dx+normDx*0.025;dz=this.state.dz+normDz*0.025}else{dx=this.state.dx;dz=this.state.dz}dy=this.state.dy-this.game.options.gravity*0.012;dy=Math.max(-0.29,dy);const oldX=this.me.position.x;const oldY=this.me.position.y;const oldZ=this.me.position.z;this.#processMovement(dx,dy,dz);dx=this.me.position.x-oldX;dy=this.me.position.y-oldY;dz=this.me.position.z-oldZ;if(this.state.onGround&&dy>0){const normY=mag===0?0:dy/mag;const n=1-normY*0.5;dx*=n;dz*=n}}dx*=0.64;dz*=0.64;if(this.me.climbing)dy*=0.64;this.state.dx=dx;this.state.dy=dy;this.state.dz=dz}if(this.state.stateIdx%FramesBetweenSyncs===0){this.$emit("tick");const out=new CommOut;out.packInt8(CommCode.syncMe);out.packInt8(this.state.stateIdx);out.packInt8(this.state.serverStateIdx);const startIdx=mod(this.state.stateIdx-FramesBetweenSyncs+1,StateBufferSize);if(isBufferDebug)console.log("--- START THIS SYNC LOGGING ---");for(let i=0;i<FramesBetweenSyncs;i++){const idx=mod(startIdx+i,StateBufferSize);const frame=this.state.buffer[idx]||{};const keys=frame.controlKeys||0;const shots=frame.shotsFired||0;const yaw=frame.yaw??this.state.yaw;const pitch=frame.pitch??this.state.pitch;if(isBufferDebug)console.log("adding",this.state.stateIdx,startIdx,idx,frame);out.packInt8(keys);out.packInt8(shots);out.packString(coords(yaw,pitch));out.packInt8(100)}out.send(this.game.socket);if(isBufferDebug)console.log("--- END SYNC LOGGING ---");this.state.buffer=[]}this.state.stateIdx=mod(this.state.stateIdx+1,StateBufferSize)}if(!this.intents.includes(Intents.PLAYER_HEALTH))return;const regen=0.1*(this.game.isPrivate?this.game.options.healthRegen:1);const players=Object.values(this.players);for(let i=0;i<players.length;i++){const player=players[i];if(player.playing&&player.hp>0){const hasOverHeal=player.streakRewards.includes(ShellStreak.OverHeal);player.hp+=hasOverHeal?-regen:regen;player.hp=hasOverHeal?Math.max(100,player.hp):Math.min(100,player.hp)}if(player.spawnShield>0)player.spawnShield-=6}}on(event,cb){if(Object.keys(this.#hooks).includes(event))this.#hooks[event].push(cb);else this.#hooks[event]=[cb]}onAny(cb){this.#globalHooks.push(cb)}off(event,cb){if(cb)this.#hooks[event]=this.#hooks[event].filter((hook)=>hook!==cb);else this.#hooks[event]=[]}$emit(event,...args){if(this.hasQuit)return;if(this.#hooks[event])for(const cb of this.#hooks[event])cb(...args);for(const cb of this.#globalHooks)cb(event,...args)}emit(event,...args){const dispatch=DispatchIndex[event];if(dispatch)this.dispatch(new dispatch(...args),true);else throw new Error(`no event found for "${event}"`)}updateGameOptions(){const out=new CommOut;out.packInt8(CommCode.gameOptions);out.packInt8(this.game.options.gravity*4);out.packInt8(this.game.options.damage*4);out.packInt8(this.game.options.healthRegen*4);const flags=(this.game.options.locked?1:0)|(this.game.options.noTeamChange?2:0)|(this.game.options.noTeamShuffle?4:0);out.packInt8(flags);this.game.options.weaponsDisabled.forEach((v)=>out.packInt8(v?1:0));out.send(this.game.socket)}packetHandlers={[CommCode.syncThem]:processSyncThemPacket.bind(null,this),[CommCode.fire]:processFirePacket.bind(null,this),[CommCode.hitThem]:processHitThemPacket.bind(null,this),[CommCode.syncMe]:processSyncMePacket.bind(null,this),[CommCode.hitMe]:processHitMePacket.bind(null,this),[CommCode.swapWeapon]:processSwapWeaponPacket.bind(null,this),[CommCode.collectItem]:processCollectItemPacket.bind(null,this),[CommCode.respawn]:processRespawnPacket.bind(null,this),[CommCode.die]:processDiePacket.bind(null,this),[CommCode.pause]:processPausePacket.bind(null,this),[CommCode.chat]:processChatPacket.bind(null,this),[CommCode.addPlayer]:processAddPlayerPacket.bind(null,this),[CommCode.removePlayer]:processRemovePlayerPacket.bind(null,this),[CommCode.eventModifier]:processEventModifierPacket.bind(null,this),[CommCode.metaGameState]:processMetaGameStatePacket.bind(null,this),[CommCode.beginShellStreak]:processBeginShellStreakPacket.bind(null,this),[CommCode.endShellStreak]:processEndShellStreakPacket.bind(null,this),[CommCode.hitMeHardBoiled]:processHitMeHardBoiledPacket.bind(null,this),[CommCode.gameOptions]:processGameOptionsPacket.bind(null,this),[CommCode.ping]:processPingPacket.bind(null,this),[CommCode.switchTeam]:processSwitchTeamPacket.bind(null,this),[CommCode.changeCharacter]:processChangeCharacterPacket.bind(null,this),[CommCode.reload]:processReloadPacket.bind(null,this),[CommCode.explode]:processExplodePacket.bind(null,this),[CommCode.throwGrenade]:processThrowGrenadePacket.bind(null,this),[CommCode.spawnItem]:processSpawnItemPacket.bind(null,this),[CommCode.melee]:processMeleePacket.bind(null,this),[CommCode.updateBalance]:processUpdateBalancePacket.bind(null,this),[CommCode.challengeCompleted]:processChallengeCompletedPacket.bind(null,this),[CommCode.gameAction]:processGameActionPacket.bind(null,this),[CommCode.socketReady]:processSocketReadyPacket.bind(null,this),[CommCode.gameJoined]:processGameJoinedPacket.bind(null,this),[CommCode.playerInfo]:processPlayerInfoPacket.bind(null,this),[CommCode.respawnDenied]:()=>{this.me.playing=false;this.$emit("respawnDenied")},[CommCode.requestGameOptions]:()=>{this.game.isPrivate=true;this.updateGameOptions()},[CommCode.expireUpgrade]:()=>{},[CommCode.clientReady]:()=>{},[CommCode.musicInfo]:()=>CommIn.unPackLongString()};processPacket(packet){if(this.hasQuit)return;CommIn.init(packet);if(this.intents.includes(Intents.PACKET_HOOK))this.$emit("packet",packet);let lastCommand=0;let lastCode=0;while(CommIn.isMoreDataAvailable()){const cmd=CommIn.unPackInt8U();const handler=this.packetHandlers[cmd];if(handler)handler();else{console.error(`processPacket: got but couldn't identify: ${Object.keys(CommCode).find((k)=>CommCode[k]===cmd)} ${cmd}`);if(lastCommand)console.error(`processPacket: it may be a result of the ${lastCommand} command (${lastCode}).`);break}lastCommand=Object.keys(CommCode).find((k)=>CommCode[k]===cmd);lastCode=cmd;if(this.intents.includes(Intents.LOG_PACKETS))console.log(`[LOG_PACKETS] packet ${lastCommand}: ${lastCode}`)}}async checkChiknWinner(){const response=await this.api.queryServices({cmd:"chicknWinnerReady",id:this.account.id,sessionId:this.account.sessionId});if(!response.ok)return response;this.account.cw.limit=response.limit;this.account.cw.atLimit=response.limit>=4;this.account.cw.secondsUntilPlay=(this.account.cw.atLimit?response.period:response.span)||0;this.account.cw.canPlayAgain=Date.now()+this.account.cw.secondsUntilPlay*1000;return{ok:true,cw:this.account.cw}}async playChiknWinner(doPrematureCooldownCheck=true){if(this.account.cw.atLimit||this.account.cw.limit>ChiknWinnerDailyLimit)return createError(ChicknWinnerError.HitDailyLimit);if(this.account.cw.canPlayAgain>Date.now()&&doPrematureCooldownCheck)return createError(ChicknWinnerError.OnCooldown);const response=await this.api.queryServices({cmd:"incentivizedVideoReward",firebaseId:this.account.firebaseId,id:this.account.id,sessionId:this.account.sessionId,token:null});if(!response.ok)return response;if(response.error){if(response.error==="RATELIMITED"||response.error==="RATELMITED"){await this.checkChiknWinner();return createError(ChicknWinnerError.OnCooldown)}else if(response.error==="SESSION_EXPIRED"){this.$emit("sessionExpired");return createError(ChicknWinnerError.SessionExpired)}this.errorLogger("unknown Chikn Winner response:",response);this.errorLogger("report this issue at https://yolkbot.xyz/community");return createError(ChicknWinnerError.InternalError)}if(response.reward){this.account.eggBalance+=response.reward.eggsGiven;response.reward.itemIds.forEach((id)=>this.account.ownedItemIds.push(id));await this.checkChiknWinner();return{ok:true,...response.reward}}this.errorLogger("unknown Chikn Winner response:",response);this.errorLogger("report this issue at https://yolkbot.xyz/community");return createError(ChicknWinnerError.InternalError)}async resetChiknWinner(){if(this.account.eggBalance<200)return createError(ChicknWinnerError.NotEnoughResetEggs);if(!this.account.cw.atLimit)return createError(ChicknWinnerError.NotAtDailyLimit);const response=await this.api.queryServices({cmd:"chwReset",sessionId:this.account.sessionId});if(!response.ok)return response;if(response.result!=="SUCCESS"){this.errorLogger("unknown Chikn Winner reset response:",response);this.errorLogger("report this issue at https://yolkbot.xyz/community");return createError(ChicknWinnerError.InternalError)}this.account.eggBalance-=200;await this.checkChiknWinner();return{ok:true,cw:this.account.cw}}canSee(target){if(!this.intents.includes(Intents.PATHFINDING))throw new Error("canSee: you need enable the PATHFINDING intent");return this.pathing.nodeList.hasLineOfSight(this.me.position,target.position)}getBestTarget(){const options=Object.values(this.players).filter((player)=>player?.playing).filter((player)=>player.hp>0).filter((player)=>player.id!==this.me.id).filter((player)=>this.me.team===0||player.team!==this.me.team);const distancePlayers=options.map((player)=>({player,distance:(player.position.x-this.me.position.x)**2+5*(player.position.y-this.me.position.y)**2+(player.position.z-this.me.position.z)**2})).sort((a,b)=>a.distance-b.distance);if(distancePlayers.length)return distancePlayers[0].player;else return null}async refreshChallenges(){const result=await this.api.queryServices({cmd:"challengeGetDaily",sessionId:this.account.sessionId,playerId:this.account.id});if(!result.ok)return result;this.#importChallenges(result);return{ok:true,challenges:this.account.challenges}}async rerollChallenge(challengeId){const result=await this.api.queryServices({cmd:"challengeRerollSlot",sessionId:this.account.sessionId,slotId:challengeId});if(!result.ok)return result;if(result["0"]){this.#importChallenges(Object.values(result));return{ok:true,challenges:this.account.challenges}}const isInEnum=Object.values(ChallengeRerollError).includes(result.error);if(isInEnum)return{ok:false,error:result.error};this.errorLogger("rerollChallenge: unknown error response",result);return{ok:false,error:ChallengeRerollError.InternalError}}async claimChallenge(challengeId){const result=await this.api.queryServices({cmd:"challengeClaimReward",sessionId:this.account.sessionId,slotId:challengeId});if(!result.ok)return result;if(result.reward){this.#importChallenges(result.challenges);if(result.reward>0)this.account.eggBalance+=result.reward;return{ok:true,eggReward:result.reward,updatedChallenges:this.account.challenges}}const isInEnum=Object.values(ChallengeClaimError).includes(result.error);if(isInEnum)return{ok:false,error:result.error};this.errorLogger("claimChallenge: unknown error response",result);return{ok:false,error:ChallengeClaimError.InternalError}}async refreshBalance(){const result=await this.api.queryServices({cmd:"checkBalance",firebaseId:this.account.firebaseId,sessionId:this.account.sessionId});if(!result.ok)return result;this.account.eggBalance=result.currentBalance;return{ok:true,currentBalance:result.currentBalance}}async redeemCode(code){const result=await this.api.queryServices({cmd:"redeem",firebaseId:this.account.firebaseId,sessionId:this.account.sessionId,id:this.account.id,code});if(!result.ok)return result;if(result.result==="SUCCESS"){this.account.eggBalance+=result.eggs_given;result.item_ids.forEach((id)=>this.account.ownedItemIds.push(id));return{ok:true,eggsGiven:result.eggs_given,itemIds:result.item_ids}}const isInEnum=Object.values(RedeemCodeError).includes(result.result);if(isInEnum)return{ok:false,error:result.result};this.errorLogger("redeemCode: unknown error response",result);return{ok:false,error:RedeemCodeError.InternalError}}async claimURLReward(reward){const result=await this.api.queryServices({cmd:"urlRewardParams",firebaseId:this.account.firebaseId,sessionId:this.account.sessionId,reward});if(!result.ok)return result;if(result.result==="SUCCESS"){this.account.eggBalance+=result.eggsGiven;result.itemIds.forEach((id)=>this.account.ownedItemIds.push(id));return{ok:true,eggsGiven:result.eggsGiven,itemIds:result.itemIds}}const isInEnum=Object.values(ClaimURLError).includes(result.result);if(isInEnum)return{ok:false,error:result.result};this.errorLogger("claimURLReward: unknown error response",result);return{ok:false,error:ClaimURLError.InternalError}}async claimSocialReward(rewardTag){const result=await this.api.queryServices({cmd:"reward",firebaseId:this.account.firebaseId,sessionId:this.account.sessionId,rewardTag});if(!result.ok)return result;if(result.result==="SUCCESS"){this.account.eggBalance+=result.eggsGiven;result.itemIds.forEach((id)=>this.account.ownedItemIds.push(id));return{ok:true,eggsGiven:result.eggsGiven,itemIds:result.itemIds}}const isInEnum=Object.values(ClaimSocialError).includes(result.result);if(isInEnum)return{ok:false,error:result.result};this.errorLogger("claimSocialReward: unknown error response",result);return{ok:false,error:ClaimSocialError.InternalError}}async buyItem(itemId){const result=await this.api.queryServices({cmd:"buy",firebaseId:this.account.firebaseId,sessionId:this.account.sessionId,itemId,save:true});if(!result.ok)return result;if(result.result==="SUCCESS"){this.account.eggBalance=result.currentBalance;this.account.ownedItemIds.push(result.itemId);return{ok:true,itemId:result.itemId,currentBalance:result.currentBalance}}const isInEnum=Object.values(BuyItemError).includes(result.result);if(isInEnum)return{ok:false,error:result.result};this.errorLogger("buyItem: unknown error response",result);return{ok:false,error:BuyItemError.InternalError}}leave(code=CloseCode.mainMenu){if(this.hasQuit)return;this.state.inGame=false;if(code>-1){this.game?.socket?.close(code);this.$emit("leave",code)}if(this.updateIntervalId)clearInterval(this.updateIntervalId);if(this.pingTimeoutId)clearTimeout(this.pingTimeoutId);this.#dispatches=[];this.state.chatLines=0;this.state.reloading=false;this.state.swappingGun=false;this.state.usingMelee=false;this.state.stateIdx=0;this.state.serverStateIdx=0;this.state.shotsFired=0;this.state.buffer=[];this.players={};this.me=new GamePlayer({});this.game=this.#initialGame;this.ping=0;this.lastPingTime=-1;this.lastDeathTime=-1;this.pathing={nodeList:null,activePath:null,activeNode:null,activeNodeIdx:0}}logout(){this.account=this.#initialAccount;if(this.intents.includes(Intents.RENEW_SESSION))clearInterval(this.renewSessionInterval)}quit(cleanupLevel=CleanupLevel.Partial){if(this.hasQuit)return;this.hasQuit=true;this.leave();if(this.matchmaker){try{this.matchmaker.close()}catch{}if(cleanupLevel>=CleanupLevel.Partial)this.matchmaker=null}if(this.intents.includes(Intents.NO_AFK_KICK))clearInterval(this.afkKickInterval);if(this.intents.includes(Intents.RENEW_SESSION))clearInterval(this.renewSessionInterval);if(this.pingTimeoutId)clearTimeout(this.pingTimeoutId);if(cleanupLevel>=CleanupLevel.Partial){this.api=null;this.packetHandlers=null;this.pathing=null;this.#initialAccount={};this.#initialGame={};this.#hooks={};this.#globalHooks=[];this.#dispatches=[];this.matchmakerListeners=[];if(cleanupLevel>=CleanupLevel.Full){this.account=null;this.game=null;this.me=null;this.players=null;this.state=null}}}}export default Bot;