0
0
mirror of https://github.com/sp-tarkov/loot-dump-processor.git synced 2025-02-12 22:30:44 -05:00

Refactored key generation and improved type safety across storage components

This commit is contained in:
bluextx 2025-01-11 11:50:02 +03:00
parent 02802ddc10
commit 9e9933bd32
20 changed files with 151 additions and 124 deletions

View File

@ -1,6 +1,3 @@
using System.Text.Json.Serialization;
namespace LootDumpProcessor.Model.Config;
public class ReaderConfig

View File

@ -1,22 +1,24 @@
using System.Text.Json.Serialization;
using LootDumpProcessor.Storage;
using LootDumpProcessor.Utils;
namespace LootDumpProcessor.Model.Processing;
public class LooseLootCounts : IKeyable
{
[JsonPropertyName("__id__")] public string __ID { get; set; } = KeyGenerator.GetNextKey();
[JsonPropertyName("__id__")] private string Id { get; set; }
public IKey Counts { get; set; }
// public IKey Items { get; set; }
public IKey ItemProperties { get; set; }
public List<int> MapSpawnpointCount { get; set; } = new();
public IKey GetKey()
public LooseLootCounts(string id, IKey counts, IKey itemProperties)
{
return new FlatUniqueKey(new[] { __ID });
ArgumentException.ThrowIfNullOrWhiteSpace(id);
Id = id;
Counts = counts ?? throw new ArgumentNullException(nameof(counts));
ItemProperties = itemProperties ?? throw new ArgumentNullException(nameof(itemProperties));
}
public IKey GetKey() => new FlatUniqueKey([Id]);
}

View File

@ -7,70 +7,63 @@ namespace LootDumpProcessor.Model;
public class Template : IKeyable, ICloneable
{
[JsonIgnore] public string __ID { get; } = KeyGenerator.GetNextKey();
[JsonIgnore] public string internalId { get; }
public string Id { get; set; }
public bool IsContainer { get; set; }
public bool UseGravity { get; set; }
public bool RandomRotation { get; set; }
public Vector3 Position { get; set; }
public Vector3 Rotation { get; set; }
public bool IsGroupPosition { get; set; }
public List<GroupPosition> GroupPositions { get; set; }
public bool IsAlwaysSpawn { get; set; }
public string Root { get; set; }
public List<Item> Items { get; set; }
public Template(string internalId, string id, bool isContainer, bool useGravity, bool randomRotation,
Vector3 position, Vector3 rotation, bool isGroupPosition, List<GroupPosition> groupPositions,
bool isAlwaysSpawn, string root, List<Item> items)
{
this.internalId = internalId;
Id = id;
IsContainer = isContainer;
UseGravity = useGravity;
RandomRotation = randomRotation;
Position = position;
Rotation = rotation;
IsGroupPosition = isGroupPosition;
GroupPositions = groupPositions;
IsAlwaysSpawn = isAlwaysSpawn;
Root = root;
Items = items;
}
public string? Id { get; set; }
public bool? IsContainer { get; set; }
public bool? UseGravity { get; set; }
public bool? RandomRotation { get; set; }
public Vector3? Position { get; set; }
public Vector3? Rotation { get; set; }
public bool? IsGroupPosition { get; set; }
public List<GroupPosition>? GroupPositions { get; set; }
public bool? IsAlwaysSpawn { get; set; }
public string? Root { get; set; }
public required List<Item> Items { get; set; }
protected bool Equals(Template other) => Id == other.Id;
private bool Equals(Template other) => Id == other.Id;
public override bool Equals(object? obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != GetType()) return false;
return Equals((Template)obj);
return obj.GetType() == GetType() && Equals((Template)obj);
}
public override int GetHashCode() => Id != null ? Id.GetHashCode() : 0;
public IKey GetKey()
{
return new FlatUniqueKey(new[] { __ID });
}
public IKey GetKey() => new FlatUniqueKey([internalId]);
public object Clone() => new Template
{
Id = Id,
IsContainer = IsContainer,
UseGravity = UseGravity,
RandomRotation = RandomRotation,
Position = ProcessorUtil.Copy(Position),
Rotation = ProcessorUtil.Copy(Rotation),
IsGroupPosition = IsGroupPosition,
GroupPositions = ProcessorUtil.Copy(GroupPositions),
IsAlwaysSpawn = IsAlwaysSpawn,
Root = Root,
Items = ProcessorUtil.Copy(Items)
};
(
internalId,
Id,
IsContainer,
UseGravity,
RandomRotation,
ProcessorUtil.Copy(Position),
ProcessorUtil.Copy(Rotation),
IsGroupPosition,
ProcessorUtil.Copy(GroupPositions),
IsAlwaysSpawn,
Root,
ProcessorUtil.Copy(Items)
);
}

