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.
- package/browser/build/global.js +1 -1
- package/browser/build/module.js +1 -1
- package/dist/api.js +1 -244
- package/dist/bot/GamePlayer.js +1 -84
- package/dist/bot.js +1 -1226
- package/dist/comm/CommIn.js +1 -76
- package/dist/comm/CommOut.js +1 -60
- package/dist/constants/CloseCode.js +1 -27
- package/dist/constants/CommCode.js +1 -53
- package/dist/constants/challenges.js +1 -1821
- package/dist/constants/findItemById.js +1 -3
- package/dist/constants/guns.js +1 -155
- package/dist/constants/index.js +1 -141
- package/dist/constants/items.js +1 -45979
- package/dist/constants/maps.js +1 -1289
- package/dist/constants/regions.js +1 -30
- package/dist/dispatches/BanPlayerDispatch.js +1 -32
- package/dist/dispatches/BootPlayerDispatch.js +1 -21
- package/dist/dispatches/ChatDispatch.js +1 -32
- package/dist/dispatches/FireDispatch.js +1 -15
- package/dist/dispatches/GameOptionsDispatch.js +1 -66
- package/dist/dispatches/GoToAmmoDispatch.js +1 -37
- package/dist/dispatches/GoToCoopDispatch.js +1 -37
- package/dist/dispatches/GoToGrenadeDispatch.js +1 -37
- package/dist/dispatches/GoToPlayerDispatch.js +1 -34
- package/dist/dispatches/GoToSpatulaDispatch.js +1 -23
- package/dist/dispatches/LookAtDispatch.js +1 -37
- package/dist/dispatches/LookAtPosDispatch.js +1 -29
- package/dist/dispatches/MeleeDispatch.js +1 -26
- package/dist/dispatches/MovementDispatch.js +1 -26
- package/dist/dispatches/PauseDispatch.js +1 -18
- package/dist/dispatches/ReloadDispatch.js +1 -35
- package/dist/dispatches/ReportPlayerDispatch.js +1 -44
- package/dist/dispatches/ResetGameDispatch.js +1 -19
- package/dist/dispatches/SaveLoadoutDispatch.js +1 -106
- package/dist/dispatches/SpawnDispatch.js +1 -26
- package/dist/dispatches/SwapWeaponDispatch.js +1 -25
- package/dist/dispatches/SwitchTeamDispatch.js +1 -34
- package/dist/dispatches/ThrowGrenadeDispatch.js +1 -21
- package/dist/dispatches/index.js +1 -73
- package/dist/enums.js +1 -114
- package/dist/env/fetch.js +8 -129
- package/dist/env/globals.js +1 -17
- package/dist/index.js +1 -22
- package/dist/packets/addPlayer.js +1 -63
- package/dist/packets/beginShellStreak.js +1 -44
- package/dist/packets/challengeCompleted.js +1 -16
- package/dist/packets/changeCharacter.js +1 -46
- package/dist/packets/chat.js +1 -10
- package/dist/packets/collectItem.js +1 -26
- package/dist/packets/die.js +1 -40
- package/dist/packets/endShellStreak.js +1 -21
- package/dist/packets/eventModifier.js +1 -8
- package/dist/packets/explode.js +1 -19
- package/dist/packets/fire.js +1 -21
- package/dist/packets/gameAction.js +1 -33
- package/dist/packets/gameJoined.js +1 -64
- package/dist/packets/gameOptions.js +1 -27
- package/dist/packets/hitMe.js +1 -12
- package/dist/packets/hitMeHardBoiled.js +1 -18
- package/dist/packets/hitThem.js +1 -12
- package/dist/packets/melee.js +1 -8
- package/dist/packets/metaGameState.js +1 -62
- package/dist/packets/pause.js +1 -17
- package/dist/packets/ping.js +1 -19
- package/dist/packets/playerInfo.js +1 -15
- package/dist/packets/reload.js +1 -17
- package/dist/packets/removePlayer.js +1 -10
- package/dist/packets/respawn.js +1 -31
- package/dist/packets/socketReady.js +1 -16
- package/dist/packets/spawnItem.js +1 -11
- package/dist/packets/swapWeapon.js +1 -11
- package/dist/packets/switchTeam.js +1 -13
- package/dist/packets/syncMe.js +1 -25
- package/dist/packets/syncThem.js +1 -63
- package/dist/packets/throwGrenade.js +1 -17
- package/dist/packets/updateBalance.js +1 -8
- package/dist/pathing/astar.js +1 -68
- package/dist/pathing/mapnode.js +1 -284
- package/dist/socket.js +1 -107
- package/dist/util.js +1 -128
- package/dist/wasm/bytes.js +1 -8
- package/dist/wasm/direct.js +1 -84
- package/dist/wasm/legacy.js +1 -176
- 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;
|