From f9b3fecc10f790977d1da23aad642a10d8316b16 Mon Sep 17 00:00:00 2001 From: Sainan <63328889+Sainan@users.noreply.github.com> Date: Fri, 9 May 2025 00:20:54 -0700 Subject: [PATCH] chore: some initial handling of legacy oid format (#2033) This at least allows mission inventory update to succeed on U19.5 and below. Reviewed-on: https://onlyg.it/OpenWF/SpaceNinjaServer/pulls/2033 Co-authored-by: Sainan <63328889+Sainan@users.noreply.github.com> Co-committed-by: Sainan <63328889+Sainan@users.noreply.github.com> --- .../api/claimCompletedRecipeController.ts | 16 ++++---- src/controllers/api/inventoryController.ts | 41 +++++++++++++------ src/controllers/api/loginController.ts | 2 +- src/controllers/api/setGuildMotdController.ts | 2 +- src/controllers/custom/addXpController.ts | 3 +- src/helpers/inventoryHelpers.ts | 41 ++++++++++++++++++- src/helpers/nemesisHelpers.ts | 3 +- src/services/inventoryService.ts | 7 ++-- src/services/missionInventoryUpdateService.ts | 3 +- src/services/worldStateService.ts | 18 +------- src/types/commonTypes.ts | 5 +++ .../inventoryTypes/commonInventoryTypes.ts | 4 +- src/types/inventoryTypes/inventoryTypes.ts | 4 +- 13 files changed, 96 insertions(+), 53 deletions(-) diff --git a/src/controllers/api/claimCompletedRecipeController.ts b/src/controllers/api/claimCompletedRecipeController.ts index f1fa6f6e..173c7221 100644 --- a/src/controllers/api/claimCompletedRecipeController.ts +++ b/src/controllers/api/claimCompletedRecipeController.ts @@ -4,9 +4,9 @@ import { RequestHandler } from "express"; import { logger } from "@/src/utils/logger"; import { getRecipe } from "@/src/services/itemDataService"; -import { IOid } from "@/src/types/commonTypes"; +import { IOid, IOidWithLegacySupport } from "@/src/types/commonTypes"; import { getJSONfromString } from "@/src/helpers/stringHelpers"; -import { getAccountIdForRequest } from "@/src/services/loginService"; +import { getAccountForRequest } from "@/src/services/loginService"; import { getInventory, updateCurrency, @@ -18,7 +18,7 @@ import { import { IInventoryChanges } from "@/src/types/purchaseTypes"; import { IEquipmentClient } from "@/src/types/inventoryTypes/commonInventoryTypes"; import { InventorySlot } from "@/src/types/inventoryTypes/inventoryTypes"; -import { toOid } from "@/src/helpers/inventoryHelpers"; +import { toOid2 } from "@/src/helpers/inventoryHelpers"; interface IClaimCompletedRecipeRequest { RecipeIds: IOid[]; @@ -26,10 +26,8 @@ interface IClaimCompletedRecipeRequest { export const claimCompletedRecipeController: RequestHandler = async (req, res) => { const claimCompletedRecipeRequest = getJSONfromString(String(req.body)); - const accountId = await getAccountIdForRequest(req); - if (!accountId) throw new Error("no account id"); - - const inventory = await getInventory(accountId); + const account = await getAccountForRequest(req); + const inventory = await getInventory(account._id.toString()); const pendingRecipe = inventory.PendingRecipes.id(claimCompletedRecipeRequest.RecipeIds[0].$oid); if (!pendingRecipe) { throw new Error(`no pending recipe found with id ${claimCompletedRecipeRequest.RecipeIds[0].$oid}`); @@ -81,7 +79,7 @@ export const claimCompletedRecipeController: RequestHandler = async (req, res) = } else { logger.debug("Claiming Recipe", { recipe, pendingRecipe }); - let BrandedSuits: undefined | IOid[]; + let BrandedSuits: undefined | IOidWithLegacySupport[]; if (recipe.secretIngredientAction == "SIA_SPECTRE_LOADOUT_COPY") { inventory.PendingSpectreLoadouts ??= []; inventory.SpectreLoadouts ??= []; @@ -106,7 +104,7 @@ export const claimCompletedRecipeController: RequestHandler = async (req, res) = inventory.BrandedSuits!.findIndex(x => x.equals(pendingRecipe.SuitToUnbrand)), 1 ); - BrandedSuits = [toOid(pendingRecipe.SuitToUnbrand!)]; + BrandedSuits = [toOid2(pendingRecipe.SuitToUnbrand!, account.BuildLabel)]; } let InventoryChanges: IInventoryChanges = {}; diff --git a/src/controllers/api/inventoryController.ts b/src/controllers/api/inventoryController.ts index 59c3415e..a26a73c5 100644 --- a/src/controllers/api/inventoryController.ts +++ b/src/controllers/api/inventoryController.ts @@ -25,10 +25,10 @@ import { logger } from "@/src/utils/logger"; import { catBreadHash } from "@/src/helpers/stringHelpers"; import { Types } from "mongoose"; import { isNemesisCompatibleWithVersion } from "@/src/helpers/nemesisHelpers"; -import { version_compare } from "@/src/services/worldStateService"; import { getPersonalRooms } from "@/src/services/personalRoomsService"; import { IPersonalRoomsClient } from "@/src/types/personalRoomsTypes"; import { Ship } from "@/src/models/shipModel"; +import { toLegacyOid, version_compare } from "@/src/helpers/inventoryHelpers"; export const inventoryController: RequestHandler = async (request, response) => { const account = await getAccountForRequest(request); @@ -306,19 +306,34 @@ export const getInventoryResponse = async ( // Set 2FA enabled so trading post can be used inventoryResponse.HWIDProtectEnabled = true; - // Fix nemesis for older versions - if ( - inventoryResponse.Nemesis && - buildLabel && - !isNemesisCompatibleWithVersion(inventoryResponse.Nemesis, buildLabel) - ) { - inventoryResponse.Nemesis = undefined; - } + if (buildLabel) { + // Fix nemesis for older versions + if (inventoryResponse.Nemesis && !isNemesisCompatibleWithVersion(inventoryResponse.Nemesis, buildLabel)) { + inventoryResponse.Nemesis = undefined; + } - if (buildLabel && version_compare(buildLabel, "2018.02.22.14.34") < 0) { - const personalRoomsDb = await getPersonalRooms(inventory.accountOwnerId.toString()); - const personalRooms = personalRoomsDb.toJSON(); - inventoryResponse.Ship = personalRooms.Ship; + if (version_compare(buildLabel, "2018.02.22.14.34") < 0) { + const personalRoomsDb = await getPersonalRooms(inventory.accountOwnerId.toString()); + const personalRooms = personalRoomsDb.toJSON(); + inventoryResponse.Ship = personalRooms.Ship; + + if (version_compare(buildLabel, "2016.12.21.19.13") <= 0) { + // U19.5 and below use $id instead of $oid + for (const category of equipmentKeys) { + for (const item of inventoryResponse[category]) { + toLegacyOid(item.ItemId); + } + } + for (const upgrade of inventoryResponse.Upgrades) { + toLegacyOid(upgrade.ItemId); + } + if (inventoryResponse.BrandedSuits) { + for (const id of inventoryResponse.BrandedSuits) { + toLegacyOid(id); + } + } + } + } } return inventoryResponse; diff --git a/src/controllers/api/loginController.ts b/src/controllers/api/loginController.ts index f1eab10a..2007db12 100644 --- a/src/controllers/api/loginController.ts +++ b/src/controllers/api/loginController.ts @@ -7,7 +7,7 @@ import { Account } from "@/src/models/loginModel"; import { createAccount, isCorrectPassword, isNameTaken } from "@/src/services/loginService"; import { IDatabaseAccountJson, ILoginRequest, ILoginResponse } from "@/src/types/loginTypes"; import { logger } from "@/src/utils/logger"; -import { version_compare } from "@/src/services/worldStateService"; +import { version_compare } from "@/src/helpers/inventoryHelpers"; export const loginController: RequestHandler = async (request, response) => { const loginRequest = JSON.parse(String(request.body)) as ILoginRequest; // parse octet stream of json data to json object diff --git a/src/controllers/api/setGuildMotdController.ts b/src/controllers/api/setGuildMotdController.ts index 62f10521..1e09ab28 100644 --- a/src/controllers/api/setGuildMotdController.ts +++ b/src/controllers/api/setGuildMotdController.ts @@ -1,8 +1,8 @@ +import { version_compare } from "@/src/helpers/inventoryHelpers"; import { Alliance, Guild, GuildMember } from "@/src/models/guildModel"; import { hasGuildPermissionEx } from "@/src/services/guildService"; import { getInventory } from "@/src/services/inventoryService"; import { getAccountForRequest, getSuffixedName } from "@/src/services/loginService"; -import { version_compare } from "@/src/services/worldStateService"; import { GuildPermission, ILongMOTD } from "@/src/types/guildTypes"; import { RequestHandler } from "express"; diff --git a/src/controllers/custom/addXpController.ts b/src/controllers/custom/addXpController.ts index 0ca05102..7e42deb3 100644 --- a/src/controllers/custom/addXpController.ts +++ b/src/controllers/custom/addXpController.ts @@ -1,5 +1,6 @@ import { applyClientEquipmentUpdates, getInventory } from "@/src/services/inventoryService"; import { getAccountIdForRequest } from "@/src/services/loginService"; +import { IOid } from "@/src/types/commonTypes"; import { IEquipmentClient } from "@/src/types/inventoryTypes/commonInventoryTypes"; import { TEquipmentKey } from "@/src/types/inventoryTypes/inventoryTypes"; import { RequestHandler } from "express"; @@ -11,7 +12,7 @@ export const addXpController: RequestHandler = async (req, res) => { const request = req.body as IAddXpRequest; for (const [category, gear] of Object.entries(request)) { for (const clientItem of gear) { - const dbItem = inventory[category as TEquipmentKey].id(clientItem.ItemId.$oid); + const dbItem = inventory[category as TEquipmentKey].id((clientItem.ItemId as IOid).$oid); if (dbItem) { if (dbItem.ItemType in ExportMisc.uniqueLevelCaps) { if ((dbItem.Polarized ?? 0) < 5) { diff --git a/src/helpers/inventoryHelpers.ts b/src/helpers/inventoryHelpers.ts index 5efc801d..6ced29e9 100644 --- a/src/helpers/inventoryHelpers.ts +++ b/src/helpers/inventoryHelpers.ts @@ -1,9 +1,46 @@ -import { IMongoDate, IOid } from "@/src/types/commonTypes"; +import { IMongoDate, IOid, IOidWithLegacySupport } from "@/src/types/commonTypes"; import { Types } from "mongoose"; import { TRarity } from "warframe-public-export-plus"; +export const version_compare = (a: string, b: string): number => { + const a_digits = a + .split("/")[0] + .split(".") + .map(x => parseInt(x)); + const b_digits = b + .split("/")[0] + .split(".") + .map(x => parseInt(x)); + for (let i = 0; i != a_digits.length; ++i) { + if (a_digits[i] != b_digits[i]) { + return a_digits[i] > b_digits[i] ? 1 : -1; + } + } + return 0; +}; + export const toOid = (objectId: Types.ObjectId): IOid => { - return { $oid: objectId.toString() } satisfies IOid; + return { $oid: objectId.toString() }; +}; + +export function toOid2(objectId: Types.ObjectId, buildLabel: undefined): IOid; +export function toOid2(objectId: Types.ObjectId, buildLabel: string | undefined): IOidWithLegacySupport; +export function toOid2(objectId: Types.ObjectId, buildLabel: string | undefined): IOidWithLegacySupport { + if (buildLabel && version_compare(buildLabel, "2016.12.21.19.13") <= 0) { + return { $id: objectId.toString() }; + } + return { $oid: objectId.toString() }; +} + +export const toLegacyOid = (oid: IOidWithLegacySupport): void => { + if (!("$id" in oid)) { + oid.$id = oid.$oid; + delete oid.$oid; + } +}; + +export const fromOid = (oid: IOidWithLegacySupport): string => { + return (oid.$oid ?? oid.$id)!; }; export const toMongoDate = (date: Date): IMongoDate => { diff --git a/src/helpers/nemesisHelpers.ts b/src/helpers/nemesisHelpers.ts index 5df1ad9a..6e9db804 100644 --- a/src/helpers/nemesisHelpers.ts +++ b/src/helpers/nemesisHelpers.ts @@ -6,9 +6,10 @@ import { logger } from "../utils/logger"; import { IOid } from "../types/commonTypes"; import { Types } from "mongoose"; import { addMods, generateRewardSeed } from "../services/inventoryService"; -import { isArchwingMission, version_compare } from "../services/worldStateService"; +import { isArchwingMission } from "../services/worldStateService"; import { fromStoreItem, toStoreItem } from "../services/itemDataService"; import { createMessage } from "../services/inboxService"; +import { version_compare } from "./inventoryHelpers"; export const getInfNodes = (faction: string, rank: number): IInfNode[] => { const infNodes = []; diff --git a/src/services/inventoryService.ts b/src/services/inventoryService.ts index 6a30df38..2beba2e7 100644 --- a/src/services/inventoryService.ts +++ b/src/services/inventoryService.ts @@ -70,6 +70,7 @@ import { createShip } from "./shipService"; import { catbrowDetails, fromMongoDate, + fromOid, kubrowDetails, kubrowFurPatternsWeights, kubrowWeights, @@ -423,7 +424,7 @@ export const addItem = async ( changes.push({ ItemType: egg.ItemType, ExpirationDate: { $date: { $numberLong: "2000000000000" } }, - ItemId: toOid(egg._id) + ItemId: toOid(egg._id) // TODO: Pass on buildLabel from purchaseService }); } return { @@ -1491,9 +1492,9 @@ export const applyClientEquipmentUpdates = ( const category = inventory[categoryName]; gearArray.forEach(({ ItemId, XP, InfestationDate }) => { - const item = category.id(ItemId.$oid); + const item = category.id(fromOid(ItemId)); if (!item) { - throw new Error(`No item with id ${ItemId.$oid} in ${categoryName}`); + throw new Error(`No item with id ${fromOid(ItemId)} in ${categoryName}`); } if (XP) { diff --git a/src/services/missionInventoryUpdateService.ts b/src/services/missionInventoryUpdateService.ts index decc74ba..a585dceb 100644 --- a/src/services/missionInventoryUpdateService.ts +++ b/src/services/missionInventoryUpdateService.ts @@ -62,6 +62,7 @@ import { getLiteSortie, getSortie, idToBountyCycle, idToDay, idToWeek, pushClass import { config } from "./configService"; import libraryDailyTasks from "@/static/fixed_responses/libraryDailyTasks.json"; import { ISyndicateMissionInfo } from "../types/worldStateTypes"; +import { fromOid } from "../helpers/inventoryHelpers"; const getRotations = (rewardInfo: IRewardInfo, tierOverride?: number): number[] => { // For Spy missions, e.g. 3 vaults cracked = A, B, C @@ -399,7 +400,7 @@ export const addMissionInventoryUpdates = async ( break; case "Upgrades": value.forEach(clientUpgrade => { - const upgrade = inventory.Upgrades.id(clientUpgrade.ItemId.$oid)!; + const upgrade = inventory.Upgrades.id(fromOid(clientUpgrade.ItemId))!; upgrade.UpgradeFingerprint = clientUpgrade.UpgradeFingerprint; // primitive way to copy over the riven challenge progress }); break; diff --git a/src/services/worldStateService.ts b/src/services/worldStateService.ts index a9a65efa..6b953518 100644 --- a/src/services/worldStateService.ts +++ b/src/services/worldStateService.ts @@ -18,6 +18,7 @@ import { ISyndicateMissionInfo, IWorldState } from "../types/worldStateTypes"; +import { version_compare } from "../helpers/inventoryHelpers"; const sortieBosses = [ "SORTIE_BOSS_HYENA", @@ -1239,20 +1240,3 @@ export const isArchwingMission = (node: IRegion): boolean => { } return false; }; - -export const version_compare = (a: string, b: string): number => { - const a_digits = a - .split("/")[0] - .split(".") - .map(x => parseInt(x)); - const b_digits = b - .split("/")[0] - .split(".") - .map(x => parseInt(x)); - for (let i = 0; i != a_digits.length; ++i) { - if (a_digits[i] != b_digits[i]) { - return a_digits[i] > b_digits[i] ? 1 : -1; - } - } - return 0; -}; diff --git a/src/types/commonTypes.ts b/src/types/commonTypes.ts index 5ac1cac3..a9335fff 100644 --- a/src/types/commonTypes.ts +++ b/src/types/commonTypes.ts @@ -4,6 +4,11 @@ export interface IOid { $oid: string; } +export interface IOidWithLegacySupport { + $oid?: string; + $id?: string; +} + export interface IMongoDate { $date: { $numberLong: string; diff --git a/src/types/inventoryTypes/commonInventoryTypes.ts b/src/types/inventoryTypes/commonInventoryTypes.ts index 37168a7d..8cc0d56f 100644 --- a/src/types/inventoryTypes/commonInventoryTypes.ts +++ b/src/types/inventoryTypes/commonInventoryTypes.ts @@ -1,4 +1,4 @@ -import { IMongoDate, IOid } from "@/src/types/commonTypes"; +import { IMongoDate, IOid, IOidWithLegacySupport } from "@/src/types/commonTypes"; import { Types } from "mongoose"; import { ICrewShipCustomization, @@ -92,7 +92,7 @@ export interface IEquipmentClient IEquipmentDatabase, "_id" | "InfestationDate" | "Expiry" | "UpgradesExpiry" | "UmbraDate" | "CrewMembers" | "Details" > { - ItemId: IOid; + ItemId: IOidWithLegacySupport; InfestationDate?: IMongoDate; Expiry?: IMongoDate; UpgradesExpiry?: IMongoDate; diff --git a/src/types/inventoryTypes/inventoryTypes.ts b/src/types/inventoryTypes/inventoryTypes.ts index 061f616d..a32fc796 100644 --- a/src/types/inventoryTypes/inventoryTypes.ts +++ b/src/types/inventoryTypes/inventoryTypes.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { Types } from "mongoose"; -import { IOid, IMongoDate } from "../commonTypes"; +import { IOid, IMongoDate, IOidWithLegacySupport } from "../commonTypes"; import { IColor, IItemConfig, @@ -371,7 +371,7 @@ export interface IInventoryClient extends IDailyAffiliations, InventoryClientEqu EchoesHexConquestCacheScoreMission?: number; EchoesHexConquestActiveFrameVariants?: string[]; EchoesHexConquestActiveStickers?: string[]; - BrandedSuits?: IOid[]; + BrandedSuits?: IOidWithLegacySupport[]; LockedWeaponGroup?: ILockedWeaponGroupClient; HubNpcCustomizations?: IHubNpcCustomization[]; Ship?: IOrbiter; // U22 and below, response only