From 0ca06e409fbcfaec2bdfe6d757f5b4a2a2a59273 Mon Sep 17 00:00:00 2001 From: Sainan <63328889+Sainan@users.noreply.github.com> Date: Fri, 16 May 2025 09:09:39 +0200 Subject: [PATCH 1/4] feat: adjust server-side vendor prices according to syndicate standings For buying crew members from ticker --- .../api/getVendorInfoController.ts | 26 ++++--- src/services/purchaseService.ts | 7 +- src/services/serversideVendorsService.ts | 77 ++++++++++++++----- src/types/vendorTypes.ts | 6 ++ 4 files changed, 85 insertions(+), 31 deletions(-) diff --git a/src/controllers/api/getVendorInfoController.ts b/src/controllers/api/getVendorInfoController.ts index b161176e..5f9d3292 100644 --- a/src/controllers/api/getVendorInfoController.ts +++ b/src/controllers/api/getVendorInfoController.ts @@ -1,14 +1,20 @@ import { RequestHandler } from "express"; -import { getVendorManifestByTypeName } from "@/src/services/serversideVendorsService"; +import { applyStandingToVendorManifest, getVendorManifestByTypeName } from "@/src/services/serversideVendorsService"; +import { getInventory } from "@/src/services/inventoryService"; +import { getAccountIdForRequest } from "@/src/services/loginService"; -export const getVendorInfoController: RequestHandler = (req, res) => { - if (typeof req.query.vendor == "string") { - const manifest = getVendorManifestByTypeName(req.query.vendor); - if (!manifest) { - throw new Error(`Unknown vendor: ${req.query.vendor}`); - } - res.json(manifest); - } else { - res.status(400).end(); +export const getVendorInfoController: RequestHandler = async (req, res) => { + let manifest = getVendorManifestByTypeName(req.query.vendor as string); + if (!manifest) { + throw new Error(`Unknown vendor: ${req.query.vendor as string}`); } + + // For testing purposes, authenticating with this endpoint is optional here, but would be required on live. + if (req.query.accountId) { + const accountId = await getAccountIdForRequest(req); + const inventory = await getInventory(accountId); + manifest = applyStandingToVendorManifest(inventory, manifest); + } + + res.json(manifest); }; diff --git a/src/services/purchaseService.ts b/src/services/purchaseService.ts index 38cfd3d1..59a431c3 100644 --- a/src/services/purchaseService.ts +++ b/src/services/purchaseService.ts @@ -9,7 +9,7 @@ import { updateSlots } from "@/src/services/inventoryService"; import { getRandomWeightedRewardUc } from "@/src/services/rngService"; -import { getVendorManifestByOid } from "@/src/services/serversideVendorsService"; +import { applyStandingToVendorManifest, getVendorManifestByOid } from "@/src/services/serversideVendorsService"; import { IMiscItem } from "@/src/types/inventoryTypes/inventoryTypes"; import { IPurchaseRequest, IPurchaseResponse, SlotPurchase, IInventoryChanges } from "@/src/types/purchaseTypes"; import { logger } from "@/src/utils/logger"; @@ -53,8 +53,9 @@ export const handlePurchase = async ( const prePurchaseInventoryChanges: IInventoryChanges = {}; let seed: bigint | undefined; if (purchaseRequest.PurchaseParams.Source == 7) { - const manifest = getVendorManifestByOid(purchaseRequest.PurchaseParams.SourceId!); + let manifest = getVendorManifestByOid(purchaseRequest.PurchaseParams.SourceId!); if (manifest) { + manifest = applyStandingToVendorManifest(inventory, manifest); let ItemId: string | undefined; if (purchaseRequest.PurchaseParams.ExtraPurchaseInfoJson) { ItemId = (JSON.parse(purchaseRequest.PurchaseParams.ExtraPurchaseInfoJson) as { ItemId: string }) @@ -92,7 +93,7 @@ export const handlePurchase = async ( if (!config.noVendorPurchaseLimits && ItemId) { inventory.RecentVendorPurchases ??= []; let vendorPurchases = inventory.RecentVendorPurchases.find( - x => x.VendorType == manifest.VendorInfo.TypeName + x => x.VendorType == manifest!.VendorInfo.TypeName ); if (!vendorPurchases) { vendorPurchases = diff --git a/src/services/serversideVendorsService.ts b/src/services/serversideVendorsService.ts index af7d0b91..135e0bb4 100644 --- a/src/services/serversideVendorsService.ts +++ b/src/services/serversideVendorsService.ts @@ -30,6 +30,7 @@ import SolarisProspectorVendorManifest from "@/static/fixed_responses/getVendorI import Temple1999VendorManifest from "@/static/fixed_responses/getVendorInfo/Temple1999VendorManifest.json"; import TeshinHardModeVendorManifest from "@/static/fixed_responses/getVendorInfo/TeshinHardModeVendorManifest.json"; import ZarimanCommisionsManifestArchimedean from "@/static/fixed_responses/getVendorInfo/ZarimanCommisionsManifestArchimedean.json"; +import { TInventoryDatabaseDocument } from "../models/inventoryModels/inventoryModel"; const rawVendorManifests: IVendorManifest[] = [ ArchimedeanVendorManifest, @@ -145,6 +146,45 @@ export const getVendorManifestByOid = (oid: string): IVendorManifest | undefined return undefined; }; +export const applyStandingToVendorManifest = ( + inventory: TInventoryDatabaseDocument, + vendorManifest: IVendorManifest +): IVendorManifest => { + return { + VendorInfo: { + ...vendorManifest.VendorInfo, + ItemManifest: [...vendorManifest.VendorInfo.ItemManifest].map(offer => { + if (offer.Affiliation && offer.ReductionPerPositiveRank && offer.IncreasePerNegativeRank) { + const title: number = inventory.Affiliations.find(x => x.Tag == offer.Affiliation)?.Title ?? 0; + const factor = + 1 + + (title < 0 ? offer.IncreasePerNegativeRank * title : offer.ReductionPerPositiveRank * title) * + -1; + //console.log(offer.Affiliation, title, factor); + if (factor) { + offer = { ...offer }; + if (offer.RegularPrice) { + offer.RegularPriceBeforeDiscount = offer.RegularPrice; + offer.RegularPrice = [ + offer.RegularPriceBeforeDiscount[0] * factor, + offer.RegularPriceBeforeDiscount[1] * factor + ]; + } + if (offer.ItemPrices) { + offer.ItemPricesBeforeDiscount = offer.ItemPrices; + offer.ItemPrices = []; + for (const item of offer.ItemPricesBeforeDiscount) { + offer.ItemPrices.push({ ...item, ItemCount: item.ItemCount * factor }); + } + } + } + } + return offer; + }) + } + }; +}; + const preprocessVendorManifest = (originalManifest: IVendorManifest): IVendorManifest => { if (Date.now() >= parseInt(originalManifest.VendorInfo.Expiry.$date.$numberLong)) { const manifest = structuredClone(originalManifest); @@ -176,24 +216,27 @@ const toRange = (value: IRange | number): IRange => { return value; }; -const vendorInfoCache: Record = {}; +const vendorManifestCache: Record = {}; const generateVendorManifest = (vendorInfo: IGeneratableVendorInfo): IVendorManifest => { - if (!(vendorInfo.TypeName in vendorInfoCache)) { + if (!(vendorInfo.TypeName in vendorManifestCache)) { // eslint-disable-next-line @typescript-eslint/no-unused-vars const { cycleOffset, cycleDuration, ...clientVendorInfo } = vendorInfo; - vendorInfoCache[vendorInfo.TypeName] = { - ...clientVendorInfo, - ItemManifest: [], - Expiry: { $date: { $numberLong: "0" } } + vendorManifestCache[vendorInfo.TypeName] = { + VendorInfo: { + ...clientVendorInfo, + ItemManifest: [], + Expiry: { $date: { $numberLong: "0" } } + } }; } - const processed = vendorInfoCache[vendorInfo.TypeName]; - if (Date.now() >= parseInt(processed.Expiry.$date.$numberLong)) { + const cacheEntry = vendorManifestCache[vendorInfo.TypeName]; + const info = cacheEntry.VendorInfo; + if (Date.now() >= parseInt(info.Expiry.$date.$numberLong)) { // Remove expired offers - for (let i = 0; i != processed.ItemManifest.length; ) { - if (Date.now() >= parseInt(processed.ItemManifest[i].Expiry.$date.$numberLong)) { - processed.ItemManifest.splice(i, 1); + for (let i = 0; i != info.ItemManifest.length; ) { + if (Date.now() >= parseInt(info.ItemManifest[i].Expiry.$date.$numberLong)) { + info.ItemManifest.splice(i, 1); } else { ++i; } @@ -209,7 +252,7 @@ const generateVendorManifest = (vendorInfo: IGeneratableVendorInfo): IVendorMani const offersToAdd = []; if (manifest.numItems && !manifest.isOneBinPerCycle) { const numItemsTarget = rng.randomInt(manifest.numItems.minValue, manifest.numItems.maxValue); - while (processed.ItemManifest.length + offersToAdd.length < numItemsTarget) { + while (info.ItemManifest.length + offersToAdd.length < numItemsTarget) { // TODO: Consider per-bin item limits // TODO: Consider item probability weightings offersToAdd.push(rng.randomElement(manifest.items)!); @@ -288,20 +331,18 @@ const generateVendorManifest = (vendorInfo: IGeneratableVendorInfo): IVendorMani item.LocTagRandSeed = (BigInt(highDword) << 32n) | (BigInt(item.LocTagRandSeed) & 0xffffffffn); } } - processed.ItemManifest.push(item); + info.ItemManifest.push(item); } // Update vendor expiry let soonestOfferExpiry: number = Number.MAX_SAFE_INTEGER; - for (const offer of processed.ItemManifest) { + for (const offer of info.ItemManifest) { const offerExpiry = parseInt(offer.Expiry.$date.$numberLong); if (soonestOfferExpiry > offerExpiry) { soonestOfferExpiry = offerExpiry; } } - processed.Expiry.$date.$numberLong = soonestOfferExpiry.toString(); + info.Expiry.$date.$numberLong = soonestOfferExpiry.toString(); } - return { - VendorInfo: processed - }; + return cacheEntry; }; diff --git a/src/types/vendorTypes.ts b/src/types/vendorTypes.ts index 2976ce07..9249a9a0 100644 --- a/src/types/vendorTypes.ts +++ b/src/types/vendorTypes.ts @@ -15,10 +15,16 @@ export interface IItemManifest { QuantityMultiplier: number; Expiry: IMongoDate; // Either a date in the distant future or a period in milliseconds for preprocessing. PurchaseQuantityLimit?: number; + Affiliation?: string; + MinAffiliationRank?: number; + ReductionPerPositiveRank?: number; + IncreasePerNegativeRank?: number; RotatedWeekly?: boolean; AllowMultipurchase: boolean; LocTagRandSeed?: number | bigint; Id: IOid; + RegularPriceBeforeDiscount?: number[]; + ItemPricesBeforeDiscount?: IItemPrice[]; } export interface IVendorInfo { -- 2.47.2 From 4cfca8284ad949f1780fd698a93fde36acb2edd9 Mon Sep 17 00:00:00 2001 From: Sainan <63328889+Sainan@users.noreply.github.com> Date: Fri, 16 May 2025 09:12:36 +0200 Subject: [PATCH 2/4] move import --- src/services/serversideVendorsService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/serversideVendorsService.ts b/src/services/serversideVendorsService.ts index 135e0bb4..cc5659a3 100644 --- a/src/services/serversideVendorsService.ts +++ b/src/services/serversideVendorsService.ts @@ -1,5 +1,6 @@ import { unixTimesInMs } from "@/src/constants/timeConstants"; import { catBreadHash } from "@/src/helpers/stringHelpers"; +import { TInventoryDatabaseDocument } from "@/src/models/inventoryModels/inventoryModel"; import { mixSeeds, SRng } from "@/src/services/rngService"; import { IMongoDate } from "@/src/types/commonTypes"; import { IItemManifest, IVendorInfo, IVendorManifest } from "@/src/types/vendorTypes"; @@ -30,7 +31,6 @@ import SolarisProspectorVendorManifest from "@/static/fixed_responses/getVendorI import Temple1999VendorManifest from "@/static/fixed_responses/getVendorInfo/Temple1999VendorManifest.json"; import TeshinHardModeVendorManifest from "@/static/fixed_responses/getVendorInfo/TeshinHardModeVendorManifest.json"; import ZarimanCommisionsManifestArchimedean from "@/static/fixed_responses/getVendorInfo/ZarimanCommisionsManifestArchimedean.json"; -import { TInventoryDatabaseDocument } from "../models/inventoryModels/inventoryModel"; const rawVendorManifests: IVendorManifest[] = [ ArchimedeanVendorManifest, -- 2.47.2 From 12bdfae1cfbbb9c6cf88c6b627239b88a2b0254a Mon Sep 17 00:00:00 2001 From: Sainan <63328889+Sainan@users.noreply.github.com> Date: Fri, 16 May 2025 09:16:50 +0200 Subject: [PATCH 3/4] simplify --- src/services/serversideVendorsService.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/services/serversideVendorsService.ts b/src/services/serversideVendorsService.ts index cc5659a3..15f2474d 100644 --- a/src/services/serversideVendorsService.ts +++ b/src/services/serversideVendorsService.ts @@ -157,9 +157,7 @@ export const applyStandingToVendorManifest = ( if (offer.Affiliation && offer.ReductionPerPositiveRank && offer.IncreasePerNegativeRank) { const title: number = inventory.Affiliations.find(x => x.Tag == offer.Affiliation)?.Title ?? 0; const factor = - 1 + - (title < 0 ? offer.IncreasePerNegativeRank * title : offer.ReductionPerPositiveRank * title) * - -1; + 1 + (title < 0 ? offer.IncreasePerNegativeRank : offer.ReductionPerPositiveRank) * title * -1; //console.log(offer.Affiliation, title, factor); if (factor) { offer = { ...offer }; -- 2.47.2 From 9dd53adcddb66b5f9d422388a5a3d1038925f65a Mon Sep 17 00:00:00 2001 From: Sainan <63328889+Sainan@users.noreply.github.com> Date: Fri, 16 May 2025 09:18:38 +0200 Subject: [PATCH 4/4] ensure prices stay as ints --- src/services/serversideVendorsService.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/services/serversideVendorsService.ts b/src/services/serversideVendorsService.ts index 15f2474d..3db2aee9 100644 --- a/src/services/serversideVendorsService.ts +++ b/src/services/serversideVendorsService.ts @@ -164,15 +164,15 @@ export const applyStandingToVendorManifest = ( if (offer.RegularPrice) { offer.RegularPriceBeforeDiscount = offer.RegularPrice; offer.RegularPrice = [ - offer.RegularPriceBeforeDiscount[0] * factor, - offer.RegularPriceBeforeDiscount[1] * factor + Math.trunc(offer.RegularPriceBeforeDiscount[0] * factor), + Math.trunc(offer.RegularPriceBeforeDiscount[1] * factor) ]; } if (offer.ItemPrices) { offer.ItemPricesBeforeDiscount = offer.ItemPrices; offer.ItemPrices = []; for (const item of offer.ItemPricesBeforeDiscount) { - offer.ItemPrices.push({ ...item, ItemCount: item.ItemCount * factor }); + offer.ItemPrices.push({ ...item, ItemCount: Math.trunc(item.ItemCount * factor) }); } } } -- 2.47.2