UNPKG

@fnlb-project/fnbr

Version:

A library to interact with Epic Games' Fortnite HTTP and XMPP services

640 lines 29.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const tslib_1 = require("tslib"); const async_queue_1 = require("@sapphire/async-queue"); const Endpoints_1 = tslib_1.__importDefault(require("../../../resources/Endpoints")); const ClientPartyMemberMeta_1 = tslib_1.__importDefault(require("./ClientPartyMemberMeta")); const PartyMember_1 = tslib_1.__importDefault(require("./PartyMember")); const enums_1 = require("../../../resources/enums"); const EpicgamesAPIError_1 = tslib_1.__importDefault(require("../../exceptions/EpicgamesAPIError")); /** * Represents the client's party member */ class ClientPartyMember extends PartyMember_1.default { /** * @param party The party this member belongs to * @param data The member data */ constructor(party, data) { super(party, data); this.meta = new ClientPartyMemberMeta_1.default(this, data.meta); this.patchQueue = new async_queue_1.AsyncQueue(); this.update({ id: this.id, displayName: this.client.user.self.displayName, externalAuths: this.client.user.self.externalAuths }); if (this.client.lastPartyMemberMeta) this.meta.update(this.client.lastPartyMemberMeta, true); } /** * Sends a meta patch to Epicgames's servers * @param updated The updated schema * @throws {EpicgamesAPIError} */ async sendPatch(updated) { await this.patchQueue.wait(); try { await this.client.http.epicgamesRequest({ method: 'PATCH', url: `${Endpoints_1.default.BR_PARTY}/parties/${this.party.id}/members/${this.id}/meta`, headers: { 'Content-Type': 'application/json', }, data: { delete: [], revision: this.revision, update: updated, }, }, enums_1.AuthSessionStoreKey.Fortnite); } catch (e) { if (e instanceof EpicgamesAPIError_1.default && e.code === 'errors.com.epicgames.social.party.stale_revision') { this.revision = parseInt(e.messageVars[1], 10); this.patchQueue.shift(); return this.sendPatch(updated); } this.patchQueue.shift(); throw e; } this.revision += 1; this.patchQueue.shift(); if (this.client.config.savePartyMemberMeta) this.client.lastPartyMemberMeta = this.meta.schema; return undefined; } /** * Updates the client party member's readiness * @param ready Whether the client party member is ready * @throws {EpicgamesAPIError} */ async setReadiness(ready) { let data = this.meta.get('Default:MatchmakingInfo_j'); data = this.meta.set('Default:MatchmakingInfo_j', { ...data, MatchmakingInfo: { ...data.MatchmakingInfo, readyStatus: ready ? 'Ready' : 'NotReady', readyInputType: ready ? 'Touch' : 'Count', }, }); await this.sendPatch({ 'Default:MatchmakingInfo_j': data, }); } /** * Updates the client party member's sitting out state * @param sittingOut Whether the client party member is sitting out * @throws {EpicgamesAPIError} */ async setSittingOut(sittingOut) { let data = this.meta.get('Default:MatchmakingInfo_j'); data = this.meta.set('Default:MatchmakingInfo_j', { ...data, MatchmakingInfo: { ...data.MatchmakingInfo, readyStatus: sittingOut ? 'SittingOut' : 'NotReady', readyInputType: 'Count', }, }); await this.sendPatch({ 'Default:MatchmakingInfo_j': data, }); } /** * Updates the client party member's level * @param level The new level * @throws {EpicgamesAPIError} */ async setLevel(level) { let data = this.meta.get('Default:AthenaBannerInfo_j'); data = this.meta.set('Default:AthenaBannerInfo_j', { ...data, AthenaBannerInfo: { ...data.AthenaBannerInfo, seasonLevel: level, }, }); await this.sendPatch({ 'Default:AthenaBannerInfo_j': data, }); } /** * Updates the client party member's battle pass info * @param isPurchased Whether the battle pass is purchased * @param level The battle pass level * @param selfBoost The battle pass self boost percentage * @param friendBoost The battle pass friend boost percentage * @throws {EpicgamesAPIError} */ async setBattlePass(isPurchased, level, selfBoost, friendBoost) { let data = this.meta.get('Default:BattlePassInfo_j'); data = this.meta.set('Default:BattlePassInfo_j', { ...data, BattlePassInfo: { ...data.BattlePassInfo, bHasPurchasedPass: typeof isPurchased === 'boolean' ? isPurchased : data.BattlePassInfo.bHasPurchasedPass, passLevel: typeof level === 'number' ? level : data.BattlePassInfo.passLevel, selfBoostXp: typeof selfBoost === 'number' ? selfBoost : data.BattlePassInfo.selfBoostXp, friendBoostXp: typeof friendBoost === 'number' ? friendBoost : data.BattlePassInfo.friendBoostXp, }, }); await this.sendPatch({ 'Default:BattlePassInfo_j': data, }); } /** * Updates the client party member's banner * @param bannerId The new banner's id * @param color The new banner's color * @throws {EpicgamesAPIError} */ async setBanner(bannerId, color) { let data = this.meta.get('Default:AthenaBannerInfo_j'); data = this.meta.set('Default:AthenaBannerInfo_j', { ...data, AthenaBannerInfo: { ...data.AthenaBannerInfo, bannerIconId: bannerId, bannerColorId: color, }, }); await this.sendPatch({ 'Default:AthenaBannerInfo_j': data, }); } /** * Updates multiple cosmetics for the client party member. * If a cosmetic is set to `null`, it will be cleared. * If a cosmetic is set to `undefined` or not provided, it will remain unchanged. * * @param cosmetics An object specifying the cosmetics to update. * @throws {EpicgamesAPIError} */ async setCosmetics({ outfit, backpack, pickaxe, shoes, sidekick, } = {}) { var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12; const patches = {}; let data = this.meta.get('Default:AthenaCosmeticLoadout_j'); let variantData = this.meta.get('Default:AthenaCosmeticLoadoutVariants_j'); let mpData = this.meta.get('Default:MpLoadout_j'); let mpDataObj = mpData || {}; if (!mpDataObj.MpLoadout) { mpDataObj.MpLoadout = { d: JSON.stringify({}) }; } else if (!mpDataObj.MpLoadout.d) { mpDataObj.MpLoadout.d = JSON.stringify({}); } if (outfit) { const mpLoadoutD = JSON.parse(mpDataObj.MpLoadout.d || '{}'); mpDataObj = { ...mpDataObj, MpLoadout: { ...mpDataObj.MpLoadout, d: JSON.stringify({ ...mpLoadoutD, ac: { i: outfit.id, v: (_b = (_a = outfit.variants) === null || _a === void 0 ? void 0 : _a.map(() => 0)) !== null && _b !== void 0 ? _b : [], }, }), }, }; patches['Default:MpLoadout_j'] = this.meta.set('Default:MpLoadout_j', mpDataObj); const parsedVariants = { athenaCharacter: { i: (_d = (_c = outfit.variants) === null || _c === void 0 ? void 0 : _c.map((v, i) => `${i}|${v.variantIndex}`)) !== null && _d !== void 0 ? _d : [], }, }; const scratchpad = []; if (((_e = outfit.enlightment) === null || _e === void 0 ? void 0 : _e.length) === 2) { scratchpad.push({ t: outfit.enlightment[0], v: outfit.enlightment[1], }); } data = { ...data, AthenaCosmeticLoadout: { ...((data === null || data === void 0 ? void 0 : data.AthenaCosmeticLoadout) || {}), characterPrimaryAssetId: `AthenaCharacter:${outfit.id}`, scratchpad, }, }; patches['Default:AthenaCosmeticLoadout_j'] = this.meta.set('Default:AthenaCosmeticLoadout_j', data); (_g = (_f = variantData === null || variantData === void 0 ? void 0 : variantData.AthenaCosmeticLoadoutVariants) === null || _f === void 0 ? void 0 : _f.vD) === null || _g === void 0 ? true : delete _g.athenaCharacter; if ((_h = parsedVariants.athenaCharacter) === null || _h === void 0 ? void 0 : _h.i[0]) { variantData = { ...variantData, AthenaCosmeticLoadoutVariants: { ...(variantData === null || variantData === void 0 ? void 0 : variantData.AthenaCosmeticLoadoutVariants) || {}, vD: { ...((_j = variantData === null || variantData === void 0 ? void 0 : variantData.AthenaCosmeticLoadoutVariants) === null || _j === void 0 ? void 0 : _j.vD) || {}, ...parsedVariants, }, }, }; patches['Default:AthenaCosmeticLoadoutVariants_j'] = this.meta.set('Default:AthenaCosmeticLoadoutVariants_j', variantData); } } if (backpack !== undefined) { const mpLoadoutD = JSON.parse(mpDataObj.MpLoadout.d || '{}'); mpDataObj = { ...mpDataObj, MpLoadout: { ...mpDataObj.MpLoadout, d: JSON.stringify({ ...mpLoadoutD, ab: backpack ? { i: backpack.id, v: (_l = (_k = backpack.variants) === null || _k === void 0 ? void 0 : _k.map(() => 0)) !== null && _l !== void 0 ? _l : [], } : undefined, }), }, }; patches['Default:MpLoadout_j'] = this.meta.set('Default:MpLoadout_j', mpDataObj); if (backpack === null) { data = { ...data, AthenaCosmeticLoadout: { ...((data === null || data === void 0 ? void 0 : data.AthenaCosmeticLoadout) || {}), backpackDef: '', }, }; patches['Default:AthenaCosmeticLoadout_j'] = this.meta.set('Default:AthenaCosmeticLoadout_j', data); } else { const parsedVariants = { athenaBackpack: { i: (_o = (_m = backpack.variants) === null || _m === void 0 ? void 0 : _m.map((v, i) => `${i}|${v.variantIndex}`)) !== null && _o !== void 0 ? _o : [], }, }; data = { ...data, AthenaCosmeticLoadout: { ...((data === null || data === void 0 ? void 0 : data.AthenaCosmeticLoadout) || {}), backpackDef: `${(_q = (_p = backpack.path) === null || _p === void 0 ? void 0 : _p.replace(/\/$/, '')) !== null && _q !== void 0 ? _q : '/BRCosmetics/Athena/Items/Cosmetics/Backpacks'}/${backpack.id}.${backpack.id}`, }, }; patches['Default:AthenaCosmeticLoadout_j'] = this.meta.set('Default:AthenaCosmeticLoadout_j', data); (_s = (_r = variantData === null || variantData === void 0 ? void 0 : variantData.AthenaCosmeticLoadoutVariants) === null || _r === void 0 ? void 0 : _r.vD) === null || _s === void 0 ? true : delete _s.athenaBackpack; if ((_t = parsedVariants.athenaBackpack) === null || _t === void 0 ? void 0 : _t.i[0]) { variantData = { ...variantData, AthenaCosmeticLoadoutVariants: { ...(variantData === null || variantData === void 0 ? void 0 : variantData.AthenaCosmeticLoadoutVariants) || {}, vD: { ...((_u = variantData === null || variantData === void 0 ? void 0 : variantData.AthenaCosmeticLoadoutVariants) === null || _u === void 0 ? void 0 : _u.vD) || {}, ...parsedVariants, }, }, }; patches['Default:AthenaCosmeticLoadoutVariants_j'] = this.meta.set('Default:AthenaCosmeticLoadoutVariants_j', variantData); } } } if (pickaxe !== undefined) { const parsedVariants = { athenaPickaxe: { i: (_w = (_v = pickaxe.variants) === null || _v === void 0 ? void 0 : _v.map((v, i) => `${i}|${v.variantIndex}`)) !== null && _w !== void 0 ? _w : [], }, }; data = { ...data, AthenaCosmeticLoadout: { ...((data === null || data === void 0 ? void 0 : data.AthenaCosmeticLoadout) || {}), pickaxeDef: `${(_y = (_x = pickaxe.path) === null || _x === void 0 ? void 0 : _x.replace(/\/$/, '')) !== null && _y !== void 0 ? _y : '/BRCosmetics/Athena/Items/Cosmetics/Pickaxes'}/${pickaxe.id}.${pickaxe.id}`, }, }; patches['Default:AthenaCosmeticLoadout_j'] = this.meta.set('Default:AthenaCosmeticLoadout_j', data); (_0 = (_z = variantData === null || variantData === void 0 ? void 0 : variantData.AthenaCosmeticLoadoutVariants) === null || _z === void 0 ? void 0 : _z.vD) === null || _0 === void 0 ? true : delete _0.AthenaPickaxe; if ((_1 = parsedVariants.athenaPickaxe) === null || _1 === void 0 ? void 0 : _1.i[0]) { variantData = { ...variantData, AthenaCosmeticLoadoutVariants: { ...(variantData === null || variantData === void 0 ? void 0 : variantData.AthenaCosmeticLoadoutVariants) || {}, vD: { ...((_2 = variantData === null || variantData === void 0 ? void 0 : variantData.AthenaCosmeticLoadoutVariants) === null || _2 === void 0 ? void 0 : _2.vD) || {}, ...parsedVariants, }, }, }; patches['Default:AthenaCosmeticLoadoutVariants_j'] = this.meta.set('Default:AthenaCosmeticLoadoutVariants_j', variantData); } } if (shoes !== undefined) { if (shoes === null) { data = { ...data, AthenaCosmeticLoadout: { ...((data === null || data === void 0 ? void 0 : data.AthenaCosmeticLoadout) || {}), shoesDef: '', }, }; patches['Default:AthenaCosmeticLoadout_j'] = this.meta.set('Default:AthenaCosmeticLoadout_j', data); } else { data = { ...data, AthenaCosmeticLoadout: { ...((data === null || data === void 0 ? void 0 : data.AthenaCosmeticLoadout) || {}), shoesDef: `${(_4 = (_3 = shoes.path) === null || _3 === void 0 ? void 0 : _3.replace(/\/$/, '')) !== null && _4 !== void 0 ? _4 : '/CosmeticShoes/Assets/Items/Cosmetics'}/${shoes.id}.${shoes.id}`, }, }; patches['Default:AthenaCosmeticLoadout_j'] = this.meta.set('Default:AthenaCosmeticLoadout_j', data); } } if (sidekick !== undefined) { const mpLoadoutD = JSON.parse(mpDataObj.MpLoadout.d || '{}'); if (sidekick === null) { data = { ...data, AthenaCosmeticLoadout: { ...((data === null || data === void 0 ? void 0 : data.AthenaCosmeticLoadout) || {}), mimosaDef: '', }, }; patches['Default:AthenaCosmeticLoadout_j'] = this.meta.set('Default:AthenaCosmeticLoadout_j', data); mpDataObj = { ...mpDataObj, MpLoadout: { ...mpDataObj.MpLoadout, d: JSON.stringify({ ...mpLoadoutD, mm: undefined, }), }, }; patches['Default:MpLoadout_j'] = this.meta.set('Default:MpLoadout_j', mpDataObj); } else { const parsedVariants = { cosmeticMimosa: { i: (_6 = (_5 = sidekick.variants) === null || _5 === void 0 ? void 0 : _5.map((v, i) => `${i}|${v.variantIndex}`)) !== null && _6 !== void 0 ? _6 : [], }, }; data = { ...data, AthenaCosmeticLoadout: { ...((data === null || data === void 0 ? void 0 : data.AthenaCosmeticLoadout) || {}), mimosaDef: `${(_8 = (_7 = sidekick.path) === null || _7 === void 0 ? void 0 : _7.replace(/\/$/, '')) !== null && _8 !== void 0 ? _8 : '/CosmeticCompanions/Assets/Items'}/${sidekick.id}.${sidekick.id}`, }, }; patches['Default:AthenaCosmeticLoadout_j'] = this.meta.set('Default:AthenaCosmeticLoadout_j', data); (_10 = (_9 = variantData === null || variantData === void 0 ? void 0 : variantData.AthenaCosmeticLoadoutVariants) === null || _9 === void 0 ? void 0 : _9.vD) === null || _10 === void 0 ? true : delete _10.CosmeticMimosa; if ((_11 = parsedVariants.cosmeticMimosa) === null || _11 === void 0 ? void 0 : _11.i[0]) { variantData = { ...variantData, AthenaCosmeticLoadoutVariants: { ...(variantData === null || variantData === void 0 ? void 0 : variantData.AthenaCosmeticLoadoutVariants) || {}, vD: { ...((_12 = variantData === null || variantData === void 0 ? void 0 : variantData.AthenaCosmeticLoadoutVariants) === null || _12 === void 0 ? void 0 : _12.vD) || {}, ...parsedVariants, }, }, }; patches['Default:AthenaCosmeticLoadoutVariants_j'] = this.meta.set('Default:AthenaCosmeticLoadoutVariants_j', variantData); } mpDataObj = { ...mpDataObj, MpLoadout: { ...mpDataObj.MpLoadout, d: JSON.stringify({ ...mpLoadoutD, mm: sidekick ? { "i": "CosmeticMimosa:" + sidekick.id, "d": "0691f1ee-29d5-4619-9632-e2dd24e6674b", "v": { "0": "Big Sniff", "1": "CosmeticMimosaC:companion_reactfx_ripequiver", "2": "1", "3": "2", "4": "0", "5": "1", "6": "3" } } : undefined, }), }, }; patches['Default:MpLoadout_j'] = this.meta.set('Default:MpLoadout_j', mpDataObj); } } await this.sendPatch(patches); } /** * Updates the client party member's outfit * @param id The outfit's ID * @param variants The outfit's variants * @param enlightment The outfit's enlightment * @throws {EpicgamesAPIError} */ async setOutfit(id, variants = [], enlightment) { return this.setCosmetics({ outfit: { id, variants, enlightment } }); } /** * Updates the client party member's backpack * @param id The backpack's ID * @param variants The backpack's variants * @param path The backpack's path in the game files * @throws {EpicgamesAPIError} */ async setBackpack(id, variants = [], path) { return this.setCosmetics({ backpack: { id, variants, path } }); } /** * Updates the client party member's pet * @param id The pet's ID * @param variants The pet's variants * @param path The pet's path in the game files */ async setPet(id, variants = [], path) { return this.setCosmetics({ backpack: { id, variants, path: path !== null && path !== void 0 ? path : '/BRCosmetics/Athena/Items/Cosmetics/PetCarriers' } }); } /** * Updates the client party member's pickaxe * @param id The pickaxe's ID * @param variants The pickaxe's variants * @param path The pickaxe's path in the game files * @throws {EpicgamesAPIError} */ async setPickaxe(id, variants = [], path) { return this.setCosmetics({ pickaxe: { id, variants, path } }); } /** * Updates the client party member's shoes * @param id The shoes' ID * @param variants The shoes' variants * @param path The shoes' path in the game files * @throws {EpicgamesAPIError} */ async setShoes(id, variants = [], path) { return this.setCosmetics({ shoes: { id, variants, path } }); } /** * Updates the client party member's sidekick * @param id The sidekick's ID * @param variants The sidekick's variants * @param path The sidekick's path in the game files * @throws {EpicgamesAPIError} */ async setSidekick(id, variants = [], path) { return this.setCosmetics({ sidekick: { id, variants, path } }); } /** * Updates the client party member's emote * @param id The emote's ID * @param path The emote's path in the game files * @throws {EpicgamesAPIError} */ async setEmote(id, path) { var _a; if (this.meta.get('Default:FrontendEmote_j').FrontendEmote.pickable !== 'None') await this.clearEmote(); let data = this.meta.get('Default:FrontendEmote_j'); data = this.meta.set('Default:FrontendEmote_j', { ...data, FrontendEmote: { ...data.FrontendEmote, pickable: `${(_a = path === null || path === void 0 ? void 0 : path.replace(/\/$/, '')) !== null && _a !== void 0 ? _a : '/BRCosmetics/Athena/Items/Cosmetics/Dances'}/${id}.${id}`, emoteSection: -2, }, }); await this.sendPatch({ 'Default:FrontendEmote_j': data, }); } /** * Updates the client party member's emoji * @param id The emoji's ID * @param path The emoji's path in the game files * @throws {EpicgamesAPIError} */ async setEmoji(id, path) { return this.setEmote(id, path !== null && path !== void 0 ? path : '/BRCosmetics/Athena/Items/Cosmetics/Dances/Emoji'); } /** * Clears the client party member's emote and emoji * @throws {EpicgamesAPIError} */ async clearEmote() { let data = this.meta.get('Default:FrontendEmote_j'); data = this.meta.set('Default:FrontendEmote_j', { ...data, FrontendEmote: { ...data.FrontendEmote, pickable: 'None', emoteSection: -1, }, }); await this.sendPatch({ 'Default:FrontendEmote_j': data, }); } /** * Clears the client party member's backpack * @throws {EpicgamesAPIError} */ async clearBackpack() { return this.setCosmetics({ backpack: null }); } /** * Clears the client party member's shoes * @throws {EpicgamesAPIError} */ async clearShoes() { return this.setCosmetics({ shoes: null }); } /** * Clears the client party member's sidekick * @throws {EpicgamesAPIError} */ async clearSidekick() { return this.setCosmetics({ sidekick: null }); } /** * Updates the client party member's match state. * NOTE: This is visually, the client will not actually join a match * @param isPlaying Whether the client is in a match * @param playerCount The match player count (must be between 0 and 255) * @param startedAt The start date of the match * @throws {EpicgamesAPIError} */ async setPlaying(isPlaying = true, playerCount = 100, startedAt = new Date()) { await this.sendPatch({ 'Default:DownloadOnDemandProgress_d': this.meta.set('Default:DownloadOnDemandProgress_d', isPlaying ? '1.000000' : '0.000000'), 'Default:PackedState_j': this.meta.set('Default:PackedState_j', { ...this.meta.get('Default:PackedState_j'), PackedState: { ...this.meta.get('Default:PackedState_j').PackedState, location: isPlaying ? 'InGame' : 'PreLobby', gameMode: isPlaying ? 'InBattleRoyale' : 'None', }, }), 'Default:MatchmakingInfo_j': this.meta.set('Default:MatchmakingInfo_j', { ...this.meta.get('Default:MatchmakingInfo_j'), LobbyState: { ...this.meta.get('Default:MatchmakingInfo_j').MatchmakingInfo, hasPreloadedAthena: isPlaying, }, }), 'Default:NumAthenaPlayersLeft_U': this.meta.set('Default:NumAthenaPlayersLeft_U', playerCount), 'Default:UtcTimeStartedMatchAthena_s': this.meta.set('Default:UtcTimeStartedMatchAthena_s', startedAt.toISOString()), }); } /** * Updates the client party member's pre lobby map marker. * [0, 0] would be the center of the map * @param isSet Whether the marker is set * @param locationX The marker x location * @param locationY The marker y location * @throws {EpicgamesAPIError} */ async setMarker(isSet, locationX, locationY) { let data = this.meta.get('Default:FrontEndMapMarker_j'); data = this.meta.set('Default:FrontEndMapMarker_j', { ...data, FrontEndMapMarker: { ...data.FrontEndMapMarker, bIsSet: isSet, markerLocation: { ...data.FrontEndMapMarker.markerLocation, x: locationY || 0, y: locationX || 0, }, }, }); await this.sendPatch({ 'Default:FrontEndMapMarker_j': data, }); } /** * Updates the client party member's cosmetic stats. * Crowns are shown when using the EID_Coronet emote * @param crowns The amount of crowns / "Royal Royales" * @param rankedProgression The ranked progression * @throws {EpicgamesAPIError} */ async setCosmeticStats(crowns, rankedProgression) { let data = this.meta.get('Default:AthenaCosmeticLoadout_j'); data = this.meta.set('Default:AthenaCosmeticLoadout_j', { ...data, AthenaCosmeticLoadout: { ...data.AthenaCosmeticLoadout, cosmeticStats: [{ statName: 'HabaneroProgression', statValue: rankedProgression, }, { statName: 'TotalVictoryCrowns', statValue: 0, }, { statName: 'TotalRoyalRoyales', statValue: crowns, }, { statName: 'HasCrown', statValue: 0, }], }, }); await this.sendPatch({ 'Default:AthenaCosmeticLoadout_j': data, }); } } exports.default = ClientPartyMember; //# sourceMappingURL=ClientPartyMember.js.map