Persist durability changes + faceshield hit values incurred during a raid (!110)

Fixes insured items comin back full durability

Co-authored-by: CWX <CWX@noreply.dev.sp-tarkov.com>
Co-authored-by: Dev <dev@dev.sp-tarkov.com>
Reviewed-on: https://dev.sp-tarkov.com/SPT-AKI/Server/pulls/110
This commit is contained in:
chomp 2023-08-05 17:26:16 +00:00
parent 9dd2d3cd04
commit a962823192
4 changed files with 161 additions and 65 deletions

View File

@ -341,6 +341,7 @@ export interface BackendCounter
export interface InsuredItem
{
/** Trader Id item was insured by */
tid: string
itemId: string
}

View File

@ -0,0 +1,7 @@
export interface IInsuredItemsData
{
id: string
durability?: number
maxDurability?: number
hits?: number
}

View File

@ -1,6 +1,7 @@
import { PlayerRaidEndState } from "../../../models/enums/PlayerRaidEndState";
import { IPmcData } from "../common/IPmcData";
import { ISyncHealthRequestData } from "../health/ISyncHealthRequestData";
import { IInsuredItemsData } from "./IInsuredItemsData";
export interface ISaveProgressRequestData
{
@ -8,4 +9,5 @@ export interface ISaveProgressRequestData
profile: IPmcData
isPlayerScav: boolean
health: ISyncHealthRequestData
insurance: IInsuredItemsData[]
}

View File

