Refactored botgen

This commit is contained in:
Chomp
2025-02-17 13:07:32 +00:00
parent 7d7c8bdd31
commit 924fff686d
19 changed files with 7011 additions and 4808 deletions
+2 -2
View File
@@ -53,8 +53,8 @@ public class InraidCallbacks(
return _httpResponseUtil.NoBody(_inRaidController.GetTraitorScavHostileChance(url, sessionID));
}
public string GetBossConvertSettings(string url, EmptyRequestData info, string sessionID)
public string GetBossTypes(string url, EmptyRequestData info, string sessionID)
{
return _httpResponseUtil.NoBody(_inRaidController.GetBossConvertSettings(url, sessionID));
return _httpResponseUtil.NoBody(_inRaidController.GetBossTypes(url, sessionID));
}
}
+65 -145
View File
@@ -147,14 +147,31 @@ public class BotController(
{
var pmcProfile = _profileHelper.GetPmcProfile(sessionId);
// Use this opportunity to create and cache bots for later retrieval
var multipleBotTypesRequested = info.Conditions?.Count > 1;
return multipleBotTypesRequested
? GenerateMultipleBotsAndCache(info, pmcProfile, sessionId)
: ReturnSingleBotFromCache(sessionId, info);
// If we don't have enough bots cached to satisfy this request, populate the cache
if (!CacheSatisfiesRequest(info))
{
GenerateAndCacheBots(info, pmcProfile, sessionId);
}
return ReturnBotsFromCache(info);
}
private List<BotBase> GenerateMultipleBotsAndCache(GenerateBotsRequestData request, PmcData? pmcProfile, string sessionId)
/**
* Return true if the current cache satisfies the passed in bot generation request
*/
public bool CacheSatisfiesRequest(GenerateBotsRequestData info) {
return info.Conditions.All((condition) => {
// Create the key that would be used for caching this bot type, so we can check how many exist
var cacheKey = _botGenerationCacheService.CreateCacheKey(
condition.Role,
condition.Difficulty
);
return _botGenerationCacheService.GetCachedBotCount(cacheKey) >= condition.Limit;
});
}
private void GenerateAndCacheBots(GenerateBotsRequestData request, PmcData? pmcProfile, string sessionId)
{
var raidSettings = GetMostRecentRaidSettings();
@@ -162,38 +179,35 @@ public class BotController(
_pmcConfig.AllPMCsHavePlayerNameWithRandomPrefixChance
);
var stopwatch = Stopwatch.StartNew();
var tasks = new List<Task>();
// Map conditions to promises for bot generation
foreach (var condition in request.Conditions ?? [])
Task.WaitAll((request.Conditions ?? [])
.Select(condition => Task.Factory.StartNew(() =>
{
tasks.Add(
Task.Factory.StartNew(
() =>
{
var botGenerationDetails = GetBotGenerationDetailsForWave(
condition,
pmcProfile,
allPmcsHaveSameNameAsPlayer,
raidSettings,
_botConfig.PresetBatch!.GetValueOrDefault(condition.Role, 15),
_botHelper.IsBotPmc(condition.Role)
);
var cacheKey = _botGenerationCacheService.CreateCacheKey(condition.Role, condition.Difficulty);
// Generate bots for the current condition
GenerateWithBotDetails(condition, botGenerationDetails, sessionId);
}
)
);
}
if (_botGenerationCacheService.GetCachedBotCount(cacheKey) >= condition.Limit)
{
return;
}
var botGenerationDetails = GetBotGenerationDetailsForWave(
condition,
pmcProfile,
allPmcsHaveSameNameAsPlayer,
raidSettings,
_botConfig.PresetBatch!.GetValueOrDefault(condition.Role, condition.Limit),
_botHelper.IsBotPmc(condition.Role));
// Generate bots for the current condition
GenerateWithBotDetails(condition, botGenerationDetails, sessionId);
})).ToArray());
Task.WaitAll(tasks.ToArray());
stopwatch.Stop();
if (_logger.IsLogEnabled(LogLevel.Debug))
{
_logger.Debug($"Took {stopwatch.ElapsedMilliseconds}ms to GenerateMultipleBotsAndCache");
}
return [];
}
private void GenerateWithBotDetails(GenerateCondition condition, BotGenerationDetails botGenerationDetails, string sessionId)
@@ -259,102 +273,36 @@ public class BotController(
}
}
private List<BotBase> ReturnSingleBotFromCache(string sessionId, GenerateBotsRequestData request)
/// <summary>
/// Return requested bots by the given bot generation request
/// </summary>
/// <param name="request"></param>
/// <returns>An array of IBotBase objects as requested by request</returns>
private List<BotBase> ReturnBotsFromCache(GenerateBotsRequestData request)
{
var pmcProfile = _profileHelper.GetPmcProfile(sessionId);
var requestedBot = request.Conditions?.FirstOrDefault();
var result = new List<BotBase>();
var raidSettings = GetMostRecentRaidSettings();
// Create generation request for when cache is empty
var condition = new GenerateCondition
// We assume that during request we have enough bots cached to cover requirement
foreach (var condition in request.Conditions)
{
Role = requestedBot?.Role,
Limit = 5,
Difficulty = requestedBot?.Difficulty
};
var botGenerationDetails = GetBotGenerationDetailsForWave(
condition,
pmcProfile,
false,
raidSettings,
_botConfig.PresetBatch.GetValueOrDefault(requestedBot?.Role, 5),
_botHelper.IsBotPmc(requestedBot?.Role)
);
// Event bots need special actions to occur, set data up for them
var isEventBot = requestedBot?.Role?.ToLower().Contains("event");
if (isEventBot ?? false)
{
// Add eventRole data + reassign role property
botGenerationDetails.EventRole = requestedBot?.Role;
botGenerationDetails.Role = _seasonalEventService.GetBaseRoleForEventBot(
botGenerationDetails.EventRole
var cacheKey = _botGenerationCacheService.CreateCacheKey(
condition.Role,
condition.Difficulty
);
}
// Does non pmc bot have a chance of being converted into a pmc
var convertIntoPmcChanceMinMax = GetPmcConversionMinMaxForLocation(
requestedBot?.Role,
raidSettings?.Location
);
if (convertIntoPmcChanceMinMax is not null && !botGenerationDetails.IsPmc.GetValueOrDefault(false))
{
// Bot has % chance to become pmc and isn't one pmc already
var convertToPmc = _botHelper.RollChanceToBePmc(convertIntoPmcChanceMinMax);
if (convertToPmc)
// Fetch enough bots to satisfy the request
for (var i = 0; i < condition.Limit; i++)
{
// Update requirements
botGenerationDetails.IsPmc = true;
botGenerationDetails.Role = _botHelper.GetRandomizedPmcRole();
botGenerationDetails.Side = _botHelper.GetPmcSideByRole(botGenerationDetails.Role);
botGenerationDetails.BotDifficulty = GetPmcDifficulty(requestedBot?.Difficulty);
botGenerationDetails.BotCountToGenerate = _botConfig.PresetBatch?.GetValueOrDefault(botGenerationDetails.Role, 5);
var desiredBot = _botGenerationCacheService.GetBot(cacheKey);
// Store details for later use post-raid
_botGenerationCacheService.StoreUsedBot(desiredBot);
result.Add(desiredBot);
}
}
// Only convert to boss when not already converted to PMC & Boss Convert is enabled
var bossConvertEnabled = _botConfig.AssaultToBossConversion.BossConvertEnabled;
var bossConvertMinMax = _botConfig.AssaultToBossConversion.BossConvertMinMax;
var bossesToConvertToWeights = _botConfig.AssaultToBossConversion.BossesToConvertToWeights;
if (bossConvertEnabled && botGenerationDetails.IsPmc is not null && !botGenerationDetails.IsPmc.Value)
{
var bossConvertPercent = bossConvertMinMax.GetValueOrDefault(requestedBot?.Role?.ToLower());
if (bossConvertPercent is not null)
// Roll a percentage check if we should convert scav to boss
{
if (_randomUtil.GetChance100(_randomUtil.GetDouble(bossConvertPercent.Min, bossConvertPercent.Max)))
{
UpdateBotGenerationDetailsToRandomBoss(botGenerationDetails, bossesToConvertToWeights);
}
}
}
// Create a compound key to store bots in cache against
var cacheKey = _botGenerationCacheService.CreateCacheKey(
botGenerationDetails.EventRole ?? botGenerationDetails.Role,
botGenerationDetails.BotDifficulty
);
// Check cache for bot using above key
if (!_botGenerationCacheService.CacheHasBotWithKey(cacheKey))
{
// No bot in cache, generate new and store in cache
GenerateSingleBotAndStoreInCache(botGenerationDetails, sessionId, cacheKey);
if (_logger.IsLogEnabled(LogLevel.Debug))
{
_logger.Debug(
$"Generated {botGenerationDetails.BotCountToGenerate} " +
$"{botGenerationDetails.Role} ({botGenerationDetails.EventRole ?? ""}) {botGenerationDetails.BotDifficulty} bots"
);
}
}
var desiredBot = _botGenerationCacheService.GetBot(cacheKey);
_botGenerationCacheService.StoreUsedBot(desiredBot);
return [desiredBot];
return result;
}
private void GenerateSingleBotAndStoreInCache(BotGenerationDetails? botGenerationDetails, string sessionId, string cacheKey)
@@ -366,34 +314,6 @@ public class BotController(
_matchBotDetailsCacheService.CacheBot(botToCache);
}
private void UpdateBotGenerationDetailsToRandomBoss(BotGenerationDetails botGenerationDetails, Dictionary<string, double> bossesToConvertToWeights)
{
// Seems Actual bosses have the same Brain issues like PMC gaining Boss Brains We can't use all bosses
botGenerationDetails.Role = _weightedRandomHelper.GetWeightedValue(bossesToConvertToWeights);
// Bosses are only ever 'normal'
botGenerationDetails.BotDifficulty = "normal";
botGenerationDetails.BotCountToGenerate = _botConfig.PresetBatch?.GetValueOrDefault(botGenerationDetails.Role);
}
private string? GetPmcDifficulty(string? requestedBotDifficulty)
{
var difficulty = _pmcConfig.Difficulty.ToLower();
return difficulty switch
{
"asonline" => requestedBotDifficulty,
"random" => _botDifficultyHelper.ChooseRandomDifficulty(),
_ => _pmcConfig.Difficulty
};
}
private MinMax<double>? GetPmcConversionMinMaxForLocation(string? requestedBotRole, string? location)
{
return _pmcConfig.ConvertIntoPmcChance!.TryGetValue(location?.ToLower() ?? "", out var mapSpecificConversionValues)
? mapSpecificConversionValues.GetValueOrDefault(requestedBotRole?.ToLower())
: _pmcConfig.ConvertIntoPmcChance["default"]?.GetValueOrDefault(requestedBotRole);
}
private GetRaidConfigurationRequestData? GetMostRecentRaidSettings()
{
var raidSettings = _applicationContext
@@ -426,7 +346,7 @@ public class BotController(
IsPmc = generateAsPmc,
Side = generateAsPmc ? _botHelper.GetPmcSideByRole(condition.Role ?? string.Empty) : "Savage",
Role = condition.Role,
PlayerLevel = pmcProfile?.Info?.Level,
PlayerLevel = pmcProfile?.Info?.Level ?? 1,
PlayerName = pmcProfile?.Info?.Nickname,
BotRelativeLevelDeltaMax = _pmcConfig.BotRelativeLevelDeltaMax,
BotRelativeLevelDeltaMin = _pmcConfig.BotRelativeLevelDeltaMin,
@@ -286,11 +286,6 @@ public class GameController(
/// <returns></returns>
public GetRaidTimeResponse GetRaidTime(string sessionId, GetRaidTimeRequest request)
{
// Set interval times to in-raid value
_ragfairConfig.RunIntervalSeconds = _ragfairConfig.RunIntervalValues.InRaid;
_hideoutConfig.RunIntervalSeconds = _hideoutConfig.RunIntervalValues.InRaid;
return _raidTimeAdjustmentService.GetRaidAdjustments(sessionId, request);
}
@@ -67,12 +67,13 @@ public class InRaidController(
}
/// <summary>
/// Get all boss role types e.g. bossTagilla
/// </summary>
/// <param name="url"></param>
/// <param name="sessionId"></param>
/// <returns></returns>
public List<string> GetBossConvertSettings(string url, string sessionId)
/// <returns>string array of boss types</returns>
public List<string> GetBossTypes(string url, string sessionId)
{
return _botConfig.AssaultToBossConversion.BossesToConvertToWeights.Keys.ToList();
return _botConfig.Bosses;
}
}
+8 -1
View File
@@ -133,6 +133,13 @@ public class BotGenerator(
_logger.Error($"Unable to retrieve: {botRole} bot template, cannot generate bot of this type");
}
// The client expects the Side for PMCs to be Savage
if (botRole is "Bear" or "Usec")
{
// TODO: cleanup later
preparedBotBase.Info.Side = "Savage";
}
return GenerateBot(sessionId, preparedBotBase, botJsonTemplateClone, botGenerationDetails);
}
@@ -277,7 +284,7 @@ public class BotGenerator(
botJsonTemplate,
botRoleLowercase,
botGenerationDetails.IsPmc.GetValueOrDefault(false),
botLevel.Level.Value,
bot.Info.Level.Value,
bot.Info.GameVersion
);
@@ -0,0 +1,70 @@
using Core.Models.Eft.Common;
using Core.Models.Spt.Config;
using Core.Models.Utils;
using Core.Servers;
using Core.Services;
using Core.Utils;
using SptCommon.Annotations;
namespace Core.Generators
{
[Injectable]
public class PmcWaveGenerator
{
protected ISptLogger<PmcWaveGenerator> _logger;
protected RandomUtil _randomUtil;
protected DatabaseService _databaseService;
protected ConfigServer _configServer;
protected PmcConfig _pmcConfig;
public PmcWaveGenerator(
ISptLogger<PmcWaveGenerator> _logger,
RandomUtil _randomUtil,
DatabaseService _databaseService,
ConfigServer _configServer
)
{
this._logger = _logger;
this._randomUtil = _randomUtil;
this._databaseService = _databaseService;
this._configServer = _configServer;
_pmcConfig = _configServer.GetConfig<PmcConfig>();
}
public void AddPmcWaveToLocation(string locationId, BossLocationSpawn waveToAdd)
{
_pmcConfig.CustomPmcWaves[locationId].Add(waveToAdd);
}
/**
* Add custom boss and normal waves to maps found in config/location.json to db
*/
public void ApplyWaveChangesToAllMaps() {
foreach (var location in _pmcConfig.CustomPmcWaves) {
ApplyWaveChangesToMapByName(location.Key);
}
}
public void ApplyWaveChangesToMapByName(string name) {
if (!_pmcConfig.CustomPmcWaves.TryGetValue(name, out var pmcWavesToAdd)) {
return;
}
var location = _databaseService.GetLocation(name);
if (location is null) {
return;
}
location.Base.BossLocationSpawn.AddRange(pmcWavesToAdd);
}
public void ApplyWaveChangesToMap(LocationBase location) {
if (!_pmcConfig.CustomPmcWaves.TryGetValue(location.Id.ToLower(), out var pmcWavesToAdd))
{
return;
}
location.BossLocationSpawn.AddRange(pmcWavesToAdd);
}
}
}
+4 -20
View File
@@ -39,7 +39,7 @@ public class BotHelper(
}
/// <summary>
/// Is the passed in bot role a PMC (usec/bear/pmc)
/// Is the passed in bot role a PMC (USEC/Bear/PMC)
/// </summary>
/// <param name="botRole">bot role to check</param>
/// <returns>true if is pmc</returns>
@@ -50,17 +50,17 @@ public class BotHelper(
public bool IsBotBoss(string botRole)
{
return _botConfig.Bosses.Any(x => string.Equals(x, botRole, StringComparison.CurrentCultureIgnoreCase));
return !IsBotFollower(botRole) && _botConfig.Bosses.Any(x => string.Equals(x, botRole, StringComparison.CurrentCultureIgnoreCase));
}
public bool IsBotFollower(string botRole)
{
return botRole?.ToLower().StartsWith("follower") ?? false;
return botRole?.StartsWith("follower", StringComparison.CurrentCultureIgnoreCase) ?? false;
}
public bool IsBotZombie(string botRole)
{
return botRole?.ToLower().StartsWith("infected") ?? false;
return botRole?.StartsWith("infected", StringComparison.CurrentCultureIgnoreCase) ?? false;
}
/// <summary>
@@ -112,22 +112,6 @@ public class BotHelper(
}
}
public bool RollChanceToBePmc(MinMax<double> botConvertMinMax)
{
return _randomUtil.GetChance100(_randomUtil.GetDouble(botConvertMinMax.Min, botConvertMinMax.Max));
}
protected Dictionary<string, MinMax<double>> GetPmcConversionValuesForLocation(string location)
{
var result = _pmcConfig.ConvertIntoPmcChance[location.ToLower()];
if (result is null)
{
_pmcConfig.ConvertIntoPmcChance = new Dictionary<string, Dictionary<string, MinMax<double>>>();
}
return result;
}
/// <summary>
/// is the provided role a PMC, case-agnostic
/// </summary>
@@ -4,13 +4,6 @@ namespace Core.Models.Eft.Game;
public record GetRaidTimeResponse
{
[JsonPropertyName("RaidTimeMinutes")]
public double? RaidTimeMinutes
{
get;
set;
}
[JsonPropertyName("NewSurviveTimeSeconds")]
public double? NewSurviveTimeSeconds
{
@@ -24,42 +17,4 @@ public record GetRaidTimeResponse
get;
set;
}
[JsonPropertyName("ExitChanges")]
public List<ExtractChange>? ExitChanges
{
get;
set;
}
}
public record ExtractChange
{
[JsonPropertyName("Name")]
public string? Name
{
get;
set;
}
[JsonPropertyName("MinTime")]
public double? MinTime
{
get;
set;
}
[JsonPropertyName("MaxTime")]
public double? MaxTime
{
get;
set;
}
[JsonPropertyName("Chance")]
public double? Chance
{
get;
set;
}
}
@@ -206,13 +206,6 @@ public record BotConfig : BaseConfig
set;
}
[JsonPropertyName("assaultToBossConversion")]
public AssaultToBossConversion AssaultToBossConversion
{
get;
set;
}
/**
* Max length a bots name can be
*/
@@ -234,30 +227,6 @@ public record BotConfig : BaseConfig
}
}
public record AssaultToBossConversion
{
[JsonPropertyName("bossConvertEnabled")]
public bool BossConvertEnabled
{
get;
set;
}
[JsonPropertyName("bossesToConvertToWeights")]
public Dictionary<string, double> BossesToConvertToWeights
{
get;
set;
}
[JsonPropertyName("bossConvertMinMax")]
public Dictionary<string, MinMax<double>> BossConvertMinMax
{
get;
set;
}
}
/**
* Number of bots to generate and store in cache on raid start per bot type
*/
+14 -10
View File
@@ -196,16 +196,6 @@ public record PmcConfig : BaseConfig
set;
}
/**
* Percentage chance a bot from a wave is converted into a PMC, first key = map, second key = bot wildspawn type (assault/exusec), value: min+max chance to be converted
*/
[JsonPropertyName("convertIntoPmcChance")]
public Dictionary<string, Dictionary<string, MinMax<double>>> ConvertIntoPmcChance
{
get;
set;
}
/**
* How many levels above player level can a PMC be
*/
@@ -280,6 +270,20 @@ public record PmcConfig : BaseConfig
get;
set;
}
[JsonPropertyName("removeExistingPmcWaves")]
public bool? RemoveExistingPmcWaves
{
get;
set;
}
[JsonPropertyName("customPmcWaves")]
public Dictionary<string, List<BossLocationSpawn>> CustomPmcWaves
{
get;
set;
}
}
public record HostilitySettings
@@ -24,4 +24,67 @@ public record RaidChanges
get;
set;
}
/** How many minutes are in the raid total */
[JsonPropertyName("RaidTimeMinutes")]
public double? RaidTimeMinutes
{
get;
set;
}
/** The new number of seconds required to avoid a run through */
[JsonPropertyName("NewSurviveTimeSeconds")]
public double? NewSurviveTimeSeconds
{
get;
set;
}
/** The original number of seconds required to avoid a run through */
[JsonPropertyName("OriginalSurvivalTimeSeconds")]
public double? OriginalSurvivalTimeSeconds
{
get;
set;
}
/** Any changes required to the extract list */
[JsonPropertyName("ExitChanges")]
public List<ExtractChange>? ExitChanges
{
get;
set;
}
}
public record ExtractChange
{
[JsonPropertyName("Name")]
public string? Name
{
get;
set;
}
[JsonPropertyName("MinTime")]
public double? MinTime
{
get;
set;
}
[JsonPropertyName("MaxTime")]
public double? MaxTime
{
get;
set;
}
[JsonPropertyName("Chance")]
public double? Chance
{
get;
set;
}
}
+9 -2
View File
@@ -50,8 +50,15 @@ public class HttpRouter
wrapper.Output = wrapper.Output.Replace(sessionID, sessionID);
}
//var filepath = $"c:\\SharpServer\\{req.Path.ToString().Substring(1).Replace("/", ".")}.json";
//File.WriteAllText(filepath, wrapper.Output);
try
{
var filepath = $"c:\\SharpServer\\{req.Path.ToString().Substring(1).Replace("/", ".")}.json";
File.WriteAllText(filepath, wrapper.Output);
}
catch (Exception e)
{
Console.WriteLine(e);
}
return wrapper.Output;
}
@@ -48,7 +48,7 @@ public class InraidStaticRouter : StaticRouter
info,
sessionID,
output
) => inraidCallbacks.GetBossConvertSettings(url, info as EmptyRequestData, sessionID)
) => inraidCallbacks.GetBossTypes(url, info as EmptyRequestData, sessionID)
)
]
)
@@ -21,39 +21,40 @@ namespace Core.Services;
[Injectable(InjectionType.Singleton)]
public class LocationLifecycleService
{
private readonly ApplicationContext _applicationContext;
private readonly BotGenerationCacheService _botGenerationCacheService;
private readonly BotLootCacheService _botLootCacheService;
private readonly BotNameService _botNameService;
private readonly ICloner _cloner;
private readonly ConfigServer _configServer;
private readonly DatabaseService _databaseService;
private readonly HashUtil _hashUtil;
private readonly HealthHelper _healthHelper;
private readonly HideoutConfig _hideoutConfig;
private readonly InRaidConfig _inRaidConfig;
private readonly InRaidHelper _inRaidHelper;
private readonly InsuranceService _insuranceService;
private readonly LocalisationService _localisationService;
private readonly LocationConfig _locationConfig;
private readonly LocationLootGenerator _locationLootGenerator;
private readonly ISptLogger<LocationLifecycleService> _logger;
private readonly LootGenerator _lootGenerator;
private readonly MailSendService _mailSendService;
private readonly MatchBotDetailsCacheService _matchBotDetailsCacheService;
private readonly PlayerScavGenerator _playerScavGenerator;
private readonly PmcChatResponseService _pmcChatResponseService;
private readonly PmcConfig _pmcConfig;
private readonly ProfileHelper _profileHelper;
private readonly QuestHelper _questHelper;
private readonly RagfairConfig _ragfairConfig;
private readonly RaidTimeAdjustmentService _raidTimeAdjustmentService;
private readonly RandomUtil _randomUtil;
private readonly RewardHelper _rewardHelper;
private readonly SaveServer _saveServer;
private readonly TimeUtil _timeUtil;
private readonly TraderConfig _traderConfig;
private readonly TraderHelper _traderHelper;
protected ApplicationContext _applicationContext;
protected BotGenerationCacheService _botGenerationCacheService;
protected BotLootCacheService _botLootCacheService;
protected BotNameService _botNameService;
protected ICloner _cloner;
protected ConfigServer _configServer;
protected DatabaseService _databaseService;
protected HashUtil _hashUtil;
protected HealthHelper _healthHelper;
protected HideoutConfig _hideoutConfig;
protected InRaidConfig _inRaidConfig;
protected InRaidHelper _inRaidHelper;
protected InsuranceService _insuranceService;
protected LocalisationService _localisationService;
protected LocationConfig _locationConfig;
protected LocationLootGenerator _locationLootGenerator;
protected ISptLogger<LocationLifecycleService> _logger;
protected LootGenerator _lootGenerator;
protected MailSendService _mailSendService;
protected MatchBotDetailsCacheService _matchBotDetailsCacheService;
protected PlayerScavGenerator _playerScavGenerator;
protected PmcChatResponseService _pmcChatResponseService;
protected PmcWaveGenerator _pmcWaveGenerator;
protected PmcConfig _pmcConfig;
protected ProfileHelper _profileHelper;
protected QuestHelper _questHelper;
protected RagfairConfig _ragfairConfig;
protected RaidTimeAdjustmentService _raidTimeAdjustmentService;
protected RandomUtil _randomUtil;
protected RewardHelper _rewardHelper;
protected SaveServer _saveServer;
protected TimeUtil _timeUtil;
protected TraderConfig _traderConfig;
protected TraderHelper _traderHelper;
public LocationLifecycleService(
ISptLogger<LocationLifecycleService> logger,
@@ -80,6 +81,7 @@ public class LocationLifecycleService
SaveServer saveServer,
HealthHelper healthHelper,
PmcChatResponseService pmcChatResponseService,
PmcWaveGenerator pmcWaveGenerator,
QuestHelper questHelper,
InsuranceService insuranceService,
MatchBotDetailsCacheService matchBotDetailsCacheService
@@ -109,6 +111,7 @@ public class LocationLifecycleService
_saveServer = saveServer;
_healthHelper = healthHelper;
_pmcChatResponseService = pmcChatResponseService;
_pmcWaveGenerator = pmcWaveGenerator;
_questHelper = questHelper;
_insuranceService = insuranceService;
_matchBotDetailsCacheService = matchBotDetailsCacheService;
@@ -130,6 +133,10 @@ public class LocationLifecycleService
var playerProfile = _profileHelper.GetPmcProfile(sessionId);
// Raid is starting, adjust run times to reduce server load while player is in raid
_ragfairConfig.RunIntervalSeconds = _ragfairConfig.RunIntervalValues.InRaid;
_hideoutConfig.RunIntervalSeconds = _hideoutConfig.RunIntervalValues.InRaid;
var result = new StartLocalRaidResponseData
{
ServerId = $"{request.Location}.{request.PlayerSide} {_timeUtil.GetTimeStamp()}", // TODO - does this need to be more verbose - investigate client?
@@ -347,7 +354,10 @@ public class LocationLifecycleService
return locationBaseClone;
}
// Check for a loot multipler adjustment in app context and apply if one is found
// Add cusom pmcs to map every time its run
_pmcWaveGenerator.ApplyWaveChangesToMap(locationBaseClone);
// Adjust raid based on whether this is a scav run
LocationConfig? locationConfigClone = null;
var raidAdjustments = _applicationContext
.GetLatestValue(ContextVariableType.RAID_ADJUSTMENTS)
@@ -385,7 +395,7 @@ public class LocationLifecycleService
_logger.Success(_localisationService.GetText("location-generated_success", name));
// Reset loot multipliers back to original values
if (raidAdjustments is not null)
if (raidAdjustments is not null && locationConfigClone is not null)
{
_logger.Debug("Resetting loot multipliers back to their original values");
_locationConfig.StaticLootMultiplier = locationConfigClone.StaticLootMultiplier;
+24 -2
View File
@@ -1,5 +1,4 @@
using Core.Models.Eft.Common;
using Core.Models.Eft.Common.Tables;
using Core.Models.Enums;
using Core.Models.Spt.Config;
using Core.Models.Utils;
@@ -31,6 +30,7 @@ public class PostDbLoadService(
protected LocationConfig _locationConfig = _configServer.GetConfig<LocationConfig>();
protected LootConfig _lootConfig = _configServer.GetConfig<LootConfig>();
protected RagfairConfig _ragfairConfig = _configServer.GetConfig<RagfairConfig>();
protected PmcConfig _pmcConfig = _configServer.GetConfig<PmcConfig>();
public void PerformPostDbLoadActions()
{
@@ -62,6 +62,11 @@ public class PostDbLoadService(
_openZoneService.ApplyZoneChangesToAllMaps();
}
if (_pmcConfig.RemoveExistingPmcWaves.GetValueOrDefault(false))
{
RemoveExistingPmcWaves();
}
if (_locationConfig.AddCustomBotWavesToMaps)
{
_customLocationWaveService.ApplyWaveChangesToAllMaps();
@@ -246,7 +251,24 @@ public class PostDbLoadService(
}
}
// Apply custom limits on bot types as defined in configs/location.json/botTypeLimits
protected void RemoveExistingPmcWaves()
{
var locations = _databaseService.GetLocations().GetDictionary();
var pmcTypes = new HashSet<string> { "pmcUSEC", "pmcBEAR" };
foreach (var locationkvP in locations)
{
if (locationkvP.Value?.Base?.BossLocationSpawn is null)
{
continue;
}
locationkvP.Value.Base.BossLocationSpawn = locationkvP.Value.Base.BossLocationSpawn.Where(
(bossSpawn) => !pmcTypes.Contains(bossSpawn.BossName)).ToList();
}
}
// Apply custom limits on bot types as defined in configs/location.json/botTypeLimits
protected void AdjustMapBotLimits()
{
var mapsDb = _databaseService.GetLocations().GetDictionary();
@@ -32,17 +32,56 @@ public class RaidTimeAdjustmentService(
/// <param name="mapBase">Map to adjust</param>
public void MakeAdjustmentsToMap(RaidChanges raidAdjustments, LocationBase mapBase)
{
_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)
{
_logger.Debug(
$"Adjusting dynamic loot multipliers to {raidAdjustments.DynamicLootPercent}% and static loot multipliers to {raidAdjustments.StaticLootPercent}% of original"
);
}
// Change loot multiplier values before they're used below
AdjustLootMultipliers(_locationConfig.LooseLootMultiplier, raidAdjustments.DynamicLootPercent);
AdjustLootMultipliers(_locationConfig.StaticLootMultiplier, raidAdjustments.StaticLootPercent);
if (raidAdjustments.DynamicLootPercent < 100)
{
AdjustLootMultipliers(_locationConfig.LooseLootMultiplier, raidAdjustments.DynamicLootPercent);
}
if (raidAdjustments.StaticLootPercent < 100)
{
AdjustLootMultipliers(_locationConfig.StaticLootMultiplier, raidAdjustments.StaticLootPercent);
}
// Adjust the escape time limit
mapBase.EscapeTimeLimit = raidAdjustments.RaidTimeMinutes;
// Adjust map exits
foreach (var exitChange in raidAdjustments.ExitChanges)
{
var exitToChange = mapBase.Exits.FirstOrDefault(exit => exit.Name == exitChange.Name);
if (exitToChange is null)
{
_logger.Debug($"Exit with Id: { exitChange.Name} not found, skipping");
return;
}
if (exitChange.Chance is not null)
{
exitToChange.Chance = exitChange.Chance;
}
if (exitChange.MinTime is not null)
{
exitToChange.MinTime = exitChange.MinTime;
}
if (exitChange.MaxTime is not null)
{
exitToChange.MaxTime = exitChange.MaxTime;
}
}
// Make alterations to bot spawn waves now player is simulated spawning later
var mapSettings = GetMapSettings(mapBase.Id);
if (mapSettings.AdjustWaves)
// Make alterations to bot spawn waves now player is simulated spawning later
{
AdjustWaves(mapBase, raidAdjustments);
}
@@ -101,8 +140,6 @@ public class RaidTimeAdjustmentService(
// Prep result object to return
var result = new GetRaidTimeResponse
{
RaidTimeMinutes = baseEscapeTimeMinutes,
ExitChanges = [],
NewSurviveTimeSeconds = null,
OriginalSurvivalTimeSeconds = globals.Configuration.Exp.MatchEnd.SurvivedSecondsRequirement
};
@@ -137,22 +174,25 @@ public class RaidTimeAdjustmentService(
// Time player spawns into the raid if it was online
var simulatedRaidStartTimeMinutes = baseEscapeTimeMinutes - newRaidTimeMinutes;
if (mapSettings.ReduceLootByPercent)
// Store time reduction percent in app context so loot gen can pick it up later
{
_applicationContext.AddValue(
ContextVariableType.RAID_ADJUSTMENTS,
new RaidChanges
{
DynamicLootPercent = Math.Max(raidTimeRemainingPercent, mapSettings.MinDynamicLootPercent),
StaticLootPercent = Math.Max(raidTimeRemainingPercent, mapSettings.MinStaticLootPercent),
SimulatedRaidStartSeconds = simulatedRaidStartTimeMinutes * 60
}
);
}
// Calculate how long player needs to be in raid to get a `survived` extract status
result.NewSurviveTimeSeconds = Math.Max(result.OriginalSurvivalTimeSeconds.Value - (baseEscapeTimeMinutes.Value - newRaidTimeMinutes) * 60, 0);
// Update result object with new time
result.RaidTimeMinutes = newRaidTimeMinutes;
// State that we'll pass to loot generation
var raidChanges = new RaidChanges {
DynamicLootPercent = 100,
StaticLootPercent = 100,
RaidTimeMinutes = newRaidTimeMinutes,
OriginalSurvivalTimeSeconds = result.OriginalSurvivalTimeSeconds,
ExitChanges = [],
NewSurviveTimeSeconds = result.NewSurviveTimeSeconds,
SimulatedRaidStartSeconds = 0 };
if (mapSettings.ReduceLootByPercent)
{
raidChanges.DynamicLootPercent = Math.Max(raidTimeRemainingPercent, mapSettings.MinDynamicLootPercent);
raidChanges.StaticLootPercent = Math.Max(raidTimeRemainingPercent, mapSettings.MinStaticLootPercent);
raidChanges.SimulatedRaidStartSeconds = simulatedRaidStartTimeMinutes * 60;
}
_logger.Debug($"Reduced: {request.Location} raid time by: {chosenRaidReductionPercent}% to {newRaidTimeMinutes} minutes");
@@ -165,9 +205,12 @@ public class RaidTimeAdjustmentService(
var exitAdjustments = GetExitAdjustments(mapBase, newRaidTimeMinutes);
if (exitAdjustments is not null)
{
result.ExitChanges.AddRange(exitAdjustments);
raidChanges.ExitChanges.AddRange(exitAdjustments);
}
// Store state to use in loot generation
_applicationContext.AddValue(ContextVariableType.RAID_ADJUSTMENTS, raidChanges);
return result;
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff