Moved more code into extension methods

This commit is contained in:
Chomp
2025-07-04 19:29:43 +01:00
parent 7b606fb2a7
commit 01cd251b68
16 changed files with 134 additions and 172 deletions
@@ -215,5 +215,16 @@ namespace SPTarkov.Server.Core.Extensions
fullProfile.SptData.ExtraRepeatableQuests[repeatableId] += rewardValue;
}
}
/// <summary>
/// Is the provided session id for a developer account
/// </summary>
/// <param name="fullProfile">Profile to check</param>
/// <returns>True if account is developer</returns>
public static bool IsDeveloperAccount(this SptProfile fullProfile)
{
return fullProfile?.ProfileInfo?.Edition?.ToLowerInvariant().StartsWith("spt developer")
?? false;
}
}
}
@@ -227,5 +227,18 @@ namespace SPTarkov.Server.Core.Extensions
return false;
}
/// <summary>
/// Get status of a quest in player profile by its id
/// </summary>
/// <param name="pmcData">Profile to search</param>
/// <param name="questId">Quest id to look up</param>
/// <returns>QuestStatus enum</returns>
public static QuestStatusEnum GetQuestStatus(this PmcData pmcData, string questId)
{
var quest = pmcData.Quests?.FirstOrDefault(q => q.QId == questId);
return quest?.Status ?? QuestStatusEnum.Locked;
}
}
}
@@ -0,0 +1,44 @@
using System.Text;
namespace SPTarkov.Server.Core.Extensions
{
public static class StringExtensions
{
public static string Encode(this string value, EncodeType encode)
{
return encode switch
{
EncodeType.BASE64 => Convert.ToBase64String(Encoding.Default.GetBytes(value)),
EncodeType.HEX => Convert.ToHexString(Encoding.Default.GetBytes(value)),
EncodeType.ASCII => Encoding.ASCII.GetString(Encoding.Default.GetBytes(value)),
EncodeType.UTF8 => Encoding.UTF8.GetString(Encoding.Default.GetBytes(value)),
_ => throw new ArgumentOutOfRangeException(nameof(encode), encode, null),
};
}
public static string Decode(this string value, EncodeType encode)
{
switch (encode)
{
case EncodeType.BASE64:
return Encoding.UTF8.GetString(Convert.FromBase64String(value));
case EncodeType.HEX:
return Encoding.UTF8.GetString(Convert.FromHexString(value));
case EncodeType.ASCII:
return Encoding.ASCII.GetString(Encoding.Default.GetBytes(value));
case EncodeType.UTF8:
return Encoding.UTF8.GetString(Encoding.Default.GetBytes(value));
default:
throw new ArgumentOutOfRangeException(nameof(encode), encode, null);
}
}
public enum EncodeType
{
BASE64,
HEX,
ASCII,
UTF8,
}
}
}
@@ -48,5 +48,20 @@ namespace SPTarkov.Server.Core.Extensions
{
return weaponTemplate.Properties.DefMagType;
}
/// <summary>
/// Get the default plate an armor has in its db item
/// </summary>
/// <param name="armorItem">Item to look up default plate</param>
/// <param name="modSlot">front/back</param>
/// <returns>Tpl of plate</returns>
public static string? GetDefaultPlateTpl(this TemplateItem armorItem, string modSlot)
{
var relatedItemDbModSlot = armorItem.Properties.Slots?.FirstOrDefault(slot =>
string.Equals(slot.Name, modSlot, StringComparison.OrdinalIgnoreCase)
);
return relatedItemDbModSlot?.Props?.Filters?.FirstOrDefault()?.Plate;
}
}
}
@@ -1,6 +1,7 @@
using System.Collections.Frozen;
using System.Globalization;
using SPTarkov.DI.Annotations;
using SPTarkov.Server.Core.Extensions;
using SPTarkov.Server.Core.Helpers;
using SPTarkov.Server.Core.Models.Common;
using SPTarkov.Server.Core.Models.Eft.Common;
@@ -118,7 +119,7 @@ public class BotEquipmentModGenerator(
foreach (var (modSlotName, modPool) in compatibleModsPool ?? [])
{
// Get the templates slot object from db
var itemSlotTemplate = GetModItemSlotFromDb(modSlotName, parentTemplate);
var itemSlotTemplate = GetModItemSlotFromDbTemplate(modSlotName, parentTemplate);
if (itemSlotTemplate is null)
{
_logger.Error(
@@ -185,7 +186,8 @@ public class BotEquipmentModGenerator(
);
switch (plateSlotFilteringOutcome.Result)
{
case Result.UNKNOWN_FAILURE or Result.NO_DEFAULT_FILTER:
case Result.UNKNOWN_FAILURE
or Result.NO_DEFAULT_FILTER:
if (_logger.IsLogEnabled(LogLevel.Debug))
{
_logger.Debug(
@@ -406,7 +408,7 @@ public class BotEquipmentModGenerator(
);
}
var defaultPlate = GetDefaultPlateTpl(armorItem, modSlot);
var defaultPlate = armorItem.GetDefaultPlateTpl(modSlot);
if (defaultPlate is not null)
{
// Return Default Plates cause couldn't get lowest level available from original selection
@@ -474,21 +476,6 @@ public class BotEquipmentModGenerator(
};
}
/// <summary>
/// Get the default plate an armor has in its db item
/// </summary>
/// <param name="armorItem">Item to look up default plate</param>
/// <param name="modSlot">front/back</param>
/// <returns>Tpl of plate</returns>
protected string? GetDefaultPlateTpl(TemplateItem armorItem, string modSlot)
{
var relatedItemDbModSlot = armorItem.Properties.Slots?.FirstOrDefault(slot =>
string.Equals(slot.Name, modSlot, StringComparison.OrdinalIgnoreCase)
);
return relatedItemDbModSlot?.Props?.Filters.FirstOrDefault()?.Plate;
}
/// <summary>
/// Get the matching armor slot from the default preset matching passed in armor tpl
/// </summary>
@@ -555,7 +542,7 @@ public class BotEquipmentModGenerator(
foreach (var modSlot in sortedModKeys)
{
// Check weapon has slot for mod to fit in
var modsParentSlot = GetModItemSlotFromDb(modSlot, request.ParentTemplate);
var modsParentSlot = GetModItemSlotFromDbTemplate(modSlot, request.ParentTemplate);
if (modsParentSlot is null)
{
_logger.Error(
@@ -1055,7 +1042,7 @@ public class BotEquipmentModGenerator(
/// <param name="modSlot">e.g patron_in_weapon</param>
/// <param name="parentTemplate">item template</param>
/// <returns>Slot item</returns>
public Slot? GetModItemSlotFromDb(string modSlot, TemplateItem parentTemplate)
public Slot? GetModItemSlotFromDbTemplate(string modSlot, TemplateItem parentTemplate)
{
var modSlotLower = modSlot.ToLowerInvariant();
switch (modSlotLower)
@@ -1317,7 +1304,7 @@ public class BotEquipmentModGenerator(
/// <param name="request"></param>
/// <param name="modPool">Pool of mods that can be picked from</param>
/// <param name="parentSlot">Slot the picked mod will have as a parent</param>
/// <param name="choiceTypeEnum">How should chosen tpl be treated: DEFAULT_MOD/SPAWN/SKIP</param>
/// <param name="choiceTypeEnum">How should the chosen tpl be handled: DEFAULT_MOD/SPAWN/SKIP</param>
/// <param name="weapon">Array of weapon items chosen item will be added to</param>
/// <param name="modSlotName">Name of slot picked mod will be placed into</param>
/// <returns>Chosen weapon details</returns>
@@ -11,8 +11,7 @@ namespace SPTarkov.Server.Core.Helpers;
[Injectable]
public class AssortHelper(
ISptLogger<AssortHelper> _logger,
ServerLocalisationService _serverLocalisationService,
QuestHelper _questHelper
ServerLocalisationService _serverLocalisationService
)
{
/// <summary>
@@ -55,10 +54,7 @@ public class AssortHelper(
}
// Remove assort if quest in profile does not have status that unlocks assort
var questStatusInProfile = _questHelper.GetQuestStatus(
pmcProfile,
unlockValues.Value.Key
);
var questStatusInProfile = pmcProfile.GetQuestStatus(unlockValues.Value.Key);
if (!unlockValues.Value.Value.Contains(questStatusInProfile))
{
strippedTraderAssorts = traderAssorts.RemoveItemFromAssort(assortId.Key, isFlea);
@@ -596,8 +596,7 @@ public class HideoutHelper(
* GetTimeElapsedSinceLastServerTick(pmcData, isGeneratorOn);
// Get all fuel consumption bonuses, returns an empty array if none found
var profileFuelConsomptionBonusSum = _profileHelper.GetBonusValueFromProfile(
pmcData,
var profileFuelConsomptionBonusSum = pmcData.GetBonusValueFromProfile(
BonusType.FuelConsumption
);
@@ -602,24 +602,6 @@ public class ProfileHelper(
}
}
/// <summary>
/// Iterate over all bonuses and sum up all bonuses of desired type in provided profile
/// </summary>
/// <param name="pmcProfile">Player profile</param>
/// <param name="desiredBonus">Bonus to sum up</param>
/// <returns>Summed bonus value or 0 if no bonus found</returns>
public double GetBonusValueFromProfile(PmcData pmcProfile, BonusType desiredBonus)
{
var bonuses = pmcProfile?.Bonuses?.Where(b => b.Type == desiredBonus);
if (bonuses is null || !bonuses.Any())
{
return 0;
}
// Sum all bonuses found above
return bonuses?.Sum(bonus => bonus?.Value ?? 0) ?? 0;
}
public bool HasAccessToRepeatableFreeRefreshSystem(PmcData pmcProfile)
{
return _gameEditionsWithFreeRefresh.Contains(pmcProfile.Info.GameVersion);
@@ -57,19 +57,6 @@ public class QuestHelper(
}
}
/// <summary>
/// Get status of a quest in player profile by its id
/// </summary>
/// <param name="pmcData">Profile to search</param>
/// <param name="questId">Quest id to look up</param>
/// <returns>QuestStatus enum</returns>
public QuestStatusEnum GetQuestStatus(PmcData pmcData, string questId)
{
var quest = pmcData.Quests?.FirstOrDefault(q => q.QId == questId);
return quest?.Status ?? QuestStatusEnum.Locked;
}
/// <summary>
/// returns true if the level condition is satisfied
/// </summary>
@@ -967,7 +954,7 @@ public class QuestHelper(
/// <param name="pmcData">Profile to update</param>
/// <param name="newQuestState">New state the quest should be in</param>
/// <param name="questId">Id of the quest to alter the status of</param>
public void UpdateQuestState(PmcData pmcData, QuestStatusEnum newQuestState, string questId)
protected void UpdateQuestState(PmcData pmcData, QuestStatusEnum newQuestState, string questId)
{
// Find quest in profile, update status to desired status
var questToUpdate = pmcData.Quests.FirstOrDefault(quest => quest.QId == questId);
@@ -1606,12 +1593,12 @@ public class QuestHelper(
);
}
/**
* Look for newly available quests after completing a quest with a requirement to wait x minutes (time-locked) before being available and add data to profile
* @param pmcData Player profile to update
* @param quests Quests to look for wait conditions in
* @param completedQuestId Quest just completed
*/
/// <summary>
/// Look for newly available quests after completing a quest with a requirement to wait x minutes (time-locked) before being available and add data to profile
/// </summary>
/// <param name="pmcData">Player profile to update</param>
/// <param name="quests">Quests to look for wait conditions in</param>
/// <param name="completedQuestId">Quest just completed</param>
protected void AddTimeLockedQuestsToProfile(
PmcData pmcData,
List<Quest> quests,
@@ -123,7 +123,7 @@ public class EventOutputHolder(
/// Required as continuous productions don't reset and stay at 100% completion but client thinks it hasn't started
/// </summary>
/// <param name="productions"> Productions in a profile </param>
private void CleanUpCompleteCraftsInProfile(Dictionary<string, Production>? productions)
protected void CleanUpCompleteCraftsInProfile(Dictionary<string, Production>? productions)
{
foreach (var production in productions)
{
@@ -156,7 +156,7 @@ public class EventOutputHolder(
/// </summary>
/// <param name="pmcData"> Player profile </param>
/// <returns> Dictionary of hideout improvements </returns>
private Dictionary<string, HideoutImprovement>? GetImprovementsFromProfileAndFlagComplete(
protected Dictionary<string, HideoutImprovement>? GetImprovementsFromProfileAndFlagComplete(
PmcData pmcData
)
{
@@ -185,7 +185,7 @@ public class EventOutputHolder(
/// <param name="productions"> Productions from player profile </param>
/// <param name="sessionId"> Player session ID</param>
/// <returns> Dictionary of hideout productions </returns>
private Dictionary<string, Production>? GetProductionsFromProfileAndFlagComplete(
protected Dictionary<string, Production>? GetProductionsFromProfileAndFlagComplete(
Dictionary<string, Production>? productions,
string sessionId
)
@@ -240,7 +240,7 @@ public class EventOutputHolder(
return productions.Keys.Count > 0 ? productions : null;
}
private void ResetMoneyTransferLimit(MoneyTransferLimits limit)
protected void ResetMoneyTransferLimit(MoneyTransferLimits limit)
{
if (limit.NextResetTime < timeUtil.GetTimeStamp())
{
@@ -254,7 +254,7 @@ public class EventOutputHolder(
/// </summary>
/// <param name="traderData"> Server data for traders </param>
/// <returns> Dict of trader id + TraderData </returns>
private Dictionary<string, TraderData> ConstructTraderRelations(
protected Dictionary<string, TraderData> ConstructTraderRelations(
Dictionary<string, TraderInfo> traderData
)
{
@@ -168,8 +168,7 @@ public class InsuranceService(
return _timeUtil.GetTimeStamp() + _insuranceConfig.ReturnTimeOverrideSeconds;
}
var insuranceReturnTimeBonusSum = _profileHelper.GetBonusValueFromProfile(
pmcData,
var insuranceReturnTimeBonusSum = pmcData.GetBonusValueFromProfile(
BonusType.InsuranceReturnTime
);
@@ -98,8 +98,7 @@ public class RagfairTaxService(
itemPriceMult = Math.Pow(4.0, itemPriceMult);
requirementPriceMult = Math.Pow(4.0, requirementPriceMult);
var hideoutFleaTaxDiscountBonusSum = _profileHelper.GetBonusValueFromProfile(
pmcData,
var hideoutFleaTaxDiscountBonusSum = pmcData.GetBonusValueFromProfile(
BonusType.RagfairCommission
);
// A negative bonus implies a lower discount, since we subtract later, invert the value here
+3 -2
View File
@@ -1,10 +1,12 @@
using SPTarkov.DI.Annotations;
using SPTarkov.Server.Core.DI;
using SPTarkov.Server.Core.Extensions;
using SPTarkov.Server.Core.Helpers;
using SPTarkov.Server.Core.Models.Spt.Config;
using SPTarkov.Server.Core.Models.Utils;
using SPTarkov.Server.Core.Servers;
using SPTarkov.Server.Core.Services;
using static SPTarkov.Server.Core.Extensions.StringExtensions;
using LogLevel = SPTarkov.Server.Core.Models.Spt.Logging.LogLevel;
namespace SPTarkov.Server.Core.Utils;
@@ -17,7 +19,6 @@ public class App(
RandomUtil _randomUtil,
ServerLocalisationService _serverLocalisationService,
ConfigServer _configServer,
EncodingUtil _encodingUtil,
HttpServer _httpServer,
DatabaseService _databaseService,
IHostApplicationLifetime _appLifeTime,
@@ -48,7 +49,7 @@ public class App(
_logger.Debug($"Ran as admin: {Environment.IsPrivilegedProcess}");
_logger.Debug($"CPU cores: {Environment.ProcessorCount}");
_logger.Debug(
$"PATH: {_encodingUtil.ToBase64(Environment.ProcessPath ?? "null returned")}"
$"PATH: {(Environment.ProcessPath ?? "null returned").Encode(EncodeType.BASE64)}"
);
_logger.Debug($"Server: {ProgramStatics.SPT_VERSION() ?? _coreConfig.SptVersion}");
@@ -1,65 +0,0 @@
using System.Text;
using SPTarkov.DI.Annotations;
namespace SPTarkov.Server.Core.Utils;
[Injectable(InjectionType.Singleton)]
public class EncodingUtil
{
public string Encode(string value, EncodeType encode)
{
return encode switch
{
EncodeType.BASE64 => Convert.ToBase64String(Encoding.Default.GetBytes(value)),
EncodeType.HEX => Convert.ToHexString(Encoding.Default.GetBytes(value)),
EncodeType.ASCII => Encoding.ASCII.GetString(Encoding.Default.GetBytes(value)),
EncodeType.UTF8 => Encoding.UTF8.GetString(Encoding.Default.GetBytes(value)),
_ => throw new ArgumentOutOfRangeException(nameof(encode), encode, null),
};
}
public string Decode(string value, EncodeType encode)
{
switch (encode)
{
case EncodeType.BASE64:
return Encoding.UTF8.GetString(Convert.FromBase64String(value));
case EncodeType.HEX:
return Encoding.UTF8.GetString(Convert.FromHexString(value));
case EncodeType.ASCII:
return Encoding.ASCII.GetString(Encoding.Default.GetBytes(value));
case EncodeType.UTF8:
return Encoding.UTF8.GetString(Encoding.Default.GetBytes(value));
default:
throw new ArgumentOutOfRangeException(nameof(encode), encode, null);
}
}
public string FromBase64(string value)
{
return Decode(value, EncodeType.BASE64);
}
public string ToBase64(string value)
{
return Encode(value, EncodeType.BASE64);
}
public string FromHex(string value)
{
return Decode(value, EncodeType.HEX);
}
public string ToHex(string value)
{
return Encode(value, EncodeType.HEX);
}
}
public enum EncodeType
{
BASE64,
HEX,
ASCII,
UTF8,
}
@@ -9,11 +9,11 @@ using SPTarkov.Server.Core.Services;
namespace SPTarkov.Server.Core.Utils;
[Injectable]
public class HttpResponseUtil
public class HttpResponseUtil(
JsonUtil jsonUtil,
ServerLocalisationService serverLocalisationService
)
{
protected readonly JsonUtil _jsonUtil;
protected readonly ServerLocalisationService _serverLocalisationService;
protected readonly ImmutableList<Regex> _cleanupRegexList =
[
new("[\\b]"),
@@ -23,12 +23,6 @@ public class HttpResponseUtil
new("[\\t]"),
];
public HttpResponseUtil(JsonUtil jsonUtil, ServerLocalisationService localisationService)
{
_serverLocalisationService = localisationService;
_jsonUtil = jsonUtil;
}
protected string ClearString(string? s)
{
var value = s ?? "";
@@ -47,7 +41,7 @@ public class HttpResponseUtil
*/
public string NoBody<T>(T data)
{
return ClearString(_jsonUtil.Serialize(data));
return ClearString(jsonUtil.Serialize(data));
}
/**
@@ -75,7 +69,7 @@ public class HttpResponseUtil
string? errmsg = null
)
{
return _jsonUtil.Serialize(
return jsonUtil.Serialize(
new GetBodyResponseData<T>
{
Err = err,
@@ -115,7 +109,7 @@ public class HttpResponseUtil
{
if (string.IsNullOrEmpty(message))
{
message = _serverLocalisationService.GetText("http-unknown_error");
message = serverLocalisationService.GetText("http-unknown_error");
}
if (output.Warnings?.Count > 0)
@@ -9,12 +9,12 @@ namespace SPTarkov.Server.Core.Utils;
[Injectable(InjectionType.Singleton)]
public class JsonUtil
{
private static JsonSerializerOptions? jsonSerializerOptionsIndented;
private static JsonSerializerOptions jsonSerializerOptionsNoIndent;
private static JsonSerializerOptions? _jsonSerializerOptionsIndented;
private static JsonSerializerOptions? _jsonSerializerOptionsNoIndent;
public JsonUtil(IEnumerable<IJsonConverterRegistrator> registrators)
{
jsonSerializerOptionsNoIndent = new JsonSerializerOptions()
_jsonSerializerOptionsNoIndent = new JsonSerializerOptions()
{
WriteIndented = false,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
@@ -26,11 +26,11 @@ public class JsonUtil
{
foreach (var converter in registrator.GetJsonConverters())
{
jsonSerializerOptionsNoIndent.Converters.Add(converter);
_jsonSerializerOptionsNoIndent.Converters.Add(converter);
}
}
jsonSerializerOptionsIndented = new JsonSerializerOptions(jsonSerializerOptionsNoIndent)
_jsonSerializerOptionsIndented = new JsonSerializerOptions(_jsonSerializerOptionsNoIndent)
{
WriteIndented = true,
};
@@ -46,7 +46,7 @@ public class JsonUtil
{
return string.IsNullOrEmpty(json)
? default
: JsonSerializer.Deserialize<T>(json, jsonSerializerOptionsNoIndent);
: JsonSerializer.Deserialize<T>(json, _jsonSerializerOptionsNoIndent);
}
/// <summary>
@@ -59,7 +59,7 @@ public class JsonUtil
{
return string.IsNullOrEmpty(json)
? null
: JsonSerializer.Deserialize(json, type, jsonSerializerOptionsNoIndent);
: JsonSerializer.Deserialize(json, type, _jsonSerializerOptionsNoIndent);
}
/// <summary>
@@ -76,7 +76,7 @@ public class JsonUtil
using (FileStream fs = new(file, FileMode.Open, FileAccess.Read))
{
return JsonSerializer.Deserialize<T>(fs, jsonSerializerOptionsNoIndent);
return JsonSerializer.Deserialize<T>(fs, _jsonSerializerOptionsNoIndent);
}
}
@@ -101,7 +101,7 @@ public class JsonUtil
useAsync: true
);
return await JsonSerializer.DeserializeAsync<T>(fs, jsonSerializerOptionsNoIndent);
return await JsonSerializer.DeserializeAsync<T>(fs, _jsonSerializerOptionsNoIndent);
}
/// <summary>
@@ -119,7 +119,7 @@ public class JsonUtil
using (FileStream fs = new(file, FileMode.Open, FileAccess.Read))
{
return JsonSerializer.Deserialize(fs, type, jsonSerializerOptionsNoIndent);
return JsonSerializer.Deserialize(fs, type, _jsonSerializerOptionsNoIndent);
}
}
@@ -145,7 +145,7 @@ public class JsonUtil
useAsync: true
);
return await JsonSerializer.DeserializeAsync(fs, type, jsonSerializerOptionsNoIndent);
return await JsonSerializer.DeserializeAsync(fs, type, _jsonSerializerOptionsNoIndent);
}
/// <summary>
@@ -156,7 +156,7 @@ public class JsonUtil
/// <returns></returns>
public object? DeserializeFromFileStream(FileStream fs, Type type)
{
return JsonSerializer.Deserialize(fs, type, jsonSerializerOptionsNoIndent);
return JsonSerializer.Deserialize(fs, type, _jsonSerializerOptionsNoIndent);
}
/// <summary>
@@ -167,7 +167,7 @@ public class JsonUtil
/// <returns></returns>
public async Task<object?> DeserializeFromFileStreamAsync(FileStream fs, Type type)
{
return await JsonSerializer.DeserializeAsync(fs, type, jsonSerializerOptionsNoIndent);
return await JsonSerializer.DeserializeAsync(fs, type, _jsonSerializerOptionsNoIndent);
}
/// <summary>
@@ -177,7 +177,7 @@ public class JsonUtil
/// <returns>T</returns>
public async Task<T?> DeserializeFromMemoryStreamAsync<T>(MemoryStream ms)
{
return await JsonSerializer.DeserializeAsync<T>(ms, jsonSerializerOptionsNoIndent);
return await JsonSerializer.DeserializeAsync<T>(ms, _jsonSerializerOptionsNoIndent);
}
/// <summary>
@@ -193,7 +193,7 @@ public class JsonUtil
? null
: JsonSerializer.Serialize(
obj,
indented ? jsonSerializerOptionsIndented : jsonSerializerOptionsNoIndent
indented ? _jsonSerializerOptionsIndented : _jsonSerializerOptionsNoIndent
);
}
@@ -211,7 +211,7 @@ public class JsonUtil
: JsonSerializer.Serialize(
obj,
type,
indented ? jsonSerializerOptionsIndented : jsonSerializerOptionsNoIndent
indented ? _jsonSerializerOptionsIndented : _jsonSerializerOptionsNoIndent
);
}
}