Controller house keeping (#532)

* Cleanup BotController.cs

* More controller cleanup

* More dialogue changes
This commit is contained in:
Cj
2025-08-05 11:24:59 -04:00
committed by GitHub
parent c3b36f4c7d
commit 529fe61f23
15 changed files with 189 additions and 146 deletions
@@ -75,6 +75,7 @@
"dialog-chatbot_id_already_exists": "Chat bot: %s being registered already exists, unable to register bot",
"dialog-missing_item_template": "Unable to find item tpl {{tpl}} in db, cannot send message of type {{type}}, skipping",
"dialogue-unable_to_find_dialogs_in_profile": "No dialog object in profile: {{sessionId}}",
"dialogue-list_from_client_empty": "No dialog object sent from client: {{sessionId}}",
"dialogue-unable_to_find_in_profile": "No dialog in profile: {{sessionId}} found with id: {{dialogueId}}",
"event-unhandled_event": "[UNHANDLED EVENT] %s",
"executing_startup_callbacks": "Server: executing startup callbacks...",
@@ -52,7 +52,7 @@ public class CustomizationCallbacks(
/// <returns></returns>
public ValueTask<string> GetHideoutCustomisation(string url, EmptyRequestData _, MongoId sessionID)
{
return new ValueTask<string>(httpResponseUtil.GetBody(customizationController.GetHideoutCustomisation(sessionID)));
return new ValueTask<string>(httpResponseUtil.GetBody(customizationController.GetHideoutCustomisation()));
}
/// <summary>
@@ -37,7 +37,7 @@ public class DialogueCallbacks(TimeUtil timeUtil, HttpResponseUtil httpResponseU
{
new()
{
Id = new Models.Common.MongoId(),
Id = new MongoId(),
RegistrationId = 20,
DateTime = timeUtil.GetTimeStamp(),
IsDeveloper = true,
@@ -80,7 +80,9 @@ public class DialogueCallbacks(TimeUtil timeUtil, HttpResponseUtil httpResponseU
/// <returns></returns>
public virtual ValueTask<string> GetMailDialogInfo(string url, GetMailDialogInfoRequestData request, MongoId sessionID)
{
return new ValueTask<string>(httpResponseUtil.GetBody(dialogueController.GetDialogueInfo(request.DialogId, sessionID)));
return new ValueTask<string>(
httpResponseUtil.GetBody(dialogueController.GetDialogueInfo(request.DialogId ?? MongoId.Empty(), sessionID))
);
}
/// <summary>
@@ -89,7 +91,7 @@ public class DialogueCallbacks(TimeUtil timeUtil, HttpResponseUtil httpResponseU
/// <returns></returns>
public virtual ValueTask<string> RemoveDialog(string url, RemoveDialogRequestData request, MongoId sessionID)
{
dialogueController.RemoveDialogue(request.DialogId, sessionID);
dialogueController.RemoveDialogue(request.DialogId ?? MongoId.Empty(), sessionID);
return new ValueTask<string>(httpResponseUtil.EmptyArrayResponse());
}
@@ -99,7 +101,7 @@ public class DialogueCallbacks(TimeUtil timeUtil, HttpResponseUtil httpResponseU
/// <returns></returns>
public virtual ValueTask<string> PinDialog(string url, PinDialogRequestData request, MongoId sessionID)
{
dialogueController.SetDialoguePin(request.DialogId, true, sessionID);
dialogueController.SetDialoguePin(request.DialogId ?? MongoId.Empty(), true, sessionID);
return new ValueTask<string>(httpResponseUtil.EmptyArrayResponse());
}
@@ -109,7 +111,7 @@ public class DialogueCallbacks(TimeUtil timeUtil, HttpResponseUtil httpResponseU
/// <returns></returns>
public virtual ValueTask<string> UnpinDialog(string url, PinDialogRequestData request, MongoId sessionID)
{
dialogueController.SetDialoguePin(request.DialogId, false, sessionID);
dialogueController.SetDialoguePin(request.DialogId ?? MongoId.Empty(), false, sessionID);
return new ValueTask<string>(httpResponseUtil.EmptyArrayResponse());
}
@@ -232,6 +234,10 @@ public class DialogueCallbacks(TimeUtil timeUtil, HttpResponseUtil httpResponseU
return new ValueTask<string>(httpResponseUtil.NullResponse());
}
/// <summary>
/// Handle /client/mail/dialog/clear
/// </summary>
/// <returns></returns>
public virtual ValueTask<string> ClearMail(string url, ClearMailMessageRequest request, MongoId sessionID)
{
dialogueController.ClearMessages(sessionID, request);
@@ -239,21 +245,37 @@ public class DialogueCallbacks(TimeUtil timeUtil, HttpResponseUtil httpResponseU
return new ValueTask<string>(httpResponseUtil.EmptyArrayResponse());
}
/// <summary>
/// Handle /client/mail/dialog/group/create
/// </summary>
/// <returns></returns>
public virtual ValueTask<string> CreateGroupMail(string url, CreateGroupMailRequest request, MongoId sessionID)
{
return new ValueTask<string>(httpResponseUtil.EmptyArrayResponse());
}
/// <summary>
/// Handle /client/mail/dialog/group/owner/change
/// </summary>
/// <returns></returns>
public virtual ValueTask<string> ChangeMailGroupOwner(string url, ChangeGroupMailOwnerRequest request, MongoId sessionID)
{
return new ValueTask<string>("Not Implemented!"); // Not implemented in Node
}
/// <summary>
/// Handle /client/mail/dialog/group/users/add
/// </summary>
/// <returns></returns>
public virtual ValueTask<string> AddUserToMail(string url, AddUserGroupMailRequest request, MongoId sessionID)
{
return new ValueTask<string>("Not Implemented!"); // Not implemented in Node
}
/// <summary>
/// Handle /client/mail/dialog/group/users/remove
/// </summary>
/// <returns></returns>
public virtual ValueTask<string> RemoveUserFromMail(string url, RemoveUserGroupMailRequest request, MongoId sessionID)
{
return new ValueTask<string>("Not Implemented!"); // Not implemented in Node
@@ -23,24 +23,23 @@ namespace SPTarkov.Server.Core.Controllers;
[Injectable]
public class BotController(
ISptLogger<BotController> _logger,
DatabaseService _databaseService,
BotGenerator _botGenerator,
BotHelper _botHelper,
BotDifficultyHelper _botDifficultyHelper,
ServerLocalisationService _serverLocalisationService,
SeasonalEventService _seasonalEventService,
MatchBotDetailsCacheService _matchBotDetailsCacheService,
ProfileHelper _profileHelper,
ConfigServer _configServer,
ProfileActivityService _profileActivityService,
RandomUtil _randomUtil,
ICloner _cloner
ISptLogger<BotController> logger,
DatabaseService databaseService,
BotGenerator botGenerator,
BotHelper botHelper,
BotDifficultyHelper botDifficultyHelper,
ServerLocalisationService serverLocalisationService,
SeasonalEventService seasonalEventService,
MatchBotDetailsCacheService matchBotDetailsCacheService,
ProfileHelper profileHelper,
ConfigServer configServer,
ProfileActivityService profileActivityService,
RandomUtil randomUtil,
ICloner cloner
)
{
private readonly BotConfig _botConfig = _configServer.GetConfig<BotConfig>();
private readonly PmcConfig _pmcConfig = _configServer.GetConfig<PmcConfig>();
private static readonly Lock _botListLock = new();
protected readonly BotConfig botConfig = configServer.GetConfig<BotConfig>();
protected readonly PmcConfig pmcConfig = configServer.GetConfig<PmcConfig>();
/// <summary>
/// Return the number of bot load-out varieties to be generated
@@ -49,9 +48,9 @@ public class BotController(
/// <returns>number of bots to generate</returns>
public int GetBotPresetGenerationLimit(string type)
{
if (!_botConfig.PresetBatch.TryGetValue(type, out var limit))
if (!botConfig.PresetBatch.TryGetValue(type, out var limit))
{
_logger.Warning(_serverLocalisationService.GetText("bot-bot_preset_count_value_missing", type));
logger.Warning(serverLocalisationService.GetText("bot-bot_preset_count_value_missing", type));
return 10;
}
@@ -66,7 +65,7 @@ public class BotController(
/// <returns></returns>
public Dictionary<string, object> GetBotCoreDifficulty()
{
return _databaseService.GetBots().Core!;
return databaseService.GetBots().Core;
}
/// <summary>
@@ -82,11 +81,11 @@ public class BotController(
{
var difficulty = diffLevel.ToLowerInvariant();
var raidConfig = _profileActivityService.GetProfileActivityRaidData(sessionId).RaidConfiguration;
var raidConfig = profileActivityService.GetProfileActivityRaidData(sessionId).RaidConfiguration;
if (!(raidConfig != null || ignoreRaidSettings))
{
_logger.Error(_serverLocalisationService.GetText("bot-missing_application_context", "RAID_CONFIGURATION"));
logger.Error(serverLocalisationService.GetText("bot-missing_application_context", "RAID_CONFIGURATION"));
}
// Check value chosen in pre-raid difficulty dropdown
@@ -94,11 +93,11 @@ public class BotController(
var botDifficultyDropDownValue = raidConfig?.WavesSettings?.BotDifficulty?.ToString().ToLowerInvariant() ?? "asonline";
if (botDifficultyDropDownValue != "asonline")
{
difficulty = _botDifficultyHelper.ConvertBotDifficultyDropdownToBotDifficulty(botDifficultyDropDownValue);
difficulty = botDifficultyHelper.ConvertBotDifficultyDropdownToBotDifficulty(botDifficultyDropDownValue);
}
var botDb = _databaseService.GetBots();
return _botDifficultyHelper.GetBotDifficultySettings(type, difficulty, botDb);
var botDb = databaseService.GetBots();
return botDifficultyHelper.GetBotDifficultySettings(type, difficulty, botDb);
}
/// <summary>
@@ -109,7 +108,7 @@ public class BotController(
{
var result = new Dictionary<string, Dictionary<string, DifficultyCategories>>();
var botTypesDb = _databaseService.GetBots().Types;
var botTypesDb = databaseService.GetBots().Types;
if (botTypesDb is null)
{
return result;
@@ -128,9 +127,9 @@ public class BotController(
{
// No bot of this type found, copy details from assault
result[botTypeLower] = result[Roles.Assault];
if (_logger.IsLogEnabled(LogLevel.Debug))
if (logger.IsLogEnabled(LogLevel.Debug))
{
_logger.Debug($"Unable to find bot: {botTypeLower} in db, copying: '{Roles.Assault}'");
logger.Debug($"Unable to find bot: {botTypeLower} in db, copying: '{Roles.Assault}'");
}
continue;
@@ -139,7 +138,7 @@ public class BotController(
if (botDetails?.BotDifficulty is null)
{
// Bot has no difficulty values, skip
_logger.Warning($"Unable to find bot: {botTypeLower} difficulty values in db, skipping");
logger.Warning($"Unable to find bot: {botTypeLower} difficulty values in db, skipping");
continue;
}
@@ -166,9 +165,9 @@ public class BotController(
/// <param name="sessionId">Session/Player id</param>
/// <param name="request"></param>
/// <returns>List of bots</returns>
public async Task<IEnumerable<BotBase>> Generate(MongoId sessionId, GenerateBotsRequestData request)
public async Task<IEnumerable<BotBase?>> Generate(MongoId sessionId, GenerateBotsRequestData request)
{
var pmcProfile = _profileHelper.GetPmcProfile(sessionId);
var pmcProfile = profileHelper.GetPmcProfile(sessionId);
return await GenerateBotWaves(sessionId, request, pmcProfile);
}
@@ -180,7 +179,7 @@ public class BotController(
/// <param name="request">Client bot generation request</param>
/// <param name="pmcProfile">Player profile generating bots</param>
/// <returns>List of generated bots</returns>
protected async Task<IEnumerable<BotBase>> GenerateBotWaves(MongoId sessionId, GenerateBotsRequestData request, PmcData? pmcProfile)
protected async Task<IEnumerable<BotBase?>> GenerateBotWaves(MongoId sessionId, GenerateBotsRequestData request, PmcData? pmcProfile)
{
if (request.Conditions is null || !request.Conditions.Any())
{
@@ -191,7 +190,7 @@ public class BotController(
// Get chosen raid settings from app context
var raidSettings = GetMostRecentRaidSettings(sessionId);
var allPmcsHaveSameNameAsPlayer = _randomUtil.GetChance100(_pmcConfig.AllPMCsHavePlayerNameWithRandomPrefixChance);
var allPmcsHaveSameNameAsPlayer = randomUtil.GetChance100(pmcConfig.AllPMCsHavePlayerNameWithRandomPrefixChance);
// Split each bot wave into its own task
var waveGenerationTasks = request.Conditions.Select(condition =>
@@ -213,9 +212,9 @@ public class BotController(
var results = await Task.WhenAll(waveGenerationTasks);
stopwatch.Stop();
if (_logger.IsLogEnabled(LogLevel.Debug))
if (logger.IsLogEnabled(LogLevel.Debug))
{
_logger.Debug($"Took {stopwatch.ElapsedMilliseconds}ms to GenerateMultipleBotsAndCache()");
logger.Debug($"Took {stopwatch.ElapsedMilliseconds}ms to GenerateMultipleBotsAndCache()");
}
// Merge + flatten results of all wave generations
@@ -229,7 +228,7 @@ public class BotController(
/// <param name="generateRequest"></param>
/// <param name="botGenerationDetails"></param>
/// <returns>Result of generating bot wave</returns>
protected IEnumerable<BotBase> GenerateBotWave(
protected IEnumerable<BotBase?> GenerateBotWave(
MongoId sessionId,
GenerateCondition generateRequest,
BotGenerationDetails botGenerationDetails
@@ -240,33 +239,33 @@ public class BotController(
{
// Add eventRole data + reassign role property to be base type
botGenerationDetails.EventRole = generateRequest.Role;
botGenerationDetails.Role = _seasonalEventService.GetBaseRoleForEventBot(botGenerationDetails.EventRole);
botGenerationDetails.Role = seasonalEventService.GetBaseRoleForEventBot(botGenerationDetails.EventRole);
}
// Event role must take priority to generate correctly
var role = botGenerationDetails.EventRole ?? botGenerationDetails.Role;
if (_logger.IsLogEnabled(LogLevel.Debug))
if (logger.IsLogEnabled(LogLevel.Debug))
{
_logger.Debug(
logger.Debug(
$"Generating wave of: {botGenerationDetails.BotCountToGenerate} bots of type: {role} {botGenerationDetails.BotDifficulty}"
);
}
var generatedBots = Enumerable
.Range(0, botGenerationDetails.BotCountToGenerate)
.AsParallel() // Parallelise above range of values so they can each generate a bot
.AsParallel() // Parallelize above range of values so they can each generate a bot
.Select(i => TryGenerateSingleBot(sessionId, botGenerationDetails, i))
.Where(bot =>
bot is not null
) // Skip failed bots
; // Materialise parallel query into data
if (_logger.IsLogEnabled(LogLevel.Debug))
if (logger.IsLogEnabled(LogLevel.Debug))
{
_logger.Debug(
logger.Debug(
$"Generated: {botGenerationDetails.BotCountToGenerate} {botGenerationDetails.Role}"
+ $"({botGenerationDetails.EventRole ?? botGenerationDetails.Role ?? ""}) {botGenerationDetails.BotDifficulty} bots"
+ $"({botGenerationDetails.EventRole ?? botGenerationDetails.Role}) {botGenerationDetails.BotDifficulty} bots"
);
}
@@ -282,22 +281,22 @@ public class BotController(
try
{
// Clone for thread safety TODO: confirm if clone is necessary (likely not)
var bot = _botGenerator.PrepareAndGenerateBot(sessionId, _cloner.Clone(generationDetails));
var bot = botGenerator.PrepareAndGenerateBot(sessionId, cloner.Clone(generationDetails)!);
// Client expects Side for PMCs to be `Savage`, must be altered here before it's cached
if (bot.Info.Side is Sides.Bear or Sides.Usec)
if (bot.Info?.Side is Sides.Bear or Sides.Usec)
{
bot.Info.Side = Sides.Savage;
}
// Store bot details in cache before returning.
_matchBotDetailsCacheService.CacheBot(bot);
matchBotDetailsCacheService.CacheBot(bot);
return bot;
}
catch (Exception e)
{
_logger.Error($"Failed to generate bot #{botIndex + 1} ({generationDetails.Role}): {e.Message}");
logger.Error($"Failed to generate bot #{botIndex + 1} ({generationDetails.Role}): {e.Message}");
return null;
}
}
@@ -308,11 +307,11 @@ public class BotController(
/// <returns>GetRaidConfigurationRequestData if it exists</returns>
protected GetRaidConfigurationRequestData? GetMostRecentRaidSettings(MongoId sessionId)
{
var raidConfiguration = _profileActivityService.GetProfileActivityRaidData(sessionId)?.RaidConfiguration;
var raidConfiguration = profileActivityService.GetProfileActivityRaidData(sessionId).RaidConfiguration;
if (raidConfiguration is null)
{
_logger.Warning(_serverLocalisationService.GetText("bot-unable_to_load_raid_settings_from_appcontext"));
logger.Warning(serverLocalisationService.GetText("bot-unable_to_load_raid_settings_from_appcontext"));
}
return raidConfiguration;
@@ -323,9 +322,9 @@ public class BotController(
/// </summary>
/// <param name="location">Map name e.g. factory4_day</param>
/// <returns>MinMax values</returns>
protected MinMax<int> GetPmcLevelRangeForMap(string? location)
protected MinMax<int>? GetPmcLevelRangeForMap(string? location)
{
return _pmcConfig.LocationSpecificPmcLevelOverride!.GetValueOrDefault(location?.ToLowerInvariant() ?? "", null);
return pmcConfig.LocationSpecificPmcLevelOverride!.GetValueOrDefault(location?.ToLowerInvariant() ?? "", null);
}
/// <summary>
@@ -343,18 +342,18 @@ public class BotController(
GetRaidConfigurationRequestData? raidSettings
)
{
var generateAsPmc = _botHelper.IsBotPmc(condition.Role);
var generateAsPmc = botHelper.IsBotPmc(condition.Role);
return new BotGenerationDetails
{
IsPmc = generateAsPmc,
Side = generateAsPmc ? _botHelper.GetPmcSideByRole(condition.Role ?? string.Empty) : "Savage",
Role = condition.Role,
Side = generateAsPmc ? botHelper.GetPmcSideByRole(condition.Role ?? string.Empty) : "Savage",
Role = condition.Role!,
PlayerLevel = pmcProfile?.Info?.Level ?? 1,
PlayerName = pmcProfile?.Info?.Nickname,
BotRelativeLevelDeltaMax = _pmcConfig.BotRelativeLevelDelta.Max,
BotRelativeLevelDeltaMin = _pmcConfig.BotRelativeLevelDelta.Min,
BotCountToGenerate = Math.Max(GetBotPresetGenerationLimit(condition.Role), condition.Limit), // Choose largest between value passed in from request vs what's in bot.config
BotRelativeLevelDeltaMax = pmcConfig.BotRelativeLevelDelta.Max,
BotRelativeLevelDeltaMin = pmcConfig.BotRelativeLevelDelta.Min,
BotCountToGenerate = Math.Max(GetBotPresetGenerationLimit(condition.Role!), condition.Limit), // Choose largest between value passed in from request vs what's in bot.config
BotDifficulty = condition.Difficulty,
LocationSpecificPmcLevelOverride = GetPmcLevelRangeForMap(raidSettings?.Location), // Min/max levels for PMCs to generate within
IsPlayerScav = false,
@@ -371,14 +370,14 @@ public class BotController(
/// <returns>bot cap for map</returns>
public int GetBotCap(string location)
{
if (!_botConfig.MaxBotCap.TryGetValue(location.ToLowerInvariant(), out var maxCap))
if (!botConfig.MaxBotCap.TryGetValue(location.ToLowerInvariant(), out var maxCap))
{
return _botConfig.MaxBotCap["default"];
return botConfig.MaxBotCap["default"];
}
if (location == "default")
{
_logger.Warning(_serverLocalisationService.GetText("bot-no_bot_cap_found_for_location", location.ToLowerInvariant()));
logger.Warning(serverLocalisationService.GetText("bot-no_bot_cap_found_for_location", location.ToLowerInvariant()));
}
return maxCap;
@@ -392,9 +391,9 @@ public class BotController(
{
return new AiBotBrainTypes
{
PmcType = _pmcConfig.PmcType,
Assault = _botConfig.AssaultBrainType,
PlayerScav = _botConfig.PlayerScavBrainType,
PmcType = pmcConfig.PmcType,
Assault = botConfig.AssaultBrainType,
PlayerScav = botConfig.PlayerScavBrainType,
};
}
}
@@ -402,11 +401,11 @@ public class BotController(
public record AiBotBrainTypes
{
[JsonPropertyName("pmc")]
public Dictionary<string, Dictionary<string, Dictionary<string, double>>> PmcType { get; set; }
public required Dictionary<string, Dictionary<string, Dictionary<string, double>>> PmcType { get; set; }
[JsonPropertyName("assault")]
public Dictionary<string, Dictionary<string, int>> Assault { get; set; }
public required Dictionary<string, Dictionary<string, int>> Assault { get; set; }
[JsonPropertyName("playerScav")]
public Dictionary<string, Dictionary<string, int>> PlayerScav { get; set; }
public required Dictionary<string, Dictionary<string, int>> PlayerScav { get; set; }
}
@@ -13,6 +13,7 @@ using SPTarkov.Server.Core.Routers;
using SPTarkov.Server.Core.Servers;
using SPTarkov.Server.Core.Services;
using SPTarkov.Server.Core.Utils.Cloners;
using Customization = SPTarkov.Server.Core.Models.Eft.Common.Tables.Customization;
namespace SPTarkov.Server.Core.Controllers;
@@ -38,7 +39,7 @@ public class CustomizationController(
{
var pmcData = profileHelper.GetPmcProfile(sessionId);
var clothing = databaseService.GetCustomization();
var suits = databaseService.GetTrader(traderId).Suits;
var suits = databaseService.GetTrader(traderId)?.Suits;
var matchingSuits = suits?.Where(s => clothing.ContainsKey(s.SuiteId));
matchingSuits = matchingSuits?.Where(s =>
@@ -99,7 +100,7 @@ public class CustomizationController(
Type = CustomisationType.SUITE,
};
profile.CustomisationUnlocks.Add(rewardToStore);
profile.CustomisationUnlocks?.Add(rewardToStore);
return output;
}
@@ -115,7 +116,7 @@ public class CustomizationController(
var fullProfile = profileHelper.GetFullProfile(sessionId);
// Check if clothing can be found by id
return fullProfile.CustomisationUnlocks.Exists(customisation => Equals(customisation.Id, suitId));
return fullProfile.CustomisationUnlocks?.Exists(customisation => Equals(customisation.Id, suitId)) ?? false;
}
/// <summary>
@@ -158,11 +159,11 @@ public class CustomizationController(
{
var options = new ProcessBuyTradeRequestData
{
SchemeItems = [new IdWithCount { Count = inventoryItemToProcess.Count.Value, Id = inventoryItemToProcess.Id }],
SchemeItems = [new IdWithCount { Count = inventoryItemToProcess.Count!.Value, Id = inventoryItemToProcess.Id }],
TransactionId = Traders.RAGMAN,
Action = "BuyCustomization",
Type = "",
ItemId = "",
ItemId = MongoId.Empty(),
Count = 0,
SchemeId = 0,
};
@@ -183,7 +184,7 @@ public class CustomizationController(
foreach (var (traderId, trader) in traders)
{
if (trader.Base?.CustomizationSeller is not null && trader.Base.CustomizationSeller.Value)
if (trader.Base.CustomizationSeller is not null && trader.Base.CustomizationSeller.Value)
{
result.AddRange(GetTraderSuits(traderId, sessionId));
}
@@ -195,11 +196,10 @@ public class CustomizationController(
/// <summary>
/// Handle client/hideout/customization/offer/list
/// </summary>
/// <param name="sessionId">Session/Player id</param>
/// <returns></returns>
public HideoutCustomisation GetHideoutCustomisation(MongoId sessionId)
/// <returns>Hideout customizations</returns>
public HideoutCustomisation GetHideoutCustomisation()
{
return databaseService.GetHideout().Customisation!;
return databaseService.GetHideout().Customisation;
}
/// <summary>
@@ -212,10 +212,6 @@ public class CustomizationController(
var customisationResultsClone = cloner.Clone(databaseService.GetTemplates().CustomisationStorage);
var profile = profileHelper.GetFullProfile(sessionId);
if (profile is null)
{
return customisationResultsClone!;
}
customisationResultsClone!.AddRange(profile.CustomisationUnlocks ?? []);
@@ -231,7 +227,7 @@ public class CustomizationController(
/// <returns>ItemEventRouterResponse</returns>
public ItemEventRouterResponse SetCustomisation(MongoId sessionId, CustomizationSetRequest request, PmcData pmcData)
{
foreach (var customisation in request.Customizations)
foreach (var customisation in request.Customizations!)
{
switch (customisation.Type)
{
@@ -268,19 +264,19 @@ public class CustomizationController(
return;
}
// Body
if (dbSuit.Parent == CustomisationTypeId.UPPER)
{
pmcData.Customization.Body = dbSuit.Properties.Body;
pmcData.Customization.Hands = dbSuit.Properties.Hands;
pmcData.Customization ??= new Customization();
return;
}
// Feet
if (dbSuit.Parent == CustomisationTypeId.LOWER)
switch (dbSuit?.Parent)
{
pmcData.Customization.Feet = dbSuit.Properties.Feet;
// Body
case CustomisationTypeId.UPPER:
pmcData.Customization.Body = dbSuit.Properties.Body;
pmcData.Customization.Hands = dbSuit.Properties.Hands;
return;
// Feet
case CustomisationTypeId.LOWER:
pmcData.Customization.Feet = dbSuit.Properties.Feet;
break;
}
}
}
@@ -73,22 +73,30 @@ public class DialogueController(
// Add any friends the user has after the chatbots
var profile = profileHelper.GetFullProfile(sessionId);
if (profile?.FriendProfileIds is not null)
if (profile.FriendProfileIds is null)
{
foreach (var friendId in profile.FriendProfileIds)
return new GetFriendListDataResponse
{
var friendProfile = profileHelper.GetChatRoomMemberFromSessionId(friendId);
if (friendProfile is not null)
{
friends.Add(
new UserDialogInfo
{
Id = friendProfile.Id,
Aid = friendProfile.Aid,
Info = friendProfile.Info,
}
);
}
Friends = friends,
Ignore = [],
InIgnoreList = [],
};
}
foreach (var friendId in profile.FriendProfileIds)
{
var friendProfile = profileHelper.GetChatRoomMemberFromSessionId(friendId);
if (friendProfile is not null)
{
friends.Add(
new UserDialogInfo
{
Id = friendProfile.Id,
Aid = friendProfile.Aid,
Info = friendProfile.Info,
}
);
}
}
@@ -100,6 +108,10 @@ public class DialogueController(
};
}
/// <summary>
/// Get all active chatbots
/// </summary>
/// <returns>Active chatbots</returns>
public List<UserDialogInfo> GetActiveChatBots()
{
var activeBots = new List<UserDialogInfo>();
@@ -109,7 +121,7 @@ public class DialogueController(
foreach (var bot in _dialogueChatBots)
{
var botData = bot.GetChatBot();
if (chatBotConfig.EnabledBots.ContainsKey(botData.Id!))
if (chatBotConfig.EnabledBots.ContainsKey(botData.Id))
{
activeBots.Add(botData);
}
@@ -148,10 +160,10 @@ public class DialogueController(
/// <param name="dialogueId">Dialog id</param>
/// <param name="sessionId">Session Id</param>
/// <returns>DialogueInfo</returns>
public virtual DialogueInfo? GetDialogueInfo(string? dialogueId, MongoId sessionId)
public virtual DialogueInfo? GetDialogueInfo(MongoId dialogueId, MongoId sessionId)
{
var dialogs = dialogueHelper.GetDialogsForProfile(sessionId);
var dialogue = dialogs!.GetValueOrDefault(dialogueId);
var dialogue = dialogs.GetValueOrDefault(dialogueId);
return GetDialogueInfo(dialogue, sessionId);
}
@@ -162,9 +174,9 @@ public class DialogueController(
/// <param name="dialogue">Dialog</param>
/// <param name="sessionId">Session Id</param>
/// <returns>DialogueInfo</returns>
public virtual DialogueInfo? GetDialogueInfo(Dialogue dialogue, MongoId sessionId)
public virtual DialogueInfo? GetDialogueInfo(Dialogue? dialogue, MongoId sessionId)
{
if (!dialogue.Messages.Any())
if (dialogue is null || dialogue.Messages?.Count == 0)
{
return null;
}
@@ -236,7 +248,7 @@ public class DialogueController(
var fullProfile = saveServer.GetProfile(sessionId);
var dialogue = GetDialogByIdFromProfile(fullProfile, request);
if (!dialogue.Messages.Any())
if (dialogue.Messages?.Count == 0)
{
return new GetMailDialogViewResponseData
{
@@ -250,7 +262,7 @@ public class DialogueController(
dialogue.New = 0;
// Set number of new attachments, but ignore those that have expired.
dialogue.AttachmentsNew = GetUnreadMessagesWithAttachmentsCount(sessionId, dialogueId!);
dialogue.AttachmentsNew = GetUnreadMessagesWithAttachmentsCount(sessionId, dialogueId);
return new GetMailDialogViewResponseData
{
@@ -268,12 +280,12 @@ public class DialogueController(
/// <returns>Dialogue</returns>
protected Dialogue GetDialogByIdFromProfile(SptProfile profile, GetMailDialogViewRequestData request)
{
if (profile.DialogueRecords is null || profile.DialogueRecords.ContainsKey(request.DialogId!))
if (profile.DialogueRecords is null || profile.DialogueRecords.ContainsKey(request.DialogId))
{
return profile.DialogueRecords?[request.DialogId!] ?? throw new NullReferenceException();
return profile.DialogueRecords?[request.DialogId] ?? throw new NullReferenceException();
}
profile.DialogueRecords[request.DialogId!] = new Dialogue
profile.DialogueRecords[request.DialogId] = new Dialogue
{
Id = request.DialogId,
AttachmentsNew = 0,
@@ -285,22 +297,22 @@ public class DialogueController(
if (request.Type != MessageType.UserMessage)
{
return profile.DialogueRecords[request.DialogId!];
return profile.DialogueRecords[request.DialogId];
}
var dialogue = profile.DialogueRecords[request.DialogId!];
var dialogue = profile.DialogueRecords[request.DialogId];
dialogue.Users = [];
var chatBot = _dialogueChatBots.FirstOrDefault(cb => cb.GetChatBot().Id == request.DialogId);
if (chatBot is null)
{
return profile.DialogueRecords[request.DialogId!];
return profile.DialogueRecords[request.DialogId];
}
dialogue.Users ??= [];
dialogue.Users.Add(chatBot.GetChatBot());
return profile.DialogueRecords[request.DialogId!];
return profile.DialogueRecords[request.DialogId];
}
/// <summary>
@@ -352,7 +364,7 @@ public class DialogueController(
/// <param name="sessionId">Session id</param>
/// <param name="dialogueId">Dialog id</param>
/// <returns>Count of messages with attachments</returns>
protected int GetUnreadMessagesWithAttachmentsCount(MongoId sessionId, string dialogueId)
protected int GetUnreadMessagesWithAttachmentsCount(MongoId sessionId, MongoId dialogueId)
{
var newAttachmentCount = 0;
var activeMessages = GetActiveMessagesFromDialog(sessionId, dialogueId);
@@ -373,7 +385,7 @@ public class DialogueController(
/// <param name="sessionId">Session/Player id</param>
/// <param name="dialogueId">Dialog to get mail attachments from</param>
/// <returns>Message array</returns>
protected List<Message> GetActiveMessagesFromDialog(MongoId sessionId, string dialogueId)
protected List<Message> GetActiveMessagesFromDialog(MongoId sessionId, MongoId dialogueId)
{
var timeNow = timeUtil.GetTimeStamp();
var dialogs = dialogueHelper.GetDialogsForProfile(sessionId);
@@ -403,10 +415,10 @@ public class DialogueController(
/// </summary>
/// <param name="dialogueId">id of the dialog to remove</param>
/// <param name="sessionId">Player id</param>
public virtual void RemoveDialogue(string? dialogueId, MongoId sessionId)
public virtual void RemoveDialogue(MongoId dialogueId, MongoId sessionId)
{
var profile = saveServer.GetProfile(sessionId);
if (!profile.DialogueRecords.Remove(dialogueId))
if (!profile.DialogueRecords?.Remove(dialogueId) ?? false)
{
logger.Error(serverLocalisationService.GetText("dialogue-unable_to_find_in_profile", new { sessionId, dialogueId }));
}
@@ -418,7 +430,7 @@ public class DialogueController(
/// <param name="dialogueId"></param>
/// <param name="shouldPin"></param>
/// <param name="sessionId">Session/Player id</param>
public virtual void SetDialoguePin(string? dialogueId, bool shouldPin, MongoId sessionId)
public virtual void SetDialoguePin(MongoId dialogueId, bool shouldPin, MongoId sessionId)
{
var dialog = dialogueHelper.GetDialogsForProfile(sessionId).GetValueOrDefault(dialogueId);
if (dialog is null)
@@ -437,10 +449,17 @@ public class DialogueController(
/// </summary>
/// <param name="dialogueIds">Dialog ids to set as read</param>
/// <param name="sessionId">Player profile id</param>
public virtual void SetRead(List<string>? dialogueIds, MongoId sessionId)
public virtual void SetRead(List<MongoId>? dialogueIds, MongoId sessionId)
{
if (dialogueIds is null)
{
logger.Error(serverLocalisationService.GetText("dialogue-list_from_client_empty", new { sessionId }));
return;
}
var dialogs = dialogueHelper.GetDialogsForProfile(sessionId);
if (dialogs.Any() != true)
if (dialogs.Count == 0)
{
logger.Error(serverLocalisationService.GetText("dialogue-unable_to_find_dialogs_in_profile", new { sessionId }));
@@ -629,7 +648,7 @@ public class DialogueController(
public void ClearMessages(MongoId sessionId, ClearMailMessageRequest request)
{
var profile = saveServer.GetProfile(sessionId);
if (!profile.DialogueRecords.TryGetValue(request.DialogId, out var dialogToClear))
if (profile.DialogueRecords is null || !profile.DialogueRecords.TryGetValue(request.DialogId, out var dialogToClear))
{
logger.Warning($"unable to clear messages from dialog: {request.DialogId} as it cannot be found in profile: {sessionId}");
@@ -17,7 +17,7 @@ public class DialogueHelper(ISptLogger<DialogueHelper> logger, ProfileHelper pro
public MessagePreview GetMessagePreview(Models.Eft.Profile.Dialogue? dialogue)
{
// The last message of the dialogue should be shown on the preview.
var message = dialogue.Messages.LastOrDefault();
var message = dialogue?.Messages?.LastOrDefault();
MessagePreview result = new()
{
@@ -92,10 +92,10 @@ public class DialogueHelper(ISptLogger<DialogueHelper> logger, ProfileHelper pro
/// </summary>
/// <param name="sessionId">Session/player id</param>
/// <returns>Dialog dictionary</returns>
public Dictionary<string, Models.Eft.Profile.Dialogue> GetDialogsForProfile(MongoId sessionId)
public Dictionary<MongoId, Models.Eft.Profile.Dialogue> GetDialogsForProfile(MongoId sessionId)
{
var profile = profileHelper.GetFullProfile(sessionId);
return profile.DialogueRecords ?? (profile.DialogueRecords = new Dictionary<string, Models.Eft.Profile.Dialogue>());
return profile.DialogueRecords ?? (profile.DialogueRecords = new Dictionary<MongoId, Models.Eft.Profile.Dialogue>());
}
/// <summary>
@@ -104,7 +104,7 @@ public class DialogueHelper(ISptLogger<DialogueHelper> logger, ProfileHelper pro
/// <param name="profileId">Profile to look in</param>
/// <param name="dialogueId">Dialog to return</param>
/// <returns>Dialogue</returns>
public Models.Eft.Profile.Dialogue? GetDialogueFromProfile(MongoId profileId, string dialogueId)
public Models.Eft.Profile.Dialogue? GetDialogueFromProfile(MongoId profileId, MongoId dialogueId)
{
var dialogues = GetDialogsForProfile(profileId);
if (dialogues.TryGetValue(dialogueId, out var dialogue))
@@ -31,7 +31,7 @@ public record TemplateSide
public List<MongoId>? Suits { get; set; }
[JsonPropertyName("dialogues")]
public Dictionary<string, Dialogue>? Dialogues { get; set; }
public Dictionary<MongoId, Dialogue>? Dialogues { get; set; }
[JsonPropertyName("userbuilds")]
public UserBuilds? UserBuilds { get; set; }
@@ -1,4 +1,5 @@
using System.Text.Json.Serialization;
using SPTarkov.Server.Core.Models.Common;
using SPTarkov.Server.Core.Models.Utils;
namespace SPTarkov.Server.Core.Models.Eft.Dialog;
@@ -9,5 +10,5 @@ public record ClearMailMessageRequest : IRequestData
public Dictionary<string, object>? ExtensionData { get; set; }
[JsonPropertyName("dialogId")]
public string? DialogId { get; set; }
public required MongoId DialogId { get; set; }
}
@@ -1,4 +1,5 @@
using System.Text.Json.Serialization;
using SPTarkov.Server.Core.Models.Common;
using SPTarkov.Server.Core.Models.Utils;
namespace SPTarkov.Server.Core.Models.Eft.Dialog;
@@ -9,5 +10,5 @@ public record GetMailDialogInfoRequestData : IRequestData
public Dictionary<string, object>? ExtensionData { get; set; }
[JsonPropertyName("dialogId")]
public string? DialogId { get; set; }
public MongoId? DialogId { get; set; }
}
@@ -1,4 +1,5 @@
using System.Text.Json.Serialization;
using SPTarkov.Server.Core.Models.Common;
using SPTarkov.Server.Core.Models.Utils;
namespace SPTarkov.Server.Core.Models.Eft.Dialog;
@@ -9,5 +10,5 @@ public record PinDialogRequestData : IRequestData
public Dictionary<string, object>? ExtensionData { get; set; }
[JsonPropertyName("dialogId")]
public string? DialogId { get; set; }
public MongoId? DialogId { get; set; }
}
@@ -1,4 +1,5 @@
using System.Text.Json.Serialization;
using SPTarkov.Server.Core.Models.Common;
using SPTarkov.Server.Core.Models.Utils;
namespace SPTarkov.Server.Core.Models.Eft.Dialog;
@@ -9,5 +10,5 @@ public record RemoveDialogRequestData : IRequestData
public Dictionary<string, object>? ExtensionData { get; set; }
[JsonPropertyName("dialogId")]
public string? DialogId { get; set; }
public MongoId? DialogId { get; set; }
}
@@ -1,4 +1,5 @@
using System.Text.Json.Serialization;
using SPTarkov.Server.Core.Models.Common;
using SPTarkov.Server.Core.Models.Utils;
namespace SPTarkov.Server.Core.Models.Eft.Dialog;
@@ -9,5 +10,5 @@ public record SetDialogReadRequestData : IRequestData
public Dictionary<string, object>? ExtensionData { get; set; }
[JsonPropertyName("dialogs")]
public List<string>? Dialogs { get; set; }
public List<MongoId>? Dialogs { get; set; }
}
@@ -30,7 +30,7 @@ public record SptProfile
public UserBuilds? UserBuildData { get; set; }
[JsonPropertyName("dialogues")]
public Dictionary<string, Dialogue>? DialogueRecords { get; set; }
public Dictionary<MongoId, Dialogue>? DialogueRecords { get; set; }
[JsonPropertyName("spt")]
public Spt? SptData { get; set; }
@@ -249,7 +249,7 @@ public record DialogueInfo
public int? New { get; set; }
[JsonPropertyName("_id")]
public string? Id { get; set; }
public MongoId Id { get; set; }
[JsonPropertyName("type")]
public MessageType? Type { get; set; }
@@ -1,4 +1,5 @@
using System.Text.Json.Serialization;
using SPTarkov.Server.Core.Models.Common;
using SPTarkov.Server.Core.Models.Eft.Game;
namespace SPTarkov.Server.Core.Models.Spt.Config;
@@ -258,7 +259,7 @@ public record ChatbotFeatures
/// Bot Ids player is allowed to interact with
/// </summary>
[JsonPropertyName("enabledBots")]
public required Dictionary<string, bool> EnabledBots { get; set; }
public required Dictionary<MongoId, bool> EnabledBots { get; set; }
}
public record CommandoFeatures