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.Server.Core.DI;
using SPTarkov.Server.Core.Extensions;
using SPTarkov.Server.Core.Models.Common;
using SPTarkov.Server.Core.Models.Eft.Profile;
using SPTarkov.Server.Core.Models.Spt.Config;
using SPTarkov.Server.Core.Models.Utils;
@@ -110,7 +111,7 @@ public class BtrDeliveryCallbacks(
foreach (var package in packagesToBeDelivered)
{
// 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
package.Items = package.Items.AdoptOrphanedItems(rootItemParentId);
@@ -47,7 +47,7 @@ public class DialogueCallbacks(
{
new()
{
Id = _hashUtil.Generate(),
Id = new Models.Common.MongoId(),
RegistrationId = 20,
DateTime = _timeUtil.GetTimeStamp(),
IsDeveloper = true,
@@ -122,7 +122,7 @@ public class InsuranceController(
foreach (var insured in insuranceDetails)
{
// 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
insured.Items = insured.Items.AdoptOrphanedItems(rootItemParentId);
@@ -2064,7 +2064,7 @@ public class BotEquipmentModGenerator(
public HashSet<MongoId> FilterSightsByWeaponType(
Item weapon,
HashSet<MongoId> scopes,
Dictionary<string, List<string>> botWeaponSightWhitelist
Dictionary<MongoId, List<MongoId>> botWeaponSightWhitelist
)
{
var weaponDetails = _itemHelper.GetItem(weapon.Template);
@@ -794,6 +794,7 @@ public class BotWeaponGenerator(
var magazineTemplate = _itemHelper.GetItem(
magazineSlot.Props?.Filters.FirstOrDefault()?.Filter?.FirstOrDefault()
?? new MongoId(null)
);
if (!magazineTemplate.Key)
{
@@ -542,7 +542,7 @@ public class LocationLootGenerator(
var containerTpl = containerClone.Template.Items.FirstOrDefault().Template;
// Create new unique parent id to prevent any collisions
var parentId = _hashUtil.Generate();
var parentId = new MongoId();
containerClone.Template.Root = parentId;
containerClone.Template.Items.FirstOrDefault().Id = parentId;
@@ -180,7 +180,7 @@ public class LootGenerator(
/// </summary>
/// <param name="forcedLootToAdd">Dictionary of item tpls with minmax values</param>
/// <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>>();
@@ -22,7 +22,7 @@ public class RagfairAssortGenerator(
{
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.STASH, // Player inventory stash
@@ -149,7 +149,7 @@ public class RagfairOfferGenerator(
var offer = new RagfairOffer
{
Id = hashUtil.Generate(),
Id = new MongoId(),
InternalId = offerCounter,
User = CreateUserDataForFleaOffer(userId, ragfairServerHelper.IsTrader(userId)),
Root = rootItem.Id,
@@ -34,10 +34,11 @@ public class BotWeaponGeneratorHelper(
double? chamberBulletCount = 0;
if (MagazineIsCylinderRelated(parentItem.Name))
{
var firstSlotAmmoTpl = magTemplate
.Properties.Cartridges.FirstOrDefault()
?.Props.Filters[0]
.Filter.FirstOrDefault();
var firstSlotAmmoTpl =
magTemplate
.Properties.Cartridges.FirstOrDefault()
?.Props.Filters[0]
.Filter.FirstOrDefault() ?? new MongoId(null);
var ammoMaxStackSize =
_itemHelper.GetItem(firstSlotAmmoTpl).Value?.Properties?.StackMaxSize ?? 1;
chamberBulletCount =
@@ -1,18 +1,19 @@
using SPTarkov.DI.Annotations;
using SPTarkov.Server.Core.Models.Common;
namespace SPTarkov.Server.Core.Helpers
{
[Injectable]
public class CounterTrackerHelper
{
private Dictionary<string, int> _maxCounts = new();
private readonly Dictionary<string, int> _trackedCounts = new();
private Dictionary<MongoId, int> _maxCounts = new();
private readonly Dictionary<MongoId, int> _trackedCounts = new();
/// <summary>
/// Add dictionary of keys and their matching limits to track
/// </summary>
/// <param name="maxCounts">Values to store</param>
public void AddDataToTrack(Dictionary<string, int> maxCounts)
public void AddDataToTrack(Dictionary<MongoId, int> maxCounts)
{
_maxCounts = maxCounts;
}
@@ -23,7 +24,7 @@ namespace SPTarkov.Server.Core.Helpers
/// <param name="key"></param>
/// <param name="countToIncrementBy"></param>
/// <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
if (!_maxCounts.Any() || !_maxCounts.ContainsKey(key))
@@ -143,7 +143,7 @@ public class ProfileSptCommand(
Id = new MongoId(),
Template = Money.ROUBLES,
Upd = new Upd { StackObjectsCount = 1 },
ParentId = _hashUtil.Generate(),
ParentId = new MongoId(),
SlotId = "main",
},
],
@@ -111,7 +111,7 @@ public class TraderSptCommand(
Id = new MongoId(),
Template = Money.ROUBLES,
Upd = new Upd { StackObjectsCount = 1 },
ParentId = _hashUtil.Generate(),
ParentId = new MongoId(),
SlotId = "main",
},
],
@@ -4,6 +4,7 @@ using System.Text.Json.Serialization;
using SPTarkov.Common.Extensions;
using SPTarkov.DI.Annotations;
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.Tables;
using SPTarkov.Server.Core.Models.Eft.Inventory;
@@ -36,7 +37,7 @@ public class InventoryHelper(
ICloner _cloner
)
{
private static readonly FrozenSet<string> _variableSizeItemTypes =
private static readonly FrozenSet<MongoId> _variableSizeItemTypes =
[
BaseClasses.WEAPON,
BaseClasses.FUNCTIONAL_MOD,
@@ -962,8 +963,8 @@ public class InventoryHelper(
{
var inventoryItemHash = new InventoryItemHash
{
ByItemId = new Dictionary<string, Item>(),
ByParentId = new Dictionary<string, HashSet<Item>>(),
ByItemId = new Dictionary<MongoId, Item>(),
ByParentId = new Dictionary<MongoId, HashSet<Item>>(),
};
foreach (var item in inventoryItems)
{
@@ -1367,7 +1368,7 @@ public class InventoryHelper(
public void ValidateInventoryUsesMongoIds(List<Item> itemsToValidate)
{
var errors = itemsToValidate
.Where(item => !_hashUtil.IsValidMongoId(item.Id))
.Where(item => !item.Id.IsValidMongoId())
.Select(item => $"Id: {item.Id} - tpl: {item.Template}")
.ToList();
foreach (var message in errors)
@@ -1411,8 +1412,8 @@ public class InventoryHelper(
public class InventoryItemHash
{
[JsonPropertyName("byItemId")]
public Dictionary<string, Item> ByItemId { get; set; }
public Dictionary<MongoId, Item> ByItemId { get; set; }
[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
)
{
protected static readonly FrozenSet<string> _defaultInvalidBaseTypes =
protected static readonly FrozenSet<MongoId> _defaultInvalidBaseTypes =
[
BaseClasses.LOOT_CONTAINER,
BaseClasses.MOB_CONTAINER,
@@ -97,7 +97,7 @@ public class ItemHelper(
"right_side_plate",
];
protected static readonly FrozenSet<string> _armorSlotsThatCanHoldMods =
protected static readonly FrozenSet<MongoId> _armorSlotsThatCanHoldMods =
[
BaseClasses.HEADWEAR,
BaseClasses.VEST,
@@ -283,7 +283,7 @@ public class ItemHelper(
/// <param name="tpl">Template id to check</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>
public bool IsValidItem(string tpl, ICollection<string>? invalidBaseTypes = null)
public bool IsValidItem(MongoId tpl, ICollection<MongoId>? invalidBaseTypes = null)
{
var baseTypes = invalidBaseTypes ?? _defaultInvalidBaseTypes;
var itemDetails = GetItem(tpl);
@@ -306,7 +306,7 @@ public class ItemHelper(
/// <param name="tpl">Item template id to check</param>
/// <param name="baseClassTpl">Baseclass to check for</param>
/// <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]);
}
@@ -317,29 +317,11 @@ public class ItemHelper(
/// <param name="tpl">Item to check base classes of</param>
/// <param name="baseClassTpls">Base classes to check for</param>
/// <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);
}
/// <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>
/// Does the provided item have the chance to require soft armor inserts
/// Only applies to helmets/vest/armors
@@ -347,7 +329,7 @@ public class ItemHelper(
/// </summary>
/// <param name="itemTpl">Tpl to check</param>
/// <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);
}
@@ -526,7 +508,7 @@ public class ItemHelper(
/// </summary>
/// <param name="itemTpl">template id to look up</param>
/// <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>
if (_databaseService.GetItems().TryGetValue(itemTpl, out var item))
@@ -542,7 +524,7 @@ public class ItemHelper(
/// </summary>
/// <param name="itemTpl">Template id of the item to check</param>
/// <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))
{
@@ -1347,7 +1329,7 @@ public class ItemHelper(
/// <returns>ItemSize object (width and height)</returns>
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 height = rootTemplate.Properties.Height;
@@ -1,4 +1,5 @@
using SPTarkov.DI.Annotations;
using SPTarkov.Server.Core.Models.Common;
using SPTarkov.Server.Core.Models.Eft.Profile;
using SPTarkov.Server.Core.Models.Eft.Ws;
using SPTarkov.Server.Core.Models.Enums;
@@ -76,7 +77,7 @@ public class NotificationSendHelper(
dialog.New += 1;
var message = new Message
{
Id = _hashUtil.Generate(),
Id = new MongoId(),
UserId = dialog.Id,
MessageType = messageType,
DateTime = _timeUtil.GetTimeStamp(),
@@ -1,4 +1,5 @@
using SPTarkov.DI.Annotations;
using SPTarkov.Server.Core.Models.Common;
using SPTarkov.Server.Core.Models.Eft.Profile;
using SPTarkov.Server.Core.Models.Eft.Ws;
using SPTarkov.Server.Core.Utils;
@@ -57,7 +58,7 @@ public class NotifierHelper(HttpServerHelper httpServerHelper, HashUtil hashUtil
return new WsRagfairNewRating
{
EventType = NotificationEventType.RagfairNewRating,
EventIdentifier = hashUtil.Generate(),
EventIdentifier = new MongoId(),
Rating = rating,
IsRatingGrowing = isGrowing,
};
@@ -1,4 +1,5 @@
using SPTarkov.DI.Annotations;
using SPTarkov.Server.Core.Models.Common;
using SPTarkov.Server.Core.Models.Enums;
using SPTarkov.Server.Core.Models.Spt.Config;
using SPTarkov.Server.Core.Servers;
@@ -10,7 +11,7 @@ public class PaymentHelper(ConfigServer configServer)
{
protected bool _addedCustomMoney;
protected readonly InventoryConfig _inventoryConfig = configServer.GetConfig<InventoryConfig>();
protected readonly HashSet<string> _moneyTpls =
protected readonly HashSet<MongoId> _moneyTpls =
[
Money.DOLLARS,
Money.EUROS,
@@ -23,7 +24,7 @@ public class PaymentHelper(ConfigServer configServer)
/// </summary>
/// <param name="tpl">Item Tpl to check</param>
/// <returns></returns>
public bool IsMoneyTpl(string tpl)
public bool IsMoneyTpl(MongoId tpl)
{
// Add custom currency first time this method is accessed
if (!_addedCustomMoney)
@@ -1,6 +1,7 @@
using SPTarkov.Common.Extensions;
using SPTarkov.DI.Annotations;
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.Tables;
using SPTarkov.Server.Core.Models.Eft.ItemEvent;
@@ -112,7 +113,7 @@ public class RagfairOfferHelper(
protected void CheckAndLockOfferFromPlayerTieredFlea(
TieredFlea tieredFlea,
RagfairOffer offer,
List<string> tieredFleaLimitTypes,
List<MongoId> tieredFleaLimitTypes,
int playerLevel
)
{
@@ -819,7 +820,7 @@ public class RagfairOfferHelper(
// Create an item template item
var requestedItem = new Item
{
Id = _hashUtil.Generate(),
Id = new MongoId(),
Template = requirement.Template,
Upd = new Upd { StackObjectsCount = requirement.Count * boughtAmount },
};
@@ -23,6 +23,11 @@ public readonly struct MongoId : IEquatable<MongoId>
return;
}
if (id == "hideout")
{
throw new Exception("wtf");
}
if (id.Length != 24)
{
// TODO: Items.json root item has an empty parentId property
@@ -1,4 +1,5 @@
using System.Text.Json.Serialization;
using SPTarkov.Server.Core.Models.Common;
namespace SPTarkov.Server.Core.Models.Eft.Dialog;
@@ -8,7 +9,7 @@ public record ChatServer
public Dictionary<string, object>? ExtensionData { get; set; }
[JsonPropertyName("_id")]
public string? Id { get; set; }
public MongoId Id { get; set; }
[JsonPropertyName("RegistrationId")]
public int? RegistrationId { get; set; }
@@ -70,7 +70,7 @@ public record Requirement
public Dictionary<string, object>? ExtensionData { get; set; }
[JsonPropertyName("templateId")]
public string? TemplateId { get; set; }
public MongoId TemplateId { get; set; }
[JsonPropertyName("count")]
public int? Count { get; set; }
@@ -458,10 +458,10 @@ public record AcceptedCultistReward
public long? Timestamp { get; set; }
[JsonPropertyName("sacrificeItems")]
public List<string>? SacrificeItems { get; set; }
public List<MongoId>? SacrificeItems { get; set; }
[JsonPropertyName("rewardItems")]
public List<string>? RewardItems { get; set; }
public List<MongoId>? RewardItems { get; set; }
}
public record PendingPrestige
@@ -8,12 +8,12 @@ public record Money
[JsonExtensionData]
public Dictionary<string, object>? ExtensionData { get; set; }
public static readonly MongoId ROUBLES = new MongoId("5449016a4bdc2d6f028b456f");
public static readonly MongoId EUROS = new MongoId("569668774bdc2da2298b4568");
public static readonly MongoId DOLLARS = new MongoId("5696686a4bdc2da3298b456a");
public static readonly MongoId GP = new MongoId("5d235b4d86f7742e017bc88a");
public static readonly MongoId ROUBLES = new("5449016a4bdc2d6f028b456f");
public static readonly MongoId EUROS = new("569668774bdc2da2298b4568");
public static readonly MongoId DOLLARS = new("5696686a4bdc2da3298b456a");
public static readonly MongoId GP = new("5d235b4d86f7742e017bc88a");
public static HashSet<string> GetMoneyTpls()
public static HashSet<MongoId> GetMoneyTpls()
{
return [ROUBLES, EUROS, DOLLARS, GP];
}
@@ -121,7 +121,7 @@ public record BotModLimits
public int? ScopeMax { get; set; }
[JsonPropertyName("scopeBaseTypes")]
public List<string>? ScopeBaseTypes { get; set; }
public List<MongoId>? ScopeBaseTypes { get; set; }
[JsonPropertyName("flashlightLaser")]
public ItemCount? FlashlightLaser { get; set; }
@@ -130,7 +130,7 @@ public record BotModLimits
public int? FlashlightLaserMax { get; set; }
[JsonPropertyName("flashlightLaserBaseTypes")]
public List<string>? FlashlightLaserBaseTypes { get; set; }
public List<MongoId>? FlashlightLaserBaseTypes { get; set; }
}
public record ItemCount
@@ -30,7 +30,7 @@ public record ModToSpawnRequest
/// Parent slot the item will be a part of
/// </summary>
[JsonPropertyName("botWeaponSightWhitelist")]
public Dictionary<string, List<string>>? BotWeaponSightWhitelist { get; set; }
public Dictionary<MongoId, List<MongoId>>? BotWeaponSightWhitelist { get; set; }
/// <summary>
/// Blacklist to prevent mods from being picked
@@ -19,7 +19,7 @@ public record AirdropConfig : BaseConfig
public required Dictionary<string, AirdropLoot> Loot { get; set; }
[JsonPropertyName("customAirdropMapping")]
public required Dictionary<string, SptAirdropTypeEnum> CustomAirdropMapping { get; set; }
public required Dictionary<MongoId, SptAirdropTypeEnum> CustomAirdropMapping { get; set; }
}
/// <summary>
@@ -98,7 +98,7 @@ public record AirdropLoot
public bool UseForcedLoot { get; set; }
[JsonPropertyName("forcedLoot")]
public Dictionary<string, MinMax<int>>? ForcedLoot { get; set; }
public Dictionary<MongoId, MinMax<int>>? ForcedLoot { get; set; }
[JsonPropertyName("useRewardItemBlacklist")]
public bool UseRewardItemBlacklist { get; set; }
@@ -355,7 +355,7 @@ public record EquipmentFilters
/// Whitelist for weapon sight types allowed per gun
/// </summary>
[JsonPropertyName("weaponSightWhitelist")]
public Dictionary<string, List<string>> WeaponSightWhitelist { get; set; }
public Dictionary<MongoId, List<MongoId>> WeaponSightWhitelist { get; set; }
/// <summary>
/// Chance face shield is down/active
@@ -55,16 +55,16 @@ public record HideoutCraftToAdd
/// The new mongoId for the craft to use
/// </summary>
[JsonPropertyName("newId")]
public required string NewId { get; set; }
public required MongoId NewId { get; set; }
[JsonPropertyName("requirements")]
public required List<Requirement> Requirements { get; set; }
[JsonPropertyName("craftIdToCopy")]
public required string CraftIdToCopy { get; set; }
public required MongoId CraftIdToCopy { get; set; }
[JsonPropertyName("craftOutputTpl")]
public required string CraftOutputTpl { get; set; }
public required MongoId CraftOutputTpl { get; set; }
}
public record CultistCircleSettings
@@ -157,10 +157,10 @@ public record DirectRewardSettings
public Dictionary<string, object>? ExtensionData { get; set; }
[JsonPropertyName("reward")]
public required List<string> Reward { get; set; }
public required List<MongoId> Reward { get; set; }
[JsonPropertyName("requiredItems")]
public required List<string> RequiredItems { get; set; }
public required List<MongoId> RequiredItems { get; set; }
[JsonPropertyName("craftTimeSeconds")]
public required int CraftTimeSeconds { get; set; }
@@ -1,4 +1,5 @@
using System.Text.Json.Serialization;
using SPTarkov.Server.Core.Models.Common;
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
/// </summary>
[JsonPropertyName("returnChancePercent")]
public Dictionary<string, double> ReturnChancePercent { get; set; } = [];
public Dictionary<MongoId, double> ReturnChancePercent { get; set; } = [];
/// <summary>
/// Item slots that should never be returned as insurance
@@ -15,7 +15,7 @@ public record InventoryConfig : BaseConfig
public bool NewItemsMarkedFound { get; set; }
[JsonPropertyName("randomLootContainers")]
public required Dictionary<string, RewardDetails> RandomLootContainers { get; set; }
public required Dictionary<MongoId, RewardDetails> RandomLootContainers { get; set; }
[JsonPropertyName("sealedAirdropContainer")]
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
/// </summary>
[JsonPropertyName("customMoneyTpls")]
public required List<string> CustomMoneyTpls { get; set; }
public required List<MongoId> CustomMoneyTpls { get; set; }
/// <summary>
/// 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
/// </summary>
[JsonPropertyName("deprioritisedMoneyContainers")]
public required HashSet<string> DeprioritisedMoneyContainers { get; set; }
public required HashSet<MongoId> DeprioritisedMoneyContainers { get; set; }
}
public record RewardDetails
@@ -54,7 +54,7 @@ public record RewardDetails
public bool FoundInRaid { get; set; }
[JsonPropertyName("rewardTplPool")]
public Dictionary<string, double>? RewardTplPool { get; set; }
public Dictionary<MongoId, double>? RewardTplPool { get; set; }
[JsonPropertyName("rewardTypePool")]
public List<MongoId>? RewardTypePool { get; set; }
@@ -78,13 +78,13 @@ public record SealedAirdropContainerSettings
public bool FoundInRaid { get; set; }
[JsonPropertyName("weaponModRewardLimits")]
public required Dictionary<string, MinMax<int>> WeaponModRewardLimits { get; set; }
public required Dictionary<MongoId, MinMax<int>> WeaponModRewardLimits { get; set; }
[JsonPropertyName("rewardTypeLimits")]
public required Dictionary<string, MinMax<int>> RewardTypeLimits { get; set; }
public required Dictionary<MongoId, MinMax<int>> RewardTypeLimits { get; set; }
[JsonPropertyName("ammoBoxWhitelist")]
public required List<string> AmmoBoxWhitelist { get; set; }
public required List<MongoId> AmmoBoxWhitelist { get; set; }
[JsonPropertyName("allowBossItems")]
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
/// </summary>
[JsonPropertyName("lootMaxSpawnLimits")]
public required Dictionary<string, Dictionary<string, int>> LootMaxSpawnLimits { get; set; }
public required Dictionary<string, Dictionary<MongoId, int>> LootMaxSpawnLimits { get; set; }
/// <summary>
/// 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
/// </summary>
[JsonPropertyName("tplsToStripChildItemsFrom")]
public required HashSet<string> TplsToStripChildItemsFrom { get; set; }
public required HashSet<MongoId> TplsToStripChildItemsFrom { get; set; }
/// <summary>
/// Map ids players cannot visit
@@ -232,7 +232,7 @@ public record ContainerRandomisationSettings
/// Some container types don't work when randomised
/// </summary>
[JsonPropertyName("containerTypesToNotRandomise")]
public required HashSet<string> ContainerTypesToNotRandomise { get; set; }
public required HashSet<MongoId> ContainerTypesToNotRandomise { get; set; }
[JsonPropertyName("containerGroupMinSizeMultiplier")]
public double ContainerGroupMinSizeMultiplier { get; set; }
@@ -1,4 +1,5 @@
using System.Text.Json.Serialization;
using SPTarkov.Server.Core.Models.Common;
using SPTarkov.Server.Core.Models.Eft.Common.Tables;
using SPTarkov.Server.Core.Models.Enums;
@@ -28,7 +29,7 @@ public record KarmaLevel
public required Dictionary<string, GenerationData> ItemLimits { get; set; }
[JsonPropertyName("equipmentBlacklist")]
public required Dictionary<EquipmentSlots, List<string>> EquipmentBlacklist { get; set; }
public required Dictionary<EquipmentSlots, List<MongoId>> EquipmentBlacklist { get; set; }
[JsonPropertyName("labsAccessCardChancePercent")]
public double? LabsAccessCardChancePercent { get; set; }
@@ -30,7 +30,7 @@ public record RagfairConfig : BaseConfig
/// Trader ids + should their assorts be listed on flea
/// </summary>
[JsonPropertyName("traders")]
public Dictionary<string, bool> Traders { get; set; }
public required Dictionary<MongoId, bool> Traders { get; set; }
[JsonPropertyName("dynamic")]
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)
/// </summary>
[JsonPropertyName("ignoreQualityPriceVarianceBlacklist")]
public HashSet<string> IgnoreQualityPriceVarianceBlacklist { get; set; }
public HashSet<MongoId> IgnoreQualityPriceVarianceBlacklist { get; set; }
[JsonPropertyName("endTimeSeconds")]
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
/// </summary>
[JsonPropertyName("itemPriceMultiplier")]
public Dictionary<string, double>? ItemPriceMultiplier { get; set; }
public Dictionary<MongoId, double>? ItemPriceMultiplier { get; set; }
[JsonPropertyName("_currencies")]
public string? CurrenciesDescription { get; set; }
@@ -207,13 +207,13 @@ public record Dynamic
/// Percentages to sell offers in each currency
/// </summary>
[JsonPropertyName("currencies")]
public Dictionary<string, double> Currencies { get; set; }
public Dictionary<MongoId, double> Currencies { get; set; }
/// <summary>
/// Item tpls that should be forced to sell as a single item
/// </summary>
[JsonPropertyName("showAsSingleStack")]
public HashSet<string> ShowAsSingleStack { get; set; }
public HashSet<MongoId> ShowAsSingleStack { get; set; }
/// <summary>
/// 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
/// </summary>
[JsonPropertyName("unreasonableModPrices")]
public Dictionary<string, UnreasonableModPrices> UnreasonableModPrices { get; set; }
public Dictionary<MongoId, UnreasonableModPrices> UnreasonableModPrices { get; set; }
/// <summary>
/// Custom rouble prices for items to override values from prices.json
/// </summary>
[JsonPropertyName("itemPriceOverrideRouble")]
public Dictionary<string, double> ItemPriceOverrideRouble { get; set; }
public Dictionary<MongoId, double> ItemPriceOverrideRouble { get; set; }
}
public record PriceRanges
@@ -300,7 +300,7 @@ public record BarterDetails
/// Item Tpls to never be turned into a barter
/// </summary>
[JsonPropertyName("itemTypeBlacklist")]
public HashSet<string> ItemTypeBlacklist { get; set; }
public HashSet<MongoId> ItemTypeBlacklist { get; set; }
}
public record PackDetails
@@ -330,7 +330,7 @@ public record PackDetails
/// item types to allow being a pack
/// </summary>
[JsonPropertyName("itemTypeWhitelist")]
public HashSet<string> ItemTypeWhitelist { get; set; }
public HashSet<MongoId> ItemTypeWhitelist { get; set; }
}
public record OfferAdjustment
@@ -399,7 +399,7 @@ public record RagfairBlacklist
/// Custom blacklist for item Tpls
/// </summary>
[JsonPropertyName("custom")]
public HashSet<string> Custom { get; set; }
public HashSet<MongoId> Custom { get; set; }
/// <summary>
/// BSG blacklist a large number of items from flea, true = use blacklist
@@ -513,16 +513,16 @@ public record TieredFlea
/// key: tpl, value: playerlevel
/// </summary>
[JsonPropertyName("unlocksTpl")]
public Dictionary<string, int> UnlocksTpl { get; set; }
public Dictionary<MongoId, int> UnlocksTpl { get; set; }
/// <summary>
/// key: item type id, value: playerlevel
/// </summary>
[JsonPropertyName("unlocksType")]
public Dictionary<string, int> UnlocksType { get; set; }
public Dictionary<MongoId, int> UnlocksType { get; set; }
[JsonPropertyName("ammoTplUnlocks")]
public Dictionary<string, int>? AmmoTplUnlocks { get; set; }
public Dictionary<MongoId, int>? AmmoTplUnlocks { get; set; }
[JsonPropertyName("ammoTiersEnabled")]
public bool AmmoTiersEnabled { get; set; }
@@ -18,10 +18,10 @@ public record ScavCaseConfig : BaseConfig
public required AmmoRewards AmmoRewards { get; set; }
[JsonPropertyName("rewardItemParentBlacklist")]
public required HashSet<string> RewardItemParentBlacklist { get; set; }
public required HashSet<MongoId> RewardItemParentBlacklist { get; set; }
[JsonPropertyName("rewardItemBlacklist")]
public required HashSet<string> RewardItemBlacklist { get; set; }
public required HashSet<MongoId> RewardItemBlacklist { get; set; }
[JsonPropertyName("allowMultipleMoneyRewardsPerRarity")]
public bool AllowMultipleMoneyRewardsPerRarity { get; set; }
@@ -78,7 +78,7 @@ public record AmmoRewards
public int AmmoRewardChancePercent { get; set; }
[JsonPropertyName("ammoRewardBlacklist")]
public required Dictionary<string, List<string>> AmmoRewardBlacklist { get; set; }
public required Dictionary<string, List<MongoId>> AmmoRewardBlacklist { get; set; }
[JsonPropertyName("ammoRewardValueRangeRub")]
public required Dictionary<string, MinMax<double>> AmmoRewardValueRangeRub { get; set; }
@@ -1,4 +1,5 @@
using System.Text.Json.Serialization;
using SPTarkov.Server.Core.Models.Common;
using SPTarkov.Server.Core.Models.Eft.Common;
using SPTarkov.Server.Core.Models.Enums;
using SPTarkov.Server.Core.Utils.Json.Converters;
@@ -19,7 +20,7 @@ public record SeasonalEventConfig : BaseConfig
[JsonPropertyName("eventGear")]
public required Dictionary<
SeasonalEventType,
Dictionary<string, Dictionary<string, Dictionary<string, int>>>
Dictionary<string, Dictionary<string, Dictionary<MongoId, int>>>
> EventGear { get; set; }
/// <summary>
@@ -28,7 +29,7 @@ public record SeasonalEventConfig : BaseConfig
[JsonPropertyName("eventLoot")]
public required Dictionary<
SeasonalEventType,
Dictionary<string, Dictionary<string, Dictionary<string, int>>>
Dictionary<string, Dictionary<string, Dictionary<MongoId, int>>>
> EventLoot { get; set; }
[JsonPropertyName("events")]
@@ -70,7 +71,7 @@ public record SeasonalEventConfig : BaseConfig
[JsonPropertyName("botAppearanceChanges")]
public required Dictionary<
SeasonalEventType,
Dictionary<string, Dictionary<string, Dictionary<string, int>>>
Dictionary<string, Dictionary<string, Dictionary<MongoId, int>>>
> BotAppearanceChanges { get; set; }
}
@@ -43,7 +43,7 @@ public record UpdateTime
public string Name { get; set; } = string.Empty;
[JsonPropertyName("traderId")]
public string TraderId { get; set; } = string.Empty;
public MongoId TraderId { get; set; } = string.Empty;
/// <summary>
/// Seconds between trader resets
@@ -97,16 +97,16 @@ public record FenceConfig
/// Key: item tpl
/// </summary>
[JsonPropertyName("itemStackSizeOverrideMinMax")]
public required Dictionary<string, MinMax<int>?> ItemStackSizeOverrideMinMax { get; set; }
public required Dictionary<MongoId, MinMax<int>?> ItemStackSizeOverrideMinMax { get; set; }
[JsonPropertyName("itemTypeLimits")]
public required Dictionary<string, int> ItemTypeLimits { get; set; }
public required Dictionary<MongoId, int> ItemTypeLimits { get; set; }
/// <summary>
/// Prevent duplicate offers of items of specific categories by parentId
/// </summary>
[JsonPropertyName("preventDuplicateOffersOfCategory")]
public required List<string> PreventDuplicateOffersOfCategory { get; set; }
public required List<MongoId> PreventDuplicateOffersOfCategory { get; set; }
[JsonPropertyName("regenerateAssortsOnRefresh")]
public bool RegenerateAssortsOnRefresh { get; set; }
@@ -115,7 +115,7 @@ public record FenceConfig
/// Max rouble price before item is not listed on flea
/// </summary>
[JsonPropertyName("itemCategoryRoublePriceLimit")]
public required Dictionary<string, double?> ItemCategoryRoublePriceLimit { get; set; }
public required Dictionary<MongoId, double?> ItemCategoryRoublePriceLimit { get; set; }
/// <summary>
/// Each slotid with % to be removed prior to listing on fence
@@ -136,7 +136,7 @@ public record FenceConfig
public double AmmoMaxPenLimit { get; set; }
[JsonPropertyName("blacklist")]
public required HashSet<string> Blacklist { get; set; }
public required HashSet<MongoId> Blacklist { get; set; }
[JsonPropertyName("coopExtractGift")]
public required CoopExtractReward CoopExtractGift { get; set; }
@@ -82,7 +82,7 @@ public record LootRequest
/// Item tpls + count of items to force include
/// </summary>
[JsonPropertyName("forcedLoot")]
public Dictionary<string, MinMax<int>>? ForcedLoot { get; set; }
public Dictionary<MongoId, MinMax<int>>? ForcedLoot { get; set; }
/// <summary>
/// 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
/// </summary>
/// <returns> List of traders </returns>
public List<string> GetUpdateableTraders()
public List<MongoId> GetUpdateableTraders()
{
return _ragfairConfig.Traders.Keys.ToList();
}
@@ -1,6 +1,7 @@
using SPTarkov.Common.Extensions;
using SPTarkov.DI.Annotations;
using SPTarkov.Server.Core.Helpers;
using SPTarkov.Server.Core.Models.Common;
using SPTarkov.Server.Core.Models.Eft.Common.Tables;
using SPTarkov.Server.Core.Models.Enums;
using SPTarkov.Server.Core.Models.Spt.Bots;
@@ -160,7 +161,7 @@ public class BotEquipmentFilterService(
/// </summary>
/// <param name="botEquipmentRole">equipment role of bot to look up</param>
/// <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];
@@ -1,4 +1,5 @@
using SPTarkov.DI.Annotations;
using SPTarkov.Server.Core.Models.Common;
using SPTarkov.Server.Core.Models.Eft.Common.Tables;
using SPTarkov.Server.Core.Models.Eft.Match;
using SPTarkov.Server.Core.Models.Eft.Profile;
@@ -82,7 +83,7 @@ public class BtrDeliveryService(
.BtrDeliveryList.Add(
new BtrDelivery
{
Id = _hashUtil.Generate(),
Id = new MongoId(),
ScheduledTime = (int)GetBTRDeliveryReturnTimestamp(),
Items = items,
}
@@ -1,6 +1,7 @@
using System.Diagnostics;
using SPTarkov.Common.Extensions;
using SPTarkov.DI.Annotations;
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.Tables;
@@ -414,7 +415,7 @@ public class DatabaseService(
{
foreach (var keyValuePair in table)
{
if (!_hashUtil.IsValidMongoId(keyValuePair.Key))
if (!keyValuePair.Key.IsValidMongoId())
{
_logger.Error($"Invalid {tableType} ID: '{keyValuePair.Key}'");
return false;
@@ -428,7 +429,7 @@ public class DatabaseService(
{
foreach (var keyValuePair in table)
{
if (!_hashUtil.IsValidMongoId(keyValuePair.Key))
if (!keyValuePair.Key.IsValidMongoId())
{
_logger.Error($"Invalid {tableType} ID: '{keyValuePair.Key}'");
return false;
@@ -782,7 +782,7 @@ public class FenceService(
int? assortCount,
CreateFenceAssortsResult assorts,
TraderAssort baseFenceAssortClone,
Dictionary<string, (int current, int max)> itemTypeLimits,
Dictionary<MongoId, (int current, int max)> itemTypeLimits,
int loyaltyLevel
)
{
@@ -1066,8 +1066,8 @@ public class FenceService(
}
protected (int current, int max)? GetMatchingItemLimit(
Dictionary<string, (int current, int max)> itemTypeLimits,
string itemTpl
Dictionary<MongoId, (int current, int max)> itemTypeLimits,
MongoId itemTpl
)
{
foreach (var baseTypeKey in itemTypeLimits.Keys)
@@ -1680,11 +1680,11 @@ public class FenceService(
/// </summary>
/// <param name="limits"> Limits as defined in config </param>
/// <returns> Record, key: item tplId, value: current/max item count allowed </returns>
protected Dictionary<string, (int current, int max)> InitItemLimitCounter(
Dictionary<string, int> limits
protected Dictionary<MongoId, (int current, int max)> InitItemLimitCounter(
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)
{
@@ -1,4 +1,5 @@
using SPTarkov.DI.Annotations;
using SPTarkov.Server.Core.Models.Common;
using SPTarkov.Server.Core.Models.Eft.Common.Tables;
using SPTarkov.Server.Core.Models.Utils;
using LogLevel = SPTarkov.Server.Core.Models.Spt.Logging.LogLevel;
@@ -16,7 +17,7 @@ public class ItemBaseClassService(
)
{
private bool _cacheGenerated;
private Dictionary<string, HashSet<string>> _itemBaseClassesCache;
private Dictionary<MongoId, HashSet<MongoId>> _itemBaseClassesCache = [];
/// <summary>
/// Create cache and store inside ItemBaseClassService <br />
@@ -25,7 +26,7 @@ public class ItemBaseClassService(
public void HydrateItemBaseClassCache()
{
// Clear existing cache
_itemBaseClassesCache = new Dictionary<string, HashSet<string>>();
_itemBaseClassesCache = new Dictionary<MongoId, HashSet<MongoId>>();
var items = _databaseService.GetItems();
var filteredDbItems = items.Where(x =>
@@ -50,12 +51,12 @@ public class ItemBaseClassService(
/// </summary>
/// <param name="itemIdToUpdate"> Item tpl to store base ids against in dictionary </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);
_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);
}
@@ -67,14 +68,14 @@ public class ItemBaseClassService(
/// <param name="itemTpl"> ItemTpl item to check base classes of </param>
/// <param name="baseClasses"> BaseClass base class to check for </param>
/// <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)
{
HydrateItemBaseClassCache();
}
if (string.IsNullOrEmpty(itemTpl))
if (itemTpl.IsEmpty())
{
_logger.Warning("Unable to check itemTpl base class as value passed is null");
@@ -119,7 +120,7 @@ public class ItemBaseClassService(
/// </summary>
/// <param name="itemTemplateId"> ItemTemplateId item to check </param>
/// <returns> True if item is of type Item </returns>
private bool CachedItemIsOfItemType(string itemTemplateId)
private bool CachedItemIsOfItemType(MongoId itemTemplateId)
{
return string.Equals(
_databaseService.GetItems()[itemTemplateId]?.Type,
@@ -133,7 +134,7 @@ public class ItemBaseClassService(
/// </summary>
/// <param name="itemTpl"> ItemTpl item to get base classes for </param>
/// <returns> array of base classes </returns>
public List<string> GetItemBaseClasses(string itemTpl)
public List<MongoId> GetItemBaseClasses(MongoId itemTpl)
{
if (!_cacheGenerated)
{
@@ -171,7 +171,7 @@ public class LocationLifecycleService
Transition = new Transition
{
TransitionType = TransitionType.NONE,
TransitionRaidId = _hashUtil.Generate(),
TransitionRaidId = new MongoId(),
TransitionCount = 0,
VisitedLocations = [],
},
@@ -520,7 +520,7 @@ public class LocationLifecycleService
// Generate randomised reward for taking coop extract
var loot = _lootGenerator.CreateRandomLoot(_traderConfig.Fence.CoopExtractGift);
var parentId = _hashUtil.Generate();
var parentId = new MongoId();
foreach (var itemAndChildren in loot)
{
// Set all root items parent to new id
@@ -1,6 +1,7 @@
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.Tables;
using SPTarkov.Server.Core.Models.Eft.Profile;
using SPTarkov.Server.Core.Models.Enums;
@@ -194,7 +195,7 @@ public class MailSendService(
// add items to message
if (items?.Count > 0)
{
var rootItemParentId = _hashUtil.Generate();
var rootItemParentId = new MongoId();
details.Items.AddRange(items.AdoptOrphanedItems(rootItemParentId));
details.ItemsMaxStorageLifetimeSeconds = maxStorageTimeSeconds;
@@ -366,7 +367,7 @@ public class MailSendService(
dialogWithNpc.Messages.Add(
new Message
{
Id = _hashUtil.Generate(),
Id = new MongoId(),
DateTime = _timeUtil.GetTimeStamp(),
HasRewards = false,
UserId = playerProfile.CharacterData.PmcData.Id,
@@ -387,7 +388,7 @@ public class MailSendService(
{
Message message = new()
{
Id = _hashUtil.Generate(),
Id = new MongoId(),
UserId = dialogId,
MessageType = messageDetails.Sender,
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)
if (parentItem?.ParentId is null)
{
parentItem.ParentId = _hashUtil.Generate();
parentItem.ParentId = new MongoId();
}
// Prep return object
@@ -137,9 +137,9 @@ public class CustomItemService(
/// </summary>
/// <param name="newId"> ID supplied to code </param>
/// <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>
@@ -1,4 +1,5 @@
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.Tables;
@@ -128,8 +129,10 @@ public class RagfairLinkedItemService(
}
// Get the first cylinder filter tpl
var cylinderTpl = cylinderMod.Props?.Filters?[0].Filter?.FirstOrDefault();
if (string.IsNullOrEmpty(cylinderTpl))
var cylinderTpl =
cylinderMod.Props?.Filters?[0].Filter?.FirstOrDefault() ?? new MongoId(null);
if (!cylinderTpl.IsValidMongoId())
{
// No cylinder, nothing to do
return;
@@ -1,6 +1,7 @@
using SPTarkov.Common.Extensions;
using SPTarkov.DI.Annotations;
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.Ragfair;
using SPTarkov.Server.Core.Models.Spt.Config;
@@ -268,7 +269,7 @@ public class RagfairOfferService(
var unstackedItems = UnstackOfferItems(playerOffer.Items);
// 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)
{
// Refresh root items' parentIds
@@ -209,7 +209,7 @@ public class SeasonalEventService(
/// </summary>
/// <param name="eventName">Name of event to get gear changes for</param>
/// <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
)
{
@@ -221,7 +221,7 @@ public class SeasonalEventService(
/// </summary>
/// <param name="eventName">Name of event to get gear changes for</param>
/// <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
)
{
@@ -1,7 +1,6 @@
using System.IO.Hashing;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using SPTarkov.DI.Annotations;
namespace SPTarkov.Server.Core.Utils;
@@ -9,47 +8,6 @@ namespace SPTarkov.Server.Core.Utils;
[Injectable(InjectionType.Singleton)]
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)
{
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);
}
[GeneratedRegex("^[a-fA-F0-9]{24}$")]
private static partial Regex MongoIdRegex();
}
public enum HashingAlgorithm
@@ -2,6 +2,7 @@ using System.Collections.Concurrent;
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.Tables;
using SPTarkov.Server.Core.Models.Eft.Ragfair;
using SPTarkov.Server.Core.Models.Utils;
@@ -137,10 +138,10 @@ public class RagfairOfferHolder(
// Keep generating IDs until we get a unique one
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 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."
);
}
}