From 49414b8805d34ce9da8ef673f411b449ab627de0 Mon Sep 17 00:00:00 2001 From: DrakiaXYZ <565558+DrakiaXYZ@users.noreply.github.com> Date: Mon, 23 Dec 2024 05:26:10 -0800 Subject: [PATCH] Fix issue where insurance messages would appear as received if you only lost gear you were wearing (#993) - Insurance messages each need a unique "stash" ID, properly handle this as part of the processing side of insurance - Remove handling of parent IDs from the insurance storing side of insurance, as we don't need to do that processing twice - Add a new step to the profileFixer service that resolves any already broken trader messages with attached insurance items --------- Co-authored-by: DrakiaXYZ <565558+TheDgtl@users.noreply.github.com> Co-authored-by: Chomp <27521899+chompDev@users.noreply.github.com> --- project/src/controllers/GameController.ts | 4 +++ .../src/controllers/InsuranceController.ts | 15 +++++---- project/src/services/InsuranceService.ts | 28 ---------------- project/src/services/ProfileFixerService.ts | 32 +++++++++++++++++++ 4 files changed, 45 insertions(+), 34 deletions(-) diff --git a/project/src/controllers/GameController.ts b/project/src/controllers/GameController.ts index 394d9c75..da5fd34b 100644 --- a/project/src/controllers/GameController.ts +++ b/project/src/controllers/GameController.ts @@ -148,6 +148,10 @@ export class GameController { fullProfile.characters.scav.WishList = {}; } + if (fullProfile.dialogues) { + this.profileFixerService.checkForAndFixDialogueAttachments(fullProfile); + } + this.logger.debug(`Started game with sessionId: ${sessionID} ${fullProfile.info.username}`); const pmcProfile = fullProfile.characters.pmc; diff --git a/project/src/controllers/InsuranceController.ts b/project/src/controllers/InsuranceController.ts index de5a978e..c848d0d0 100644 --- a/project/src/controllers/InsuranceController.ts +++ b/project/src/controllers/InsuranceController.ts @@ -123,11 +123,14 @@ export class InsuranceController { )} items, in profile ${sessionID}`, ); - // Fetch the root Item parentId property value that should be used for insurance packages. - const rootItemParentID = this.insuranceService.getRootItemParentID(sessionID); - // Iterate over each of the insurance packages. for (const insured of insuranceDetails) { + // Create a new root parent ID for the message we'll be sending the player + const rootItemParentID = this.hashUtil.generate(); + + // Update the insured items to have the new root parent ID for root/orphaned items + insured.items = this.itemHelper.adoptOrphanedItems(rootItemParentID, insured.items); + const simulateItemsBeingTaken = this.insuranceConfig.simulateItemsBeingTaken; if (simulateItemsBeingTaken) { // Find items that could be taken by another player off the players body @@ -135,10 +138,10 @@ export class InsuranceController { // Actually remove them. this.removeItemsFromInsurance(insured, itemsToDelete); - } - // Ensure that all items have a valid parent. - insured.items = this.itemHelper.adoptOrphanedItems(rootItemParentID, insured.items); + // There's a chance we've orphaned weapon attachments, so adopt any orphaned items again + insured.items = this.itemHelper.adoptOrphanedItems(rootItemParentID, insured.items); + } // Send the mail to the player. this.sendMail(sessionID, insured); diff --git a/project/src/services/InsuranceService.ts b/project/src/services/InsuranceService.ts index 339dbca9..021639d1 100644 --- a/project/src/services/InsuranceService.ts +++ b/project/src/services/InsuranceService.ts @@ -176,21 +176,6 @@ export class InsuranceService { return this.timeUtil.getTimestamp() + finalReturnTimeSeconds; } - /** - * Take the insurance item packages within a profile session and ensure that each of the items in that package are - * not orphaned from their parent ID. - * - * @param sessionID The session ID to update insurance equipment packages in. - * @returns void - */ - protected adoptOrphanedInsEquipment(sessionID: string): void { - const rootID = this.getRootItemParentID(sessionID); - const insuranceData = this.getInsurance(sessionID); - for (const [traderId, items] of Object.entries(insuranceData)) { - this.insured[sessionID][traderId] = this.itemHelper.adoptOrphanedItems(rootID, items); - } - } - protected getMaxInsuranceStorageTime(traderBase: ITraderBase): number { if (this.insuranceConfig.storageTimeOverrideSeconds > 0) { // Override exists, use instead of traders value @@ -209,9 +194,6 @@ export class InsuranceService { for (const gear of equipmentPkg) { this.addGearToSend(gear); } - - // Items are separated into their individual trader packages, now we can ensure that they all have valid parents - this.adoptOrphanedInsEquipment(sessionID); } /** @@ -347,14 +329,4 @@ export class InsuranceService { return Math.ceil(price); } - - /** - * Returns the ID that should be used for a root-level Item's parentId property value within in the context of insurance. - * @param sessionID Players id - * @returns The root item Id. - */ - public getRootItemParentID(sessionID: string): string { - // Try to use the equipment id from the profile. I'm not sure this is strictly required, but it feels neat. - return this.saveServer.getProfile(sessionID)?.characters?.pmc?.Inventory?.equipment ?? this.hashUtil.generate(); - } } diff --git a/project/src/services/ProfileFixerService.ts b/project/src/services/ProfileFixerService.ts index 61a58c45..c390db80 100644 --- a/project/src/services/ProfileFixerService.ts +++ b/project/src/services/ProfileFixerService.ts @@ -75,6 +75,38 @@ export class ProfileFixerService { } } + /** + * Resolve any dialogue attachments that were accidentally created using the player's equipment ID as + * the stash root object ID + * @param fullProfile + */ + public checkForAndFixDialogueAttachments(fullProfile: ISptProfile): void { + for (const traderDialogues of Object.values(fullProfile.dialogues)) { + for (const message of traderDialogues?.messages) { + // Skip any messages without attached items + if (!message.items?.data || !message.items?.stash) { + continue; + } + + // Skip any messages that don't have a stashId collision with the player's equipment ID + if (message.items?.stash !== fullProfile.characters?.pmc?.Inventory?.equipment) { + continue; + } + + // Otherwise we need to generate a new unique stash ID for this message's attachments + message.items.stash = this.hashUtil.generate(); + message.items.data = this.itemHelper.adoptOrphanedItems(message.items.stash, message.items.data); + + // Because `adoptOrphanedItems` sets the slotId to `hideout`, we need to re-set it to `main` to work with mail + for (const item of message.items.data) { + if (item.slotId === "hideout") { + item.slotId = "main"; + } + } + } + } + } + /** * Find issues in the scav profile data that may cause issues * @param scavProfile profile to check and fix