View File

@ -1,15 +1,19 @@
using LootDumpProcessor.Model;
using System.Globalization;
using LootDumpProcessor.Model;
using LootDumpProcessor.Model.Processing;
using LootDumpProcessor.Utils;
namespace LootDumpProcessor.Process;
public class ComposedKeyGenerator(ITarkovItemsProvider tarkovItemsProvider) : IComposedKeyGenerator
public class ComposedKeyGenerator(ITarkovItemsProvider tarkovItemsProvider, IKeyGenerator keyGenerator)
: IComposedKeyGenerator
{
private readonly ITarkovItemsProvider _tarkovItemsProvider =
tarkovItemsProvider ?? throw new ArgumentNullException(nameof(tarkovItemsProvider));
public ComposedKey Generate(IEnumerable<Item> items)
private readonly IKeyGenerator
_keyGenerator = keyGenerator ?? throw new ArgumentNullException(nameof(keyGenerator));
public ComposedKey Generate(IReadOnlyList<Item>? items)
{
var key = items?.Select(i => i.Tpl)
.Where(i => !string.IsNullOrEmpty(i) &&
@ -17,8 +21,9 @@ public class ComposedKeyGenerator(ITarkovItemsProvider tarkovItemsProvider) : IC
.Cast<string>()
.Select(i => (double)i.GetHashCode())
.Sum()
.ToString() ?? KeyGenerator.GetNextKey();
var firstItem = items?.FirstOrDefault();
.ToString(CultureInfo.InvariantCulture) ?? _keyGenerator.Generate();
var firstItem = items?[0];
return new ComposedKey(key, firstItem);
}
}

View File

@ -4,5 +4,5 @@ namespace LootDumpProcessor.Process;
public interface IComposedKeyGenerator
{
ComposedKey Generate(IEnumerable<Item> items);
ComposedKey Generate(IReadOnlyList<Item>? items);
}

View File

@ -0,0 +1,6 @@
namespace LootDumpProcessor.Process;
public interface IKeyGenerator
{
string Generate();
}

View File

@ -0,0 +1,13 @@
namespace LootDumpProcessor.Process;
public class NumericKeyGenerator : IKeyGenerator
{
private ulong _currentKey;
public string Generate()
{
var key = _currentKey;
Interlocked.Increment(ref _currentKey);
return key.ToString();
}
}

View File

@ -24,7 +24,7 @@ public class MultithreadSteppedDumpProcessor(
IStaticContainersProcessor staticContainersProcessor,
IAmmoProcessor ammoProcessor,
ILooseLootProcessor looseLootProcessor,
ILogger<MultithreadSteppedDumpProcessor> logger
ILogger<MultithreadSteppedDumpProcessor> logger, IKeyGenerator keyGenerator
)
: IDumpProcessor
{
@ -43,6 +43,9 @@ public class MultithreadSteppedDumpProcessor(
private readonly ILogger<MultithreadSteppedDumpProcessor> _logger =
logger ?? throw new ArgumentNullException(nameof(logger));
private readonly IKeyGenerator
_keyGenerator = keyGenerator ?? throw new ArgumentNullException(nameof(keyGenerator));
private static readonly IDataStorage _dataStorage = DataStorageFactory.GetInstance();
public Dictionary<OutputFileType, object> ProcessDumps(List<PartialData> dumps)
@ -225,17 +228,17 @@ public class MultithreadSteppedDumpProcessor(
var lockObjectContainerCounts = new object();
var lockObjectCounts = new object();
var looseLootCounts = new LooseLootCounts();
var lockObjectDictionaryCounts = new object();
var dictionaryCounts = new FlatKeyableDictionary<string, int>();
looseLootCounts.Counts = dictionaryCounts.GetKey();
var dictionaryCounts = new FlatKeyableDictionary<string, int>(_keyGenerator.Generate());
var lockObjectDictionaryItemProperties = new object();
var dictionaryItemProperties = new FlatKeyableDictionary<string, FlatKeyableList<Template>>();
var dictionaryItemProperties =
new FlatKeyableDictionary<string, FlatKeyableList<Template>>(_keyGenerator.Generate());
var actualDictionaryItemProperties = new FlatKeyableDictionary<string, IKey>();
looseLootCounts.ItemProperties = actualDictionaryItemProperties.GetKey();
var actualDictionaryItemProperties = new FlatKeyableDictionary<string, IKey>(_keyGenerator.Generate());
var looseLootCounts = new LooseLootCounts(_keyGenerator.Generate(), dictionaryCounts.GetKey(),
actualDictionaryItemProperties.GetKey());
dumpProcessData.LooseLootCounts.Add(mapName, looseLootCounts.GetKey());
@ -316,7 +319,7 @@ public class MultithreadSteppedDumpProcessor(
{
if (!dictionaryItemProperties.TryGetValue(uniqueKey, out var values))
{
values = new FlatKeyableList<Template>();
values = new FlatKeyableList<Template>(_keyGenerator.Generate());
dictionaryItemProperties.Add(uniqueKey, values);
actualDictionaryItemProperties.Add(uniqueKey, values.GetKey());
}

View File

@ -33,7 +33,7 @@ public class FileProcessor : IFileProcessor
var staticLoot = new List<Template>();
foreach (var item in parsedData.Data.Data.LocationLoot.Loot)
if (item.IsContainer ?? false) staticLoot.Add(item);
if (item.IsContainer) staticLoot.Add(item);
else looseLoot.Add(item);
parsedData.Data = null;

View File

@ -11,7 +11,7 @@ namespace LootDumpProcessor.Process.Processor.v2.LooseLootProcessor;
public class LooseLootProcessor(
ILogger<LooseLootProcessor> logger, IDataStorage dataStorage, ITarkovItemsProvider tarkovItemsProvider,
IComposedKeyGenerator composedKeyGenerator
IComposedKeyGenerator composedKeyGenerator, IKeyGenerator keyGenerator
)
: ILooseLootProcessor
{
@ -27,6 +27,9 @@ public class LooseLootProcessor(
private readonly IComposedKeyGenerator _composedKeyGenerator =
composedKeyGenerator ?? throw new ArgumentNullException(nameof(composedKeyGenerator));
private readonly IKeyGenerator
_keyGenerator = keyGenerator ?? throw new ArgumentNullException(nameof(keyGenerator));
public PreProcessedLooseLoot PreProcessLooseLoot(List<Template> looseLoot)
{
var preProcessedLoot = new PreProcessedLooseLoot
@ -34,7 +37,8 @@ public class LooseLootProcessor(
Counts = new Dictionary<string, int>()
};
var itemPropertiesDictionary = new SubdivisionedKeyableDictionary<string, List<Template>>();
var itemPropertiesDictionary =
new SubdivisionedKeyableDictionary<string, List<Template>>(_keyGenerator.Generate());
preProcessedLoot.ItemProperties = (AbstractKey)itemPropertiesDictionary.GetKey();
preProcessedLoot.MapSpawnpointCount = looseLoot.Count;
@ -55,7 +59,7 @@ public class LooseLootProcessor(
if (!itemPropertiesDictionary.TryGetValue(sanitizedId, out var templates))
{
templates = new FlatKeyableList<Template>();
templates = new FlatKeyableList<Template>(_keyGenerator.Generate());
itemPropertiesDictionary.Add(sanitizedId, templates);
}

View File

@ -20,7 +20,7 @@ public class StaticContainersProcessor : IStaticContainersProcessor
var locationLoot = rawMapDump.Data.LocationLoot;
var mapId = locationLoot.Id.ToLower();
var staticLootPositions = locationLoot.Loot
.Where(loot => loot.IsContainer.GetValueOrDefault())
.Where(loot => loot.IsContainer)
.OrderBy(loot => loot.Id)
.ToList();
@ -44,7 +44,8 @@ public class StaticContainersProcessor : IStaticContainersProcessor
_logger.LogDebug("Added static weapon with ID {WeaponId} to Map {MapId}.", copiedLoot.Id, mapId);
}
var forcedStaticItems = LootDumpProcessorContext.GetForcedItems().TryGetValue(mapId, out var forcedItems)
var forcedStaticItems = LootDumpProcessorContext.GetForcedItems()
.TryGetValue(mapId, out List<StaticForced>? forcedItems)
? forcedItems
: new List<StaticForced>();
@ -61,13 +62,13 @@ public class StaticContainersProcessor : IStaticContainersProcessor
public IReadOnlyList<Template> CreateDynamicStaticContainers(RootData rawMapDump)
{
var dynamicContainers = rawMapDump.Data.LocationLoot.Loot
.Where(loot => loot.IsContainer.GetValueOrDefault() &&
.Where(loot => loot.IsContainer &&
!LootDumpProcessorContext.GetStaticWeaponIds().Contains(loot.Items.FirstOrDefault()?.Tpl))
.ToList();
foreach (var container in dynamicContainers)
{
if (container.Items == null || !container.Items.Any())
if (container.Items == null || container.Items.Count == 0)
{
_logger.LogWarning("Dynamic container with ID {ContainerId} has no items.", container.Id);
continue;

View File

@ -38,6 +38,7 @@ public static class Program
services.AddSingleton<ITarkovItemsProvider, TarkovItemsProvider>();
services.AddSingleton<IDataStorage>(_ => DataStorageFactory.GetInstance());
services.AddSingleton<IKeyGenerator, NumericKeyGenerator>();
services.AddTransient<IComposedKeyGenerator, ComposedKeyGenerator>();
services.AddTransient<IIntakeReader, JsonFileIntakeReader>();

View File

@ -1,11 +1,16 @@
using System.Text.Json.Serialization;
using LootDumpProcessor.Utils;
namespace LootDumpProcessor.Storage.Collections;
public class FlatKeyableDictionary<K, V> : Dictionary<K, V>, IKeyable
{
[JsonPropertyName("__id__")] public string __ID { get; set; } = KeyGenerator.GetNextKey();
[JsonPropertyName("__id__")] private string Id { get; set; }
public IKey GetKey() => new FlatUniqueKey([__ID]);
public FlatKeyableDictionary(string id)
{
ArgumentException.ThrowIfNullOrWhiteSpace(id);
Id = id;
}
public IKey GetKey() => new FlatUniqueKey([Id]);
}

View File

@ -1,10 +1,14 @@
using LootDumpProcessor.Utils;
namespace LootDumpProcessor.Storage.Collections;
public class FlatKeyableList<T> : List<T>, IKeyable
{
public string __ID { get; } = KeyGenerator.GetNextKey();
public string Id { get; }
public IKey GetKey() => new FlatUniqueKey([__ID]);
public FlatKeyableList(string id)
{
ArgumentException.ThrowIfNullOrWhiteSpace(id);
Id = id;
}
public IKey GetKey() => new FlatUniqueKey([Id]);
}

View File

@ -1,11 +1,16 @@
using System.Text.Json.Serialization;
using LootDumpProcessor.Utils;
namespace LootDumpProcessor.Storage.Collections;
public class SubdivisionedKeyableDictionary<K, V> : Dictionary<K, V>, IKeyable
{
[JsonPropertyName("__id__")] public string __ID { get; set; } = KeyGenerator.GetNextKey();
public SubdivisionedKeyableDictionary(string id)
{
ArgumentException.ThrowIfNullOrWhiteSpace(id);
Id = id;
}
[JsonPropertyName("__id__")] private string Id { get; set; }
public string? Extras
{
@ -24,10 +29,10 @@ public class SubdivisionedKeyableDictionary<K, V> : Dictionary<K, V>, IKeyable
"dictionaries"
};
subdivisions.AddRange(ExtraSubdivisions);
subdivisions.Add(__ID);
subdivisions.Add(Id);
return new SubdivisionedUniqueKey(subdivisions.ToArray());
}
return new SubdivisionedUniqueKey(["dictionaries", __ID]);
return new SubdivisionedUniqueKey(["dictionaries", Id]);
}
}

View File

@ -17,7 +17,7 @@ public static class DataStorageFactory
private static IDataStorage GetInstance(DataStorageTypes type)
{
if (DataStorage.TryGetValue(type, out var dataStorage)) return dataStorage;
dataStorage = type switch
{
DataStorageTypes.File => new FileDataStorage(),

View File

@ -9,7 +9,8 @@ public class FileDataStorage : IDataStorage
public bool Exists(IKey key) => StoreHandlerFactory.GetInstance(key.GetKeyType()).Exists(key);
public T GetItem<T>(IKey key) where T : IKeyable => StoreHandlerFactory.GetInstance(key.GetKeyType()).Retrieve<T>(key);
public T GetItem<T>(IKey key) where T : IKeyable =>
StoreHandlerFactory.GetInstance(key.GetKeyType()).Retrieve<T>(key);
public void Clear()
{

View File

@ -10,7 +10,7 @@ public class StoreHandlerFactory
public static IStoreHandler GetInstance(KeyType type)
{
if (Handlers.TryGetValue(type, out var handler)) return handler;
handler = type switch
{
KeyType.Unique => new FlatStoreHandler(),

View File

@ -2,31 +2,33 @@ using System.Text.RegularExpressions;
namespace LootDumpProcessor.Utils;
public static class FileDateParser
public static partial class FileDateParser
{
private static readonly Regex _fileDateRegex =
new(".*([0-9]{4})[-]([0-9]{2})[-]([0-9]{2})[_]([0-9]{2})[-]([0-9]{2})[-]([0-9]{2}).*");
private static readonly Regex FileDateRegex = GetRegex();
public static bool TryParseFileDate(string fileName, out DateTime? date)
{
date = null;
if (!_fileDateRegex.IsMatch(fileName))
return false;
var match = _fileDateRegex.Match(fileName);
if (!FileDateRegex.IsMatch(fileName)) return false;
var match = FileDateRegex.Match(fileName);
var year = match.Groups[1].Value;
var month = match.Groups[2].Value;
var day = match.Groups[3].Value;
var hour = match.Groups[4].Value;
var mins = match.Groups[5].Value;
var secs = match.Groups[6].Value;
var minutes = match.Groups[5].Value;
var seconds = match.Groups[6].Value;
date = new DateTime(
int.Parse(year),
int.Parse(month),
int.Parse(day),
int.Parse(hour),
int.Parse(mins),
int.Parse(secs)
int.Parse(minutes),
int.Parse(seconds)
);
return true;
}
[GeneratedRegex(".*([0-9]{4})[-]([0-9]{2})[-]([0-9]{2})[_]([0-9]{2})[-]([0-9]{2})[-]([0-9]{2}).*")]
private static partial Regex GetRegex();
}

View File

@ -1,15 +0,0 @@
namespace LootDumpProcessor.Utils;
public class KeyGenerator
{
private static long currentKey = 0L;
private static object lockObject = new();
public static string GetNextKey()
{
lock (lockObject)
{
return $"{++currentKey}";
}
}
}