Skip to content

Commit

Permalink
Refactor RaidTimeAdjustment to work as server authoritative
Browse files Browse the repository at this point in the history
- 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
  • Loading branch information
DrakiaXYZ committed Feb 17, 2025
1 parent 1324565 commit 5e4119f
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 45 deletions.
5 changes: 0 additions & 5 deletions project/src/controllers/GameController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down
9 changes: 0 additions & 9 deletions project/src/models/eft/game/IGetRaidTimeResponse.ts
Original file line number Diff line number Diff line change
@@ -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;
}
15 changes: 15 additions & 0 deletions project/src/models/spt/location/IRaidChanges.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
13 changes: 10 additions & 3 deletions project/src/services/LocationLifecycleService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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?
Expand Down Expand Up @@ -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<IRaidChanges>();
Expand Down Expand Up @@ -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;
Expand Down
2 changes: 0 additions & 2 deletions project/src/services/PostDbLoadService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,6 @@ export class PostDbLoadService {
this.removeExistingPmcWaves();
}

this.pmcWaveGenerator.applyWaveChangesToAllMaps();

if (this.locationConfig.addCustomBotWavesToMaps) {
this.customLocationWaveService.applyWaveChangesToAllMaps();
}
Expand Down
88 changes: 62 additions & 26 deletions project/src/services/RaidTimeAdjustmentService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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);
}
}
Expand Down Expand Up @@ -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,
};
Expand Down Expand Up @@ -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;
}

Expand Down

0 comments on commit 5e4119f

Please sign in to comment.