From 5e4119f3e41847753457127533bf42749fa24576 Mon Sep 17 00:00:00 2001 From: DrakiaXYZ <565558+TheDgtl@users.noreply.github.com> Date: Sun, 16 Feb 2025 18:29:01 -0800 Subject: [PATCH] Refactor RaidTimeAdjustment to work as server authoritative - Move ragfair and hideout `runIntervalSeconds` settings to `startLocalRaid` - Move pmcWaveGenerator to be run per-map instead of per session - Refactor RaidTimeAdjustmentService.makeAdjustmentsToMap to handle most of the stuff previously handled in the client patch - Change getRaidAdjustments to only return the survival time, and store all other data for use in loot generation --- project/src/controllers/GameController.ts | 5 -- .../models/eft/game/IGetRaidTimeResponse.ts | 9 -- .../src/models/spt/location/IRaidChanges.ts | 15 ++++ .../src/services/LocationLifecycleService.ts | 13 ++- project/src/services/PostDbLoadService.ts | 2 - .../src/services/RaidTimeAdjustmentService.ts | 88 +++++++++++++------ 6 files changed, 87 insertions(+), 45 deletions(-) diff --git a/project/src/controllers/GameController.ts b/project/src/controllers/GameController.ts index 4292061b3..2a6bc319f 100644 --- a/project/src/controllers/GameController.ts +++ b/project/src/controllers/GameController.ts @@ -542,11 +542,6 @@ export class GameController { * Handle singleplayer/settings/getRaidTime */ public getRaidTime(sessionId: string, request: IGetRaidTimeRequest): IGetRaidTimeResponse { - // Set interval times to in-raid value - this.ragfairConfig.runIntervalSeconds = this.ragfairConfig.runIntervalValues.inRaid; - - this.hideoutConfig.runIntervalSeconds = this.hideoutConfig.runIntervalValues.inRaid; - return this.raidTimeAdjustmentService.getRaidAdjustments(sessionId, request); } diff --git a/project/src/models/eft/game/IGetRaidTimeResponse.ts b/project/src/models/eft/game/IGetRaidTimeResponse.ts index ba97d4216..f0ae7ec7d 100644 --- a/project/src/models/eft/game/IGetRaidTimeResponse.ts +++ b/project/src/models/eft/game/IGetRaidTimeResponse.ts @@ -1,13 +1,4 @@ export interface IGetRaidTimeResponse { - RaidTimeMinutes: number; NewSurviveTimeSeconds?: number; OriginalSurvivalTimeSeconds: number; - ExitChanges: ExtractChange[]; -} - -export interface ExtractChange { - Name: string; - MinTime?: number; - MaxTime?: number; - Chance?: number; } diff --git a/project/src/models/spt/location/IRaidChanges.ts b/project/src/models/spt/location/IRaidChanges.ts index b7a989b65..33b32435a 100644 --- a/project/src/models/spt/location/IRaidChanges.ts +++ b/project/src/models/spt/location/IRaidChanges.ts @@ -5,4 +5,19 @@ export interface IRaidChanges { staticLootPercent: number; /** How many seconds into the raid is the player simulated to spawn in at */ simulatedRaidStartSeconds: number; + /** How many minutes are in the raid total */ + raidTimeMinutes: number; + /** The new number of seconds required to avoid a run through */ + newSurviveTimeSeconds?: number; + /** The original number of seconds required to avoid a run through */ + originalSurvivalTimeSeconds: number; + /** Any changes required to the extract list */ + exitChanges: ExtractChange[]; +} + +export interface ExtractChange { + Name: string; + MinTime?: number; + MaxTime?: number; + Chance?: number; } diff --git a/project/src/services/LocationLifecycleService.ts b/project/src/services/LocationLifecycleService.ts index 72654d433..b0d52c0dd 100644 --- a/project/src/services/LocationLifecycleService.ts +++ b/project/src/services/LocationLifecycleService.ts @@ -108,6 +108,10 @@ export class LocationLifecycleService { const playerProfile = this.profileHelper.getPmcProfile(sessionId); + // Set interval times to in-raid value + this.ragfairConfig.runIntervalSeconds = this.ragfairConfig.runIntervalValues.inRaid; + this.hideoutConfig.runIntervalSeconds = this.hideoutConfig.runIntervalValues.inRaid; + const result: IStartLocalRaidResponseData = { serverId: `${request.location}.${request.playerSide}.${this.timeUtil.getTimestamp()}`, // TODO - does this need to be more verbose - investigate client? serverSettings: this.databaseService.getLocationServices(), // TODO - is this per map or global? @@ -289,8 +293,11 @@ export class LocationLifecycleService { return locationBaseClone; } - // Check for a loot multipler adjustment in app context and apply if one is found - let locationConfigClone: ILocationConfig; + // Add cusom pmcs to map every time its run + this.pmcWaveGenerator.applyWaveChangesToMap(locationBaseClone); + + // Adjust raid based on whether this is a scav run + let locationConfigClone: ILocationConfig | undefined; const raidAdjustments = this.applicationContext .getLatestValue(ContextVariableType.RAID_ADJUSTMENTS) ?.getValue(); @@ -325,7 +332,7 @@ export class LocationLifecycleService { this.logger.success(this.localisationService.getText("location-generated_success", name)); // Reset loot multipliers back to original values - if (raidAdjustments) { + if (raidAdjustments && locationConfigClone) { this.logger.debug("Resetting loot multipliers back to their original values"); this.locationConfig.staticLootMultiplier = locationConfigClone.staticLootMultiplier; this.locationConfig.looseLootMultiplier = locationConfigClone.looseLootMultiplier; diff --git a/project/src/services/PostDbLoadService.ts b/project/src/services/PostDbLoadService.ts index e2a1100fb..f7dbc202e 100644 --- a/project/src/services/PostDbLoadService.ts +++ b/project/src/services/PostDbLoadService.ts @@ -89,8 +89,6 @@ export class PostDbLoadService { this.removeExistingPmcWaves(); } - this.pmcWaveGenerator.applyWaveChangesToAllMaps(); - if (this.locationConfig.addCustomBotWavesToMaps) { this.customLocationWaveService.applyWaveChangesToAllMaps(); } diff --git a/project/src/services/RaidTimeAdjustmentService.ts b/project/src/services/RaidTimeAdjustmentService.ts index 9688f0b01..931dffa28 100644 --- a/project/src/services/RaidTimeAdjustmentService.ts +++ b/project/src/services/RaidTimeAdjustmentService.ts @@ -3,14 +3,14 @@ import { ContextVariableType } from "@spt/context/ContextVariableType"; import { WeightedRandomHelper } from "@spt/helpers/WeightedRandomHelper"; import { ILocationBase } from "@spt/models/eft/common/ILocationBase"; import { IGetRaidTimeRequest } from "@spt/models/eft/game/IGetRaidTimeRequest"; -import { ExtractChange, IGetRaidTimeResponse } from "@spt/models/eft/game/IGetRaidTimeResponse"; +import { IGetRaidTimeResponse } from "@spt/models/eft/game/IGetRaidTimeResponse"; import { ConfigTypes } from "@spt/models/enums/ConfigTypes"; import { ILocationConfig, ILootMultiplier, IScavRaidTimeLocationSettings, } from "@spt/models/spt/config/ILocationConfig"; -import { IRaidChanges } from "@spt/models/spt/location/IRaidChanges"; +import { ExtractChange, IRaidChanges } from "@spt/models/spt/location/IRaidChanges"; import type { ILogger } from "@spt/models/spt/utils/ILogger"; import { ConfigServer } from "@spt/servers/ConfigServer"; import { DatabaseService } from "@spt/services/DatabaseService"; @@ -39,17 +39,47 @@ export class RaidTimeAdjustmentService { * @param mapBase Map to adjust */ public makeAdjustmentsToMap(raidAdjustments: IRaidChanges, mapBase: ILocationBase): void { - this.logger.debug( - `Adjusting dynamic loot multipliers to ${raidAdjustments.dynamicLootPercent}% and static loot multipliers to ${raidAdjustments.staticLootPercent}% of original`, - ); + if (raidAdjustments.dynamicLootPercent < 100 || raidAdjustments.staticLootPercent < 100) { + this.logger.debug( + `Adjusting dynamic loot multipliers to ${raidAdjustments.dynamicLootPercent}% and static loot multipliers to ${raidAdjustments.staticLootPercent}% of original`, + ); + } // Change loot multipler values before they're used below - this.adjustLootMultipliers(this.locationConfig.looseLootMultiplier, raidAdjustments.dynamicLootPercent); - this.adjustLootMultipliers(this.locationConfig.staticLootMultiplier, raidAdjustments.staticLootPercent); + if (raidAdjustments.dynamicLootPercent < 100) { + this.adjustLootMultipliers(this.locationConfig.looseLootMultiplier, raidAdjustments.dynamicLootPercent); + } + if (raidAdjustments.staticLootPercent < 100) { + this.adjustLootMultipliers(this.locationConfig.staticLootMultiplier, raidAdjustments.staticLootPercent); + } + + // Adjust the escape time limit + mapBase.EscapeTimeLimit = raidAdjustments.raidTimeMinutes; + + // Adjust map exits + raidAdjustments.exitChanges.forEach(exitChange => { + const exitToChange = mapBase.exits.find(exit => exit.Name === exitChange.Name); + if (!exitToChange) { + this.logger.debug(`Exit with Id: ${exitChange.Name} not found, skipping`); + return; + } + + if (typeof exitChange.Chance !== 'undefined') { + exitToChange.Chance = exitChange.Chance; + } + + if (typeof exitChange.MinTime !== 'undefined') { + exitToChange.MinTime = exitChange.MinTime; + } + + if (typeof exitChange.MaxTime !== 'undefined') { + exitToChange.MaxTime = exitChange.MaxTime; + } + }); + // Make alterations to bot spawn waves now player is simulated spawning later const mapSettings = this.getMapSettings(mapBase.Id); if (mapSettings.adjustWaves) { - // Make alterations to bot spawn waves now player is simulated spawning later this.adjustWaves(mapBase, raidAdjustments); } } @@ -102,8 +132,6 @@ export class RaidTimeAdjustmentService { // Prep result object to return const result: IGetRaidTimeResponse = { - RaidTimeMinutes: baseEscapeTimeMinutes, - ExitChanges: [], NewSurviveTimeSeconds: undefined, OriginalSurvivalTimeSeconds: globals.config.exp.match_end.survived_seconds_requirement, }; @@ -136,33 +164,41 @@ export class RaidTimeAdjustmentService { // Time player spawns into the raid if it was online const simulatedRaidStartTimeMinutes = baseEscapeTimeMinutes - newRaidTimeMinutes; + // Calculate how long player needs to be in raid to get a `survived` extract status + result.NewSurviveTimeSeconds = Math.max( + result.OriginalSurvivalTimeSeconds - (baseEscapeTimeMinutes - newRaidTimeMinutes) * 60, + 0, + ); + + // State that we'll pass to loot generation + const raidChanges: IRaidChanges = { + dynamicLootPercent: 100, + staticLootPercent: 100, + raidTimeMinutes: newRaidTimeMinutes, + originalSurvivalTimeSeconds: result.OriginalSurvivalTimeSeconds, + exitChanges: [], + newSurviveTimeSeconds: result.NewSurviveTimeSeconds, + simulatedRaidStartSeconds: 0 + }; + if (mapSettings.reduceLootByPercent) { - // Store time reduction percent in app context so loot gen can pick it up later - this.applicationContext.addValue(ContextVariableType.RAID_ADJUSTMENTS, { - dynamicLootPercent: Math.max(raidTimeRemainingPercent, mapSettings.minDynamicLootPercent), - staticLootPercent: Math.max(raidTimeRemainingPercent, mapSettings.minStaticLootPercent), - simulatedRaidStartSeconds: simulatedRaidStartTimeMinutes * 60, - }); + raidChanges.dynamicLootPercent = Math.max(raidTimeRemainingPercent, mapSettings.minDynamicLootPercent); + raidChanges.staticLootPercent = Math.max(raidTimeRemainingPercent, mapSettings.minStaticLootPercent); + raidChanges.simulatedRaidStartSeconds = simulatedRaidStartTimeMinutes * 60; } - // Update result object with new time - result.RaidTimeMinutes = newRaidTimeMinutes; - this.logger.debug( `Reduced: ${request.Location} raid time by: ${chosenRaidReductionPercent}% to ${newRaidTimeMinutes} minutes`, ); - // Calculate how long player needs to be in raid to get a `survived` extract status - result.NewSurviveTimeSeconds = Math.max( - result.OriginalSurvivalTimeSeconds - (baseEscapeTimeMinutes - newRaidTimeMinutes) * 60, - 0, - ); - const exitAdjustments = this.getExitAdjustments(mapBase, newRaidTimeMinutes); if (exitAdjustments) { - result.ExitChanges.push(...exitAdjustments); + raidChanges.exitChanges.push(...exitAdjustments); } + // Store state to use in loot generation + this.applicationContext.addValue(ContextVariableType.RAID_ADJUSTMENTS, raidChanges); + return result; }