From 42782589e0a16a66faeae554c83db6867a555774 Mon Sep 17 00:00:00 2001 From: phrisk Date: Tue, 27 Feb 2024 23:45:47 +0000 Subject: [PATCH 1/3] Return after returning player offers to avoid redundant remove offer following (!237) #### Summary > Should fix [Issue 523](https://dev.sp-tarkov.com/SPT-AKI/Issues/issues/523) Tested a few different scenarios and the items were always correctly removed and returned to the player, however I did see the console warnings reported in the issue. Both of the following call `this.ragfairOfferHandler.removeOffer(...)`: - `this.returnPlayerOffer(...)` - `this.removeOfferById(...)` The latter logs a warning to the console because the offer is already removed when we reach that point. This fix simply returns after the offer has been returned to the player, letting `returnPlayerOffer` deal with removing from the flea Co-authored-by: phrisk Reviewed-on: https://dev.sp-tarkov.com/SPT-AKI/Server/pulls/237 Co-authored-by: phrisk Co-committed-by: phrisk --- project/src/services/RagfairOfferService.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/project/src/services/RagfairOfferService.ts b/project/src/services/RagfairOfferService.ts index 900b6596..7b12174c 100644 --- a/project/src/services/RagfairOfferService.ts +++ b/project/src/services/RagfairOfferService.ts @@ -229,6 +229,7 @@ export class RagfairOfferService if (isPlayer && staleOffer.endTime <= this.timeUtil.getTimestamp()) { this.returnPlayerOffer(staleOffer); + return; } // Remove expired existing offer from global offers From 0433308fa800fb51b1ebe6e5e0fb752e8373c0df Mon Sep 17 00:00:00 2001 From: DrakiaXYZ Date: Wed, 28 Feb 2024 07:55:39 +0000 Subject: [PATCH 2/3] Fix tools not coming back with the right properties after crafting (!238) This resolves an issue where FiR tools were coming back as non-FiR. We now just store the whole .upd property, so any stats on the tool should carry over to after the craft is finished Note: Will break any in-progress crafts that use tools Co-authored-by: DrakiaXYZ <565558+TheDgtl@users.noreply.github.com> Reviewed-on: https://dev.sp-tarkov.com/SPT-AKI/Server/pulls/238 Co-authored-by: DrakiaXYZ Co-committed-by: DrakiaXYZ --- project/src/controllers/HideoutController.ts | 39 ++++++++----------- project/src/helpers/HideoutHelper.ts | 26 +++++++++++-- .../src/models/eft/common/tables/IBotBase.ts | 4 +- 3 files changed, 41 insertions(+), 28 deletions(-) diff --git a/project/src/controllers/HideoutController.ts b/project/src/controllers/HideoutController.ts index 4f8679d9..e3d2c225 100644 --- a/project/src/controllers/HideoutController.ts +++ b/project/src/controllers/HideoutController.ts @@ -920,19 +920,7 @@ export class HideoutController { for (const tool of production.sptRequiredTools) { - const toolToAdd: Item = { - _id: this.hashUtil.generate(), - _tpl: tool, - }; - - if (this.itemHelper.isItemTplStackable(tool)) - { - toolToAdd.upd = { - StackObjectsCount: 1, - } - } - - toolsToSendToPlayer.push([toolToAdd]); + toolsToSendToPlayer.push([tool]); } } @@ -963,17 +951,22 @@ export class HideoutController return; } - // Add the used tools to the stash as non-FiR - const addToolsRequest: IAddItemsDirectRequest = { - itemsWithModsToAdd: toolsToSendToPlayer, - foundInRaid: false, - useSortingTable: false, - callback: null, - }; - this.inventoryHelper.addItemsToStash(sessionID, addToolsRequest, pmcData, output); - if (output.warnings.length > 0) + // Add the tools to the stash, we have to do this individually due to FiR state potentially being different + for (const toolItem of toolsToSendToPlayer) { - return; + // Note: FIR state will be based on the first item's SpawnedInSession property per item group + const addToolsRequest: IAddItemsDirectRequest = { + itemsWithModsToAdd: [toolItem], + foundInRaid: toolItem[0].upd?.SpawnedInSession ?? false, + useSortingTable: false, + callback: null, + }; + + this.inventoryHelper.addItemsToStash(sessionID, addToolsRequest, pmcData, output); + if (output.warnings.length > 0) + { + return; + } } // Add the crafting result to the stash, marked as FiR diff --git a/project/src/helpers/HideoutHelper.ts b/project/src/helpers/HideoutHelper.ts index a9da0695..559f873f 100644 --- a/project/src/helpers/HideoutHelper.ts +++ b/project/src/helpers/HideoutHelper.ts @@ -27,6 +27,7 @@ import { PlayerService } from "@spt-aki/services/PlayerService"; import { HashUtil } from "@spt-aki/utils/HashUtil"; import { HttpResponseUtil } from "@spt-aki/utils/HttpResponseUtil"; import { TimeUtil } from "@spt-aki/utils/TimeUtil"; +import { JsonUtil } from "@spt-aki/utils/JsonUtil"; @injectable() export class HideoutHelper @@ -53,6 +54,7 @@ export class HideoutHelper @inject("LocalisationService") protected localisationService: LocalisationService, @inject("ItemHelper") protected itemHelper: ItemHelper, @inject("ConfigServer") protected configServer: ConfigServer, + @inject("JsonUtil") protected jsonUtil: JsonUtil, ) { this.hideoutConfig = this.configServer.getConfig(ConfigTypes.HIDEOUT); @@ -102,10 +104,28 @@ export class HideoutHelper ); // Store the tools used for this production, so we can return them later - const productionTools = recipe.requirements.filter(req => req.type === "Tool").map(req => req.templateId); - if (productionTools.length > 0) + const bodyAsSingle = body as IHideoutSingleProductionStartRequestData; + if (bodyAsSingle && bodyAsSingle.tools.length > 0) { - production.sptRequiredTools = productionTools; + production.sptRequiredTools = []; + + for (const tool of bodyAsSingle.tools) + { + const toolItem = this.jsonUtil.clone(pmcData.Inventory.items.find(x => x._id === tool.id)); + + // Make sure we only return as many as we took + if (!toolItem.upd) + { + toolItem.upd = {}; + } + toolItem.upd.StackObjectsCount = tool.count; + + production.sptRequiredTools.push({ + _id: this.hashUtil.generate(), + _tpl: toolItem._tpl, + upd: toolItem.upd + }); + } } pmcData.Hideout.Production[body.recipeId] = production; diff --git a/project/src/models/eft/common/tables/IBotBase.ts b/project/src/models/eft/common/tables/IBotBase.ts index 16627b6a..f308346f 100644 --- a/project/src/models/eft/common/tables/IBotBase.ts +++ b/project/src/models/eft/common/tables/IBotBase.ts @@ -393,8 +393,8 @@ export interface Productive sptIsComplete?: boolean; /** Is the craft a Continuous, e.g bitcoins/water collector */ sptIsContinuous?: boolean; - /** Stores a list of tools used in this craft, to give back once the craft is done */ - sptRequiredTools?: string[]; + /** Stores a list of tools used in this craft and whether they're FiR, to give back once the craft is done */ + sptRequiredTools?: Item[]; } export interface Production extends Productive From 4b8b62ae8344cbc2a42135ca9225089809832873 Mon Sep 17 00:00:00 2001 From: DrakiaXYZ Date: Wed, 28 Feb 2024 07:56:53 +0000 Subject: [PATCH 3/3] Utilize the release callback returned by `lockFileSync` to release the lock file (!239) The error people are getting about a lock file already existing is due to `checkFileSync` returning false if the lock file is "stale". The default "stale" timeout is 10 seconds, so if a save takes longer than this, the user will end up in a state where they can no longer save. The documentation for `proper-lockfile` recommends using the callback returned by `lockFileSync` to remove the lock file, so I've switched to using this, and the error no longer occurs with long running save operations Co-authored-by: DrakiaXYZ <565558+TheDgtl@users.noreply.github.com> Reviewed-on: https://dev.sp-tarkov.com/SPT-AKI/Server/pulls/239 Co-authored-by: DrakiaXYZ Co-committed-by: DrakiaXYZ --- project/src/utils/VFS.ts | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/project/src/utils/VFS.ts b/project/src/utils/VFS.ts index fa24c8ce..8e44fc30 100644 --- a/project/src/utils/VFS.ts +++ b/project/src/utils/VFS.ts @@ -175,7 +175,7 @@ export class VFS fs.writeFileSync(filepath, ""); } - this.lockFileSync(filepath); + const releaseCallback = this.lockFileSync(filepath); if (!append && atomic) { @@ -186,10 +186,7 @@ export class VFS fs.writeFileSync(filepath, data, options); } - if (this.checkFileSync(filepath)) - { - this.unlockFileSync(filepath); - } + releaseCallback(); } public async writeFileAsync(filepath: any, data = "", append = false, atomic = true): Promise @@ -307,12 +304,12 @@ export class VFS await this.renamePromisify(oldPath, newPath); } - protected lockFileSync(filepath: any): void + protected lockFileSync(filepath: any): () => void { - lockfile.lockSync(filepath); + return lockfile.lockSync(filepath); } - protected checkFileSync(filepath: any): any + protected checkFileSync(filepath: any): boolean { return lockfile.checkSync(filepath); }