Introduced a new ICloner interface with 3 implementations, one of them being a recursive cloner which is faster and more efficient than its counterparts by more than 50%

This commit is contained in:
clodan 2024-05-11 19:57:38 +01:00
parent ba1ac09b0b
commit 793d884293
55 changed files with 304 additions and 127 deletions

View File

@ -24,6 +24,7 @@ import { BotGenerationCacheService } from "@spt-aki/services/BotGenerationCacheS
import { LocalisationService } from "@spt-aki/services/LocalisationService"; import { LocalisationService } from "@spt-aki/services/LocalisationService";
import { MatchBotDetailsCacheService } from "@spt-aki/services/MatchBotDetailsCacheService"; import { MatchBotDetailsCacheService } from "@spt-aki/services/MatchBotDetailsCacheService";
import { SeasonalEventService } from "@spt-aki/services/SeasonalEventService"; import { SeasonalEventService } from "@spt-aki/services/SeasonalEventService";
import { ICloner } from "@spt-aki/utils/cloners/ICloner";
import { JsonUtil } from "@spt-aki/utils/JsonUtil"; import { JsonUtil } from "@spt-aki/utils/JsonUtil";
import { RandomUtil } from "@spt-aki/utils/RandomUtil"; import { RandomUtil } from "@spt-aki/utils/RandomUtil";
@ -48,6 +49,7 @@ export class BotController
@inject("ApplicationContext") protected applicationContext: ApplicationContext, @inject("ApplicationContext") protected applicationContext: ApplicationContext,
@inject("RandomUtil") protected randomUtil: RandomUtil, @inject("RandomUtil") protected randomUtil: RandomUtil,
@inject("JsonUtil") protected jsonUtil: JsonUtil, @inject("JsonUtil") protected jsonUtil: JsonUtil,
@inject("RecursiveCloner") protected cloner: ICloner,
) )
{ {
this.botConfig = this.configServer.getConfig(ConfigTypes.BOT); this.botConfig = this.configServer.getConfig(ConfigTypes.BOT);
@ -281,7 +283,7 @@ export class BotController
const botPromises: Promise<void>[] = []; const botPromises: Promise<void>[] = [];
for (let i = 0; i < botGenerationDetails.botCountToGenerate; i++) for (let i = 0; i < botGenerationDetails.botCountToGenerate; i++)
{ {
const detailsClone = this.jsonUtil.clone(botGenerationDetails); const detailsClone = this.cloner.clone(botGenerationDetails);
botPromises.push(this.generateSingleBotAndStoreInCache(detailsClone, sessionId, cacheKey)); botPromises.push(this.generateSingleBotAndStoreInCache(detailsClone, sessionId, cacheKey));
} }
return Promise.all(botPromises).then(() => return Promise.all(botPromises).then(() =>

View File

@ -10,6 +10,7 @@ import { ILogger } from "@spt-aki/models/spt/utils/ILogger";
import { EventOutputHolder } from "@spt-aki/routers/EventOutputHolder"; import { EventOutputHolder } from "@spt-aki/routers/EventOutputHolder";
import { DatabaseServer } from "@spt-aki/servers/DatabaseServer"; import { DatabaseServer } from "@spt-aki/servers/DatabaseServer";
import { SaveServer } from "@spt-aki/servers/SaveServer"; import { SaveServer } from "@spt-aki/servers/SaveServer";
import { ICloner } from "@spt-aki/utils/cloners/ICloner";
import { HashUtil } from "@spt-aki/utils/HashUtil"; import { HashUtil } from "@spt-aki/utils/HashUtil";
import { JsonUtil } from "@spt-aki/utils/JsonUtil"; import { JsonUtil } from "@spt-aki/utils/JsonUtil";
@ -25,6 +26,7 @@ export class BuildController
@inject("ProfileHelper") protected profileHelper: ProfileHelper, @inject("ProfileHelper") protected profileHelper: ProfileHelper,
@inject("ItemHelper") protected itemHelper: ItemHelper, @inject("ItemHelper") protected itemHelper: ItemHelper,
@inject("SaveServer") protected saveServer: SaveServer, @inject("SaveServer") protected saveServer: SaveServer,
@inject("RecursiveCloner") protected cloner: ICloner,
) )
{} {}
@ -39,7 +41,7 @@ export class BuildController
} }
// Ensure the secure container in the default presets match what the player has equipped // Ensure the secure container in the default presets match what the player has equipped
const defaultEquipmentPresetsClone = this.jsonUtil.clone( const defaultEquipmentPresetsClone = this.cloner.clone(
this.databaseServer.getTables().templates.defaultEquipmentPresets, this.databaseServer.getTables().templates.defaultEquipmentPresets,
); );
const playerSecureContainer = profile.characters.pmc.Inventory.items?.find(x => const playerSecureContainer = profile.characters.pmc.Inventory.items?.find(x =>
@ -63,7 +65,7 @@ export class BuildController
} }
// Clone player build data from profile and append the above defaults onto end // Clone player build data from profile and append the above defaults onto end
const userBuildsClone = this.jsonUtil.clone(profile.userbuilds); const userBuildsClone = this.cloner.clone(profile.userbuilds);
userBuildsClone.equipmentBuilds.push(...defaultEquipmentPresetsClone); userBuildsClone.equipmentBuilds.push(...defaultEquipmentPresetsClone);
return userBuildsClone; return userBuildsClone;

View File

@ -44,6 +44,7 @@ import { ProfileActivityService } from "@spt-aki/services/ProfileActivityService
import { ProfileFixerService } from "@spt-aki/services/ProfileFixerService"; import { ProfileFixerService } from "@spt-aki/services/ProfileFixerService";
import { RaidTimeAdjustmentService } from "@spt-aki/services/RaidTimeAdjustmentService"; import { RaidTimeAdjustmentService } from "@spt-aki/services/RaidTimeAdjustmentService";
import { SeasonalEventService } from "@spt-aki/services/SeasonalEventService"; import { SeasonalEventService } from "@spt-aki/services/SeasonalEventService";
import { ICloner } from "@spt-aki/utils/cloners/ICloner";
import { HashUtil } from "@spt-aki/utils/HashUtil"; import { HashUtil } from "@spt-aki/utils/HashUtil";
import { JsonUtil } from "@spt-aki/utils/JsonUtil"; import { JsonUtil } from "@spt-aki/utils/JsonUtil";
import { RandomUtil } from "@spt-aki/utils/RandomUtil"; import { RandomUtil } from "@spt-aki/utils/RandomUtil";
@ -83,6 +84,7 @@ export class GameController
@inject("ProfileActivityService") protected profileActivityService: ProfileActivityService, @inject("ProfileActivityService") protected profileActivityService: ProfileActivityService,
@inject("ApplicationContext") protected applicationContext: ApplicationContext, @inject("ApplicationContext") protected applicationContext: ApplicationContext,
@inject("ConfigServer") protected configServer: ConfigServer, @inject("ConfigServer") protected configServer: ConfigServer,
@inject("RecursiveCloner") protected cloner: ICloner,
) )
{ {
this.httpConfig = this.configServer.getConfig(ConfigTypes.HTTP); this.httpConfig = this.configServer.getConfig(ConfigTypes.HTTP);
@ -321,7 +323,7 @@ export class GameController
this.logger.warning( this.logger.warning(
`Trader ${trader.base._id} ${trader.base.nickname} is missing a repair object, adding in default values`, `Trader ${trader.base._id} ${trader.base.nickname} is missing a repair object, adding in default values`,
); );
trader.base.repair = this.jsonUtil.clone(this.databaseServer.getTables().traders.ragfair.base.repair); trader.base.repair = this.cloner.clone(this.databaseServer.getTables().traders.ragfair.base.repair);
return; return;
} }
@ -331,7 +333,7 @@ export class GameController
this.logger.warning( this.logger.warning(
`Trader ${trader.base._id} ${trader.base.nickname} is missing a repair quality value, adding in default value`, `Trader ${trader.base._id} ${trader.base.nickname} is missing a repair quality value, adding in default value`,
); );
trader.base.repair.quality = this.jsonUtil.clone( trader.base.repair.quality = this.cloner.clone(
this.databaseServer.getTables().traders.ragfair.base.repair.quality, this.databaseServer.getTables().traders.ragfair.base.repair.quality,
); );
trader.base.repair.quality = this.databaseServer.getTables().traders.ragfair.base.repair.quality; trader.base.repair.quality = this.databaseServer.getTables().traders.ragfair.base.repair.quality;
@ -791,7 +793,7 @@ export class GameController
for (let index = indexOfWaveToSplit + 1; index < indexOfWaveToSplit + waveSize; index++) for (let index = indexOfWaveToSplit + 1; index < indexOfWaveToSplit + waveSize; index++)
{ {
// Clone wave ready to insert into array // Clone wave ready to insert into array
const waveToAddClone = this.jsonUtil.clone(wave); const waveToAddClone = this.cloner.clone(wave);
// Some waves have value of 0 for some reason, preserve // Some waves have value of 0 for some reason, preserve
if (waveToAddClone.number !== 0) if (waveToAddClone.number !== 0)

View File

@ -15,6 +15,7 @@ import { ILogger } from "@spt-aki/models/spt/utils/ILogger";
import { EventOutputHolder } from "@spt-aki/routers/EventOutputHolder"; import { EventOutputHolder } from "@spt-aki/routers/EventOutputHolder";
import { LocalisationService } from "@spt-aki/services/LocalisationService"; import { LocalisationService } from "@spt-aki/services/LocalisationService";
import { PaymentService } from "@spt-aki/services/PaymentService"; import { PaymentService } from "@spt-aki/services/PaymentService";
import { ICloner } from "@spt-aki/utils/cloners/ICloner";
import { HttpResponseUtil } from "@spt-aki/utils/HttpResponseUtil"; import { HttpResponseUtil } from "@spt-aki/utils/HttpResponseUtil";
import { JsonUtil } from "@spt-aki/utils/JsonUtil"; import { JsonUtil } from "@spt-aki/utils/JsonUtil";
@ -31,6 +32,7 @@ export class HealthController
@inject("LocalisationService") protected localisationService: LocalisationService, @inject("LocalisationService") protected localisationService: LocalisationService,
@inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil, @inject("HttpResponseUtil") protected httpResponse: HttpResponseUtil,
@inject("HealthHelper") protected healthHelper: HealthHelper, @inject("HealthHelper") protected healthHelper: HealthHelper,
@inject("RecursiveCloner") protected cloner: ICloner,
) )
{} {}
@ -215,7 +217,7 @@ export class HealthController
} }
// Inform client of new post-raid, post-therapist heal values // Inform client of new post-raid, post-therapist heal values
output.profileChanges[sessionID].health = this.jsonUtil.clone(pmcData.Health); output.profileChanges[sessionID].health = this.cloner.clone(pmcData.Health);
return output; return output;
} }

View File

