mirror of
https://github.com/sp-tarkov/loot-dump-processor.git
synced 2025-02-13 06:50:45 -05:00
Refactored dependency injection and logging infrastructure
The changes include: - Replaced custom logging with Microsoft.Extensions.Logging - Added TarkovItemsProvider and ComposedKeyGenerator services - Simplified configuration models by removing redundant options - Improved dependency injection in processors and readers - Removed unused factory methods and simplified service registration
This commit is contained in:
parent
8c0d46585a
commit
047372b6dc
@ -1,9 +0,0 @@
|
|||||||
namespace LootDumpProcessor.Logger;
|
|
||||||
|
|
||||||
public interface ILogger
|
|
||||||
{
|
|
||||||
void Setup();
|
|
||||||
void Log(string message, LogLevel level);
|
|
||||||
bool CanBeLogged(LogLevel level);
|
|
||||||
void Stop();
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
namespace LootDumpProcessor.Logger;
|
|
||||||
|
|
||||||
public enum LogLevel
|
|
||||||
{
|
|
||||||
Error,
|
|
||||||
Warning,
|
|
||||||
Info,
|
|
||||||
Debug
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
namespace LootDumpProcessor.Logger;
|
|
||||||
|
|
||||||
public static class LoggerFactory
|
|
||||||
{
|
|
||||||
private static ILogger? _logger;
|
|
||||||
|
|
||||||
public static ILogger GetInstance()
|
|
||||||
{
|
|
||||||
if (_logger == null)
|
|
||||||
_logger = new QueueLogger();
|
|
||||||
// TODO: implement factory
|
|
||||||
return _logger;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,109 +0,0 @@
|
|||||||
using System.Collections.Concurrent;
|
|
||||||
|
|
||||||
namespace LootDumpProcessor.Logger;
|
|
||||||
|
|
||||||
public class QueueLogger : ILogger
|
|
||||||
{
|
|
||||||
private readonly BlockingCollection<LoggedMessage> queuedMessages = new();
|
|
||||||
private Task? loggerThread;
|
|
||||||
private bool isRunning;
|
|
||||||
private int logLevel;
|
|
||||||
private const int LogTerminationTimeoutMs = 1000;
|
|
||||||
private const int LogTerminationRetryCount = 3;
|
|
||||||
|
|
||||||
public void Setup()
|
|
||||||
{
|
|
||||||
SetLogLevel();
|
|
||||||
isRunning = true;
|
|
||||||
loggerThread = Task.Factory.StartNew(() =>
|
|
||||||
{
|
|
||||||
while (isRunning)
|
|
||||||
{
|
|
||||||
while (queuedMessages.TryTake(out var value))
|
|
||||||
{
|
|
||||||
Console.ResetColor();
|
|
||||||
switch (value.LogLevel)
|
|
||||||
{
|
|
||||||
case LogLevel.Error:
|
|
||||||
Console.BackgroundColor = ConsoleColor.Red;
|
|
||||||
Console.ForegroundColor = ConsoleColor.Black;
|
|
||||||
break;
|
|
||||||
case LogLevel.Warning:
|
|
||||||
Console.BackgroundColor = ConsoleColor.Yellow;
|
|
||||||
Console.ForegroundColor = ConsoleColor.Black;
|
|
||||||
break;
|
|
||||||
case LogLevel.Debug:
|
|
||||||
Console.ForegroundColor = ConsoleColor.DarkCyan;
|
|
||||||
break;
|
|
||||||
case LogLevel.Info:
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
Console.WriteLine(value.Message);
|
|
||||||
}
|
|
||||||
|
|
||||||
Thread.Sleep(
|
|
||||||
TimeSpan.FromMilliseconds(LootDumpProcessorContext.GetConfig().LoggerConfig.QueueLoggerPoolingTimeoutMs));
|
|
||||||
}
|
|
||||||
}, TaskCreationOptions.LongRunning);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SetLogLevel()
|
|
||||||
{
|
|
||||||
logLevel = GetLogLevel(LootDumpProcessorContext.GetConfig().LoggerConfig.LogLevel);
|
|
||||||
}
|
|
||||||
|
|
||||||
private int GetLogLevel(LogLevel level)
|
|
||||||
{
|
|
||||||
return level switch
|
|
||||||
{
|
|
||||||
LogLevel.Error => 1,
|
|
||||||
LogLevel.Warning => 2,
|
|
||||||
LogLevel.Info => 3,
|
|
||||||
LogLevel.Debug => 4,
|
|
||||||
_ => throw new ArgumentOutOfRangeException()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Log(string message, LogLevel level)
|
|
||||||
{
|
|
||||||
if (GetLogLevel(level) <= logLevel)
|
|
||||||
queuedMessages.Add(new LoggedMessage { Message = message, LogLevel = level });
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool CanBeLogged(LogLevel level)
|
|
||||||
{
|
|
||||||
return GetLogLevel(level) <= logLevel;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait for graceful termination of the logging thread
|
|
||||||
public void Stop()
|
|
||||||
{
|
|
||||||
isRunning = false;
|
|
||||||
if (loggerThread != null)
|
|
||||||
{
|
|
||||||
Console.ResetColor();
|
|
||||||
var retryCount = 0;
|
|
||||||
while (!loggerThread.IsCompleted)
|
|
||||||
{
|
|
||||||
if (retryCount == LogTerminationRetryCount)
|
|
||||||
{
|
|
||||||
Console.WriteLine(
|
|
||||||
$"Logger thread did not terminate by itself after {retryCount} retries. Some log messages may be lost.");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
Console.WriteLine($"Waiting {LogTerminationTimeoutMs}ms for logger termination");
|
|
||||||
Thread.Sleep(LogTerminationTimeoutMs);
|
|
||||||
retryCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class LoggedMessage
|
|
||||||
{
|
|
||||||
public string Message { get; init; }
|
|
||||||
public LogLevel LogLevel { get; init; }
|
|
||||||
}
|
|
||||||
}
|
|
@ -20,7 +20,7 @@ public static class LootDumpProcessorContext
|
|||||||
private static readonly object _forcedItemsLock = new();
|
private static readonly object _forcedItemsLock = new();
|
||||||
private static Dictionary<string, HashSet<string>>? _forcedLoose;
|
private static Dictionary<string, HashSet<string>>? _forcedLoose;
|
||||||
private static readonly object _forcedLooseLock = new();
|
private static readonly object _forcedLooseLock = new();
|
||||||
private static TarkovItems? _tarkovItems;
|
private static TarkovItemsProvider? _tarkovItems;
|
||||||
private static readonly object _tarkovItemsLock = new();
|
private static readonly object _tarkovItemsLock = new();
|
||||||
|
|
||||||
public static Config GetConfig()
|
public static Config GetConfig()
|
||||||
@ -54,25 +54,6 @@ public static class LootDumpProcessorContext
|
|||||||
return _forcedStatic;
|
return _forcedStatic;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Not Used
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static Dictionary<string, MapDirectoryMapping> GetDirectoryMappings()
|
|
||||||
{
|
|
||||||
lock (_mapDirectoryMappingsLock)
|
|
||||||
{
|
|
||||||
if (_mapDirectoryMappings == null)
|
|
||||||
{
|
|
||||||
_mapDirectoryMappings = YamlSerializerFactory.GetInstance()
|
|
||||||
.Deserialize<Dictionary<string, MapDirectoryMapping>>(
|
|
||||||
File.ReadAllText("./Config/map_directory_mapping.yaml"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return _mapDirectoryMappings;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static HashSet<string> GetStaticWeaponIds()
|
public static HashSet<string> GetStaticWeaponIds()
|
||||||
{
|
{
|
||||||
lock (_staticWeaponIdsLock)
|
lock (_staticWeaponIdsLock)
|
||||||
@ -112,19 +93,4 @@ public static class LootDumpProcessorContext
|
|||||||
|
|
||||||
return _forcedLoose;
|
return _forcedLoose;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static TarkovItems GetTarkovItems()
|
|
||||||
{
|
|
||||||
lock (_tarkovItemsLock)
|
|
||||||
{
|
|
||||||
if (_tarkovItems == null)
|
|
||||||
{
|
|
||||||
_tarkovItems = new TarkovItems(
|
|
||||||
$"{GetConfig().ServerLocation}/project/assets/database/templates/items.json"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return _tarkovItems;
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -1,35 +1,15 @@
|
|||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
using LootDumpProcessor.Model.Processing;
|
|
||||||
using LootDumpProcessor.Utils;
|
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace LootDumpProcessor.Model;
|
namespace LootDumpProcessor.Model;
|
||||||
|
|
||||||
public class ComposedKey
|
public class ComposedKey(string key, Item? firstItem)
|
||||||
{
|
{
|
||||||
[JsonProperty("key")]
|
[JsonProperty("key")] [JsonPropertyName("key")] public string Key { get; init; } = key;
|
||||||
[JsonPropertyName("key")]
|
|
||||||
public string Key { get; init; }
|
|
||||||
|
|
||||||
[Newtonsoft.Json.JsonIgnore]
|
[Newtonsoft.Json.JsonIgnore]
|
||||||
[System.Text.Json.Serialization.JsonIgnore]
|
[System.Text.Json.Serialization.JsonIgnore]
|
||||||
public Item? FirstItem { get; }
|
public Item? FirstItem { get; } = firstItem;
|
||||||
|
|
||||||
public ComposedKey(Template template) : this(template.Items)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public ComposedKey(List<Item>? items)
|
|
||||||
{
|
|
||||||
Key = items?.Select(i => i.Tpl)
|
|
||||||
.Where(i => !string.IsNullOrEmpty(i) &&
|
|
||||||
!LootDumpProcessorContext.GetTarkovItems().IsBaseClass(i, BaseClasses.Ammo))
|
|
||||||
.Cast<string>()
|
|
||||||
.Select(i => (double)i.GetHashCode())
|
|
||||||
.Sum()
|
|
||||||
.ToString() ?? KeyGenerator.GetNextKey();
|
|
||||||
FirstItem = items?[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool Equals(object? obj)
|
public override bool Equals(object? obj)
|
||||||
{
|
{
|
||||||
|
@ -8,7 +8,7 @@ public class Config
|
|||||||
{
|
{
|
||||||
[JsonProperty("serverLocation")]
|
[JsonProperty("serverLocation")]
|
||||||
[JsonPropertyName("serverLocation")]
|
[JsonPropertyName("serverLocation")]
|
||||||
public string? ServerLocation { get; set; }
|
public string ServerLocation { get; set; } = string.Empty;
|
||||||
|
|
||||||
[JsonProperty("threads")]
|
[JsonProperty("threads")]
|
||||||
[JsonPropertyName("threads")]
|
[JsonPropertyName("threads")]
|
||||||
|
@ -6,10 +6,6 @@ namespace LootDumpProcessor.Model.Config;
|
|||||||
|
|
||||||
public class IntakeReaderConfig
|
public class IntakeReaderConfig
|
||||||
{
|
{
|
||||||
[JsonProperty("readerType")]
|
|
||||||
[JsonPropertyName("readerType")]
|
|
||||||
public IntakeReaderTypes IntakeReaderType { get; set; } = IntakeReaderTypes.Json;
|
|
||||||
|
|
||||||
[JsonProperty("maxDumpsPerMap")]
|
[JsonProperty("maxDumpsPerMap")]
|
||||||
[JsonPropertyName("maxDumpsPerMap")]
|
[JsonPropertyName("maxDumpsPerMap")]
|
||||||
public int MaxDumpsPerMap { get; set; } = 1500;
|
public int MaxDumpsPerMap { get; set; } = 1500;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
using LootDumpProcessor.Logger;
|
using Microsoft.Extensions.Logging;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace LootDumpProcessor.Model.Config;
|
namespace LootDumpProcessor.Model.Config;
|
||||||
@ -8,7 +8,7 @@ public class LoggerConfig
|
|||||||
{
|
{
|
||||||
[JsonProperty("logLevel")]
|
[JsonProperty("logLevel")]
|
||||||
[JsonPropertyName("logLevel")]
|
[JsonPropertyName("logLevel")]
|
||||||
public LogLevel LogLevel { get; set; } = LogLevel.Info;
|
public LogLevel LogLevel { get; set; }
|
||||||
|
|
||||||
[JsonProperty("queueLoggerPoolingTimeoutMs")]
|
[JsonProperty("queueLoggerPoolingTimeoutMs")]
|
||||||
[JsonPropertyName("queueLoggerPoolingTimeoutMs")]
|
[JsonPropertyName("queueLoggerPoolingTimeoutMs")]
|
||||||
|
@ -6,10 +6,6 @@ namespace LootDumpProcessor.Model.Config;
|
|||||||
|
|
||||||
public class PreProcessorConfig
|
public class PreProcessorConfig
|
||||||
{
|
{
|
||||||
[JsonProperty("preProcessors")]
|
|
||||||
[JsonPropertyName("preProcessors")]
|
|
||||||
public List<PreProcessReaderTypes>? PreProcessors { get; set; }
|
|
||||||
|
|
||||||
[JsonProperty("preProcessorTempFolder")]
|
[JsonProperty("preProcessorTempFolder")]
|
||||||
[JsonPropertyName("preProcessorTempFolder")]
|
[JsonPropertyName("preProcessorTempFolder")]
|
||||||
public string? PreProcessorTempFolder { get; set; }
|
public string? PreProcessorTempFolder { get; set; }
|
||||||
|
@ -22,15 +22,7 @@ public class ReaderConfig
|
|||||||
[JsonPropertyName("thresholdDate")]
|
[JsonPropertyName("thresholdDate")]
|
||||||
public string? ThresholdDate { get; set; }
|
public string? ThresholdDate { get; set; }
|
||||||
|
|
||||||
[JsonProperty("acceptedFileExtensions")]
|
|
||||||
[JsonPropertyName("acceptedFileExtensions")]
|
|
||||||
public List<string> AcceptedFileExtensions { get; set; } = new();
|
|
||||||
|
|
||||||
[JsonProperty("processSubFolders")]
|
[JsonProperty("processSubFolders")]
|
||||||
[JsonPropertyName("processSubFolders")]
|
[JsonPropertyName("processSubFolders")]
|
||||||
public bool ProcessSubFolders { get; set; }
|
public bool ProcessSubFolders { get; set; }
|
||||||
|
|
||||||
[JsonProperty("fileFilters")]
|
|
||||||
[JsonPropertyName("fileFilters")]
|
|
||||||
public List<FileFilterTypes>? FileFilters { get; set; }
|
|
||||||
}
|
}
|
24
source/LootDumpProcessor/Process/ComposedKeyGenerator.cs
Normal file
24
source/LootDumpProcessor/Process/ComposedKeyGenerator.cs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
using LootDumpProcessor.Model;
|
||||||
|
using LootDumpProcessor.Model.Processing;
|
||||||
|
using LootDumpProcessor.Utils;
|
||||||
|
|
||||||
|
namespace LootDumpProcessor.Process;
|
||||||
|
|
||||||
|
public class ComposedKeyGenerator(ITarkovItemsProvider tarkovItemsProvider) : IComposedKeyGenerator
|
||||||
|
{
|
||||||
|
private readonly ITarkovItemsProvider _tarkovItemsProvider =
|
||||||
|
tarkovItemsProvider ?? throw new ArgumentNullException(nameof(tarkovItemsProvider));
|
||||||
|
|
||||||
|
public ComposedKey Generate(IEnumerable<Item> items)
|
||||||
|
{
|
||||||
|
var key = items?.Select(i => i.Tpl)
|
||||||
|
.Where(i => !string.IsNullOrEmpty(i) &&
|
||||||
|
!_tarkovItemsProvider.IsBaseClass(i, BaseClasses.Ammo))
|
||||||
|
.Cast<string>()
|
||||||
|
.Select(i => (double)i.GetHashCode())
|
||||||
|
.Sum()
|
||||||
|
.ToString() ?? KeyGenerator.GetNextKey();
|
||||||
|
var firstItem = items?.FirstOrDefault();
|
||||||
|
return new ComposedKey(key, firstItem);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
using LootDumpProcessor.Model;
|
||||||
|
|
||||||
|
namespace LootDumpProcessor.Process;
|
||||||
|
|
||||||
|
public interface IComposedKeyGenerator
|
||||||
|
{
|
||||||
|
ComposedKey Generate(IEnumerable<Item> items);
|
||||||
|
}
|
@ -2,5 +2,5 @@ namespace LootDumpProcessor.Process;
|
|||||||
|
|
||||||
public interface IPipeline
|
public interface IPipeline
|
||||||
{
|
{
|
||||||
Task DoProcess();
|
Task Execute();
|
||||||
}
|
}
|
9
source/LootDumpProcessor/Process/ITarkovItemsProvider.cs
Normal file
9
source/LootDumpProcessor/Process/ITarkovItemsProvider.cs
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
namespace LootDumpProcessor.Process;
|
||||||
|
|
||||||
|
public interface ITarkovItemsProvider
|
||||||
|
{
|
||||||
|
bool IsBaseClass(string tpl, string baseclassId);
|
||||||
|
bool IsQuestItem(string tpl);
|
||||||
|
string? MaxDurability(string tpl);
|
||||||
|
string? AmmoCaliber(string tpl);
|
||||||
|
}
|
@ -1,11 +1,13 @@
|
|||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using LootDumpProcessor.Logger;
|
using LootDumpProcessor;
|
||||||
using LootDumpProcessor.Model;
|
using LootDumpProcessor.Model;
|
||||||
using LootDumpProcessor.Model.Input;
|
using LootDumpProcessor.Model.Input;
|
||||||
using LootDumpProcessor.Model.Output;
|
using LootDumpProcessor.Model.Output;
|
||||||
using LootDumpProcessor.Model.Output.LooseLoot;
|
using LootDumpProcessor.Model.Output.LooseLoot;
|
||||||
using LootDumpProcessor.Model.Output.StaticContainer;
|
using LootDumpProcessor.Model.Output.StaticContainer;
|
||||||
using LootDumpProcessor.Model.Processing;
|
using LootDumpProcessor.Model.Processing;
|
||||||
|
using LootDumpProcessor.Process;
|
||||||
|
using LootDumpProcessor.Process.Processor.DumpProcessor;
|
||||||
using LootDumpProcessor.Process.Processor.v2.AmmoProcessor;
|
using LootDumpProcessor.Process.Processor.v2.AmmoProcessor;
|
||||||
using LootDumpProcessor.Process.Processor.v2.LooseLootProcessor;
|
using LootDumpProcessor.Process.Processor.v2.LooseLootProcessor;
|
||||||
using LootDumpProcessor.Process.Processor.v2.StaticContainersProcessor;
|
using LootDumpProcessor.Process.Processor.v2.StaticContainersProcessor;
|
||||||
@ -14,52 +16,39 @@ using LootDumpProcessor.Serializers.Json;
|
|||||||
using LootDumpProcessor.Storage;
|
using LootDumpProcessor.Storage;
|
||||||
using LootDumpProcessor.Storage.Collections;
|
using LootDumpProcessor.Storage.Collections;
|
||||||
using LootDumpProcessor.Utils;
|
using LootDumpProcessor.Utils;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
namespace LootDumpProcessor.Process.Processor.DumpProcessor;
|
|
||||||
|
|
||||||
public class MultithreadSteppedDumpProcessor(
|
public class MultithreadSteppedDumpProcessor(
|
||||||
IStaticLootProcessor staticLootProcessor, IStaticContainersProcessor staticContainersProcessor,
|
IStaticLootProcessor staticLootProcessor,
|
||||||
IAmmoProcessor ammoProcessor, ILooseLootProcessor looseLootProcessor
|
IStaticContainersProcessor staticContainersProcessor,
|
||||||
) : IDumpProcessor
|
IAmmoProcessor ammoProcessor,
|
||||||
|
ILooseLootProcessor looseLootProcessor,
|
||||||
|
ILogger<MultithreadSteppedDumpProcessor> logger
|
||||||
|
)
|
||||||
|
: IDumpProcessor
|
||||||
{
|
{
|
||||||
private readonly IStaticLootProcessor _staticLootProcessor =
|
private readonly IStaticLootProcessor _staticLootProcessor = staticLootProcessor ?? throw new ArgumentNullException(nameof(staticLootProcessor));
|
||||||
staticLootProcessor ?? throw new ArgumentNullException(nameof(staticLootProcessor));
|
private readonly IStaticContainersProcessor _staticContainersProcessor = staticContainersProcessor ?? throw new ArgumentNullException(nameof(staticContainersProcessor));
|
||||||
|
private readonly IAmmoProcessor _ammoProcessor = ammoProcessor ?? throw new ArgumentNullException(nameof(ammoProcessor));
|
||||||
private readonly IStaticContainersProcessor _staticContainersProcessor =
|
private readonly ILooseLootProcessor _looseLootProcessor = looseLootProcessor ?? throw new ArgumentNullException(nameof(looseLootProcessor));
|
||||||
staticContainersProcessor ?? throw new ArgumentNullException(nameof(staticContainersProcessor));
|
private readonly ILogger<MultithreadSteppedDumpProcessor> _logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
|
|
||||||
private readonly IAmmoProcessor _ammoProcessor =
|
|
||||||
ammoProcessor ?? throw new ArgumentNullException(nameof(ammoProcessor));
|
|
||||||
|
|
||||||
private readonly ILooseLootProcessor _looseLootProcessor =
|
|
||||||
looseLootProcessor ?? throw new ArgumentNullException(nameof(looseLootProcessor));
|
|
||||||
|
|
||||||
private static readonly IJsonSerializer _jsonSerializer = JsonSerializerFactory.GetInstance();
|
private static readonly IJsonSerializer _jsonSerializer = JsonSerializerFactory.GetInstance();
|
||||||
|
|
||||||
// if we need to, this variable can be moved to use the factory, but since the factory
|
|
||||||
// needs a locking mechanism to prevent dictionary access exceptions, its better to keep
|
|
||||||
// a reference to use here
|
|
||||||
private static readonly IDataStorage _dataStorage = DataStorageFactory.GetInstance();
|
private static readonly IDataStorage _dataStorage = DataStorageFactory.GetInstance();
|
||||||
|
|
||||||
public Dictionary<OutputFileType, object> ProcessDumps(List<PartialData> dumps)
|
public Dictionary<OutputFileType, object> ProcessDumps(List<PartialData> dumps)
|
||||||
{
|
{
|
||||||
if (LoggerFactory.GetInstance().CanBeLogged(LogLevel.Info))
|
_logger.LogInformation("Starting final dump processing");
|
||||||
LoggerFactory.GetInstance().Log("Starting final dump processing", LogLevel.Info);
|
|
||||||
var output = new Dictionary<OutputFileType, object>();
|
var output = new Dictionary<OutputFileType, object>();
|
||||||
|
|
||||||
var dumpProcessData = GetDumpProcessData(dumps);
|
var dumpProcessData = GetDumpProcessData(dumps);
|
||||||
if (LoggerFactory.GetInstance().CanBeLogged(LogLevel.Info))
|
_logger.LogInformation("Heavy processing done!");
|
||||||
LoggerFactory.GetInstance().Log("Heavy processing done!", LogLevel.Info);
|
|
||||||
|
|
||||||
var staticContainers = new ConcurrentDictionary<string, MapStaticLoot>();
|
var staticContainers = new ConcurrentDictionary<string, MapStaticLoot>();
|
||||||
// We need to count how many dumps we have for each map
|
|
||||||
var mapDumpCounter = new ConcurrentDictionary<string, int>();
|
var mapDumpCounter = new ConcurrentDictionary<string, int>();
|
||||||
// dictionary of maps, that has a dictionary of template and hit count
|
|
||||||
var mapStaticContainersAggregated = new ConcurrentDictionary<string, ConcurrentDictionary<Template, int>>();
|
var mapStaticContainersAggregated = new ConcurrentDictionary<string, ConcurrentDictionary<Template, int>>();
|
||||||
|
|
||||||
// BSG changed the map data so static containers are now dynamic, so we need to scan all dumps for the static containers.
|
_logger.LogInformation("Queuing dumps for static data processing");
|
||||||
if (LoggerFactory.GetInstance().CanBeLogged(LogLevel.Info))
|
|
||||||
LoggerFactory.GetInstance().Log("Queuing dumps for static data processing", LogLevel.Info);
|
|
||||||
|
|
||||||
var parallelOptions = new ParallelOptions
|
var parallelOptions = new ParallelOptions
|
||||||
{
|
{
|
||||||
@ -69,26 +58,23 @@ public class MultithreadSteppedDumpProcessor(
|
|||||||
async (partialData, cancellationToken) =>
|
async (partialData, cancellationToken) =>
|
||||||
await Process(partialData, staticContainers, mapStaticContainersAggregated, mapDumpCounter));
|
await Process(partialData, staticContainers, mapStaticContainersAggregated, mapDumpCounter));
|
||||||
|
|
||||||
if (LoggerFactory.GetInstance().CanBeLogged(LogLevel.Info))
|
_logger.LogInformation("All static data processing threads finished");
|
||||||
LoggerFactory.GetInstance().Log("All static data processing threads finished", LogLevel.Info);
|
|
||||||
// Aggregate and calculate the probability of a static container
|
|
||||||
mapStaticContainersAggregated.ToDictionary(
|
mapStaticContainersAggregated.ToDictionary(
|
||||||
kv => kv.Key,
|
kv => kv.Key,
|
||||||
kv => kv.Value.Select(
|
kv => kv.Value.Select(
|
||||||
td => new StaticDataPoint
|
td => new StaticDataPoint
|
||||||
{
|
{
|
||||||
Template = td.Key,
|
Template = td.Key,
|
||||||
Probability = GetStaticContainerProbability(kv.Key, td, mapDumpCounter) // kv.Key = map name
|
Probability = GetStaticContainerProbability(kv.Key, td, mapDumpCounter)
|
||||||
}
|
}
|
||||||
).ToList()
|
).ToList()
|
||||||
).ToList()
|
).ToList()
|
||||||
.ForEach(kv =>
|
.ForEach(kv =>
|
||||||
staticContainers[kv.Key].StaticContainers = kv.Value); // Hydrate staticContainers.StaticContainers
|
staticContainers[kv.Key].StaticContainers = kv.Value);
|
||||||
|
|
||||||
// Static containers
|
|
||||||
output.Add(OutputFileType.StaticContainer, staticContainers);
|
output.Add(OutputFileType.StaticContainer, staticContainers);
|
||||||
if (LoggerFactory.GetInstance().CanBeLogged(LogLevel.Info))
|
_logger.LogInformation("Processing ammo distribution");
|
||||||
LoggerFactory.GetInstance().Log("Processing ammo distribution", LogLevel.Info);
|
|
||||||
|
|
||||||
var staticAmmo = new ConcurrentDictionary<string, IReadOnlyDictionary<string, List<AmmoDistribution>>>();
|
var staticAmmo = new ConcurrentDictionary<string, IReadOnlyDictionary<string, List<AmmoDistribution>>>();
|
||||||
Parallel.ForEach(dumpProcessData.ContainerCounts.Keys, parallelOptions: parallelOptions, mapId =>
|
Parallel.ForEach(dumpProcessData.ContainerCounts.Keys, parallelOptions: parallelOptions, mapId =>
|
||||||
@ -97,14 +83,9 @@ public class MultithreadSteppedDumpProcessor(
|
|||||||
var ammoDistribution = _ammoProcessor.CreateAmmoDistribution(mapId, preProcessedStaticLoots);
|
var ammoDistribution = _ammoProcessor.CreateAmmoDistribution(mapId, preProcessedStaticLoots);
|
||||||
staticAmmo[mapId] = ammoDistribution;
|
staticAmmo[mapId] = ammoDistribution;
|
||||||
});
|
});
|
||||||
// Ammo distribution
|
output.Add(OutputFileType.StaticAmmo, staticAmmo);
|
||||||
output.Add(
|
|
||||||
OutputFileType.StaticAmmo,
|
|
||||||
staticAmmo
|
|
||||||
);
|
|
||||||
|
|
||||||
if (LoggerFactory.GetInstance().CanBeLogged(LogLevel.Info))
|
_logger.LogInformation("Processing static loot distribution");
|
||||||
LoggerFactory.GetInstance().Log("Processing static loot distribution", LogLevel.Info);
|
|
||||||
|
|
||||||
var staticLoot = new ConcurrentDictionary<string, IReadOnlyDictionary<string, StaticItemDistribution>>();
|
var staticLoot = new ConcurrentDictionary<string, IReadOnlyDictionary<string, StaticItemDistribution>>();
|
||||||
Parallel.ForEach(dumpProcessData.ContainerCounts.Keys, parallelOptions: parallelOptions, mapId =>
|
Parallel.ForEach(dumpProcessData.ContainerCounts.Keys, parallelOptions: parallelOptions, mapId =>
|
||||||
@ -114,16 +95,10 @@ public class MultithreadSteppedDumpProcessor(
|
|||||||
_staticLootProcessor.CreateStaticLootDistribution(mapId, preProcessedStaticLoots);
|
_staticLootProcessor.CreateStaticLootDistribution(mapId, preProcessedStaticLoots);
|
||||||
staticLoot[mapId] = staticLootDistribution;
|
staticLoot[mapId] = staticLootDistribution;
|
||||||
});
|
});
|
||||||
// Static loot distribution
|
output.Add(OutputFileType.StaticLoot, staticLoot);
|
||||||
output.Add(
|
|
||||||
OutputFileType.StaticLoot,
|
|
||||||
staticLoot
|
|
||||||
);
|
|
||||||
|
|
||||||
if (LoggerFactory.GetInstance().CanBeLogged(LogLevel.Info))
|
_logger.LogInformation("Processing loose loot distribution");
|
||||||
LoggerFactory.GetInstance().Log("Processing loose loot distribution", LogLevel.Info);
|
|
||||||
|
|
||||||
// Loose loot distribution
|
|
||||||
var looseLoot = new ConcurrentDictionary<string, LooseLootRoot>();
|
var looseLoot = new ConcurrentDictionary<string, LooseLootRoot>();
|
||||||
Parallel.ForEach(dumpProcessData.MapCounts.Keys, parallelOptions: parallelOptions, mapId =>
|
Parallel.ForEach(dumpProcessData.MapCounts.Keys, parallelOptions: parallelOptions, mapId =>
|
||||||
{
|
{
|
||||||
@ -133,15 +108,13 @@ public class MultithreadSteppedDumpProcessor(
|
|||||||
_looseLootProcessor.CreateLooseLootDistribution(mapId, mapCount, looseLootCount);
|
_looseLootProcessor.CreateLooseLootDistribution(mapId, mapCount, looseLootCount);
|
||||||
looseLoot[mapId] = looseLootDistribution;
|
looseLoot[mapId] = looseLootDistribution;
|
||||||
});
|
});
|
||||||
if (LoggerFactory.GetInstance().CanBeLogged(LogLevel.Info))
|
_logger.LogInformation("Collecting loose loot distribution information");
|
||||||
LoggerFactory.GetInstance().Log("Collecting loose loot distribution information", LogLevel.Info);
|
|
||||||
var loot = dumpProcessData.MapCounts
|
var loot = dumpProcessData.MapCounts
|
||||||
.Select(mapCount => mapCount.Key)
|
.Select(mapCount => mapCount.Key)
|
||||||
.ToDictionary(mi => mi, mi => looseLoot[mi]);
|
.ToDictionary(mi => mi, mi => looseLoot[mi]);
|
||||||
|
|
||||||
output.Add(OutputFileType.LooseLoot, loot);
|
output.Add(OutputFileType.LooseLoot, loot);
|
||||||
if (LoggerFactory.GetInstance().CanBeLogged(LogLevel.Info))
|
_logger.LogInformation("Dump processing fully completed!");
|
||||||
LoggerFactory.GetInstance().Log("Dump processing fully completed!", LogLevel.Info);
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,32 +123,22 @@ public class MultithreadSteppedDumpProcessor(
|
|||||||
ConcurrentDictionary<string, ConcurrentDictionary<Template, int>> mapStaticContainersAggregated,
|
ConcurrentDictionary<string, ConcurrentDictionary<Template, int>> mapStaticContainersAggregated,
|
||||||
ConcurrentDictionary<string, int> mapDumpCounter)
|
ConcurrentDictionary<string, int> mapDumpCounter)
|
||||||
{
|
{
|
||||||
if (LoggerFactory.GetInstance().CanBeLogged(LogLevel.Debug))
|
_logger.LogDebug("Processing static data for file {FileName}", partialData.BasicInfo.FileName);
|
||||||
LoggerFactory.GetInstance().Log($"Processing static data for file {partialData.BasicInfo.FileName}",
|
|
||||||
LogLevel.Debug);
|
|
||||||
|
|
||||||
var fileContent = await File.ReadAllTextAsync(partialData.BasicInfo.FileName);
|
var fileContent = await File.ReadAllTextAsync(partialData.BasicInfo.FileName);
|
||||||
var dataDump = _jsonSerializer.Deserialize<RootData>(fileContent);
|
var dataDump = _jsonSerializer.Deserialize<RootData>(fileContent);
|
||||||
|
|
||||||
if (dataDump == null)
|
if (dataDump == null)
|
||||||
{
|
{
|
||||||
if (LoggerFactory.GetInstance().CanBeLogged(LogLevel.Error))
|
_logger.LogError("Failed to deserialize data from file {FileName}", partialData.BasicInfo.FileName);
|
||||||
LoggerFactory.GetInstance()
|
|
||||||
.Log($"Failed to deserialize data from file {partialData.BasicInfo.FileName}",
|
|
||||||
LogLevel.Error);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var mapId = dataDump.Data.LocationLoot.Id.ToLower();
|
var mapId = dataDump.Data.LocationLoot.Id.ToLower();
|
||||||
|
|
||||||
// Because we may use multiple version dumps for map data, merge the static loot between dumps
|
|
||||||
|
|
||||||
if (!staticContainers.TryGetValue(mapId, out var mapStaticLoot))
|
if (!staticContainers.TryGetValue(mapId, out var mapStaticLoot))
|
||||||
{
|
{
|
||||||
if (LoggerFactory.GetInstance().CanBeLogged(LogLevel.Info))
|
_logger.LogInformation("Doing first time process for map {MapId} of real static data", mapId);
|
||||||
LoggerFactory.GetInstance()
|
|
||||||
.Log($"Doing first time process for map {mapId} of real static data",
|
|
||||||
LogLevel.Info);
|
|
||||||
|
|
||||||
staticContainers[mapId] = new MapStaticLoot
|
staticContainers[mapId] = new MapStaticLoot
|
||||||
{
|
{
|
||||||
@ -197,29 +160,19 @@ public class MultithreadSteppedDumpProcessor(
|
|||||||
mapStaticLoot.StaticForced.AddRange(newStaticForced);
|
mapStaticLoot.StaticForced.AddRange(newStaticForced);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!mapStaticContainersAggregated.TryGetValue(mapId, out ConcurrentDictionary<Template, int> mapAggregatedDataDict))
|
||||||
// Takes care of finding how many "dynamic static containers" we have on the map
|
|
||||||
ConcurrentDictionary<Template, int> mapAggregatedDataDict;
|
|
||||||
|
|
||||||
// Init dict if map key doesnt exist
|
|
||||||
if (!mapStaticContainersAggregated.TryGetValue(mapId, out mapAggregatedDataDict))
|
|
||||||
{
|
{
|
||||||
mapAggregatedDataDict = new ConcurrentDictionary<Template, int>();
|
mapAggregatedDataDict = new ConcurrentDictionary<Template, int>();
|
||||||
mapStaticContainersAggregated.TryAdd(mapId, mapAggregatedDataDict);
|
mapStaticContainersAggregated.TryAdd(mapId, mapAggregatedDataDict);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Only process the dump file if the date is higher (after) the configuration date
|
|
||||||
if (!DumpWasMadeAfterConfigThresholdDate(partialData))
|
if (!DumpWasMadeAfterConfigThresholdDate(partialData))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Keep track of how many dumps we have for each map
|
|
||||||
|
|
||||||
IncrementMapCounterDictionaryValue(mapDumpCounter, mapId);
|
IncrementMapCounterDictionaryValue(mapDumpCounter, mapId);
|
||||||
|
|
||||||
|
|
||||||
var containerIgnoreListExists = LootDumpProcessorContext.GetConfig().ContainerIgnoreList
|
var containerIgnoreListExists = LootDumpProcessorContext.GetConfig().ContainerIgnoreList
|
||||||
.TryGetValue(mapId, out var ignoreListForMap);
|
.TryGetValue(mapId, out var ignoreListForMap);
|
||||||
foreach (var dynamicStaticContainer in _staticContainersProcessor.CreateDynamicStaticContainers(
|
foreach (var dynamicStaticContainer in _staticContainersProcessor.CreateDynamicStaticContainers(
|
||||||
@ -227,11 +180,9 @@ public class MultithreadSteppedDumpProcessor(
|
|||||||
{
|
{
|
||||||
if (containerIgnoreListExists && ignoreListForMap.Contains(dynamicStaticContainer.Id))
|
if (containerIgnoreListExists && ignoreListForMap.Contains(dynamicStaticContainer.Id))
|
||||||
{
|
{
|
||||||
// Skip adding containers to aggregated data if container id is in ignore list
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Increment times container seen in dump by 1
|
|
||||||
if (!mapAggregatedDataDict.TryAdd(dynamicStaticContainer, 1))
|
if (!mapAggregatedDataDict.TryAdd(dynamicStaticContainer, 1))
|
||||||
{
|
{
|
||||||
mapAggregatedDataDict[dynamicStaticContainer] += 1;
|
mapAggregatedDataDict[dynamicStaticContainer] += 1;
|
||||||
@ -253,7 +204,6 @@ public class MultithreadSteppedDumpProcessor(
|
|||||||
{
|
{
|
||||||
if (!mapDumpCounter.TryAdd(mapName, 1))
|
if (!mapDumpCounter.TryAdd(mapName, 1))
|
||||||
{
|
{
|
||||||
// Dict has map, increment count by 1
|
|
||||||
mapDumpCounter[mapName] += 1;
|
mapDumpCounter[mapName] += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -264,7 +214,7 @@ public class MultithreadSteppedDumpProcessor(
|
|||||||
return Math.Round((double)((decimal)td.Value / (decimal)mapDumpCounter[mapName]), 2);
|
return Math.Round((double)((decimal)td.Value / (decimal)mapDumpCounter[mapName]), 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static DumpProcessData GetDumpProcessData(List<PartialData> dumps)
|
private DumpProcessData GetDumpProcessData(List<PartialData> dumps)
|
||||||
{
|
{
|
||||||
var dumpProcessData = new DumpProcessData();
|
var dumpProcessData = new DumpProcessData();
|
||||||
|
|
||||||
@ -274,15 +224,10 @@ public class MultithreadSteppedDumpProcessor(
|
|||||||
{
|
{
|
||||||
var mapName = tuple.Key;
|
var mapName = tuple.Key;
|
||||||
var partialFileMetaData = tuple.ToList();
|
var partialFileMetaData = tuple.ToList();
|
||||||
if (LoggerFactory.GetInstance().CanBeLogged(LogLevel.Info))
|
_logger.LogInformation("Processing map {MapName}, total dump data to process: {Count}", mapName, partialFileMetaData.Count);
|
||||||
LoggerFactory.GetInstance().Log(
|
|
||||||
$"Processing map {mapName}, total dump data to process: {partialFileMetaData.Count}",
|
|
||||||
LogLevel.Info
|
|
||||||
);
|
|
||||||
dumpProcessData.MapCounts[mapName] = partialFileMetaData.Count;
|
dumpProcessData.MapCounts[mapName] = partialFileMetaData.Count;
|
||||||
|
|
||||||
var lockObjectContainerCounts = new object();
|
var lockObjectContainerCounts = new object();
|
||||||
|
|
||||||
var lockObjectCounts = new object();
|
var lockObjectCounts = new object();
|
||||||
var looseLootCounts = new LooseLootCounts();
|
var looseLootCounts = new LooseLootCounts();
|
||||||
|
|
||||||
@ -290,11 +235,6 @@ public class MultithreadSteppedDumpProcessor(
|
|||||||
var dictionaryCounts = new FlatKeyableDictionary<string, int>();
|
var dictionaryCounts = new FlatKeyableDictionary<string, int>();
|
||||||
looseLootCounts.Counts = dictionaryCounts.GetKey();
|
looseLootCounts.Counts = dictionaryCounts.GetKey();
|
||||||
|
|
||||||
/*
|
|
||||||
var dictionaryItemCounts = new FlatKeyableDictionary<string, List<string>>();
|
|
||||||
counts.Items = dictionaryItemCounts.GetKey();
|
|
||||||
*/
|
|
||||||
|
|
||||||
var lockObjectDictionaryItemProperties = new object();
|
var lockObjectDictionaryItemProperties = new object();
|
||||||
var dictionaryItemProperties = new FlatKeyableDictionary<string, FlatKeyableList<Template>>();
|
var dictionaryItemProperties = new FlatKeyableDictionary<string, FlatKeyableList<Template>>();
|
||||||
|
|
||||||
@ -305,13 +245,11 @@ public class MultithreadSteppedDumpProcessor(
|
|||||||
|
|
||||||
BlockingCollection<PartialData> _partialDataToProcess = new();
|
BlockingCollection<PartialData> _partialDataToProcess = new();
|
||||||
|
|
||||||
// add the items to the queue
|
|
||||||
foreach (var partialData in partialFileMetaData)
|
foreach (var partialData in partialFileMetaData)
|
||||||
{
|
{
|
||||||
_partialDataToProcess.Add(partialData);
|
_partialDataToProcess.Add(partialData);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call GC before running threads
|
|
||||||
partialFileMetaData = null;
|
partialFileMetaData = null;
|
||||||
tuple = null;
|
tuple = null;
|
||||||
GCHandler.Collect();
|
GCHandler.Collect();
|
||||||
@ -337,11 +275,7 @@ public class MultithreadSteppedDumpProcessor(
|
|||||||
_dataStorage.Store(dictionaryCounts);
|
_dataStorage.Store(dictionaryCounts);
|
||||||
dictionaryCounts = null;
|
dictionaryCounts = null;
|
||||||
GCHandler.Collect();
|
GCHandler.Collect();
|
||||||
/*
|
|
||||||
DataStorageFactory.GetInstance().Store(dictionaryItemCounts);
|
|
||||||
dictionaryItemCounts = null;
|
|
||||||
GC.Collect();
|
|
||||||
*/
|
|
||||||
_dataStorage.Store(actualDictionaryItemProperties);
|
_dataStorage.Store(actualDictionaryItemProperties);
|
||||||
actualDictionaryItemProperties = null;
|
actualDictionaryItemProperties = null;
|
||||||
GCHandler.Collect();
|
GCHandler.Collect();
|
||||||
@ -352,7 +286,7 @@ public class MultithreadSteppedDumpProcessor(
|
|||||||
return dumpProcessData;
|
return dumpProcessData;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void ProcessPartialData(PartialData partialDataToProcess,
|
private void ProcessPartialData(PartialData partialDataToProcess,
|
||||||
object lockObjectContainerCounts,
|
object lockObjectContainerCounts,
|
||||||
DumpProcessData dumpProcessData, string mapName, object lockObjectDictionaryCounts,
|
DumpProcessData dumpProcessData, string mapName, object lockObjectDictionaryCounts,
|
||||||
FlatKeyableDictionary<string, int>? dictionaryCounts, object lockObjectDictionaryItemProperties,
|
FlatKeyableDictionary<string, int>? dictionaryCounts, object lockObjectDictionaryItemProperties,
|
||||||
@ -364,7 +298,6 @@ public class MultithreadSteppedDumpProcessor(
|
|||||||
{
|
{
|
||||||
var dumpData = _dataStorage.GetItem<ParsedDump>(partialDataToProcess.ParsedDumpKey);
|
var dumpData = _dataStorage.GetItem<ParsedDump>(partialDataToProcess.ParsedDumpKey);
|
||||||
|
|
||||||
// Static containers
|
|
||||||
lock (lockObjectContainerCounts)
|
lock (lockObjectContainerCounts)
|
||||||
{
|
{
|
||||||
if (!dumpProcessData.ContainerCounts.ContainsKey(mapName))
|
if (!dumpProcessData.ContainerCounts.ContainsKey(mapName))
|
||||||
@ -378,7 +311,6 @@ public class MultithreadSteppedDumpProcessor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loose loot into ids on files
|
|
||||||
var loadedDictionary =
|
var loadedDictionary =
|
||||||
_dataStorage
|
_dataStorage
|
||||||
.GetItem<SubdivisionedKeyableDictionary<string, List<Template>>>(
|
.GetItem<SubdivisionedKeyableDictionary<string, List<Template>>>(
|
||||||
@ -416,11 +348,7 @@ public class MultithreadSteppedDumpProcessor(
|
|||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
if (LoggerFactory.GetInstance().CanBeLogged(LogLevel.Error))
|
_logger.LogError("ERROR OCCURRED: {Message}\n{StackTrace}", e.Message, e.StackTrace);
|
||||||
LoggerFactory.GetInstance().Log(
|
|
||||||
$"ERROR OCCURRED:{e.Message}\n{e.StackTrace}",
|
|
||||||
LogLevel.Error
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,35 +1,41 @@
|
|||||||
using LootDumpProcessor.Logger;
|
|
||||||
using LootDumpProcessor.Model;
|
using LootDumpProcessor.Model;
|
||||||
using LootDumpProcessor.Model.Processing;
|
using LootDumpProcessor.Model.Processing;
|
||||||
|
using LootDumpProcessor.Process.Processor.FileProcessor;
|
||||||
using LootDumpProcessor.Process.Processor.v2.LooseLootProcessor;
|
using LootDumpProcessor.Process.Processor.v2.LooseLootProcessor;
|
||||||
using LootDumpProcessor.Process.Processor.v2.StaticLootProcessor;
|
using LootDumpProcessor.Process.Processor.v2.StaticLootProcessor;
|
||||||
using LootDumpProcessor.Storage;
|
using LootDumpProcessor.Storage;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace LootDumpProcessor.Process.Processor.FileProcessor;
|
public class FileProcessor : IFileProcessor
|
||||||
|
|
||||||
public class FileProcessor(IStaticLootProcessor staticLootProcessor, ILooseLootProcessor looseLootProcessor)
|
|
||||||
: IFileProcessor
|
|
||||||
{
|
{
|
||||||
private readonly IStaticLootProcessor _staticLootProcessor =
|
private readonly IStaticLootProcessor _staticLootProcessor;
|
||||||
staticLootProcessor ?? throw new ArgumentNullException(nameof(staticLootProcessor));
|
private readonly ILooseLootProcessor _looseLootProcessor;
|
||||||
|
private readonly ILogger<FileProcessor> _logger;
|
||||||
|
|
||||||
private readonly ILooseLootProcessor _looseLootProcessor =
|
public FileProcessor(
|
||||||
looseLootProcessor ?? throw new ArgumentNullException(nameof(looseLootProcessor));
|
IStaticLootProcessor staticLootProcessor,
|
||||||
|
ILooseLootProcessor looseLootProcessor,
|
||||||
|
ILogger<FileProcessor> logger)
|
||||||
|
{
|
||||||
|
_staticLootProcessor = staticLootProcessor
|
||||||
|
?? throw new ArgumentNullException(nameof(staticLootProcessor));
|
||||||
|
_looseLootProcessor = looseLootProcessor
|
||||||
|
?? throw new ArgumentNullException(nameof(looseLootProcessor));
|
||||||
|
_logger = logger
|
||||||
|
?? throw new ArgumentNullException(nameof(logger));
|
||||||
|
}
|
||||||
|
|
||||||
public PartialData Process(BasicInfo parsedData)
|
public PartialData Process(BasicInfo parsedData)
|
||||||
{
|
{
|
||||||
if (LoggerFactory.GetInstance().CanBeLogged(LogLevel.Debug))
|
_logger.LogDebug("Processing file {FileName}...", parsedData.FileName);
|
||||||
LoggerFactory.GetInstance().Log($"Processing file {parsedData.FileName}...", LogLevel.Debug);
|
|
||||||
|
|
||||||
List<Template> looseLoot = new List<Template>();
|
var looseLoot = new List<Template>();
|
||||||
List<Template> staticLoot = new List<Template>();
|
var staticLoot = new List<Template>();
|
||||||
|
|
||||||
foreach (var item in parsedData.Data.Data.LocationLoot.Loot)
|
foreach (var item in parsedData.Data.Data.LocationLoot.Loot)
|
||||||
{
|
{
|
||||||
if (item.IsContainer ?? false)
|
if (item.IsContainer ?? false) staticLoot.Add(item);
|
||||||
staticLoot.Add(item);
|
else looseLoot.Add(item);
|
||||||
else
|
|
||||||
looseLoot.Add(item);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
parsedData.Data = null;
|
parsedData.Data = null;
|
||||||
@ -47,18 +53,17 @@ public class FileProcessor(IStaticLootProcessor staticLootProcessor, ILooseLootP
|
|||||||
|
|
||||||
if (!DataStorageFactory.GetInstance().Exists(dumpData.GetKey()))
|
if (!DataStorageFactory.GetInstance().Exists(dumpData.GetKey()))
|
||||||
{
|
{
|
||||||
if (LoggerFactory.GetInstance().CanBeLogged(LogLevel.Debug))
|
_logger.LogDebug(
|
||||||
LoggerFactory.GetInstance().Log(
|
"Cache not found for {LookupIndex} processing.",
|
||||||
$"Cached not found for {string.Join("/", dumpData.GetKey().GetLookupIndex())} processing.",
|
string.Join("/", dumpData.GetKey().GetLookupIndex())
|
||||||
LogLevel.Debug
|
);
|
||||||
);
|
|
||||||
dumpData.Containers = _staticLootProcessor.PreProcessStaticLoot(staticLoot);
|
dumpData.Containers = _staticLootProcessor.PreProcessStaticLoot(staticLoot);
|
||||||
dumpData.LooseLoot = _looseLootProcessor.PreProcessLooseLoot(looseLoot);
|
dumpData.LooseLoot = _looseLootProcessor.PreProcessLooseLoot(looseLoot);
|
||||||
DataStorageFactory.GetInstance().Store(dumpData);
|
DataStorageFactory.GetInstance().Store(dumpData);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (LoggerFactory.GetInstance().CanBeLogged(LogLevel.Debug))
|
_logger.LogDebug("File {FileName} finished processing!", parsedData.FileName);
|
||||||
LoggerFactory.GetInstance().Log($"File {parsedData.FileName} finished processing!", LogLevel.Debug);
|
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -4,17 +4,20 @@ using Microsoft.Extensions.Logging;
|
|||||||
|
|
||||||
namespace LootDumpProcessor.Process.Processor.v2.AmmoProcessor;
|
namespace LootDumpProcessor.Process.Processor.v2.AmmoProcessor;
|
||||||
|
|
||||||
public class AmmoProcessor(ILogger<AmmoProcessor> logger) : IAmmoProcessor
|
public class AmmoProcessor(ILogger<AmmoProcessor> logger, ITarkovItemsProvider tarkovItemsProvider) : IAmmoProcessor
|
||||||
{
|
{
|
||||||
private readonly ILogger<AmmoProcessor> _logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
private readonly ILogger<AmmoProcessor> _logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
|
|
||||||
|
private readonly ITarkovItemsProvider _tarkovItemsProvider =
|
||||||
|
tarkovItemsProvider ?? throw new ArgumentNullException(nameof(tarkovItemsProvider));
|
||||||
|
|
||||||
public IReadOnlyDictionary<string, List<AmmoDistribution>> CreateAmmoDistribution(
|
public IReadOnlyDictionary<string, List<AmmoDistribution>> CreateAmmoDistribution(
|
||||||
string mapId,
|
string mapId,
|
||||||
List<PreProcessedStaticLoot> containers)
|
List<PreProcessedStaticLoot> containers)
|
||||||
{
|
{
|
||||||
var ammoTemplates = containers
|
var ammoTemplates = containers
|
||||||
.SelectMany(container => container.Items)
|
.SelectMany(container => container.Items)
|
||||||
.Where(item => LootDumpProcessorContext.GetTarkovItems().IsBaseClass(item.Tpl, BaseClasses.Ammo))
|
.Where(item => _tarkovItemsProvider.IsBaseClass(item.Tpl, BaseClasses.Ammo))
|
||||||
.Select(item => item.Tpl)
|
.Select(item => item.Tpl)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
@ -22,7 +25,7 @@ public class AmmoProcessor(ILogger<AmmoProcessor> logger) : IAmmoProcessor
|
|||||||
.GroupBy(tpl => tpl)
|
.GroupBy(tpl => tpl)
|
||||||
.Select(group => new CaliberTemplateCount
|
.Select(group => new CaliberTemplateCount
|
||||||
{
|
{
|
||||||
Caliber = LootDumpProcessorContext.GetTarkovItems().AmmoCaliber(group.Key),
|
Caliber = _tarkovItemsProvider.AmmoCaliber(group.Key),
|
||||||
Template = group.Key,
|
Template = group.Key,
|
||||||
Count = group.Count()
|
Count = group.Count()
|
||||||
})
|
})
|
||||||
|
@ -9,11 +9,23 @@ using Microsoft.Extensions.Logging;
|
|||||||
|
|
||||||
namespace LootDumpProcessor.Process.Processor.v2.LooseLootProcessor
|
namespace LootDumpProcessor.Process.Processor.v2.LooseLootProcessor
|
||||||
{
|
{
|
||||||
public class LooseLootProcessor(ILogger<LooseLootProcessor> logger, IDataStorage dataStorage)
|
public class LooseLootProcessor(
|
||||||
|
ILogger<LooseLootProcessor> logger, IDataStorage dataStorage, ITarkovItemsProvider tarkovItemsProvider,
|
||||||
|
IComposedKeyGenerator composedKeyGenerator
|
||||||
|
)
|
||||||
: ILooseLootProcessor
|
: ILooseLootProcessor
|
||||||
{
|
{
|
||||||
private readonly ILogger<LooseLootProcessor> _logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
private readonly ILogger<LooseLootProcessor>
|
||||||
private readonly IDataStorage _dataStorage = dataStorage ?? throw new ArgumentNullException(nameof(dataStorage));
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
|
|
||||||
|
private readonly IDataStorage
|
||||||
|
_dataStorage = dataStorage ?? throw new ArgumentNullException(nameof(dataStorage));
|
||||||
|
|
||||||
|
private readonly ITarkovItemsProvider _tarkovItemsProvider =
|
||||||
|
tarkovItemsProvider ?? throw new ArgumentNullException(nameof(tarkovItemsProvider));
|
||||||
|
|
||||||
|
private readonly IComposedKeyGenerator _composedKeyGenerator =
|
||||||
|
composedKeyGenerator ?? throw new ArgumentNullException(nameof(composedKeyGenerator));
|
||||||
|
|
||||||
public PreProcessedLooseLoot PreProcessLooseLoot(List<Template> looseLoot)
|
public PreProcessedLooseLoot PreProcessLooseLoot(List<Template> looseLoot)
|
||||||
{
|
{
|
||||||
@ -35,9 +47,10 @@ namespace LootDumpProcessor.Process.Processor.v2.LooseLootProcessor
|
|||||||
if (!uniqueLootIds.ContainsKey(sanitizedId))
|
if (!uniqueLootIds.ContainsKey(sanitizedId))
|
||||||
{
|
{
|
||||||
uniqueLootIds[sanitizedId] = template.Id;
|
uniqueLootIds[sanitizedId] = template.Id;
|
||||||
preProcessedLoot.Counts[sanitizedId] = preProcessedLoot.Counts.TryGetValue(sanitizedId, out var count)
|
preProcessedLoot.Counts[sanitizedId] =
|
||||||
? count + 1
|
preProcessedLoot.Counts.TryGetValue(sanitizedId, out var count)
|
||||||
: 1;
|
? count + 1
|
||||||
|
: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!itemPropertiesDictionary.TryGetValue(sanitizedId, out var templates))
|
if (!itemPropertiesDictionary.TryGetValue(sanitizedId, out var templates))
|
||||||
@ -84,12 +97,14 @@ namespace LootDumpProcessor.Process.Processor.v2.LooseLootProcessor
|
|||||||
looseLootDistribution.SpawnPointCount = new SpawnPointCount
|
looseLootDistribution.SpawnPointCount = new SpawnPointCount
|
||||||
{
|
{
|
||||||
Mean = CalculateMean(looseLootCountsItem.MapSpawnpointCount.Select(Convert.ToDouble).ToList()),
|
Mean = CalculateMean(looseLootCountsItem.MapSpawnpointCount.Select(Convert.ToDouble).ToList()),
|
||||||
Std = CalculateStandardDeviation(looseLootCountsItem.MapSpawnpointCount.Select(Convert.ToDouble).ToList())
|
Std = CalculateStandardDeviation(looseLootCountsItem.MapSpawnpointCount.Select(Convert.ToDouble)
|
||||||
|
.ToList())
|
||||||
};
|
};
|
||||||
looseLootDistribution.SpawnPointsForced = new List<SpawnPointsForced>();
|
looseLootDistribution.SpawnPointsForced = new List<SpawnPointsForced>();
|
||||||
looseLootDistribution.SpawnPoints = new List<SpawnPoint>();
|
looseLootDistribution.SpawnPoints = new List<SpawnPoint>();
|
||||||
|
|
||||||
var itemProperties = _dataStorage.GetItem<FlatKeyableDictionary<string, IKey>>(looseLootCountsItem.ItemProperties);
|
var itemProperties =
|
||||||
|
_dataStorage.GetItem<FlatKeyableDictionary<string, IKey>>(looseLootCountsItem.ItemProperties);
|
||||||
foreach (var (spawnPoint, itemListKey) in itemProperties)
|
foreach (var (spawnPoint, itemListKey) in itemProperties)
|
||||||
{
|
{
|
||||||
var itemCounts = new Dictionary<ComposedKey, int>();
|
var itemCounts = new Dictionary<ComposedKey, int>();
|
||||||
@ -97,7 +112,7 @@ namespace LootDumpProcessor.Process.Processor.v2.LooseLootProcessor
|
|||||||
|
|
||||||
foreach (var template in savedTemplates)
|
foreach (var template in savedTemplates)
|
||||||
{
|
{
|
||||||
var composedKey = new ComposedKey(template);
|
var composedKey = _composedKeyGenerator.Generate(template.Items);
|
||||||
if (!itemCounts.TryAdd(composedKey, 1))
|
if (!itemCounts.TryAdd(composedKey, 1))
|
||||||
itemCounts[composedKey]++;
|
itemCounts[composedKey]++;
|
||||||
}
|
}
|
||||||
@ -122,8 +137,9 @@ namespace LootDumpProcessor.Process.Processor.v2.LooseLootProcessor
|
|||||||
}).ToList();
|
}).ToList();
|
||||||
|
|
||||||
if (itemDistributions.Count == 1 &&
|
if (itemDistributions.Count == 1 &&
|
||||||
(LootDumpProcessorContext.GetTarkovItems().IsQuestItem(itemDistributions[0].ComposedKey?.FirstItem?.Tpl) ||
|
(_tarkovItemsProvider.IsQuestItem(itemDistributions[0].ComposedKey?.FirstItem?.Tpl) ||
|
||||||
LootDumpProcessorContext.GetForcedLooseItems()[mapId].Contains(itemDistributions[0].ComposedKey?.FirstItem?.Tpl)))
|
LootDumpProcessorContext.GetForcedLooseItems()[mapId]
|
||||||
|
.Contains(itemDistributions[0].ComposedKey?.FirstItem?.Tpl)))
|
||||||
{
|
{
|
||||||
var forcedSpawnPoint = new SpawnPointsForced
|
var forcedSpawnPoint = new SpawnPointsForced
|
||||||
{
|
{
|
||||||
@ -162,7 +178,7 @@ namespace LootDumpProcessor.Process.Processor.v2.LooseLootProcessor
|
|||||||
templateCopy.Items = new List<Item>();
|
templateCopy.Items = new List<Item>();
|
||||||
|
|
||||||
var groupedByKey = spawnPoints
|
var groupedByKey = spawnPoints
|
||||||
.GroupBy(t => new ComposedKey(t))
|
.GroupBy(t => _composedKeyGenerator.Generate(t.Items))
|
||||||
.ToDictionary(g => g.Key, g => g.ToList());
|
.ToDictionary(g => g.Key, g => g.ToList());
|
||||||
|
|
||||||
foreach (var distribution in itemDistributions)
|
foreach (var distribution in itemDistributions)
|
||||||
@ -198,8 +214,10 @@ namespace LootDumpProcessor.Process.Processor.v2.LooseLootProcessor
|
|||||||
.OrderBy(x => x.Template.Id)
|
.OrderBy(x => x.Template.Id)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
var configuredForcedTemplates = new HashSet<string>(LootDumpProcessorContext.GetForcedLooseItems()[mapId].Select(item => item));
|
var configuredForcedTemplates =
|
||||||
var foundForcedTemplates = new HashSet<string>(looseLootDistribution.SpawnPointsForced.Select(fp => fp.Template.Items[0].Tpl));
|
new HashSet<string>(LootDumpProcessorContext.GetForcedLooseItems()[mapId].Select(item => item));
|
||||||
|
var foundForcedTemplates =
|
||||||
|
new HashSet<string>(looseLootDistribution.SpawnPointsForced.Select(fp => fp.Template.Items[0].Tpl));
|
||||||
|
|
||||||
foreach (var expectedTpl in configuredForcedTemplates)
|
foreach (var expectedTpl in configuredForcedTemplates)
|
||||||
{
|
{
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using LootDumpProcessor.Logger;
|
|
||||||
using LootDumpProcessor.Model.Input;
|
using LootDumpProcessor.Model.Input;
|
||||||
using LootDumpProcessor.Process.Collector;
|
using LootDumpProcessor.Process.Collector;
|
||||||
using LootDumpProcessor.Process.Processor.DumpProcessor;
|
using LootDumpProcessor.Process.Processor.DumpProcessor;
|
||||||
@ -11,10 +10,15 @@ using LootDumpProcessor.Process.Writer;
|
|||||||
using LootDumpProcessor.Serializers.Json;
|
using LootDumpProcessor.Serializers.Json;
|
||||||
using LootDumpProcessor.Storage;
|
using LootDumpProcessor.Storage;
|
||||||
using LootDumpProcessor.Utils;
|
using LootDumpProcessor.Utils;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace LootDumpProcessor.Process;
|
namespace LootDumpProcessor.Process;
|
||||||
|
|
||||||
public class QueuePipeline(IFileProcessor fileProcessor, IDumpProcessor dumpProcessor) : IPipeline
|
public class QueuePipeline(
|
||||||
|
IFileProcessor fileProcessor, IDumpProcessor dumpProcessor, ILogger<QueuePipeline> logger,
|
||||||
|
IPreProcessReader preProcessReader, IFileFilter fileFilter, IIntakeReader intakeReader
|
||||||
|
)
|
||||||
|
: IPipeline
|
||||||
{
|
{
|
||||||
private readonly IFileProcessor _fileProcessor =
|
private readonly IFileProcessor _fileProcessor =
|
||||||
fileProcessor ?? throw new ArgumentNullException(nameof(fileProcessor));
|
fileProcessor ?? throw new ArgumentNullException(nameof(fileProcessor));
|
||||||
@ -22,24 +26,21 @@ public class QueuePipeline(IFileProcessor fileProcessor, IDumpProcessor dumpProc
|
|||||||
private readonly IDumpProcessor _dumpProcessor =
|
private readonly IDumpProcessor _dumpProcessor =
|
||||||
dumpProcessor ?? throw new ArgumentNullException(nameof(dumpProcessor));
|
dumpProcessor ?? throw new ArgumentNullException(nameof(dumpProcessor));
|
||||||
|
|
||||||
|
private readonly ILogger<QueuePipeline> _logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
|
|
||||||
|
private readonly IPreProcessReader _preProcessReader =
|
||||||
|
preProcessReader ?? throw new ArgumentNullException(nameof(preProcessReader));
|
||||||
|
|
||||||
|
private readonly IFileFilter _fileFilter = fileFilter ?? throw new ArgumentNullException(nameof(fileFilter));
|
||||||
|
|
||||||
|
private readonly IIntakeReader
|
||||||
|
_intakeReader = intakeReader ?? throw new ArgumentNullException(nameof(intakeReader));
|
||||||
|
|
||||||
private readonly List<string> _filesToRename = new();
|
private readonly List<string> _filesToRename = new();
|
||||||
private readonly BlockingCollection<string> _filesToProcess = new();
|
private readonly BlockingCollection<string> _filesToProcess = new();
|
||||||
|
|
||||||
private readonly List<string> _mapNames = LootDumpProcessorContext.GetConfig().MapsToProcess;
|
private readonly List<string> _mapNames = LootDumpProcessorContext.GetConfig().MapsToProcess;
|
||||||
|
|
||||||
private static readonly Dictionary<string, IPreProcessReader> _preProcessReaders;
|
|
||||||
|
|
||||||
static QueuePipeline()
|
|
||||||
{
|
|
||||||
_preProcessReaders = LootDumpProcessorContext.GetConfig()
|
|
||||||
.ReaderConfig
|
|
||||||
.PreProcessorConfig
|
|
||||||
?.PreProcessors
|
|
||||||
?.ToDictionary(
|
|
||||||
t => PreProcessReaderFactory.GetInstance(t).GetHandleExtension().ToLower(),
|
|
||||||
PreProcessReaderFactory.GetInstance
|
|
||||||
) ?? new Dictionary<string, IPreProcessReader>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task DoProcess()
|
public async Task DoProcess()
|
||||||
{
|
{
|
||||||
@ -47,10 +48,7 @@ public class QueuePipeline(IFileProcessor fileProcessor, IDumpProcessor dumpProc
|
|||||||
var collector = CollectorFactory.GetInstance();
|
var collector = CollectorFactory.GetInstance();
|
||||||
collector.Setup();
|
collector.Setup();
|
||||||
|
|
||||||
if (LoggerFactory.GetInstance().CanBeLogged(LogLevel.Info))
|
_logger.LogInformation("Gathering files to begin processing");
|
||||||
{
|
|
||||||
LoggerFactory.GetInstance().Log("Gathering files to begin processing", LogLevel.Info);
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -62,11 +60,7 @@ public class QueuePipeline(IFileProcessor fileProcessor, IDumpProcessor dumpProc
|
|||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
// use dispose on the preprocessreaders to eliminate any temporary files generated
|
_preProcessReader.Dispose();
|
||||||
foreach (var (_, value) in _preProcessReaders)
|
|
||||||
{
|
|
||||||
value.Dispose();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,36 +89,30 @@ public class QueuePipeline(IFileProcessor fileProcessor, IDumpProcessor dumpProc
|
|||||||
throw new Exception("No files matched accepted extension types in configs");
|
throw new Exception("No files matched accepted extension types in configs");
|
||||||
}
|
}
|
||||||
|
|
||||||
var fileFilters = GetFileFilters() ?? new Dictionary<string, IFileFilter>();
|
|
||||||
|
|
||||||
while (queuedFilesToProcess.TryDequeue(out var file))
|
while (queuedFilesToProcess.TryDequeue(out var file))
|
||||||
{
|
{
|
||||||
var extensionFull = Path.GetExtension(file);
|
var extensionFull = Path.GetExtension(file);
|
||||||
if (extensionFull.Length > 1)
|
if (extensionFull.Length > 1)
|
||||||
{
|
{
|
||||||
var extension = extensionFull[1..].ToLower();
|
// if the preprocessor found something new to process or generated new files, add them to the queue
|
||||||
// if there is a preprocessor, call it and preprocess the file, then add them to the queue
|
if (extensionFull == "7z" &&
|
||||||
if (_preProcessReaders.TryGetValue(extension, out var preProcessor))
|
_preProcessReader.TryPreProcess(file, out var outputFiles, out var outputDirectories))
|
||||||
{
|
{
|
||||||
// if the preprocessor found something new to process or generated new files, add them to the queue
|
// all new directories need to be processed as well
|
||||||
if (preProcessor.TryPreProcess(file, out var outputFiles, out var outputDirectories))
|
GetFileQueue(outputDirectories).ToList().ForEach(queuedFilesToProcess.Enqueue);
|
||||||
{
|
// all output files need to be queued as well
|
||||||
// all new directories need to be processed as well
|
outputFiles.ForEach(queuedFilesToProcess.Enqueue);
|
||||||
GetFileQueue(outputDirectories).ToList().ForEach(queuedFilesToProcess.Enqueue);
|
|
||||||
// all output files need to be queued as well
|
|
||||||
outputFiles.ForEach(queuedFilesToProcess.Enqueue);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// if there is no preprocessor for the file, means its ready to filter or accept
|
// if there is no preprocessor for the file, means its ready to filter or accept
|
||||||
if (fileFilters.TryGetValue(extension, out var filter))
|
|
||||||
|
if (_fileFilter.Accept(file))
|
||||||
{
|
{
|
||||||
if (filter.Accept(file))
|
gatheredFiles.Add(file);
|
||||||
{
|
|
||||||
gatheredFiles.Add(file);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
gatheredFiles.Add(file);
|
gatheredFiles.Add(file);
|
||||||
@ -134,6 +122,7 @@ public class QueuePipeline(IFileProcessor fileProcessor, IDumpProcessor dumpProc
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Handle invalid extension
|
// Handle invalid extension
|
||||||
|
_logger.LogWarning("File '{File}' has an invalid extension.", file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,11 +134,6 @@ public class QueuePipeline(IFileProcessor fileProcessor, IDumpProcessor dumpProc
|
|||||||
var queuedPathsToProcess = new Queue<string>();
|
var queuedPathsToProcess = new Queue<string>();
|
||||||
var queuedFilesToProcess = new Queue<string>();
|
var queuedFilesToProcess = new Queue<string>();
|
||||||
|
|
||||||
// Accepted file extensions on raw files
|
|
||||||
var acceptedFileExtension = LootDumpProcessorContext.GetConfig()
|
|
||||||
.ReaderConfig
|
|
||||||
.AcceptedFileExtensions
|
|
||||||
.Select(ex => ex.ToLower());
|
|
||||||
inputPath.ForEach(p => queuedPathsToProcess.Enqueue(p));
|
inputPath.ForEach(p => queuedPathsToProcess.Enqueue(p));
|
||||||
|
|
||||||
while (queuedPathsToProcess.TryDequeue(out var path))
|
while (queuedPathsToProcess.TryDequeue(out var path))
|
||||||
@ -173,27 +157,13 @@ public class QueuePipeline(IFileProcessor fileProcessor, IDumpProcessor dumpProc
|
|||||||
|
|
||||||
foreach (var file in files)
|
foreach (var file in files)
|
||||||
{
|
{
|
||||||
if (acceptedFileExtension.Contains(Path.GetExtension(file)[1..].ToLower()))
|
queuedFilesToProcess.Enqueue(file);
|
||||||
{
|
|
||||||
queuedFilesToProcess.Enqueue(file);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return queuedFilesToProcess;
|
return queuedFilesToProcess;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Dictionary<string, IFileFilter>? GetFileFilters()
|
|
||||||
{
|
|
||||||
return LootDumpProcessorContext.GetConfig()
|
|
||||||
.ReaderConfig
|
|
||||||
.FileFilters
|
|
||||||
?.ToDictionary(
|
|
||||||
t => FileFilterFactory.GetInstance(t).GetExtension(),
|
|
||||||
FileFilterFactory.GetInstance
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ProcessFilesFromDumpsPerMap(ICollector collector, string mapName)
|
private void ProcessFilesFromDumpsPerMap(ICollector collector, string mapName)
|
||||||
{
|
{
|
||||||
// Gather all files, sort them by date descending and then add them into the processing queue
|
// Gather all files, sort them by date descending and then add them into the processing queue
|
||||||
@ -204,36 +174,24 @@ public class QueuePipeline(IFileProcessor fileProcessor, IDumpProcessor dumpProc
|
|||||||
}
|
}
|
||||||
).ToList().ForEach(f => _filesToProcess.Add(f));
|
).ToList().ForEach(f => _filesToProcess.Add(f));
|
||||||
|
|
||||||
if (LoggerFactory.GetInstance().CanBeLogged(LogLevel.Info))
|
_logger.LogInformation("Files sorted and ready to begin pre-processing");
|
||||||
{
|
|
||||||
LoggerFactory.GetInstance().Log("Files sorted and ready to begin pre-processing", LogLevel.Info);
|
|
||||||
}
|
|
||||||
|
|
||||||
Parallel.ForEach(_filesToProcess, file =>
|
Parallel.ForEach(_filesToProcess, file =>
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var reader = IntakeReaderFactory.GetInstance();
|
if (_intakeReader.Read(file, out var basicInfo))
|
||||||
if (reader.Read(file, out var basicInfo))
|
|
||||||
{
|
{
|
||||||
collector.Hold(_fileProcessor.Process(basicInfo));
|
collector.Hold(_fileProcessor.Process(basicInfo));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
if (LoggerFactory.GetInstance().CanBeLogged(LogLevel.Error))
|
_logger.LogError(e, "Error occurred while processing file {File}", file);
|
||||||
{
|
|
||||||
LoggerFactory.GetInstance().Log(
|
|
||||||
$"Error occurred while processing file {file}\n{e.Message}\n{e.StackTrace}",
|
|
||||||
LogLevel.Error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (LoggerFactory.GetInstance().CanBeLogged(LogLevel.Info))
|
_logger.LogInformation("Pre-processing finished");
|
||||||
{
|
|
||||||
LoggerFactory.GetInstance().Log("Pre-processing finished", LogLevel.Info);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Single writer instance to collect results
|
// Single writer instance to collect results
|
||||||
var writer = WriterFactory.GetInstance();
|
var writer = WriterFactory.GetInstance();
|
||||||
@ -246,7 +204,7 @@ public class QueuePipeline(IFileProcessor fileProcessor, IDumpProcessor dumpProc
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds map name to file if they dont have it already.
|
/// Adds map name to file if they don't have it already.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="threads">Number of threads to use</param>
|
/// <param name="threads">Number of threads to use</param>
|
||||||
private async Task FixFilesFromDumps()
|
private async Task FixFilesFromDumps()
|
||||||
@ -275,18 +233,10 @@ public class QueuePipeline(IFileProcessor fileProcessor, IDumpProcessor dumpProc
|
|||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
if (LoggerFactory.GetInstance().CanBeLogged(LogLevel.Error))
|
_logger.LogError(e, "Error occurred while processing file {File}", file);
|
||||||
{
|
|
||||||
LoggerFactory.GetInstance().Log(
|
|
||||||
$"Error occurred while processing file {file}\n{e.Message}\n{e.StackTrace}",
|
|
||||||
LogLevel.Error);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (LoggerFactory.GetInstance().CanBeLogged(LogLevel.Info))
|
_logger.LogInformation("File-processing finished");
|
||||||
{
|
|
||||||
LoggerFactory.GetInstance().Log("File-processing finished", LogLevel.Info);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,25 +0,0 @@
|
|||||||
namespace LootDumpProcessor.Process.Reader.Filters;
|
|
||||||
|
|
||||||
public static class FileFilterFactory
|
|
||||||
{
|
|
||||||
private static readonly Dictionary<FileFilterTypes, IFileFilter> _fileFilters = new();
|
|
||||||
private static object lockObject = new();
|
|
||||||
|
|
||||||
public static IFileFilter GetInstance(FileFilterTypes type)
|
|
||||||
{
|
|
||||||
lock (lockObject)
|
|
||||||
{
|
|
||||||
if (!_fileFilters.TryGetValue(type, out var filter))
|
|
||||||
{
|
|
||||||
filter = type switch
|
|
||||||
{
|
|
||||||
FileFilterTypes.JsonDump => new JsonDumpFileFilter(),
|
|
||||||
_ => throw new ArgumentOutOfRangeException(nameof(type), type, null)
|
|
||||||
};
|
|
||||||
_fileFilters.Add(type, filter);
|
|
||||||
}
|
|
||||||
|
|
||||||
return filter;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +0,0 @@
|
|||||||
namespace LootDumpProcessor.Process.Reader.Filters;
|
|
||||||
|
|
||||||
public enum FileFilterTypes
|
|
||||||
{
|
|
||||||
JsonDump
|
|
||||||
}
|
|
@ -1,24 +1,23 @@
|
|||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using LootDumpProcessor.Logger;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace LootDumpProcessor.Process.Reader.Filters;
|
namespace LootDumpProcessor.Process.Reader.Filters;
|
||||||
|
|
||||||
public class JsonDumpFileFilter : IFileFilter
|
public class JsonDumpFileFilter : IFileFilter
|
||||||
{
|
{
|
||||||
private static readonly Regex _fileNameDateRegex = new("([0-9]{4}(-[0-9]{2}){2}_((-){0,1}[0-9]{2}){3})");
|
private readonly ILogger<JsonDumpFileFilter> _logger;
|
||||||
private static readonly DateTime _parsedThresholdDate;
|
private readonly Regex _fileNameDateRegex = new("([0-9]{4}(-[0-9]{2}){2}_((-){0,1}[0-9]{2}){3})");
|
||||||
|
private readonly DateTime _parsedThresholdDate;
|
||||||
|
|
||||||
static JsonDumpFileFilter()
|
public JsonDumpFileFilter(ILogger<JsonDumpFileFilter> logger)
|
||||||
{
|
{
|
||||||
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
// Calculate parsed date from config threshold
|
// Calculate parsed date from config threshold
|
||||||
if (string.IsNullOrEmpty(LootDumpProcessorContext.GetConfig().ReaderConfig.ThresholdDate))
|
if (string.IsNullOrEmpty(LootDumpProcessorContext.GetConfig().ReaderConfig.ThresholdDate))
|
||||||
{
|
{
|
||||||
if (LoggerFactory.GetInstance().CanBeLogged(LogLevel.Warning))
|
_logger.LogWarning("ThresholdDate is null or empty in configs, defaulting to current day minus 30 days");
|
||||||
LoggerFactory.GetInstance()
|
_parsedThresholdDate = DateTime.Now - TimeSpan.FromDays(30);
|
||||||
.Log($"ThresholdDate is null or empty in configs, defaulting to current day minus 30 days",
|
|
||||||
LogLevel.Warning);
|
|
||||||
_parsedThresholdDate = (DateTime.Now - TimeSpan.FromDays(30));
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -1,28 +0,0 @@
|
|||||||
namespace LootDumpProcessor.Process.Reader.Intake;
|
|
||||||
|
|
||||||
public static class IntakeReaderFactory
|
|
||||||
{
|
|
||||||
private static readonly Dictionary<IntakeReaderTypes, IIntakeReader> Instances = new();
|
|
||||||
private static readonly object DictionaryLock = new();
|
|
||||||
public static IIntakeReader GetInstance()
|
|
||||||
{
|
|
||||||
var type = LootDumpProcessorContext.GetConfig().ReaderConfig.IntakeReaderConfig?.IntakeReaderType ??
|
|
||||||
IntakeReaderTypes.Json;
|
|
||||||
lock (DictionaryLock)
|
|
||||||
{
|
|
||||||
if (!Instances.TryGetValue(type, out var intakeReader))
|
|
||||||
{
|
|
||||||
intakeReader = type switch
|
|
||||||
{
|
|
||||||
IntakeReaderTypes.Json => new JsonFileIntakeReader(),
|
|
||||||
_ => throw new ArgumentOutOfRangeException(
|
|
||||||
"IntakeReaderType",
|
|
||||||
"Value was not defined on IntakeReaderConfig"
|
|
||||||
)
|
|
||||||
};
|
|
||||||
Instances.Add(type, intakeReader);
|
|
||||||
}
|
|
||||||
return intakeReader;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +0,0 @@
|
|||||||
namespace LootDumpProcessor.Process.Reader.Intake;
|
|
||||||
|
|
||||||
public enum IntakeReaderTypes
|
|
||||||
{
|
|
||||||
Json
|
|
||||||
}
|
|
@ -1,81 +1,84 @@
|
|||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using LootDumpProcessor.Logger;
|
using System.Text.Json;
|
||||||
|
using LootDumpProcessor;
|
||||||
using LootDumpProcessor.Model.Input;
|
using LootDumpProcessor.Model.Input;
|
||||||
using LootDumpProcessor.Model.Processing;
|
using LootDumpProcessor.Model.Processing;
|
||||||
using LootDumpProcessor.Serializers.Json;
|
using LootDumpProcessor.Process.Reader.Intake;
|
||||||
using LootDumpProcessor.Utils;
|
using LootDumpProcessor.Utils;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace LootDumpProcessor.Process.Reader.Intake;
|
public class JsonFileIntakeReader(ILogger<JsonFileIntakeReader> logger) : IIntakeReader
|
||||||
|
|
||||||
public class JsonFileIntakeReader : IIntakeReader
|
|
||||||
{
|
{
|
||||||
private static readonly IJsonSerializer _jsonSerializer = JsonSerializerFactory.GetInstance();
|
private readonly ILogger<JsonFileIntakeReader> _logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
|
|
||||||
private static readonly HashSet<string>? _ignoredLocations =
|
private readonly HashSet<string>? _ignoredLocations = LootDumpProcessorContext.GetConfig()
|
||||||
LootDumpProcessorContext.GetConfig().ReaderConfig.IntakeReaderConfig?.IgnoredDumpLocations.ToHashSet();
|
.ReaderConfig.IntakeReaderConfig?.IgnoredDumpLocations.ToHashSet();
|
||||||
|
|
||||||
private static readonly ConcurrentDictionary<string, int> _totalMapDumpsCounter = new();
|
private readonly ConcurrentDictionary<string, int> _totalMapDumpsCounter = new();
|
||||||
|
|
||||||
public bool Read(string file, out BasicInfo basicInfo)
|
public bool Read(string file, out BasicInfo basicInfo)
|
||||||
{
|
{
|
||||||
var fileData = File.ReadAllText(file);
|
basicInfo = null;
|
||||||
|
string? fileData;
|
||||||
|
|
||||||
if (fileData == null)
|
try
|
||||||
{
|
{
|
||||||
if (LoggerFactory.GetInstance().CanBeLogged(LogLevel.Error))
|
fileData = File.ReadAllText(file);
|
||||||
LoggerFactory.GetInstance().Log($"Couldnt parse date from file: {file}", LogLevel.Error);
|
}
|
||||||
|
catch (Exception ex)
|
||||||
basicInfo = null;
|
{
|
||||||
|
_logger.LogError(ex, "Failed to read file: {File}", file);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the file format changes it may screw up this date parser
|
if (string.IsNullOrWhiteSpace(fileData))
|
||||||
if (!FileDateParser.TryParseFileDate(file, out var date))
|
|
||||||
{
|
{
|
||||||
if (LoggerFactory.GetInstance().CanBeLogged(LogLevel.Error))
|
_logger.LogError("Could not parse data from file: {File}", file);
|
||||||
LoggerFactory.GetInstance().Log($"Couldnt parse date from file: {file}", LogLevel.Error);
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var fi = _jsonSerializer.Deserialize<RootData>(fileData);
|
// If the file format changes, it may affect the date parser
|
||||||
|
if (!FileDateParser.TryParseFileDate(file, out var date))
|
||||||
|
{
|
||||||
|
_logger.LogError("Could not parse date from file: {File}", file);
|
||||||
|
}
|
||||||
|
|
||||||
|
var fi = JsonSerializer.Deserialize<RootData>(fileData);
|
||||||
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))
|
||||||
{
|
{
|
||||||
if (!_totalMapDumpsCounter.TryGetValue(fi.Data.LocationLoot.Name, out var counter))
|
var mapName = fi.Data.LocationLoot.Name;
|
||||||
{
|
var mapId = fi.Data.LocationLoot.Id.ToLower();
|
||||||
counter = 0;
|
|
||||||
_totalMapDumpsCounter[fi.Data.LocationLoot.Name] = counter;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (counter < (LootDumpProcessorContext.GetConfig().ReaderConfig.IntakeReaderConfig?.MaxDumpsPerMap ?? 1500))
|
var counter = _totalMapDumpsCounter.AddOrUpdate(mapName, 0, (_, current) => current);
|
||||||
|
|
||||||
|
var maxDumpsPerMap = LootDumpProcessorContext.GetConfig()
|
||||||
|
.ReaderConfig.IntakeReaderConfig?.MaxDumpsPerMap ?? 1500;
|
||||||
|
|
||||||
|
if (counter < maxDumpsPerMap)
|
||||||
{
|
{
|
||||||
basicInfo = new BasicInfo
|
basicInfo = new BasicInfo
|
||||||
{
|
{
|
||||||
Map = fi.Data.LocationLoot.Id.ToLower(),
|
Map = mapId,
|
||||||
FileHash = ProcessorUtil.HashFile(fileData),
|
FileHash = ProcessorUtil.HashFile(fileData),
|
||||||
Data = fi,
|
Data = fi,
|
||||||
Date = date ?? DateTime.MinValue,
|
Date = date ?? DateTime.MinValue,
|
||||||
FileName = file
|
FileName = file
|
||||||
};
|
};
|
||||||
|
|
||||||
_totalMapDumpsCounter[fi.Data.LocationLoot.Name] += 1;
|
_totalMapDumpsCounter[mapName] += 1;
|
||||||
|
|
||||||
if (LoggerFactory.GetInstance().CanBeLogged(LogLevel.Debug))
|
|
||||||
LoggerFactory.GetInstance().Log($"File {file} fully read, returning data", LogLevel.Debug);
|
|
||||||
|
|
||||||
|
_logger.LogDebug("File {File} fully read, returning data", file);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Map dump limit reached, exit
|
// Map dump limit reached, exit
|
||||||
if (LoggerFactory.GetInstance().CanBeLogged(LogLevel.Debug))
|
_logger.LogDebug("Ignoring file {File} as the file cap for map {MapId} has been reached", file, mapId);
|
||||||
LoggerFactory.GetInstance().Log($"Ignoring file {file} as the file cap for map {fi.Data.LocationLoot.Id} has been reached", LogLevel.Debug);
|
|
||||||
|
|
||||||
basicInfo = null;
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (LoggerFactory.GetInstance().CanBeLogged(LogLevel.Warning))
|
_logger.LogWarning(
|
||||||
LoggerFactory.GetInstance().Log($"File {file} was not eligible for dump data, it did not contain a location name or it was on ignored locations config", LogLevel.Warning);
|
"File {File} was not eligible for dump data; it did not contain a location name or it was on the ignored locations config",
|
||||||
|
file);
|
||||||
basicInfo = null;
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
using LootDumpProcessor.Logger;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace LootDumpProcessor.Process.Reader.PreProcess;
|
namespace LootDumpProcessor.Process.Reader.PreProcess;
|
||||||
|
|
||||||
@ -6,18 +6,16 @@ public abstract class AbstractPreProcessReader : IPreProcessReader
|
|||||||
{
|
{
|
||||||
protected readonly string _tempFolder;
|
protected readonly string _tempFolder;
|
||||||
|
|
||||||
public AbstractPreProcessReader()
|
public AbstractPreProcessReader(ILogger logger)
|
||||||
{
|
{
|
||||||
var tempFolder = LootDumpProcessorContext.GetConfig().ReaderConfig.PreProcessorConfig?.PreProcessorTempFolder;
|
var tempFolder = LootDumpProcessorContext.GetConfig().ReaderConfig.PreProcessorConfig?.PreProcessorTempFolder;
|
||||||
if (string.IsNullOrEmpty(tempFolder))
|
if (string.IsNullOrEmpty(tempFolder))
|
||||||
{
|
{
|
||||||
tempFolder = GetBaseDirectory();
|
tempFolder = GetBaseDirectory();
|
||||||
if (LoggerFactory.GetInstance().CanBeLogged(LogLevel.Warning))
|
logger.LogWarning(
|
||||||
LoggerFactory.GetInstance()
|
"No temp folder was assigned preProcessorTempFolder in PreProcessorConfig, defaulting to {tempFolder}",
|
||||||
.Log(
|
tempFolder
|
||||||
$"No temp folder was assigned preProcessorTempFolder in PreProcessorConfig, defaulting to {tempFolder}",
|
);
|
||||||
LogLevel.Warning
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cleanup the temp directory before starting the process
|
// Cleanup the temp directory before starting the process
|
||||||
@ -34,16 +32,12 @@ public abstract class AbstractPreProcessReader : IPreProcessReader
|
|||||||
public abstract string GetHandleExtension();
|
public abstract string GetHandleExtension();
|
||||||
public abstract bool TryPreProcess(string file, out List<string> files, out List<string> directories);
|
public abstract bool TryPreProcess(string file, out List<string> files, out List<string> directories);
|
||||||
|
|
||||||
protected string GetBaseDirectory()
|
protected string GetBaseDirectory() =>
|
||||||
{
|
$@"{Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)}\SPT\tmp\PreProcessor";
|
||||||
return $@"{Environment.GetFolderPath(Environment.SpecialFolder.UserProfile)}\SPT\tmp\PreProcessor";
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
if (LootDumpProcessorContext.GetConfig().ReaderConfig.PreProcessorConfig?.CleanupTempFolderAfterProcess ?? true)
|
if (LootDumpProcessorContext.GetConfig().ReaderConfig.PreProcessorConfig?.CleanupTempFolderAfterProcess ?? true)
|
||||||
{
|
|
||||||
Directory.Delete(_tempFolder, true);
|
Directory.Delete(_tempFolder, true);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,21 +0,0 @@
|
|||||||
namespace LootDumpProcessor.Process.Reader.PreProcess;
|
|
||||||
|
|
||||||
public static class PreProcessReaderFactory
|
|
||||||
{
|
|
||||||
private static readonly Dictionary<PreProcessReaderTypes, IPreProcessReader> _proProcessReaders = new();
|
|
||||||
|
|
||||||
public static IPreProcessReader GetInstance(PreProcessReaderTypes type)
|
|
||||||
{
|
|
||||||
if (!_proProcessReaders.TryGetValue(type, out var preProcessReader))
|
|
||||||
{
|
|
||||||
preProcessReader = type switch
|
|
||||||
{
|
|
||||||
PreProcessReaderTypes.SevenZip => new SevenZipPreProcessReader(),
|
|
||||||
_ => throw new ArgumentOutOfRangeException(nameof(type), type, null)
|
|
||||||
};
|
|
||||||
_proProcessReaders.Add(type, preProcessReader);
|
|
||||||
}
|
|
||||||
|
|
||||||
return preProcessReader;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,10 +1,16 @@
|
|||||||
using LootDumpProcessor.Logger;
|
using LootDumpProcessor.Process.Reader.PreProcess;
|
||||||
using SevenZip;
|
using SevenZip;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
namespace LootDumpProcessor.Process.Reader.PreProcess;
|
|
||||||
|
|
||||||
public class SevenZipPreProcessReader : AbstractPreProcessReader
|
public class SevenZipPreProcessReader : AbstractPreProcessReader
|
||||||
{
|
{
|
||||||
|
private readonly ILogger<SevenZipPreProcessReader> _logger;
|
||||||
|
|
||||||
|
public SevenZipPreProcessReader(ILogger<SevenZipPreProcessReader> logger) : base(logger)
|
||||||
|
{
|
||||||
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
|
}
|
||||||
|
|
||||||
public override string GetHandleExtension() => "7z";
|
public override string GetHandleExtension() => "7z";
|
||||||
|
|
||||||
static SevenZipPreProcessReader()
|
static SevenZipPreProcessReader()
|
||||||
@ -14,29 +20,32 @@ public class SevenZipPreProcessReader : AbstractPreProcessReader
|
|||||||
|
|
||||||
public override bool TryPreProcess(string file, out List<string> files, out List<string> directories)
|
public override bool TryPreProcess(string file, out List<string> files, out List<string> directories)
|
||||||
{
|
{
|
||||||
|
files = new List<string>();
|
||||||
|
directories = new List<string>();
|
||||||
|
|
||||||
var fileRaw = Path.GetFileNameWithoutExtension(file);
|
var fileRaw = Path.GetFileNameWithoutExtension(file);
|
||||||
// SevenZip library doesnt like forward slashes for some reason
|
// SevenZip library doesn't handle forward slashes properly
|
||||||
var outPath = $"{_tempFolder}\\{fileRaw}".Replace("/", "\\");
|
var outPath = $"{_tempFolder}\\{fileRaw}".Replace("/", "\\");
|
||||||
if (LoggerFactory.GetInstance().CanBeLogged(LogLevel.Info))
|
|
||||||
LoggerFactory.GetInstance().Log(
|
_logger.LogInformation("Unzipping {File} into temp path {OutPath}, this may take a while...", file, outPath);
|
||||||
$"Unzipping {file} into temp path {outPath}, this may take a while...",
|
|
||||||
LogLevel.Info);
|
|
||||||
var extractor = new SevenZipExtractor(file);
|
var extractor = new SevenZipExtractor(file);
|
||||||
// Only log process on debug mode
|
|
||||||
if (LoggerFactory.GetInstance().CanBeLogged(LogLevel.Debug))
|
// Log progress in debug mode
|
||||||
|
extractor.Extracting += (_, args) =>
|
||||||
{
|
{
|
||||||
extractor.Extracting += (_, args) =>
|
if (args.PercentDone % 10 == 0)
|
||||||
{
|
{
|
||||||
if (args.PercentDone % 10 == 0)
|
_logger.LogDebug("Unzip progress: {PercentDone}%", args.PercentDone);
|
||||||
LoggerFactory.GetInstance().Log($"Unzip progress: {args.PercentDone}%", LogLevel.Debug);
|
}
|
||||||
};
|
};
|
||||||
}
|
|
||||||
extractor.ExtractArchive(outPath);
|
extractor.ExtractArchive(outPath);
|
||||||
if (LoggerFactory.GetInstance().CanBeLogged(LogLevel.Info))
|
_logger.LogInformation("Finished unzipping {File} into temp path {OutPath}", file, outPath);
|
||||||
LoggerFactory.GetInstance().Log($"Finished unzipping {file} into temp path {outPath}", LogLevel.Info);
|
|
||||||
|
|
||||||
files = Directory.GetFiles(outPath).ToList();
|
files = Directory.GetFiles(outPath).ToList();
|
||||||
directories = Directory.GetDirectories(outPath).ToList();
|
directories = Directory.GetDirectories(outPath).ToList();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,68 +0,0 @@
|
|||||||
using LootDumpProcessor.Logger;
|
|
||||||
using LootDumpProcessor.Model.Tarkov;
|
|
||||||
using LootDumpProcessor.Serializers.Json;
|
|
||||||
|
|
||||||
namespace LootDumpProcessor.Process;
|
|
||||||
|
|
||||||
public class TarkovItems(string items)
|
|
||||||
{
|
|
||||||
private static readonly IJsonSerializer _jsonSerializer = JsonSerializerFactory.GetInstance();
|
|
||||||
|
|
||||||
private readonly Dictionary<string, TemplateFileItem>? _items = _jsonSerializer.Deserialize<Dictionary<string, TemplateFileItem>>(File.ReadAllText(items));
|
|
||||||
|
|
||||||
public virtual bool IsBaseClass(string tpl, string baseclass_id)
|
|
||||||
{
|
|
||||||
if (_items == null)
|
|
||||||
throw new Exception("The server items couldnt be found or loaded. Check server config is pointing to the correct place");
|
|
||||||
if (!_items.TryGetValue(tpl, out var item_template))
|
|
||||||
{
|
|
||||||
if (LoggerFactory.GetInstance().CanBeLogged(LogLevel.Error))
|
|
||||||
LoggerFactory.GetInstance().Log($"[IsBaseClass] Item template '{tpl}' with base class id '{baseclass_id}' was not found on the server items!", LogLevel.Error);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(item_template.Parent))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return item_template.Parent == baseclass_id || IsBaseClass(item_template.Parent, baseclass_id);
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual bool IsQuestItem(string tpl)
|
|
||||||
{
|
|
||||||
if (_items == null)
|
|
||||||
throw new Exception("The server items couldnt be found or loaded. Check server config is pointing to the correct place");
|
|
||||||
if (!_items.TryGetValue(tpl, out var item_template))
|
|
||||||
{
|
|
||||||
if (LoggerFactory.GetInstance().CanBeLogged(LogLevel.Error))
|
|
||||||
LoggerFactory.GetInstance().Log($"[IsQuestItem] Item template '{tpl}' was not found on the server items!", LogLevel.Error);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return item_template.Props.QuestItem;
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual string? MaxDurability(string tpl)
|
|
||||||
{
|
|
||||||
if (_items == null)
|
|
||||||
throw new Exception("The server items couldnt be found or loaded. Check server config is pointing to the correct place");
|
|
||||||
if (!_items.TryGetValue(tpl, out var item_template))
|
|
||||||
{
|
|
||||||
if (LoggerFactory.GetInstance().CanBeLogged(LogLevel.Error))
|
|
||||||
LoggerFactory.GetInstance().Log($"[MaxDurability] Item template '{tpl}' was not found on the server items!", LogLevel.Error);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return item_template.Props.MaxDurability?.ToString() ?? "";
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual string? AmmoCaliber(string tpl)
|
|
||||||
{
|
|
||||||
if (_items == null)
|
|
||||||
throw new Exception("The server items couldnt be found or loaded. Check server config is pointing to the correct place");
|
|
||||||
if (!_items.TryGetValue(tpl, out var item_template))
|
|
||||||
{
|
|
||||||
if (LoggerFactory.GetInstance().CanBeLogged(LogLevel.Error))
|
|
||||||
LoggerFactory.GetInstance().Log($"[AmmoCaliber] Item template '{tpl}' was not found on the server items!", LogLevel.Error);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return item_template.Props.Caliber;
|
|
||||||
}
|
|
||||||
}
|
|
113
source/LootDumpProcessor/Process/TarkovItemsProvider.cs
Normal file
113
source/LootDumpProcessor/Process/TarkovItemsProvider.cs
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
using System.Collections.Frozen;
|
||||||
|
using System.Text.Json;
|
||||||
|
using LootDumpProcessor.Model.Tarkov;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace LootDumpProcessor.Process
|
||||||
|
{
|
||||||
|
public class TarkovItemsProvider : ITarkovItemsProvider
|
||||||
|
{
|
||||||
|
private readonly ILogger<TarkovItemsProvider> _logger;
|
||||||
|
private readonly FrozenDictionary<string, TemplateFileItem>? _items;
|
||||||
|
|
||||||
|
private static readonly string ItemsFilePath = Path.Combine(
|
||||||
|
LootDumpProcessorContext.GetConfig().ServerLocation,
|
||||||
|
"project", "assets", "database", "templates", "items.json");
|
||||||
|
|
||||||
|
public TarkovItemsProvider(ILogger<TarkovItemsProvider> logger)
|
||||||
|
{
|
||||||
|
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var jsonContent = File.ReadAllText(ItemsFilePath);
|
||||||
|
_items = (JsonSerializer.Deserialize<Dictionary<string, TemplateFileItem>>(jsonContent)
|
||||||
|
?? throw new InvalidOperationException()).ToFrozenDictionary();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Failed to load server items from {ItemsPath}", ItemsFilePath);
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
"The server items couldn't be found or loaded. Check server config is pointing to the correct place.",
|
||||||
|
ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsBaseClass(string tpl, string baseclassId)
|
||||||
|
{
|
||||||
|
if (_items == null)
|
||||||
|
{
|
||||||
|
_logger.LogError("The server items are null. Check server config is pointing to the correct place.");
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
"The server items couldn't be found or loaded. Check server config is pointing to the correct place.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_items.TryGetValue(tpl, out var itemTemplate))
|
||||||
|
{
|
||||||
|
_logger.LogError(
|
||||||
|
"Item template '{Tpl}' with base class id '{BaseclassId}' was not found in the server items!", tpl,
|
||||||
|
baseclassId);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(itemTemplate.Parent))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return itemTemplate.Parent == baseclassId || IsBaseClass(itemTemplate.Parent, baseclassId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsQuestItem(string tpl)
|
||||||
|
{
|
||||||
|
if (_items == null)
|
||||||
|
{
|
||||||
|
_logger.LogError("The server items are null. Check server config is pointing to the correct place.");
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
"The server items couldn't be found or loaded. Check server config is pointing to the correct place.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_items.TryGetValue(tpl, out var itemTemplate))
|
||||||
|
{
|
||||||
|
_logger.LogError("Item template '{Tpl}' was not found in the server items!", tpl);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return itemTemplate.Props.QuestItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string? MaxDurability(string tpl)
|
||||||
|
{
|
||||||
|
if (_items == null)
|
||||||
|
{
|
||||||
|
_logger.LogError("The server items are null. Check server config is pointing to the correct place.");
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
"The server items couldn't be found or loaded. Check server config is pointing to the correct place.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_items.TryGetValue(tpl, out var itemTemplate))
|
||||||
|
{
|
||||||
|
_logger.LogError("Item template '{Tpl}' was not found in the server items!", tpl);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return itemTemplate.Props.MaxDurability?.ToString() ?? string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string? AmmoCaliber(string tpl)
|
||||||
|
{
|
||||||
|
if (_items == null)
|
||||||
|
{
|
||||||
|
_logger.LogError("The server items are null. Check server config is pointing to the correct place.");
|
||||||
|
throw new InvalidOperationException(
|
||||||
|
"The server items couldn't be found or loaded. Check server config is pointing to the correct place.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_items.TryGetValue(tpl, out var itemTemplate))
|
||||||
|
{
|
||||||
|
_logger.LogError("Item template '{Tpl}' was not found in the server items!", tpl);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return itemTemplate.Props.Caliber;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -5,10 +5,12 @@ using LootDumpProcessor.Process.Processor.v2.AmmoProcessor;
|
|||||||
using LootDumpProcessor.Process.Processor.v2.LooseLootProcessor;
|
using LootDumpProcessor.Process.Processor.v2.LooseLootProcessor;
|
||||||
using LootDumpProcessor.Process.Processor.v2.StaticContainersProcessor;
|
using LootDumpProcessor.Process.Processor.v2.StaticContainersProcessor;
|
||||||
using LootDumpProcessor.Process.Processor.v2.StaticLootProcessor;
|
using LootDumpProcessor.Process.Processor.v2.StaticLootProcessor;
|
||||||
|
using LootDumpProcessor.Process.Reader.Filters;
|
||||||
|
using LootDumpProcessor.Process.Reader.Intake;
|
||||||
|
using LootDumpProcessor.Process.Reader.PreProcess;
|
||||||
using LootDumpProcessor.Storage;
|
using LootDumpProcessor.Storage;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using LoggerFactory = LootDumpProcessor.Logger.LoggerFactory;
|
|
||||||
|
|
||||||
namespace LootDumpProcessor;
|
namespace LootDumpProcessor;
|
||||||
|
|
||||||
@ -24,8 +26,13 @@ public static class Program
|
|||||||
services.AddTransient<IAmmoProcessor, AmmoProcessor>();
|
services.AddTransient<IAmmoProcessor, AmmoProcessor>();
|
||||||
services.AddTransient<ILooseLootProcessor, LooseLootProcessor>();
|
services.AddTransient<ILooseLootProcessor, LooseLootProcessor>();
|
||||||
|
|
||||||
|
services.AddSingleton<ITarkovItemsProvider, TarkovItemsProvider>();
|
||||||
services.AddSingleton<IDataStorage>(_ => DataStorageFactory.GetInstance());
|
services.AddSingleton<IDataStorage>(_ => DataStorageFactory.GetInstance());
|
||||||
|
services.AddTransient<IComposedKeyGenerator, ComposedKeyGenerator>();
|
||||||
|
|
||||||
|
services.AddTransient<IIntakeReader, JsonFileIntakeReader>();
|
||||||
|
services.AddTransient<IFileFilter, JsonDumpFileFilter>();
|
||||||
|
services.AddTransient<IPreProcessReader, SevenZipPreProcessReader>();
|
||||||
services.AddTransient<IFileProcessor, FileProcessor>();
|
services.AddTransient<IFileProcessor, FileProcessor>();
|
||||||
services.AddTransient<IDumpProcessor, MultithreadSteppedDumpProcessor>();
|
services.AddTransient<IDumpProcessor, MultithreadSteppedDumpProcessor>();
|
||||||
services.AddTransient<IPipeline, QueuePipeline>();
|
services.AddTransient<IPipeline, QueuePipeline>();
|
||||||
@ -34,15 +41,11 @@ public static class Program
|
|||||||
|
|
||||||
// Bootstrap the config before anything else, its required by the whole application to work
|
// Bootstrap the config before anything else, its required by the whole application to work
|
||||||
LootDumpProcessorContext.GetConfig();
|
LootDumpProcessorContext.GetConfig();
|
||||||
// Some loggers may need a startup and stop mechanism
|
|
||||||
LoggerFactory.GetInstance().Setup();
|
|
||||||
// Setup Data storage
|
// Setup Data storage
|
||||||
DataStorageFactory.GetInstance().Setup();
|
DataStorageFactory.GetInstance().Setup();
|
||||||
// startup the pipeline
|
// startup the pipeline
|
||||||
var pipeline = serviceProvider.GetRequiredService<IPipeline>();
|
var pipeline = serviceProvider.GetRequiredService<IPipeline>();
|
||||||
pipeline.DoProcess();
|
await pipeline.Execute();
|
||||||
// stop loggers at the end
|
|
||||||
LoggerFactory.GetInstance().Stop();
|
|
||||||
Thread.Sleep(10000);
|
Thread.Sleep(10000);
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user