Mongoid changes (#440)

* Remove old IsValidMongoId and Regex

* Convert more configs to MongoId, as well as BaseClasses

* Remove HashUtil.Generate(), replaced with new MongoId()
This commit is contained in:
Jesse
2025-07-03 16:42:16 +02:00
committed by GitHub
parent 020cfa7ab8
commit ebe8f9ded5
53 changed files with 159 additions and 291 deletions
@@ -1,6 +1,7 @@
using SPTarkov.DI.Annotations; using SPTarkov.DI.Annotations;
using SPTarkov.Server.Core.DI; using SPTarkov.Server.Core.DI;
using SPTarkov.Server.Core.Extensions; using SPTarkov.Server.Core.Extensions;
using SPTarkov.Server.Core.Models.Common;
using SPTarkov.Server.Core.Models.Eft.Profile; using SPTarkov.Server.Core.Models.Eft.Profile;
using SPTarkov.Server.Core.Models.Spt.Config; using SPTarkov.Server.Core.Models.Spt.Config;
using SPTarkov.Server.Core.Models.Utils; using SPTarkov.Server.Core.Models.Utils;
@@ -110,7 +111,7 @@ public class BtrDeliveryCallbacks(
foreach (var package in packagesToBeDelivered) foreach (var package in packagesToBeDelivered)
{ {
// Create a new root parent ID for the message we'll be sending the player // Create a new root parent ID for the message we'll be sending the player
var rootItemParentId = _hashUtil.Generate(); var rootItemParentId = new MongoId();
// Update the delivery items to have the new root parent ID for root/orphaned items // Update the delivery items to have the new root parent ID for root/orphaned items
package.Items = package.Items.AdoptOrphanedItems(rootItemParentId); package.Items = package.Items.AdoptOrphanedItems(rootItemParentId);
@@ -47,7 +47,7 @@ public class DialogueCallbacks(
{ {
new() new()
{ {
Id = _hashUtil.Generate(), Id = new Models.Common.MongoId(),
RegistrationId = 20, RegistrationId = 20,
DateTime = _timeUtil.GetTimeStamp(), DateTime = _timeUtil.GetTimeStamp(),
IsDeveloper = true, IsDeveloper = true,
@@ -122,7 +122,7 @@ public class InsuranceController(
foreach (var insured in insuranceDetails) foreach (var insured in insuranceDetails)
{ {
// Create a new root parent ID for the message we'll be sending the player // Create a new root parent ID for the message we'll be sending the player
var rootItemParentId = _hashUtil.Generate(); var rootItemParentId = new MongoId();
// Update the insured items to have the new root parent ID for root/orphaned items // Update the insured items to have the new root parent ID for root/orphaned items
insured.Items = insured.Items.AdoptOrphanedItems(rootItemParentId); insured.Items = insured.Items.AdoptOrphanedItems(rootItemParentId);
@@ -2064,7 +2064,7 @@ public class BotEquipmentModGenerator(
public HashSet<MongoId> FilterSightsByWeaponType( public HashSet<MongoId> FilterSightsByWeaponType(
Item weapon, Item weapon,
HashSet<MongoId> scopes, HashSet<MongoId> scopes,
Dictionary<string, List<string>> botWeaponSightWhitelist Dictionary<MongoId, List<MongoId>> botWeaponSightWhitelist
) )
{ {
var weaponDetails = _itemHelper.GetItem(weapon.Template); var weaponDetails = _itemHelper.GetItem(weapon.Template);
@@ -794,6 +794,7 @@ public class BotWeaponGenerator(
var magazineTemplate = _itemHelper.GetItem( var magazineTemplate = _itemHelper.GetItem(
magazineSlot.Props?.Filters.FirstOrDefault()?.Filter?.FirstOrDefault() magazineSlot.Props?.Filters.FirstOrDefault()?.Filter?.FirstOrDefault()
?? new MongoId(null)
); );
if (!magazineTemplate.Key) if (!magazineTemplate.Key)
{ {
@@ -542,7 +542,7 @@ public class LocationLootGenerator(
var containerTpl = containerClone.Template.Items.FirstOrDefault().Template; var containerTpl = containerClone.Template.Items.FirstOrDefault().Template;
// Create new unique parent id to prevent any collisions // Create new unique parent id to prevent any collisions
var parentId = _hashUtil.Generate(); var parentId = new MongoId();
containerClone.Template.Root = parentId; containerClone.Template.Root = parentId;
containerClone.Template.Items.FirstOrDefault().Id = parentId; containerClone.Template.Items.FirstOrDefault().Id = parentId;
@@ -180,7 +180,7 @@ public class LootGenerator(
/// </summary> /// </summary>
/// <param name="forcedLootToAdd">Dictionary of item tpls with minmax values</param> /// <param name="forcedLootToAdd">Dictionary of item tpls with minmax values</param>
/// <returns>Array of Item</returns> /// <returns>Array of Item</returns>
public List<List<Item>> CreateForcedLoot(Dictionary<string, MinMax<int>> forcedLootToAdd) public List<List<Item>> CreateForcedLoot(Dictionary<MongoId, MinMax<int>> forcedLootToAdd)
{ {
var result = new List<List<Item>>(); var result = new List<List<Item>>();
@@ -22,7 +22,7 @@ public class RagfairAssortGenerator(
{ {
protected readonly RagfairConfig RagfairConfig = configServer.GetConfig<RagfairConfig>(); protected readonly RagfairConfig RagfairConfig = configServer.GetConfig<RagfairConfig>();
protected readonly List<string> RagfairItemInvalidBaseTypes = protected readonly List<MongoId> RagfairItemInvalidBaseTypes =
[ [
BaseClasses.LOOT_CONTAINER, // Safe, barrel cache etc BaseClasses.LOOT_CONTAINER, // Safe, barrel cache etc
BaseClasses.STASH, // Player inventory stash BaseClasses.STASH, // Player inventory stash
@@ -149,7 +149,7 @@ public class RagfairOfferGenerator(
var offer = new RagfairOffer var offer = new RagfairOffer
{ {
Id = hashUtil.Generate(), Id = new MongoId(),
InternalId = offerCounter, InternalId = offerCounter,
User = CreateUserDataForFleaOffer(userId, ragfairServerHelper.IsTrader(userId)), User = CreateUserDataForFleaOffer(userId, ragfairServerHelper.IsTrader(userId)),
Root = rootItem.Id, Root = rootItem.Id,
@@ -34,10 +34,11 @@ public class BotWeaponGeneratorHelper(
double? chamberBulletCount = 0; double? chamberBulletCount = 0;
if (MagazineIsCylinderRelated(parentItem.Name)) if (MagazineIsCylinderRelated(parentItem.Name))
{ {
var firstSlotAmmoTpl = magTemplate var firstSlotAmmoTpl =
.Properties.Cartridges.FirstOrDefault() magTemplate
?.Props.Filters[0] .Properties.Cartridges.FirstOrDefault()
.Filter.FirstOrDefault(); ?.Props.Filters[0]
.Filter.FirstOrDefault() ?? new MongoId(null);
var ammoMaxStackSize = var ammoMaxStackSize =
_itemHelper.GetItem(firstSlotAmmoTpl).Value?.Properties?.StackMaxSize ?? 1; _itemHelper.GetItem(firstSlotAmmoTpl).Value?.Properties?.StackMaxSize ?? 1;
chamberBulletCount = chamberBulletCount =
@@ -1,18 +1,19 @@
using SPTarkov.DI.Annotations; using SPTarkov.DI.Annotations;
using SPTarkov.Server.Core.Models.Common;
namespace SPTarkov.Server.Core.Helpers namespace SPTarkov.Server.Core.Helpers
{ {
[Injectable] [Injectable]
public class CounterTrackerHelper public class CounterTrackerHelper
{ {
private Dictionary<string, int> _maxCounts = new(); private Dictionary<MongoId, int> _maxCounts = new();
private readonly Dictionary<string, int> _trackedCounts = new(); private readonly Dictionary<MongoId, int> _trackedCounts = new();
/// <summary> /// <summary>
/// Add dictionary of keys and their matching limits to track /// Add dictionary of keys and their matching limits to track
/// </summary> /// </summary>
/// <param name="maxCounts">Values to store</param> /// <param name="maxCounts">Values to store</param>
public void AddDataToTrack(Dictionary<string, int> maxCounts) public void AddDataToTrack(Dictionary<MongoId, int> maxCounts)
{ {
_maxCounts = maxCounts; _maxCounts = maxCounts;
} }
@@ -23,7 +24,7 @@ namespace SPTarkov.Server.Core.Helpers
/// <param name="key"></param> /// <param name="key"></param>
/// <param name="countToIncrementBy"></param> /// <param name="countToIncrementBy"></param>
/// <returns>True = above max count</returns> /// <returns>True = above max count</returns>
public bool IncrementCount(string key, int countToIncrementBy = 1) public bool IncrementCount(MongoId key, int countToIncrementBy = 1)
{ {
// Not tracked, skip // Not tracked, skip
if (!_maxCounts.Any() || !_maxCounts.ContainsKey(key)) if (!_maxCounts.Any() || !_maxCounts.ContainsKey(key))
@@ -143,7 +143,7 @@ public class ProfileSptCommand(
Id = new MongoId(), Id = new MongoId(),
Template = Money.ROUBLES, Template = Money.ROUBLES,
Upd = new Upd { StackObjectsCount = 1 }, Upd = new Upd { StackObjectsCount = 1 },
ParentId = _hashUtil.Generate(), ParentId = new MongoId(),
SlotId = "main", SlotId = "main",
}, },
], ],
@@ -111,7 +111,7 @@ public class TraderSptCommand(
Id = new MongoId(), Id = new MongoId(),
Template = Money.ROUBLES, Template = Money.ROUBLES,
Upd = new Upd { StackObjectsCount = 1 }, Upd = new Upd { StackObjectsCount = 1 },
ParentId = _hashUtil.Generate(), ParentId = new MongoId(),
SlotId = "main", SlotId = "main",
}, },
], ],
@@ -4,6 +4,7 @@ using System.Text.Json.Serialization;
using SPTarkov.Common.Extensions; using SPTarkov.Common.Extensions;
using SPTarkov.DI.Annotations; using SPTarkov.DI.Annotations;
using SPTarkov.Server.Core.Extensions; using SPTarkov.Server.Core.Extensions;
using SPTarkov.Server.Core.Models.Common;
using SPTarkov.Server.Core.Models.Eft.Common; using SPTarkov.Server.Core.Models.Eft.Common;
using SPTarkov.Server.Core.Models.Eft.Common.Tables; using SPTarkov.Server.Core.Models.Eft.Common.Tables;
using SPTarkov.Server.Core.Models.Eft.Inventory; using SPTarkov.Server.Core.Models.Eft.Inventory;
@@ -36,7 +37,7 @@ public class InventoryHelper(
ICloner _cloner ICloner _cloner
) )
{ {
private static readonly FrozenSet<string> _variableSizeItemTypes = private static readonly FrozenSet<MongoId> _variableSizeItemTypes =
[ [
BaseClasses.WEAPON, BaseClasses.WEAPON,
BaseClasses.FUNCTIONAL_MOD, BaseClasses.FUNCTIONAL_MOD,
@@ -962,8 +963,8 @@ public class InventoryHelper(
{ {
var inventoryItemHash = new InventoryItemHash var inventoryItemHash = new InventoryItemHash
{ {
ByItemId = new Dictionary<string, Item>(), ByItemId = new Dictionary<MongoId, Item>(),
ByParentId = new Dictionary<string, HashSet<Item>>(), ByParentId = new Dictionary<MongoId, HashSet<Item>>(),
}; };
foreach (var item in inventoryItems) foreach (var item in inventoryItems)
{ {
@@ -1367,7 +1368,7 @@ public class InventoryHelper(
public void ValidateInventoryUsesMongoIds(List<Item> itemsToValidate) public void ValidateInventoryUsesMongoIds(List<Item> itemsToValidate)
{ {
var errors = itemsToValidate var errors = itemsToValidate
.Where(item => !_hashUtil.IsValidMongoId(item.Id)) .Where(item => !item.Id.IsValidMongoId())
.Select(item => $"Id: {item.Id} - tpl: {item.Template}") .Select(item => $"Id: {item.Id} - tpl: {item.Template}")
.ToList(); .ToList();
foreach (var message in errors) foreach (var message in errors)
@@ -1411,8 +1412,8 @@ public class InventoryHelper(
public class InventoryItemHash public class InventoryItemHash
{ {
[JsonPropertyName("byItemId")] [JsonPropertyName("byItemId")]
public Dictionary<string, Item> ByItemId { get; set; } public Dictionary<MongoId, Item> ByItemId { get; set; }
[JsonPropertyName("byParentId")] [JsonPropertyName("byParentId")]
public Dictionary<string, HashSet<Item>> ByParentId { get; set; } public Dictionary<MongoId, HashSet<Item>> ByParentId { get; set; }
} }
@@ -28,7 +28,7 @@ public class ItemHelper(
ICloner _cloner ICloner _cloner
) )
{ {
protected static readonly FrozenSet<string> _defaultInvalidBaseTypes = protected static readonly FrozenSet<MongoId> _defaultInvalidBaseTypes =
[ [
BaseClasses.LOOT_CONTAINER, BaseClasses.LOOT_CONTAINER,
BaseClasses.MOB_CONTAINER, BaseClasses.MOB_CONTAINER,
@@ -97,7 +97,7 @@ public class ItemHelper(
"right_side_plate", "right_side_plate",
]; ];
protected static readonly FrozenSet<string> _armorSlotsThatCanHoldMods = protected static readonly FrozenSet<MongoId> _armorSlotsThatCanHoldMods =
[ [
BaseClasses.HEADWEAR, BaseClasses.HEADWEAR,
BaseClasses.VEST, BaseClasses.VEST,
@@ -283,7 +283,7 @@ public class ItemHelper(
/// <param name="tpl">Template id to check</param> /// <param name="tpl">Template id to check</param>
/// <param name="invalidBaseTypes">OPTIONAL - Base types deemed invalid</param> /// <param name="invalidBaseTypes">OPTIONAL - Base types deemed invalid</param>
/// <returns>true for items that may be in player possession and not quest items</returns> /// <returns>true for items that may be in player possession and not quest items</returns>
public bool IsValidItem(string tpl, ICollection<string>? invalidBaseTypes = null) public bool IsValidItem(MongoId tpl, ICollection<MongoId>? invalidBaseTypes = null)
{ {
var baseTypes = invalidBaseTypes ?? _defaultInvalidBaseTypes; var baseTypes = invalidBaseTypes ?? _defaultInvalidBaseTypes;
var itemDetails = GetItem(tpl); var itemDetails = GetItem(tpl);
@@ -306,7 +306,7 @@ public class ItemHelper(
/// <param name="tpl">Item template id to check</param> /// <param name="tpl">Item template id to check</param>
/// <param name="baseClassTpl">Baseclass to check for</param> /// <param name="baseClassTpl">Baseclass to check for</param>
/// <returns>is the tpl a descendant</returns> /// <returns>is the tpl a descendant</returns>
public bool IsOfBaseclass(MongoId tpl, string baseClassTpl) public bool IsOfBaseclass(MongoId tpl, MongoId baseClassTpl)
{ {
return _itemBaseClassService.ItemHasBaseClass(tpl, [baseClassTpl]); return _itemBaseClassService.ItemHasBaseClass(tpl, [baseClassTpl]);
} }
@@ -317,29 +317,11 @@ public class ItemHelper(
/// <param name="tpl">Item to check base classes of</param> /// <param name="tpl">Item to check base classes of</param>
/// <param name="baseClassTpls">Base classes to check for</param> /// <param name="baseClassTpls">Base classes to check for</param>
/// <returns>True if any supplied base classes match</returns> /// <returns>True if any supplied base classes match</returns>
public bool IsOfBaseclasses(string tpl, ICollection<string> baseClassTpls) public bool IsOfBaseclasses(MongoId tpl, ICollection<MongoId> baseClassTpls)
{ {
return _itemBaseClassService.ItemHasBaseClass(tpl, baseClassTpls); return _itemBaseClassService.ItemHasBaseClass(tpl, baseClassTpls);
} }
/// <summary>
/// Temporary until we have better MongoId handling
/// </summary>
/// <param name="tpl"></param>
/// <param name="baseClassTpls"></param>
/// <returns></returns>
public bool IsOfBaseclasses(string tpl, ICollection<MongoId> baseClassTpls)
{
List<string> MongoList = [];
foreach (var baseTpl in baseClassTpls)
{
MongoList.Add(baseTpl);
}
return _itemBaseClassService.ItemHasBaseClass(tpl, MongoList);
}
/// <summary> /// <summary>
/// Does the provided item have the chance to require soft armor inserts /// Does the provided item have the chance to require soft armor inserts
/// Only applies to helmets/vest/armors /// Only applies to helmets/vest/armors
@@ -347,7 +329,7 @@ public class ItemHelper(
/// </summary> /// </summary>
/// <param name="itemTpl">Tpl to check</param> /// <param name="itemTpl">Tpl to check</param>
/// <returns>Does item have the possibility ot need soft inserts</returns> /// <returns>Does item have the possibility ot need soft inserts</returns>
public bool ArmorItemCanHoldMods(string itemTpl) public bool ArmorItemCanHoldMods(MongoId itemTpl)
{ {
return IsOfBaseclasses(itemTpl, _armorSlotsThatCanHoldMods); return IsOfBaseclasses(itemTpl, _armorSlotsThatCanHoldMods);
} }
@@ -526,7 +508,7 @@ public class ItemHelper(
/// </summary> /// </summary>
/// <param name="itemTpl">template id to look up</param> /// <param name="itemTpl">template id to look up</param>
/// <returns>KvP, key = bool, value = template item object</returns> /// <returns>KvP, key = bool, value = template item object</returns>
public KeyValuePair<bool, TemplateItem?> GetItem(string itemTpl) public KeyValuePair<bool, TemplateItem?> GetItem(MongoId itemTpl)
{ {
// -> Gets item from <input: _tpl> // -> Gets item from <input: _tpl>
if (_databaseService.GetItems().TryGetValue(itemTpl, out var item)) if (_databaseService.GetItems().TryGetValue(itemTpl, out var item))
@@ -542,7 +524,7 @@ public class ItemHelper(
/// </summary> /// </summary>
/// <param name="itemTpl">Template id of the item to check</param> /// <param name="itemTpl">Template id of the item to check</param>
/// <returns>True if the item has slots</returns> /// <returns>True if the item has slots</returns>
public bool ItemHasSlots(string itemTpl) public bool ItemHasSlots(MongoId itemTpl)
{ {
if (_databaseService.GetItems().TryGetValue(itemTpl, out var item)) if (_databaseService.GetItems().TryGetValue(itemTpl, out var item))
{ {
@@ -1347,7 +1329,7 @@ public class ItemHelper(
/// <returns>ItemSize object (width and height)</returns> /// <returns>ItemSize object (width and height)</returns>
public ItemSize GetItemSize(ICollection<Item> items, MongoId rootItemId) public ItemSize GetItemSize(ICollection<Item> items, MongoId rootItemId)
{ {
var rootTemplate = GetItem(items.FirstOrDefault(x => x.Id == rootItemId)?.Template).Value; var rootTemplate = GetItem(items.FirstOrDefault(x => x.Id == rootItemId).Template).Value;
var width = rootTemplate.Properties.Width; var width = rootTemplate.Properties.Width;
var height = rootTemplate.Properties.Height; var height = rootTemplate.Properties.Height;
@@ -1,4 +1,5 @@
using SPTarkov.DI.Annotations; using SPTarkov.DI.Annotations;
using SPTarkov.Server.Core.Models.Common;
using SPTarkov.Server.Core.Models.Eft.Profile; using SPTarkov.Server.Core.Models.Eft.Profile;
using SPTarkov.Server.Core.Models.Eft.Ws; using SPTarkov.Server.Core.Models.Eft.Ws;
using SPTarkov.Server.Core.Models.Enums; using SPTarkov.Server.Core.Models.Enums;
@@ -76,7 +77,7 @@ public class NotificationSendHelper(
dialog.New += 1; dialog.New += 1;
var message = new Message var message = new Message
{ {
Id = _hashUtil.Generate(), Id = new MongoId(),
UserId = dialog.Id, UserId = dialog.Id,
MessageType = messageType, MessageType = messageType,
DateTime = _timeUtil.GetTimeStamp(), DateTime = _timeUtil.GetTimeStamp(),
@@ -1,4 +1,5 @@
using SPTarkov.DI.Annotations; using SPTarkov.DI.Annotations;
using SPTarkov.Server.Core.Models.Common;
using SPTarkov.Server.Core.Models.Eft.Profile; using SPTarkov.Server.Core.Models.Eft.Profile;
using SPTarkov.Server.Core.Models.Eft.Ws; using SPTarkov.Server.Core.Models.Eft.Ws;
using SPTarkov.Server.Core.Utils; using SPTarkov.Server.Core.Utils;
@@ -57,7 +58,7 @@ public class NotifierHelper(HttpServerHelper httpServerHelper, HashUtil hashUtil
return new WsRagfairNewRating return new WsRagfairNewRating
{ {
EventType = NotificationEventType.RagfairNewRating, EventType = NotificationEventType.RagfairNewRating,
EventIdentifier = hashUtil.Generate(), EventIdentifier = new MongoId(),
Rating = rating, Rating = rating,
IsRatingGrowing = isGrowing, IsRatingGrowing = isGrowing,
}; };
@@ -1,4 +1,5 @@
using SPTarkov.DI.Annotations; using SPTarkov.DI.Annotations;
using SPTarkov.Server.Core.Models.Common;
using SPTarkov.Server.Core.Models.Enums; using SPTarkov.Server.Core.Models.Enums;
using SPTarkov.Server.Core.Models.Spt.Config; using SPTarkov.Server.Core.Models.Spt.Config;
using SPTarkov.Server.Core.Servers; using SPTarkov.Server.Core.Servers;
@@ -10,7 +11,7 @@ public class PaymentHelper(ConfigServer configServer)
{ {
protected bool _addedCustomMoney; protected bool _addedCustomMoney;
protected readonly InventoryConfig _inventoryConfig = configServer.GetConfig<InventoryConfig>(); protected readonly InventoryConfig _inventoryConfig = configServer.GetConfig<InventoryConfig>();
protected readonly HashSet<string> _moneyTpls = protected readonly HashSet<MongoId> _moneyTpls =
[ [
Money.DOLLARS, Money.DOLLARS,
Money.EUROS, Money.EUROS,
@@ -23,7 +24,7 @@ public class PaymentHelper(ConfigServer configServer)
/// </summary> /// </summary>
/// <param name="tpl">Item Tpl to check</param> /// <param name="tpl">Item Tpl to check</param>
/// <returns></returns> /// <returns></returns>
public bool IsMoneyTpl(string tpl) public bool IsMoneyTpl(MongoId tpl)
{ {
// Add custom currency first time this method is accessed // Add custom currency first time this method is accessed
if (!_addedCustomMoney) if (!_addedCustomMoney)
@@ -1,6 +1,7 @@
using SPTarkov.Common.Extensions; using SPTarkov.Common.Extensions;
using SPTarkov.DI.Annotations; using SPTarkov.DI.Annotations;
using SPTarkov.Server.Core.Extensions; using SPTarkov.Server.Core.Extensions;
using SPTarkov.Server.Core.Models.Common;
using SPTarkov.Server.Core.Models.Eft.Common; using SPTarkov.Server.Core.Models.Eft.Common;
using SPTarkov.Server.Core.Models.Eft.Common.Tables; using SPTarkov.Server.Core.Models.Eft.Common.Tables;
using SPTarkov.Server.Core.Models.Eft.ItemEvent; using SPTarkov.Server.Core.Models.Eft.ItemEvent;
@@ -112,7 +113,7 @@ public class RagfairOfferHelper(
protected void CheckAndLockOfferFromPlayerTieredFlea( protected void CheckAndLockOfferFromPlayerTieredFlea(
TieredFlea tieredFlea, TieredFlea tieredFlea,
RagfairOffer offer, RagfairOffer offer,
List<string> tieredFleaLimitTypes, List<MongoId> tieredFleaLimitTypes,
int playerLevel int playerLevel
) )
{ {
@@ -819,7 +820,7 @@ public class RagfairOfferHelper(
// Create an item template item // Create an item template item
var requestedItem = new Item var requestedItem = new Item
{ {
Id = _hashUtil.Generate(), Id = new MongoId(),
Template = requirement.Template, Template = requirement.Template,
Upd = new Upd { StackObjectsCount = requirement.Count * boughtAmount }, Upd = new Upd { StackObjectsCount = requirement.Count * boughtAmount },
}; };
@@ -23,6 +23,11 @@ public readonly struct MongoId : IEquatable<MongoId>
return; return;
} }
if (id == "hideout")
{
throw new Exception("wtf");
}
if (id.Length != 24) if (id.Length != 24)
{ {
// TODO: Items.json root item has an empty parentId property // TODO: Items.json root item has an empty parentId property
@@ -1,4 +1,5 @@
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using SPTarkov.Server.Core.Models.Common;
namespace SPTarkov.Server.Core.Models.Eft.Dialog; namespace SPTarkov.Server.Core.Models.Eft.Dialog;
@@ -8,7 +9,7 @@ public record ChatServer
public Dictionary<string, object>? ExtensionData { get; set; } public Dictionary<string, object>? ExtensionData { get; set; }
[JsonPropertyName("_id")] [JsonPropertyName("_id")]
public string? Id { get; set; } public MongoId Id { get; set; }
[JsonPropertyName("RegistrationId")] [JsonPropertyName("RegistrationId")]
public int? RegistrationId { get; set; } public int? RegistrationId { get; set; }
@@ -70,7 +70,7 @@ public record Requirement
public Dictionary<string, object>? ExtensionData { get; set; } public Dictionary<string, object>? ExtensionData { get; set; }
[JsonPropertyName("templateId")] [JsonPropertyName("templateId")]
public string? TemplateId { get; set; } public MongoId TemplateId { get; set; }
[JsonPropertyName("count")] [JsonPropertyName("count")]
public int? Count { get; set; } public int? Count { get; set; }
@@ -458,10 +458,10 @@ public record AcceptedCultistReward
public long? Timestamp { get; set; } public long? Timestamp { get; set; }
[JsonPropertyName("sacrificeItems")] [JsonPropertyName("sacrificeItems")]
public List<string>? SacrificeItems { get; set; } public List<MongoId>? SacrificeItems { get; set; }
[JsonPropertyName("rewardItems")] [JsonPropertyName("rewardItems")]
public List<string>? RewardItems { get; set; } public List<MongoId>? RewardItems { get; set; }
} }
public record PendingPrestige public record PendingPrestige
@@ -8,12 +8,12 @@ public record Money
[JsonExtensionData] [JsonExtensionData]
public Dictionary<string, object>? ExtensionData { get; set; } public Dictionary<string, object>? ExtensionData { get; set; }
public static readonly MongoId ROUBLES = new MongoId("5449016a4bdc2d6f028b456f"); public static readonly MongoId ROUBLES = new("5449016a4bdc2d6f028b456f");
public static readonly MongoId EUROS = new MongoId("569668774bdc2da2298b4568"); public static readonly MongoId EUROS = new("569668774bdc2da2298b4568");
public static readonly MongoId DOLLARS = new MongoId("5696686a4bdc2da3298b456a"); public static readonly MongoId DOLLARS = new("5696686a4bdc2da3298b456a");
public static readonly MongoId GP = new MongoId("5d235b4d86f7742e017bc88a"); public static readonly MongoId GP = new("5d235b4d86f7742e017bc88a");
public static HashSet<string> GetMoneyTpls() public static HashSet<MongoId> GetMoneyTpls()
{ {
return [ROUBLES, EUROS, DOLLARS, GP]; return [ROUBLES, EUROS, DOLLARS, GP];
} }
@@ -121,7 +121,7 @@ public record BotModLimits
public int? ScopeMax { get; set; } public int? ScopeMax { get; set; }
[JsonPropertyName("scopeBaseTypes")] [JsonPropertyName("scopeBaseTypes")]
public List<string>? ScopeBaseTypes { get; set; } public List<MongoId>? ScopeBaseTypes { get; set; }
[JsonPropertyName("flashlightLaser")] [JsonPropertyName("flashlightLaser")]
public ItemCount? FlashlightLaser { get; set; } public ItemCount? FlashlightLaser { get; set; }
@@ -130,7 +130,7 @@ public record BotModLimits
public int? FlashlightLaserMax { get; set; } public int? FlashlightLaserMax { get; set; }
[JsonPropertyName("flashlightLaserBaseTypes")] [JsonPropertyName("flashlightLaserBaseTypes")]
public List<string>? FlashlightLaserBaseTypes { get; set; } public List<MongoId>? FlashlightLaserBaseTypes { get; set; }
} }
public record ItemCount public record ItemCount
@@ -30,7 +30,7 @@ public record ModToSpawnRequest
/// Parent slot the item will be a part of /// Parent slot the item will be a part of
/// </summary> /// </summary>
[JsonPropertyName("botWeaponSightWhitelist")] [JsonPropertyName("botWeaponSightWhitelist")]
public Dictionary<string, List<string>>? BotWeaponSightWhitelist { get; set; } public Dictionary<MongoId, List<MongoId>>? BotWeaponSightWhitelist { get; set; }
/// <summary> /// <summary>
/// Blacklist to prevent mods from being picked /// Blacklist to prevent mods from being picked
@@ -19,7 +19,7 @@ public record AirdropConfig : BaseConfig
public required Dictionary<string, AirdropLoot> Loot { get; set; } public required Dictionary<string, AirdropLoot> Loot { get; set; }
[JsonPropertyName("customAirdropMapping")] [JsonPropertyName("customAirdropMapping")]
public required Dictionary<string, SptAirdropTypeEnum> CustomAirdropMapping { get; set; } public required Dictionary<MongoId, SptAirdropTypeEnum> CustomAirdropMapping { get; set; }
} }
/// <summary> /// <summary>
@@ -98,7 +98,7 @@ public record AirdropLoot
public bool UseForcedLoot { get; set; } public bool UseForcedLoot { get; set; }
[JsonPropertyName("forcedLoot")] [JsonPropertyName("forcedLoot")]
public Dictionary<string, MinMax<int>>? ForcedLoot { get; set; } public Dictionary<MongoId, MinMax<int>>? ForcedLoot { get; set; }
[JsonPropertyName("useRewardItemBlacklist")] [JsonPropertyName("useRewardItemBlacklist")]
public bool UseRewardItemBlacklist { get; set; } public bool UseRewardItemBlacklist { get; set; }
@@ -355,7 +355,7 @@ public record EquipmentFilters
/// Whitelist for weapon sight types allowed per gun /// Whitelist for weapon sight types allowed per gun
/// </summary> /// </summary>
[JsonPropertyName("weaponSightWhitelist")] [JsonPropertyName("weaponSightWhitelist")]
public Dictionary<string, List<string>> WeaponSightWhitelist { get; set; } public Dictionary<MongoId, List<MongoId>> WeaponSightWhitelist { get; set; }
/// <summary> /// <summary>
/// Chance face shield is down/active /// Chance face shield is down/active
@@ -55,16 +55,16 @@ public record HideoutCraftToAdd
/// The new mongoId for the craft to use /// The new mongoId for the craft to use
/// </summary> /// </summary>
[JsonPropertyName("newId")] [JsonPropertyName("newId")]
public required string NewId { get; set; } public required MongoId NewId { get; set; }
[JsonPropertyName("requirements")] [JsonPropertyName("requirements")]
public required List<Requirement> Requirements { get; set; } public required List<Requirement> Requirements { get; set; }
[JsonPropertyName("craftIdToCopy")] [JsonPropertyName("craftIdToCopy")]
public required string CraftIdToCopy { get; set; } public required MongoId CraftIdToCopy { get; set; }
[JsonPropertyName("craftOutputTpl")] [JsonPropertyName("craftOutputTpl")]
public required string CraftOutputTpl { get; set; } public required MongoId CraftOutputTpl { get; set; }
} }
public record CultistCircleSettings public record CultistCircleSettings
@@ -157,10 +157,10 @@ public record DirectRewardSettings
public Dictionary<string, object>? ExtensionData { get; set; } public Dictionary<string, object>? ExtensionData { get; set; }
[JsonPropertyName("reward")] [JsonPropertyName("reward")]
public required List<string> Reward { get; set; } public required List<MongoId> Reward { get; set; }
[JsonPropertyName("requiredItems")] [JsonPropertyName("requiredItems")]
public required List<string> RequiredItems { get; set; } public required List<MongoId> RequiredItems { get; set; }
[JsonPropertyName("craftTimeSeconds")] [JsonPropertyName("craftTimeSeconds")]
public required int CraftTimeSeconds { get; set; } public required int CraftTimeSeconds { get; set; }
@@ -1,4 +1,5 @@
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using SPTarkov.Server.Core.Models.Common;
namespace SPTarkov.Server.Core.Models.Spt.Config; namespace SPTarkov.Server.Core.Models.Spt.Config;
@@ -11,7 +12,7 @@ public record InsuranceConfig : BaseConfig
/// Chance item is returned as insurance, keyed by trader id /// Chance item is returned as insurance, keyed by trader id
/// </summary> /// </summary>
[JsonPropertyName("returnChancePercent")] [JsonPropertyName("returnChancePercent")]
public Dictionary<string, double> ReturnChancePercent { get; set; } = []; public Dictionary<MongoId, double> ReturnChancePercent { get; set; } = [];
/// <summary> /// <summary>
/// Item slots that should never be returned as insurance /// Item slots that should never be returned as insurance
@@ -15,7 +15,7 @@ public record InventoryConfig : BaseConfig
public bool NewItemsMarkedFound { get; set; } public bool NewItemsMarkedFound { get; set; }
[JsonPropertyName("randomLootContainers")] [JsonPropertyName("randomLootContainers")]
public required Dictionary<string, RewardDetails> RandomLootContainers { get; set; } public required Dictionary<MongoId, RewardDetails> RandomLootContainers { get; set; }
[JsonPropertyName("sealedAirdropContainer")] [JsonPropertyName("sealedAirdropContainer")]
public required SealedAirdropContainerSettings SealedAirdropContainer { get; set; } public required SealedAirdropContainerSettings SealedAirdropContainer { get; set; }
@@ -24,7 +24,7 @@ public record InventoryConfig : BaseConfig
/// Contains item tpls that the server should consider money and treat the same as roubles/euros/dollars /// Contains item tpls that the server should consider money and treat the same as roubles/euros/dollars
/// </summary> /// </summary>
[JsonPropertyName("customMoneyTpls")] [JsonPropertyName("customMoneyTpls")]
public required List<string> CustomMoneyTpls { get; set; } public required List<MongoId> CustomMoneyTpls { get; set; }
/// <summary> /// <summary>
/// Multipliers for skill gain when inside menus, NOT in-game /// Multipliers for skill gain when inside menus, NOT in-game
@@ -36,7 +36,7 @@ public record InventoryConfig : BaseConfig
/// Container Tpls that should be deprioritised when choosing where to take money from for payments /// Container Tpls that should be deprioritised when choosing where to take money from for payments
/// </summary> /// </summary>
[JsonPropertyName("deprioritisedMoneyContainers")] [JsonPropertyName("deprioritisedMoneyContainers")]
public required HashSet<string> DeprioritisedMoneyContainers { get; set; } public required HashSet<MongoId> DeprioritisedMoneyContainers { get; set; }
} }
public record RewardDetails public record RewardDetails
@@ -54,7 +54,7 @@ public record RewardDetails
public bool FoundInRaid { get; set; } public bool FoundInRaid { get; set; }
[JsonPropertyName("rewardTplPool")] [JsonPropertyName("rewardTplPool")]
public Dictionary<string, double>? RewardTplPool { get; set; } public Dictionary<MongoId, double>? RewardTplPool { get; set; }
[JsonPropertyName("rewardTypePool")] [JsonPropertyName("rewardTypePool")]
public List<MongoId>? RewardTypePool { get; set; } public List<MongoId>? RewardTypePool { get; set; }
@@ -78,13 +78,13 @@ public record SealedAirdropContainerSettings
public bool FoundInRaid { get; set; } public bool FoundInRaid { get; set; }
[JsonPropertyName("weaponModRewardLimits")] [JsonPropertyName("weaponModRewardLimits")]
public required Dictionary<string, MinMax<int>> WeaponModRewardLimits { get; set; } public required Dictionary<MongoId, MinMax<int>> WeaponModRewardLimits { get; set; }
[JsonPropertyName("rewardTypeLimits")] [JsonPropertyName("rewardTypeLimits")]
public required Dictionary<string, MinMax<int>> RewardTypeLimits { get; set; } public required Dictionary<MongoId, MinMax<int>> RewardTypeLimits { get; set; }
[JsonPropertyName("ammoBoxWhitelist")] [JsonPropertyName("ammoBoxWhitelist")]
public required List<string> AmmoBoxWhitelist { get; set; } public required List<MongoId> AmmoBoxWhitelist { get; set; }
[JsonPropertyName("allowBossItems")] [JsonPropertyName("allowBossItems")]
public bool AllowBossItems { get; set; } public bool AllowBossItems { get; set; }
@@ -37,7 +37,7 @@ public record LocationConfig : BaseConfig
/// Key = map id, value = dict of item tpls that should only have x forced loot spawn position /// Key = map id, value = dict of item tpls that should only have x forced loot spawn position
/// </summary> /// </summary>
[JsonPropertyName("lootMaxSpawnLimits")] [JsonPropertyName("lootMaxSpawnLimits")]
public required Dictionary<string, Dictionary<string, int>> LootMaxSpawnLimits { get; set; } public required Dictionary<string, Dictionary<MongoId, int>> LootMaxSpawnLimits { get; set; }
/// <summary> /// <summary>
/// How many attempts should be taken to fit an item into a container before giving up /// How many attempts should be taken to fit an item into a container before giving up
@@ -130,7 +130,7 @@ public record LocationConfig : BaseConfig
/// Containers to remove all children from when generating static/loose loot /// Containers to remove all children from when generating static/loose loot
/// </summary> /// </summary>
[JsonPropertyName("tplsToStripChildItemsFrom")] [JsonPropertyName("tplsToStripChildItemsFrom")]
public required HashSet<string> TplsToStripChildItemsFrom { get; set; } public required HashSet<MongoId> TplsToStripChildItemsFrom { get; set; }
/// <summary> /// <summary>
/// Map ids players cannot visit /// Map ids players cannot visit
@@ -232,7 +232,7 @@ public record ContainerRandomisationSettings
/// Some container types don't work when randomised /// Some container types don't work when randomised
/// </summary> /// </summary>
[JsonPropertyName("containerTypesToNotRandomise")] [JsonPropertyName("containerTypesToNotRandomise")]
public required HashSet<string> ContainerTypesToNotRandomise { get; set; } public required HashSet<MongoId> ContainerTypesToNotRandomise { get; set; }
[JsonPropertyName("containerGroupMinSizeMultiplier")] [JsonPropertyName("containerGroupMinSizeMultiplier")]
public double ContainerGroupMinSizeMultiplier { get; set; } public double ContainerGroupMinSizeMultiplier { get; set; }
@@ -1,4 +1,5 @@
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using SPTarkov.Server.Core.Models.Common;
using SPTarkov.Server.Core.Models.Eft.Common.Tables; using SPTarkov.Server.Core.Models.Eft.Common.Tables;
using SPTarkov.Server.Core.Models.Enums; using SPTarkov.Server.Core.Models.Enums;
@@ -28,7 +29,7 @@ public record KarmaLevel
public required Dictionary<string, GenerationData> ItemLimits { get; set; } public required Dictionary<string, GenerationData> ItemLimits { get; set; }
[JsonPropertyName("equipmentBlacklist")] [JsonPropertyName("equipmentBlacklist")]
public required Dictionary<EquipmentSlots, List<string>> EquipmentBlacklist { get; set; } public required Dictionary<EquipmentSlots, List<MongoId>> EquipmentBlacklist { get; set; }
[JsonPropertyName("labsAccessCardChancePercent")] [JsonPropertyName("labsAccessCardChancePercent")]
public double? LabsAccessCardChancePercent { get; set; } public double? LabsAccessCardChancePercent { get; set; }
@@ -30,7 +30,7 @@ public record RagfairConfig : BaseConfig
/// Trader ids + should their assorts be listed on flea /// Trader ids + should their assorts be listed on flea
/// </summary> /// </summary>
[JsonPropertyName("traders")] [JsonPropertyName("traders")]
public Dictionary<string, bool> Traders { get; set; } public required Dictionary<MongoId, bool> Traders { get; set; }
[JsonPropertyName("dynamic")] [JsonPropertyName("dynamic")]
public Dynamic Dynamic { get; set; } public Dynamic Dynamic { get; set; }
@@ -159,7 +159,7 @@ public record Dynamic
/// Tpls that should not use the variable price system when their quality is less than 100% (lower dura/uses = lower price) /// Tpls that should not use the variable price system when their quality is less than 100% (lower dura/uses = lower price)
/// </summary> /// </summary>
[JsonPropertyName("ignoreQualityPriceVarianceBlacklist")] [JsonPropertyName("ignoreQualityPriceVarianceBlacklist")]
public HashSet<string> IgnoreQualityPriceVarianceBlacklist { get; set; } public HashSet<MongoId> IgnoreQualityPriceVarianceBlacklist { get; set; }
[JsonPropertyName("endTimeSeconds")] [JsonPropertyName("endTimeSeconds")]
public MinMax<int> EndTimeSeconds { get; set; } public MinMax<int> EndTimeSeconds { get; set; }
@@ -198,7 +198,7 @@ public record Dynamic
/// A multipler to apply to individual tpls price just prior to item quality adjustment /// A multipler to apply to individual tpls price just prior to item quality adjustment
/// </summary> /// </summary>
[JsonPropertyName("itemPriceMultiplier")] [JsonPropertyName("itemPriceMultiplier")]
public Dictionary<string, double>? ItemPriceMultiplier { get; set; } public Dictionary<MongoId, double>? ItemPriceMultiplier { get; set; }
[JsonPropertyName("_currencies")] [JsonPropertyName("_currencies")]
public string? CurrenciesDescription { get; set; } public string? CurrenciesDescription { get; set; }
@@ -207,13 +207,13 @@ public record Dynamic
/// Percentages to sell offers in each currency /// Percentages to sell offers in each currency
/// </summary> /// </summary>
[JsonPropertyName("currencies")] [JsonPropertyName("currencies")]
public Dictionary<string, double> Currencies { get; set; } public Dictionary<MongoId, double> Currencies { get; set; }
/// <summary> /// <summary>
/// Item tpls that should be forced to sell as a single item /// Item tpls that should be forced to sell as a single item
/// </summary> /// </summary>
[JsonPropertyName("showAsSingleStack")] [JsonPropertyName("showAsSingleStack")]
public HashSet<string> ShowAsSingleStack { get; set; } public HashSet<MongoId> ShowAsSingleStack { get; set; }
/// <summary> /// <summary>
/// Should christmas/halloween items be removed from flea when not within the seasonal bounds /// Should christmas/halloween items be removed from flea when not within the seasonal bounds
@@ -231,13 +231,13 @@ public record Dynamic
/// Dict of price limits keyed by item type /// Dict of price limits keyed by item type
/// </summary> /// </summary>
[JsonPropertyName("unreasonableModPrices")] [JsonPropertyName("unreasonableModPrices")]
public Dictionary<string, UnreasonableModPrices> UnreasonableModPrices { get; set; } public Dictionary<MongoId, UnreasonableModPrices> UnreasonableModPrices { get; set; }
/// <summary> /// <summary>
/// Custom rouble prices for items to override values from prices.json /// Custom rouble prices for items to override values from prices.json
/// </summary> /// </summary>
[JsonPropertyName("itemPriceOverrideRouble")] [JsonPropertyName("itemPriceOverrideRouble")]
public Dictionary<string, double> ItemPriceOverrideRouble { get; set; } public Dictionary<MongoId, double> ItemPriceOverrideRouble { get; set; }
} }
public record PriceRanges public record PriceRanges
@@ -300,7 +300,7 @@ public record BarterDetails
/// Item Tpls to never be turned into a barter /// Item Tpls to never be turned into a barter
/// </summary> /// </summary>
[JsonPropertyName("itemTypeBlacklist")] [JsonPropertyName("itemTypeBlacklist")]
public HashSet<string> ItemTypeBlacklist { get; set; } public HashSet<MongoId> ItemTypeBlacklist { get; set; }
} }
public record PackDetails public record PackDetails
@@ -330,7 +330,7 @@ public record PackDetails
/// item types to allow being a pack /// item types to allow being a pack
/// </summary> /// </summary>
[JsonPropertyName("itemTypeWhitelist")] [JsonPropertyName("itemTypeWhitelist")]
public HashSet<string> ItemTypeWhitelist { get; set; } public HashSet<MongoId> ItemTypeWhitelist { get; set; }
} }
public record OfferAdjustment public record OfferAdjustment
@@ -399,7 +399,7 @@ public record RagfairBlacklist
/// Custom blacklist for item Tpls /// Custom blacklist for item Tpls
/// </summary> /// </summary>
[JsonPropertyName("custom")] [JsonPropertyName("custom")]
public HashSet<string> Custom { get; set; } public HashSet<MongoId> Custom { get; set; }
/// <summary> /// <summary>
/// BSG blacklist a large number of items from flea, true = use blacklist /// BSG blacklist a large number of items from flea, true = use blacklist
@@ -513,16 +513,16 @@ public record TieredFlea
/// key: tpl, value: playerlevel /// key: tpl, value: playerlevel
/// </summary> /// </summary>
[JsonPropertyName("unlocksTpl")] [JsonPropertyName("unlocksTpl")]
public Dictionary<string, int> UnlocksTpl { get; set; } public Dictionary<MongoId, int> UnlocksTpl { get; set; }
/// <summary> /// <summary>
/// key: item type id, value: playerlevel /// key: item type id, value: playerlevel
/// </summary> /// </summary>
[JsonPropertyName("unlocksType")] [JsonPropertyName("unlocksType")]
public Dictionary<string, int> UnlocksType { get; set; } public Dictionary<MongoId, int> UnlocksType { get; set; }
[JsonPropertyName("ammoTplUnlocks")] [JsonPropertyName("ammoTplUnlocks")]
public Dictionary<string, int>? AmmoTplUnlocks { get; set; } public Dictionary<MongoId, int>? AmmoTplUnlocks { get; set; }
[JsonPropertyName("ammoTiersEnabled")] [JsonPropertyName("ammoTiersEnabled")]
public bool AmmoTiersEnabled { get; set; } public bool AmmoTiersEnabled { get; set; }
@@ -18,10 +18,10 @@ public record ScavCaseConfig : BaseConfig
public required AmmoRewards AmmoRewards { get; set; } public required AmmoRewards AmmoRewards { get; set; }
[JsonPropertyName("rewardItemParentBlacklist")] [JsonPropertyName("rewardItemParentBlacklist")]
public required HashSet<string> RewardItemParentBlacklist { get; set; } public required HashSet<MongoId> RewardItemParentBlacklist { get; set; }
[JsonPropertyName("rewardItemBlacklist")] [JsonPropertyName("rewardItemBlacklist")]
public required HashSet<string> RewardItemBlacklist { get; set; } public required HashSet<MongoId> RewardItemBlacklist { get; set; }
[JsonPropertyName("allowMultipleMoneyRewardsPerRarity")] [JsonPropertyName("allowMultipleMoneyRewardsPerRarity")]
public bool AllowMultipleMoneyRewardsPerRarity { get; set; } public bool AllowMultipleMoneyRewardsPerRarity { get; set; }
@@ -78,7 +78,7 @@ public record AmmoRewards
public int AmmoRewardChancePercent { get; set; } public int AmmoRewardChancePercent { get; set; }
[JsonPropertyName("ammoRewardBlacklist")] [JsonPropertyName("ammoRewardBlacklist")]
public required Dictionary<string, List<string>> AmmoRewardBlacklist { get; set; } public required Dictionary<string, List<MongoId>> AmmoRewardBlacklist { get; set; }
[JsonPropertyName("ammoRewardValueRangeRub")] [JsonPropertyName("ammoRewardValueRangeRub")]
public required Dictionary<string, MinMax<double>> AmmoRewardValueRangeRub { get; set; } public required Dictionary<string, MinMax<double>> AmmoRewardValueRangeRub { get; set; }
@@ -1,4 +1,5 @@
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using SPTarkov.Server.Core.Models.Common;
using SPTarkov.Server.Core.Models.Eft.Common; using SPTarkov.Server.Core.Models.Eft.Common;
using SPTarkov.Server.Core.Models.Enums; using SPTarkov.Server.Core.Models.Enums;
using SPTarkov.Server.Core.Utils.Json.Converters; using SPTarkov.Server.Core.Utils.Json.Converters;
@@ -19,7 +20,7 @@ public record SeasonalEventConfig : BaseConfig
[JsonPropertyName("eventGear")] [JsonPropertyName("eventGear")]
public required Dictionary< public required Dictionary<
SeasonalEventType, SeasonalEventType,
Dictionary<string, Dictionary<string, Dictionary<string, int>>> Dictionary<string, Dictionary<string, Dictionary<MongoId, int>>>
> EventGear { get; set; } > EventGear { get; set; }
/// <summary> /// <summary>
@@ -28,7 +29,7 @@ public record SeasonalEventConfig : BaseConfig
[JsonPropertyName("eventLoot")] [JsonPropertyName("eventLoot")]
public required Dictionary< public required Dictionary<
SeasonalEventType, SeasonalEventType,
Dictionary<string, Dictionary<string, Dictionary<string, int>>> Dictionary<string, Dictionary<string, Dictionary<MongoId, int>>>
> EventLoot { get; set; } > EventLoot { get; set; }
[JsonPropertyName("events")] [JsonPropertyName("events")]
@@ -70,7 +71,7 @@ public record SeasonalEventConfig : BaseConfig
[JsonPropertyName("botAppearanceChanges")] [JsonPropertyName("botAppearanceChanges")]
public required Dictionary< public required Dictionary<
SeasonalEventType, SeasonalEventType,
Dictionary<string, Dictionary<string, Dictionary<string, int>>> Dictionary<string, Dictionary<string, Dictionary<MongoId, int>>>
> BotAppearanceChanges { get; set; } > BotAppearanceChanges { get; set; }
} }
@@ -43,7 +43,7 @@ public record UpdateTime
public string Name { get; set; } = string.Empty; public string Name { get; set; } = string.Empty;
[JsonPropertyName("traderId")] [JsonPropertyName("traderId")]
public string TraderId { get; set; } = string.Empty; public MongoId TraderId { get; set; } = string.Empty;
/// <summary> /// <summary>
/// Seconds between trader resets /// Seconds between trader resets
@@ -97,16 +97,16 @@ public record FenceConfig
/// Key: item tpl /// Key: item tpl
/// </summary> /// </summary>
[JsonPropertyName("itemStackSizeOverrideMinMax")] [JsonPropertyName("itemStackSizeOverrideMinMax")]
public required Dictionary<string, MinMax<int>?> ItemStackSizeOverrideMinMax { get; set; } public required Dictionary<MongoId, MinMax<int>?> ItemStackSizeOverrideMinMax { get; set; }
[JsonPropertyName("itemTypeLimits")] [JsonPropertyName("itemTypeLimits")]
public required Dictionary<string, int> ItemTypeLimits { get; set; } public required Dictionary<MongoId, int> ItemTypeLimits { get; set; }
/// <summary> /// <summary>
/// Prevent duplicate offers of items of specific categories by parentId /// Prevent duplicate offers of items of specific categories by parentId
/// </summary> /// </summary>
[JsonPropertyName("preventDuplicateOffersOfCategory")] [JsonPropertyName("preventDuplicateOffersOfCategory")]
public required List<string> PreventDuplicateOffersOfCategory { get; set; } public required List<MongoId> PreventDuplicateOffersOfCategory { get; set; }
[JsonPropertyName("regenerateAssortsOnRefresh")] [JsonPropertyName("regenerateAssortsOnRefresh")]
public bool RegenerateAssortsOnRefresh { get; set; } public bool RegenerateAssortsOnRefresh { get; set; }
@@ -115,7 +115,7 @@ public record FenceConfig
/// Max rouble price before item is not listed on flea /// Max rouble price before item is not listed on flea
/// </summary> /// </summary>
[JsonPropertyName("itemCategoryRoublePriceLimit")] [JsonPropertyName("itemCategoryRoublePriceLimit")]
public required Dictionary<string, double?> ItemCategoryRoublePriceLimit { get; set; } public required Dictionary<MongoId, double?> ItemCategoryRoublePriceLimit { get; set; }
/// <summary> /// <summary>
/// Each slotid with % to be removed prior to listing on fence /// Each slotid with % to be removed prior to listing on fence
@@ -136,7 +136,7 @@ public record FenceConfig
public double AmmoMaxPenLimit { get; set; } public double AmmoMaxPenLimit { get; set; }
[JsonPropertyName("blacklist")] [JsonPropertyName("blacklist")]
public required HashSet<string> Blacklist { get; set; } public required HashSet<MongoId> Blacklist { get; set; }
[JsonPropertyName("coopExtractGift")] [JsonPropertyName("coopExtractGift")]
public required CoopExtractReward CoopExtractGift { get; set; } public required CoopExtractReward CoopExtractGift { get; set; }
@@ -82,7 +82,7 @@ public record LootRequest
/// Item tpls + count of items to force include /// Item tpls + count of items to force include
/// </summary> /// </summary>
[JsonPropertyName("forcedLoot")] [JsonPropertyName("forcedLoot")]
public Dictionary<string, MinMax<int>>? ForcedLoot { get; set; } public Dictionary<MongoId, MinMax<int>>? ForcedLoot { get; set; }
/// <summary> /// <summary>
/// Should seasonal items appear when it's not the season for them /// Should seasonal items appear when it's not the season for them
@@ -77,7 +77,7 @@ public class RagfairServer(
/// Get traders who need to be periodically refreshed /// Get traders who need to be periodically refreshed
/// </summary> /// </summary>
/// <returns> List of traders </returns> /// <returns> List of traders </returns>
public List<string> GetUpdateableTraders() public List<MongoId> GetUpdateableTraders()
{ {
return _ragfairConfig.Traders.Keys.ToList(); return _ragfairConfig.Traders.Keys.ToList();
} }
@@ -1,6 +1,7 @@
using SPTarkov.Common.Extensions; using SPTarkov.Common.Extensions;
using SPTarkov.DI.Annotations; using SPTarkov.DI.Annotations;
using SPTarkov.Server.Core.Helpers; using SPTarkov.Server.Core.Helpers;
using SPTarkov.Server.Core.Models.Common;
using SPTarkov.Server.Core.Models.Eft.Common.Tables; using SPTarkov.Server.Core.Models.Eft.Common.Tables;
using SPTarkov.Server.Core.Models.Enums; using SPTarkov.Server.Core.Models.Enums;
using SPTarkov.Server.Core.Models.Spt.Bots; using SPTarkov.Server.Core.Models.Spt.Bots;
@@ -160,7 +161,7 @@ public class BotEquipmentFilterService(
/// </summary> /// </summary>
/// <param name="botEquipmentRole">equipment role of bot to look up</param> /// <param name="botEquipmentRole">equipment role of bot to look up</param>
/// <returns>Dictionary of weapon type and their whitelisted scope types</returns> /// <returns>Dictionary of weapon type and their whitelisted scope types</returns>
public Dictionary<string, List<string>> GetBotWeaponSightWhitelist(string botEquipmentRole) public Dictionary<MongoId, List<MongoId>> GetBotWeaponSightWhitelist(string botEquipmentRole)
{ {
var botEquipmentSettings = _botConfig.Equipment[botEquipmentRole]; var botEquipmentSettings = _botConfig.Equipment[botEquipmentRole];
@@ -1,4 +1,5 @@
using SPTarkov.DI.Annotations; using SPTarkov.DI.Annotations;
using SPTarkov.Server.Core.Models.Common;
using SPTarkov.Server.Core.Models.Eft.Common.Tables; using SPTarkov.Server.Core.Models.Eft.Common.Tables;
using SPTarkov.Server.Core.Models.Eft.Match; using SPTarkov.Server.Core.Models.Eft.Match;
using SPTarkov.Server.Core.Models.Eft.Profile; using SPTarkov.Server.Core.Models.Eft.Profile;
@@ -82,7 +83,7 @@ public class BtrDeliveryService(
.BtrDeliveryList.Add( .BtrDeliveryList.Add(
new BtrDelivery new BtrDelivery
{ {
Id = _hashUtil.Generate(), Id = new MongoId(),
ScheduledTime = (int)GetBTRDeliveryReturnTimestamp(), ScheduledTime = (int)GetBTRDeliveryReturnTimestamp(),
Items = items, Items = items,
} }
@@ -1,6 +1,7 @@
using System.Diagnostics; using System.Diagnostics;
using SPTarkov.Common.Extensions; using SPTarkov.Common.Extensions;
using SPTarkov.DI.Annotations; using SPTarkov.DI.Annotations;
using SPTarkov.Server.Core.Extensions;
using SPTarkov.Server.Core.Models.Common; using SPTarkov.Server.Core.Models.Common;
using SPTarkov.Server.Core.Models.Eft.Common; using SPTarkov.Server.Core.Models.Eft.Common;
using SPTarkov.Server.Core.Models.Eft.Common.Tables; using SPTarkov.Server.Core.Models.Eft.Common.Tables;
@@ -414,7 +415,7 @@ public class DatabaseService(
{ {
foreach (var keyValuePair in table) foreach (var keyValuePair in table)
{ {
if (!_hashUtil.IsValidMongoId(keyValuePair.Key)) if (!keyValuePair.Key.IsValidMongoId())
{ {
_logger.Error($"Invalid {tableType} ID: '{keyValuePair.Key}'"); _logger.Error($"Invalid {tableType} ID: '{keyValuePair.Key}'");
return false; return false;
@@ -428,7 +429,7 @@ public class DatabaseService(
{ {
foreach (var keyValuePair in table) foreach (var keyValuePair in table)
{ {
if (!_hashUtil.IsValidMongoId(keyValuePair.Key)) if (!keyValuePair.Key.IsValidMongoId())
{ {
_logger.Error($"Invalid {tableType} ID: '{keyValuePair.Key}'"); _logger.Error($"Invalid {tableType} ID: '{keyValuePair.Key}'");
return false; return false;
@@ -782,7 +782,7 @@ public class FenceService(
int? assortCount, int? assortCount,
CreateFenceAssortsResult assorts, CreateFenceAssortsResult assorts,
TraderAssort baseFenceAssortClone, TraderAssort baseFenceAssortClone,
Dictionary<string, (int current, int max)> itemTypeLimits, Dictionary<MongoId, (int current, int max)> itemTypeLimits,
int loyaltyLevel int loyaltyLevel
) )
{ {
@@ -1066,8 +1066,8 @@ public class FenceService(
} }
protected (int current, int max)? GetMatchingItemLimit( protected (int current, int max)? GetMatchingItemLimit(
Dictionary<string, (int current, int max)> itemTypeLimits, Dictionary<MongoId, (int current, int max)> itemTypeLimits,
string itemTpl MongoId itemTpl
) )
{ {
foreach (var baseTypeKey in itemTypeLimits.Keys) foreach (var baseTypeKey in itemTypeLimits.Keys)
@@ -1680,11 +1680,11 @@ public class FenceService(
/// </summary> /// </summary>
/// <param name="limits"> Limits as defined in config </param> /// <param name="limits"> Limits as defined in config </param>
/// <returns> Record, key: item tplId, value: current/max item count allowed </returns> /// <returns> Record, key: item tplId, value: current/max item count allowed </returns>
protected Dictionary<string, (int current, int max)> InitItemLimitCounter( protected Dictionary<MongoId, (int current, int max)> InitItemLimitCounter(
Dictionary<string, int> limits Dictionary<MongoId, int> limits
) )
{ {
var itemTypeCounts = new Dictionary<string, (int current, int max)>(); var itemTypeCounts = new Dictionary<MongoId, (int current, int max)>();
foreach (var x in limits.Keys) foreach (var x in limits.Keys)
{ {
@@ -1,4 +1,5 @@
using SPTarkov.DI.Annotations; using SPTarkov.DI.Annotations;
using SPTarkov.Server.Core.Models.Common;
using SPTarkov.Server.Core.Models.Eft.Common.Tables; using SPTarkov.Server.Core.Models.Eft.Common.Tables;
using SPTarkov.Server.Core.Models.Utils; using SPTarkov.Server.Core.Models.Utils;
using LogLevel = SPTarkov.Server.Core.Models.Spt.Logging.LogLevel; using LogLevel = SPTarkov.Server.Core.Models.Spt.Logging.LogLevel;
@@ -16,7 +17,7 @@ public class ItemBaseClassService(
) )
{ {
private bool _cacheGenerated; private bool _cacheGenerated;
private Dictionary<string, HashSet<string>> _itemBaseClassesCache; private Dictionary<MongoId, HashSet<MongoId>> _itemBaseClassesCache = [];
/// <summary> /// <summary>
/// Create cache and store inside ItemBaseClassService <br /> /// Create cache and store inside ItemBaseClassService <br />
@@ -25,7 +26,7 @@ public class ItemBaseClassService(
public void HydrateItemBaseClassCache() public void HydrateItemBaseClassCache()
{ {
// Clear existing cache // Clear existing cache
_itemBaseClassesCache = new Dictionary<string, HashSet<string>>(); _itemBaseClassesCache = new Dictionary<MongoId, HashSet<MongoId>>();
var items = _databaseService.GetItems(); var items = _databaseService.GetItems();
var filteredDbItems = items.Where(x => var filteredDbItems = items.Where(x =>
@@ -50,12 +51,12 @@ public class ItemBaseClassService(
/// </summary> /// </summary>
/// <param name="itemIdToUpdate"> Item tpl to store base ids against in dictionary </param> /// <param name="itemIdToUpdate"> Item tpl to store base ids against in dictionary </param>
/// <param name="item"> Item being checked </param> /// <param name="item"> Item being checked </param>
protected void AddBaseItems(string itemIdToUpdate, TemplateItem item) protected void AddBaseItems(MongoId itemIdToUpdate, TemplateItem item)
{ {
_itemBaseClassesCache[itemIdToUpdate].Add(item.Parent); _itemBaseClassesCache[itemIdToUpdate].Add(item.Parent);
_databaseService.GetItems().TryGetValue(item.Parent, out var parent); _databaseService.GetItems().TryGetValue(item.Parent, out var parent);
if (parent is not null && !string.IsNullOrEmpty(parent.Parent)) if (parent is not null && !parent.Parent.IsEmpty())
{ {
AddBaseItems(itemIdToUpdate, parent); AddBaseItems(itemIdToUpdate, parent);
} }
@@ -67,14 +68,14 @@ public class ItemBaseClassService(
/// <param name="itemTpl"> ItemTpl item to check base classes of </param> /// <param name="itemTpl"> ItemTpl item to check base classes of </param>
/// <param name="baseClasses"> BaseClass base class to check for </param> /// <param name="baseClasses"> BaseClass base class to check for </param>
/// <returns> true if item inherits from base class passed in </returns> /// <returns> true if item inherits from base class passed in </returns>
public bool ItemHasBaseClass(string itemTpl, ICollection<string> baseClasses) public bool ItemHasBaseClass(MongoId itemTpl, ICollection<MongoId> baseClasses)
{ {
if (!_cacheGenerated) if (!_cacheGenerated)
{ {
HydrateItemBaseClassCache(); HydrateItemBaseClassCache();
} }
if (string.IsNullOrEmpty(itemTpl)) if (itemTpl.IsEmpty())
{ {
_logger.Warning("Unable to check itemTpl base class as value passed is null"); _logger.Warning("Unable to check itemTpl base class as value passed is null");
@@ -119,7 +120,7 @@ public class ItemBaseClassService(
/// </summary> /// </summary>
/// <param name="itemTemplateId"> ItemTemplateId item to check </param> /// <param name="itemTemplateId"> ItemTemplateId item to check </param>
/// <returns> True if item is of type Item </returns> /// <returns> True if item is of type Item </returns>
private bool CachedItemIsOfItemType(string itemTemplateId) private bool CachedItemIsOfItemType(MongoId itemTemplateId)
{ {
return string.Equals( return string.Equals(
_databaseService.GetItems()[itemTemplateId]?.Type, _databaseService.GetItems()[itemTemplateId]?.Type,
@@ -133,7 +134,7 @@ public class ItemBaseClassService(
/// </summary> /// </summary>
/// <param name="itemTpl"> ItemTpl item to get base classes for </param> /// <param name="itemTpl"> ItemTpl item to get base classes for </param>
/// <returns> array of base classes </returns> /// <returns> array of base classes </returns>
public List<string> GetItemBaseClasses(string itemTpl) public List<MongoId> GetItemBaseClasses(MongoId itemTpl)
{ {
if (!_cacheGenerated) if (!_cacheGenerated)
{ {
@@ -171,7 +171,7 @@ public class LocationLifecycleService
Transition = new Transition Transition = new Transition
{ {
TransitionType = TransitionType.NONE, TransitionType = TransitionType.NONE,
TransitionRaidId = _hashUtil.Generate(), TransitionRaidId = new MongoId(),
TransitionCount = 0, TransitionCount = 0,
VisitedLocations = [], VisitedLocations = [],
}, },
@@ -520,7 +520,7 @@ public class LocationLifecycleService
// Generate randomised reward for taking coop extract // Generate randomised reward for taking coop extract
var loot = _lootGenerator.CreateRandomLoot(_traderConfig.Fence.CoopExtractGift); var loot = _lootGenerator.CreateRandomLoot(_traderConfig.Fence.CoopExtractGift);
var parentId = _hashUtil.Generate(); var parentId = new MongoId();
foreach (var itemAndChildren in loot) foreach (var itemAndChildren in loot)
{ {
// Set all root items parent to new id // Set all root items parent to new id
@@ -1,6 +1,7 @@
using SPTarkov.DI.Annotations; using SPTarkov.DI.Annotations;
using SPTarkov.Server.Core.Extensions; using SPTarkov.Server.Core.Extensions;
using SPTarkov.Server.Core.Helpers; using SPTarkov.Server.Core.Helpers;
using SPTarkov.Server.Core.Models.Common;
using SPTarkov.Server.Core.Models.Eft.Common.Tables; using SPTarkov.Server.Core.Models.Eft.Common.Tables;
using SPTarkov.Server.Core.Models.Eft.Profile; using SPTarkov.Server.Core.Models.Eft.Profile;
using SPTarkov.Server.Core.Models.Enums; using SPTarkov.Server.Core.Models.Enums;
@@ -194,7 +195,7 @@ public class MailSendService(
// add items to message // add items to message
if (items?.Count > 0) if (items?.Count > 0)
{ {
var rootItemParentId = _hashUtil.Generate(); var rootItemParentId = new MongoId();
details.Items.AddRange(items.AdoptOrphanedItems(rootItemParentId)); details.Items.AddRange(items.AdoptOrphanedItems(rootItemParentId));
details.ItemsMaxStorageLifetimeSeconds = maxStorageTimeSeconds; details.ItemsMaxStorageLifetimeSeconds = maxStorageTimeSeconds;
@@ -366,7 +367,7 @@ public class MailSendService(
dialogWithNpc.Messages.Add( dialogWithNpc.Messages.Add(
new Message new Message
{ {
Id = _hashUtil.Generate(), Id = new MongoId(),
DateTime = _timeUtil.GetTimeStamp(), DateTime = _timeUtil.GetTimeStamp(),
HasRewards = false, HasRewards = false,
UserId = playerProfile.CharacterData.PmcData.Id, UserId = playerProfile.CharacterData.PmcData.Id,
@@ -387,7 +388,7 @@ public class MailSendService(
{ {
Message message = new() Message message = new()
{ {
Id = _hashUtil.Generate(), Id = new MongoId(),
UserId = dialogId, UserId = dialogId,
MessageType = messageDetails.Sender, MessageType = messageDetails.Sender,
DateTime = _timeUtil.GetTimeStamp(), DateTime = _timeUtil.GetTimeStamp(),
@@ -506,7 +507,7 @@ public class MailSendService(
// No parent id, generate random id and add (doesn't need to be actual parentId from db, only unique) // No parent id, generate random id and add (doesn't need to be actual parentId from db, only unique)
if (parentItem?.ParentId is null) if (parentItem?.ParentId is null)
{ {
parentItem.ParentId = _hashUtil.Generate(); parentItem.ParentId = new MongoId();
} }
// Prep return object // Prep return object
@@ -137,9 +137,9 @@ public class CustomItemService(
/// </summary> /// </summary>
/// <param name="newId"> ID supplied to code </param> /// <param name="newId"> ID supplied to code </param>
/// <returns> ItemID </returns> /// <returns> ItemID </returns>
protected string GetOrGenerateIdForItem(string newId) protected MongoId GetOrGenerateIdForItem(string newId)
{ {
return newId == "" ? hashUtil.Generate() : newId; return string.IsNullOrEmpty(newId) ? new MongoId() : new MongoId(newId);
} }
/// <summary> /// <summary>
@@ -1,4 +1,5 @@
using SPTarkov.DI.Annotations; using SPTarkov.DI.Annotations;
using SPTarkov.Server.Core.Extensions;
using SPTarkov.Server.Core.Helpers; using SPTarkov.Server.Core.Helpers;
using SPTarkov.Server.Core.Models.Common; using SPTarkov.Server.Core.Models.Common;
using SPTarkov.Server.Core.Models.Eft.Common.Tables; using SPTarkov.Server.Core.Models.Eft.Common.Tables;
@@ -128,8 +129,10 @@ public class RagfairLinkedItemService(
} }
// Get the first cylinder filter tpl // Get the first cylinder filter tpl
var cylinderTpl = cylinderMod.Props?.Filters?[0].Filter?.FirstOrDefault(); var cylinderTpl =
if (string.IsNullOrEmpty(cylinderTpl)) cylinderMod.Props?.Filters?[0].Filter?.FirstOrDefault() ?? new MongoId(null);
if (!cylinderTpl.IsValidMongoId())
{ {
// No cylinder, nothing to do // No cylinder, nothing to do
return; return;
@@ -1,6 +1,7 @@
using SPTarkov.Common.Extensions; using SPTarkov.Common.Extensions;
using SPTarkov.DI.Annotations; using SPTarkov.DI.Annotations;
using SPTarkov.Server.Core.Helpers; using SPTarkov.Server.Core.Helpers;
using SPTarkov.Server.Core.Models.Common;
using SPTarkov.Server.Core.Models.Eft.Common.Tables; using SPTarkov.Server.Core.Models.Eft.Common.Tables;
using SPTarkov.Server.Core.Models.Eft.Ragfair; using SPTarkov.Server.Core.Models.Eft.Ragfair;
using SPTarkov.Server.Core.Models.Spt.Config; using SPTarkov.Server.Core.Models.Spt.Config;
@@ -268,7 +269,7 @@ public class RagfairOfferService(
var unstackedItems = UnstackOfferItems(playerOffer.Items); var unstackedItems = UnstackOfferItems(playerOffer.Items);
// Need to regenerate Ids to ensure returned item(s) have correct parent values // Need to regenerate Ids to ensure returned item(s) have correct parent values
var newParentId = hashUtil.Generate(); var newParentId = new MongoId();
foreach (var item in unstackedItems) foreach (var item in unstackedItems)
{ {
// Refresh root items' parentIds // Refresh root items' parentIds
@@ -209,7 +209,7 @@ public class SeasonalEventService(
/// </summary> /// </summary>
/// <param name="eventName">Name of event to get gear changes for</param> /// <param name="eventName">Name of event to get gear changes for</param>
/// <returns>bots with equipment changes</returns> /// <returns>bots with equipment changes</returns>
protected Dictionary<string, Dictionary<string, Dictionary<string, int>>>? GetEventBotGear( protected Dictionary<string, Dictionary<string, Dictionary<MongoId, int>>>? GetEventBotGear(
SeasonalEventType eventType SeasonalEventType eventType
) )
{ {
@@ -221,7 +221,7 @@ public class SeasonalEventService(
/// </summary> /// </summary>
/// <param name="eventName">Name of event to get gear changes for</param> /// <param name="eventName">Name of event to get gear changes for</param>
/// <returns>bots with loot changes</returns> /// <returns>bots with loot changes</returns>
protected Dictionary<string, Dictionary<string, Dictionary<string, int>>> GetEventBotLoot( protected Dictionary<string, Dictionary<string, Dictionary<MongoId, int>>> GetEventBotLoot(
SeasonalEventType eventType SeasonalEventType eventType
) )
{ {
@@ -1,7 +1,6 @@
using System.IO.Hashing; using System.IO.Hashing;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text; using System.Text;
using System.Text.RegularExpressions;
using SPTarkov.DI.Annotations; using SPTarkov.DI.Annotations;
namespace SPTarkov.Server.Core.Utils; namespace SPTarkov.Server.Core.Utils;
@@ -9,47 +8,6 @@ namespace SPTarkov.Server.Core.Utils;
[Injectable(InjectionType.Singleton)] [Injectable(InjectionType.Singleton)]
public partial class HashUtil(RandomUtil _randomUtil) public partial class HashUtil(RandomUtil _randomUtil)
{ {
/// <summary>
/// Create a 24 character MongoId
/// </summary>
/// <returns>24 character objectId</returns>
public string Generate()
{
// Allocate a span directly onto the stack, will dispose whenever we finished running
// Span is recommended to work with stackalloc and we can use stackalloc here because we don't do anything with this afterwards
Span<byte> objectId = stackalloc byte[12];
// Time stamp (4 bytes)
var timestamp = (int)DateTimeOffset.UtcNow.ToUnixTimeSeconds();
// Convert to big-endian
objectId[0] = (byte)(timestamp >> 24);
objectId[1] = (byte)(timestamp >> 16);
objectId[2] = (byte)(timestamp >> 8);
objectId[3] = (byte)timestamp;
// Random value (5 bytes)
_randomUtil.NextBytes(objectId.Slice(4, 5));
// Incrementing counter (3 bytes)
// 24-bit counter
var counter = _randomUtil.GetInt(0, 16777215);
objectId[9] = (byte)(counter >> 16);
objectId[10] = (byte)(counter >> 8);
objectId[11] = (byte)counter;
return Convert.ToHexStringLower(objectId);
}
/// <summary>
/// is the passed in string a valid mongo id
/// </summary>
/// <param name="stringToCheck">String to check</param>
/// <returns>True when string is a valid mongo id</returns>
public bool IsValidMongoId(string stringToCheck)
{
return MongoIdRegex().IsMatch(stringToCheck);
}
public uint GenerateCrc32ForData(string data) public uint GenerateCrc32ForData(string data)
{ {
return Crc32.HashToUInt32(new ArraySegment<byte>(Encoding.UTF8.GetBytes(data))); return Crc32.HashToUInt32(new ArraySegment<byte>(Encoding.UTF8.GetBytes(data)));
@@ -124,9 +82,6 @@ public partial class HashUtil(RandomUtil _randomUtil)
return _randomUtil.Random.Next(min, max + 1); return _randomUtil.Random.Next(min, max + 1);
} }
[GeneratedRegex("^[a-fA-F0-9]{24}$")]
private static partial Regex MongoIdRegex();
} }
public enum HashingAlgorithm public enum HashingAlgorithm
@@ -2,6 +2,7 @@ using System.Collections.Concurrent;
using SPTarkov.DI.Annotations; using SPTarkov.DI.Annotations;
using SPTarkov.Server.Core.Extensions; using SPTarkov.Server.Core.Extensions;
using SPTarkov.Server.Core.Helpers; using SPTarkov.Server.Core.Helpers;
using SPTarkov.Server.Core.Models.Common;
using SPTarkov.Server.Core.Models.Eft.Common.Tables; using SPTarkov.Server.Core.Models.Eft.Common.Tables;
using SPTarkov.Server.Core.Models.Eft.Ragfair; using SPTarkov.Server.Core.Models.Eft.Ragfair;
using SPTarkov.Server.Core.Models.Utils; using SPTarkov.Server.Core.Models.Utils;
@@ -137,10 +138,10 @@ public class RagfairOfferHolder(
// Keep generating IDs until we get a unique one // Keep generating IDs until we get a unique one
while (_offersById.ContainsKey(offer.Id)) while (_offersById.ContainsKey(offer.Id))
{ {
offer.Id = _hashUtil.Generate(); offer.Id = new MongoId();
} }
var itemTpl = offer.Items?.FirstOrDefault()?.Template; var itemTpl = offer.Items?.FirstOrDefault()?.Template ?? new MongoId(null);
var sellerId = offer.User.Id; var sellerId = offer.User.Id;
var sellerIsTrader = _ragfairServerHelper.IsTrader(sellerId); var sellerIsTrader = _ragfairServerHelper.IsTrader(sellerId);
-97
View File
@@ -1,97 +0,0 @@
using System.Collections.Concurrent;
using System.Diagnostics;
using SPTarkov.Server.Core.Utils;
namespace UnitTests.Tests.Utils;
[TestClass]
public class HashUtilTests
{
private HashUtil _hashUtil;
[TestInitialize]
public void Initialize()
{
_hashUtil = DI.GetService<HashUtil>();
}
[TestMethod]
public void GenerateTest()
{
// Generate 100 MongoId's
for (var i = 0; i < 100; i++)
{
// Invalid mongoId character
var result = _hashUtil.Generate();
// Invalid mongoId length
var test = _hashUtil.IsValidMongoId(result);
Assert.AreEqual(true, test, $"IsValidMongoId() `{result}` is not a valid MongoId.");
}
}
[TestMethod]
[DataRow(
"677ddb67406e9918a0264bbz",
false,
"677ddb67406e9918a0264bbz contains invalid char `z`, but result was true"
)]
[DataRow(
"677ddb67406e9918a0264bbcc",
false,
"677ddb67406e9918a0264bbcc is 25 characters, but result was true"
)]
[DataRow(
"677ddb67406e9918a0264bbc",
true,
"IsValidMongoId() `677ddb67406e9918a0264bbc` is a valid mongoId, but result was false"
)]
public void IsValidMongoIdTest(string mongoId, bool passes, string failMessage)
{
var result = _hashUtil.IsValidMongoId(mongoId);
Assert.AreEqual(passes, result, failMessage);
}
[TestMethod]
[DataRow(
"123456789",
"25F9E794323B453885F5181F1B624D0B",
"Not valid output, expected '25F9E794323B453885F5181F1B624D0B'"
)]
public void GenerateValidMd5Test(string input, string expectedOutput, string failMessage)
{
var result = _hashUtil.GenerateHashForData(HashingAlgorithm.MD5, input);
Assert.AreEqual(expectedOutput, result, failMessage);
}
[TestMethod]
public void MultiThreadedMongoIDGenerationTest()
{
var concurrentBag = new ConcurrentBag<string>();
var random = new Random();
var stopwatch = new Stopwatch();
stopwatch.Start();
Parallel.For(
0,
1000,
i =>
{
Thread.Sleep(random.Next(0, 10));
var mongoId = _hashUtil.Generate();
concurrentBag.Add(mongoId);
}
);
stopwatch.Stop();
Console.WriteLine($"Elapsed time: {stopwatch.ElapsedMilliseconds} ms");
var uniqueCount = concurrentBag.Distinct().Count();
var totalCount = concurrentBag.Count;
Assert.AreEqual(
totalCount,
uniqueCount,
$"Expected all generated MongoId's to be unique, but found {totalCount - uniqueCount} duplicates."
);
}
}