@ -48,6 +48,7 @@ import { FenceService } from "@spt-aki/services/FenceService";
import { LocalisationService } from "@spt-aki/services/LocalisationService"; import { LocalisationService } from "@spt-aki/services/LocalisationService";
import { PlayerService } from "@spt-aki/services/PlayerService"; import { PlayerService } from "@spt-aki/services/PlayerService";
import { ProfileActivityService } from "@spt-aki/services/ProfileActivityService"; import { ProfileActivityService } from "@spt-aki/services/ProfileActivityService";
import { ICloner } from "@spt-aki/utils/cloners/ICloner";
import { HashUtil } from "@spt-aki/utils/HashUtil"; import { HashUtil } from "@spt-aki/utils/HashUtil";
import { HttpResponseUtil } from "@spt-aki/utils/HttpResponseUtil"; import { HttpResponseUtil } from "@spt-aki/utils/HttpResponseUtil";
import { JsonUtil } from "@spt-aki/utils/JsonUtil"; import { JsonUtil } from "@spt-aki/utils/JsonUtil";
@ -83,6 +84,7 @@ export class HideoutController
@inject("ConfigServer") protected configServer: ConfigServer, @inject("ConfigServer") protected configServer: ConfigServer,
@inject("JsonUtil") protected jsonUtil: JsonUtil, @inject("JsonUtil") protected jsonUtil: JsonUtil,
@inject("FenceService") protected fenceService: FenceService, @inject("FenceService") protected fenceService: FenceService,
@inject("RecursiveCloner") protected cloner: ICloner,
) )
{ {
this.hideoutConfig = this.configServer.getConfig(ConfigTypes.HIDEOUT); this.hideoutConfig = this.configServer.getConfig(ConfigTypes.HIDEOUT);
@ -594,7 +596,7 @@ export class HideoutController
const recipe = this.databaseServer.getTables().hideout.production.find(p => p._id === body.recipeId); const recipe = this.databaseServer.getTables().hideout.production.find(p => p._id === body.recipeId);
// Find the actual amount of items we need to remove because body can send weird data // Find the actual amount of items we need to remove because body can send weird data
const recipeRequirementsClone = this.jsonUtil.clone( const recipeRequirementsClone = this.cloner.clone(
recipe.requirements.filter(i => i.type === "Item" || i.type === "Tool"), recipe.requirements.filter(i => i.type === "Item" || i.type === "Tool"),
); );

View File

@ -56,6 +56,7 @@ export class InsuranceController
@inject("MailSendService") protected mailSendService: MailSendService, @inject("MailSendService") protected mailSendService: MailSendService,
@inject("RagfairPriceService") protected ragfairPriceService: RagfairPriceService, @inject("RagfairPriceService") protected ragfairPriceService: RagfairPriceService,
@inject("ConfigServer") protected configServer: ConfigServer, @inject("ConfigServer") protected configServer: ConfigServer,
@inject("RecursiveCloner") protected cloner: ICloner,
) )
{ {
this.insuranceConfig = this.configServer.getConfig(ConfigTypes.INSURANCE); this.insuranceConfig = this.configServer.getConfig(ConfigTypes.INSURANCE);
@ -458,7 +459,7 @@ export class InsuranceController
const countOfAttachmentsToRemove = this.getAttachmentCountToRemove(weightedAttachmentByPrice, traderId); const countOfAttachmentsToRemove = this.getAttachmentCountToRemove(weightedAttachmentByPrice, traderId);
// Create prob array and add all attachments with rouble price as the weight // Create prob array and add all attachments with rouble price as the weight
const attachmentsProbabilityArray = new ProbabilityObjectArray<string, number>(this.mathUtil, this.jsonUtil); const attachmentsProbabilityArray = new ProbabilityObjectArray<string, number>(this.mathUtil, this.cloner);
for (const attachmentTpl of Object.keys(weightedAttachmentByPrice)) for (const attachmentTpl of Object.keys(weightedAttachmentByPrice))
{ {
attachmentsProbabilityArray.push( attachmentsProbabilityArray.push(

View File

@ -41,6 +41,7 @@ import { FenceService } from "@spt-aki/services/FenceService";
import { LocalisationService } from "@spt-aki/services/LocalisationService"; import { LocalisationService } from "@spt-aki/services/LocalisationService";
import { PlayerService } from "@spt-aki/services/PlayerService"; import { PlayerService } from "@spt-aki/services/PlayerService";
import { RagfairOfferService } from "@spt-aki/services/RagfairOfferService"; import { RagfairOfferService } from "@spt-aki/services/RagfairOfferService";
import { ICloner } from "@spt-aki/utils/cloners/ICloner";
import { HashUtil } from "@spt-aki/utils/HashUtil"; import { HashUtil } from "@spt-aki/utils/HashUtil";
import { HttpResponseUtil } from "@spt-aki/utils/HttpResponseUtil"; import { HttpResponseUtil } from "@spt-aki/utils/HttpResponseUtil";
import { JsonUtil } from "@spt-aki/utils/JsonUtil"; import { JsonUtil } from "@spt-aki/utils/JsonUtil";
@ -69,6 +70,7 @@ export class InventoryController
@inject("LootGenerator") protected lootGenerator: LootGenerator, @inject("LootGenerator") protected lootGenerator: LootGenerator,
@inject("EventOutputHolder") protected eventOutputHolder: EventOutputHolder, @inject("EventOutputHolder") protected eventOutputHolder: EventOutputHolder,
@inject("HttpResponseUtil") protected httpResponseUtil: HttpResponseUtil, @inject("HttpResponseUtil") protected httpResponseUtil: HttpResponseUtil,
@inject("RecursiveCloner") protected cloner: ICloner,
) )
{} {}
@ -212,7 +214,7 @@ export class InventoryController
} }
// Create new upd object that retains properties of original upd + new stack count size // Create new upd object that retains properties of original upd + new stack count size
const updatedUpd = this.jsonUtil.clone(itemToSplit.upd); const updatedUpd = this.cloner.clone(itemToSplit.upd);
updatedUpd.StackObjectsCount = request.count; updatedUpd.StackObjectsCount = request.count;
// Remove split item count from source stack // Remove split item count from source stack

View File

@ -23,6 +23,7 @@ import { DatabaseServer } from "@spt-aki/servers/DatabaseServer";
import { ItemFilterService } from "@spt-aki/services/ItemFilterService"; import { ItemFilterService } from "@spt-aki/services/ItemFilterService";
import { LocalisationService } from "@spt-aki/services/LocalisationService"; import { LocalisationService } from "@spt-aki/services/LocalisationService";
import { RaidTimeAdjustmentService } from "@spt-aki/services/RaidTimeAdjustmentService"; import { RaidTimeAdjustmentService } from "@spt-aki/services/RaidTimeAdjustmentService";
import { ICloner } from "@spt-aki/utils/cloners/ICloner";
import { HashUtil } from "@spt-aki/utils/HashUtil"; import { HashUtil } from "@spt-aki/utils/HashUtil";
import { JsonUtil } from "@spt-aki/utils/JsonUtil"; import { JsonUtil } from "@spt-aki/utils/JsonUtil";
import { RandomUtil } from "@spt-aki/utils/RandomUtil"; import { RandomUtil } from "@spt-aki/utils/RandomUtil";
@ -49,6 +50,7 @@ export class LocationController
@inject("TimeUtil") protected timeUtil: TimeUtil, @inject("TimeUtil") protected timeUtil: TimeUtil,
@inject("ConfigServer") protected configServer: ConfigServer, @inject("ConfigServer") protected configServer: ConfigServer,
@inject("ApplicationContext") protected applicationContext: ApplicationContext, @inject("ApplicationContext") protected applicationContext: ApplicationContext,
@inject("RecursiveCloner") protected cloner: ICloner,
) )
{ {
this.airdropConfig = this.configServer.getConfig(ConfigTypes.AIRDROP); this.airdropConfig = this.configServer.getConfig(ConfigTypes.AIRDROP);
@ -80,7 +82,7 @@ export class LocationController
{ {
const db = this.databaseServer.getTables(); const db = this.databaseServer.getTables();
const location: ILocation = db.locations[name]; const location: ILocation = db.locations[name];
const locationBaseClone: ILocationBase = this.jsonUtil.clone(location.base); const locationBaseClone: ILocationBase = this.cloner.clone(location.base);
locationBaseClone.UnixDateTime = this.timeUtil.getTimestamp(); locationBaseClone.UnixDateTime = this.timeUtil.getTimestamp();
@ -97,18 +99,18 @@ export class LocationController
>(); >();
if (raidAdjustments) if (raidAdjustments)
{ {
locationConfigCopy = this.jsonUtil.clone(this.locationConfig); // Clone values so they can be used to reset originals later locationConfigCopy = this.cloner.clone(this.locationConfig); // Clone values so they can be used to reset originals later
this.raidTimeAdjustmentService.makeAdjustmentsToMap(raidAdjustments, locationBaseClone); this.raidTimeAdjustmentService.makeAdjustmentsToMap(raidAdjustments, locationBaseClone);
} }
const staticAmmoDist = this.jsonUtil.clone(location.staticAmmo); const staticAmmoDist = this.cloner.clone(location.staticAmmo);
// Create containers and add loot to them // Create containers and add loot to them
const staticLoot = this.locationGenerator.generateStaticContainers(locationBaseClone, staticAmmoDist); const staticLoot = this.locationGenerator.generateStaticContainers(locationBaseClone, staticAmmoDist);
locationBaseClone.Loot.push(...staticLoot); locationBaseClone.Loot.push(...staticLoot);
// Add dynamic loot to output loot // Add dynamic loot to output loot
const dynamicLootDistClone: ILooseLoot = this.jsonUtil.clone(location.looseLoot); const dynamicLootDistClone: ILooseLoot = this.cloner.clone(location.looseLoot);
const dynamicSpawnPoints: SpawnpointTemplate[] = this.locationGenerator.generateDynamicLoot( const dynamicSpawnPoints: SpawnpointTemplate[] = this.locationGenerator.generateDynamicLoot(
dynamicLootDistClone, dynamicLootDistClone,
staticAmmoDist, staticAmmoDist,

View File

@ -29,6 +29,7 @@ import { LocalisationService } from "@spt-aki/services/LocalisationService";
import { MailSendService } from "@spt-aki/services/MailSendService"; import { MailSendService } from "@spt-aki/services/MailSendService";
import { PlayerService } from "@spt-aki/services/PlayerService"; import { PlayerService } from "@spt-aki/services/PlayerService";
import { SeasonalEventService } from "@spt-aki/services/SeasonalEventService"; import { SeasonalEventService } from "@spt-aki/services/SeasonalEventService";
import { ICloner } from "@spt-aki/utils/cloners/ICloner";
import { HttpResponseUtil } from "@spt-aki/utils/HttpResponseUtil"; import { HttpResponseUtil } from "@spt-aki/utils/HttpResponseUtil";
import { JsonUtil } from "@spt-aki/utils/JsonUtil"; import { JsonUtil } from "@spt-aki/utils/JsonUtil";
import { TimeUtil } from "@spt-aki/utils/TimeUtil"; import { TimeUtil } from "@spt-aki/utils/TimeUtil";
@ -57,6 +58,7 @@ export class QuestController
@inject("SeasonalEventService") protected seasonalEventService: SeasonalEventService, @inject("SeasonalEventService") protected seasonalEventService: SeasonalEventService,
@inject("LocalisationService") protected localisationService: LocalisationService, @inject("LocalisationService") protected localisationService: LocalisationService,
@inject("ConfigServer") protected configServer: ConfigServer, @inject("ConfigServer") protected configServer: ConfigServer,
@inject("RecursiveCloner") protected cloner: ICloner,
) )
{ {
this.questConfig = this.configServer.getConfig(ConfigTypes.QUEST); this.questConfig = this.configServer.getConfig(ConfigTypes.QUEST);
@ -466,10 +468,10 @@ export class QuestController
const completeQuestResponse = this.eventOutputHolder.getOutput(sessionID); const completeQuestResponse = this.eventOutputHolder.getOutput(sessionID);
const completedQuest = this.questHelper.getQuestFromDb(body.qid, pmcData); const completedQuest = this.questHelper.getQuestFromDb(body.qid, pmcData);
const preCompleteProfileQuests = this.jsonUtil.clone(pmcData.Quests); const preCompleteProfileQuests = this.cloner.clone(pmcData.Quests);
const completedQuestId = body.qid; const completedQuestId = body.qid;
const clientQuestsClone = this.jsonUtil.clone(this.getClientQuests(sessionID)); // Must be gathered prior to applyQuestReward() & failQuests() const clientQuestsClone = this.cloner.clone(this.getClientQuests(sessionID)); // Must be gathered prior to applyQuestReward() & failQuests()
const newQuestState = QuestStatus.Success; const newQuestState = QuestStatus.Success;
this.questHelper.updateQuestState(pmcData, newQuestState, completedQuestId); this.questHelper.updateQuestState(pmcData, newQuestState, completedQuestId);

View File

@ -26,6 +26,7 @@ import { ConfigServer } from "@spt-aki/servers/ConfigServer";
import { DatabaseServer } from "@spt-aki/servers/DatabaseServer"; import { DatabaseServer } from "@spt-aki/servers/DatabaseServer";
import { PaymentService } from "@spt-aki/services/PaymentService"; import { PaymentService } from "@spt-aki/services/PaymentService";
import { ProfileFixerService } from "@spt-aki/services/ProfileFixerService"; import { ProfileFixerService } from "@spt-aki/services/ProfileFixerService";
import { ICloner } from "@spt-aki/utils/cloners/ICloner";
import { HttpResponseUtil } from "@spt-aki/utils/HttpResponseUtil"; import { HttpResponseUtil } from "@spt-aki/utils/HttpResponseUtil";
import { JsonUtil } from "@spt-aki/utils/JsonUtil"; import { JsonUtil } from "@spt-aki/utils/JsonUtil";
import { ObjectId } from "@spt-aki/utils/ObjectId"; import { ObjectId } from "@spt-aki/utils/ObjectId";
@ -53,6 +54,7 @@ export class RepeatableQuestController
@inject("RepeatableQuestHelper") protected repeatableQuestHelper: RepeatableQuestHelper, @inject("RepeatableQuestHelper") protected repeatableQuestHelper: RepeatableQuestHelper,
@inject("QuestHelper") protected questHelper: QuestHelper, @inject("QuestHelper") protected questHelper: QuestHelper,
@inject("ConfigServer") protected configServer: ConfigServer, @inject("ConfigServer") protected configServer: ConfigServer,
@inject("RecursiveCloner") protected cloner: ICloner,
) )
{ {
this.questConfig = this.configServer.getConfig(ConfigTypes.QUEST); this.questConfig = this.configServer.getConfig(ConfigTypes.QUEST);
@ -458,7 +460,7 @@ export class RepeatableQuestController
); );
// Get cost to replace existing quest // Get cost to replace existing quest
changeRequirement = this.jsonUtil.clone(currentRepeatablePool.changeRequirement[changeRequest.qid]); changeRequirement = this.cloner.clone(currentRepeatablePool.changeRequirement[changeRequest.qid]);
delete currentRepeatablePool.changeRequirement[changeRequest.qid]; delete currentRepeatablePool.changeRequirement[changeRequest.qid];
// TODO: somehow we need to reduce the questPool by the currently active quests (for all repeatables) // TODO: somehow we need to reduce the questPool by the currently active quests (for all repeatables)
@ -490,7 +492,7 @@ export class RepeatableQuestController
} }
// Found and replaced the quest in current repeatable // Found and replaced the quest in current repeatable
repeatableToChange = this.jsonUtil.clone(currentRepeatablePool); repeatableToChange = this.cloner.clone(currentRepeatablePool);
delete repeatableToChange.inactiveQuests; delete repeatableToChange.inactiveQuests;
break; break;

View File

@ -13,6 +13,7 @@ import { DatabaseServer } from "@spt-aki/servers/DatabaseServer";
import { FenceService } from "@spt-aki/services/FenceService"; import { FenceService } from "@spt-aki/services/FenceService";
import { TraderAssortService } from "@spt-aki/services/TraderAssortService"; import { TraderAssortService } from "@spt-aki/services/TraderAssortService";
import { TraderPurchasePersisterService } from "@spt-aki/services/TraderPurchasePersisterService"; import { TraderPurchasePersisterService } from "@spt-aki/services/TraderPurchasePersisterService";
import { ICloner } from "@spt-aki/utils/cloners/ICloner";
import { JsonUtil } from "@spt-aki/utils/JsonUtil"; import { JsonUtil } from "@spt-aki/utils/JsonUtil";
import { TimeUtil } from "@spt-aki/utils/TimeUtil"; import { TimeUtil } from "@spt-aki/utils/TimeUtil";
@ -34,6 +35,7 @@ export class TraderController
@inject("FenceBaseAssortGenerator") protected fenceBaseAssortGenerator: FenceBaseAssortGenerator, @inject("FenceBaseAssortGenerator") protected fenceBaseAssortGenerator: FenceBaseAssortGenerator,
@inject("JsonUtil") protected jsonUtil: JsonUtil, @inject("JsonUtil") protected jsonUtil: JsonUtil,
@inject("ConfigServer") protected configServer: ConfigServer, @inject("ConfigServer") protected configServer: ConfigServer,
@inject("RecursiveCloner") protected cloner: ICloner,
) )
{ {
this.traderConfig = this.configServer.getConfig(ConfigTypes.TRADER); this.traderConfig = this.configServer.getConfig(ConfigTypes.TRADER);
@ -67,7 +69,7 @@ export class TraderController
// Create dict of trader assorts on server start // Create dict of trader assorts on server start
if (!this.traderAssortService.getPristineTraderAssort(traderId)) if (!this.traderAssortService.getPristineTraderAssort(traderId))
{ {
const assortsClone = this.jsonUtil.clone(trader.assort); const assortsClone = this.cloner.clone(trader.assort);
this.traderAssortService.setPristineTraderAssort(traderId, assortsClone); this.traderAssortService.setPristineTraderAssort(traderId, assortsClone);
} }

View File

@ -240,6 +240,10 @@ import { TraderPurchasePersisterService } from "@spt-aki/services/TraderPurchase
import { TraderServicesService } from "@spt-aki/services/TraderServicesService"; import { TraderServicesService } from "@spt-aki/services/TraderServicesService";
import { App } from "@spt-aki/utils/App"; import { App } from "@spt-aki/utils/App";
import { AsyncQueue } from "@spt-aki/utils/AsyncQueue"; import { AsyncQueue } from "@spt-aki/utils/AsyncQueue";
import type { ICloner } from "@spt-aki/utils/cloners/ICloner";
import { JsonCloner } from "@spt-aki/utils/cloners/JsonCloner";
import { RecursiveCloner } from "@spt-aki/utils/cloners/RecursiveCloner";
import { StructuredCloner } from "@spt-aki/utils/cloners/StructuredCloner";
import { CompareUtil } from "@spt-aki/utils/CompareUtil"; import { CompareUtil } from "@spt-aki/utils/CompareUtil";
import { DatabaseImporter } from "@spt-aki/utils/DatabaseImporter"; import { DatabaseImporter } from "@spt-aki/utils/DatabaseImporter";
import { EncodingUtil } from "@spt-aki/utils/EncodingUtil"; import { EncodingUtil } from "@spt-aki/utils/EncodingUtil";
@ -411,6 +415,9 @@ export class Container
depContainer.register<ModLoadOrder>("ModLoadOrder", ModLoadOrder, { lifecycle: Lifecycle.Singleton }); depContainer.register<ModLoadOrder>("ModLoadOrder", ModLoadOrder, { lifecycle: Lifecycle.Singleton });
depContainer.register<ModTypeCheck>("ModTypeCheck", ModTypeCheck, { lifecycle: Lifecycle.Singleton }); depContainer.register<ModTypeCheck>("ModTypeCheck", ModTypeCheck, { lifecycle: Lifecycle.Singleton });
depContainer.register<CompareUtil>("CompareUtil", CompareUtil, { lifecycle: Lifecycle.Singleton }); depContainer.register<CompareUtil>("CompareUtil", CompareUtil, { lifecycle: Lifecycle.Singleton });
depContainer.register<ICloner>("StructuredCloner", StructuredCloner, { lifecycle: Lifecycle.Singleton });
depContainer.register<ICloner>("JsonCloner", JsonCloner, { lifecycle: Lifecycle.Singleton });
depContainer.register<ICloner>("RecursiveCloner", RecursiveCloner, { lifecycle: Lifecycle.Singleton });
} }
private static registerRouters(depContainer: DependencyContainer): void private static registerRouters(depContainer: DependencyContainer): void

View File

