Refactored botgen
This commit is contained in:
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+3068
-3000
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
Reference in New Issue
Block a user