mirror of
https://github.com/sp-tarkov/loot-dump-processor.git
synced 2025-02-13 02:30:45 -05:00
Refactored processors to use dependency injection and improved immutability
This commit is contained in:
parent
8cc4340340
commit
9be5d6e342
@ -12,6 +12,8 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="7z.Libs" Version="21.7.0" />
|
<PackageReference Include="7z.Libs" Version="21.7.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Logging" Version="9.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="9.0.0" />
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
|
||||||
<PackageReference Include="NumSharp" Version="0.30.0" />
|
<PackageReference Include="NumSharp" Version="0.30.0" />
|
||||||
<PackageReference Include="SevenZipSharp.Interop" Version="19.1.0" />
|
<PackageReference Include="SevenZipSharp.Interop" Version="19.1.0" />
|
||||||
|
@ -7,10 +7,10 @@ namespace LootDumpProcessor.Model.Output
|
|||||||
{
|
{
|
||||||
[JsonProperty("itemcountDistribution", NullValueHandling = NullValueHandling.Ignore)]
|
[JsonProperty("itemcountDistribution", NullValueHandling = NullValueHandling.Ignore)]
|
||||||
[JsonPropertyName("itemcountDistribution")]
|
[JsonPropertyName("itemcountDistribution")]
|
||||||
public List<ItemCountDistribution>? ItemCountDistribution { get; set; }
|
public IReadOnlyList<ItemCountDistribution>? ItemCountDistribution { get; set; }
|
||||||
|
|
||||||
[JsonProperty("itemDistribution", NullValueHandling = NullValueHandling.Ignore)]
|
[JsonProperty("itemDistribution", NullValueHandling = NullValueHandling.Ignore)]
|
||||||
[JsonPropertyName("itemDistribution")]
|
[JsonPropertyName("itemDistribution")]
|
||||||
public List<StaticDistribution>? ItemDistribution { get; set; }
|
public IReadOnlyList<StaticDistribution>? ItemDistribution { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -8,7 +8,7 @@ public class ParsedDump : IKeyable
|
|||||||
private static readonly Regex _hashRegex = new("([^a-zA-Z0-9])");
|
private static readonly Regex _hashRegex = new("([^a-zA-Z0-9])");
|
||||||
public BasicInfo BasicInfo { get; set; }
|
public BasicInfo BasicInfo { get; set; }
|
||||||
public PreProcessedLooseLoot LooseLoot { get; set; }
|
public PreProcessedLooseLoot LooseLoot { get; set; }
|
||||||
public List<PreProcessedStaticLoot> Containers { get; set; }
|
public IReadOnlyList<PreProcessedStaticLoot> Containers { get; set; }
|
||||||
|
|
||||||
public override bool Equals(object? obj)
|
public override bool Equals(object? obj)
|
||||||
{
|
{
|
||||||
|
@ -1,10 +0,0 @@
|
|||||||
namespace LootDumpProcessor.Process;
|
|
||||||
|
|
||||||
public static class PipelineFactory
|
|
||||||
{
|
|
||||||
public static IPipeline GetInstance()
|
|
||||||
{
|
|
||||||
// implement actual factory at some point
|
|
||||||
return new QueuePipeline();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
namespace LootDumpProcessor.Process.Processor.DumpProcessor;
|
|
||||||
|
|
||||||
public static class DumpProcessorFactory
|
|
||||||
{
|
|
||||||
|
|
||||||
public static IDumpProcessor GetInstance()
|
|
||||||
{
|
|
||||||
// Implement real factory
|
|
||||||
return new MultithreadSteppedDumpProcessor();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -4,6 +4,9 @@ using LootDumpProcessor.Model;
|
|||||||
using LootDumpProcessor.Model.Input;
|
using LootDumpProcessor.Model.Input;
|
||||||
using LootDumpProcessor.Model.Output.StaticContainer;
|
using LootDumpProcessor.Model.Output.StaticContainer;
|
||||||
using LootDumpProcessor.Model.Processing;
|
using LootDumpProcessor.Model.Processing;
|
||||||
|
using LootDumpProcessor.Process.Processor.v2.AmmoProcessor;
|
||||||
|
using LootDumpProcessor.Process.Processor.v2.StaticContainersProcessor;
|
||||||
|
using LootDumpProcessor.Process.Processor.v2.StaticLootProcessor;
|
||||||
using LootDumpProcessor.Serializers.Json;
|
using LootDumpProcessor.Serializers.Json;
|
||||||
using LootDumpProcessor.Storage;
|
using LootDumpProcessor.Storage;
|
||||||
using LootDumpProcessor.Storage.Collections;
|
using LootDumpProcessor.Storage.Collections;
|
||||||
@ -11,8 +14,20 @@ using LootDumpProcessor.Utils;
|
|||||||
|
|
||||||
namespace LootDumpProcessor.Process.Processor.DumpProcessor;
|
namespace LootDumpProcessor.Process.Processor.DumpProcessor;
|
||||||
|
|
||||||
public class MultithreadSteppedDumpProcessor : IDumpProcessor
|
public class MultithreadSteppedDumpProcessor(
|
||||||
|
IStaticLootProcessor staticLootProcessor, IStaticContainersProcessor staticContainersProcessor,
|
||||||
|
IAmmoProcessor ammoProcessor
|
||||||
|
) : IDumpProcessor
|
||||||
{
|
{
|
||||||
|
private readonly IStaticLootProcessor _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 static IJsonSerializer _jsonSerializer = JsonSerializerFactory.GetInstance();
|
private static IJsonSerializer _jsonSerializer = JsonSerializerFactory.GetInstance();
|
||||||
|
|
||||||
private static readonly List<Task> Runners = new();
|
private static readonly List<Task> Runners = new();
|
||||||
@ -51,14 +66,17 @@ public class MultithreadSteppedDumpProcessor : IDumpProcessor
|
|||||||
Task.Factory.StartNew(() =>
|
Task.Factory.StartNew(() =>
|
||||||
{
|
{
|
||||||
if (LoggerFactory.GetInstance().CanBeLogged(LogLevel.Debug))
|
if (LoggerFactory.GetInstance().CanBeLogged(LogLevel.Debug))
|
||||||
LoggerFactory.GetInstance().Log($"Processing static data for file {dumped.BasicInfo.FileName}", LogLevel.Debug);
|
LoggerFactory.GetInstance().Log($"Processing static data for file {dumped.BasicInfo.FileName}",
|
||||||
|
LogLevel.Debug);
|
||||||
|
|
||||||
var dataDump = _jsonSerializer.Deserialize<RootData>(File.ReadAllText(dumped.BasicInfo.FileName));
|
var dataDump = _jsonSerializer.Deserialize<RootData>(File.ReadAllText(dumped.BasicInfo.FileName));
|
||||||
|
|
||||||
if (dataDump == null)
|
if (dataDump == null)
|
||||||
{
|
{
|
||||||
if (LoggerFactory.GetInstance().CanBeLogged(LogLevel.Error))
|
if (LoggerFactory.GetInstance().CanBeLogged(LogLevel.Error))
|
||||||
LoggerFactory.GetInstance().Log($"Failed to deserialize data from file {dumped.BasicInfo.FileName}", LogLevel.Error);
|
LoggerFactory.GetInstance()
|
||||||
|
.Log($"Failed to deserialize data from file {dumped.BasicInfo.FileName}",
|
||||||
|
LogLevel.Error);
|
||||||
return; // Skip processing this dump
|
return; // Skip processing this dump
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -70,7 +88,9 @@ public class MultithreadSteppedDumpProcessor : IDumpProcessor
|
|||||||
if (!staticContainers.TryGetValue(mapId, out var mapStaticLoot))
|
if (!staticContainers.TryGetValue(mapId, out var mapStaticLoot))
|
||||||
{
|
{
|
||||||
if (LoggerFactory.GetInstance().CanBeLogged(LogLevel.Info))
|
if (LoggerFactory.GetInstance().CanBeLogged(LogLevel.Info))
|
||||||
LoggerFactory.GetInstance().Log($"Doing first time process for map {mapId} of real static data", LogLevel.Info);
|
LoggerFactory.GetInstance()
|
||||||
|
.Log($"Doing first time process for map {mapId} of real static data",
|
||||||
|
LogLevel.Info);
|
||||||
|
|
||||||
staticContainers[mapId] = new MapStaticLoot
|
staticContainers[mapId] = new MapStaticLoot
|
||||||
{
|
{
|
||||||
@ -82,10 +102,13 @@ public class MultithreadSteppedDumpProcessor : IDumpProcessor
|
|||||||
{
|
{
|
||||||
// .Item1 = map name
|
// .Item1 = map name
|
||||||
// .Item2 = force/weapon static arrays
|
// .Item2 = force/weapon static arrays
|
||||||
var mapStaticContainers = StaticLootProcessor.CreateStaticWeaponsAndStaticForcedContainers(dataDump);
|
var mapStaticContainers =
|
||||||
|
_staticContainersProcessor.CreateStaticWeaponsAndForcedContainers(dataDump);
|
||||||
|
|
||||||
var newStaticWeapons = mapStaticContainers.Item2.StaticWeapons.Where(x => !mapStaticLoot.StaticWeapons.Exists(y => y.Id == x.Id));
|
var newStaticWeapons = mapStaticContainers.Item2.StaticWeapons.Where(x =>
|
||||||
var newStaticForced = mapStaticContainers.Item2.StaticForced.Where(x => !mapStaticLoot.StaticForced.Exists(y => y.ContainerId == x.ContainerId));
|
!mapStaticLoot.StaticWeapons.Exists(y => y.Id == x.Id));
|
||||||
|
var newStaticForced = mapStaticContainers.Item2.StaticForced.Where(x =>
|
||||||
|
!mapStaticLoot.StaticForced.Exists(y => y.ContainerId == x.ContainerId));
|
||||||
|
|
||||||
mapStaticLoot.StaticWeapons.AddRange(newStaticWeapons);
|
mapStaticLoot.StaticWeapons.AddRange(newStaticWeapons);
|
||||||
mapStaticLoot.StaticForced.AddRange(newStaticForced);
|
mapStaticLoot.StaticForced.AddRange(newStaticForced);
|
||||||
@ -116,8 +139,10 @@ public class MultithreadSteppedDumpProcessor : IDumpProcessor
|
|||||||
IncrementMapCounterDictionaryValue(mapDumpCounter, mapId);
|
IncrementMapCounterDictionaryValue(mapDumpCounter, mapId);
|
||||||
}
|
}
|
||||||
|
|
||||||
var containerIgnoreListExists = LootDumpProcessorContext.GetConfig().ContainerIgnoreList.TryGetValue(mapId, out string[]? ignoreListForMap);
|
var containerIgnoreListExists = LootDumpProcessorContext.GetConfig().ContainerIgnoreList
|
||||||
foreach (var dynamicStaticContainer in StaticLootProcessor.CreateDynamicStaticContainers(dataDump))
|
.TryGetValue(mapId, out string[]? ignoreListForMap);
|
||||||
|
foreach (var dynamicStaticContainer in _staticContainersProcessor.CreateDynamicStaticContainers(
|
||||||
|
dataDump))
|
||||||
{
|
{
|
||||||
lock (mapStaticContainersAggregatedLock)
|
lock (mapStaticContainersAggregatedLock)
|
||||||
{
|
{
|
||||||
@ -145,15 +170,17 @@ public class MultithreadSteppedDumpProcessor : IDumpProcessor
|
|||||||
LoggerFactory.GetInstance().Log("All static data processing threads finished", LogLevel.Info);
|
LoggerFactory.GetInstance().Log("All static data processing threads finished", LogLevel.Info);
|
||||||
// Aggregate and calculate the probability of a static container
|
// 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) // kv.Key = map name
|
||||||
}
|
}
|
||||||
|
).ToList()
|
||||||
).ToList()
|
).ToList()
|
||||||
).ToList().ForEach(kv => staticContainers[kv.Key].StaticContainers = kv.Value); // Hydrate staticContainers.StaticContainers
|
.ForEach(kv =>
|
||||||
|
staticContainers[kv.Key].StaticContainers = kv.Value); // Hydrate staticContainers.StaticContainers
|
||||||
|
|
||||||
// Static containers
|
// Static containers
|
||||||
output.Add(OutputFileType.StaticContainer, staticContainers);
|
output.Add(OutputFileType.StaticContainer, staticContainers);
|
||||||
@ -162,7 +189,7 @@ public class MultithreadSteppedDumpProcessor : IDumpProcessor
|
|||||||
// Ammo distribution
|
// Ammo distribution
|
||||||
output.Add(
|
output.Add(
|
||||||
OutputFileType.StaticAmmo,
|
OutputFileType.StaticAmmo,
|
||||||
StaticLootProcessor.CreateAmmoDistribution(dumpProcessData.ContainerCounts)
|
_ammoProcessor.CreateAmmoDistribution(dumpProcessData.ContainerCounts)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (LoggerFactory.GetInstance().CanBeLogged(LogLevel.Info))
|
if (LoggerFactory.GetInstance().CanBeLogged(LogLevel.Info))
|
||||||
@ -170,7 +197,7 @@ public class MultithreadSteppedDumpProcessor : IDumpProcessor
|
|||||||
// Static loot distribution
|
// Static loot distribution
|
||||||
output.Add(
|
output.Add(
|
||||||
OutputFileType.StaticLoot,
|
OutputFileType.StaticLoot,
|
||||||
StaticLootProcessor.CreateStaticLootDistribution(dumpProcessData.ContainerCounts, staticContainers)
|
_staticLootProcessor.CreateStaticLootDistribution(dumpProcessData.ContainerCounts, staticContainers)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (LoggerFactory.GetInstance().CanBeLogged(LogLevel.Info))
|
if (LoggerFactory.GetInstance().CanBeLogged(LogLevel.Info))
|
||||||
@ -196,9 +223,9 @@ public class MultithreadSteppedDumpProcessor : IDumpProcessor
|
|||||||
private static bool DumpWasMadeAfterConfigThresholdDate(PartialData dataDump)
|
private static bool DumpWasMadeAfterConfigThresholdDate(PartialData dataDump)
|
||||||
{
|
{
|
||||||
return FileDateParser.TryParseFileDate(dataDump.BasicInfo.FileName, out var fileDate) &&
|
return FileDateParser.TryParseFileDate(dataDump.BasicInfo.FileName, out var fileDate) &&
|
||||||
fileDate.HasValue &&
|
fileDate.HasValue &&
|
||||||
fileDate.Value > LootDumpProcessorContext.GetConfig().DumpProcessorConfig
|
fileDate.Value > LootDumpProcessorContext.GetConfig().DumpProcessorConfig
|
||||||
.SpawnContainerChanceIncludeAfterDate;
|
.SpawnContainerChanceIncludeAfterDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void IncrementMapCounterDictionaryValue(Dictionary<string, int> mapDumpCounter, string mapName)
|
private static void IncrementMapCounterDictionaryValue(Dictionary<string, int> mapDumpCounter, string mapName)
|
||||||
@ -210,7 +237,8 @@ public class MultithreadSteppedDumpProcessor : IDumpProcessor
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static double GetStaticContainerProbability(string mapName, KeyValuePair<Template, int> td, Dictionary<string, int> mapDumpCounter)
|
private static double GetStaticContainerProbability(string mapName, KeyValuePair<Template, int> td,
|
||||||
|
Dictionary<string, int> mapDumpCounter)
|
||||||
{
|
{
|
||||||
return Math.Round((double)((decimal)td.Value / (decimal)mapDumpCounter[mapName]), 2);
|
return Math.Round((double)((decimal)td.Value / (decimal)mapDumpCounter[mapName]), 2);
|
||||||
}
|
}
|
||||||
@ -255,7 +283,7 @@ public class MultithreadSteppedDumpProcessor : IDumpProcessor
|
|||||||
dumpProcessData.LooseLootCounts.Add(mapName, looseLootCounts.GetKey());
|
dumpProcessData.LooseLootCounts.Add(mapName, looseLootCounts.GetKey());
|
||||||
|
|
||||||
BlockingCollection<PartialData> _partialDataToProcess = new();
|
BlockingCollection<PartialData> _partialDataToProcess = new();
|
||||||
|
|
||||||
// add the items to the queue
|
// add the items to the queue
|
||||||
foreach (var partialData in partialFileMetaData)
|
foreach (var partialData in partialFileMetaData)
|
||||||
{
|
{
|
||||||
@ -286,7 +314,8 @@ public class MultithreadSteppedDumpProcessor : IDumpProcessor
|
|||||||
{
|
{
|
||||||
if (!dumpProcessData.ContainerCounts.ContainsKey(mapName))
|
if (!dumpProcessData.ContainerCounts.ContainsKey(mapName))
|
||||||
{
|
{
|
||||||
dumpProcessData.ContainerCounts.Add(mapName, dumpData.Containers);
|
dumpProcessData.ContainerCounts.Add(mapName,
|
||||||
|
dumpData.Containers.ToList());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -336,7 +365,8 @@ public class MultithreadSteppedDumpProcessor : IDumpProcessor
|
|||||||
|
|
||||||
lock (lockObjectCounts)
|
lock (lockObjectCounts)
|
||||||
{
|
{
|
||||||
looseLootCounts.MapSpawnpointCount.Add(dumpData.LooseLoot.MapSpawnpointCount);
|
looseLootCounts.MapSpawnpointCount.Add(
|
||||||
|
dumpData.LooseLoot.MapSpawnpointCount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
|
@ -1,12 +1,15 @@
|
|||||||
using LootDumpProcessor.Logger;
|
using LootDumpProcessor.Logger;
|
||||||
using LootDumpProcessor.Model;
|
using LootDumpProcessor.Model;
|
||||||
using LootDumpProcessor.Model.Processing;
|
using LootDumpProcessor.Model.Processing;
|
||||||
|
using LootDumpProcessor.Process.Processor.v2.StaticLootProcessor;
|
||||||
using LootDumpProcessor.Storage;
|
using LootDumpProcessor.Storage;
|
||||||
|
|
||||||
namespace LootDumpProcessor.Process.Processor.FileProcessor;
|
namespace LootDumpProcessor.Process.Processor.FileProcessor;
|
||||||
|
|
||||||
public class FileProcessor : IFileProcessor
|
public class FileProcessor(IStaticLootProcessor staticLootProcessor) : IFileProcessor
|
||||||
{
|
{
|
||||||
|
private readonly IStaticLootProcessor _staticLootProcessor = staticLootProcessor ?? throw new ArgumentNullException(nameof(staticLootProcessor));
|
||||||
|
|
||||||
public PartialData Process(BasicInfo parsedData)
|
public PartialData Process(BasicInfo parsedData)
|
||||||
{
|
{
|
||||||
if (LoggerFactory.GetInstance().CanBeLogged(LogLevel.Debug))
|
if (LoggerFactory.GetInstance().CanBeLogged(LogLevel.Debug))
|
||||||
@ -43,10 +46,11 @@ public class FileProcessor : IFileProcessor
|
|||||||
$"Cached not found for {string.Join("/", dumpData.GetKey().GetLookupIndex())} processing.",
|
$"Cached not found for {string.Join("/", dumpData.GetKey().GetLookupIndex())} processing.",
|
||||||
LogLevel.Debug
|
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))
|
if (LoggerFactory.GetInstance().CanBeLogged(LogLevel.Debug))
|
||||||
LoggerFactory.GetInstance().Log($"File {parsedData.FileName} finished processing!", LogLevel.Debug);
|
LoggerFactory.GetInstance().Log($"File {parsedData.FileName} finished processing!", LogLevel.Debug);
|
||||||
return data;
|
return data;
|
||||||
|
@ -1,13 +0,0 @@
|
|||||||
namespace LootDumpProcessor.Process.Processor.FileProcessor;
|
|
||||||
|
|
||||||
public static class FileProcessorFactory
|
|
||||||
{
|
|
||||||
private static IFileProcessor? _fileProcessor;
|
|
||||||
public static IFileProcessor GetInstance()
|
|
||||||
{
|
|
||||||
// TODO: implement actual factory someday
|
|
||||||
if (_fileProcessor == null)
|
|
||||||
_fileProcessor = new FileProcessor();
|
|
||||||
return _fileProcessor;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,281 +0,0 @@
|
|||||||
using LootDumpProcessor.Model;
|
|
||||||
using LootDumpProcessor.Model.Input;
|
|
||||||
using LootDumpProcessor.Model.Output;
|
|
||||||
using LootDumpProcessor.Model.Output.StaticContainer;
|
|
||||||
using LootDumpProcessor.Model.Processing;
|
|
||||||
using LootDumpProcessor.Utils;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace LootDumpProcessor.Process.Processor;
|
|
||||||
|
|
||||||
public static class StaticLootProcessor
|
|
||||||
{
|
|
||||||
public static List<PreProcessedStaticLoot> PreProcessStaticLoot(List<Template> staticloot)
|
|
||||||
{
|
|
||||||
var containers = new List<PreProcessedStaticLoot>();
|
|
||||||
foreach (var lootSpawnPosition in staticloot)
|
|
||||||
{
|
|
||||||
var tpl = lootSpawnPosition.Items[0].Tpl;
|
|
||||||
if (!LootDumpProcessorContext.GetStaticWeaponIds().Contains(tpl))
|
|
||||||
{
|
|
||||||
// Only add non-weapon static containers
|
|
||||||
containers.Add(new PreProcessedStaticLoot
|
|
||||||
{
|
|
||||||
Type = tpl,
|
|
||||||
ContainerId = lootSpawnPosition.Items[0].Id,
|
|
||||||
Items = lootSpawnPosition.Items.Skip(1).ToList()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return containers;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Tuple<string, MapStaticLoot> CreateStaticWeaponsAndStaticForcedContainers(RootData rawMapDump)
|
|
||||||
{
|
|
||||||
var mapName = rawMapDump.Data.LocationLoot.Name;
|
|
||||||
var mapId = rawMapDump.Data.LocationLoot.Id.ToLower();
|
|
||||||
var staticLootPositions = (from li in rawMapDump.Data.LocationLoot.Loot
|
|
||||||
where li.IsContainer ?? false
|
|
||||||
select li).ToList();
|
|
||||||
var staticWeapons = new List<Template>();
|
|
||||||
staticLootPositions = staticLootPositions.OrderBy(x => x.Id).ToList();
|
|
||||||
foreach (var staticLootPosition in staticLootPositions)
|
|
||||||
{
|
|
||||||
if (LootDumpProcessorContext.GetStaticWeaponIds().Contains(staticLootPosition.Items[0].Tpl))
|
|
||||||
{
|
|
||||||
staticWeapons.Add(ProcessorUtil.Copy(staticLootPosition));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var forcedStaticItems = LootDumpProcessorContext.GetForcedItems().ContainsKey(mapId)
|
|
||||||
? LootDumpProcessorContext.GetForcedItems()[mapId]
|
|
||||||
: new List<StaticForced>();
|
|
||||||
|
|
||||||
var mapStaticData = new MapStaticLoot
|
|
||||||
{
|
|
||||||
StaticWeapons = staticWeapons,
|
|
||||||
StaticForced = forcedStaticItems
|
|
||||||
};
|
|
||||||
return Tuple.Create(mapId, mapStaticData);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<Template> CreateDynamicStaticContainers(RootData rawMapDump)
|
|
||||||
{
|
|
||||||
var data = (from li in rawMapDump.Data.LocationLoot.Loot
|
|
||||||
where (li.IsContainer ?? false) && (!LootDumpProcessorContext.GetStaticWeaponIds().Contains(li.Items[0].Tpl))
|
|
||||||
select li).ToList();
|
|
||||||
|
|
||||||
foreach (var item in data)
|
|
||||||
{
|
|
||||||
// remove all but first item from containers items
|
|
||||||
item.Items = new List<Item> { item.Items[0] };
|
|
||||||
}
|
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="container_counts"></param>
|
|
||||||
/// <returns>key = mapid / </returns>
|
|
||||||
public static Dictionary<string, Dictionary<string, List<AmmoDistribution>>> CreateAmmoDistribution(
|
|
||||||
Dictionary<string, List<PreProcessedStaticLoot>> container_counts
|
|
||||||
)
|
|
||||||
{
|
|
||||||
var allMapsAmmoDistro = new Dictionary<string, Dictionary<string, List<AmmoDistribution>>>();
|
|
||||||
foreach (var mapAndContainers in container_counts)
|
|
||||||
{
|
|
||||||
var mapid = mapAndContainers.Key;
|
|
||||||
var containers = mapAndContainers.Value;
|
|
||||||
|
|
||||||
|
|
||||||
var ammo = new List<string>();
|
|
||||||
foreach (var ci in containers)
|
|
||||||
{
|
|
||||||
ammo.AddRange(from item in ci.Items
|
|
||||||
where LootDumpProcessorContext.GetTarkovItems().IsBaseClass(item.Tpl, BaseClasses.Ammo)
|
|
||||||
select item.Tpl);
|
|
||||||
}
|
|
||||||
|
|
||||||
var ammo_counts = new List<CaliberTemplateCount>();
|
|
||||||
ammo_counts.AddRange(
|
|
||||||
ammo.GroupBy(a => a)
|
|
||||||
.Select(g => new CaliberTemplateCount
|
|
||||||
{
|
|
||||||
Caliber = LootDumpProcessorContext.GetTarkovItems().AmmoCaliber(g.Key),
|
|
||||||
Template = g.Key,
|
|
||||||
Count = g.Count()
|
|
||||||
})
|
|
||||||
);
|
|
||||||
ammo_counts = ammo_counts.OrderBy(x => x.Caliber).ToList();
|
|
||||||
var ammo_distribution = new Dictionary<string, List<AmmoDistribution>>();
|
|
||||||
foreach (var _tup_3 in ammo_counts.GroupBy(x => x.Caliber))
|
|
||||||
{
|
|
||||||
var k = _tup_3.Key;
|
|
||||||
var g = _tup_3.ToList();
|
|
||||||
ammo_distribution[k] = (from gi in g
|
|
||||||
select new AmmoDistribution
|
|
||||||
{
|
|
||||||
Tpl = gi.Template,
|
|
||||||
RelativeProbability = gi.Count
|
|
||||||
}).ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
allMapsAmmoDistro.TryAdd(mapid, ammo_distribution);
|
|
||||||
}
|
|
||||||
|
|
||||||
return allMapsAmmoDistro;
|
|
||||||
|
|
||||||
//var ammo = new List<string>();
|
|
||||||
//foreach (var ci in container_counts)
|
|
||||||
//{
|
|
||||||
// ammo.AddRange(from item in ci.Items
|
|
||||||
// where LootDumpProcessorContext.GetTarkovItems().IsBaseClass(item.Tpl, BaseClasses.Ammo)
|
|
||||||
// select item.Tpl);
|
|
||||||
//}
|
|
||||||
|
|
||||||
//var ammo_counts = new List<CaliberTemplateCount>();
|
|
||||||
//ammo_counts.AddRange(
|
|
||||||
// ammo.GroupBy(a => a)
|
|
||||||
// .Select(g => new CaliberTemplateCount
|
|
||||||
// {
|
|
||||||
// Caliber = LootDumpProcessorContext.GetTarkovItems().AmmoCaliber(g.Key),
|
|
||||||
// Template = g.Key,
|
|
||||||
// Count = g.Count()
|
|
||||||
// })
|
|
||||||
//);
|
|
||||||
//ammo_counts = ammo_counts.OrderBy(x => x.Caliber).ToList();
|
|
||||||
//var ammo_distribution = new Dictionary<string, List<AmmoDistribution>>();
|
|
||||||
//foreach (var _tup_3 in ammo_counts.GroupBy(x => x.Caliber))
|
|
||||||
//{
|
|
||||||
// var k = _tup_3.Key;
|
|
||||||
// var g = _tup_3.ToList();
|
|
||||||
// ammo_distribution[k] = (from gi in g
|
|
||||||
// select new AmmoDistribution
|
|
||||||
// {
|
|
||||||
// Tpl = gi.Template,
|
|
||||||
// RelativeProbability = gi.Count
|
|
||||||
// }).ToList();
|
|
||||||
//}
|
|
||||||
|
|
||||||
//return ammo_distribution;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Dict key = map,
|
|
||||||
/// value = sub dit:
|
|
||||||
/// key = container Ids
|
|
||||||
/// value = items + counts
|
|
||||||
/// </summary>
|
|
||||||
public static Dictionary<string, Dictionary<string, StaticItemDistribution>> CreateStaticLootDistribution(
|
|
||||||
Dictionary<string, List<PreProcessedStaticLoot>> container_counts,
|
|
||||||
Dictionary<string, MapStaticLoot> staticContainers)
|
|
||||||
{
|
|
||||||
var allMapsStaticLootDisto = new Dictionary< string, Dictionary<string, StaticItemDistribution>>();
|
|
||||||
// Iterate over each map we have containers for
|
|
||||||
foreach (var mapContainersKvp in container_counts)
|
|
||||||
{
|
|
||||||
var mapName = mapContainersKvp.Key;
|
|
||||||
var containers = mapContainersKvp.Value;
|
|
||||||
|
|
||||||
var static_loot_distribution = new Dictionary<string, StaticItemDistribution>();
|
|
||||||
var uniqueContainerTypeIds = Enumerable.Distinct((from ci in containers
|
|
||||||
select ci.Type).ToList());
|
|
||||||
|
|
||||||
foreach (var typeId in uniqueContainerTypeIds)
|
|
||||||
{
|
|
||||||
var container_counts_selected = (from ci in containers
|
|
||||||
where ci.Type == typeId
|
|
||||||
select ci).ToList();
|
|
||||||
|
|
||||||
// Get array of all times a count of items was found in container
|
|
||||||
List<int> itemCountsInContainer = GetCountOfItemsInContainer(container_counts_selected);
|
|
||||||
|
|
||||||
// Create structure to hold item count + weight that it will be picked
|
|
||||||
// Group same counts together
|
|
||||||
static_loot_distribution[typeId] = new StaticItemDistribution();
|
|
||||||
static_loot_distribution[typeId].ItemCountDistribution = itemCountsInContainer.GroupBy(i => i)
|
|
||||||
.Select(g => new ItemCountDistribution
|
|
||||||
{
|
|
||||||
Count = g.Key,
|
|
||||||
RelativeProbability = g.Count()
|
|
||||||
}).ToList();
|
|
||||||
|
|
||||||
static_loot_distribution[typeId].ItemDistribution = CreateItemDistribution(container_counts_selected);
|
|
||||||
}
|
|
||||||
// Key = containers tpl, value = items + count weights
|
|
||||||
allMapsStaticLootDisto.TryAdd(mapName, static_loot_distribution);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return allMapsStaticLootDisto;
|
|
||||||
|
|
||||||
//var static_loot_distribution = new Dictionary<string, StaticItemDistribution>();
|
|
||||||
//var uniqueContainerTypeIds = Enumerable.Distinct((from ci in container_counts
|
|
||||||
// select ci.Type).ToList());
|
|
||||||
|
|
||||||
//foreach (var typeId in uniqueContainerTypeIds)
|
|
||||||
//{
|
|
||||||
// var container_counts_selected = (from ci in container_counts
|
|
||||||
// where ci.Type == typeId
|
|
||||||
// select ci).ToList();
|
|
||||||
|
|
||||||
// // Get array of all times a count of items was found in container
|
|
||||||
// List<int> itemCountsInContainer = GetCountOfItemsInContainer(container_counts_selected);
|
|
||||||
|
|
||||||
// // Create structure to hold item count + weight that it will be picked
|
|
||||||
// // Group same counts together
|
|
||||||
// static_loot_distribution[typeId] = new StaticItemDistribution();
|
|
||||||
// static_loot_distribution[typeId].ItemCountDistribution = itemCountsInContainer.GroupBy(i => i)
|
|
||||||
// .Select(g => new ItemCountDistribution
|
|
||||||
// {
|
|
||||||
// Count = g.Key,
|
|
||||||
// RelativeProbability = g.Count()
|
|
||||||
// }).ToList();
|
|
||||||
|
|
||||||
// static_loot_distribution[typeId].ItemDistribution = CreateItemDistribution(container_counts_selected);
|
|
||||||
//}
|
|
||||||
//// Key = containers tpl, value = items + count weights
|
|
||||||
//return static_loot_distribution;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static List<StaticDistribution> CreateItemDistribution(List<PreProcessedStaticLoot> container_counts_selected)
|
|
||||||
{
|
|
||||||
// TODO: Change for different algo that splits items per parent once parentid = containerid, then compose
|
|
||||||
// TODO: key and finally create distribution based on composed Id instead
|
|
||||||
var itemsHitCounts = new Dictionary<string, int>();
|
|
||||||
foreach (var ci in container_counts_selected)
|
|
||||||
{
|
|
||||||
foreach (var cii in ci.Items.Where(cii => cii.ParentId == ci.ContainerId))
|
|
||||||
{
|
|
||||||
if (itemsHitCounts.ContainsKey(cii.Tpl))
|
|
||||||
itemsHitCounts[cii.Tpl] += 1;
|
|
||||||
else
|
|
||||||
itemsHitCounts[cii.Tpl] = 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WIll create array of objects that have a tpl + relative probability weight value
|
|
||||||
return itemsHitCounts.Select(v => new StaticDistribution
|
|
||||||
{
|
|
||||||
Tpl = v.Key,
|
|
||||||
RelativeProbability = v.Value
|
|
||||||
}).ToList();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static List<int> GetCountOfItemsInContainer(List<PreProcessedStaticLoot> container_counts_selected)
|
|
||||||
{
|
|
||||||
var itemCountsInContainer = new List<int>();
|
|
||||||
foreach (var containerWithItems in container_counts_selected)
|
|
||||||
{
|
|
||||||
// Only count item if its parent is the container, only root items are counted (not mod/attachment items)
|
|
||||||
itemCountsInContainer.Add((from cii in containerWithItems.Items
|
|
||||||
where cii.ParentId == containerWithItems.ContainerId
|
|
||||||
select cii).ToList().Count);
|
|
||||||
}
|
|
||||||
|
|
||||||
return itemCountsInContainer;
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,56 @@
|
|||||||
|
using LootDumpProcessor.Model.Output;
|
||||||
|
using LootDumpProcessor.Model.Processing;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace LootDumpProcessor.Process.Processor.v2.AmmoProcessor
|
||||||
|
{
|
||||||
|
public class AmmoProcessor(ILogger<AmmoProcessor> logger) : IAmmoProcessor
|
||||||
|
{
|
||||||
|
private readonly ILogger<AmmoProcessor> _logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
|
|
||||||
|
public IReadOnlyDictionary<string, IReadOnlyDictionary<string, List<AmmoDistribution>>> CreateAmmoDistribution(
|
||||||
|
IReadOnlyDictionary<string, List<PreProcessedStaticLoot>> containerCounts)
|
||||||
|
{
|
||||||
|
var allMapsAmmoDistribution = new Dictionary<string, IReadOnlyDictionary<string, List<AmmoDistribution>>>();
|
||||||
|
|
||||||
|
foreach (var mapEntry in containerCounts)
|
||||||
|
{
|
||||||
|
var mapId = mapEntry.Key;
|
||||||
|
var containers = mapEntry.Value;
|
||||||
|
|
||||||
|
var ammoTemplates = containers
|
||||||
|
.SelectMany(container => container.Items)
|
||||||
|
.Where(item => LootDumpProcessorContext.GetTarkovItems().IsBaseClass(item.Tpl, BaseClasses.Ammo))
|
||||||
|
.Select(item => item.Tpl)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
var caliberTemplateCounts = ammoTemplates
|
||||||
|
.GroupBy(tpl => tpl)
|
||||||
|
.Select(group => new CaliberTemplateCount
|
||||||
|
{
|
||||||
|
Caliber = LootDumpProcessorContext.GetTarkovItems().AmmoCaliber(group.Key),
|
||||||
|
Template = group.Key,
|
||||||
|
Count = group.Count()
|
||||||
|
})
|
||||||
|
.OrderBy(ctc => ctc.Caliber)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
var ammoDistribution = caliberTemplateCounts
|
||||||
|
.GroupBy(ctc => ctc.Caliber)
|
||||||
|
.ToDictionary(
|
||||||
|
group => group.Key,
|
||||||
|
group => group.Select(ctc => new AmmoDistribution
|
||||||
|
{
|
||||||
|
Tpl = ctc.Template,
|
||||||
|
RelativeProbability = ctc.Count
|
||||||
|
}).ToList()
|
||||||
|
);
|
||||||
|
|
||||||
|
allMapsAmmoDistribution[mapId] = ammoDistribution;
|
||||||
|
_logger.LogInformation("Created ammo distribution for Map {MapId}.", mapId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return allMapsAmmoDistribution;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,10 @@
|
|||||||
|
using LootDumpProcessor.Model.Output;
|
||||||
|
using LootDumpProcessor.Model.Processing;
|
||||||
|
|
||||||
|
namespace LootDumpProcessor.Process.Processor.v2.AmmoProcessor;
|
||||||
|
|
||||||
|
public interface IAmmoProcessor
|
||||||
|
{
|
||||||
|
IReadOnlyDictionary<string, IReadOnlyDictionary<string, List<AmmoDistribution>>> CreateAmmoDistribution(
|
||||||
|
IReadOnlyDictionary<string, List<PreProcessedStaticLoot>> containerCounts);
|
||||||
|
}
|
@ -0,0 +1,11 @@
|
|||||||
|
using LootDumpProcessor.Model;
|
||||||
|
using LootDumpProcessor.Model.Input;
|
||||||
|
using LootDumpProcessor.Model.Output.StaticContainer;
|
||||||
|
|
||||||
|
namespace LootDumpProcessor.Process.Processor.v2.StaticContainersProcessor;
|
||||||
|
|
||||||
|
public interface IStaticContainersProcessor
|
||||||
|
{
|
||||||
|
(string, MapStaticLoot) CreateStaticWeaponsAndForcedContainers(RootData rawMapDump);
|
||||||
|
IReadOnlyList<Template> CreateDynamicStaticContainers(RootData rawMapDump);
|
||||||
|
}
|
@ -0,0 +1,79 @@
|
|||||||
|
using LootDumpProcessor.Model;
|
||||||
|
using LootDumpProcessor.Model.Input;
|
||||||
|
using LootDumpProcessor.Model.Output.StaticContainer;
|
||||||
|
using LootDumpProcessor.Utils;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace LootDumpProcessor.Process.Processor.v2.StaticContainersProcessor;
|
||||||
|
|
||||||
|
public class StaticContainersProcessor(ILogger<StaticContainersProcessor> logger)
|
||||||
|
: IStaticContainersProcessor
|
||||||
|
{
|
||||||
|
private readonly ILogger<StaticContainersProcessor> _logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
|
|
||||||
|
public (string, MapStaticLoot) CreateStaticWeaponsAndForcedContainers(RootData rawMapDump)
|
||||||
|
{
|
||||||
|
var locationLoot = rawMapDump.Data.LocationLoot;
|
||||||
|
var mapId = locationLoot.Id.ToLower();
|
||||||
|
var staticLootPositions = locationLoot.Loot
|
||||||
|
.Where(loot => loot.IsContainer.GetValueOrDefault())
|
||||||
|
.OrderBy(loot => loot.Id)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
var staticWeapons = new List<Template>();
|
||||||
|
|
||||||
|
foreach (var lootPosition in staticLootPositions)
|
||||||
|
{
|
||||||
|
if (lootPosition.Items == null || lootPosition.Items.Count == 0)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Loot position with ID {LootId} has no items.", lootPosition.Id);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var firstItemTpl = lootPosition.Items[0].Tpl;
|
||||||
|
|
||||||
|
if (!LootDumpProcessorContext.GetStaticWeaponIds().Contains(firstItemTpl)) continue;
|
||||||
|
|
||||||
|
var copiedLoot = ProcessorUtil.Copy(lootPosition);
|
||||||
|
staticWeapons.Add(copiedLoot);
|
||||||
|
_logger.LogDebug("Added static weapon with ID {WeaponId} to Map {MapId}.", copiedLoot.Id, mapId);
|
||||||
|
}
|
||||||
|
|
||||||
|
var forcedStaticItems = LootDumpProcessorContext.GetForcedItems().TryGetValue(mapId, out var forcedItems)
|
||||||
|
? forcedItems
|
||||||
|
: new List<StaticForced>();
|
||||||
|
|
||||||
|
var mapStaticLoot = new MapStaticLoot
|
||||||
|
{
|
||||||
|
StaticWeapons = staticWeapons,
|
||||||
|
StaticForced = forcedStaticItems
|
||||||
|
};
|
||||||
|
|
||||||
|
_logger.LogInformation("Created static weapons and forced containers for Map {MapId}.", mapId);
|
||||||
|
return (mapId, mapStaticLoot);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyList<Template> CreateDynamicStaticContainers(RootData rawMapDump)
|
||||||
|
{
|
||||||
|
var dynamicContainers = rawMapDump.Data.LocationLoot.Loot
|
||||||
|
.Where(loot => loot.IsContainer.GetValueOrDefault() &&
|
||||||
|
!LootDumpProcessorContext.GetStaticWeaponIds().Contains(loot.Items.FirstOrDefault()?.Tpl))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
foreach (var container in dynamicContainers)
|
||||||
|
{
|
||||||
|
if (container.Items == null || container.Items.Count == 0)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Dynamic container with ID {ContainerId} has no items.", container.Id);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var firstItem = container.Items.First();
|
||||||
|
container.Items = [firstItem];
|
||||||
|
_logger.LogDebug("Retained only the first item in dynamic container with ID {ContainerId}.", container.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogInformation("Created {Count} dynamic static containers.", dynamicContainers.Count);
|
||||||
|
return dynamicContainers;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
using LootDumpProcessor.Model;
|
||||||
|
using LootDumpProcessor.Model.Output;
|
||||||
|
using LootDumpProcessor.Model.Output.StaticContainer;
|
||||||
|
using LootDumpProcessor.Model.Processing;
|
||||||
|
|
||||||
|
namespace LootDumpProcessor.Process.Processor.v2.StaticLootProcessor;
|
||||||
|
|
||||||
|
public interface IStaticLootProcessor
|
||||||
|
{
|
||||||
|
IReadOnlyList<PreProcessedStaticLoot> PreProcessStaticLoot(IReadOnlyList<Template> staticLoot);
|
||||||
|
|
||||||
|
IReadOnlyDictionary<string, IReadOnlyDictionary<string, StaticItemDistribution>> CreateStaticLootDistribution(
|
||||||
|
IReadOnlyDictionary<string, List<PreProcessedStaticLoot>> containerCounts,
|
||||||
|
IReadOnlyDictionary<string, MapStaticLoot> staticContainers);
|
||||||
|
}
|
@ -0,0 +1,117 @@
|
|||||||
|
using LootDumpProcessor.Model;
|
||||||
|
using LootDumpProcessor.Model.Output;
|
||||||
|
using LootDumpProcessor.Model.Output.StaticContainer;
|
||||||
|
using LootDumpProcessor.Model.Processing;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace LootDumpProcessor.Process.Processor.v2.StaticLootProcessor;
|
||||||
|
|
||||||
|
public class StaticLootProcessor(ILogger<StaticLootProcessor> logger) : IStaticLootProcessor
|
||||||
|
{
|
||||||
|
private readonly ILogger<StaticLootProcessor> _logger =
|
||||||
|
logger ?? throw new ArgumentNullException(nameof(logger));
|
||||||
|
|
||||||
|
public IReadOnlyList<PreProcessedStaticLoot> PreProcessStaticLoot(IReadOnlyList<Template> staticLoot)
|
||||||
|
{
|
||||||
|
var nonWeaponContainers = new List<PreProcessedStaticLoot>();
|
||||||
|
|
||||||
|
foreach (var lootSpawn in staticLoot)
|
||||||
|
{
|
||||||
|
if (lootSpawn.Items == null || lootSpawn.Items.Count == 0)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Loot spawn position with ID {LootId} has no items.", lootSpawn.Id);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var firstItemTpl = lootSpawn.Items[0].Tpl;
|
||||||
|
|
||||||
|
if (!LootDumpProcessorContext.GetStaticWeaponIds().Contains(firstItemTpl))
|
||||||
|
{
|
||||||
|
nonWeaponContainers.Add(new PreProcessedStaticLoot
|
||||||
|
{
|
||||||
|
Type = firstItemTpl,
|
||||||
|
ContainerId = lootSpawn.Items[0].Id,
|
||||||
|
Items = lootSpawn.Items.Skip(1).ToList()
|
||||||
|
});
|
||||||
|
|
||||||
|
_logger.LogDebug("Added non-weapon container with ID {ContainerId} and Type {Type}.",
|
||||||
|
lootSpawn.Items[0].Id, firstItemTpl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_logger.LogInformation("Preprocessed {Count} static loot containers.", nonWeaponContainers.Count);
|
||||||
|
return nonWeaponContainers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyDictionary<string, IReadOnlyDictionary<string, StaticItemDistribution>>
|
||||||
|
CreateStaticLootDistribution(
|
||||||
|
IReadOnlyDictionary<string, List<PreProcessedStaticLoot>> containerCounts,
|
||||||
|
IReadOnlyDictionary<string, MapStaticLoot> staticContainers)
|
||||||
|
{
|
||||||
|
var allMapsStaticLootDistribution =
|
||||||
|
new Dictionary<string, IReadOnlyDictionary<string, StaticItemDistribution>>();
|
||||||
|
|
||||||
|
foreach (var (mapName, containers) in containerCounts)
|
||||||
|
{
|
||||||
|
var staticLootDistribution = new Dictionary<string, StaticItemDistribution>();
|
||||||
|
var uniqueContainerTypes = containers.Select(container => container.Type).Distinct();
|
||||||
|
|
||||||
|
foreach (var containerType in uniqueContainerTypes)
|
||||||
|
{
|
||||||
|
var selectedContainers = containers.Where(container => container.Type == containerType).ToArray();
|
||||||
|
var itemCounts = GetItemCountsInContainers(selectedContainers);
|
||||||
|
var itemDistribution = GenerateItemDistribution(selectedContainers);
|
||||||
|
|
||||||
|
staticLootDistribution[containerType] = new StaticItemDistribution
|
||||||
|
{
|
||||||
|
ItemCountDistribution = itemCounts
|
||||||
|
.GroupBy(count => count)
|
||||||
|
.Select(group => new ItemCountDistribution
|
||||||
|
{
|
||||||
|
Count = group.Key,
|
||||||
|
RelativeProbability = group.Count()
|
||||||
|
})
|
||||||
|
.ToList(),
|
||||||
|
ItemDistribution = itemDistribution
|
||||||
|
};
|
||||||
|
|
||||||
|
_logger.LogDebug(
|
||||||
|
"Processed static loot distribution for ContainerType {ContainerType} in Map {MapName}.",
|
||||||
|
containerType, mapName);
|
||||||
|
}
|
||||||
|
|
||||||
|
allMapsStaticLootDistribution[mapName] = staticLootDistribution;
|
||||||
|
_logger.LogInformation("Created static loot distribution for Map {MapName}.", mapName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return allMapsStaticLootDistribution;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IReadOnlyList<int> GetItemCountsInContainers(IReadOnlyList<PreProcessedStaticLoot> selectedContainers)
|
||||||
|
{
|
||||||
|
return selectedContainers
|
||||||
|
.Select(container => container.Items.Count(item => item.ParentId == container.ContainerId))
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IReadOnlyList<StaticDistribution> GenerateItemDistribution(
|
||||||
|
IReadOnlyList<PreProcessedStaticLoot> selectedContainers)
|
||||||
|
{
|
||||||
|
var itemHitCounts = new Dictionary<string, int>();
|
||||||
|
|
||||||
|
foreach (var container in selectedContainers)
|
||||||
|
{
|
||||||
|
foreach (var item in container.Items.Where(item => item.ParentId == container.ContainerId))
|
||||||
|
{
|
||||||
|
if (!itemHitCounts.TryAdd(item.Tpl, 1))
|
||||||
|
itemHitCounts[item.Tpl]++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return itemHitCounts.Select(kv => new StaticDistribution
|
||||||
|
{
|
||||||
|
Tpl = kv.Key,
|
||||||
|
RelativeProbability = kv.Value
|
||||||
|
}).ToArray();
|
||||||
|
}
|
||||||
|
}
|
@ -16,8 +16,14 @@ using LootDumpProcessor.Utils;
|
|||||||
|
|
||||||
namespace LootDumpProcessor.Process;
|
namespace LootDumpProcessor.Process;
|
||||||
|
|
||||||
public class QueuePipeline : IPipeline
|
public class QueuePipeline(IFileProcessor fileProcessor, IDumpProcessor dumpProcessor) : IPipeline
|
||||||
{
|
{
|
||||||
|
private readonly IFileProcessor _fileProcessor =
|
||||||
|
fileProcessor ?? throw new ArgumentNullException(nameof(fileProcessor));
|
||||||
|
|
||||||
|
private readonly IDumpProcessor _dumpProcessor =
|
||||||
|
dumpProcessor ?? throw new ArgumentNullException(nameof(dumpProcessor));
|
||||||
|
|
||||||
private static readonly BlockingCollection<string> _filesToRename = new();
|
private static readonly BlockingCollection<string> _filesToRename = new();
|
||||||
private static readonly BlockingCollection<string> _filesToProcess = new();
|
private static readonly BlockingCollection<string> _filesToProcess = new();
|
||||||
private static readonly List<Task> Runners = new();
|
private static readonly List<Task> Runners = new();
|
||||||
@ -227,10 +233,9 @@ public class QueuePipeline : IPipeline
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var reader = IntakeReaderFactory.GetInstance();
|
var reader = IntakeReaderFactory.GetInstance();
|
||||||
var processor = FileProcessorFactory.GetInstance();
|
|
||||||
if (reader.Read(file, out var basicInfo))
|
if (reader.Read(file, out var basicInfo))
|
||||||
{
|
{
|
||||||
collector.Hold(processor.Process(basicInfo));
|
collector.Hold(_fileProcessor.Process(basicInfo));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
@ -269,8 +274,7 @@ public class QueuePipeline : IPipeline
|
|||||||
// Single writer instance to collect results
|
// Single writer instance to collect results
|
||||||
var writer = WriterFactory.GetInstance();
|
var writer = WriterFactory.GetInstance();
|
||||||
// Single collector instance to collect results
|
// Single collector instance to collect results
|
||||||
var dumpProcessor = DumpProcessorFactory.GetInstance();
|
writer.WriteAll(_dumpProcessor.ProcessDumps(collector.Retrieve()));
|
||||||
writer.WriteAll(dumpProcessor.ProcessDumps(collector.Retrieve()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ProcessFilesFromDumpsPerMap(int threads, ICollector collector, string mapName)
|
private void ProcessFilesFromDumpsPerMap(int threads, ICollector collector, string mapName)
|
||||||
@ -305,10 +309,9 @@ public class QueuePipeline : IPipeline
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var reader = IntakeReaderFactory.GetInstance();
|
var reader = IntakeReaderFactory.GetInstance();
|
||||||
var processor = FileProcessorFactory.GetInstance();
|
|
||||||
if (reader.Read(file, out var basicInfo))
|
if (reader.Read(file, out var basicInfo))
|
||||||
{
|
{
|
||||||
collector.Hold(processor.Process(basicInfo));
|
collector.Hold(_fileProcessor.Process(basicInfo));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
@ -347,8 +350,7 @@ public class QueuePipeline : IPipeline
|
|||||||
// Single writer instance to collect results
|
// Single writer instance to collect results
|
||||||
var writer = WriterFactory.GetInstance();
|
var writer = WriterFactory.GetInstance();
|
||||||
// Single collector instance to collect results
|
// Single collector instance to collect results
|
||||||
var dumpProcessor = DumpProcessorFactory.GetInstance();
|
writer.WriteAll(_dumpProcessor.ProcessDumps(collector.Retrieve()));
|
||||||
writer.WriteAll(dumpProcessor.ProcessDumps(collector.Retrieve()));
|
|
||||||
|
|
||||||
// clear collector and datastorage as we process per map now
|
// clear collector and datastorage as we process per map now
|
||||||
collector.Clear();
|
collector.Clear();
|
||||||
@ -413,7 +415,9 @@ public class QueuePipeline : IPipeline
|
|||||||
if (LoggerFactory.GetInstance().CanBeLogged(LogLevel.Info))
|
if (LoggerFactory.GetInstance().CanBeLogged(LogLevel.Info))
|
||||||
{
|
{
|
||||||
LoggerFactory.GetInstance()
|
LoggerFactory.GetInstance()
|
||||||
.Log($"one or more files are being processed. Waiting {LootDumpProcessorContext.GetConfig().ThreadPoolingTimeoutMs} ms", LogLevel.Info);
|
.Log(
|
||||||
|
$"one or more files are being processed. Waiting {LootDumpProcessorContext.GetConfig().ThreadPoolingTimeoutMs} ms",
|
||||||
|
LogLevel.Info);
|
||||||
}
|
}
|
||||||
|
|
||||||
Thread.Sleep(TimeSpan.FromMilliseconds(LootDumpProcessorContext.GetConfig().ThreadPoolingTimeoutMs));
|
Thread.Sleep(TimeSpan.FromMilliseconds(LootDumpProcessorContext.GetConfig().ThreadPoolingTimeoutMs));
|
||||||
|
@ -66,7 +66,7 @@ public class FileWriter : IWriter
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
case OutputFileType.StaticLoot:
|
case OutputFileType.StaticLoot:
|
||||||
var staticLootData = (Dictionary<string, Dictionary<string, StaticItemDistribution>>)data;
|
var staticLootData = (IReadOnlyDictionary<string, IReadOnlyDictionary<string, StaticItemDistribution>>)data;
|
||||||
foreach (var (key, value) in staticLootData)
|
foreach (var (key, value) in staticLootData)
|
||||||
{
|
{
|
||||||
if (!Directory.Exists($@"{_outputPath}\locations\{key}"))
|
if (!Directory.Exists($@"{_outputPath}\locations\{key}"))
|
||||||
@ -77,7 +77,7 @@ public class FileWriter : IWriter
|
|||||||
|
|
||||||
break;
|
break;
|
||||||
case OutputFileType.StaticAmmo:
|
case OutputFileType.StaticAmmo:
|
||||||
var staticAmmo = (Dictionary<string, Dictionary<string, List<AmmoDistribution>>>)data;
|
var staticAmmo = (IReadOnlyDictionary<string, IReadOnlyDictionary<string, List<AmmoDistribution>>>)data;
|
||||||
foreach (var (key, value) in staticAmmo)
|
foreach (var (key, value) in staticAmmo)
|
||||||
{
|
{
|
||||||
if (!Directory.Exists($@"{_outputPath}\locations\{key}"))
|
if (!Directory.Exists($@"{_outputPath}\locations\{key}"))
|
||||||
|
@ -1,13 +1,34 @@
|
|||||||
using LootDumpProcessor.Logger;
|
|
||||||
using LootDumpProcessor.Process;
|
using LootDumpProcessor.Process;
|
||||||
|
using LootDumpProcessor.Process.Processor.DumpProcessor;
|
||||||
|
using LootDumpProcessor.Process.Processor.FileProcessor;
|
||||||
|
using LootDumpProcessor.Process.Processor.v2.AmmoProcessor;
|
||||||
|
using LootDumpProcessor.Process.Processor.v2.StaticContainersProcessor;
|
||||||
|
using LootDumpProcessor.Process.Processor.v2.StaticLootProcessor;
|
||||||
using LootDumpProcessor.Storage;
|
using LootDumpProcessor.Storage;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using LoggerFactory = LootDumpProcessor.Logger.LoggerFactory;
|
||||||
|
|
||||||
namespace LootDumpProcessor;
|
namespace LootDumpProcessor;
|
||||||
|
|
||||||
public static class Program
|
public static class Program
|
||||||
{
|
{
|
||||||
public static void Main()
|
public static async Task Main()
|
||||||
{
|
{
|
||||||
|
var services = new ServiceCollection();
|
||||||
|
|
||||||
|
services.AddLogging(configure => configure.AddConsole());
|
||||||
|
services.AddTransient<IStaticLootProcessor, StaticLootProcessor>();
|
||||||
|
services.AddTransient<IStaticContainersProcessor, StaticContainersProcessor>();
|
||||||
|
services.AddTransient<IAmmoProcessor, AmmoProcessor>();
|
||||||
|
services.AddTransient<StaticLootProcessor>();
|
||||||
|
|
||||||
|
services.AddTransient<IFileProcessor, FileProcessor>();
|
||||||
|
services.AddTransient<IDumpProcessor, MultithreadSteppedDumpProcessor>();
|
||||||
|
services.AddTransient<IPipeline, QueuePipeline>();
|
||||||
|
|
||||||
|
await using var serviceProvider = services.BuildServiceProvider();
|
||||||
|
|
||||||
// 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
|
// Some loggers may need a startup and stop mechanism
|
||||||
@ -15,7 +36,8 @@ public static class Program
|
|||||||
// Setup Data storage
|
// Setup Data storage
|
||||||
DataStorageFactory.GetInstance().Setup();
|
DataStorageFactory.GetInstance().Setup();
|
||||||
// startup the pipeline
|
// startup the pipeline
|
||||||
PipelineFactory.GetInstance().DoProcess();
|
var pipeline = serviceProvider.GetRequiredService<IPipeline>();
|
||||||
|
pipeline.DoProcess();
|
||||||
// stop loggers at the end
|
// stop loggers at the end
|
||||||
LoggerFactory.GetInstance().Stop();
|
LoggerFactory.GetInstance().Stop();
|
||||||
Thread.Sleep(10000);
|
Thread.Sleep(10000);
|
||||||
|
@ -15,7 +15,8 @@ public class NetJsonSerializer : IJsonSerializer
|
|||||||
new NetJsonKeyConverter(),
|
new NetJsonKeyConverter(),
|
||||||
new JsonStringEnumConverter(),
|
new JsonStringEnumConverter(),
|
||||||
new NetDateTimeConverter()
|
new NetDateTimeConverter()
|
||||||
}
|
},
|
||||||
|
WriteIndented = true
|
||||||
};
|
};
|
||||||
|
|
||||||
public string Serialize<T>(T obj)
|
public string Serialize<T>(T obj)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user