From f2fd256aac0a82e2e85d574400d9531ef27374aa Mon Sep 17 00:00:00 2001
From: BlueXTX <48766766+BlueXTX@users.noreply.github.com>
Date: Mon, 13 Jan 2025 20:05:36 +0300
Subject: [PATCH] Get rid of static and use dependency injection instead (#8)
* Improved thread safety and async processing in dump processor components
* Removed unused _processedDumps field and simplified variable scope in QueuePipeline
* Refactored service registration into dedicated extension methods
* Added configuration binding and environment variables support
* Refactored collector initialization to use dependency injection
* Refactored data storage to use dependency injection
* Refactored configuration models to use records and added validation
* Refactored static loot configuration to use dependency injection
The changes include:
- Moved static weapon IDs and forced items from LootDumpProcessorContext to ForcedStatic record
- Added ForcedStatic configuration injection in StaticLootProcessor and StaticContainerProcessor
- Improved immutability by using read-only collections in ForcedStatic model
- Simplified LootDumpProcessorContext by removing unused methods
* Refactored configuration access to use dependency injection consistently
* Fixed ForcedStatic configuration
* Refactored forced items configuration to use async provider pattern
The changes introduce a new `IForcedItemsProvider` abstraction to handle loading and caching of forced static and loose loot configurations. This improves the code by:
1. Making configuration loading asynchronous
2. Implementing caching of loaded configurations
3. Centralizing forced items configuration access
4. Removing direct file system dependencies from processors
5. Improving testability through dependency injection
The change also updates related processors and interfaces to use async/await pattern consistently.
* Refactored loose loot processor to use async forced items provider
* Reorganized processor and service components into dedicated namespaces
---
source/LootDumpProcessor/GCHandler.cs | 9 --
.../LootDumpProcessor.csproj | 4 +
.../LootDumpProcessorContext.cs | 80 --------------
.../Model/Config/CollectorConfig.cs | 14 ++-
.../LootDumpProcessor/Model/Config/Config.cs | 26 +++--
.../Model/Config/DataStorageConfig.cs | 12 +-
.../Model/Config/DumpProcessorConfig.cs | 12 +-
.../Model/Config/ForcedStatic.cs | 13 ++-
.../Model/Config/ForcedStaticDto.cs | 13 +++
.../Model/Config/IntakeReaderConfig.cs | 9 +-
.../Model/Config/ProcessorConfig.cs | 13 +--
.../Model/Config/ReaderConfig.cs | 17 +--
.../Model/Config/WriterConfig.cs | 9 +-
.../Process/Collector/CollectorFactory.cs | 18 ---
.../Process/Collector/DumpCollector.cs | 26 +++--
.../Process/IKeyGenerator.cs | 6 -
.../{v2 => }/AmmoProcessor/AmmoProcessor.cs | 3 +-
.../{v2 => }/AmmoProcessor/IAmmoProcessor.cs | 2 +-
.../MultithreadSteppedDumpProcessor.cs | 48 ++++----
.../Processor/FileProcessor/FileProcessor.cs | 45 ++++----
.../Processor/FileProcessor/IFileProcessor.cs | 2 +-
.../LooseLootProcessor/ILooseLootProcessor.cs | 8 +-
.../LooseLootProcessor/LooseLootProcessor.cs | 34 ++++--
.../IStaticContainersProcessor.cs | 11 ++
.../StaticContainerProcessor.cs | 34 +++---
.../IStaticLootProcessor.cs | 4 +-
.../StaticLootProcessor.cs | 14 ++-
.../IStaticContainersProcessor.cs | 11 --
.../Process/QueuePipeline.cs | 40 ++++---
.../Reader/Filters/JsonDumpFileFilter.cs | 10 +-
.../Reader/Intake/JsonFileIntakeReader.cs | 14 ++-
.../ComposedKeyGenerator.cs | 4 +-
.../IComposedKeyGenerator.cs | 2 +-
.../ForcedItemsProvider.cs | 56 ++++++++++
.../IForcedItemsProvider.cs | 11 ++
.../Services/KeyGenerator/IKeyGenerator.cs | 6 +
.../KeyGenerator}/NumericKeyGenerator.cs | 2 +-
.../ITarkovItemsProvider.cs | 2 +-
.../TarkovItemsProvider.cs | 12 +-
.../Process/Writer/FileWriter.cs | 9 +-
.../Process/Writer/WriterFactory.cs | 8 --
source/LootDumpProcessor/Program.cs | 34 +-----
.../ServiceCollectionExtensions.cs | 103 ++++++++++++++++++
.../Storage/DataStorageFactory.cs | 32 ------
.../Implementations/File/FileDataStorage.cs | 11 +-
.../File/Handlers/AbstractStoreHandler.cs | 10 +-
.../File/Handlers/FlatStoreHandler.cs | 5 +-
.../Handlers/SubdivisionedStoreHandler.cs | 5 +-
.../File/StoreHandlerFactory.cs | 15 ++-
49 files changed, 489 insertions(+), 399 deletions(-)
delete mode 100644 source/LootDumpProcessor/GCHandler.cs
delete mode 100644 source/LootDumpProcessor/LootDumpProcessorContext.cs
create mode 100644 source/LootDumpProcessor/Model/Config/ForcedStaticDto.cs
delete mode 100644 source/LootDumpProcessor/Process/Collector/CollectorFactory.cs
delete mode 100644 source/LootDumpProcessor/Process/IKeyGenerator.cs
rename source/LootDumpProcessor/Process/Processor/{v2 => }/AmmoProcessor/AmmoProcessor.cs (93%)
rename source/LootDumpProcessor/Process/Processor/{v2 => }/AmmoProcessor/IAmmoProcessor.cs (80%)
rename source/LootDumpProcessor/Process/Processor/{v2 => }/LooseLootProcessor/ILooseLootProcessor.cs (62%)
rename source/LootDumpProcessor/Process/Processor/{v2 => }/LooseLootProcessor/LooseLootProcessor.cs (88%)
create mode 100644 source/LootDumpProcessor/Process/Processor/StaticContainersProcessor/IStaticContainersProcessor.cs
rename source/LootDumpProcessor/Process/Processor/{v2 => }/StaticContainersProcessor/StaticContainerProcessor.cs (64%)
rename source/LootDumpProcessor/Process/Processor/{v2 => }/StaticLootProcessor/IStaticLootProcessor.cs (65%)
rename source/LootDumpProcessor/Process/Processor/{v2 => }/StaticLootProcessor/StaticLootProcessor.cs (85%)
delete mode 100644 source/LootDumpProcessor/Process/Processor/v2/StaticContainersProcessor/IStaticContainersProcessor.cs
rename source/LootDumpProcessor/Process/{ => Services/ComposedKeyGenerator}/ComposedKeyGenerator.cs (84%)
rename source/LootDumpProcessor/Process/{ => Services/ComposedKeyGenerator}/IComposedKeyGenerator.cs (66%)
create mode 100644 source/LootDumpProcessor/Process/Services/ForcedItemsProvider/ForcedItemsProvider.cs
create mode 100644 source/LootDumpProcessor/Process/Services/ForcedItemsProvider/IForcedItemsProvider.cs
create mode 100644 source/LootDumpProcessor/Process/Services/KeyGenerator/IKeyGenerator.cs
rename source/LootDumpProcessor/Process/{ => Services/KeyGenerator}/NumericKeyGenerator.cs (79%)
rename source/LootDumpProcessor/Process/{ => Services/TarkovItemsProvider}/ITarkovItemsProvider.cs (74%)
rename source/LootDumpProcessor/Process/{ => Services/TarkovItemsProvider}/TarkovItemsProvider.cs (91%)
delete mode 100644 source/LootDumpProcessor/Process/Writer/WriterFactory.cs
create mode 100644 source/LootDumpProcessor/ServiceCollectionExtensions.cs
delete mode 100644 source/LootDumpProcessor/Storage/DataStorageFactory.cs
diff --git a/source/LootDumpProcessor/GCHandler.cs b/source/LootDumpProcessor/GCHandler.cs
deleted file mode 100644
index f4efcb9..0000000
--- a/source/LootDumpProcessor/GCHandler.cs
+++ /dev/null
@@ -1,9 +0,0 @@
-namespace LootDumpProcessor;
-
-public static class GCHandler
-{
- public static void Collect()
- {
- if (LootDumpProcessorContext.GetConfig().ManualGarbageCollectionCalls) GC.Collect();
- }
-}
\ No newline at end of file
diff --git a/source/LootDumpProcessor/LootDumpProcessor.csproj b/source/LootDumpProcessor/LootDumpProcessor.csproj
index 64cb73e..16c0bb7 100644
--- a/source/LootDumpProcessor/LootDumpProcessor.csproj
+++ b/source/LootDumpProcessor/LootDumpProcessor.csproj
@@ -11,7 +11,11 @@
+
+
+
+
diff --git a/source/LootDumpProcessor/LootDumpProcessorContext.cs b/source/LootDumpProcessor/LootDumpProcessorContext.cs
deleted file mode 100644
index 9c50f90..0000000
--- a/source/LootDumpProcessor/LootDumpProcessorContext.cs
+++ /dev/null
@@ -1,80 +0,0 @@
-using System.Text.Json;
-using LootDumpProcessor.Model.Config;
-using LootDumpProcessor.Model.Output.StaticContainer;
-using LootDumpProcessor.Serializers.Json;
-using LootDumpProcessor.Serializers.Yaml;
-
-namespace LootDumpProcessor;
-
-public static class LootDumpProcessorContext
-{
- private static Config? _config;
- private static readonly object _configLock = new();
- private static ForcedStatic? _forcedStatic;
- private static readonly object _forcedStaticLock = new();
- private static HashSet? _staticWeaponIds;
- private static readonly object _staticWeaponIdsLock = new();
- private static Dictionary>? _forcedItems;
- private static readonly object _forcedItemsLock = new();
- private static Dictionary>? _forcedLoose;
- private static readonly object _forcedLooseLock = new();
-
- public static Config GetConfig()
- {
- lock (_configLock)
- {
- if (_config == null)
- // This is the only instance where manual selection of the serializer is required
- // after this, GetInstance() for the JsonSerializerFactory should used without
- // parameters
- _config = JsonSerializer.Deserialize(File.ReadAllText("./Config/config.json"),
- JsonSerializerSettings.Default);
- }
-
- return _config;
- }
-
- public static ForcedStatic GetForcedStatic()
- {
- lock (_forcedStaticLock)
- {
- if (_forcedStatic == null)
- _forcedStatic = Yaml.Deserializer
- .Deserialize(File.ReadAllText("./Config/forced_static.yaml"));
- }
-
- return _forcedStatic;
- }
-
- public static HashSet GetStaticWeaponIds()
- {
- lock (_staticWeaponIdsLock)
- {
- if (_staticWeaponIds == null) _staticWeaponIds = GetForcedStatic().StaticWeaponIds.ToHashSet();
- }
-
- return _staticWeaponIds;
- }
-
- public static Dictionary> GetForcedItems()
- {
- lock (_forcedItemsLock)
- {
- if (_forcedItems == null) _forcedItems = GetForcedStatic().ForcedItems;
- }
-
- return _forcedItems;
- }
-
- public static Dictionary> GetForcedLooseItems()
- {
- lock (_forcedLooseLock)
- {
- if (_forcedLoose == null)
- _forcedLoose = Yaml.Deserializer.Deserialize>>(
- File.ReadAllText("./Config/forced_loose.yaml"));
- }
-
- return _forcedLoose;
- }
-}
\ No newline at end of file
diff --git a/source/LootDumpProcessor/Model/Config/CollectorConfig.cs b/source/LootDumpProcessor/Model/Config/CollectorConfig.cs
index 9c5d39e..42f8685 100644
--- a/source/LootDumpProcessor/Model/Config/CollectorConfig.cs
+++ b/source/LootDumpProcessor/Model/Config/CollectorConfig.cs
@@ -1,10 +1,12 @@
+using System.ComponentModel.DataAnnotations;
+using JetBrains.Annotations;
using LootDumpProcessor.Process.Collector;
namespace LootDumpProcessor.Model.Config;
-public class CollectorConfig
-{
- public CollectorType CollectorType { get; set; }
- public int MaxEntitiesBeforeDumping { get; set; }
- public string DumpLocation { get; set; }
-}
\ No newline at end of file
+[UsedImplicitly]
+public record CollectorConfig(
+ [Required] CollectorType CollectorType,
+ [Required] int MaxEntitiesBeforeDumping,
+ [Required] string DumpLocation
+);
\ No newline at end of file
diff --git a/source/LootDumpProcessor/Model/Config/Config.cs b/source/LootDumpProcessor/Model/Config/Config.cs
index 2d0f9a0..60f93b7 100644
--- a/source/LootDumpProcessor/Model/Config/Config.cs
+++ b/source/LootDumpProcessor/Model/Config/Config.cs
@@ -1,15 +1,19 @@
+using System.ComponentModel.DataAnnotations;
+using JetBrains.Annotations;
+
namespace LootDumpProcessor.Model.Config;
-public class Config
+[UsedImplicitly]
+public record Config
{
- public string ServerLocation { get; set; } = string.Empty;
- public bool ManualGarbageCollectionCalls { get; set; }
- public DataStorageConfig DataStorageConfig { get; set; }
- public ReaderConfig ReaderConfig { get; set; }
- public ProcessorConfig ProcessorConfig { get; set; }
- public DumpProcessorConfig DumpProcessorConfig { get; set; }
- public WriterConfig WriterConfig { get; set; }
- public CollectorConfig CollectorConfig { get; set; }
- public Dictionary ContainerIgnoreList { get; set; }
- public List MapsToProcess { get; set; }
+ [Required] public string ServerLocation { get; init; } = string.Empty;
+ [Required] public bool ManualGarbageCollectionCalls { get; init; }
+ [Required] public DataStorageConfig DataStorageConfig { get; init; } = null!;
+ [Required] public ReaderConfig ReaderConfig { get; init; } = null!;
+ [Required] public ProcessorConfig ProcessorConfig { get; init; } = null!;
+ [Required] public DumpProcessorConfig DumpProcessorConfig { get; init; } = null!;
+ [Required] public WriterConfig WriterConfig { get; init; } = null!;
+ [Required] public CollectorConfig CollectorConfig { get; init; } = null!;
+ [Required] public IReadOnlyDictionary ContainerIgnoreList { get; init; } = null!;
+ [Required] public IReadOnlyList MapsToProcess { get; init; } = null!;
}
\ No newline at end of file
diff --git a/source/LootDumpProcessor/Model/Config/DataStorageConfig.cs b/source/LootDumpProcessor/Model/Config/DataStorageConfig.cs
index a6f9def..612bba4 100644
--- a/source/LootDumpProcessor/Model/Config/DataStorageConfig.cs
+++ b/source/LootDumpProcessor/Model/Config/DataStorageConfig.cs
@@ -1,10 +1,12 @@
+using System.ComponentModel.DataAnnotations;
+using JetBrains.Annotations;
using LootDumpProcessor.Storage;
namespace LootDumpProcessor.Model.Config;
-public class DataStorageConfig
-{
- public DataStorageTypes DataStorageType { get; set; } = DataStorageTypes.File;
- public string? FileDataStorageTempLocation { get; set; }
-}
\ No newline at end of file
+[UsedImplicitly]
+public record DataStorageConfig(
+ [Required] string FileDataStorageTempLocation,
+ [Required] DataStorageTypes DataStorageType = DataStorageTypes.File
+);
\ No newline at end of file
diff --git a/source/LootDumpProcessor/Model/Config/DumpProcessorConfig.cs b/source/LootDumpProcessor/Model/Config/DumpProcessorConfig.cs
index 6dfe09a..dc10f48 100644
--- a/source/LootDumpProcessor/Model/Config/DumpProcessorConfig.cs
+++ b/source/LootDumpProcessor/Model/Config/DumpProcessorConfig.cs
@@ -1,10 +1,12 @@
-using System.Text.Json.Serialization;
+using System.ComponentModel.DataAnnotations;
+using System.Text.Json.Serialization;
+using JetBrains.Annotations;
using LootDumpProcessor.Serializers.Json.Converters;
namespace LootDumpProcessor.Model.Config;
-public class DumpProcessorConfig
-{
- [JsonConverter(typeof(NetDateTimeConverter))] public DateTime SpawnContainerChanceIncludeAfterDate { get; set; }
-}
\ No newline at end of file
+[UsedImplicitly]
+public record DumpProcessorConfig(
+ [Required] [property: JsonConverter(typeof(NetDateTimeConverter))] DateTime SpawnContainerChanceIncludeAfterDate
+);
\ No newline at end of file
diff --git a/source/LootDumpProcessor/Model/Config/ForcedStatic.cs b/source/LootDumpProcessor/Model/Config/ForcedStatic.cs
index ef10aff..da32f67 100644
--- a/source/LootDumpProcessor/Model/Config/ForcedStatic.cs
+++ b/source/LootDumpProcessor/Model/Config/ForcedStatic.cs
@@ -1,11 +1,14 @@
+using System.Collections.Frozen;
+using JetBrains.Annotations;
using LootDumpProcessor.Model.Output.StaticContainer;
-using YamlDotNet.Serialization;
namespace LootDumpProcessor.Model.Config;
-public class ForcedStatic
+[UsedImplicitly]
+public class ForcedStatic(
+ IReadOnlyList staticWeaponIds, FrozenDictionary> forcedItems
+)
{
- [YamlMember(Alias = "static_weapon_ids")] public List StaticWeaponIds { get; set; }
-
- [YamlMember(Alias = "forced_items")] public Dictionary> ForcedItems { get; set; }
+ public readonly IReadOnlyList StaticWeaponIds = staticWeaponIds;
+ public readonly FrozenDictionary> ForcedItems = forcedItems;
}
\ No newline at end of file
diff --git a/source/LootDumpProcessor/Model/Config/ForcedStaticDto.cs b/source/LootDumpProcessor/Model/Config/ForcedStaticDto.cs
new file mode 100644
index 0000000..e5aa6b2
--- /dev/null
+++ b/source/LootDumpProcessor/Model/Config/ForcedStaticDto.cs
@@ -0,0 +1,13 @@
+using JetBrains.Annotations;
+using LootDumpProcessor.Model.Output.StaticContainer;
+using YamlDotNet.Serialization;
+
+namespace LootDumpProcessor.Model.Config;
+
+[UsedImplicitly]
+public class ForcedStaticDto
+{
+ [YamlMember(Alias = "static_weapon_ids")] public List StaticWeaponIds { get; set; }
+
+ [YamlMember(Alias = "forced_items")] public Dictionary> ForcedItems { get; set; }
+}
\ No newline at end of file
diff --git a/source/LootDumpProcessor/Model/Config/IntakeReaderConfig.cs b/source/LootDumpProcessor/Model/Config/IntakeReaderConfig.cs
index 961f6e5..1db5aec 100644
--- a/source/LootDumpProcessor/Model/Config/IntakeReaderConfig.cs
+++ b/source/LootDumpProcessor/Model/Config/IntakeReaderConfig.cs
@@ -1,7 +1,6 @@
+using JetBrains.Annotations;
+
namespace LootDumpProcessor.Model.Config;
-public class IntakeReaderConfig
-{
- public int MaxDumpsPerMap { get; set; } = 1500;
- public List IgnoredDumpLocations { get; set; } = new();
-}
\ No newline at end of file
+[UsedImplicitly]
+public record IntakeReaderConfig(IReadOnlyList IgnoredDumpLocations, int MaxDumpsPerMap = 1500);
\ No newline at end of file
diff --git a/source/LootDumpProcessor/Model/Config/ProcessorConfig.cs b/source/LootDumpProcessor/Model/Config/ProcessorConfig.cs
index 0e89f98..ad1a018 100644
--- a/source/LootDumpProcessor/Model/Config/ProcessorConfig.cs
+++ b/source/LootDumpProcessor/Model/Config/ProcessorConfig.cs
@@ -1,13 +1,6 @@
-using System.Text.Json.Serialization;
-
+using JetBrains.Annotations;
namespace LootDumpProcessor.Model.Config;
-public class ProcessorConfig
-{
- [JsonPropertyName("spawnPointToleranceForForced")] public double SpawnPointToleranceForForced { get; set; } = 99D;
-
-
- [JsonPropertyName("looseLootCountTolerancePercentage")]
- public double LooseLootCountTolerancePercentage { get; set; } = 75D;
-}
\ No newline at end of file
+[UsedImplicitly]
+public record ProcessorConfig(double SpawnPointToleranceForForced = 99, double LooseLootCountTolerancePercentage = 75);
\ No newline at end of file
diff --git a/source/LootDumpProcessor/Model/Config/ReaderConfig.cs b/source/LootDumpProcessor/Model/Config/ReaderConfig.cs
index 0627b2e..ba4f7db 100644
--- a/source/LootDumpProcessor/Model/Config/ReaderConfig.cs
+++ b/source/LootDumpProcessor/Model/Config/ReaderConfig.cs
@@ -1,9 +1,12 @@
+using System.ComponentModel.DataAnnotations;
+using JetBrains.Annotations;
+
namespace LootDumpProcessor.Model.Config;
-public class ReaderConfig
-{
- public IntakeReaderConfig? IntakeReaderConfig { get; set; }
- public List? DumpFilesLocation { get; set; }
- public string? ThresholdDate { get; set; }
- public bool ProcessSubFolders { get; set; }
-}
\ No newline at end of file
+[UsedImplicitly]
+public record ReaderConfig(
+ [Required] IntakeReaderConfig IntakeReaderConfig,
+ [Required] IReadOnlyList DumpFilesLocation,
+ string? ThresholdDate,
+ bool ProcessSubFolders = true
+);
\ No newline at end of file
diff --git a/source/LootDumpProcessor/Model/Config/WriterConfig.cs b/source/LootDumpProcessor/Model/Config/WriterConfig.cs
index bb19e8f..b12f69f 100644
--- a/source/LootDumpProcessor/Model/Config/WriterConfig.cs
+++ b/source/LootDumpProcessor/Model/Config/WriterConfig.cs
@@ -1,9 +1,8 @@
-using System.Text.Json.Serialization;
+using System.ComponentModel.DataAnnotations;
+using JetBrains.Annotations;
namespace LootDumpProcessor.Model.Config;
-public class WriterConfig
-{
- [JsonPropertyName("outputLocation")] public string? OutputLocation { get; set; }
-}
\ No newline at end of file
+[UsedImplicitly]
+public record WriterConfig([Required] string OutputLocation);
\ No newline at end of file
diff --git a/source/LootDumpProcessor/Process/Collector/CollectorFactory.cs b/source/LootDumpProcessor/Process/Collector/CollectorFactory.cs
deleted file mode 100644
index e5bdd09..0000000
--- a/source/LootDumpProcessor/Process/Collector/CollectorFactory.cs
+++ /dev/null
@@ -1,18 +0,0 @@
-namespace LootDumpProcessor.Process.Collector;
-
-public static class CollectorFactory
-{
- private static ICollector? _collector;
-
- public static ICollector GetInstance()
- {
- if (_collector == null)
- _collector = LootDumpProcessorContext.GetConfig().CollectorConfig.CollectorType switch
- {
- CollectorType.Memory => new HashSetCollector(),
- CollectorType.Dump => new DumpCollector(),
- _ => throw new ArgumentOutOfRangeException()
- };
- return _collector;
- }
-}
\ No newline at end of file
diff --git a/source/LootDumpProcessor/Process/Collector/DumpCollector.cs b/source/LootDumpProcessor/Process/Collector/DumpCollector.cs
index d0a19f4..2895a76 100644
--- a/source/LootDumpProcessor/Process/Collector/DumpCollector.cs
+++ b/source/LootDumpProcessor/Process/Collector/DumpCollector.cs
@@ -1,16 +1,18 @@
using System.Text.Json;
+using LootDumpProcessor.Model.Config;
using LootDumpProcessor.Model.Processing;
using LootDumpProcessor.Serializers.Json;
+using Microsoft.Extensions.Options;
namespace LootDumpProcessor.Process.Collector;
-public class DumpCollector : ICollector
+public class DumpCollector(IOptions config) : ICollector
{
- private static readonly string DumpLocation =
- $"{LootDumpProcessorContext.GetConfig().CollectorConfig.DumpLocation}/collector/";
+ private readonly Config _config = (config ?? throw new ArgumentNullException(nameof(config))).Value;
- private readonly List processedDumps =
- new(LootDumpProcessorContext.GetConfig().CollectorConfig.MaxEntitiesBeforeDumping + 50);
+ private string DumpLocation => Path.Combine(_config.CollectorConfig.DumpLocation, "collector");
+
+ private List ProcessedDumps => new(_config.CollectorConfig.MaxEntitiesBeforeDumping + 50);
private readonly object lockObject = new();
@@ -25,13 +27,13 @@ public class DumpCollector : ICollector
{
lock (lockObject)
{
- processedDumps.Add(parsedDump);
- if (processedDumps.Count > LootDumpProcessorContext.GetConfig().CollectorConfig.MaxEntitiesBeforeDumping)
+ ProcessedDumps.Add(parsedDump);
+ if (ProcessedDumps.Count > _config.CollectorConfig.MaxEntitiesBeforeDumping)
{
var fileName = $"collector-{DateTime.Now.ToString("yyyyMMddHHmmssfffff")}.json";
File.WriteAllText($"{DumpLocation}{fileName}",
- JsonSerializer.Serialize(processedDumps, JsonSerializerSettings.Default));
- processedDumps.Clear();
+ JsonSerializer.Serialize(ProcessedDumps, JsonSerializerSettings.Default));
+ ProcessedDumps.Clear();
}
}
}
@@ -39,10 +41,10 @@ public class DumpCollector : ICollector
public List Retrieve()
{
foreach (var file in Directory.GetFiles(DumpLocation))
- processedDumps.AddRange(JsonSerializer
+ ProcessedDumps.AddRange(JsonSerializer
.Deserialize>(File.ReadAllText(file), JsonSerializerSettings.Default));
- return processedDumps;
+ return ProcessedDumps;
}
public void Clear()
@@ -51,7 +53,7 @@ public class DumpCollector : ICollector
{
foreach (var file in Directory.GetFiles(DumpLocation)) File.Delete(file);
- processedDumps.Clear();
+ ProcessedDumps.Clear();
}
}
}
\ No newline at end of file
diff --git a/source/LootDumpProcessor/Process/IKeyGenerator.cs b/source/LootDumpProcessor/Process/IKeyGenerator.cs
deleted file mode 100644
index 5bf967e..0000000
--- a/source/LootDumpProcessor/Process/IKeyGenerator.cs
+++ /dev/null
@@ -1,6 +0,0 @@
-namespace LootDumpProcessor.Process;
-
-public interface IKeyGenerator
-{
- string Generate();
-}
\ No newline at end of file
diff --git a/source/LootDumpProcessor/Process/Processor/v2/AmmoProcessor/AmmoProcessor.cs b/source/LootDumpProcessor/Process/Processor/AmmoProcessor/AmmoProcessor.cs
similarity index 93%
rename from source/LootDumpProcessor/Process/Processor/v2/AmmoProcessor/AmmoProcessor.cs
rename to source/LootDumpProcessor/Process/Processor/AmmoProcessor/AmmoProcessor.cs
index fe65694..5994eb5 100644
--- a/source/LootDumpProcessor/Process/Processor/v2/AmmoProcessor/AmmoProcessor.cs
+++ b/source/LootDumpProcessor/Process/Processor/AmmoProcessor/AmmoProcessor.cs
@@ -1,8 +1,9 @@
using LootDumpProcessor.Model.Output;
using LootDumpProcessor.Model.Processing;
+using LootDumpProcessor.Process.Services.TarkovItemsProvider;
using Microsoft.Extensions.Logging;
-namespace LootDumpProcessor.Process.Processor.v2.AmmoProcessor;
+namespace LootDumpProcessor.Process.Processor.AmmoProcessor;
public class AmmoProcessor(ILogger logger, ITarkovItemsProvider tarkovItemsProvider) : IAmmoProcessor
{
diff --git a/source/LootDumpProcessor/Process/Processor/v2/AmmoProcessor/IAmmoProcessor.cs b/source/LootDumpProcessor/Process/Processor/AmmoProcessor/IAmmoProcessor.cs
similarity index 80%
rename from source/LootDumpProcessor/Process/Processor/v2/AmmoProcessor/IAmmoProcessor.cs
rename to source/LootDumpProcessor/Process/Processor/AmmoProcessor/IAmmoProcessor.cs
index d7e703d..9547ace 100644
--- a/source/LootDumpProcessor/Process/Processor/v2/AmmoProcessor/IAmmoProcessor.cs
+++ b/source/LootDumpProcessor/Process/Processor/AmmoProcessor/IAmmoProcessor.cs
@@ -1,7 +1,7 @@
using LootDumpProcessor.Model.Output;
using LootDumpProcessor.Model.Processing;
-namespace LootDumpProcessor.Process.Processor.v2.AmmoProcessor;
+namespace LootDumpProcessor.Process.Processor.AmmoProcessor;
public interface IAmmoProcessor
{
diff --git a/source/LootDumpProcessor/Process/Processor/DumpProcessor/MultithreadSteppedDumpProcessor.cs b/source/LootDumpProcessor/Process/Processor/DumpProcessor/MultithreadSteppedDumpProcessor.cs
index 1e5aaea..0fea688 100644
--- a/source/LootDumpProcessor/Process/Processor/DumpProcessor/MultithreadSteppedDumpProcessor.cs
+++ b/source/LootDumpProcessor/Process/Processor/DumpProcessor/MultithreadSteppedDumpProcessor.cs
@@ -1,20 +1,23 @@
using System.Collections.Concurrent;
using System.Text.Json;
using LootDumpProcessor.Model;
+using LootDumpProcessor.Model.Config;
using LootDumpProcessor.Model.Input;
using LootDumpProcessor.Model.Output;
using LootDumpProcessor.Model.Output.LooseLoot;
using LootDumpProcessor.Model.Output.StaticContainer;
using LootDumpProcessor.Model.Processing;
-using LootDumpProcessor.Process.Processor.v2.AmmoProcessor;
-using LootDumpProcessor.Process.Processor.v2.LooseLootProcessor;
-using LootDumpProcessor.Process.Processor.v2.StaticContainersProcessor;
-using LootDumpProcessor.Process.Processor.v2.StaticLootProcessor;
+using LootDumpProcessor.Process.Processor.AmmoProcessor;
+using LootDumpProcessor.Process.Processor.LooseLootProcessor;
+using LootDumpProcessor.Process.Processor.StaticContainersProcessor;
+using LootDumpProcessor.Process.Processor.StaticLootProcessor;
+using LootDumpProcessor.Process.Services.KeyGenerator;
using LootDumpProcessor.Serializers.Json;
using LootDumpProcessor.Storage;
using LootDumpProcessor.Storage.Collections;
using LootDumpProcessor.Utils;
using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
namespace LootDumpProcessor.Process.Processor.DumpProcessor;
@@ -23,7 +26,8 @@ public class MultithreadSteppedDumpProcessor(
IStaticContainersProcessor staticContainersProcessor,
IAmmoProcessor ammoProcessor,
ILooseLootProcessor looseLootProcessor,
- ILogger logger, IKeyGenerator keyGenerator
+ ILogger logger, IKeyGenerator keyGenerator, IDataStorage dataStorage,
+ IOptions config
)
: IDumpProcessor
{
@@ -45,8 +49,10 @@ public class MultithreadSteppedDumpProcessor(
private readonly IKeyGenerator
_keyGenerator = keyGenerator ?? throw new ArgumentNullException(nameof(keyGenerator));
- private static readonly IDataStorage _dataStorage = DataStorageFactory.GetInstance();
+ private readonly IDataStorage _dataStorage = dataStorage ?? throw new ArgumentNullException(nameof(dataStorage));
+ private readonly Config _config = (config ?? throw new ArgumentNullException(nameof(config))).Value;
+
public async Task> ProcessDumps(List dumps)
{
_logger.LogInformation("Starting final dump processing");
@@ -111,12 +117,12 @@ public class MultithreadSteppedDumpProcessor(
_logger.LogInformation("Processing loose loot distribution");
var looseLoot = new ConcurrentDictionary();
- Parallel.ForEach(dumpProcessData.MapCounts.Keys, parallelOptions, mapId =>
+ await Parallel.ForEachAsync(dumpProcessData.MapCounts.Keys, parallelOptions, async (mapId, _) =>
{
var mapCount = dumpProcessData.MapCounts[mapId];
var looseLootCount = dumpProcessData.LooseLootCounts[mapId];
var looseLootDistribution =
- _looseLootProcessor.CreateLooseLootDistribution(mapId, mapCount, looseLootCount);
+ await _looseLootProcessor.CreateLooseLootDistribution(mapId, mapCount, looseLootCount);
looseLoot[mapId] = looseLootDistribution;
});
_logger.LogInformation("Collecting loose loot distribution information");
@@ -159,8 +165,7 @@ public class MultithreadSteppedDumpProcessor(
}
else
{
- var mapStaticContainers =
- _staticContainersProcessor.CreateStaticWeaponsAndForcedContainers(dataDump);
+ var mapStaticContainers = await _staticContainersProcessor.CreateStaticWeaponsAndForcedContainers(dataDump);
var newStaticWeapons = mapStaticContainers.StaticWeapons.Where(x =>
!mapStaticLoot.StaticWeapons.Exists(y => y.Id == x.Id));
@@ -172,7 +177,7 @@ public class MultithreadSteppedDumpProcessor(
}
if (!mapStaticContainersAggregated.TryGetValue(mapId,
- out ConcurrentDictionary mapAggregatedDataDict))
+ out var mapAggregatedDataDict))
{
mapAggregatedDataDict = new ConcurrentDictionary();
mapStaticContainersAggregated.TryAdd(mapId, mapAggregatedDataDict);
@@ -182,9 +187,9 @@ public class MultithreadSteppedDumpProcessor(
IncrementMapCounterDictionaryValue(mapDumpCounter, mapId);
- var containerIgnoreListExists = LootDumpProcessorContext.GetConfig().ContainerIgnoreList
+ var containerIgnoreListExists = _config.ContainerIgnoreList
.TryGetValue(mapId, out var ignoreListForMap);
- foreach (var dynamicStaticContainer in _staticContainersProcessor.CreateDynamicStaticContainers(
+ foreach (var dynamicStaticContainer in await _staticContainersProcessor.CreateDynamicStaticContainers(
dataDump))
{
if (containerIgnoreListExists && ignoreListForMap.Contains(dynamicStaticContainer.Id)) continue;
@@ -193,16 +198,17 @@ public class MultithreadSteppedDumpProcessor(
mapAggregatedDataDict[dynamicStaticContainer] += 1;
}
- GCHandler.Collect();
+ if (_config.ManualGarbageCollectionCalls) GC.Collect();
}
- private static bool DumpWasMadeAfterConfigThresholdDate(PartialData dataDump) =>
+ private bool DumpWasMadeAfterConfigThresholdDate(PartialData dataDump) =>
FileDateParser.TryParseFileDate(dataDump.BasicInfo.FileName, out var fileDate) &&
fileDate.HasValue &&
- fileDate.Value > LootDumpProcessorContext.GetConfig().DumpProcessorConfig
+ fileDate.Value > _config.DumpProcessorConfig
.SpawnContainerChanceIncludeAfterDate;
- private static void IncrementMapCounterDictionaryValue(ConcurrentDictionary mapDumpCounter, string mapName)
+ private static void IncrementMapCounterDictionaryValue(ConcurrentDictionary mapDumpCounter,
+ string mapName)
{
if (!mapDumpCounter.TryAdd(mapName, 1)) mapDumpCounter[mapName] += 1;
}
@@ -247,7 +253,7 @@ public class MultithreadSteppedDumpProcessor(
partialFileMetaData = null;
tuple = null;
- GCHandler.Collect();
+ if (_config.ManualGarbageCollectionCalls) GC.Collect();
var parallelOptions = new ParallelOptions
{
@@ -266,14 +272,14 @@ public class MultithreadSteppedDumpProcessor(
_dataStorage.Store(dictionaryCounts);
dictionaryCounts = null;
- GCHandler.Collect();
+ if (_config.ManualGarbageCollectionCalls) GC.Collect();
_dataStorage.Store(actualDictionaryItemProperties);
actualDictionaryItemProperties = null;
- GCHandler.Collect();
+ if (_config.ManualGarbageCollectionCalls) GC.Collect();
_dataStorage.Store(looseLootCounts);
looseLootCounts = null;
- GCHandler.Collect();
+ if (_config.ManualGarbageCollectionCalls) GC.Collect();
});
return dumpProcessData;
}
diff --git a/source/LootDumpProcessor/Process/Processor/FileProcessor/FileProcessor.cs b/source/LootDumpProcessor/Process/Processor/FileProcessor/FileProcessor.cs
index 428c01b..522d27e 100644
--- a/source/LootDumpProcessor/Process/Processor/FileProcessor/FileProcessor.cs
+++ b/source/LootDumpProcessor/Process/Processor/FileProcessor/FileProcessor.cs
@@ -1,32 +1,33 @@
using LootDumpProcessor.Model;
using LootDumpProcessor.Model.Processing;
-using LootDumpProcessor.Process.Processor.v2.LooseLootProcessor;
-using LootDumpProcessor.Process.Processor.v2.StaticLootProcessor;
+using LootDumpProcessor.Process.Processor.LooseLootProcessor;
+using LootDumpProcessor.Process.Processor.StaticLootProcessor;
using LootDumpProcessor.Storage;
using Microsoft.Extensions.Logging;
namespace LootDumpProcessor.Process.Processor.FileProcessor;
-public class FileProcessor : IFileProcessor
+public class FileProcessor(
+ IStaticLootProcessor staticLootProcessor,
+ ILooseLootProcessor looseLootProcessor,
+ ILogger logger, IDataStorage dataStorage
+)
+ : IFileProcessor
{
- private readonly IStaticLootProcessor _staticLootProcessor;
- private readonly ILooseLootProcessor _looseLootProcessor;
- private readonly ILogger _logger;
+ private readonly IStaticLootProcessor _staticLootProcessor = staticLootProcessor
+ ?? throw new ArgumentNullException(
+ nameof(staticLootProcessor));
- public FileProcessor(
- IStaticLootProcessor staticLootProcessor,
- ILooseLootProcessor looseLootProcessor,
- ILogger logger)
- {
- _staticLootProcessor = staticLootProcessor
- ?? throw new ArgumentNullException(nameof(staticLootProcessor));
- _looseLootProcessor = looseLootProcessor
- ?? throw new ArgumentNullException(nameof(looseLootProcessor));
- _logger = logger
- ?? throw new ArgumentNullException(nameof(logger));
- }
+ private readonly ILooseLootProcessor _looseLootProcessor = looseLootProcessor
+ ?? throw new ArgumentNullException(
+ nameof(looseLootProcessor));
- public PartialData Process(BasicInfo parsedData)
+ private readonly ILogger _logger = logger
+ ?? throw new ArgumentNullException(nameof(logger));
+
+ private readonly IDataStorage _dataStorage = dataStorage ?? throw new ArgumentNullException(nameof(dataStorage));
+
+ public async Task Process(BasicInfo parsedData)
{
_logger.LogDebug("Processing file {FileName}...", parsedData.FileName);
@@ -50,16 +51,16 @@ public class FileProcessor : IFileProcessor
ParsedDumpKey = (AbstractKey)dumpData.GetKey()
};
- if (!DataStorageFactory.GetInstance().Exists(dumpData.GetKey()))
+ if (!_dataStorage.Exists(dumpData.GetKey()))
{
_logger.LogDebug(
"Cache not found for {LookupIndex} processing.",
string.Join("/", dumpData.GetKey().GetLookupIndex())
);
- dumpData.Containers = _staticLootProcessor.PreProcessStaticLoot(staticLoot);
+ dumpData.Containers = await _staticLootProcessor.PreProcessStaticLoot(staticLoot);
dumpData.LooseLoot = _looseLootProcessor.PreProcessLooseLoot(looseLoot);
- DataStorageFactory.GetInstance().Store(dumpData);
+ dataStorage.Store(dumpData);
}
_logger.LogDebug("File {FileName} finished processing!", parsedData.FileName);
diff --git a/source/LootDumpProcessor/Process/Processor/FileProcessor/IFileProcessor.cs b/source/LootDumpProcessor/Process/Processor/FileProcessor/IFileProcessor.cs
index 04e66d6..d096a90 100644
--- a/source/LootDumpProcessor/Process/Processor/FileProcessor/IFileProcessor.cs
+++ b/source/LootDumpProcessor/Process/Processor/FileProcessor/IFileProcessor.cs
@@ -4,5 +4,5 @@ namespace LootDumpProcessor.Process.Processor.FileProcessor;
public interface IFileProcessor
{
- PartialData Process(BasicInfo parsedData);
+ Task Process(BasicInfo parsedData);
}
\ No newline at end of file
diff --git a/source/LootDumpProcessor/Process/Processor/v2/LooseLootProcessor/ILooseLootProcessor.cs b/source/LootDumpProcessor/Process/Processor/LooseLootProcessor/ILooseLootProcessor.cs
similarity index 62%
rename from source/LootDumpProcessor/Process/Processor/v2/LooseLootProcessor/ILooseLootProcessor.cs
rename to source/LootDumpProcessor/Process/Processor/LooseLootProcessor/ILooseLootProcessor.cs
index 94bf430..dc9bb65 100644
--- a/source/LootDumpProcessor/Process/Processor/v2/LooseLootProcessor/ILooseLootProcessor.cs
+++ b/source/LootDumpProcessor/Process/Processor/LooseLootProcessor/ILooseLootProcessor.cs
@@ -3,15 +3,13 @@ using LootDumpProcessor.Model.Output.LooseLoot;
using LootDumpProcessor.Model.Processing;
using LootDumpProcessor.Storage;
-namespace LootDumpProcessor.Process.Processor.v2.LooseLootProcessor;
+namespace LootDumpProcessor.Process.Processor.LooseLootProcessor;
public interface ILooseLootProcessor
{
PreProcessedLooseLoot PreProcessLooseLoot(List looseLoot);
- LooseLootRoot CreateLooseLootDistribution(
- string mapId,
+ Task CreateLooseLootDistribution(string mapId,
int mapCount,
- IKey looseLootCountKey
- );
+ IKey looseLootCountKey);
}
\ No newline at end of file
diff --git a/source/LootDumpProcessor/Process/Processor/v2/LooseLootProcessor/LooseLootProcessor.cs b/source/LootDumpProcessor/Process/Processor/LooseLootProcessor/LooseLootProcessor.cs
similarity index 88%
rename from source/LootDumpProcessor/Process/Processor/v2/LooseLootProcessor/LooseLootProcessor.cs
rename to source/LootDumpProcessor/Process/Processor/LooseLootProcessor/LooseLootProcessor.cs
index 123bddf..6174fb6 100644
--- a/source/LootDumpProcessor/Process/Processor/v2/LooseLootProcessor/LooseLootProcessor.cs
+++ b/source/LootDumpProcessor/Process/Processor/LooseLootProcessor/LooseLootProcessor.cs
@@ -1,17 +1,24 @@
using LootDumpProcessor.Model;
+using LootDumpProcessor.Model.Config;
using LootDumpProcessor.Model.Output;
using LootDumpProcessor.Model.Output.LooseLoot;
using LootDumpProcessor.Model.Processing;
+using LootDumpProcessor.Process.Services.ComposedKeyGenerator;
+using LootDumpProcessor.Process.Services.ForcedItemsProvider;
+using LootDumpProcessor.Process.Services.KeyGenerator;
+using LootDumpProcessor.Process.Services.TarkovItemsProvider;
using LootDumpProcessor.Storage;
using LootDumpProcessor.Storage.Collections;
using LootDumpProcessor.Utils;
using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
-namespace LootDumpProcessor.Process.Processor.v2.LooseLootProcessor;
+namespace LootDumpProcessor.Process.Processor.LooseLootProcessor;
public class LooseLootProcessor(
ILogger logger, IDataStorage dataStorage, ITarkovItemsProvider tarkovItemsProvider,
- IComposedKeyGenerator composedKeyGenerator, IKeyGenerator keyGenerator
+ IComposedKeyGenerator composedKeyGenerator, IKeyGenerator keyGenerator, IOptions config,
+ IForcedItemsProvider forcedItemsProvider
)
: ILooseLootProcessor
{
@@ -30,6 +37,11 @@ public class LooseLootProcessor(
private readonly IKeyGenerator
_keyGenerator = keyGenerator ?? throw new ArgumentNullException(nameof(keyGenerator));
+ private readonly Config _config = (config ?? throw new ArgumentNullException(nameof(config))).Value;
+
+ private readonly IForcedItemsProvider _forcedItemsProvider =
+ forcedItemsProvider ?? throw new ArgumentNullException(nameof(forcedItemsProvider));
+
public PreProcessedLooseLoot PreProcessLooseLoot(List looseLoot)
{
var preProcessedLoot = new PreProcessedLooseLoot
@@ -70,25 +82,23 @@ public class LooseLootProcessor(
return preProcessedLoot;
}
- public LooseLootRoot CreateLooseLootDistribution(
- string mapId,
+ public async Task CreateLooseLootDistribution(string mapId,
int mapCount,
- IKey looseLootCountKey
- )
+ IKey looseLootCountKey)
{
- var config = LootDumpProcessorContext.GetConfig();
- var spawnPointTolerance = config.ProcessorConfig.SpawnPointToleranceForForced / 100.0;
+ var spawnPointTolerance = _config.ProcessorConfig.SpawnPointToleranceForForced / 100.0;
var looseLootDistribution = new LooseLootRoot();
var probabilities = new Dictionary();
var looseLootCountsItem = _dataStorage.GetItem(looseLootCountKey);
+ var forcedLooseItems = await _forcedItemsProvider.GetForcedLooseItems();
var counts = _dataStorage.GetItem>(looseLootCountsItem.Counts);
foreach (var (itemId, count) in counts) probabilities[itemId] = (double)count / mapCount;
var spawnPointCount = looseLootCountsItem.MapSpawnpointCount.Select(Convert.ToDouble).ToList();
var initialMean = CalculateMean(spawnPointCount);
- var tolerancePercentage = config.ProcessorConfig.LooseLootCountTolerancePercentage / 100.0;
+ var tolerancePercentage = _config.ProcessorConfig.LooseLootCountTolerancePercentage / 100.0;
var highThreshold = initialMean * (1 + tolerancePercentage);
looseLootCountsItem.MapSpawnpointCount = looseLootCountsItem.MapSpawnpointCount
@@ -137,7 +147,7 @@ public class LooseLootProcessor(
if (itemDistributions.Count == 1 &&
(_tarkovItemsProvider.IsQuestItem(itemDistributions[0].ComposedKey?.FirstItem?.Tpl) ||
- LootDumpProcessorContext.GetForcedLooseItems()[mapId]
+ forcedLooseItems[mapId]
.Contains(itemDistributions[0].ComposedKey?.FirstItem?.Tpl)))
{
var forcedSpawnPoint = new SpawnPointsForced
@@ -160,7 +170,7 @@ public class LooseLootProcessor(
_logger.LogWarning(
"Item: {ItemId} has > {Tolerance}% spawn chance in spawn point: {LocationId} but isn't in forced loot, adding to forced",
templateCopy.Id,
- config.ProcessorConfig.SpawnPointToleranceForForced,
+ _config.ProcessorConfig.SpawnPointToleranceForForced,
forcedSpawnPoint.LocationId
);
}
@@ -210,7 +220,7 @@ public class LooseLootProcessor(
.ToList();
var configuredForcedTemplates =
- new HashSet(LootDumpProcessorContext.GetForcedLooseItems()[mapId].Select(item => item));
+ new HashSet(forcedLooseItems[mapId].Select(item => item));
var foundForcedTemplates =
new HashSet(looseLootDistribution.SpawnPointsForced.Select(fp => fp.Template.Items[0].Tpl));
diff --git a/source/LootDumpProcessor/Process/Processor/StaticContainersProcessor/IStaticContainersProcessor.cs b/source/LootDumpProcessor/Process/Processor/StaticContainersProcessor/IStaticContainersProcessor.cs
new file mode 100644
index 0000000..a0a82ef
--- /dev/null
+++ b/source/LootDumpProcessor/Process/Processor/StaticContainersProcessor/IStaticContainersProcessor.cs
@@ -0,0 +1,11 @@
+using LootDumpProcessor.Model;
+using LootDumpProcessor.Model.Input;
+using LootDumpProcessor.Model.Output.StaticContainer;
+
+namespace LootDumpProcessor.Process.Processor.StaticContainersProcessor;
+
+public interface IStaticContainersProcessor
+{
+ Task CreateStaticWeaponsAndForcedContainers(RootData rawMapDump);
+ Task> CreateDynamicStaticContainers(RootData rawMapDump);
+}
\ No newline at end of file
diff --git a/source/LootDumpProcessor/Process/Processor/v2/StaticContainersProcessor/StaticContainerProcessor.cs b/source/LootDumpProcessor/Process/Processor/StaticContainersProcessor/StaticContainerProcessor.cs
similarity index 64%
rename from source/LootDumpProcessor/Process/Processor/v2/StaticContainersProcessor/StaticContainerProcessor.cs
rename to source/LootDumpProcessor/Process/Processor/StaticContainersProcessor/StaticContainerProcessor.cs
index afad205..72aa1f4 100644
--- a/source/LootDumpProcessor/Process/Processor/v2/StaticContainersProcessor/StaticContainerProcessor.cs
+++ b/source/LootDumpProcessor/Process/Processor/StaticContainersProcessor/StaticContainerProcessor.cs
@@ -1,21 +1,24 @@
using LootDumpProcessor.Model;
using LootDumpProcessor.Model.Input;
using LootDumpProcessor.Model.Output.StaticContainer;
+using LootDumpProcessor.Process.Services.ForcedItemsProvider;
using LootDumpProcessor.Utils;
using Microsoft.Extensions.Logging;
-namespace LootDumpProcessor.Process.Processor.v2.StaticContainersProcessor;
+namespace LootDumpProcessor.Process.Processor.StaticContainersProcessor;
-public class StaticContainersProcessor : IStaticContainersProcessor
+public class StaticContainersProcessor(
+ ILogger logger, IForcedItemsProvider forcedItemsProvider
+)
+ : IStaticContainersProcessor
{
- private readonly ILogger _logger;
+ private readonly ILogger _logger =
+ logger ?? throw new ArgumentNullException(nameof(logger));
- public StaticContainersProcessor(ILogger logger)
- {
- _logger = logger ?? throw new ArgumentNullException(nameof(logger));
- }
+ private readonly IForcedItemsProvider _forcedItemsProvider =
+ forcedItemsProvider ?? throw new ArgumentNullException(nameof(forcedItemsProvider));
- public MapStaticLoot CreateStaticWeaponsAndForcedContainers(RootData rawMapDump)
+ public async Task CreateStaticWeaponsAndForcedContainers(RootData rawMapDump)
{
var locationLoot = rawMapDump.Data.LocationLoot;
var mapId = locationLoot.Id.ToLower();
@@ -25,6 +28,7 @@ public class StaticContainersProcessor : IStaticContainersProcessor
.ToList();
var staticWeapons = new List();
+ var forcedStatic = await _forcedItemsProvider.GetForcedStatic();
foreach (var lootPosition in staticLootPositions)
{
@@ -36,7 +40,7 @@ public class StaticContainersProcessor : IStaticContainersProcessor
var firstItemTpl = lootPosition.Items.First().Tpl;
- if (!LootDumpProcessorContext.GetStaticWeaponIds().Contains(firstItemTpl))
+ if (!forcedStatic.StaticWeaponIds.Contains(firstItemTpl))
continue;
var copiedLoot = ProcessorUtil.Copy(lootPosition);
@@ -44,26 +48,28 @@ 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 List? forcedItems)
+ var forcedStaticItems = forcedStatic.ForcedItems
+ .TryGetValue(mapId, out var forcedItems)
? forcedItems
: new List();
var mapStaticLoot = new MapStaticLoot
{
StaticWeapons = staticWeapons,
- StaticForced = forcedStaticItems
+ StaticForced = forcedStaticItems.ToList()
};
_logger.LogInformation("Created static weapons and forced containers for Map {MapId}.", mapId);
return mapStaticLoot;
}
- public IReadOnlyList CreateDynamicStaticContainers(RootData rawMapDump)
+ public async Task> CreateDynamicStaticContainers(RootData rawMapDump)
{
+ var forcedStatic = await _forcedItemsProvider.GetForcedStatic();
+
var dynamicContainers = rawMapDump.Data.LocationLoot.Loot
.Where(loot => loot.IsContainer &&
- !LootDumpProcessorContext.GetStaticWeaponIds().Contains(loot.Items.FirstOrDefault()?.Tpl))
+ !forcedStatic.StaticWeaponIds.Contains(loot.Items.FirstOrDefault()?.Tpl))
.ToList();
foreach (var container in dynamicContainers)
diff --git a/source/LootDumpProcessor/Process/Processor/v2/StaticLootProcessor/IStaticLootProcessor.cs b/source/LootDumpProcessor/Process/Processor/StaticLootProcessor/IStaticLootProcessor.cs
similarity index 65%
rename from source/LootDumpProcessor/Process/Processor/v2/StaticLootProcessor/IStaticLootProcessor.cs
rename to source/LootDumpProcessor/Process/Processor/StaticLootProcessor/IStaticLootProcessor.cs
index e58579b..6c8572c 100644
--- a/source/LootDumpProcessor/Process/Processor/v2/StaticLootProcessor/IStaticLootProcessor.cs
+++ b/source/LootDumpProcessor/Process/Processor/StaticLootProcessor/IStaticLootProcessor.cs
@@ -2,11 +2,11 @@
using LootDumpProcessor.Model.Output;
using LootDumpProcessor.Model.Processing;
-namespace LootDumpProcessor.Process.Processor.v2.StaticLootProcessor;
+namespace LootDumpProcessor.Process.Processor.StaticLootProcessor;
public interface IStaticLootProcessor
{
- IReadOnlyList PreProcessStaticLoot(IReadOnlyList staticLoot);
+ Task> PreProcessStaticLoot(IReadOnlyList staticLoot);
IReadOnlyDictionary CreateStaticLootDistribution(
string mapName,
diff --git a/source/LootDumpProcessor/Process/Processor/v2/StaticLootProcessor/StaticLootProcessor.cs b/source/LootDumpProcessor/Process/Processor/StaticLootProcessor/StaticLootProcessor.cs
similarity index 85%
rename from source/LootDumpProcessor/Process/Processor/v2/StaticLootProcessor/StaticLootProcessor.cs
rename to source/LootDumpProcessor/Process/Processor/StaticLootProcessor/StaticLootProcessor.cs
index 72cdb67..0f28281 100644
--- a/source/LootDumpProcessor/Process/Processor/v2/StaticLootProcessor/StaticLootProcessor.cs
+++ b/source/LootDumpProcessor/Process/Processor/StaticLootProcessor/StaticLootProcessor.cs
@@ -1,16 +1,21 @@
using LootDumpProcessor.Model;
using LootDumpProcessor.Model.Output;
using LootDumpProcessor.Model.Processing;
+using LootDumpProcessor.Process.Services.ForcedItemsProvider;
using Microsoft.Extensions.Logging;
-namespace LootDumpProcessor.Process.Processor.v2.StaticLootProcessor;
+namespace LootDumpProcessor.Process.Processor.StaticLootProcessor;
-public class StaticLootProcessor(ILogger logger) : IStaticLootProcessor
+public class StaticLootProcessor(ILogger logger, IForcedItemsProvider forcedItemsProvider)
+ : IStaticLootProcessor
{
private readonly ILogger _logger =
logger ?? throw new ArgumentNullException(nameof(logger));
- public IReadOnlyList PreProcessStaticLoot(IReadOnlyList staticLoot)
+ private readonly IForcedItemsProvider _forcedItemsProvider =
+ forcedItemsProvider ?? throw new ArgumentNullException(nameof(forcedItemsProvider));
+
+ public async Task> PreProcessStaticLoot(IReadOnlyList staticLoot)
{
var nonWeaponContainers = new List();
@@ -24,7 +29,8 @@ public class StaticLootProcessor(ILogger logger) : IStaticL
var firstItemTpl = lootSpawn.Items[0].Tpl;
- if (!LootDumpProcessorContext.GetStaticWeaponIds().Contains(firstItemTpl))
+ var forcedStatic = await _forcedItemsProvider.GetForcedStatic();
+ if (!forcedStatic.StaticWeaponIds.Contains(firstItemTpl))
{
nonWeaponContainers.Add(new PreProcessedStaticLoot
{
diff --git a/source/LootDumpProcessor/Process/Processor/v2/StaticContainersProcessor/IStaticContainersProcessor.cs b/source/LootDumpProcessor/Process/Processor/v2/StaticContainersProcessor/IStaticContainersProcessor.cs
deleted file mode 100644
index ff151f2..0000000
--- a/source/LootDumpProcessor/Process/Processor/v2/StaticContainersProcessor/IStaticContainersProcessor.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-using LootDumpProcessor.Model;
-using LootDumpProcessor.Model.Input;
-using LootDumpProcessor.Model.Output.StaticContainer;
-
-namespace LootDumpProcessor.Process.Processor.v2.StaticContainersProcessor;
-
-public interface IStaticContainersProcessor
-{
- MapStaticLoot CreateStaticWeaponsAndForcedContainers(RootData rawMapDump);
- IReadOnlyList CreateDynamicStaticContainers(RootData rawMapDump);
-}
\ No newline at end of file
diff --git a/source/LootDumpProcessor/Process/QueuePipeline.cs b/source/LootDumpProcessor/Process/QueuePipeline.cs
index db6de8f..2bb90b9 100644
--- a/source/LootDumpProcessor/Process/QueuePipeline.cs
+++ b/source/LootDumpProcessor/Process/QueuePipeline.cs
@@ -1,6 +1,7 @@
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Text.Json;
+using LootDumpProcessor.Model.Config;
using LootDumpProcessor.Model.Input;
using LootDumpProcessor.Process.Collector;
using LootDumpProcessor.Process.Processor.DumpProcessor;
@@ -12,12 +13,13 @@ using LootDumpProcessor.Serializers.Json;
using LootDumpProcessor.Storage;
using LootDumpProcessor.Utils;
using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
namespace LootDumpProcessor.Process;
public class QueuePipeline(
IFileProcessor fileProcessor, IDumpProcessor dumpProcessor, ILogger logger, IFileFilter fileFilter,
- IIntakeReader intakeReader
+ IIntakeReader intakeReader, ICollector collector, IDataStorage dataStorage, IOptions config, IWriter writer
)
: IPipeline
{
@@ -34,25 +36,32 @@ public class QueuePipeline(
private readonly IIntakeReader
_intakeReader = intakeReader ?? throw new ArgumentNullException(nameof(intakeReader));
+ private readonly ICollector _collector = collector ?? throw new ArgumentNullException(nameof(collector));
+
+ private readonly IDataStorage _dataStorage = dataStorage ?? throw new ArgumentNullException(nameof(dataStorage));
+
+ private readonly IWriter _writer = writer ?? throw new ArgumentNullException(nameof(writer));
+
+ private readonly Config _config = (config ?? throw new ArgumentNullException(nameof(config))).Value;
+
private readonly List _filesToRename = new();
private readonly BlockingCollection _filesToProcess = new();
- private readonly List _mapNames = LootDumpProcessorContext.GetConfig().MapsToProcess;
+ private IReadOnlyList MapNames => _config.MapsToProcess;
public async Task Execute()
{
var stopwatch = Stopwatch.StartNew();
// Single collector instance to collect results
- var collector = CollectorFactory.GetInstance();
- collector.Setup();
+ _collector.Setup();
_logger.LogInformation("Gathering files to begin processing");
try
{
await FixFilesFromDumps();
- foreach (var mapName in _mapNames) await ProcessFilesFromDumpsPerMap(collector, mapName);
+ foreach (var mapName in MapNames) await ProcessFilesFromDumpsPerMap(_collector, mapName);
}
finally
{
@@ -64,7 +73,7 @@ public class QueuePipeline(
private List GatherFiles()
{
// Read locations
- var inputPath = LootDumpProcessorContext.GetConfig().ReaderConfig.DumpFilesLocation;
+ var inputPath = _config.ReaderConfig.DumpFilesLocation;
if (inputPath == null || inputPath.Count == 0)
throw new Exception("Reader dumpFilesLocations must be set to a valid value");
@@ -101,12 +110,12 @@ public class QueuePipeline(
return gatheredFiles;
}
- private Queue GetFileQueue(List inputPath)
+ private Queue GetFileQueue(IReadOnlyList inputPath)
{
var queuedPathsToProcess = new Queue();
var queuedFilesToProcess = new Queue();
- inputPath.ForEach(p => queuedPathsToProcess.Enqueue(p));
+ foreach (var path in inputPath) queuedPathsToProcess.Enqueue(path);
while (queuedPathsToProcess.TryDequeue(out var path))
{
@@ -114,7 +123,7 @@ public class QueuePipeline(
if (!Directory.Exists(path)) throw new Exception($"Input directory \"{path}\" could not be found");
// If we should process subfolder, queue them up as well
- if (LootDumpProcessorContext.GetConfig().ReaderConfig.ProcessSubFolders)
+ if (_config.ReaderConfig.ProcessSubFolders)
foreach (var directory in Directory.GetDirectories(path))
queuedPathsToProcess.Enqueue(directory);
@@ -138,13 +147,13 @@ public class QueuePipeline(
_logger.LogInformation("Files sorted and ready to begin pre-processing");
- Parallel.ForEach(_filesToProcess, file =>
+ await Parallel.ForEachAsync(_filesToProcess, async (file, _) =>
{
try
{
if (!_intakeReader.Read(file, out var basicInfo)) return;
- var partialData = _fileProcessor.Process(basicInfo);
+ var partialData = await _fileProcessor.Process(basicInfo);
collector.Hold(partialData);
}
catch (Exception e)
@@ -156,15 +165,14 @@ public class QueuePipeline(
_logger.LogInformation("Pre-processing finished");
// Single writer instance to collect results
- var writer = WriterFactory.GetInstance();
// Single collector instance to collect results
var partialData = collector.Retrieve();
var processedDumps = await _dumpProcessor.ProcessDumps(partialData);
- writer.WriteAll(processedDumps);
+ _writer.WriteAll(processedDumps);
// clear collector and datastorage as we process per map now
collector.Clear();
- DataStorageFactory.GetInstance().Clear();
+ _dataStorage.Clear();
}
///
@@ -172,7 +180,7 @@ public class QueuePipeline(
///
private async Task FixFilesFromDumps()
{
- var inputPath = LootDumpProcessorContext.GetConfig().ReaderConfig.DumpFilesLocation;
+ var inputPath = _config.ReaderConfig.DumpFilesLocation;
if (inputPath == null || inputPath.Count == 0)
throw new Exception("Reader dumpFilesLocations must be set to a valid value");
@@ -181,7 +189,7 @@ public class QueuePipeline(
await Parallel.ForEachAsync(_filesToRename, async (file, cancellationToken) =>
{
- if (_mapNames.Any(file.Contains)) return;
+ if (MapNames.Any(file.Contains)) return;
try
{
diff --git a/source/LootDumpProcessor/Process/Reader/Filters/JsonDumpFileFilter.cs b/source/LootDumpProcessor/Process/Reader/Filters/JsonDumpFileFilter.cs
index d13e43b..ebdc31d 100644
--- a/source/LootDumpProcessor/Process/Reader/Filters/JsonDumpFileFilter.cs
+++ b/source/LootDumpProcessor/Process/Reader/Filters/JsonDumpFileFilter.cs
@@ -1,6 +1,8 @@
using System.Globalization;
using System.Text.RegularExpressions;
+using LootDumpProcessor.Model.Config;
using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
namespace LootDumpProcessor.Process.Reader.Filters;
@@ -9,12 +11,14 @@ public class JsonDumpFileFilter : IFileFilter
private readonly ILogger _logger;
private readonly Regex _fileNameDateRegex = new("([0-9]{4}(-[0-9]{2}){2}_((-){0,1}[0-9]{2}){3})");
private readonly DateTime _parsedThresholdDate;
+ private readonly Config _config;
- public JsonDumpFileFilter(ILogger logger)
+ public JsonDumpFileFilter(ILogger logger, IOptions config)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
+ _config = (config ?? throw new ArgumentNullException(nameof(config))).Value;
// Calculate parsed date from config threshold
- if (string.IsNullOrEmpty(LootDumpProcessorContext.GetConfig().ReaderConfig.ThresholdDate))
+ if (string.IsNullOrEmpty(_config.ReaderConfig.ThresholdDate))
{
_logger.LogWarning("ThresholdDate is null or empty in configs, defaulting to current day minus 30 days");
_parsedThresholdDate = DateTime.Now - TimeSpan.FromDays(30);
@@ -22,7 +26,7 @@ public class JsonDumpFileFilter : IFileFilter
else
{
_parsedThresholdDate = DateTime.ParseExact(
- LootDumpProcessorContext.GetConfig().ReaderConfig.ThresholdDate,
+ _config.ReaderConfig.ThresholdDate,
"yyyy-MM-dd",
CultureInfo.InvariantCulture
);
diff --git a/source/LootDumpProcessor/Process/Reader/Intake/JsonFileIntakeReader.cs b/source/LootDumpProcessor/Process/Reader/Intake/JsonFileIntakeReader.cs
index 683cab1..b0dc91d 100644
--- a/source/LootDumpProcessor/Process/Reader/Intake/JsonFileIntakeReader.cs
+++ b/source/LootDumpProcessor/Process/Reader/Intake/JsonFileIntakeReader.cs
@@ -1,19 +1,23 @@
using System.Collections.Concurrent;
using System.Text.Json;
+using LootDumpProcessor.Model.Config;
using LootDumpProcessor.Model.Input;
using LootDumpProcessor.Model.Processing;
using LootDumpProcessor.Serializers.Json;
using LootDumpProcessor.Utils;
using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
namespace LootDumpProcessor.Process.Reader.Intake;
-public class JsonFileIntakeReader(ILogger logger) : IIntakeReader
+public class JsonFileIntakeReader(ILogger logger, IOptions config) : IIntakeReader
{
private readonly ILogger _logger = logger ?? throw new ArgumentNullException(nameof(logger));
- private readonly HashSet? _ignoredLocations = LootDumpProcessorContext.GetConfig()
- .ReaderConfig.IntakeReaderConfig?.IgnoredDumpLocations.ToHashSet();
+ private readonly Config _config = (config ?? throw new ArgumentNullException(nameof(config))).Value;
+
+ private HashSet IgnoredLocations => _config
+ .ReaderConfig.IntakeReaderConfig.IgnoredDumpLocations.ToHashSet();
private readonly ConcurrentDictionary _totalMapDumpsCounter = new();
@@ -43,14 +47,14 @@ public class JsonFileIntakeReader(ILogger logger) : IIntak
_logger.LogError("Could not parse date from file: {File}", file);
var fi = JsonSerializer.Deserialize(fileData, JsonSerializerSettings.Default);
- if (fi?.Data.LocationLoot.Name != null && (!_ignoredLocations?.Contains(fi.Data.LocationLoot.Name) ?? true))
+ if (fi?.Data.LocationLoot.Name != null && (!IgnoredLocations?.Contains(fi.Data.LocationLoot.Name) ?? true))
{
var mapName = fi.Data.LocationLoot.Name;
var mapId = fi.Data.LocationLoot.Id.ToLower();
var counter = _totalMapDumpsCounter.AddOrUpdate(mapName, 0, (_, current) => current);
- var maxDumpsPerMap = LootDumpProcessorContext.GetConfig()
+ var maxDumpsPerMap = _config
.ReaderConfig.IntakeReaderConfig?.MaxDumpsPerMap ?? 1500;
if (counter < maxDumpsPerMap)
diff --git a/source/LootDumpProcessor/Process/ComposedKeyGenerator.cs b/source/LootDumpProcessor/Process/Services/ComposedKeyGenerator/ComposedKeyGenerator.cs
similarity index 84%
rename from source/LootDumpProcessor/Process/ComposedKeyGenerator.cs
rename to source/LootDumpProcessor/Process/Services/ComposedKeyGenerator/ComposedKeyGenerator.cs
index d552d56..8e5d49c 100644
--- a/source/LootDumpProcessor/Process/ComposedKeyGenerator.cs
+++ b/source/LootDumpProcessor/Process/Services/ComposedKeyGenerator/ComposedKeyGenerator.cs
@@ -1,8 +1,10 @@
using System.Globalization;
using LootDumpProcessor.Model;
using LootDumpProcessor.Model.Processing;
+using LootDumpProcessor.Process.Services.KeyGenerator;
+using LootDumpProcessor.Process.Services.TarkovItemsProvider;
-namespace LootDumpProcessor.Process;
+namespace LootDumpProcessor.Process.Services.ComposedKeyGenerator;
public class ComposedKeyGenerator(ITarkovItemsProvider tarkovItemsProvider, IKeyGenerator keyGenerator)
: IComposedKeyGenerator
diff --git a/source/LootDumpProcessor/Process/IComposedKeyGenerator.cs b/source/LootDumpProcessor/Process/Services/ComposedKeyGenerator/IComposedKeyGenerator.cs
similarity index 66%
rename from source/LootDumpProcessor/Process/IComposedKeyGenerator.cs
rename to source/LootDumpProcessor/Process/Services/ComposedKeyGenerator/IComposedKeyGenerator.cs
index 09d974e..af1187d 100644
--- a/source/LootDumpProcessor/Process/IComposedKeyGenerator.cs
+++ b/source/LootDumpProcessor/Process/Services/ComposedKeyGenerator/IComposedKeyGenerator.cs
@@ -1,6 +1,6 @@
using LootDumpProcessor.Model;
-namespace LootDumpProcessor.Process;
+namespace LootDumpProcessor.Process.Services.ComposedKeyGenerator;
public interface IComposedKeyGenerator
{
diff --git a/source/LootDumpProcessor/Process/Services/ForcedItemsProvider/ForcedItemsProvider.cs b/source/LootDumpProcessor/Process/Services/ForcedItemsProvider/ForcedItemsProvider.cs
new file mode 100644
index 0000000..f06824f
--- /dev/null
+++ b/source/LootDumpProcessor/Process/Services/ForcedItemsProvider/ForcedItemsProvider.cs
@@ -0,0 +1,56 @@
+using System.Collections.Frozen;
+using System.Collections.Immutable;
+using LootDumpProcessor.Model.Config;
+using LootDumpProcessor.Model.Output.StaticContainer;
+using LootDumpProcessor.Serializers.Yaml;
+
+namespace LootDumpProcessor.Process.Services.ForcedItemsProvider;
+
+public class ForcedItemsProvider : IForcedItemsProvider
+{
+ private static readonly string ForcedStaticPath = Path.Combine("Config", "forced_static.yaml");
+ private static readonly string ForcedLooseLootPath = Path.Combine("Config", "forced_loose.yaml");
+
+ private ForcedStatic? _forcedStatic;
+ private FrozenDictionary>? _forcedLoose;
+
+ public async Task GetForcedStatic()
+ {
+ if (_forcedStatic is not null) return _forcedStatic;
+ _forcedStatic = await ReadForcedStatic();
+ return _forcedStatic;
+ }
+
+ private async Task ReadForcedStatic()
+ {
+ var forcedStaticContent = await File.ReadAllTextAsync(ForcedStaticPath);
+
+ // Workaround needed because YamlDotNet cannot deserialize properly
+ var forcedStaticDto = Yaml.Deserializer.Deserialize(forcedStaticContent);
+ var forcedStatic = new ForcedStatic(
+ forcedStaticDto.StaticWeaponIds.AsReadOnly(),
+ forcedStaticDto.ForcedItems.ToFrozenDictionary(
+ kvp => kvp.Key,
+ IReadOnlyList (kvp) => kvp.Value.AsReadOnly()
+ ));
+
+ return forcedStatic;
+ }
+
+ public async Task>> GetForcedLooseItems()
+ {
+ if (_forcedLoose is not null) return _forcedLoose;
+ _forcedLoose = await ReadForcedLooseItems();
+ return _forcedLoose;
+ }
+
+ private async Task>> ReadForcedLooseItems()
+ {
+ var forcedLooseContent = await File.ReadAllTextAsync(ForcedLooseLootPath);
+ var forcedLooseLoot = Yaml.Deserializer.Deserialize>>(forcedLooseContent);
+ return forcedLooseLoot.ToFrozenDictionary(
+ pair => pair.Key,
+ pair => ImmutableHashSet.CreateRange(pair.Value)
+ );
+ }
+}
\ No newline at end of file
diff --git a/source/LootDumpProcessor/Process/Services/ForcedItemsProvider/IForcedItemsProvider.cs b/source/LootDumpProcessor/Process/Services/ForcedItemsProvider/IForcedItemsProvider.cs
new file mode 100644
index 0000000..50a9214
--- /dev/null
+++ b/source/LootDumpProcessor/Process/Services/ForcedItemsProvider/IForcedItemsProvider.cs
@@ -0,0 +1,11 @@
+using System.Collections.Frozen;
+using System.Collections.Immutable;
+using LootDumpProcessor.Model.Config;
+
+namespace LootDumpProcessor.Process.Services.ForcedItemsProvider;
+
+public interface IForcedItemsProvider
+{
+ Task GetForcedStatic();
+ Task>> GetForcedLooseItems();
+}
\ No newline at end of file
diff --git a/source/LootDumpProcessor/Process/Services/KeyGenerator/IKeyGenerator.cs b/source/LootDumpProcessor/Process/Services/KeyGenerator/IKeyGenerator.cs
new file mode 100644
index 0000000..0c951df
--- /dev/null
+++ b/source/LootDumpProcessor/Process/Services/KeyGenerator/IKeyGenerator.cs
@@ -0,0 +1,6 @@
+namespace LootDumpProcessor.Process.Services.KeyGenerator;
+
+public interface IKeyGenerator
+{
+ string Generate();
+}
\ No newline at end of file
diff --git a/source/LootDumpProcessor/Process/NumericKeyGenerator.cs b/source/LootDumpProcessor/Process/Services/KeyGenerator/NumericKeyGenerator.cs
similarity index 79%
rename from source/LootDumpProcessor/Process/NumericKeyGenerator.cs
rename to source/LootDumpProcessor/Process/Services/KeyGenerator/NumericKeyGenerator.cs
index a224509..947dc39 100644
--- a/source/LootDumpProcessor/Process/NumericKeyGenerator.cs
+++ b/source/LootDumpProcessor/Process/Services/KeyGenerator/NumericKeyGenerator.cs
@@ -1,4 +1,4 @@
-namespace LootDumpProcessor.Process;
+namespace LootDumpProcessor.Process.Services.KeyGenerator;
public class NumericKeyGenerator : IKeyGenerator
{
diff --git a/source/LootDumpProcessor/Process/ITarkovItemsProvider.cs b/source/LootDumpProcessor/Process/Services/TarkovItemsProvider/ITarkovItemsProvider.cs
similarity index 74%
rename from source/LootDumpProcessor/Process/ITarkovItemsProvider.cs
rename to source/LootDumpProcessor/Process/Services/TarkovItemsProvider/ITarkovItemsProvider.cs
index ca0c9fc..737fbfb 100644
--- a/source/LootDumpProcessor/Process/ITarkovItemsProvider.cs
+++ b/source/LootDumpProcessor/Process/Services/TarkovItemsProvider/ITarkovItemsProvider.cs
@@ -1,4 +1,4 @@
-namespace LootDumpProcessor.Process;
+namespace LootDumpProcessor.Process.Services.TarkovItemsProvider;
public interface ITarkovItemsProvider
{
diff --git a/source/LootDumpProcessor/Process/TarkovItemsProvider.cs b/source/LootDumpProcessor/Process/Services/TarkovItemsProvider/TarkovItemsProvider.cs
similarity index 91%
rename from source/LootDumpProcessor/Process/TarkovItemsProvider.cs
rename to source/LootDumpProcessor/Process/Services/TarkovItemsProvider/TarkovItemsProvider.cs
index 62dac1f..1d1479b 100644
--- a/source/LootDumpProcessor/Process/TarkovItemsProvider.cs
+++ b/source/LootDumpProcessor/Process/Services/TarkovItemsProvider/TarkovItemsProvider.cs
@@ -1,22 +1,26 @@
using System.Collections.Frozen;
using System.Text.Json;
+using LootDumpProcessor.Model.Config;
using LootDumpProcessor.Model.Tarkov;
using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
-namespace LootDumpProcessor.Process;
+namespace LootDumpProcessor.Process.Services.TarkovItemsProvider;
public class TarkovItemsProvider : ITarkovItemsProvider
{
private readonly ILogger _logger;
private readonly FrozenDictionary? _items;
+ private readonly Config _config;
- private static readonly string ItemsFilePath = Path.Combine(
- LootDumpProcessorContext.GetConfig().ServerLocation,
+ private string ItemsFilePath => Path.Combine(
+ _config.ServerLocation,
"project", "assets", "database", "templates", "items.json");
- public TarkovItemsProvider(ILogger logger)
+ public TarkovItemsProvider(ILogger logger, IOptions config)
{
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
+ _config = (config ?? throw new ArgumentNullException(nameof(config))).Value;
try
{
diff --git a/source/LootDumpProcessor/Process/Writer/FileWriter.cs b/source/LootDumpProcessor/Process/Writer/FileWriter.cs
index 8866f46..be769a8 100644
--- a/source/LootDumpProcessor/Process/Writer/FileWriter.cs
+++ b/source/LootDumpProcessor/Process/Writer/FileWriter.cs
@@ -1,18 +1,21 @@
using System.Text.Json;
+using LootDumpProcessor.Model.Config;
using LootDumpProcessor.Model.Output;
using LootDumpProcessor.Model.Output.LooseLoot;
using LootDumpProcessor.Model.Output.StaticContainer;
using LootDumpProcessor.Serializers.Json;
+using Microsoft.Extensions.Options;
namespace LootDumpProcessor.Process.Writer;
public class FileWriter : IWriter
{
- private static readonly string _outputPath;
+ private readonly string _outputPath;
- static FileWriter()
+ public FileWriter(IOptions config)
{
- var path = LootDumpProcessorContext.GetConfig().WriterConfig.OutputLocation;
+ var config1 = (config ?? throw new ArgumentNullException(nameof(config))).Value;
+ var path = config1.WriterConfig.OutputLocation;
if (string.IsNullOrEmpty(path))
throw new Exception("Output directory must be set in WriterConfigs");
diff --git a/source/LootDumpProcessor/Process/Writer/WriterFactory.cs b/source/LootDumpProcessor/Process/Writer/WriterFactory.cs
deleted file mode 100644
index 67e5eae..0000000
--- a/source/LootDumpProcessor/Process/Writer/WriterFactory.cs
+++ /dev/null
@@ -1,8 +0,0 @@
-namespace LootDumpProcessor.Process.Writer;
-
-public static class WriterFactory
-{
- public static IWriter GetInstance() =>
- // implement actual factory someday
- new FileWriter();
-}
\ No newline at end of file
diff --git a/source/LootDumpProcessor/Program.cs b/source/LootDumpProcessor/Program.cs
index 6df3a17..749ed2f 100644
--- a/source/LootDumpProcessor/Program.cs
+++ b/source/LootDumpProcessor/Program.cs
@@ -1,15 +1,5 @@
using LootDumpProcessor.Process;
-using LootDumpProcessor.Process.Processor.DumpProcessor;
-using LootDumpProcessor.Process.Processor.FileProcessor;
-using LootDumpProcessor.Process.Processor.v2.AmmoProcessor;
-using LootDumpProcessor.Process.Processor.v2.LooseLootProcessor;
-using LootDumpProcessor.Process.Processor.v2.StaticContainersProcessor;
-using LootDumpProcessor.Process.Processor.v2.StaticLootProcessor;
-using LootDumpProcessor.Process.Reader.Filters;
-using LootDumpProcessor.Process.Reader.Intake;
-using LootDumpProcessor.Storage;
using Microsoft.Extensions.DependencyInjection;
-using Microsoft.Extensions.Logging;
namespace LootDumpProcessor;
@@ -18,33 +8,11 @@ public static class Program
public static async Task Main()
{
var services = new ServiceCollection();
- RegisterServices(services);
+ services.AddLootDumpProcessor();
await using var serviceProvider = services.BuildServiceProvider();
- // startup the pipeline
var pipeline = serviceProvider.GetRequiredService();
await pipeline.Execute();
}
-
- private static void RegisterServices(ServiceCollection services)
- {
- services.AddLogging(configure => configure.AddConsole());
-
- services.AddTransient();
- services.AddTransient();
- services.AddTransient();
- services.AddTransient();
-
- services.AddSingleton();
- services.AddSingleton(_ => DataStorageFactory.GetInstance());
- services.AddSingleton();
- services.AddTransient();
-
- services.AddTransient();
- services.AddTransient();
- services.AddTransient();
- services.AddTransient();
- services.AddTransient();
- }
}
\ No newline at end of file
diff --git a/source/LootDumpProcessor/ServiceCollectionExtensions.cs b/source/LootDumpProcessor/ServiceCollectionExtensions.cs
new file mode 100644
index 0000000..6f90459
--- /dev/null
+++ b/source/LootDumpProcessor/ServiceCollectionExtensions.cs
@@ -0,0 +1,103 @@
+using LootDumpProcessor.Model.Config;
+using LootDumpProcessor.Process;
+using LootDumpProcessor.Process.Collector;
+using LootDumpProcessor.Process.Processor.AmmoProcessor;
+using LootDumpProcessor.Process.Processor.DumpProcessor;
+using LootDumpProcessor.Process.Processor.FileProcessor;
+using LootDumpProcessor.Process.Processor.LooseLootProcessor;
+using LootDumpProcessor.Process.Processor.StaticContainersProcessor;
+using LootDumpProcessor.Process.Processor.StaticLootProcessor;
+using LootDumpProcessor.Process.Reader.Filters;
+using LootDumpProcessor.Process.Reader.Intake;
+using LootDumpProcessor.Process.Services.ComposedKeyGenerator;
+using LootDumpProcessor.Process.Services.ForcedItemsProvider;
+using LootDumpProcessor.Process.Services.KeyGenerator;
+using LootDumpProcessor.Process.Services.TarkovItemsProvider;
+using LootDumpProcessor.Process.Writer;
+using LootDumpProcessor.Storage;
+using LootDumpProcessor.Storage.Implementations.File;
+using LootDumpProcessor.Storage.Implementations.Memory;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Options;
+
+namespace LootDumpProcessor;
+
+public static class ServiceCollectionExtensions
+{
+ public static void AddLootDumpProcessor(this ServiceCollection services)
+ {
+ services.AddLogging(configure => configure.AddConsole());
+ AddConfiguration(services);
+ AddCollector(services);
+ AddDataStorage(services);
+ RegisterProcessors(services);
+
+ services.AddSingleton();
+
+ services.AddSingleton();
+ services.AddSingleton();
+ services.AddSingleton();
+ services.AddTransient();
+
+ services.AddTransient();
+ services.AddTransient();
+ services.AddTransient();
+ services.AddTransient();
+ services.AddTransient();
+ services.AddTransient();
+ }
+
+ private static void AddConfiguration(IServiceCollection services)
+ {
+ const string configPath = "Config/config.json";
+ var configuration = new ConfigurationBuilder()
+ .AddJsonFile(configPath)
+ .AddEnvironmentVariables()
+ .Build();
+
+ services.AddOptions()
+ .Bind(configuration)
+ .ValidateDataAnnotations()
+ .ValidateOnStart();
+ }
+
+ private static void RegisterProcessors(IServiceCollection services)
+ {
+ services.AddTransient();
+ services.AddTransient();
+ services.AddTransient();
+ services.AddTransient();
+ }
+
+ private static void AddCollector(IServiceCollection services)
+ {
+ services.AddSingleton(provider =>
+ {
+ var config = provider.GetRequiredService>();
+ var collectorType = config.Value.CollectorConfig.CollectorType;
+ return collectorType switch
+ {
+ CollectorType.Memory => new HashSetCollector(),
+ CollectorType.Dump => new DumpCollector(config),
+ _ => throw new ArgumentOutOfRangeException($"CollectorType '{collectorType} is not supported'")
+ };
+ });
+ }
+
+ private static void AddDataStorage(IServiceCollection services)
+ {
+ services.AddSingleton(provider =>
+ {
+ var config = provider.GetRequiredService>().Value;
+ var dataStorageType = config.DataStorageConfig.DataStorageType;
+ return dataStorageType switch
+ {
+ DataStorageTypes.File => new FileDataStorage(provider.GetRequiredService()),
+ DataStorageTypes.Memory => new MemoryDataStorage(),
+ _ => throw new ArgumentOutOfRangeException($"DataStorageType '{dataStorageType} is not supported'")
+ };
+ });
+ }
+}
\ No newline at end of file
diff --git a/source/LootDumpProcessor/Storage/DataStorageFactory.cs b/source/LootDumpProcessor/Storage/DataStorageFactory.cs
deleted file mode 100644
index 6fb5ae0..0000000
--- a/source/LootDumpProcessor/Storage/DataStorageFactory.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-using System.Collections.Concurrent;
-using LootDumpProcessor.Storage.Implementations.File;
-using LootDumpProcessor.Storage.Implementations.Memory;
-
-namespace LootDumpProcessor.Storage;
-
-public static class DataStorageFactory
-{
- private static readonly ConcurrentDictionary DataStorage = new();
-
- /**
- * Requires LootDumpProcessorContext to be initialized before using
- */
- public static IDataStorage GetInstance() =>
- GetInstance(LootDumpProcessorContext.GetConfig().DataStorageConfig.DataStorageType);
-
- private static IDataStorage GetInstance(DataStorageTypes type)
- {
- if (DataStorage.TryGetValue(type, out var dataStorage)) return dataStorage;
-
- dataStorage = type switch
- {
- DataStorageTypes.File => new FileDataStorage(),
- DataStorageTypes.Memory => new MemoryDataStorage(),
- _ => throw new ArgumentOutOfRangeException(nameof(type), type, null)
- };
-
- DataStorage.TryAdd(type, dataStorage);
-
- return dataStorage;
- }
-}
\ No newline at end of file
diff --git a/source/LootDumpProcessor/Storage/Implementations/File/FileDataStorage.cs b/source/LootDumpProcessor/Storage/Implementations/File/FileDataStorage.cs
index c132999..a4a459e 100644
--- a/source/LootDumpProcessor/Storage/Implementations/File/FileDataStorage.cs
+++ b/source/LootDumpProcessor/Storage/Implementations/File/FileDataStorage.cs
@@ -1,16 +1,19 @@
namespace LootDumpProcessor.Storage.Implementations.File;
-public class FileDataStorage : IDataStorage
+public class FileDataStorage(StoreHandlerFactory storeHandlerFactory) : IDataStorage
{
+ private readonly StoreHandlerFactory _storeHandlerFactory =
+ storeHandlerFactory ?? throw new ArgumentNullException(nameof(storeHandlerFactory));
+
public void Store(TEntity entity) where TEntity : IKeyable
{
- StoreHandlerFactory.GetInstance(entity.GetKey().GetKeyType()).Store(entity);
+ _storeHandlerFactory.GetInstance(entity.GetKey().GetKeyType()).Store(entity);
}
- public bool Exists(IKey key) => StoreHandlerFactory.GetInstance(key.GetKeyType()).Exists(key);
+ public bool Exists(IKey key) => _storeHandlerFactory.GetInstance(key.GetKeyType()).Exists(key);
public T GetItem(IKey key) where T : IKeyable =>
- StoreHandlerFactory.GetInstance(key.GetKeyType()).Retrieve(key);
+ _storeHandlerFactory.GetInstance(key.GetKeyType()).Retrieve(key);
public void Clear()
{
diff --git a/source/LootDumpProcessor/Storage/Implementations/File/Handlers/AbstractStoreHandler.cs b/source/LootDumpProcessor/Storage/Implementations/File/Handlers/AbstractStoreHandler.cs
index 5793226..b1c4685 100644
--- a/source/LootDumpProcessor/Storage/Implementations/File/Handlers/AbstractStoreHandler.cs
+++ b/source/LootDumpProcessor/Storage/Implementations/File/Handlers/AbstractStoreHandler.cs
@@ -1,10 +1,14 @@
using System.Text.Json;
+using LootDumpProcessor.Model.Config;
using LootDumpProcessor.Serializers.Json;
+using Microsoft.Extensions.Options;
namespace LootDumpProcessor.Storage.Implementations.File.Handlers;
-public abstract class AbstractStoreHandler : IStoreHandler
+public abstract class AbstractStoreHandler(IOptions config) : IStoreHandler
{
+ private readonly Config _config = (config ?? throw new ArgumentNullException(nameof(config))).Value;
+
public void Store(TEntity entity, bool failIfDuplicate = true) where TEntity : IKeyable
{
var locationWithFile = GetLocation(entity.GetKey());
@@ -34,8 +38,8 @@ public abstract class AbstractStoreHandler : IStoreHandler
protected virtual string GetBaseLocation()
{
var location =
- string.IsNullOrEmpty(LootDumpProcessorContext.GetConfig().DataStorageConfig.FileDataStorageTempLocation)
- ? LootDumpProcessorContext.GetConfig().DataStorageConfig.FileDataStorageTempLocation
+ string.IsNullOrEmpty(_config.DataStorageConfig.FileDataStorageTempLocation)
+ ? _config.DataStorageConfig.FileDataStorageTempLocation
: Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
return $"{location}/SPT/tmp/LootGen";
diff --git a/source/LootDumpProcessor/Storage/Implementations/File/Handlers/FlatStoreHandler.cs b/source/LootDumpProcessor/Storage/Implementations/File/Handlers/FlatStoreHandler.cs
index d8c9a9f..63f2eb8 100644
--- a/source/LootDumpProcessor/Storage/Implementations/File/Handlers/FlatStoreHandler.cs
+++ b/source/LootDumpProcessor/Storage/Implementations/File/Handlers/FlatStoreHandler.cs
@@ -1,6 +1,9 @@
+using LootDumpProcessor.Model.Config;
+using Microsoft.Extensions.Options;
+
namespace LootDumpProcessor.Storage.Implementations.File.Handlers;
-public class FlatStoreHandler : AbstractStoreHandler
+public class FlatStoreHandler(IOptions config) : AbstractStoreHandler(config)
{
protected override string GetLocation(IKey key)
{
diff --git a/source/LootDumpProcessor/Storage/Implementations/File/Handlers/SubdivisionedStoreHandler.cs b/source/LootDumpProcessor/Storage/Implementations/File/Handlers/SubdivisionedStoreHandler.cs
index e720d42..7dc3d81 100644
--- a/source/LootDumpProcessor/Storage/Implementations/File/Handlers/SubdivisionedStoreHandler.cs
+++ b/source/LootDumpProcessor/Storage/Implementations/File/Handlers/SubdivisionedStoreHandler.cs
@@ -1,6 +1,9 @@
+using LootDumpProcessor.Model.Config;
+using Microsoft.Extensions.Options;
+
namespace LootDumpProcessor.Storage.Implementations.File.Handlers;
-public class SubdivisionedStoreHandler : AbstractStoreHandler
+public class SubdivisionedStoreHandler(IOptions config) : AbstractStoreHandler(config)
{
protected override string GetLocation(IKey key)
{
diff --git a/source/LootDumpProcessor/Storage/Implementations/File/StoreHandlerFactory.cs b/source/LootDumpProcessor/Storage/Implementations/File/StoreHandlerFactory.cs
index c09a3aa..1ca698f 100644
--- a/source/LootDumpProcessor/Storage/Implementations/File/StoreHandlerFactory.cs
+++ b/source/LootDumpProcessor/Storage/Implementations/File/StoreHandlerFactory.cs
@@ -1,20 +1,27 @@
using System.Collections.Concurrent;
+using LootDumpProcessor.Model.Config;
using LootDumpProcessor.Storage.Implementations.File.Handlers;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Options;
namespace LootDumpProcessor.Storage.Implementations.File;
-public class StoreHandlerFactory
+public class StoreHandlerFactory(IServiceProvider serviceProvider)
{
+ private readonly IServiceProvider _serviceProvider =
+ serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider));
+
private static readonly ConcurrentDictionary Handlers = new();
- public static IStoreHandler GetInstance(KeyType type)
+ public IStoreHandler GetInstance(KeyType type)
{
if (Handlers.TryGetValue(type, out var handler)) return handler;
+ var config = _serviceProvider.GetRequiredService>();
handler = type switch
{
- KeyType.Unique => new FlatStoreHandler(),
- KeyType.Subdivisioned => new SubdivisionedStoreHandler(),
+ KeyType.Unique => new FlatStoreHandler(config),
+ KeyType.Subdivisioned => new SubdivisionedStoreHandler(config),
_ => throw new ArgumentOutOfRangeException(nameof(type), type, null)
};
Handlers.TryAdd(type, handler);