@ -25,6 +25,7 @@ import { BotEquipmentModPoolService } from "@spt-aki/services/BotEquipmentModPoo
import { BotModLimits, BotWeaponModLimitService } from "@spt-aki/services/BotWeaponModLimitService"; import { BotModLimits, BotWeaponModLimitService } from "@spt-aki/services/BotWeaponModLimitService";
import { ItemFilterService } from "@spt-aki/services/ItemFilterService"; import { ItemFilterService } from "@spt-aki/services/ItemFilterService";
import { LocalisationService } from "@spt-aki/services/LocalisationService"; import { LocalisationService } from "@spt-aki/services/LocalisationService";
import { ICloner } from "@spt-aki/utils/cloners/ICloner";
import { HashUtil } from "@spt-aki/utils/HashUtil"; import { HashUtil } from "@spt-aki/utils/HashUtil";
import { JsonUtil } from "@spt-aki/utils/JsonUtil"; import { JsonUtil } from "@spt-aki/utils/JsonUtil";
import { RandomUtil } from "@spt-aki/utils/RandomUtil"; import { RandomUtil } from "@spt-aki/utils/RandomUtil";
@ -56,6 +57,7 @@ export class BotEquipmentModGenerator
@inject("LocalisationService") protected localisationService: LocalisationService, @inject("LocalisationService") protected localisationService: LocalisationService,
@inject("BotEquipmentModPoolService") protected botEquipmentModPoolService: BotEquipmentModPoolService, @inject("BotEquipmentModPoolService") protected botEquipmentModPoolService: BotEquipmentModPoolService,
@inject("ConfigServer") protected configServer: ConfigServer, @inject("ConfigServer") protected configServer: ConfigServer,
@inject("RecursiveCloner") protected cloner: ICloner,
) )
{ {
this.botConfig = this.configServer.getConfig(ConfigTypes.BOT); this.botConfig = this.configServer.getConfig(ConfigTypes.BOT);
@ -157,7 +159,7 @@ export class BotEquipmentModGenerator
// Find random mod and check its compatible // Find random mod and check its compatible
let modTpl: string; let modTpl: string;
let found = false; let found = false;
const exhaustableModPool = new ExhaustableArray(modPoolToChooseFrom, this.randomUtil, this.jsonUtil); const exhaustableModPool = new ExhaustableArray(modPoolToChooseFrom, this.randomUtil, this.cloner);
while (exhaustableModPool.hasValues()) while (exhaustableModPool.hasValues())
{ {
modTpl = exhaustableModPool.getRandomValue(); modTpl = exhaustableModPool.getRandomValue();
@ -857,7 +859,7 @@ export class BotEquipmentModGenerator
): IChooseRandomCompatibleModResult ): IChooseRandomCompatibleModResult
{ {
let chosenTpl: string; let chosenTpl: string;
const exhaustableModPool = new ExhaustableArray(modPool, this.randomUtil, this.jsonUtil); const exhaustableModPool = new ExhaustableArray(modPool, this.randomUtil, this.cloner);
let chosenModResult: IChooseRandomCompatibleModResult = { incompatible: true, found: false, reason: "unknown" }; let chosenModResult: IChooseRandomCompatibleModResult = { incompatible: true, found: false, reason: "unknown" };
const modParentFilterList = parentSlot._props.filters[0].Filter; const modParentFilterList = parentSlot._props.filters[0].Filter;
@ -1091,7 +1093,7 @@ export class BotEquipmentModGenerator
const allowedItems = parentSlot._props.filters[0].Filter; const allowedItems = parentSlot._props.filters[0].Filter;
// Find mod item that fits slot from sorted mod array // Find mod item that fits slot from sorted mod array
const exhaustableModPool = new ExhaustableArray(allowedItems, this.randomUtil, this.jsonUtil); const exhaustableModPool = new ExhaustableArray(allowedItems, this.randomUtil, this.cloner);
let tmpModTpl = modTpl; let tmpModTpl = modTpl;
while (exhaustableModPool.hasValues()) while (exhaustableModPool.hasValues())
{ {
@ -1221,7 +1223,7 @@ export class BotEquipmentModGenerator
botEquipBlacklist: EquipmentFilterDetails, botEquipBlacklist: EquipmentFilterDetails,
): string[] ): string[]
{ {
const modsFromDynamicPool = this.jsonUtil.clone( const modsFromDynamicPool = this.cloner.clone(
this.botEquipmentModPoolService.getCompatibleModsForWeaponSlot(parentItemId, modSlot), this.botEquipmentModPoolService.getCompatibleModsForWeaponSlot(parentItemId, modSlot),
); );
@ -1303,7 +1305,7 @@ export class BotEquipmentModGenerator
const camoraFirstSlot = "camora_000"; const camoraFirstSlot = "camora_000";
if (modSlot in itemModPool) if (modSlot in itemModPool)
{ {
exhaustableModPool = new ExhaustableArray(itemModPool[modSlot], this.randomUtil, this.jsonUtil); exhaustableModPool = new ExhaustableArray(itemModPool[modSlot], this.randomUtil, this.cloner);
} }
else if (camoraFirstSlot in itemModPool) else if (camoraFirstSlot in itemModPool)
{ {
@ -1311,7 +1313,7 @@ export class BotEquipmentModGenerator
exhaustableModPool = new ExhaustableArray( exhaustableModPool = new ExhaustableArray(
this.mergeCamoraPoolsTogether(itemModPool), this.mergeCamoraPoolsTogether(itemModPool),
this.randomUtil, this.randomUtil,
this.jsonUtil, this.cloner,
); );
} }
else else

View File

@ -29,6 +29,7 @@ import { DatabaseServer } from "@spt-aki/servers/DatabaseServer";
import { BotEquipmentFilterService } from "@spt-aki/services/BotEquipmentFilterService"; import { BotEquipmentFilterService } from "@spt-aki/services/BotEquipmentFilterService";
import { LocalisationService } from "@spt-aki/services/LocalisationService"; import { LocalisationService } from "@spt-aki/services/LocalisationService";
import { SeasonalEventService } from "@spt-aki/services/SeasonalEventService"; import { SeasonalEventService } from "@spt-aki/services/SeasonalEventService";
import { ICloner } from "@spt-aki/utils/cloners/ICloner";
import { HashUtil } from "@spt-aki/utils/HashUtil"; import { HashUtil } from "@spt-aki/utils/HashUtil";
import { JsonUtil } from "@spt-aki/utils/JsonUtil"; import { JsonUtil } from "@spt-aki/utils/JsonUtil";
import { RandomUtil } from "@spt-aki/utils/RandomUtil"; import { RandomUtil } from "@spt-aki/utils/RandomUtil";
@ -57,6 +58,7 @@ export class BotGenerator
@inject("SeasonalEventService") protected seasonalEventService: SeasonalEventService, @inject("SeasonalEventService") protected seasonalEventService: SeasonalEventService,
@inject("LocalisationService") protected localisationService: LocalisationService, @inject("LocalisationService") protected localisationService: LocalisationService,
@inject("ConfigServer") protected configServer: ConfigServer, @inject("ConfigServer") protected configServer: ConfigServer,
@inject("RecursiveCloner") protected cloner: ICloner,
) )
{ {
this.botConfig = this.configServer.getConfig(ConfigTypes.BOT); this.botConfig = this.configServer.getConfig(ConfigTypes.BOT);
@ -109,7 +111,7 @@ export class BotGenerator
bot.Info.Settings.BotDifficulty = botGenerationDetails.botDifficulty; bot.Info.Settings.BotDifficulty = botGenerationDetails.botDifficulty;
// Get raw json data for bot (Cloned) // Get raw json data for bot (Cloned)
const botJsonTemplateClone = this.jsonUtil.clone( const botJsonTemplateClone = this.cloner.clone(
this.botHelper.getBotTemplate(botGenerationDetails.isPmc ? bot.Info.Side : botGenerationDetails.role), this.botHelper.getBotTemplate(botGenerationDetails.isPmc ? bot.Info.Side : botGenerationDetails.role),
); );
@ -124,7 +126,7 @@ export class BotGenerator
*/ */
protected getCloneOfBotBase(): IBotBase protected getCloneOfBotBase(): IBotBase
{ {
return this.jsonUtil.clone(this.databaseServer.getTables().bots.base); return this.cloner.clone(this.databaseServer.getTables().bots.base);
} }
/** /**

View File

@ -23,6 +23,7 @@ import { ConfigServer } from "@spt-aki/servers/ConfigServer";
import { DatabaseServer } from "@spt-aki/servers/DatabaseServer"; import { DatabaseServer } from "@spt-aki/servers/DatabaseServer";
import { BotLootCacheService } from "@spt-aki/services/BotLootCacheService"; import { BotLootCacheService } from "@spt-aki/services/BotLootCacheService";
import { LocalisationService } from "@spt-aki/services/LocalisationService"; import { LocalisationService } from "@spt-aki/services/LocalisationService";
import { ICloner } from "@spt-aki/utils/cloners/ICloner";
import { HashUtil } from "@spt-aki/utils/HashUtil"; import { HashUtil } from "@spt-aki/utils/HashUtil";
import { JsonUtil } from "@spt-aki/utils/JsonUtil"; import { JsonUtil } from "@spt-aki/utils/JsonUtil";
import { RandomUtil } from "@spt-aki/utils/RandomUtil"; import { RandomUtil } from "@spt-aki/utils/RandomUtil";
@ -49,6 +50,7 @@ export class BotLootGenerator
@inject("BotLootCacheService") protected botLootCacheService: BotLootCacheService, @inject("BotLootCacheService") protected botLootCacheService: BotLootCacheService,
@inject("LocalisationService") protected localisationService: LocalisationService, @inject("LocalisationService") protected localisationService: LocalisationService,
@inject("ConfigServer") protected configServer: ConfigServer, @inject("ConfigServer") protected configServer: ConfigServer,
@inject("RecursiveCloner") protected cloner: ICloner,
) )
{ {
this.botConfig = this.configServer.getConfig(ConfigTypes.BOT); this.botConfig = this.configServer.getConfig(ConfigTypes.BOT);
@ -459,7 +461,7 @@ export class BotLootGenerator
// Check if all the chosen currency items fit into wallet // Check if all the chosen currency items fit into wallet
const canAddToContainer = this.inventoryHelper.canPlaceItemsInContainer( const canAddToContainer = this.inventoryHelper.canPlaceItemsInContainer(
this.jsonUtil.clone(containerGrid), // MUST clone grid before passing in as function modifies grid this.cloner.clone(containerGrid), // MUST clone grid before passing in as function modifies grid
itemsToAdd, itemsToAdd,
); );
if (canAddToContainer) if (canAddToContainer)

View File

@ -23,6 +23,7 @@ import { DatabaseServer } from "@spt-aki/servers/DatabaseServer";
import { BotWeaponModLimitService } from "@spt-aki/services/BotWeaponModLimitService"; import { BotWeaponModLimitService } from "@spt-aki/services/BotWeaponModLimitService";
import { LocalisationService } from "@spt-aki/services/LocalisationService"; import { LocalisationService } from "@spt-aki/services/LocalisationService";
import { RepairService } from "@spt-aki/services/RepairService"; import { RepairService } from "@spt-aki/services/RepairService";
import { ICloner } from "@spt-aki/utils/cloners/ICloner";
import { HashUtil } from "@spt-aki/utils/HashUtil"; import { HashUtil } from "@spt-aki/utils/HashUtil";
import { JsonUtil } from "@spt-aki/utils/JsonUtil"; import { JsonUtil } from "@spt-aki/utils/JsonUtil";
import { RandomUtil } from "@spt-aki/utils/RandomUtil"; import { RandomUtil } from "@spt-aki/utils/RandomUtil";
@ -51,6 +52,7 @@ export class BotWeaponGenerator
@inject("LocalisationService") protected localisationService: LocalisationService, @inject("LocalisationService") protected localisationService: LocalisationService,
@inject("RepairService") protected repairService: RepairService, @inject("RepairService") protected repairService: RepairService,
@injectAll("InventoryMagGen") protected inventoryMagGenComponents: IInventoryMagGen[], @injectAll("InventoryMagGen") protected inventoryMagGenComponents: IInventoryMagGen[],
@inject("RecursiveCloner") protected cloner: ICloner,
) )
{ {
this.botConfig = this.configServer.getConfig(ConfigTypes.BOT); this.botConfig = this.configServer.getConfig(ConfigTypes.BOT);
@ -323,7 +325,7 @@ export class BotWeaponGenerator
{ {
if (presetObj._items[0]._tpl === weaponTpl) if (presetObj._items[0]._tpl === weaponTpl)
{ {
preset = this.jsonUtil.clone(presetObj); preset = this.cloner.clone(presetObj);
break; break;
} }
} }
@ -582,7 +584,7 @@ export class BotWeaponGenerator
{ {
const desiredCaliber = this.getWeaponCaliber(weaponTemplate); const desiredCaliber = this.getWeaponCaliber(weaponTemplate);
const compatibleCartridges = this.jsonUtil.clone(ammo[desiredCaliber]); const compatibleCartridges = this.cloner.clone(ammo[desiredCaliber]);
if (!compatibleCartridges || compatibleCartridges?.length === 0) if (!compatibleCartridges || compatibleCartridges?.length === 0)
{ {
this.logger.debug( this.logger.debug(

View File

@ -23,6 +23,7 @@ import { ConfigServer } from "@spt-aki/servers/ConfigServer";
import { DatabaseServer } from "@spt-aki/servers/DatabaseServer"; import { DatabaseServer } from "@spt-aki/servers/DatabaseServer";
import { LocalisationService } from "@spt-aki/services/LocalisationService"; import { LocalisationService } from "@spt-aki/services/LocalisationService";
import { SeasonalEventService } from "@spt-aki/services/SeasonalEventService"; import { SeasonalEventService } from "@spt-aki/services/SeasonalEventService";
import { ICloner } from "@spt-aki/utils/cloners/ICloner";
import { JsonUtil } from "@spt-aki/utils/JsonUtil"; import { JsonUtil } from "@spt-aki/utils/JsonUtil";
import { MathUtil } from "@spt-aki/utils/MathUtil"; import { MathUtil } from "@spt-aki/utils/MathUtil";
import { ObjectId } from "@spt-aki/utils/ObjectId"; import { ObjectId } from "@spt-aki/utils/ObjectId";
@ -61,6 +62,7 @@ export class LocationGenerator
@inject("PresetHelper") protected presetHelper: PresetHelper, @inject("PresetHelper") protected presetHelper: PresetHelper,
@inject("LocalisationService") protected localisationService: LocalisationService, @inject("LocalisationService") protected localisationService: LocalisationService,
@inject("ConfigServer") protected configServer: ConfigServer, @inject("ConfigServer") protected configServer: ConfigServer,
@inject("RecursiveCloner") protected cloner: ICloner,
) )
{ {
this.locationConfig = this.configServer.getConfig(ConfigTypes.LOCATION); this.locationConfig = this.configServer.getConfig(ConfigTypes.LOCATION);
@ -84,7 +86,8 @@ export class LocationGenerator
const db = this.databaseServer.getTables(); const db = this.databaseServer.getTables();
const mapData: ILocation = db.locations[locationId]; const mapData: ILocation = db.locations[locationId];
const staticWeaponsOnMapClone = this.jsonUtil.clone(mapData.staticContainers.staticWeapons); const staticWeaponsOnMapClone = this.cloner.clone(mapData.staticContainers.staticWeapons);
if (!staticWeaponsOnMapClone) if (!staticWeaponsOnMapClone)
{ {
this.logger.error(`Unable to find static weapon data for map: ${locationBase.Name}`); this.logger.error(`Unable to find static weapon data for map: ${locationBase.Name}`);
@ -93,7 +96,8 @@ export class LocationGenerator
// Add mounted weapons to output loot // Add mounted weapons to output loot
result.push(...staticWeaponsOnMapClone ?? []); result.push(...staticWeaponsOnMapClone ?? []);
const allStaticContainersOnMapClone = this.jsonUtil.clone(mapData.staticContainers.staticContainers); const allStaticContainersOnMapClone = this.cloner.clone(mapData.staticContainers.staticContainers);
if (!allStaticContainersOnMapClone) if (!allStaticContainersOnMapClone)
{ {
this.logger.error(`Unable to find static container data for map: ${locationBase.Name}`); this.logger.error(`Unable to find static container data for map: ${locationBase.Name}`);
@ -101,7 +105,8 @@ export class LocationGenerator
const staticRandomisableContainersOnMap = this.getRandomisableContainersOnMap(allStaticContainersOnMapClone); const staticRandomisableContainersOnMap = this.getRandomisableContainersOnMap(allStaticContainersOnMapClone);
// Containers that MUST be added to map (quest containers etc) // Containers that MUST be added to map (quest containers etc)
const staticForcedOnMapClone = this.jsonUtil.clone(mapData.staticContainers.staticForced); const staticForcedOnMapClone = this.cloner.clone(mapData.staticContainers.staticForced);
if (!staticForcedOnMapClone) if (!staticForcedOnMapClone)
{ {
this.logger.error(`Unable to find forced static data for map: ${locationBase.Name}`); this.logger.error(`Unable to find forced static data for map: ${locationBase.Name}`);
@ -192,7 +197,7 @@ export class LocationGenerator
// EDGE CASE: These are containers without a group and have a probability < 100% // EDGE CASE: These are containers without a group and have a probability < 100%
if (groupId === "") if (groupId === "")
{ {
const containerIdsCopy = this.jsonUtil.clone(data.containerIdsWithProbability); const containerIdsCopy = this.cloner.clone(data.containerIdsWithProbability);
// Roll each containers probability, if it passes, it gets added // Roll each containers probability, if it passes, it gets added
data.containerIdsWithProbability = {}; data.containerIdsWithProbability = {};
for (const containerId in containerIdsCopy) for (const containerId in containerIdsCopy)
@ -305,7 +310,7 @@ export class LocationGenerator
} }
// Create probability array with all possible container ids in this group and their relataive probability of spawning // Create probability array with all possible container ids in this group and their relataive probability of spawning
const containerDistribution = new ProbabilityObjectArray<string>(this.mathUtil, this.jsonUtil); const containerDistribution = new ProbabilityObjectArray<string>(this.mathUtil, this.cloner);
for (const x of containerIds) for (const x of containerIds)
{ {
containerDistribution.push(new ProbabilityObject(x, containerData.containerIdsWithProbability[x])); containerDistribution.push(new ProbabilityObject(x, containerData.containerIdsWithProbability[x]));
@ -395,7 +400,7 @@ export class LocationGenerator
locationName: string, locationName: string,
): IStaticContainerData ): IStaticContainerData
{ {
const containerClone = this.jsonUtil.clone(staticContainer); const containerClone = this.cloner.clone(staticContainer);
const containerTpl = containerClone.template.Items[0]._tpl; const containerTpl = containerClone.template.Items[0]._tpl;
// Create new unique parent id to prevent any collisions // Create new unique parent id to prevent any collisions
@ -509,7 +514,7 @@ export class LocationGenerator
): number ): number
{ {
// Create probability array to calcualte the total count of lootable items inside container // Create probability array to calcualte the total count of lootable items inside container
const itemCountArray = new ProbabilityObjectArray<number>(this.mathUtil, this.jsonUtil); const itemCountArray = new ProbabilityObjectArray<number>(this.mathUtil, this.cloner);
const countDistribution = staticLootDist[containerTypeId]?.itemcountDistribution; const countDistribution = staticLootDist[containerTypeId]?.itemcountDistribution;
if (!countDistribution) if (!countDistribution)
{ {
@ -546,7 +551,7 @@ export class LocationGenerator
const seasonalEventActive = this.seasonalEventService.seasonalEventEnabled(); const seasonalEventActive = this.seasonalEventService.seasonalEventEnabled();
const seasonalItemTplBlacklist = this.seasonalEventService.getInactiveSeasonalEventItems(); const seasonalItemTplBlacklist = this.seasonalEventService.getInactiveSeasonalEventItems();
const itemDistribution = new ProbabilityObjectArray<string>(this.mathUtil, this.jsonUtil); const itemDistribution = new ProbabilityObjectArray<string>(this.mathUtil, this.cloner);
const itemContainerDistribution = staticLootDist[containerTypeId]?.itemDistribution; const itemContainerDistribution = staticLootDist[containerTypeId]?.itemDistribution;
if (!itemContainerDistribution) if (!itemContainerDistribution)
@ -617,7 +622,7 @@ export class LocationGenerator
const guaranteedLoosePoints: Spawnpoint[] = []; const guaranteedLoosePoints: Spawnpoint[] = [];
const blacklistedSpawnpoints = this.locationConfig.looseLootBlacklist[locationName]; const blacklistedSpawnpoints = this.locationConfig.looseLootBlacklist[locationName];
const spawnpointArray = new ProbabilityObjectArray<string, Spawnpoint>(this.mathUtil, this.jsonUtil); const spawnpointArray = new ProbabilityObjectArray<string, Spawnpoint>(this.mathUtil, this.cloner);
for (const spawnpoint of allDynamicSpawnpoints) for (const spawnpoint of allDynamicSpawnpoints)
{ {
@ -699,7 +704,7 @@ export class LocationGenerator
continue; continue;
} }
const itemArray = new ProbabilityObjectArray<string>(this.mathUtil, this.jsonUtil); const itemArray = new ProbabilityObjectArray<string>(this.mathUtil, this.cloner);
for (const itemDist of spawnPoint.itemDistribution) for (const itemDist of spawnPoint.itemDistribution)
{ {
if ( if (
@ -769,7 +774,7 @@ export class LocationGenerator
// Create probability array of all spawn positions for this spawn id // Create probability array of all spawn positions for this spawn id
const spawnpointArray = new ProbabilityObjectArray<string, SpawnpointsForced>( const spawnpointArray = new ProbabilityObjectArray<string, SpawnpointsForced>(
this.mathUtil, this.mathUtil,
this.jsonUtil, this.cloner,
); );
for (const si of items) for (const si of items)
{ {
@ -986,7 +991,7 @@ export class LocationGenerator
else if (this.itemHelper.isOfBaseclass(chosenTpl, BaseClasses.WEAPON)) else if (this.itemHelper.isOfBaseclass(chosenTpl, BaseClasses.WEAPON))
{ {
let children: Item[] = []; let children: Item[] = [];
const defaultPreset = this.jsonUtil.clone(this.presetHelper.getDefaultPreset(chosenTpl)); const defaultPreset = this.cloner.clone(this.presetHelper.getDefaultPreset(chosenTpl));
if (defaultPreset) if (defaultPreset)
{ {
try try

View File

@ -22,6 +22,7 @@ import { SaveServer } from "@spt-aki/servers/SaveServer";
import { BotLootCacheService } from "@spt-aki/services/BotLootCacheService"; import { BotLootCacheService } from "@spt-aki/services/BotLootCacheService";
import { FenceService } from "@spt-aki/services/FenceService"; import { FenceService } from "@spt-aki/services/FenceService";
import { LocalisationService } from "@spt-aki/services/LocalisationService"; import { LocalisationService } from "@spt-aki/services/LocalisationService";
import { ICloner } from "@spt-aki/utils/cloners/ICloner";
import { HashUtil } from "@spt-aki/utils/HashUtil"; import { HashUtil } from "@spt-aki/utils/HashUtil";
import { JsonUtil } from "@spt-aki/utils/JsonUtil"; import { JsonUtil } from "@spt-aki/utils/JsonUtil";
import { RandomUtil } from "@spt-aki/utils/RandomUtil"; import { RandomUtil } from "@spt-aki/utils/RandomUtil";
@ -47,6 +48,7 @@ export class PlayerScavGenerator
@inject("LocalisationService") protected localisationService: LocalisationService, @inject("LocalisationService") protected localisationService: LocalisationService,
@inject("BotGenerator") protected botGenerator: BotGenerator, @inject("BotGenerator") protected botGenerator: BotGenerator,
@inject("ConfigServer") protected configServer: ConfigServer, @inject("ConfigServer") protected configServer: ConfigServer,
@inject("RecursiveCloner") protected cloner: ICloner,
) )
{ {
this.playerScavConfig = this.configServer.getConfig(ConfigTypes.PLAYERSCAV); this.playerScavConfig = this.configServer.getConfig(ConfigTypes.PLAYERSCAV);
@ -61,8 +63,8 @@ export class PlayerScavGenerator
{ {
// get karma level from profile // get karma level from profile
const profile = this.saveServer.getProfile(sessionID); const profile = this.saveServer.getProfile(sessionID);
const pmcDataClone = this.jsonUtil.clone(profile.characters.pmc); const pmcDataClone = this.cloner.clone(profile.characters.pmc);
const existingScavDataClone = this.jsonUtil.clone(profile.characters.scav); const existingScavDataClone = this.cloner.clone(profile.characters.scav);
const scavKarmaLevel = this.getScavKarmaLevel(pmcDataClone); const scavKarmaLevel = this.getScavKarmaLevel(pmcDataClone);
@ -219,7 +221,7 @@ export class PlayerScavGenerator
protected constructBotBaseTemplate(botTypeForLoot: string): IBotType protected constructBotBaseTemplate(botTypeForLoot: string): IBotType
{ {
const baseScavType = "assault"; const baseScavType = "assault";
const assaultBase = this.jsonUtil.clone(this.botHelper.getBotTemplate(baseScavType)); const assaultBase = this.cloner.clone(this.botHelper.getBotTemplate(baseScavType));
// Loot bot is same as base bot, return base with no modification // Loot bot is same as base bot, return base with no modification
if (botTypeForLoot === baseScavType) if (botTypeForLoot === baseScavType)
@ -227,7 +229,7 @@ export class PlayerScavGenerator
return assaultBase; return assaultBase;
} }
const lootBase = this.jsonUtil.clone(this.botHelper.getBotTemplate(botTypeForLoot)); const lootBase = this.cloner.clone(this.botHelper.getBotTemplate(botTypeForLoot));
assaultBase.inventory = lootBase.inventory; assaultBase.inventory = lootBase.inventory;
assaultBase.chances = lootBase.chances; assaultBase.chances = lootBase.chances;
assaultBase.generation = lootBase.generation; assaultBase.generation = lootBase.generation;

View File

@ -27,6 +27,7 @@ import { FenceService } from "@spt-aki/services/FenceService";
import { LocalisationService } from "@spt-aki/services/LocalisationService"; import { LocalisationService } from "@spt-aki/services/LocalisationService";
import { RagfairOfferService } from "@spt-aki/services/RagfairOfferService"; import { RagfairOfferService } from "@spt-aki/services/RagfairOfferService";
import { RagfairPriceService } from "@spt-aki/services/RagfairPriceService"; import { RagfairPriceService } from "@spt-aki/services/RagfairPriceService";
import { ICloner } from "@spt-aki/utils/cloners/ICloner";
import { HashUtil } from "@spt-aki/utils/HashUtil"; import { HashUtil } from "@spt-aki/utils/HashUtil";
import { JsonUtil } from "@spt-aki/utils/JsonUtil"; import { JsonUtil } from "@spt-aki/utils/JsonUtil";
import { RandomUtil } from "@spt-aki/utils/RandomUtil"; import { RandomUtil } from "@spt-aki/utils/RandomUtil";
@ -60,6 +61,7 @@ export class RagfairOfferGenerator
@inject("FenceService") protected fenceService: FenceService, @inject("FenceService") protected fenceService: FenceService,
@inject("ItemHelper") protected itemHelper: ItemHelper, @inject("ItemHelper") protected itemHelper: ItemHelper,
@inject("ConfigServer") protected configServer: ConfigServer, @inject("ConfigServer") protected configServer: ConfigServer,
@inject("RecursiveCloner") protected cloner: ICloner,
) )
{ {
this.ragfairConfig = this.configServer.getConfig(ConfigTypes.RAGFAIR); this.ragfairConfig = this.configServer.getConfig(ConfigTypes.RAGFAIR);
@ -123,7 +125,7 @@ export class RagfairOfferGenerator
offerRequirements.push(requirement); offerRequirements.push(requirement);
} }
const itemsClone = this.jsonUtil.clone(items); const itemsClone = this.cloner.clone(items);
// Add cartridges to offers for ammo boxes // Add cartridges to offers for ammo boxes
if (this.itemHelper.isOfBaseclass(itemsClone[0]._tpl, BaseClasses.AMMO_BOX)) if (this.itemHelper.isOfBaseclass(itemsClone[0]._tpl, BaseClasses.AMMO_BOX))
@ -380,7 +382,7 @@ export class RagfairOfferGenerator
for (let index = 0; index < offerCount; index++) for (let index = 0; index < offerCount; index++)
{ {
// Clone the item so we don't have shared references and generate new item IDs // Clone the item so we don't have shared references and generate new item IDs
const clonedAssort = this.jsonUtil.clone(assortItemWithChildren); const clonedAssort = this.cloner.clone(assortItemWithChildren);
this.itemHelper.reparentItemAndChildren(clonedAssort[0], clonedAssort); this.itemHelper.reparentItemAndChildren(clonedAssort[0], clonedAssort);
// Clear unnecessary properties // Clear unnecessary properties

View File

@ -20,6 +20,7 @@ import { ILogger } from "@spt-aki/models/spt/utils/ILogger";
import { ConfigServer } from "@spt-aki/servers/ConfigServer"; import { ConfigServer } from "@spt-aki/servers/ConfigServer";
import { DatabaseServer } from "@spt-aki/servers/DatabaseServer"; import { DatabaseServer } from "@spt-aki/servers/DatabaseServer";
import { LocalisationService } from "@spt-aki/services/LocalisationService"; import { LocalisationService } from "@spt-aki/services/LocalisationService";
import { ICloner } from "@spt-aki/utils/cloners/ICloner";
import { JsonUtil } from "@spt-aki/utils/JsonUtil"; import { JsonUtil } from "@spt-aki/utils/JsonUtil";
import { MathUtil } from "@spt-aki/utils/MathUtil"; import { MathUtil } from "@spt-aki/utils/MathUtil";
import { ObjectId } from "@spt-aki/utils/ObjectId"; import { ObjectId } from "@spt-aki/utils/ObjectId";
@ -43,6 +44,7 @@ export class RepeatableQuestGenerator
@inject("RepeatableQuestRewardGenerator") protected repeatableQuestRewardGenerator: @inject("RepeatableQuestRewardGenerator") protected repeatableQuestRewardGenerator:
RepeatableQuestRewardGenerator, RepeatableQuestRewardGenerator,
@inject("ConfigServer") protected configServer: ConfigServer, @inject("ConfigServer") protected configServer: ConfigServer,
@inject("RecursiveCloner") protected cloner: ICloner,
) )
{ {
this.questConfig = this.configServer.getConfig(ConfigTypes.QUEST); this.questConfig = this.configServer.getConfig(ConfigTypes.QUEST);
@ -873,7 +875,7 @@ export class RepeatableQuestGenerator
// @Incomplete: define Type for "type". // @Incomplete: define Type for "type".
protected generateRepeatableTemplate(type: string, traderId: string, side: string): IRepeatableQuest protected generateRepeatableTemplate(type: string, traderId: string, side: string): IRepeatableQuest
{ {
const questClone = this.jsonUtil.clone<IRepeatableQuest>( const questClone = this.cloner.clone<IRepeatableQuest>(
this.databaseServer.getTables().templates.repeatableQuests.templates[type], this.databaseServer.getTables().templates.repeatableQuests.templates[type],
); );
questClone._id = this.objectId.generate(); questClone._id = this.objectId.generate();

View File

@ -19,6 +19,7 @@ import { DatabaseServer } from "@spt-aki/servers/DatabaseServer";
import { ItemFilterService } from "@spt-aki/services/ItemFilterService"; import { ItemFilterService } from "@spt-aki/services/ItemFilterService";
import { LocalisationService } from "@spt-aki/services/LocalisationService"; import { LocalisationService } from "@spt-aki/services/LocalisationService";
import { SeasonalEventService } from "@spt-aki/services/SeasonalEventService"; import { SeasonalEventService } from "@spt-aki/services/SeasonalEventService";
import { ICloner } from "@spt-aki/utils/cloners/ICloner";
import { JsonUtil } from "@spt-aki/utils/JsonUtil"; import { JsonUtil } from "@spt-aki/utils/JsonUtil";
import { MathUtil } from "@spt-aki/utils/MathUtil"; import { MathUtil } from "@spt-aki/utils/MathUtil";
import { ObjectId } from "@spt-aki/utils/ObjectId"; import { ObjectId } from "@spt-aki/utils/ObjectId";
@ -43,6 +44,7 @@ export class RepeatableQuestRewardGenerator
@inject("ItemFilterService") protected itemFilterService: ItemFilterService, @inject("ItemFilterService") protected itemFilterService: ItemFilterService,
@inject("SeasonalEventService") protected seasonalEventService: SeasonalEventService, @inject("SeasonalEventService") protected seasonalEventService: SeasonalEventService,
@inject("ConfigServer") protected configServer: ConfigServer, @inject("ConfigServer") protected configServer: ConfigServer,
@inject("RecursiveCloner") protected cloner: ICloner,
) )
{ {
this.questConfig = this.configServer.getConfig(ConfigTypes.QUEST); this.questConfig = this.configServer.getConfig(ConfigTypes.QUEST);
@ -144,7 +146,7 @@ export class RepeatableQuestRewardGenerator
const defaultPresetPool = new ExhaustableArray( const defaultPresetPool = new ExhaustableArray(
Object.values(this.presetHelper.getDefaultWeaponPresets()), Object.values(this.presetHelper.getDefaultWeaponPresets()),
this.randomUtil, this.randomUtil,
this.jsonUtil, this.cloner,
); );
let chosenPreset: IPreset; let chosenPreset: IPreset;
while (defaultPresetPool.hasValues()) while (defaultPresetPool.hasValues())
@ -156,7 +158,7 @@ export class RepeatableQuestRewardGenerator
{ {
this.logger.debug(` Added weapon ${tpls[0]} with price ${presetPrice}`); this.logger.debug(` Added weapon ${tpls[0]} with price ${presetPrice}`);
roublesBudget -= presetPrice; roublesBudget -= presetPrice;
chosenPreset = this.jsonUtil.clone(randomPreset); chosenPreset = this.cloner.clone(randomPreset);
break; break;
} }
} }

View File

@ -7,6 +7,7 @@ import { ILogger } from "@spt-aki/models/spt/utils/ILogger";
import { ConfigServer } from "@spt-aki/servers/ConfigServer"; import { ConfigServer } from "@spt-aki/servers/ConfigServer";
import { DatabaseServer } from "@spt-aki/servers/DatabaseServer"; import { DatabaseServer } from "@spt-aki/servers/DatabaseServer";
import { LocalisationService } from "@spt-aki/services/LocalisationService"; import { LocalisationService } from "@spt-aki/services/LocalisationService";
import { ICloner } from "@spt-aki/utils/cloners/ICloner";
import { JsonUtil } from "@spt-aki/utils/JsonUtil"; import { JsonUtil } from "@spt-aki/utils/JsonUtil";
import { RandomUtil } from "@spt-aki/utils/RandomUtil"; import { RandomUtil } from "@spt-aki/utils/RandomUtil";
@ -23,6 +24,7 @@ export class BotDifficultyHelper
@inject("LocalisationService") protected localisationService: LocalisationService, @inject("LocalisationService") protected localisationService: LocalisationService,
@inject("BotHelper") protected botHelper: BotHelper, @inject("BotHelper") protected botHelper: BotHelper,
@inject("ConfigServer") protected configServer: ConfigServer, @inject("ConfigServer") protected configServer: ConfigServer,
@inject("RecursiveCloner") protected cloner: ICloner,
) )
{ {
this.pmcConfig = this.configServer.getConfig(ConfigTypes.PMC); this.pmcConfig = this.configServer.getConfig(ConfigTypes.PMC);
@ -61,7 +63,7 @@ export class BotDifficultyHelper
{ {
// get fallback // get fallback
this.logger.warning(this.localisationService.getText("bot-unable_to_get_bot_fallback_to_assault", type)); this.logger.warning(this.localisationService.getText("bot-unable_to_get_bot_fallback_to_assault", type));
this.databaseServer.getTables().bots.types[type] = this.jsonUtil.clone( this.databaseServer.getTables().bots.types[type] = this.cloner.clone(
this.databaseServer.getTables().bots.types.assault, this.databaseServer.getTables().bots.types.assault,
); );
} }
@ -75,12 +77,12 @@ export class BotDifficultyHelper
difficulty: difficulty, difficulty: difficulty,
}), }),
); );
this.databaseServer.getTables().bots.types[type].difficulty[difficulty] = this.jsonUtil.clone( this.databaseServer.getTables().bots.types[type].difficulty[difficulty] = this.cloner.clone(
this.databaseServer.getTables().bots.types.assault.difficulty[difficulty], this.databaseServer.getTables().bots.types.assault.difficulty[difficulty],
); );
} }
return this.jsonUtil.clone(difficultySettings); return this.cloner.clone(difficultySettings);
} }
/** /**
@ -97,7 +99,7 @@ export class BotDifficultyHelper
difficultySetting = this.convertBotDifficultyDropdownToBotDifficulty(difficultySetting); difficultySetting = this.convertBotDifficultyDropdownToBotDifficulty(difficultySetting);
return this.jsonUtil.clone(this.databaseServer.getTables().bots.types[type].difficulty[difficultySetting]); return this.cloner.clone(this.databaseServer.getTables().bots.types[type].difficulty[difficultySetting]);
} }
/** /**

View File

@ -14,6 +14,7 @@ import { DatabaseServer } from "@spt-aki/servers/DatabaseServer";
import { ItemFilterService } from "@spt-aki/services/ItemFilterService"; import { ItemFilterService } from "@spt-aki/services/ItemFilterService";
import { LocaleService } from "@spt-aki/services/LocaleService"; import { LocaleService } from "@spt-aki/services/LocaleService";
import { MailSendService } from "@spt-aki/services/MailSendService"; import { MailSendService } from "@spt-aki/services/MailSendService";
import { ICloner } from "@spt-aki/utils/cloners/ICloner";
import { HashUtil } from "@spt-aki/utils/HashUtil"; import { HashUtil } from "@spt-aki/utils/HashUtil";
import { JsonUtil } from "@spt-aki/utils/JsonUtil"; import { JsonUtil } from "@spt-aki/utils/JsonUtil";
@ -43,6 +44,7 @@ export class GiveSptCommand implements ISptCommand
@inject("LocaleService") protected localeService: LocaleService, @inject("LocaleService") protected localeService: LocaleService,
@inject("DatabaseServer") protected databaseServer: DatabaseServer, @inject("DatabaseServer") protected databaseServer: DatabaseServer,
@inject("ItemFilterService") protected itemFilterService: ItemFilterService, @inject("ItemFilterService") protected itemFilterService: ItemFilterService,
@inject("RecursiveCloner") protected cloner: ICloner,
) )
{ {
} }
@ -229,7 +231,7 @@ export class GiveSptCommand implements ISptCommand
} }
for (let i = 0; i < quantity; i++) for (let i = 0; i < quantity; i++)
{ {
let items = this.jsonUtil.clone(preset._items); let items = this.cloner.clone(preset._items);
items = this.itemHelper.replaceIDs(items); items = this.itemHelper.replaceIDs(items);
itemsToSend.push(...items); itemsToSend.push(...items);
} }

View File

@ -6,6 +6,7 @@ import { Money } from "@spt-aki/models/enums/Money";
import { IItemConfig } from "@spt-aki/models/spt/config/IItemConfig"; import { IItemConfig } from "@spt-aki/models/spt/config/IItemConfig";
import { ConfigServer } from "@spt-aki/servers/ConfigServer"; import { ConfigServer } from "@spt-aki/servers/ConfigServer";
import { DatabaseServer } from "@spt-aki/servers/DatabaseServer"; import { DatabaseServer } from "@spt-aki/servers/DatabaseServer";
import { ICloner } from "@spt-aki/utils/cloners/ICloner";
import { JsonUtil } from "@spt-aki/utils/JsonUtil"; import { JsonUtil } from "@spt-aki/utils/JsonUtil";
class LookupItem<T, I> class LookupItem<T, I>
@ -43,6 +44,7 @@ export class HandbookHelper
@inject("DatabaseServer") protected databaseServer: DatabaseServer, @inject("DatabaseServer") protected databaseServer: DatabaseServer,
@inject("JsonUtil") protected jsonUtil: JsonUtil, @inject("JsonUtil") protected jsonUtil: JsonUtil,
@inject("ConfigServer") protected configServer: ConfigServer, @inject("ConfigServer") protected configServer: ConfigServer,
@inject("RecursiveCloner") protected cloner: ICloner,
) )
{ {
this.itemConfig = this.configServer.getConfig(ConfigTypes.ITEM); this.itemConfig = this.configServer.getConfig(ConfigTypes.ITEM);
@ -74,7 +76,7 @@ export class HandbookHelper
itemToUpdate.Price = this.itemConfig.handbookPriceOverride[itemTpl]; itemToUpdate.Price = this.itemConfig.handbookPriceOverride[itemTpl];
} }
const handbookDbClone = this.jsonUtil.clone(this.databaseServer.getTables().templates.handbook); const handbookDbClone = this.cloner.clone(this.databaseServer.getTables().templates.handbook);
for (const handbookItem of handbookDbClone.Items) for (const handbookItem of handbookDbClone.Items)
{ {
this.handbookPriceCache.items.byId.set(handbookItem.Id, handbookItem.Price); this.handbookPriceCache.items.byId.set(handbookItem.Id, handbookItem.Price);

View File

@ -7,6 +7,7 @@ import { IHealthConfig } from "@spt-aki/models/spt/config/IHealthConfig";
import { ILogger } from "@spt-aki/models/spt/utils/ILogger"; import { ILogger } from "@spt-aki/models/spt/utils/ILogger";
import { ConfigServer } from "@spt-aki/servers/ConfigServer"; import { ConfigServer } from "@spt-aki/servers/ConfigServer";
import { SaveServer } from "@spt-aki/servers/SaveServer"; import { SaveServer } from "@spt-aki/servers/SaveServer";
import { ICloner } from "@spt-aki/utils/cloners/ICloner";
import { JsonUtil } from "@spt-aki/utils/JsonUtil"; import { JsonUtil } from "@spt-aki/utils/JsonUtil";
import { TimeUtil } from "@spt-aki/utils/TimeUtil"; import { TimeUtil } from "@spt-aki/utils/TimeUtil";
@ -21,6 +22,7 @@ export class HealthHelper
@inject("TimeUtil") protected timeUtil: TimeUtil, @inject("TimeUtil") protected timeUtil: TimeUtil,
@inject("SaveServer") protected saveServer: SaveServer, @inject("SaveServer") protected saveServer: SaveServer,
@inject("ConfigServer") protected configServer: ConfigServer, @inject("ConfigServer") protected configServer: ConfigServer,
@inject("RecursiveCloner") protected cloner: ICloner,
) )
{ {
this.healthConfig = this.configServer.getConfig(ConfigTypes.HEALTH); this.healthConfig = this.configServer.getConfig(ConfigTypes.HEALTH);
@ -115,7 +117,7 @@ export class HealthHelper
this.saveEffects( this.saveEffects(
pmcData, pmcData,
sessionID, sessionID,
this.jsonUtil.clone(this.saveServer.getProfile(sessionID).vitality.effects), this.cloner.clone(this.saveServer.getProfile(sessionID).vitality.effects),
deleteExistingEffects, deleteExistingEffects,
); );
} }

View File

@ -23,6 +23,7 @@ import { ConfigServer } from "@spt-aki/servers/ConfigServer";
import { DatabaseServer } from "@spt-aki/servers/DatabaseServer"; import { DatabaseServer } from "@spt-aki/servers/DatabaseServer";
import { LocalisationService } from "@spt-aki/services/LocalisationService"; import { LocalisationService } from "@spt-aki/services/LocalisationService";
import { PlayerService } from "@spt-aki/services/PlayerService"; import { PlayerService } from "@spt-aki/services/PlayerService";
import { ICloner } from "@spt-aki/utils/cloners/ICloner";
import { HashUtil } from "@spt-aki/utils/HashUtil"; import { HashUtil } from "@spt-aki/utils/HashUtil";
import { HttpResponseUtil } from "@spt-aki/utils/HttpResponseUtil"; import { HttpResponseUtil } from "@spt-aki/utils/HttpResponseUtil";
import { JsonUtil } from "@spt-aki/utils/JsonUtil"; import { JsonUtil } from "@spt-aki/utils/JsonUtil";
@ -54,6 +55,7 @@ export class HideoutHelper
@inject("ItemHelper") protected itemHelper: ItemHelper, @inject("ItemHelper") protected itemHelper: ItemHelper,
@inject("ConfigServer") protected configServer: ConfigServer, @inject("ConfigServer") protected configServer: ConfigServer,
@inject("JsonUtil") protected jsonUtil: JsonUtil, @inject("JsonUtil") protected jsonUtil: JsonUtil,
@inject("RecursiveCloner") protected cloner: ICloner,
) )
{ {
this.hideoutConfig = this.configServer.getConfig(ConfigTypes.HIDEOUT); this.hideoutConfig = this.configServer.getConfig(ConfigTypes.HIDEOUT);
@ -106,7 +108,7 @@ export class HideoutHelper
for (const tool of bodyAsSingle.tools) for (const tool of bodyAsSingle.tools)
{ {
const toolItem = this.jsonUtil.clone(pmcData.Inventory.items.find(x => x._id === tool.id)); const toolItem = this.cloner.clone(pmcData.Inventory.items.find(x => x._id === tool.id));
// Make sure we only return as many as we took // Make sure we only return as many as we took
this.itemHelper.addUpdObjectToItem(toolItem); this.itemHelper.addUpdObjectToItem(toolItem);

View File

@ -19,6 +19,7 @@ import { DatabaseServer } from "@spt-aki/servers/DatabaseServer";
import { SaveServer } from "@spt-aki/servers/SaveServer"; import { SaveServer } from "@spt-aki/servers/SaveServer";
import { LocalisationService } from "@spt-aki/services/LocalisationService"; import { LocalisationService } from "@spt-aki/services/LocalisationService";
import { ProfileFixerService } from "@spt-aki/services/ProfileFixerService"; import { ProfileFixerService } from "@spt-aki/services/ProfileFixerService";
import { ICloner } from "@spt-aki/utils/cloners/ICloner";
import { JsonUtil } from "@spt-aki/utils/JsonUtil"; import { JsonUtil } from "@spt-aki/utils/JsonUtil";
import { RandomUtil } from "@spt-aki/utils/RandomUtil"; import { RandomUtil } from "@spt-aki/utils/RandomUtil";
import { TimeUtil } from "@spt-aki/utils/TimeUtil"; import { TimeUtil } from "@spt-aki/utils/TimeUtil";
@ -45,6 +46,7 @@ export class InRaidHelper
@inject("ProfileFixerService") protected profileFixerService: ProfileFixerService, @inject("ProfileFixerService") protected profileFixerService: ProfileFixerService,
@inject("ConfigServer") protected configServer: ConfigServer, @inject("ConfigServer") protected configServer: ConfigServer,
@inject("RandomUtil") protected randomUtil: RandomUtil, @inject("RandomUtil") protected randomUtil: RandomUtil,
@inject("RecursiveCloner") protected cloner: ICloner,
) )
{ {
this.lostOnDeathConfig = this.configServer.getConfig(ConfigTypes.LOST_ON_DEATH); this.lostOnDeathConfig = this.configServer.getConfig(ConfigTypes.LOST_ON_DEATH);
@ -480,7 +482,7 @@ export class InRaidHelper
public setInventory(sessionID: string, serverProfile: IPmcData, postRaidProfile: IPmcData): void public setInventory(sessionID: string, serverProfile: IPmcData, postRaidProfile: IPmcData): void
{ {
// Store insurance (as removeItem() removes insurance also) // Store insurance (as removeItem() removes insurance also)
const insured = this.jsonUtil.clone(serverProfile.InsuredItems); const insured = this.cloner.clone(serverProfile.InsuredItems);
// Remove possible equipped items from before the raid // Remove possible equipped items from before the raid
this.inventoryHelper.removeItem(serverProfile, serverProfile.Inventory.equipment, sessionID); this.inventoryHelper.removeItem(serverProfile, serverProfile.Inventory.equipment, sessionID);

View File

@ -30,6 +30,7 @@ import { ConfigServer } from "@spt-aki/servers/ConfigServer";
import { DatabaseServer } from "@spt-aki/servers/DatabaseServer"; import { DatabaseServer } from "@spt-aki/servers/DatabaseServer";
import { FenceService } from "@spt-aki/services/FenceService"; import { FenceService } from "@spt-aki/services/FenceService";
import { LocalisationService } from "@spt-aki/services/LocalisationService"; import { LocalisationService } from "@spt-aki/services/LocalisationService";
import { ICloner } from "@spt-aki/utils/cloners/ICloner";
import { HashUtil } from "@spt-aki/utils/HashUtil"; import { HashUtil } from "@spt-aki/utils/HashUtil";
import { HttpResponseUtil } from "@spt-aki/utils/HttpResponseUtil"; import { HttpResponseUtil } from "@spt-aki/utils/HttpResponseUtil";
import { JsonUtil } from "@spt-aki/utils/JsonUtil"; import { JsonUtil } from "@spt-aki/utils/JsonUtil";
@ -65,6 +66,7 @@ export class InventoryHelper
@inject("PresetHelper") protected presetHelper: PresetHelper, @inject("PresetHelper") protected presetHelper: PresetHelper,
@inject("LocalisationService") protected localisationService: LocalisationService, @inject("LocalisationService") protected localisationService: LocalisationService,
@inject("ConfigServer") protected configServer: ConfigServer, @inject("ConfigServer") protected configServer: ConfigServer,
@inject("RecursiveCloner") protected cloner: ICloner,
) )
{ {
this.inventoryConfig = this.configServer.getConfig(ConfigTypes.INVENTORY); this.inventoryConfig = this.configServer.getConfig(ConfigTypes.INVENTORY);
@ -129,7 +131,7 @@ export class InventoryHelper
output: IItemEventRouterResponse, output: IItemEventRouterResponse,
): void ): void
{ {
const itemWithModsToAddClone = this.jsonUtil.clone(request.itemWithModsToAdd); const itemWithModsToAddClone = this.cloner.clone(request.itemWithModsToAdd);
// Get stash layouts ready for use // Get stash layouts ready for use
const stashFS2D = this.getStashSlotMap(pmcData, sessionId); const stashFS2D = this.getStashSlotMap(pmcData, sessionId);
@ -245,7 +247,7 @@ export class InventoryHelper
{ {
const pmcData = this.profileHelper.getPmcProfile(sessionId); const pmcData = this.profileHelper.getPmcProfile(sessionId);
const stashFS2D = this.jsonUtil.clone(this.getStashSlotMap(pmcData, sessionId)); const stashFS2D = this.cloner.clone(this.getStashSlotMap(pmcData, sessionId));
for (const itemWithChildren of itemsWithChildren) for (const itemWithChildren of itemsWithChildren)
{ {
if (this.canPlaceItemInContainer(stashFS2D, itemWithChildren)) if (this.canPlaceItemInContainer(stashFS2D, itemWithChildren))
@ -534,7 +536,7 @@ export class InventoryHelper
// Keep splitting items into stacks until none left // Keep splitting items into stacks until none left
if (remainingCountOfItemToAdd > 0) if (remainingCountOfItemToAdd > 0)
{ {
const newChildItemToAdd = this.jsonUtil.clone(itemToAdd); const newChildItemToAdd = this.cloner.clone(itemToAdd);
if (remainingCountOfItemToAdd > itemDetails._props.StackMaxSize) if (remainingCountOfItemToAdd > itemDetails._props.StackMaxSize)
{ {
// Reduce total count of item purchased by stack size we're going to add to inventory // Reduce total count of item purchased by stack size we're going to add to inventory

View File

@ -13,6 +13,7 @@ import { ItemBaseClassService } from "@spt-aki/services/ItemBaseClassService";
import { ItemFilterService } from "@spt-aki/services/ItemFilterService"; import { ItemFilterService } from "@spt-aki/services/ItemFilterService";
import { LocaleService } from "@spt-aki/services/LocaleService"; import { LocaleService } from "@spt-aki/services/LocaleService";
import { LocalisationService } from "@spt-aki/services/LocalisationService"; import { LocalisationService } from "@spt-aki/services/LocalisationService";
import { ICloner } from "@spt-aki/utils/cloners/ICloner";
import { CompareUtil } from "@spt-aki/utils/CompareUtil"; import { CompareUtil } from "@spt-aki/utils/CompareUtil";
import { HashUtil } from "@spt-aki/utils/HashUtil"; import { HashUtil } from "@spt-aki/utils/HashUtil";
import { JsonUtil } from "@spt-aki/utils/JsonUtil"; import { JsonUtil } from "@spt-aki/utils/JsonUtil";
@ -47,6 +48,7 @@ export class ItemHelper
@inject("LocalisationService") protected localisationService: LocalisationService, @inject("LocalisationService") protected localisationService: LocalisationService,
@inject("LocaleService") protected localeService: LocaleService, @inject("LocaleService") protected localeService: LocaleService,
@inject("CompareUtil") protected compareUtil: CompareUtil, @inject("CompareUtil") protected compareUtil: CompareUtil,
@inject("RecursiveCloner") protected cloner: ICloner,
) )
{} {}
@ -428,7 +430,7 @@ export class ItemHelper
*/ */
public getItems(): ITemplateItem[] public getItems(): ITemplateItem[]
{ {
return this.jsonUtil.clone(Object.values(this.databaseServer.getTables().templates.items)); return this.cloner.clone(Object.values(this.databaseServer.getTables().templates.items));
} }
/** /**
@ -734,7 +736,7 @@ export class ItemHelper
// return the item as is. // return the item as is.
if (remainingCount <= maxStackSize) if (remainingCount <= maxStackSize)
{ {
rootAndChildren.push(this.jsonUtil.clone(itemToSplit)); rootAndChildren.push(this.cloner.clone(itemToSplit));
return rootAndChildren; return rootAndChildren;
} }
@ -742,7 +744,7 @@ export class ItemHelper
while (remainingCount) while (remainingCount)
{ {
const amount = Math.min(remainingCount, maxStackSize); const amount = Math.min(remainingCount, maxStackSize);
const newStackClone = this.jsonUtil.clone(itemToSplit); const newStackClone = this.cloner.clone(itemToSplit);
newStackClone._id = this.hashUtil.generate(); newStackClone._id = this.hashUtil.generate();
newStackClone.upd.StackObjectsCount = amount; newStackClone.upd.StackObjectsCount = amount;
@ -775,7 +777,7 @@ export class ItemHelper
while (remainingCount) while (remainingCount)
{ {
const amount = Math.min(remainingCount, itemMaxStackSize); const amount = Math.min(remainingCount, itemMaxStackSize);
const newItemClone = this.jsonUtil.clone(itemToSplit); const newItemClone = this.cloner.clone(itemToSplit);
newItemClone._id = this.hashUtil.generate(); newItemClone._id = this.hashUtil.generate();
newItemClone.upd.StackObjectsCount = amount; newItemClone.upd.StackObjectsCount = amount;
@ -836,7 +838,7 @@ export class ItemHelper
fastPanel = null, fastPanel = null,
): Item[] ): Item[]
{ {
let items = this.jsonUtil.clone(originalItems); // Deep-clone the items to avoid mutation. let items = this.cloner.clone(originalItems); // Deep-clone the items to avoid mutation.
let serialisedInventory = this.jsonUtil.serialize(items); let serialisedInventory = this.jsonUtil.serialize(items);
for (const item of items) for (const item of items)

View File

@ -2,6 +2,7 @@ import { inject, injectable } from "tsyringe";
import { IPreset } from "@spt-aki/models/eft/common/IGlobals"; import { IPreset } from "@spt-aki/models/eft/common/IGlobals";
import { BaseClasses } from "@spt-aki/models/enums/BaseClasses"; import { BaseClasses } from "@spt-aki/models/enums/BaseClasses";
import { DatabaseServer } from "@spt-aki/servers/DatabaseServer"; import { DatabaseServer } from "@spt-aki/servers/DatabaseServer";
import { ICloner } from "@spt-aki/utils/cloners/ICloner";
import { JsonUtil } from "@spt-aki/utils/JsonUtil"; import { JsonUtil } from "@spt-aki/utils/JsonUtil";
import { ItemHelper } from "./ItemHelper"; import { ItemHelper } from "./ItemHelper";
@ -16,6 +17,7 @@ export class PresetHelper
@inject("JsonUtil") protected jsonUtil: JsonUtil, @inject("JsonUtil") protected jsonUtil: JsonUtil,
@inject("DatabaseServer") protected databaseServer: DatabaseServer, @inject("DatabaseServer") protected databaseServer: DatabaseServer,
@inject("ItemHelper") protected itemHelper: ItemHelper, @inject("ItemHelper") protected itemHelper: ItemHelper,
@inject("RecursiveCloner") protected cloner: ICloner,
) )
{} {}
@ -70,8 +72,10 @@ export class PresetHelper
{ {
if (!this.defaultEquipmentPresets) if (!this.defaultEquipmentPresets)
{ {
this.defaultEquipmentPresets = Object.values(this.databaseServer.getTables().globals.ItemPresets).filter(preset => preset._encyclopedia !== undefined && this.itemHelper.armorItemCanHoldMods(preset._encyclopedia)) this.defaultEquipmentPresets = Object.values(this.databaseServer.getTables().globals.ItemPresets)
.reduce((acc, cur) => .filter(preset => preset._encyclopedia !== undefined
&& this.itemHelper.armorItemCanHoldMods(preset._encyclopedia),
).reduce((acc, cur) =>
{ {
acc[cur._id] = cur; acc[cur._id] = cur;
return acc; return acc;
@ -104,12 +108,12 @@ export class PresetHelper
public getPreset(id: string): IPreset public getPreset(id: string): IPreset
{ {
return this.jsonUtil.clone(this.databaseServer.getTables().globals.ItemPresets[id]); return this.cloner.clone(this.databaseServer.getTables().globals.ItemPresets[id]);
} }
public getAllPresets(): IPreset[] public getAllPresets(): IPreset[]
{ {
return this.jsonUtil.clone(Object.values(this.databaseServer.getTables().globals.ItemPresets)); return this.cloner.clone(Object.values(this.databaseServer.getTables().globals.ItemPresets));
} }
public getPresets(templateId: string): IPreset[] public getPresets(templateId: string): IPreset[]

View File

@ -15,6 +15,7 @@ import { DatabaseServer } from "@spt-aki/servers/DatabaseServer";
import { SaveServer } from "@spt-aki/servers/SaveServer"; import { SaveServer } from "@spt-aki/servers/SaveServer";
import { LocalisationService } from "@spt-aki/services/LocalisationService"; import { LocalisationService } from "@spt-aki/services/LocalisationService";
import { ProfileSnapshotService } from "@spt-aki/services/ProfileSnapshotService"; import { ProfileSnapshotService } from "@spt-aki/services/ProfileSnapshotService";
import { ICloner } from "@spt-aki/utils/cloners/ICloner";
import { HashUtil } from "@spt-aki/utils/HashUtil"; import { HashUtil } from "@spt-aki/utils/HashUtil";
import { JsonUtil } from "@spt-aki/utils/JsonUtil"; import { JsonUtil } from "@spt-aki/utils/JsonUtil";
import { TimeUtil } from "@spt-aki/utils/TimeUtil"; import { TimeUtil } from "@spt-aki/utils/TimeUtil";
@ -37,6 +38,7 @@ export class ProfileHelper
@inject("ProfileSnapshotService") protected profileSnapshotService: ProfileSnapshotService, @inject("ProfileSnapshotService") protected profileSnapshotService: ProfileSnapshotService,
@inject("LocalisationService") protected localisationService: LocalisationService, @inject("LocalisationService") protected localisationService: LocalisationService,
@inject("ConfigServer") protected configServer: ConfigServer, @inject("ConfigServer") protected configServer: ConfigServer,
@inject("RecursiveCloner") protected cloner: ICloner,
) )
{ {
this.inventoryConfig = this.configServer.getConfig(ConfigTypes.INVENTORY); this.inventoryConfig = this.configServer.getConfig(ConfigTypes.INVENTORY);
@ -120,8 +122,8 @@ export class ProfileHelper
scavProfile: IPmcData, scavProfile: IPmcData,
): IPmcData[] ): IPmcData[]
{ {
const clonedPmc = this.jsonUtil.clone(pmcProfile); const clonedPmc = this.cloner.clone(pmcProfile);
const clonedScav = this.jsonUtil.clone(scavProfile); const clonedScav = this.cloner.clone(scavProfile);
const profileSnapshot = this.profileSnapshotService.getProfileSnapshot(sessionId); const profileSnapshot = this.profileSnapshotService.getProfileSnapshot(sessionId);
clonedPmc.Info.Level = profileSnapshot.characters.pmc.Info.Level; clonedPmc.Info.Level = profileSnapshot.characters.pmc.Info.Level;

View File

@ -28,6 +28,7 @@ import { DatabaseServer } from "@spt-aki/servers/DatabaseServer";
import { LocaleService } from "@spt-aki/services/LocaleService"; import { LocaleService } from "@spt-aki/services/LocaleService";
import { LocalisationService } from "@spt-aki/services/LocalisationService"; import { LocalisationService } from "@spt-aki/services/LocalisationService";
import { MailSendService } from "@spt-aki/services/MailSendService"; import { MailSendService } from "@spt-aki/services/MailSendService";
import { ICloner } from "@spt-aki/utils/cloners/ICloner";
import { HashUtil } from "@spt-aki/utils/HashUtil"; import { HashUtil } from "@spt-aki/utils/HashUtil";
import { JsonUtil } from "@spt-aki/utils/JsonUtil"; import { JsonUtil } from "@spt-aki/utils/JsonUtil";
import { TimeUtil } from "@spt-aki/utils/TimeUtil"; import { TimeUtil } from "@spt-aki/utils/TimeUtil";
@ -56,6 +57,7 @@ export class QuestHelper
@inject("PresetHelper") protected presetHelper: PresetHelper, @inject("PresetHelper") protected presetHelper: PresetHelper,
@inject("MailSendService") protected mailSendService: MailSendService, @inject("MailSendService") protected mailSendService: MailSendService,
@inject("ConfigServer") protected configServer: ConfigServer, @inject("ConfigServer") protected configServer: ConfigServer,
@inject("RecursiveCloner") protected cloner: ICloner,
) )
{ {
this.questConfig = this.configServer.getConfig(ConfigTypes.QUEST); this.questConfig = this.configServer.getConfig(ConfigTypes.QUEST);
@ -320,13 +322,13 @@ export class QuestHelper
for (const target of targets) for (const target of targets)
{ {
// This has all the original id relations since we reset the id to the original after the splitStack // This has all the original id relations since we reset the id to the original after the splitStack
const itemsClone = [this.jsonUtil.clone(target)]; const itemsClone = [this.cloner.clone(target)];
// Here we generate a new id for the root item // Here we generate a new id for the root item
target._id = this.hashUtil.generate(); target._id = this.hashUtil.generate();
for (const mod of mods) for (const mod of mods)
{ {
itemsClone.push(this.jsonUtil.clone(mod)); itemsClone.push(this.cloner.clone(mod));
} }
rewardItems = rewardItems.concat(this.itemHelper.reparentItemAndChildren(target, itemsClone)); rewardItems = rewardItems.concat(this.itemHelper.reparentItemAndChildren(target, itemsClone));
@ -691,7 +693,7 @@ export class QuestHelper
*/ */
public getQuestWithOnlyLevelRequirementStartCondition(quest: IQuest): IQuest public getQuestWithOnlyLevelRequirementStartCondition(quest: IQuest): IQuest
{ {
const updatedQuest = this.jsonUtil.clone(quest); const updatedQuest = this.cloner.clone(quest);
updatedQuest.conditions.AvailableForStart = updatedQuest.conditions.AvailableForStart.filter(q => updatedQuest.conditions.AvailableForStart = updatedQuest.conditions.AvailableForStart.filter(q =>
q.conditionType === "Level", q.conditionType === "Level",
); );

View File

@ -13,6 +13,7 @@ import { ILogger } from "@spt-aki/models/spt/utils/ILogger";
import { ConfigServer } from "@spt-aki/servers/ConfigServer"; import { ConfigServer } from "@spt-aki/servers/ConfigServer";
import { DatabaseServer } from "@spt-aki/servers/DatabaseServer"; import { DatabaseServer } from "@spt-aki/servers/DatabaseServer";
import { RagfairLinkedItemService } from "@spt-aki/services/RagfairLinkedItemService"; import { RagfairLinkedItemService } from "@spt-aki/services/RagfairLinkedItemService";
import { ICloner } from "@spt-aki/utils/cloners/ICloner";
import { JsonUtil } from "@spt-aki/utils/JsonUtil"; import { JsonUtil } from "@spt-aki/utils/JsonUtil";
@injectable() @injectable()
@ -30,6 +31,7 @@ export class RagfairHelper
@inject("RagfairLinkedItemService") protected ragfairLinkedItemService: RagfairLinkedItemService, @inject("RagfairLinkedItemService") protected ragfairLinkedItemService: RagfairLinkedItemService,
@inject("UtilityHelper") protected utilityHelper: UtilityHelper, @inject("UtilityHelper") protected utilityHelper: UtilityHelper,
@inject("ConfigServer") protected configServer: ConfigServer, @inject("ConfigServer") protected configServer: ConfigServer,
@inject("RecursiveCloner") protected cloner: ICloner,
) )
{ {
this.ragfairConfig = this.configServer.getConfig(ConfigTypes.RAGFAIR); this.ragfairConfig = this.configServer.getConfig(ConfigTypes.RAGFAIR);
@ -162,7 +164,7 @@ export class RagfairHelper
{ {
if (!rootItem) if (!rootItem)
{ {
rootItem = this.jsonUtil.clone(item); rootItem = this.cloner.clone(item);
rootItem.upd.OriginalStackObjectsCount = rootItem.upd.StackObjectsCount; rootItem.upd.OriginalStackObjectsCount = rootItem.upd.StackObjectsCount;
} }
else else

View File

@ -19,6 +19,7 @@ import { SaveServer } from "@spt-aki/servers/SaveServer";
import { ItemFilterService } from "@spt-aki/services/ItemFilterService"; import { ItemFilterService } from "@spt-aki/services/ItemFilterService";
import { LocaleService } from "@spt-aki/services/LocaleService"; import { LocaleService } from "@spt-aki/services/LocaleService";
import { MailSendService } from "@spt-aki/services/MailSendService"; import { MailSendService } from "@spt-aki/services/MailSendService";
import { ICloner } from "@spt-aki/utils/cloners/ICloner";
import { HashUtil } from "@spt-aki/utils/HashUtil"; import { HashUtil } from "@spt-aki/utils/HashUtil";
import { JsonUtil } from "@spt-aki/utils/JsonUtil"; import { JsonUtil } from "@spt-aki/utils/JsonUtil";
import { RandomUtil } from "@spt-aki/utils/RandomUtil"; import { RandomUtil } from "@spt-aki/utils/RandomUtil";
@ -50,6 +51,7 @@ export class RagfairServerHelper
@inject("MailSendService") protected mailSendService: MailSendService, @inject("MailSendService") protected mailSendService: MailSendService,
@inject("ItemFilterService") protected itemFilterService: ItemFilterService, @inject("ItemFilterService") protected itemFilterService: ItemFilterService,
@inject("ConfigServer") protected configServer: ConfigServer, @inject("ConfigServer") protected configServer: ConfigServer,
@inject("RecursiveCloner") protected cloner: ICloner,
) )
{ {
this.ragfairConfig = this.configServer.getConfig(ConfigTypes.RAGFAIR); this.ragfairConfig = this.configServer.getConfig(ConfigTypes.RAGFAIR);
@ -291,7 +293,7 @@ export class RagfairServerHelper
*/ */
public getPresetItems(item: Item): Item[] public getPresetItems(item: Item): Item[]
{ {
const preset = this.jsonUtil.clone(this.databaseServer.getTables().globals.ItemPresets[item._id]._items); const preset = this.cloner.clone(this.databaseServer.getTables().globals.ItemPresets[item._id]._items);
return this.itemHelper.reparentItemAndChildren(item, preset); return this.itemHelper.reparentItemAndChildren(item, preset);
} }
@ -307,7 +309,7 @@ export class RagfairServerHelper
{ {
if (this.databaseServer.getTables().globals.ItemPresets[itemId]._items[0]._tpl === item._tpl) if (this.databaseServer.getTables().globals.ItemPresets[itemId]._items[0]._tpl === item._tpl)
{ {
const presetItems = this.jsonUtil.clone( const presetItems = this.cloner.clone(
this.databaseServer.getTables().globals.ItemPresets[itemId]._items, this.databaseServer.getTables().globals.ItemPresets[itemId]._items,
); );
presets.push(this.itemHelper.reparentItemAndChildren(item, presetItems)); presets.push(this.itemHelper.reparentItemAndChildren(item, presetItems));

View File

@ -7,6 +7,7 @@ import { IRepairConfig } from "@spt-aki/models/spt/config/IRepairConfig";
import { ILogger } from "@spt-aki/models/spt/utils/ILogger"; import { ILogger } from "@spt-aki/models/spt/utils/ILogger";
import { ConfigServer } from "@spt-aki/servers/ConfigServer"; import { ConfigServer } from "@spt-aki/servers/ConfigServer";
import { DatabaseServer } from "@spt-aki/servers/DatabaseServer"; import { DatabaseServer } from "@spt-aki/servers/DatabaseServer";
import { ICloner } from "@spt-aki/utils/cloners/ICloner";
import { JsonUtil } from "@spt-aki/utils/JsonUtil"; import { JsonUtil } from "@spt-aki/utils/JsonUtil";
import { RandomUtil } from "@spt-aki/utils/RandomUtil"; import { RandomUtil } from "@spt-aki/utils/RandomUtil";
@ -21,6 +22,7 @@ export class RepairHelper
@inject("RandomUtil") protected randomUtil: RandomUtil, @inject("RandomUtil") protected randomUtil: RandomUtil,
@inject("DatabaseServer") protected databaseServer: DatabaseServer, @inject("DatabaseServer") protected databaseServer: DatabaseServer,
@inject("ConfigServer") protected configServer: ConfigServer, @inject("ConfigServer") protected configServer: ConfigServer,
@inject("RecursiveCloner") protected cloner: ICloner,
) )
{ {
this.repairConfig = this.configServer.getConfig(ConfigTypes.REPAIR); this.repairConfig = this.configServer.getConfig(ConfigTypes.REPAIR);
@ -48,9 +50,9 @@ export class RepairHelper
{ {
this.logger.debug(`Adding ${amountToRepair} to ${itemToRepairDetails._name} using kit: ${useRepairKit}`); this.logger.debug(`Adding ${amountToRepair} to ${itemToRepairDetails._name} using kit: ${useRepairKit}`);
const itemMaxDurability = this.jsonUtil.clone(itemToRepair.upd.Repairable.MaxDurability); const itemMaxDurability = this.cloner.clone(itemToRepair.upd.Repairable.MaxDurability);
const itemCurrentDurability = this.jsonUtil.clone(itemToRepair.upd.Repairable.Durability); const itemCurrentDurability = this.cloner.clone(itemToRepair.upd.Repairable.Durability);
const itemCurrentMaxDurability = this.jsonUtil.clone(itemToRepair.upd.Repairable.MaxDurability); const itemCurrentMaxDurability = this.cloner.clone(itemToRepair.upd.Repairable.MaxDurability);
let newCurrentDurability = itemCurrentDurability + amountToRepair; let newCurrentDurability = itemCurrentDurability + amountToRepair;
let newCurrentMaxDurability = itemCurrentMaxDurability + amountToRepair; let newCurrentMaxDurability = itemCurrentMaxDurability + amountToRepair;

View File

@ -2,6 +2,7 @@ import { inject, injectable } from "tsyringe";
import { ConfigTypes } from "@spt-aki/models/enums/ConfigTypes"; import { ConfigTypes } from "@spt-aki/models/enums/ConfigTypes";
import { IEliminationConfig, IQuestConfig, IRepeatableQuestConfig } from "@spt-aki/models/spt/config/IQuestConfig"; import { IEliminationConfig, IQuestConfig, IRepeatableQuestConfig } from "@spt-aki/models/spt/config/IQuestConfig";
import { ConfigServer } from "@spt-aki/servers/ConfigServer"; import { ConfigServer } from "@spt-aki/servers/ConfigServer";
import { ICloner } from "@spt-aki/utils/cloners/ICloner";
import { JsonUtil } from "@spt-aki/utils/JsonUtil"; import { JsonUtil } from "@spt-aki/utils/JsonUtil";
import { MathUtil } from "@spt-aki/utils/MathUtil"; import { MathUtil } from "@spt-aki/utils/MathUtil";
import { ProbabilityObject, ProbabilityObjectArray } from "@spt-aki/utils/RandomUtil"; import { ProbabilityObject, ProbabilityObjectArray } from "@spt-aki/utils/RandomUtil";
@ -15,6 +16,7 @@ export class RepeatableQuestHelper
@inject("MathUtil") protected mathUtil: MathUtil, @inject("MathUtil") protected mathUtil: MathUtil,
@inject("JsonUtil") protected jsonUtil: JsonUtil, @inject("JsonUtil") protected jsonUtil: JsonUtil,
@inject("ConfigServer") protected configServer: ConfigServer, @inject("ConfigServer") protected configServer: ConfigServer,
@inject("RecursiveCloner") protected cloner: ICloner,
) )
{ {
this.questConfig = this.configServer.getConfig(ConfigTypes.QUEST); this.questConfig = this.configServer.getConfig(ConfigTypes.QUEST);
@ -38,8 +40,8 @@ export class RepeatableQuestHelper
public probabilityObjectArray<K, V>(configArrayInput: ProbabilityObject<K, V>[]): ProbabilityObjectArray<K, V> public probabilityObjectArray<K, V>(configArrayInput: ProbabilityObject<K, V>[]): ProbabilityObjectArray<K, V>
{ {
const configArray = this.jsonUtil.clone(configArrayInput); const configArray = this.cloner.clone(configArrayInput);
const probabilityArray = new ProbabilityObjectArray<K, V>(this.mathUtil, this.jsonUtil); const probabilityArray = new ProbabilityObjectArray<K, V>(this.mathUtil, this.cloner);
for (const configObject of configArray) for (const configObject of configArray)
{ {
probabilityArray.push( probabilityArray.push(

View File

@ -22,6 +22,7 @@ import { FenceService } from "@spt-aki/services/FenceService";
import { LocalisationService } from "@spt-aki/services/LocalisationService"; import { LocalisationService } from "@spt-aki/services/LocalisationService";
import { PaymentService } from "@spt-aki/services/PaymentService"; import { PaymentService } from "@spt-aki/services/PaymentService";
import { TraderPurchasePersisterService } from "@spt-aki/services/TraderPurchasePersisterService"; import { TraderPurchasePersisterService } from "@spt-aki/services/TraderPurchasePersisterService";
import { ICloner } from "@spt-aki/utils/cloners/ICloner";
import { HttpResponseUtil } from "@spt-aki/utils/HttpResponseUtil"; import { HttpResponseUtil } from "@spt-aki/utils/HttpResponseUtil";
import { JsonUtil } from "@spt-aki/utils/JsonUtil"; import { JsonUtil } from "@spt-aki/utils/JsonUtil";
@ -47,6 +48,7 @@ export class TradeHelper
@inject("TraderPurchasePersisterService") protected traderPurchasePersisterService: @inject("TraderPurchasePersisterService") protected traderPurchasePersisterService:
TraderPurchasePersisterService, TraderPurchasePersisterService,
@inject("ConfigServer") protected configServer: ConfigServer, @inject("ConfigServer") protected configServer: ConfigServer,
@inject("RecursiveCloner") protected cloner: ICloner,
) )
{ {
this.traderConfig = this.configServer.getConfig(ConfigTypes.TRADER); this.traderConfig = this.configServer.getConfig(ConfigTypes.TRADER);
@ -105,7 +107,7 @@ export class TradeHelper
// Get raw offer from ragfair, clone to prevent altering offer itself // Get raw offer from ragfair, clone to prevent altering offer itself
const allOffers = this.ragfairServer.getOffers(); const allOffers = this.ragfairServer.getOffers();
const offerWithItemCloned = this.jsonUtil.clone(allOffers.find(x => x._id === buyRequestData.item_id)); const offerWithItemCloned = this.cloner.clone(allOffers.find(x => x._id === buyRequestData.item_id));
offerItems = offerWithItemCloned.items; offerItems = offerWithItemCloned.items;
} }
else if (buyRequestData.tid === Traders.FENCE) else if (buyRequestData.tid === Traders.FENCE)
@ -196,7 +198,7 @@ export class TradeHelper
const itemsToSendToPlayer: Item[][] = []; const itemsToSendToPlayer: Item[][] = [];
while (itemsToSendRemaining > 0) while (itemsToSendRemaining > 0)
{ {
const offerClone = this.jsonUtil.clone(offerItems); const offerClone = this.cloner.clone(offerItems);
// Handle stackable items that have a max stack size limit // Handle stackable items that have a max stack size limit
const itemCountToSend = Math.min(itemMaxStackSize, itemsToSendRemaining); const itemCountToSend = Math.min(itemMaxStackSize, itemsToSendRemaining);
offerClone[0].upd.StackObjectsCount = itemCountToSend; offerClone[0].upd.StackObjectsCount = itemCountToSend;

View File

@ -17,6 +17,7 @@ import { FenceService } from "@spt-aki/services/FenceService";
import { LocalisationService } from "@spt-aki/services/LocalisationService"; import { LocalisationService } from "@spt-aki/services/LocalisationService";
import { TraderAssortService } from "@spt-aki/services/TraderAssortService"; import { TraderAssortService } from "@spt-aki/services/TraderAssortService";
import { TraderPurchasePersisterService } from "@spt-aki/services/TraderPurchasePersisterService"; import { TraderPurchasePersisterService } from "@spt-aki/services/TraderPurchasePersisterService";
import { ICloner } from "@spt-aki/utils/cloners/ICloner";
import { JsonUtil } from "@spt-aki/utils/JsonUtil"; import { JsonUtil } from "@spt-aki/utils/JsonUtil";
import { MathUtil } from "@spt-aki/utils/MathUtil"; import { MathUtil } from "@spt-aki/utils/MathUtil";
import { TimeUtil } from "@spt-aki/utils/TimeUtil"; import { TimeUtil } from "@spt-aki/utils/TimeUtil";
@ -46,6 +47,7 @@ export class TraderAssortHelper
@inject("TraderHelper") protected traderHelper: TraderHelper, @inject("TraderHelper") protected traderHelper: TraderHelper,
@inject("FenceService") protected fenceService: FenceService, @inject("FenceService") protected fenceService: FenceService,
@inject("ConfigServer") protected configServer: ConfigServer, @inject("ConfigServer") protected configServer: ConfigServer,
@inject("RecursiveCloner") protected cloner: ICloner,
) )
{ {
this.traderConfig = this.configServer.getConfig(ConfigTypes.TRADER); this.traderConfig = this.configServer.getConfig(ConfigTypes.TRADER);
@ -68,7 +70,7 @@ export class TraderAssortHelper
return this.getRagfairDataAsTraderAssort(); return this.getRagfairDataAsTraderAssort();
} }
const traderClone = this.jsonUtil.clone(this.databaseServer.getTables().traders[traderId]); const traderClone = this.cloner.clone(this.databaseServer.getTables().traders[traderId]);
const pmcProfile = this.profileHelper.getPmcProfile(sessionId); const pmcProfile = this.profileHelper.getPmcProfile(sessionId);
if (traderId === Traders.FENCE) if (traderId === Traders.FENCE)
@ -250,7 +252,7 @@ export class TraderAssortHelper
*/ */
protected getPristineTraderAssorts(traderId: string): Item[] protected getPristineTraderAssorts(traderId: string): Item[]
{ {
return this.jsonUtil.clone(this.traderAssortService.getPristineTraderAssort(traderId).items); return this.cloner.clone(this.traderAssortService.getPristineTraderAssort(traderId).items);
} }
/** /**

View File

@ -2,6 +2,7 @@ import path from "node:path";
import { inject, injectable } from "tsyringe"; import { inject, injectable } from "tsyringe";
import { HttpServerHelper } from "@spt-aki/helpers/HttpServerHelper"; import { HttpServerHelper } from "@spt-aki/helpers/HttpServerHelper";
import { BundleHashCacheService } from "@spt-aki/services/cache/BundleHashCacheService"; import { BundleHashCacheService } from "@spt-aki/services/cache/BundleHashCacheService";
import { ICloner } from "@spt-aki/utils/cloners/ICloner";
import { JsonUtil } from "@spt-aki/utils/JsonUtil"; import { JsonUtil } from "@spt-aki/utils/JsonUtil";
import { VFS } from "@spt-aki/utils/VFS"; import { VFS } from "@spt-aki/utils/VFS";
@ -31,6 +32,7 @@ export class BundleLoader
@inject("VFS") protected vfs: VFS, @inject("VFS") protected vfs: VFS,
@inject("JsonUtil") protected jsonUtil: JsonUtil, @inject("JsonUtil") protected jsonUtil: JsonUtil,
@inject("BundleHashCacheService") protected bundleHashCacheService: BundleHashCacheService, @inject("BundleHashCacheService") protected bundleHashCacheService: BundleHashCacheService,
@inject("RecursiveCloner") protected cloner: ICloner,
) )
{} {}
@ -51,7 +53,7 @@ export class BundleLoader
public getBundle(key: string): BundleInfo public getBundle(key: string): BundleInfo
{ {
return this.jsonUtil.clone(this.bundles[key]); return this.cloner.clone(this.bundles[key]);
} }
public addBundles(modpath: string): void public addBundles(modpath: string): void

View File

@ -1,3 +1,4 @@
import { ICloner } from "@spt-aki/utils/cloners/ICloner";
import { JsonUtil } from "@spt-aki/utils/JsonUtil"; import { JsonUtil } from "@spt-aki/utils/JsonUtil";
import { RandomUtil } from "@spt-aki/utils/RandomUtil"; import { RandomUtil } from "@spt-aki/utils/RandomUtil";
@ -5,9 +6,9 @@ export class ExhaustableArray<T> implements IExhaustableArray<T>
{ {
private pool: T[]; private pool: T[];
constructor(private itemPool: T[], private randomUtil: RandomUtil, private jsonUtil: JsonUtil) constructor(private itemPool: T[], private randomUtil: RandomUtil, private cloner: ICloner)
{ {
this.pool = this.jsonUtil.clone(itemPool); this.pool = this.cloner.clone(itemPool);
} }
public getRandomValue(): T public getRandomValue(): T
@ -18,7 +19,7 @@ export class ExhaustableArray<T> implements IExhaustableArray<T>
} }
const index = this.randomUtil.getInt(0, this.pool.length - 1); const index = this.randomUtil.getInt(0, this.pool.length - 1);
const toReturn = this.jsonUtil.clone(this.pool[index]); const toReturn = this.cloner.clone(this.pool[index]);
this.pool.splice(index, 1); this.pool.splice(index, 1);
return toReturn; return toReturn;
} }
@ -30,7 +31,7 @@ export class ExhaustableArray<T> implements IExhaustableArray<T>
return null; return null;
} }
const toReturn = this.jsonUtil.clone(this.pool[0]); const toReturn = this.cloner.clone(this.pool[0]);
this.pool.splice(0, 1); this.pool.splice(0, 1);
return toReturn; return toReturn;
} }

View File

@ -4,6 +4,7 @@ import { IPmcData } from "@spt-aki/models/eft/common/IPmcData";
import { IHideoutImprovement, Productive, TraderInfo } from "@spt-aki/models/eft/common/tables/IBotBase"; import { IHideoutImprovement, Productive, TraderInfo } from "@spt-aki/models/eft/common/tables/IBotBase";
import { ProfileChange, TraderData } from "@spt-aki/models/eft/itemEvent/IItemEventRouterBase"; import { ProfileChange, TraderData } from "@spt-aki/models/eft/itemEvent/IItemEventRouterBase";
import { IItemEventRouterResponse } from "@spt-aki/models/eft/itemEvent/IItemEventRouterResponse"; import { IItemEventRouterResponse } from "@spt-aki/models/eft/itemEvent/IItemEventRouterResponse";
import { ICloner } from "@spt-aki/utils/cloners/ICloner";
import { JsonUtil } from "@spt-aki/utils/JsonUtil"; import { JsonUtil } from "@spt-aki/utils/JsonUtil";
import { TimeUtil } from "@spt-aki/utils/TimeUtil"; import { TimeUtil } from "@spt-aki/utils/TimeUtil";
@ -17,6 +18,7 @@ export class EventOutputHolder
@inject("JsonUtil") protected jsonUtil: JsonUtil, @inject("JsonUtil") protected jsonUtil: JsonUtil,
@inject("ProfileHelper") protected profileHelper: ProfileHelper, @inject("ProfileHelper") protected profileHelper: ProfileHelper,
@inject("TimeUtil") protected timeUtil: TimeUtil, @inject("TimeUtil") protected timeUtil: TimeUtil,
@inject("RecursiveCloner") protected cloner: ICloner,
) )
{} {}
@ -54,7 +56,7 @@ export class EventOutputHolder
production: {}, production: {},
improvements: {}, improvements: {},
skills: { Common: [], Mastering: [], Points: 0 }, skills: { Common: [], Mastering: [], Points: 0 },
health: this.jsonUtil.clone(pmcData.Health), health: this.cloner.clone(pmcData.Health),
traderRelations: {}, traderRelations: {},
// changedHideoutStashes: {}, // changedHideoutStashes: {},
recipeUnlocked: {}, recipeUnlocked: {},
@ -72,15 +74,15 @@ export class EventOutputHolder
const profileChanges: ProfileChange = this.output.profileChanges[sessionId]; const profileChanges: ProfileChange = this.output.profileChanges[sessionId];
profileChanges.experience = pmcData.Info.Experience; profileChanges.experience = pmcData.Info.Experience;
profileChanges.health = this.jsonUtil.clone(pmcData.Health); profileChanges.health = this.cloner.clone(pmcData.Health);
profileChanges.skills.Common = this.jsonUtil.clone(pmcData.Skills.Common); // Always send skills for Item event route response profileChanges.skills.Common = this.cloner.clone(pmcData.Skills.Common); // Always send skills for Item event route response
profileChanges.skills.Mastering = this.jsonUtil.clone(pmcData.Skills.Mastering); profileChanges.skills.Mastering = this.cloner.clone(pmcData.Skills.Mastering);
// Clone productions to ensure we preseve the profile jsons data // Clone productions to ensure we preseve the profile jsons data
profileChanges.production = this.getProductionsFromProfileAndFlagComplete( profileChanges.production = this.getProductionsFromProfileAndFlagComplete(
this.jsonUtil.clone(pmcData.Hideout.Production), this.cloner.clone(pmcData.Hideout.Production),
); );
profileChanges.improvements = this.jsonUtil.clone(this.getImprovementsFromProfileAndFlagComplete(pmcData)); profileChanges.improvements = this.cloner.clone(this.getImprovementsFromProfileAndFlagComplete(pmcData));
profileChanges.traderRelations = this.constructTraderRelations(pmcData.TradersInfo); profileChanges.traderRelations = this.constructTraderRelations(pmcData.TradersInfo);
// Fixes container craft from water collector not resetting after collection + removed completed normal crafts // Fixes container craft from water collector not resetting after collection + removed completed normal crafts

View File

@ -6,6 +6,7 @@ import { IItemEventRouterResponse } from "@spt-aki/models/eft/itemEvent/IItemEve
import { ILogger } from "@spt-aki/models/spt/utils/ILogger"; import { ILogger } from "@spt-aki/models/spt/utils/ILogger";
import { EventOutputHolder } from "@spt-aki/routers/EventOutputHolder"; import { EventOutputHolder } from "@spt-aki/routers/EventOutputHolder";
import { LocalisationService } from "@spt-aki/services/LocalisationService"; import { LocalisationService } from "@spt-aki/services/LocalisationService";
import { ICloner } from "@spt-aki/utils/cloners/ICloner";
import { JsonUtil } from "@spt-aki/utils/JsonUtil"; import { JsonUtil } from "@spt-aki/utils/JsonUtil";
@injectable() @injectable()
@ -18,6 +19,7 @@ export class ItemEventRouter
@injectAll("IERouters") protected itemEventRouters: ItemEventRouterDefinition[], @injectAll("IERouters") protected itemEventRouters: ItemEventRouterDefinition[],
@inject("LocalisationService") protected localisationService: LocalisationService, @inject("LocalisationService") protected localisationService: LocalisationService,
@inject("EventOutputHolder") protected eventOutputHolder: EventOutputHolder, @inject("EventOutputHolder") protected eventOutputHolder: EventOutputHolder,
@inject("RecursiveCloner") protected cloner: ICloner,
) )
{} {}
@ -54,7 +56,7 @@ export class ItemEventRouter
this.eventOutputHolder.updateOutputProperties(sessionID); this.eventOutputHolder.updateOutputProperties(sessionID);
// Clone output before resetting the output object ready for use next time // Clone output before resetting the output object ready for use next time
const outputClone = this.jsonUtil.clone(output); const outputClone = this.cloner.clone(output);
this.eventOutputHolder.resetOutput(sessionID); this.eventOutputHolder.resetOutput(sessionID);
return outputClone; return outputClone;

View File

@ -9,6 +9,7 @@ import { ILogger } from "@spt-aki/models/spt/utils/ILogger";
import { DatabaseServer } from "@spt-aki/servers/DatabaseServer"; import { DatabaseServer } from "@spt-aki/servers/DatabaseServer";
import { LocalisationService } from "@spt-aki/services/LocalisationService"; import { LocalisationService } from "@spt-aki/services/LocalisationService";
import { RagfairPriceService } from "@spt-aki/services/RagfairPriceService"; import { RagfairPriceService } from "@spt-aki/services/RagfairPriceService";
import { ICloner } from "@spt-aki/utils/cloners/ICloner";
import { JsonUtil } from "@spt-aki/utils/JsonUtil"; import { JsonUtil } from "@spt-aki/utils/JsonUtil";
@injectable() @injectable()
@ -24,6 +25,7 @@ export class BotLootCacheService
@inject("PMCLootGenerator") protected pmcLootGenerator: PMCLootGenerator, @inject("PMCLootGenerator") protected pmcLootGenerator: PMCLootGenerator,
@inject("LocalisationService") protected localisationService: LocalisationService, @inject("LocalisationService") protected localisationService: LocalisationService,
@inject("RagfairPriceService") protected ragfairPriceService: RagfairPriceService, @inject("RagfairPriceService") protected ragfairPriceService: RagfairPriceService,
@inject("RecursiveCloner") protected cloner: ICloner,
) )
{ {
this.clearCache(); this.clearCache();
@ -111,7 +113,7 @@ export class BotLootCacheService
break; break;
} }
return this.jsonUtil.clone(result); return this.cloner.clone(result);
} }
/** /**
@ -136,9 +138,9 @@ export class BotLootCacheService
if (isPmc) if (isPmc)
{ {
// Replace lootPool from bot json with our own generated list for PMCs // Replace lootPool from bot json with our own generated list for PMCs
lootPool.Backpack = this.jsonUtil.clone(this.pmcLootGenerator.generatePMCBackpackLootPool(botRole)); lootPool.Backpack = this.cloner.clone(this.pmcLootGenerator.generatePMCBackpackLootPool(botRole));
lootPool.Pockets = this.jsonUtil.clone(this.pmcLootGenerator.generatePMCPocketLootPool(botRole)); lootPool.Pockets = this.cloner.clone(this.pmcLootGenerator.generatePMCPocketLootPool(botRole));
lootPool.TacticalVest = this.jsonUtil.clone(this.pmcLootGenerator.generatePMCVestLootPool(botRole)); lootPool.TacticalVest = this.cloner.clone(this.pmcLootGenerator.generatePMCVestLootPool(botRole));
} }
// Backpack/Pockets etc // Backpack/Pockets etc

View File

@ -21,6 +21,7 @@ import { ILogger } from "@spt-aki/models/spt/utils/ILogger";
import { ConfigServer } from "@spt-aki/servers/ConfigServer"; import { ConfigServer } from "@spt-aki/servers/ConfigServer";
import { DatabaseServer } from "@spt-aki/servers/DatabaseServer"; import { DatabaseServer } from "@spt-aki/servers/DatabaseServer";
import { LocalisationService } from "@spt-aki/services/LocalisationService"; import { LocalisationService } from "@spt-aki/services/LocalisationService";
import { ICloner } from "@spt-aki/utils/cloners/ICloner";
import { JsonUtil } from "@spt-aki/utils/JsonUtil"; import { JsonUtil } from "@spt-aki/utils/JsonUtil";
import { RandomUtil } from "@spt-aki/utils/RandomUtil"; import { RandomUtil } from "@spt-aki/utils/RandomUtil";
import { TimeUtil } from "@spt-aki/utils/TimeUtil"; import { TimeUtil } from "@spt-aki/utils/TimeUtil";
@ -69,6 +70,7 @@ export class FenceService
@inject("PresetHelper") protected presetHelper: PresetHelper, @inject("PresetHelper") protected presetHelper: PresetHelper,
@inject("LocalisationService") protected localisationService: LocalisationService, @inject("LocalisationService") protected localisationService: LocalisationService,
@inject("ConfigServer") protected configServer: ConfigServer, @inject("ConfigServer") protected configServer: ConfigServer,
@inject("RecursiveCloner") protected cloner: ICloner,
) )
{ {
this.traderConfig = this.configServer.getConfig(ConfigTypes.TRADER); this.traderConfig = this.configServer.getConfig(ConfigTypes.TRADER);
@ -134,13 +136,13 @@ export class FenceService
} }
// Clone assorts so we can adjust prices before sending to client // Clone assorts so we can adjust prices before sending to client
const assort = this.jsonUtil.clone(this.fenceAssort); const assort = this.cloner.clone(this.fenceAssort);
this.adjustAssortItemPricesByConfigMultiplier(assort, 1, this.traderConfig.fence.presetPriceMult); this.adjustAssortItemPricesByConfigMultiplier(assort, 1, this.traderConfig.fence.presetPriceMult);
// merge normal fence assorts + discount assorts if player standing is large enough // merge normal fence assorts + discount assorts if player standing is large enough
if (pmcProfile.TradersInfo[Traders.FENCE].standing >= 6) if (pmcProfile.TradersInfo[Traders.FENCE].standing >= 6)
{ {
const discountAssort = this.jsonUtil.clone(this.fenceDiscountAssort); const discountAssort = this.cloner.clone(this.fenceDiscountAssort);
this.adjustAssortItemPricesByConfigMultiplier( this.adjustAssortItemPricesByConfigMultiplier(
discountAssort, discountAssort,
this.traderConfig.fence.discountOptions.itemPriceMult, this.traderConfig.fence.discountOptions.itemPriceMult,
@ -163,7 +165,7 @@ export class FenceService
{ {
// HUGE THANKS TO LACYWAY AND LEAVES FOR PROVIDING THIS SOLUTION FOR SPT TO IMPLEMENT!! // HUGE THANKS TO LACYWAY AND LEAVES FOR PROVIDING THIS SOLUTION FOR SPT TO IMPLEMENT!!
// Copy the item and its children // Copy the item and its children
let clonedItems = this.jsonUtil.clone(this.itemHelper.findAndReturnChildrenAsItems(items, mainItem._id)); let clonedItems = this.cloner.clone(this.itemHelper.findAndReturnChildrenAsItems(items, mainItem._id));
const root = clonedItems[0]; const root = clonedItems[0];
const cost = this.getItemPrice(root._tpl, clonedItems); const cost = this.getItemPrice(root._tpl, clonedItems);
@ -310,7 +312,7 @@ export class FenceService
*/ */
public getRawFenceAssorts(): ITraderAssort public getRawFenceAssorts(): ITraderAssort
{ {
return this.mergeAssorts(this.jsonUtil.clone(this.fenceAssort), this.jsonUtil.clone(this.fenceDiscountAssort)); return this.mergeAssorts(this.cloner.clone(this.fenceAssort), this.cloner.clone(this.fenceDiscountAssort));
} }
/** /**
@ -682,7 +684,7 @@ export class FenceService
{ {
const result: ICreateFenceAssortsResult = { sptItems: [], barter_scheme: {}, loyal_level_items: {} }; const result: ICreateFenceAssortsResult = { sptItems: [], barter_scheme: {}, loyal_level_items: {} };
const baseFenceAssortClone = this.jsonUtil.clone(this.databaseServer.getTables().traders[Traders.FENCE].assort); const baseFenceAssortClone = this.cloner.clone(this.databaseServer.getTables().traders[Traders.FENCE].assort);
const itemTypeLimitCounts = this.initItemLimitCounter(this.traderConfig.fence.itemTypeLimits); const itemTypeLimitCounts = this.initItemLimitCounter(this.traderConfig.fence.itemTypeLimits);
if (itemCounts.item > 0) if (itemCounts.item > 0)
@ -743,7 +745,7 @@ export class FenceService
continue; continue;
} }
let desiredAssortItemAndChildrenClone = this.jsonUtil.clone( let desiredAssortItemAndChildrenClone = this.cloner.clone(
this.itemHelper.findAndReturnChildrenAsItems(baseFenceAssortClone.items, chosenBaseAssortRoot._id), this.itemHelper.findAndReturnChildrenAsItems(baseFenceAssortClone.items, chosenBaseAssortRoot._id),
); );
@ -814,7 +816,7 @@ export class FenceService
assorts.sptItems.push(desiredAssortItemAndChildrenClone); assorts.sptItems.push(desiredAssortItemAndChildrenClone);
assorts.barter_scheme[rootItemBeingAdded._id] = this.jsonUtil.clone( assorts.barter_scheme[rootItemBeingAdded._id] = this.cloner.clone(
baseFenceAssortClone.barter_scheme[chosenBaseAssortRoot._id], baseFenceAssortClone.barter_scheme[chosenBaseAssortRoot._id],
); );
@ -1004,7 +1006,7 @@ export class FenceService
const rootItemDb = this.itemHelper.getItem(randomPresetRoot._tpl)[1]; const rootItemDb = this.itemHelper.getItem(randomPresetRoot._tpl)[1];
const presetWithChildrenClone = this.jsonUtil.clone( const presetWithChildrenClone = this.cloner.clone(
this.itemHelper.findAndReturnChildrenAsItems(baseFenceAssort.items, randomPresetRoot._id), this.itemHelper.findAndReturnChildrenAsItems(baseFenceAssort.items, randomPresetRoot._id),
); );
@ -1060,7 +1062,7 @@ export class FenceService
const randomPresetRoot = this.randomUtil.getArrayValue(equipmentPresetRootItems); const randomPresetRoot = this.randomUtil.getArrayValue(equipmentPresetRootItems);
const rootItemDb = this.itemHelper.getItem(randomPresetRoot._tpl)[1]; const rootItemDb = this.itemHelper.getItem(randomPresetRoot._tpl)[1];
const presetWithChildrenClone = this.jsonUtil.clone( const presetWithChildrenClone = this.cloner.clone(
this.itemHelper.findAndReturnChildrenAsItems(baseFenceAssort.items, randomPresetRoot._id), this.itemHelper.findAndReturnChildrenAsItems(baseFenceAssort.items, randomPresetRoot._id),
); );

View File

@ -12,7 +12,6 @@ import { ISaveProgressRequestData } from "@spt-aki/models/eft/inRaid/ISaveProgre
import { BonusType } from "@spt-aki/models/enums/BonusType"; import { BonusType } from "@spt-aki/models/enums/BonusType";
import { ConfigTypes } from "@spt-aki/models/enums/ConfigTypes"; import { ConfigTypes } from "@spt-aki/models/enums/ConfigTypes";
import { MessageType } from "@spt-aki/models/enums/MessageType"; import { MessageType } from "@spt-aki/models/enums/MessageType";
import { Traders } from "@spt-aki/models/enums/Traders";
import { IInsuranceConfig } from "@spt-aki/models/spt/config/IInsuranceConfig"; import { IInsuranceConfig } from "@spt-aki/models/spt/config/IInsuranceConfig";
import { ILostOnDeathConfig } from "@spt-aki/models/spt/config/ILostOnDeathConfig"; import { ILostOnDeathConfig } from "@spt-aki/models/spt/config/ILostOnDeathConfig";
import { IInsuranceEquipmentPkg } from "@spt-aki/models/spt/services/IInsuranceEquipmentPkg"; import { IInsuranceEquipmentPkg } from "@spt-aki/models/spt/services/IInsuranceEquipmentPkg";
@ -23,6 +22,7 @@ import { SaveServer } from "@spt-aki/servers/SaveServer";
import { LocaleService } from "@spt-aki/services/LocaleService"; import { LocaleService } from "@spt-aki/services/LocaleService";
import { LocalisationService } from "@spt-aki/services/LocalisationService"; import { LocalisationService } from "@spt-aki/services/LocalisationService";
import { MailSendService } from "@spt-aki/services/MailSendService"; import { MailSendService } from "@spt-aki/services/MailSendService";
import { ICloner } from "@spt-aki/utils/cloners/ICloner";
import { HashUtil } from "@spt-aki/utils/HashUtil"; import { HashUtil } from "@spt-aki/utils/HashUtil";
import { JsonUtil } from "@spt-aki/utils/JsonUtil"; import { JsonUtil } from "@spt-aki/utils/JsonUtil";
import { RandomUtil } from "@spt-aki/utils/RandomUtil"; import { RandomUtil } from "@spt-aki/utils/RandomUtil";
@ -52,6 +52,7 @@ export class InsuranceService
@inject("LocaleService") protected localeService: LocaleService, @inject("LocaleService") protected localeService: LocaleService,
@inject("MailSendService") protected mailSendService: MailSendService, @inject("MailSendService") protected mailSendService: MailSendService,
@inject("ConfigServer") protected configServer: ConfigServer, @inject("ConfigServer") protected configServer: ConfigServer,
@inject("RecursiveCloner") protected cloner: ICloner,
) )
{ {
this.insuranceConfig = this.configServer.getConfig(ConfigTypes.INSURANCE); this.insuranceConfig = this.configServer.getConfig(ConfigTypes.INSURANCE);

View File

@ -21,6 +21,7 @@ import { ILogger } from "@spt-aki/models/spt/utils/ILogger";
import { ConfigServer } from "@spt-aki/servers/ConfigServer"; import { ConfigServer } from "@spt-aki/servers/ConfigServer";
import { DatabaseServer } from "@spt-aki/servers/DatabaseServer"; import { DatabaseServer } from "@spt-aki/servers/DatabaseServer";
import { LocalisationService } from "@spt-aki/services/LocalisationService"; import { LocalisationService } from "@spt-aki/services/LocalisationService";
import { ICloner } from "@spt-aki/utils/cloners/ICloner";
import { HashUtil } from "@spt-aki/utils/HashUtil"; import { HashUtil } from "@spt-aki/utils/HashUtil";
import { JsonUtil } from "@spt-aki/utils/JsonUtil"; import { JsonUtil } from "@spt-aki/utils/JsonUtil";
import { TimeUtil } from "@spt-aki/utils/TimeUtil"; import { TimeUtil } from "@spt-aki/utils/TimeUtil";
@ -46,6 +47,7 @@ export class ProfileFixerService
@inject("HashUtil") protected hashUtil: HashUtil, @inject("HashUtil") protected hashUtil: HashUtil,
@inject("DatabaseServer") protected databaseServer: DatabaseServer, @inject("DatabaseServer") protected databaseServer: DatabaseServer,
@inject("ConfigServer") protected configServer: ConfigServer, @inject("ConfigServer") protected configServer: ConfigServer,
@inject("RecursiveCloner") protected cloner: ICloner,
) )
{ {
this.coreConfig = this.configServer.getConfig(ConfigTypes.CORE); this.coreConfig = this.configServer.getConfig(ConfigTypes.CORE);
@ -1322,7 +1324,7 @@ export class ProfileFixerService
if ("OverallCounters" in fullProfile.characters.pmc.Stats) if ("OverallCounters" in fullProfile.characters.pmc.Stats)
{ {
this.logger.debug("Migrating stats object into new structure"); this.logger.debug("Migrating stats object into new structure");
const statsCopy = this.jsonUtil.clone(fullProfile.characters.pmc.Stats); const statsCopy = this.cloner.clone(fullProfile.characters.pmc.Stats);
// Clear stats object // Clear stats object
fullProfile.characters.pmc.Stats = { Eft: null }; fullProfile.characters.pmc.Stats = { Eft: null };
@ -1400,7 +1402,7 @@ export class ProfileFixerService
if ("Improvements" in pmcProfile.Hideout) if ("Improvements" in pmcProfile.Hideout)
{ {
const improvements = pmcProfile.Hideout.Improvements as Record<string, IHideoutImprovement>; const improvements = pmcProfile.Hideout.Improvements as Record<string, IHideoutImprovement>;
pmcProfile.Hideout.Improvement = this.jsonUtil.clone(improvements); pmcProfile.Hideout.Improvement = this.cloner.clone(improvements);
delete pmcProfile.Hideout.Improvements; delete pmcProfile.Hideout.Improvements;
this.logger.success("Successfully migrated hideout Improvements data to new location, deleted old data"); this.logger.success("Successfully migrated hideout Improvements data to new location, deleted old data");
} }

View File

@ -1,5 +1,6 @@
import { inject, injectable } from "tsyringe"; import { inject, injectable } from "tsyringe";
import { IAkiProfile } from "@spt-aki/models/eft/profile/IAkiProfile"; import { IAkiProfile } from "@spt-aki/models/eft/profile/IAkiProfile";
import { ICloner } from "@spt-aki/utils/cloners/ICloner";
import { JsonUtil } from "@spt-aki/utils/JsonUtil"; import { JsonUtil } from "@spt-aki/utils/JsonUtil";
@injectable() @injectable()
@ -7,7 +8,7 @@ export class ProfileSnapshotService
{ {
protected storedProfileSnapshots: Record<string, IAkiProfile> = {}; protected storedProfileSnapshots: Record<string, IAkiProfile> = {};
constructor(@inject("JsonUtil") protected jsonUtil: JsonUtil) constructor(@inject("JsonUtil") protected jsonUtil: JsonUtil, @inject("RecursiveCloner") protected cloner: ICloner)
{} {}
/** /**
@ -17,7 +18,7 @@ export class ProfileSnapshotService
*/ */
public storeProfileSnapshot(sessionID: string, profile: IAkiProfile): void public storeProfileSnapshot(sessionID: string, profile: IAkiProfile): void
{ {
this.storedProfileSnapshots[sessionID] = this.jsonUtil.clone(profile); this.storedProfileSnapshots[sessionID] = this.cloner.clone(profile);
} }
/** /**

View File

@ -4,6 +4,7 @@ import { QuestStatus } from "@spt-aki/models/enums/QuestStatus";
import { ITraderServiceModel } from "@spt-aki/models/spt/services/ITraderServiceModel"; import { ITraderServiceModel } from "@spt-aki/models/spt/services/ITraderServiceModel";
import { ILogger } from "@spt-aki/models/spt/utils/ILogger"; import { ILogger } from "@spt-aki/models/spt/utils/ILogger";
import { DatabaseServer } from "@spt-aki/servers/DatabaseServer"; import { DatabaseServer } from "@spt-aki/servers/DatabaseServer";
import { ICloner } from "@spt-aki/utils/cloners/ICloner";
import { JsonUtil } from "@spt-aki/utils/JsonUtil"; import { JsonUtil } from "@spt-aki/utils/JsonUtil";
@injectable() @injectable()
@ -14,13 +15,14 @@ export class TraderServicesService
@inject("JsonUtil") protected jsonUtil: JsonUtil, @inject("JsonUtil") protected jsonUtil: JsonUtil,
@inject("WinstonLogger") protected logger: ILogger, @inject("WinstonLogger") protected logger: ILogger,
@inject("DatabaseServer") protected databaseServer: DatabaseServer, @inject("DatabaseServer") protected databaseServer: DatabaseServer,
@inject("RecursiveCloner") protected cloner: ICloner,
) )
{} {}
public getTraderServices(sessionId: string, traderId: string): ITraderServiceModel[] public getTraderServices(sessionId: string, traderId: string): ITraderServiceModel[]
{ {
const pmcData = this.profileHelper.getPmcProfile(sessionId); const pmcData = this.profileHelper.getPmcProfile(sessionId);
let traderServices = this.jsonUtil.clone(this.databaseServer.getTables().traders[traderId]?.services); let traderServices = this.cloner.clone(this.databaseServer.getTables().traders[traderId]?.services);
if (!traderServices) if (!traderServices)
{ {
return []; return [];

View File

@ -12,6 +12,7 @@ import { IDatabaseTables } from "@spt-aki/models/spt/server/IDatabaseTables";
import { ILogger } from "@spt-aki/models/spt/utils/ILogger"; import { ILogger } from "@spt-aki/models/spt/utils/ILogger";
import { DatabaseServer } from "@spt-aki/servers/DatabaseServer"; import { DatabaseServer } from "@spt-aki/servers/DatabaseServer";
import { ItemBaseClassService } from "@spt-aki/services/ItemBaseClassService"; import { ItemBaseClassService } from "@spt-aki/services/ItemBaseClassService";
import { ICloner } from "@spt-aki/utils/cloners/ICloner";
import { HashUtil } from "@spt-aki/utils/HashUtil"; import { HashUtil } from "@spt-aki/utils/HashUtil";
import { JsonUtil } from "@spt-aki/utils/JsonUtil"; import { JsonUtil } from "@spt-aki/utils/JsonUtil";
@ -27,6 +28,7 @@ export class CustomItemService
@inject("DatabaseServer") protected databaseServer: DatabaseServer, @inject("DatabaseServer") protected databaseServer: DatabaseServer,
@inject("ItemHelper") protected itemHelper: ItemHelper, @inject("ItemHelper") protected itemHelper: ItemHelper,
@inject("ItemBaseClassService") protected itemBaseClassService: ItemBaseClassService, @inject("ItemBaseClassService") protected itemBaseClassService: ItemBaseClassService,
@inject("RecursiveCloner") protected cloner: ICloner,
) )
{ {
this.tables = this.databaseServer.getTables(); this.tables = this.databaseServer.getTables();
@ -61,7 +63,7 @@ export class CustomItemService
} }
// Clone existing item // Clone existing item
const itemClone = this.jsonUtil.clone(tables.templates.items[newItemDetails.itemTplToClone]); const itemClone = this.cloner.clone(tables.templates.items[newItemDetails.itemTplToClone]);
// Update id and parentId of item // Update id and parentId of item
itemClone._id = newItemId; itemClone._id = newItemId;

View File

@ -242,6 +242,7 @@ export class JsonUtil
* Convert into string and back into object to clone object * Convert into string and back into object to clone object
* @param objectToClone Item to clone * @param objectToClone Item to clone
* @returns Cloned parameter * @returns Cloned parameter
* @deprecated Use ICloner implementations, such as RecursiveCloner or StructuredCloner
*/ */
public clone<T>(objectToClone: T): T public clone<T>(objectToClone: T): T
{ {

View File

@ -1,5 +1,6 @@
import { inject, injectable } from "tsyringe"; import { inject, injectable } from "tsyringe";
import { ILogger } from "@spt-aki/models/spt/utils/ILogger"; import { ILogger } from "@spt-aki/models/spt/utils/ILogger";
import { ICloner } from "@spt-aki/utils/cloners/ICloner";
import { JsonUtil } from "@spt-aki/utils/JsonUtil"; import { JsonUtil } from "@spt-aki/utils/JsonUtil";
import { MathUtil } from "@spt-aki/utils/MathUtil"; import { MathUtil } from "@spt-aki/utils/MathUtil";
@ -20,7 +21,7 @@ import { MathUtil } from "@spt-aki/utils/MathUtil";
*/ */
export class ProbabilityObjectArray<K, V = undefined> extends Array<ProbabilityObject<K, V>> export class ProbabilityObjectArray<K, V = undefined> extends Array<ProbabilityObject<K, V>>
{ {
constructor(private mathUtil: MathUtil, private jsonUtil: JsonUtil, ...items: ProbabilityObject<K, V>[]) constructor(private mathUtil: MathUtil, private cloner: ICloner, ...items: ProbabilityObject<K, V>[])
{ {
super(); super();
this.push(...items); this.push(...items);
@ -30,7 +31,7 @@ export class ProbabilityObjectArray<K, V = undefined> extends Array<ProbabilityO
callbackfn: (value: ProbabilityObject<K, V>, index: number, array: ProbabilityObject<K, V>[]) => any, callbackfn: (value: ProbabilityObject<K, V>, index: number, array: ProbabilityObject<K, V>[]) => any,
): ProbabilityObjectArray<K, V> ): ProbabilityObjectArray<K, V>
{ {
return new ProbabilityObjectArray(this.mathUtil, this.jsonUtil, ...super.filter(callbackfn)); return new ProbabilityObjectArray(this.mathUtil, this.cloner, ...super.filter(callbackfn));
} }
/** /**
@ -52,8 +53,8 @@ export class ProbabilityObjectArray<K, V = undefined> extends Array<ProbabilityO
*/ */
clone(): ProbabilityObjectArray<K, V> clone(): ProbabilityObjectArray<K, V>
{ {
const clone = this.jsonUtil.clone(this); const clone = this.cloner.clone(this);
const probabliltyObjects = new ProbabilityObjectArray<K, V>(this.mathUtil, this.jsonUtil); const probabliltyObjects = new ProbabilityObjectArray<K, V>(this.mathUtil, this.cloner);
for (const ci of clone) for (const ci of clone)
{ {
probabliltyObjects.push(new ProbabilityObject(ci.key, ci.relativeProbability, ci.data)); probabliltyObjects.push(new ProbabilityObject(ci.key, ci.relativeProbability, ci.data));
@ -204,7 +205,7 @@ export class ProbabilityObject<K, V = undefined>
@injectable() @injectable()
export class RandomUtil export class RandomUtil
{ {
constructor(@inject("JsonUtil") protected jsonUtil: JsonUtil, @inject("WinstonLogger") protected logger: ILogger) constructor(@inject("RecursiveCloner") protected cloner: ICloner, @inject("WinstonLogger") protected logger: ILogger)
{ {
} }
@ -342,7 +343,7 @@ export class RandomUtil
let list = originalList; let list = originalList;
if (!replacement) if (!replacement)
{ {
list = this.jsonUtil.clone(originalList); list = this.cloner.clone(originalList);
} }
const results = []; const results = [];

View File

@ -0,0 +1,4 @@
export interface ICloner
{
clone<T>(obj: T): T
}

View File

@ -0,0 +1,11 @@
import { injectable } from "tsyringe";
import type { ICloner } from "@spt-aki/utils/cloners/ICloner";
@injectable()
export class JsonCloner implements ICloner
{
public clone<T>(obj: T): T
{
return JSON.parse(JSON.stringify(obj));
}
}

View File

@ -0,0 +1,45 @@
import { injectable } from "tsyringe";
import type { ICloner } from "@spt-aki/utils/cloners/ICloner";
@injectable()
export class RecursiveCloner implements ICloner
{
private static primitives = new Set<string>([
"string",
"number",
"boolean",
"bigint",
"symbol",
"undefined",
"null",
]);
public clone<T>(obj: T): T
{
const typeOfObj = typeof obj;
// no need to clone these types, they are primitives
if (RecursiveCloner.primitives.has(typeOfObj))
{
return obj;
}
// clone the object types
if (typeOfObj === "object")
{
if (Array.isArray(obj))
{
// biome-ignore lint/suspicious/noExplicitAny: used for clone
const objArr = obj as Array<any>;
return objArr.map(v => this.clone(v)) as T;
}
const newObj = {};
for (const propOf1 in obj)
{
newObj[propOf1.toString()] = this.clone(obj[propOf1]);
}
return newObj as T;
}
throw new Error(`Cant clone ${JSON.stringify(obj)}`);
}
}

View File

@ -0,0 +1,11 @@
import { injectable } from "tsyringe";
import type { ICloner } from "@spt-aki/utils/cloners/ICloner";
@injectable()
export class StructuredCloner implements ICloner
{
public clone<T>(obj: T): T
{
return structuredClone(obj);
}
}