@ -8,8 +8,8 @@ import { NotificationSendHelper } from "../helpers/NotificationSendHelper";
import { SecureContainerHelper } from "../helpers/SecureContainerHelper";
import { TraderHelper } from "../helpers/TraderHelper";
import { IPmcData } from "../models/eft/common/IPmcData";
import { InsuredItem } from "../models/eft/common/tables/IBotBase";
import { Item } from "../models/eft/common/tables/IItem";
import { IInsuredItemsData } from "../models/eft/inRaid/IInsuredItemsData";
import { ISaveProgressRequestData } from "../models/eft/inRaid/ISaveProgressRequestData";
import { IUserDialogInfo } from "../models/eft/profile/IAkiProfile";
import { ConfigTypes } from "../models/enums/ConfigTypes";
@ -21,6 +21,7 @@ import { ILogger } from "../models/spt/utils/ILogger";
import { ConfigServer } from "../servers/ConfigServer";
import { DatabaseServer } from "../servers/DatabaseServer";
import { SaveServer } from "../servers/SaveServer";
import { JsonUtil } from "../utils/JsonUtil";
import { RandomUtil } from "../utils/RandomUtil";
import { TimeUtil } from "../utils/TimeUtil";
import { LocaleService } from "./LocaleService";
@ -38,6 +39,7 @@ export class InsuranceService
@inject("SecureContainerHelper") protected secureContainerHelper: SecureContainerHelper,
@inject("RandomUtil") protected randomUtil: RandomUtil,
@inject("ItemHelper") protected itemHelper: ItemHelper,
@inject("JsonUtil") protected jsonUtil: JsonUtil,
@inject("TimeUtil") protected timeUtil: TimeUtil,
@inject("SaveServer") protected saveServer: SaveServer,
@inject("TraderHelper") protected traderHelper: TraderHelper,
@ -52,6 +54,11 @@ export class InsuranceService
this.insuranceConfig = this.configServer.getConfig(ConfigTypes.INSURANCE);
}
/**
* Does player have insurance array
* @param sessionId Player id
* @returns True if exists
*/
public insuranceExists(sessionId: string): boolean
{
return this.insured[sessionId] !== undefined;
@ -203,7 +210,7 @@ export class InsuranceService
}
/**
* Store lost gear post-raid inside profile
* Store lost gear post-raid inside profile, ready for later code to pick it up and mail it
* @param pmcData player profile to store gear in
* @param offraidData post-raid request object
* @param preRaidGear gear player wore prior to raid
@ -218,27 +225,136 @@ export class InsuranceService
const equipmentToSendToPlayer = [];
for (const insuredItem of pmcData.InsuredItems)
{
// Check insured item was on player during raid
if (preRaidGearHash[insuredItem.itemId])
// Skip insured items not on player when they started raid
const preRaidItem = preRaidGearHash[insuredItem.itemId];
if (!preRaidItem)
{
// This item exists in preRaidGear, meaning we brought it into the raid
// Check if item missing OR player died with item on
if (!offRaidGearHash[insuredItem.itemId] || playerDied)
{
equipmentToSendToPlayer.push({
"pmcData": pmcData,
"insuredItem": insuredItem,
"item": preRaidGearHash[insuredItem.itemId],
"sessionID": sessionID
});
}
continue;
}
// Skip items we should never return
if (this.insuranceConfig.blacklistedEquipment.includes(preRaidItem.slotId))
{
continue;
}
// Check if item missing in post-raid gear OR player died
// Catches both events: player died with item on + player survied but dropped item in raid
if (!offRaidGearHash[insuredItem.itemId] || playerDied)
{
equipmentToSendToPlayer.push({
pmcData: pmcData,
itemToReturnToPlayer: this.getInsuredItemDetails(pmcData, preRaidItem, offraidData.insurance.find(x => x.id === insuredItem.itemId)),
traderId: insuredItem.tid,
sessionID: sessionID
});
}
}
// Process all insured items lost in-raid
for (const gear of equipmentToSendToPlayer)
{
this.addGearToSend(gear.pmcData, gear.insuredItem, gear.item, gear.sessionID);
this.addGearToSend(gear);
}
}
/**
* Take preraid item and update properties to ensure its ready to be given to player in insurance return mail
* @param pmcData Player profile
* @param insuredItem Insured items properties
* @param preRaidItem Insured item as it was pre-raid
* @param insuredItemFromClient Item data when player left raid (durability values)
* @returns Item object
*/
protected getInsuredItemDetails(pmcData: IPmcData, preRaidItem: Item, insuredItemFromClient: IInsuredItemsData): Item
{
// Get baseline item to return, clone pre raid item
const itemToReturn: Item = this.jsonUtil.clone(preRaidItem);
// Add upd if it doesnt exist
if (!itemToReturn.upd)
{
itemToReturn.upd = {};
}
// Check for slotid values that need to be updated and adjust
this.updateSlotIdValue(pmcData.Inventory.equipment, itemToReturn);
// Remove location property
if (itemToReturn.slotId === "hideout" && "location" in itemToReturn)
{
delete itemToReturn.location;
}
// Remove found in raid status when upd exists + SpawnedInSession value exists
if ("upd" in itemToReturn && "SpawnedInSession" in itemToReturn.upd)
{
itemToReturn.upd.SpawnedInSession = false;
}
// Client item has durability values, Ensure values persist into server data
if (insuredItemFromClient.durability)
{
// Item didnt have Repairable object pre-raid, add it
if (!itemToReturn.upd.Repairable)
{
itemToReturn.upd.Repairable = {
Durability: insuredItemFromClient.durability,
MaxDurability: insuredItemFromClient.maxDurability
};
}
else
{
itemToReturn.upd.Repairable.Durability = insuredItemFromClient.durability;
itemToReturn.upd.Repairable.MaxDurability = insuredItemFromClient.maxDurability;
}
}
// Client item has FaceShield values, Ensure values persist into server data
if (insuredItemFromClient?.hits)
{
// Item didnt have faceshield object pre-raid, add it
if (!itemToReturn.upd.FaceShield)
{
itemToReturn.upd.FaceShield = {
Hits: insuredItemFromClient.hits
};
}
else
{
itemToReturn.upd.FaceShield.Hits = insuredItemFromClient.hits;
}
}
return itemToReturn;
}
/**
* Reset slotId property to "hideout" when necessary (used to be in )
* @param pmcData Players pmcData.Inventory.equipment value
* @param itemToReturn item we will send to player as insurance return
*/
protected updateSlotIdValue(playerBaseInventoryEquipmentId: string, itemToReturn: Item): void
{
const pocketSlots = [
"pocket1",
"pocket2",
"pocket3",
"pocket4"
];
// Some pockets can lose items with player death, some don't
if (!("slotId" in itemToReturn) || pocketSlots.includes(itemToReturn.slotId))
{
itemToReturn.slotId = "hideout";
}
// Mark root-level items for later processing
if (itemToReturn.parentId === playerBaseInventoryEquipmentId)
{
itemToReturn.slotId = "hideout";
}
}
@ -260,67 +376,36 @@ export class InsuranceService
/**
* Add gear item to InsuredItems array in player profile
* @param pmcData profile to store item in
* @param insuredItem Item to store in profile
* @param actualItem item to store
* @param sessionID Session id
* @param pmcData Player profile
* @param itemToReturnToPlayer item to store
* @param traderId Id of trader item was insured with
*/
protected addGearToSend(pmcData: IPmcData, insuredItem: InsuredItem, actualItem: Item, sessionID: string): void
protected addGearToSend(gear: { sessionID: string; pmcData: IPmcData; itemToReturnToPlayer: Item; traderId: string}): void
{
// Skip items defined in config
if (this.insuranceConfig.blacklistedEquipment.includes(actualItem.slotId))
const sessionId = gear.sessionID;
const pmcData = gear.pmcData;
const itemToReturnToPlayer = gear.itemToReturnToPlayer;
const traderId = gear.traderId;
// Ensure insurance array is init
if (!this.insuranceExists(sessionId))
{
return;
this.resetInsurance(sessionId);
}
const pocketSlots = [
"pocket1",
"pocket2",
"pocket3",
"pocket4"
];
// Check and correct the validity of the slotId
if (!("slotId" in actualItem) || pocketSlots.includes(actualItem.slotId))
// init trader insurance array
if (!this.insuranceTraderArrayExists(sessionId, traderId))
{
actualItem.slotId = "hideout";
this.resetInsuranceTraderArray(sessionId, traderId);
}
// Mark root-level items for later
if (actualItem.parentId === pmcData.Inventory.equipment)
{
actualItem.slotId = "hideout";
}
this.addInsuranceItemToArray(sessionId, traderId, itemToReturnToPlayer);
// Clear the location attribute of the item in the container.
if (actualItem.slotId === "hideout" && "location" in actualItem)
{
delete actualItem.location;
}
// Remove found in raid status
if ("upd" in actualItem && "SpawnedInSession" in actualItem.upd)
{
actualItem.upd.SpawnedInSession = false;
}
// Mark to add to insurance
if (!this.insuranceExists(sessionID))
{
this.resetInsurance(sessionID);
}
if (!this.insuranceTraderArrayExists(sessionID, insuredItem.tid))
{
this.resetInsuranceTraderArray(sessionID, insuredItem.tid);
}
this.addInsuranceItemToArray(sessionID, insuredItem.tid, actualItem);
// Remove processed item from array
// Remove item from insured items array as its been processed
pmcData.InsuredItems = pmcData.InsuredItems.filter((item) =>
{
return item.itemId !== insuredItem.itemId;
return item.itemId !== itemToReturnToPlayer._id;
});
}
@ -353,6 +438,7 @@ export class InsuranceService
*/
public addInsuranceItemToArray(sessionId: string, traderId: string, itemToAdd: Item): void
{
this.insured[sessionId][traderId].push(itemToAdd);
}