diff --git a/src/controllers/api/inventoryController.ts b/src/controllers/api/inventoryController.ts index 4f73a5f7..8febdcf7 100644 --- a/src/controllers/api/inventoryController.ts +++ b/src/controllers/api/inventoryController.ts @@ -1,5 +1,5 @@ import { RequestHandler } from "express"; -import { getAccountIdForRequest } from "@/src/services/loginService"; +import { getAccountForRequest } from "@/src/services/loginService"; import { Inventory, TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel"; import { config } from "@/src/services/configService"; import allDialogue from "@/static/fixed_responses/allDialogue.json"; @@ -24,11 +24,12 @@ import { import { logger } from "@/src/utils/logger"; import { catBreadHash } from "@/src/helpers/stringHelpers"; import { Types } from "mongoose"; +import { isNemesisCompatibleWithVersion } from "@/src/helpers/nemesisHelpers"; export const inventoryController: RequestHandler = async (request, response) => { - const accountId = await getAccountIdForRequest(request); + const account = await getAccountForRequest(request); - const inventory = await Inventory.findOne({ accountOwnerId: accountId }); + const inventory = await Inventory.findOne({ accountOwnerId: account._id }); if (!inventory) { response.status(400).json({ error: "inventory was undefined" }); @@ -119,12 +120,15 @@ export const inventoryController: RequestHandler = async (request, response) => inventory.LastInventorySync = new Types.ObjectId(); await inventory.save(); - response.json(await getInventoryResponse(inventory, "xpBasedLevelCapDisabled" in request.query)); + response.json( + await getInventoryResponse(inventory, "xpBasedLevelCapDisabled" in request.query, account.BuildLabel) + ); }; export const getInventoryResponse = async ( inventory: TInventoryDatabaseDocument, - xpBasedLevelCapDisabled: boolean + xpBasedLevelCapDisabled: boolean, + buildLabel: string | undefined ): Promise => { const inventoryWithLoadOutPresets = await inventory.populate<{ LoadOutPresets: ILoadoutDatabase }>( "LoadOutPresets" @@ -299,6 +303,15 @@ 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; + } + return inventoryResponse; }; diff --git a/src/controllers/api/loginController.ts b/src/controllers/api/loginController.ts index 0d595e95..366e87c3 100644 --- a/src/controllers/api/loginController.ts +++ b/src/controllers/api/loginController.ts @@ -47,7 +47,8 @@ export const loginController: RequestHandler = async (request, response) => { ForceLogoutVersion: 0, ConsentNeeded: false, TrackedSettings: [], - Nonce: nonce + Nonce: nonce, + BuildLabel: buildLabel }); logger.debug("created new account"); response.json(createLoginResponse(myAddress, newAccount, buildLabel)); @@ -88,6 +89,7 @@ export const loginController: RequestHandler = async (request, response) => { account.ClientType = loginRequest.ClientType; account.Nonce = nonce; account.CountryCode = loginRequest.lang.toUpperCase(); + account.BuildLabel = buildLabel; } await account.save(); diff --git a/src/controllers/api/missionInventoryUpdateController.ts b/src/controllers/api/missionInventoryUpdateController.ts index ff181e0b..5e642c27 100644 --- a/src/controllers/api/missionInventoryUpdateController.ts +++ b/src/controllers/api/missionInventoryUpdateController.ts @@ -1,6 +1,6 @@ import { RequestHandler } from "express"; import { getJSONfromString } from "@/src/helpers/stringHelpers"; -import { getAccountIdForRequest } from "@/src/services/loginService"; +import { getAccountForRequest } from "@/src/services/loginService"; import { IMissionInventoryUpdateRequest } from "@/src/types/requestTypes"; import { addMissionInventoryUpdates, addMissionRewards } from "@/src/services/missionInventoryUpdateService"; import { generateRewardSeed, getInventory } from "@/src/services/inventoryService"; @@ -49,11 +49,11 @@ import { IMissionInventoryUpdateResponse } from "@/src/types/missionTypes"; */ //move credit calc in here, return MissionRewards: [] if no reward info export const missionInventoryUpdateController: RequestHandler = async (req, res): Promise => { - const accountId = await getAccountIdForRequest(req); + const account = await getAccountForRequest(req); const missionReport = getJSONfromString((req.body as string).toString()); logger.debug("mission report:", missionReport); - const inventory = await getInventory(accountId); + const inventory = await getInventory(account._id.toString()); const firstCompletion = missionReport.SortieId ? inventory.CompletedSorties.indexOf(missionReport.SortieId) == -1 : false; @@ -65,7 +65,7 @@ export const missionInventoryUpdateController: RequestHandler = async (req, res) ) { inventory.RewardSeed = generateRewardSeed(); await inventory.save(); - const inventoryResponse = await getInventoryResponse(inventory, true); + const inventoryResponse = await getInventoryResponse(inventory, true, account.BuildLabel); res.json({ InventoryJson: JSON.stringify(inventoryResponse), MissionRewards: [] @@ -84,7 +84,7 @@ export const missionInventoryUpdateController: RequestHandler = async (req, res) inventory.RewardSeed = generateRewardSeed(); await inventory.save(); - const inventoryResponse = await getInventoryResponse(inventory, true); + const inventoryResponse = await getInventoryResponse(inventory, true, account.BuildLabel); //TODO: figure out when to send inventory. it is needed for many cases. res.json({ diff --git a/src/helpers/nemesisHelpers.ts b/src/helpers/nemesisHelpers.ts index 3a146831..5df1ad9a 100644 --- a/src/helpers/nemesisHelpers.ts +++ b/src/helpers/nemesisHelpers.ts @@ -6,7 +6,7 @@ import { logger } from "../utils/logger"; import { IOid } from "../types/commonTypes"; import { Types } from "mongoose"; import { addMods, generateRewardSeed } from "../services/inventoryService"; -import { isArchwingMission } from "../services/worldStateService"; +import { isArchwingMission, version_compare } from "../services/worldStateService"; import { fromStoreItem, toStoreItem } from "../services/itemDataService"; import { createMessage } from "../services/inboxService"; @@ -237,15 +237,39 @@ const corpusVersionThreeWeapons = [ export const getWeaponsForManifest = (manifest: string): readonly string[] => { switch (manifest) { - case "/Lotus/Types/Game/Nemesis/KuvaLich/KuvaLichManifestVersionSix": + case "/Lotus/Types/Game/Nemesis/KuvaLich/KuvaLichManifestVersionSix": // >= 35.6.0 return kuvaLichVersionSixWeapons; - case "/Lotus/Types/Enemies/Corpus/Lawyers/LawyerManifestVersionThree": - case "/Lotus/Types/Enemies/Corpus/Lawyers/LawyerManifestVersionFour": + case "/Lotus/Types/Enemies/Corpus/Lawyers/LawyerManifestVersionThree": // >= 35.6.0 + case "/Lotus/Types/Enemies/Corpus/Lawyers/LawyerManifestVersionFour": // >= 37.0.0 return corpusVersionThreeWeapons; } throw new Error(`unknown nemesis manifest: ${manifest}`); }; +export const isNemesisCompatibleWithVersion = ( + nemesis: { manifest: string; Faction: string }, + buildLabel: string +): boolean => { + // Anything below 35.6.0 is not going to be okay given our set of supported manifests. + if (version_compare(buildLabel, "2024.05.15.11.07") < 0) { + return false; + } + + if (nemesis.Faction == "FC_INFESTATION") { + // Anything below 38.5.0 isn't gonna like an infested lich. + if (version_compare(buildLabel, "2025.03.18.16.07") < 0) { + return false; + } + } else if (nemesis.manifest == "/Lotus/Types/Enemies/Corpus/Lawyers/LawyerManifestVersionFour") { + // Anything below 37.0.0 isn't gonna know version 4, but version 3 is identical in terms of weapon choices, so we can spoof it to that. + if (version_compare(buildLabel, "2024.10.01.11.03") < 0) { + nemesis.manifest = "/Lotus/Types/Enemies/Corpus/Lawyers/LawyerManifestVersionThree"; + } + } + + return true; +}; + export const getInnateDamageTag = ( KillingSuit: string ): diff --git a/src/models/loginModel.ts b/src/models/loginModel.ts index 3e564aa6..fe9d0b5f 100644 --- a/src/models/loginModel.ts +++ b/src/models/loginModel.ts @@ -20,6 +20,7 @@ const databaseAccountSchema = new Schema( ConsentNeeded: { type: Boolean, required: true }, TrackedSettings: { type: [String], default: [] }, Nonce: { type: Number, default: 0 }, + BuildLabel: String, Dropped: Boolean, LatestEventMessageDate: { type: Date, default: 0 }, LastLoginRewardDate: { type: Number, default: 0 }, diff --git a/src/types/loginTypes.ts b/src/types/loginTypes.ts index 79cf538d..dfdebe0e 100644 --- a/src/types/loginTypes.ts +++ b/src/types/loginTypes.ts @@ -16,6 +16,7 @@ export interface IAccountAndLoginResponseCommons { export interface IDatabaseAccountRequiredFields extends IAccountAndLoginResponseCommons { email: string; password: string; + BuildLabel?: string; } export interface IDatabaseAccount extends IDatabaseAccountRequiredFields {