Compare commits

..

15 Commits

Author SHA1 Message Date
Chomp
92c624b246 Update 2025-01-22 09:54:23 +00:00
Chomp
2777b18bdd Updated bot difficulty values 2025-01-06 20:29:43 +00:00
Chomp
0e49c2bfc3 Updated bot difficulty values 2024-12-27 15:16:00 +00:00
Chomp
6ff0aa5120 Reduce accuracy of large loot weights 2024-12-13 13:18:06 +00:00
Chomp
e7813b48dd Added code to reduce the accuracy of values deemed too accurate 2024-12-12 22:24:15 +00:00
Dev
5eebd817b4 Fixed badly named keys 2024-11-21 17:57:36 +00:00
Dev
b826e97b30 Added infected bot weights 2024-11-18 17:38:31 +00:00
294881dcca Minor Changes to Resolve Warnings (#5)
Resolves a number of CS0109, HAA060, and HAA0401 warnings. See commits for details.

Reviewed-on: chomp/BotGenerator#5
Co-authored-by: Refringe <me@refringe.com>
Co-committed-by: Refringe <me@refringe.com>
2024-11-14 20:03:41 +00:00
Dev
5083c7c5c3 Updated to store experience/aggressor bonus/exp reward in dictionaries keyed by bot difficulty 2024-11-14 11:52:11 +00:00
Dev
8c0db8e0c2 Updated to store standing for kill in a dictionary keyed by difficulty 2024-11-14 11:20:01 +00:00
Dev
1dbf46179d Updated to use .net 9 2024-11-14 10:02:21 +00:00
f927a64a13 Merge pull request 'Drastically reduce memory footprint at the cost of removing threading' (#4) from DrakiaXYZ/BotGenerator:feat-memory-improvements into master
Reviewed-on: chomp/BotGenerator#4
2024-11-04 15:44:47 +00:00
32c0239c3a Merge pull request 'Fix exceptions when run against latest bot dumps' (#3) from DrakiaXYZ/BotGenerator:fix-310-newbots into master
Reviewed-on: chomp/BotGenerator#3
2024-11-04 15:37:35 +00:00
DrakiaXYZ
9ddbc91977 Drastically reduce memory footprint at the cost of removing threading
- Drastically improve performance, where non-threaded perf is now better than previous threaded perf
- Process all data in-line, as opposed to building huge lists of bot data that is then processed in groups later
- Memory footprint should now be relatively static no matter the amount of dumps
2024-11-03 23:29:50 -08:00
DrakiaXYZ
e0f9cfbfaa Fix exceptions when run against latest bot dumps
- Update items.json with latest from server repo
- Add new infected bot types to list of bots processed
2024-11-03 17:57:31 -08:00
18 changed files with 574018 additions and 466014 deletions

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net9.0</TargetFramework>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@ -69,7 +69,7 @@ namespace Common.Models.Output
public Dictionary<string, int> TacticalVest { get; set; } public Dictionary<string, int> TacticalVest { get; set; }
public Dictionary<string, int> Pockets { get; set; } public Dictionary<string, int> Pockets { get; set; }
public Dictionary<string, int> Backpack { get; set; } public Dictionary<string, int> Backpack { get; set; }
public new Dictionary<string, int> SecuredContainer { get; set; } public Dictionary<string, int> SecuredContainer { get; set; }
public new Dictionary<string, int> SpecialLoot { get; set; } public Dictionary<string, int> SpecialLoot { get; set; }
} }
} }

View File

@ -1,4 +1,5 @@
using System.Collections.Generic; using System;
using System.Collections.Generic;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace Common.Models.Output; namespace Common.Models.Output;
@ -18,6 +19,7 @@ public class Bot
public Bot(BotType botType) public Bot(BotType botType)
{ {
this.botType = botType; this.botType = botType;
botCount = 0;
appearance = new Appearance(); appearance = new Appearance();
experience = new Experience(); experience = new Experience();
health = new Health(); health = new Health();
@ -32,6 +34,8 @@ public class Bot
[JsonIgnore] [JsonIgnore]
public BotType botType { get; set; } public BotType botType { get; set; }
[JsonIgnore]
public int botCount { get; set; }
public Appearance appearance { get; set; } public Appearance appearance { get; set; }
public Experience experience { get; set; } public Experience experience { get; set; }
public Health health { get; set; } public Health health { get; set; }
@ -66,20 +70,20 @@ public class Experience
public Experience() public Experience()
{ {
level = new MinMax(0, 1); level = new MinMax(0, 1);
reward = new MinMax(-1, -1); reward = new Dictionary<string, MinMax>();
standingForKill = -0.02; standingForKill = new Dictionary<string, object>();
aggressorBonus = 0.01; aggressorBonus = new Dictionary<string, object>(); ;
useSimpleAnimator = false; useSimpleAnimator = false;
} }
public MinMax level { get; set; } public MinMax level { get; set; }
public MinMax reward { get; set; } public Dictionary<string, MinMax> reward { get; set; }
public object standingForKill { get; set; } public Dictionary<string, object> standingForKill { get; set; }
public object aggressorBonus { get; set; } public new Dictionary<string, object> aggressorBonus { get; set; }
public bool useSimpleAnimator { get; set; } public bool useSimpleAnimator { get; set; }
} }
public class BodyParts public class BodyParts : IEquatable<BodyParts>
{ {
public BodyParts() public BodyParts()
{ {
@ -92,6 +96,17 @@ public class BodyParts
RightLeg = new MinMax(65, 65); RightLeg = new MinMax(65, 65);
} }
public bool Equals(BodyParts other)
{
return this.Head.Equals(other.Head) &&
this.Chest.Equals(other.Chest) &&
this.Stomach.Equals(other.Stomach) &&
this.LeftArm.Equals(other.LeftArm) &&
this.RightArm.Equals(other.RightArm) &&
this.LeftLeg.Equals(other.LeftLeg) &&
this.RightLeg.Equals(other.RightLeg);
}
public MinMax Head { get; set; } public MinMax Head { get; set; }
public MinMax Chest { get; set; } public MinMax Chest { get; set; }
public MinMax Stomach { get; set; } public MinMax Stomach { get; set; }
@ -256,7 +271,7 @@ public class ItemChances
public GenerationWeightData grenades { get; set; } public GenerationWeightData grenades { get; set; }
} }
public class MinMax public class MinMax : IEquatable<MinMax>
{ {
public MinMax(int min, int max) public MinMax(int min, int max)
{ {
@ -264,6 +279,11 @@ public class MinMax
this.max = max; this.max = max;
} }
public bool Equals(MinMax other)
{
return this.min == other.min && this.max == other.max;
}
public int min { get; set; } public int min { get; set; }
public int max { get; set; } public int max { get; set; }
} }

File diff suppressed because it is too large Load Diff

View File

@ -1,137 +0,0 @@
using System.Collections.Concurrent;
using Common.Models.Input;
using Newtonsoft.Json.Linq;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using Common.Models.Output;
namespace Common.Bots;
public static class BotParser
{
private static readonly JsonSerializerOptions serialiserOptions = new() { };
public static async Task<List<Datum>> ParseAsync(string dumpPath, HashSet<string> botTypes)
{
var stopwatch = Stopwatch.StartNew();
DiskHelpers.CreateDirIfDoesntExist(dumpPath);
var botFiles = Directory.GetFiles(dumpPath, "*.json", SearchOption.TopDirectoryOnly);
LoggingHelpers.LogToConsole($"{botFiles.Length} bot dump files found");
// key = bot type
// Store bots keyed against their ID so we never get duplicates
var parsedBotsDict = new ConcurrentDictionary<string, Datum>();
int totalDupeCount = 0;
var tasks = new List<Task>();
foreach (var filePath in botFiles)
{
tasks.Add(ProcessBotFile(botTypes, filePath, parsedBotsDict, totalDupeCount));
}
await Task.WhenAll(tasks.ToArray());
stopwatch.Stop();
LoggingHelpers.LogToConsole($"Cleaned and Parsed: {parsedBotsDict.Count} bots. {totalDupeCount} dupes were ignored. Took {LoggingHelpers.LogTimeTaken(stopwatch.Elapsed.TotalSeconds)} seconds");
return [.. parsedBotsDict.Values];
}
private static async Task<int> ProcessBotFile(
HashSet<string> botTypes,
string filePath,
ConcurrentDictionary<string, Datum> parsedBotsDict,
int totalDupeCount)
{
var splitFilePath = filePath.Split("\\");
int dupeCount = 0;
List<Datum> bots = [];
try
{
// Parse the bots inside the json file
using (var reader = new StreamReader(filePath))
{
var deSerialisedObject = JsonSerializer.Deserialize<Root>(reader.ReadToEnd(), serialiserOptions);
bots.AddRange(deSerialisedObject.data.Where(botData => botTypes.Contains(botData.Info.Settings.Role.ToLower())));
}
}
catch (Exception ex)
{
Console.WriteLine($"File parse fucked up: {filePath}");
throw;
}
if (bots == null || bots.Count == 0)
{
Console.WriteLine($"Skipping file: {splitFilePath.Last()}. no bots found, ");
return totalDupeCount;
}
//Console.WriteLine($"parsing: {bots.Count} bots in file {splitFilePath.Last()}");
foreach (var bot in bots)
{
// Bot fucks up something, never allow it in
if (bot._id == "6483938c53cc9087c70eae86")
{
Console.WriteLine("oh no");
continue;
}
// null out unnecessary data to save ram
bot.Stats = null;
bot.Encyclopedia = null;
bot.Hideout = null;
bot.TaskConditionCounters = null;
bot.Bonuses = null;
bot.InsuredItems = null;
// Add bot if not already added
if (!parsedBotsDict.TryAdd(bot._id, bot))
{
dupeCount++;
}
}
totalDupeCount += dupeCount;
//Console.WriteLine($"Parsed file: {filePath}");
return totalDupeCount;
}
private static IEnumerable<Datum> ParseJson(string json)
{
var deSerialisedObject = JsonSerializer.Deserialize<Root>(json, serialiserOptions);
return deSerialisedObject.data;
}
private static string PruneMalformedBsgJson(string json, string fileName)
{
// Bsg send json where an item has a location of 1 but it should be an object with x/y/z coords
var o = JObject.Parse(json);
var jItemsToReplace = o.SelectTokens("$.data[*].Inventory.items[?(@.location == 1)].location");
//var jItemsToReplace = o.SelectTokens("$.data[*].Inventory.items[?(@.location == 1 && @.slotId == 'cartridges')].location");
if (jItemsToReplace != null && jItemsToReplace.Any())
{
LoggingHelpers.LogToConsole($"file {fileName} has {jItemsToReplace.Count()} json issues, cleaning up.", ConsoleColor.Yellow);
foreach (var item in jItemsToReplace)
{
var obj = new { x = 1, y = 0, r = 0 };
item.Replace(JToken.FromObject(obj));
}
}
var returnString = o.ToString();
o = null;
jItemsToReplace = null;
return returnString;
}
}

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net9.0</TargetFramework>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@ -46,7 +46,7 @@
}, },
"grenades": { "grenades": {
"weights": { "weights": {
"0": 8, "0": 13,
"1": 6, "1": 6,
"2": 2, "2": 2,
"3": 1, "3": 1,
@ -1339,5 +1339,375 @@
}, },
"whitelist": [] "whitelist": []
} }
},
"infectedpmc": {
"backpackLoot": {
"weights": {
"0": 1
},
"whitelist": []
},
"currency": {
"weights": {
"0": 1
},
"whitelist": []
},
"drinks": {
"weights": {
"0": 1
},
"whitelist": []
},
"drugs": {
"weights": {
"0": 1
},
"whitelist": []
},
"food": {
"weights": {
"0": 1
},
"whitelist": []
},
"grenades": {
"weights": {
"0": 1
},
"whitelist": []
},
"healing": {
"weights": {
"0": 1
},
"whitelist": []
},
"magazines": {
"weights": {
"0": 1
},
"whitelist": []
},
"pocketLoot": {
"weights": {
"0": 1
},
"whitelist": []
},
"specialItems": {
"weights": {
"0": 1
},
"whitelist": []
},
"stims": {
"weights": {
"0": 1
},
"whitelist": []
},
"vestLoot": {
"weights": {
"0": 1
},
"whitelist": []
}
},
"infectedassault": {
"backpackLoot": {
"weights": {
"0": 1
},
"whitelist": []
},
"currency": {
"weights": {
"0": 1
},
"whitelist": []
},
"drinks": {
"weights": {
"0": 1
},
"whitelist": []
},
"drugs": {
"weights": {
"0": 1
},
"whitelist": []
},
"food": {
"weights": {
"0": 1
},
"whitelist": []
},
"grenades": {
"weights": {
"0": 1
},
"whitelist": []
},
"healing": {
"weights": {
"0": 1
},
"whitelist": []
},
"magazines": {
"weights": {
"0": 1
},
"whitelist": []
},
"pocketLoot": {
"weights": {
"0": 1
},
"whitelist": []
},
"specialItems": {
"weights": {
"0": 1
},
"whitelist": []
},
"stims": {
"weights": {
"0": 1
},
"whitelist": []
},
"vestLoot": {
"weights": {
"0": 1
},
"whitelist": []
}
},
"infectedcivil": {
"backpackLoot": {
"weights": {
"0": 1
},
"whitelist": []
},
"currency": {
"weights": {
"0": 1
},
"whitelist": []
},
"drinks": {
"weights": {
"0": 1
},
"whitelist": []
},
"drugs": {
"weights": {
"0": 1
},
"whitelist": []
},
"food": {
"weights": {
"0": 1
},
"whitelist": []
},
"grenades": {
"weights": {
"0": 1
},
"whitelist": []
},
"healing": {
"weights": {
"0": 1
},
"whitelist": []
},
"magazines": {
"weights": {
"0": 1
},
"whitelist": []
},
"pocketLoot": {
"weights": {
"0": 1
},
"whitelist": []
},
"specialItems": {
"weights": {
"0": 1
},
"whitelist": []
},
"stims": {
"weights": {
"0": 1
},
"whitelist": []
},
"vestLoot": {
"weights": {
"0": 1
},
"whitelist": []
}
},
"infectedlaborant": {
"backpackLoot": {
"weights": {
"0": 1
},
"whitelist": []
},
"currency": {
"weights": {
"0": 1
},
"whitelist": []
},
"drinks": {
"weights": {
"0": 1
},
"whitelist": []
},
"drugs": {
"weights": {
"0": 1
},
"whitelist": []
},
"food": {
"weights": {
"0": 1
},
"whitelist": []
},
"grenades": {
"weights": {
"0": 1
},
"whitelist": []
},
"healing": {
"weights": {
"0": 1
},
"whitelist": []
},
"magazines": {
"weights": {
"0": 1
},
"whitelist": []
},
"pocketLoot": {
"weights": {
"0": 1
},
"whitelist": []
},
"specialItems": {
"weights": {
"0": 1
},
"whitelist": []
},
"stims": {
"weights": {
"0": 1
},
"whitelist": []
},
"vestLoot": {
"weights": {
"0": 1
},
"whitelist": []
}
},
"infectedtagilla": {
"backpackLoot": {
"weights": {
"0": 1
},
"whitelist": []
},
"currency": {
"weights": {
"0": 1
},
"whitelist": []
},
"drinks": {
"weights": {
"0": 1
},
"whitelist": []
},
"drugs": {
"weights": {
"0": 1
},
"whitelist": []
},
"food": {
"weights": {
"0": 1
},
"whitelist": []
},
"grenades": {
"weights": {
"0": 1
},
"whitelist": []
},
"healing": {
"weights": {
"0": 1
},
"whitelist": []
},
"magazines": {
"weights": {
"0": 1
},
"whitelist": []
},
"pocketLoot": {
"weights": {
"0": 1
},
"whitelist": []
},
"specialItems": {
"weights": {
"0": 1
},
"whitelist": []
},
"stims": {
"weights": {
"0": 1
},
"whitelist": []
},
"vestLoot": {
"weights": {
"0": 1
},
"whitelist": []
}
} }
} }

View File

@ -4,110 +4,87 @@ using Common.Models.Input;
using Common.Models.Output; using Common.Models.Output;
using Generator.Helpers; using Generator.Helpers;
using Generator.Helpers.Gear; using Generator.Helpers.Gear;
using System.Diagnostics;
namespace Generator namespace Generator
{ {
public static class BaseBotGenerator public static class BaseBotGenerator
{ {
//TODO: pass in bot types and use those to create the classes in rawBots list public static void UpdateBaseDetails(Bot botData, Datum rawBotData)
public static IEnumerable<Bot> GenerateBaseDetails(IEnumerable<Datum> rawBots, string workingPath, IEnumerable<string> botTypes)
{ {
var stopwatch = Stopwatch.StartNew(); UpdateBodyPartHealth(botData, rawBotData);
LoggingHelpers.LogToConsole("Started processing bot base"); AddExperience(botData, rawBotData);
AddStandingForKill(botData, rawBotData);
AddAggressorBonus(botData, rawBotData);
AddSkills(botData, rawBotData);
botData.experience.useSimpleAnimator = rawBotData.Info.Settings.UseSimpleAnimator;
// Create a list of bot objects ready to be hydrated AddVisualAppearanceItems(botData, rawBotData);
var baseBots = new List<Bot>(); AddName(botData, rawBotData);
foreach (var botType in botTypes) AddVoice(botData, rawBotData);
{
var typeToAdd = (BotType)Enum.Parse(typeof(BotType), botType);
baseBots.Add(new Bot(typeToAdd));
}
// Iterate over each bot type we just made and put some data into them
foreach (var botToUpdate in baseBots)
{
var rawBotType = botToUpdate.botType.ToString();
var rawBotsOfSameType = rawBots.Where(x => string.Equals(x.Info.Settings.Role, rawBotType, StringComparison.OrdinalIgnoreCase))
.ToList();
var rawBotsOfSameTypeCount = rawBotsOfSameType.Count.ToString();
if (rawBotsOfSameType.Count == 0)
{
LoggingHelpers.LogToConsole($"no bots of type {rawBotType}, skipping", ConsoleColor.DarkRed);
continue;
}
LoggingHelpers.LogToConsole($"Found {rawBotsOfSameTypeCount} bots of type: {rawBotType}");
UpdateBodyPartHealth(botToUpdate, rawBotsOfSameType);
AddDifficulties(botToUpdate, workingPath);
AddExperience(botToUpdate, rawBotsOfSameType);
AddStandingForKill(botToUpdate, rawBotsOfSameType);
AddSkills(botToUpdate, rawBotsOfSameType);
botToUpdate.experience.useSimpleAnimator = rawBotsOfSameType.First().Info.Settings.UseSimpleAnimator;
foreach (var rawParsedBot in rawBotsOfSameType)
{
AddVisualAppearanceItems(botToUpdate, rawParsedBot);
AddName(botToUpdate, rawParsedBot);
AddVoice(botToUpdate, rawParsedBot);
}
}
stopwatch.Stop();
LoggingHelpers.LogToConsole($"Finished processing bot base. Took {LoggingHelpers.LogTimeTaken(stopwatch.Elapsed.TotalSeconds)} seconds");
return baseBots;
} }
private static void AddSkills(Bot botToUpdate, IEnumerable<Datum> rawBotsOfSameType) private static void AddSkills(Bot botToUpdate, Datum rawBotData)
{ {
var firstBotOfDesiredType = rawBotsOfSameType.Last();
// Find the smallest and biggest value for each skill // Find the smallest and biggest value for each skill
foreach (var skill in firstBotOfDesiredType.Skills.Common) foreach (var skill in rawBotData.Skills.Common)
{ {
var skills = new List<Common.Models.Input.Common>(); if (botToUpdate.skills.Common.TryGetValue(skill.Id, out var existingSkill))
foreach (var bot in rawBotsOfSameType)
{ {
skills.Add(bot.Skills.Common.Find(x => x.Id == skill.Id)); existingSkill.min = Math.Min(existingSkill.min, skill.Progress);
existingSkill.max = Math.Max(existingSkill.max, skill.Progress);
} }
else
var min = skills.Min(x => x?.Progress);
var max = skills.Max(x => x?.Progress);
if (min.HasValue && max.HasValue)
{ {
botToUpdate.skills.Common.Add(skill.Id, new MinMax(min.Value, max.Value)); botToUpdate.skills.Common.Add(skill.Id, new MinMax(skill.Progress, skill.Progress));
} }
} }
} }
private static void AddStandingForKill(Bot botToUpdate, IEnumerable<Datum> rawBotsOfSameType) private static void AddStandingForKill(Bot botToUpdate, Datum rawBotData)
{ {
var firstBotOfDesiredType = rawBotsOfSameType.Last(); botToUpdate.experience.standingForKill ??= new Dictionary<string, object>();
botToUpdate.experience.standingForKill = firstBotOfDesiredType.Info.Settings.StandingForKill; if (!botToUpdate.experience.standingForKill.ContainsKey(rawBotData.Info.Settings.BotDifficulty))
botToUpdate.experience.aggressorBonus = firstBotOfDesiredType.Info.Settings.AggressorBonus; {
botToUpdate.experience.standingForKill.Add(rawBotData.Info.Settings.BotDifficulty, rawBotData.Info.Settings.StandingForKill);
}
} }
private static void AddExperience(Bot botToUpdate, IEnumerable<Datum> rawBotsOfSameType) private static void AddAggressorBonus(Bot botToUpdate, Datum rawBotData)
{ {
var firstBotOfDesiredType = rawBotsOfSameType.Last(); botToUpdate.experience.aggressorBonus ??= new Dictionary<string, object>();
if (!botToUpdate.experience.aggressorBonus.ContainsKey(rawBotData.Info.Settings.BotDifficulty))
{
botToUpdate.experience.aggressorBonus.Add(rawBotData.Info.Settings.BotDifficulty, rawBotData.Info.Settings.AggressorBonus);
}
}
private static void AddExperience(Bot botToUpdate, Datum rawBotData)
{
botToUpdate.experience.reward ??= new();
botToUpdate.experience.reward.TryGetValue(rawBotData.Info.Settings.BotDifficulty, out var minMaxValues);
if (minMaxValues is null)
{
botToUpdate.experience.reward.Add(rawBotData.Info.Settings.BotDifficulty, new(rawBotData.Info.Settings.Experience, rawBotData.Info.Settings.Experience));
return;
}
minMaxValues.min = Math.Min(minMaxValues.min, rawBotData.Info.Settings.Experience);
minMaxValues.max = Math.Max(minMaxValues.max, rawBotData.Info.Settings.Experience);
botToUpdate.experience.reward.min = firstBotOfDesiredType.Info.Settings.Experience;
botToUpdate.experience.reward.max = firstBotOfDesiredType.Info.Settings.Experience;
} }
private static void AddVoice(Bot bot, Datum rawBot) private static void AddVoice(Bot bot, Datum rawBot)
{ {
GearHelpers.IncrementDictionaryValue(bot.appearance.voice, rawBot.Info.Voice); GearHelpers.IncrementDictionaryValue(bot.appearance.voice, rawBot.Info.Voice);
GearHelpers.ReduceWeightValues(bot.appearance.voice);
} }
private static void AddDifficulties(Bot bot, string workingPath) public static void AddDifficulties(Bot bot)
{ {
string workingPath = Directory.GetCurrentDirectory();
string botType = bot.botType.ToString(); string botType = bot.botType.ToString();
var botDifficultyFiles = Directory var botDifficultyFiles = Directory
.GetFiles($"{workingPath}//Assets", "*.txt", SearchOption.TopDirectoryOnly) .GetFiles($"{workingPath}//Assets", "*.txt", SearchOption.TopDirectoryOnly)
@ -117,51 +94,34 @@ namespace Generator
DifficultyHelper.AddDifficultySettings(bot, botDifficultyFiles); DifficultyHelper.AddDifficultySettings(bot, botDifficultyFiles);
} }
private static void UpdateBodyPartHealth(Bot botToUpdate, List<Datum> rawBots) private static void UpdateBodyPartHealth(Bot botToUpdate, Datum rawBot)
{ {
var uniqueHealthSetups = new Dictionary<int, Common.Models.Output.BodyParts>(); var bodyPartHpToAdd = new Common.Models.Output.BodyParts()
foreach (var bot in rawBots)
{ {
var healthTotal = bot.Health.BodyParts.GetHpMaxTotal(); Head = new MinMax(rawBot.Health.BodyParts.Head.Health.Current, rawBot.Health.BodyParts.Head.Health.Maximum),
var alreadyExists = uniqueHealthSetups.ContainsKey(healthTotal); Chest = new MinMax(rawBot.Health.BodyParts.Chest.Health.Current, rawBot.Health.BodyParts.Chest.Health.Maximum),
Stomach = new MinMax(rawBot.Health.BodyParts.Stomach.Health.Current, rawBot.Health.BodyParts.Stomach.Health.Maximum),
LeftArm = new MinMax(rawBot.Health.BodyParts.LeftArm.Health.Current, rawBot.Health.BodyParts.LeftArm.Health.Maximum),
RightArm = new MinMax(rawBot.Health.BodyParts.RightArm.Health.Current, rawBot.Health.BodyParts.RightArm.Health.Maximum),
LeftLeg = new MinMax(rawBot.Health.BodyParts.LeftLeg.Health.Current, rawBot.Health.BodyParts.LeftLeg.Health.Maximum),
RightLeg = new MinMax(rawBot.Health.BodyParts.RightLeg.Health.Current, rawBot.Health.BodyParts.RightLeg.Health.Maximum)
};
if (!alreadyExists) if (!botToUpdate.health.BodyParts.Contains(bodyPartHpToAdd))
{ {
var bodyPartHpToAdd = new Common.Models.Output.BodyParts() botToUpdate.health.BodyParts.Add(bodyPartHpToAdd);
{
Head = new MinMax(bot.Health.BodyParts.Head.Health.Current, bot.Health.BodyParts.Head.Health.Maximum),
Chest = new MinMax(bot.Health.BodyParts.Chest.Health.Current, bot.Health.BodyParts.Chest.Health.Maximum),
Stomach = new MinMax(bot.Health.BodyParts.Stomach.Health.Current, bot.Health.BodyParts.Stomach.Health.Maximum),
LeftArm = new MinMax(bot.Health.BodyParts.LeftArm.Health.Current, bot.Health.BodyParts.LeftArm.Health.Maximum),
RightArm = new MinMax(bot.Health.BodyParts.RightArm.Health.Current, bot.Health.BodyParts.RightArm.Health.Maximum),
};
bodyPartHpToAdd.LeftLeg.min = bot.Health.BodyParts.LeftLeg.Health.Current;
bodyPartHpToAdd.LeftLeg.max = bot.Health.BodyParts.LeftLeg.Health.Maximum;
bodyPartHpToAdd.RightLeg.min = bot.Health.BodyParts.RightLeg.Health.Current;
bodyPartHpToAdd.RightLeg.max = bot.Health.BodyParts.RightLeg.Health.Maximum;
uniqueHealthSetups.Add(healthTotal, bodyPartHpToAdd);
}
} }
botToUpdate.health.BodyParts = uniqueHealthSetups.Values.ToList();
} }
private static void AddVisualAppearanceItems(Bot botToUpdate, Datum rawBot) private static void AddVisualAppearanceItems(Bot botToUpdate, Datum rawBot)
{ {
GearHelpers.IncrementDictionaryValue(botToUpdate.appearance.feet, rawBot.Customization.Feet); GearHelpers.IncrementDictionaryValue(botToUpdate.appearance.feet, rawBot.Customization.Feet);
GearHelpers.ReduceWeightValues(botToUpdate.appearance.feet);
GearHelpers.IncrementDictionaryValue(botToUpdate.appearance.body, rawBot.Customization.Body); GearHelpers.IncrementDictionaryValue(botToUpdate.appearance.body, rawBot.Customization.Body);
GearHelpers.ReduceWeightValues(botToUpdate.appearance.body);
GearHelpers.IncrementDictionaryValue(botToUpdate.appearance.head, rawBot.Customization.Head); GearHelpers.IncrementDictionaryValue(botToUpdate.appearance.head, rawBot.Customization.Head);
GearHelpers.ReduceWeightValues(botToUpdate.appearance.head);
GearHelpers.IncrementDictionaryValue(botToUpdate.appearance.hands, rawBot.Customization.Hands); GearHelpers.IncrementDictionaryValue(botToUpdate.appearance.hands, rawBot.Customization.Hands);
GearHelpers.ReduceWeightValues(botToUpdate.appearance.hands);
} }
private static void AddName(Bot botToUpdate, Datum rawBot) private static void AddName(Bot botToUpdate, Datum rawBot)

View File

@ -8,51 +8,15 @@ namespace Generator
{ {
public static class BotChancesGenerator public static class BotChancesGenerator
{ {
public static IEnumerable<Bot> AddChances(this IEnumerable<Bot> botsToUpdate, Dictionary<string, List<Datum>> rawBots) public static void AddChances(Bot botToUpdate, Datum rawBot)
{ {
var stopwatch = Stopwatch.StartNew();
LoggingHelpers.LogToConsole("Started processing bot gear");
// use lock for lock safety
var dictionaryLock = new object();
var weightHelper = new WeightingService(); var weightHelper = new WeightingService();
// multithread
var tasks = new List<Task>();
foreach (var botToUpdate in botsToUpdate)
{
tasks.Add(Task.Factory.StartNew(() =>
{
var botType = botToUpdate.botType.ToString().ToLower();
List<Datum> rawBotsOfSameType;
lock (dictionaryLock)
{
if (!rawBots.TryGetValue(botType, out rawBotsOfSameType))
{
Console.WriteLine($"(chances) Unable to find {botType} on rawBots data");
return;
}
}
if (rawBotsOfSameType.Count == 0) // TODO: Add check to make sure incoming bot list has gear
{ GearChanceHelpers.AddEquipmentChances(botToUpdate, rawBot);
return; GearChanceHelpers.AddGenerationChances(botToUpdate, weightHelper);
} GearChanceHelpers.AddModChances(botToUpdate, rawBot);
GearChanceHelpers.AddEquipmentModChances(botToUpdate, rawBot);
// TODO: Add check to make sure incoming bot list has gear
GearChanceHelpers.CalculateEquipmentChances(botToUpdate, rawBotsOfSameType);
GearChanceHelpers.AddGenerationChances(botToUpdate, weightHelper);
GearChanceHelpers.CalculateModChances(botToUpdate, rawBotsOfSameType);
GearChanceHelpers.CalculateEquipmentModChances(botToUpdate, rawBotsOfSameType);
GearChanceHelpers.ApplyModChanceOverrides(botToUpdate);
GearChanceHelpers.ApplyEquipmentChanceOverrides(botToUpdate);
}));
}
Task.WaitAll(tasks.ToArray());
stopwatch.Stop();
LoggingHelpers.LogToConsole($"Finished processing bot chances. Took {LoggingHelpers.LogTimeTaken(stopwatch.Elapsed.TotalSeconds)} seconds");
return botsToUpdate;
} }
} }
} }

View File

@ -7,52 +7,12 @@ namespace Generator
{ {
public static class BotGearGenerator public static class BotGearGenerator
{ {
public static IEnumerable<Bot> AddGear(this IEnumerable<Bot> baseBots, Dictionary<string, List<Datum>> rawBots) public static void AddGear(Bot botToUpdate, Datum rawBotData)
{ {
var stopwatch = Stopwatch.StartNew(); GearHelpers.AddEquippedGear(botToUpdate, rawBotData);
LoggingHelpers.LogToConsole("Started processing bot gear"); GearHelpers.AddAmmo(botToUpdate, rawBotData);
GearHelpers.AddEquippedMods(botToUpdate, rawBotData);
var dictionaryLock = new object(); //GearHelpers.AddCartridges(botToUpdate, rawBotData);
var tasks = new List<Task>();
foreach (var botToUpdate in baseBots)
{
tasks.Add(Task.Factory.StartNew(() =>
{
var botType = botToUpdate.botType.ToString().ToLower();
List<Datum> rawBotsOfSameType;
lock (dictionaryLock)
{
if (!rawBots.TryGetValue(botType, out rawBotsOfSameType))
{
Console.WriteLine($"(gear) Unable to find {botType} on rawBots data");
return;
}
}
if (rawBotsOfSameType.Count == 0)
{
return;
}
foreach (var rawParsedBot in rawBotsOfSameType)
{
GearHelpers.AddEquippedGear(botToUpdate, rawParsedBot);
GearHelpers.AddAmmo(botToUpdate, rawParsedBot);
GearHelpers.AddEquippedMods(botToUpdate, rawParsedBot);
//GearHelpers.AddCartridges(botToUpdate, rawParsedBot);
}
GearHelpers.ReduceAmmoWeightValues(botToUpdate);
GearHelpers.ReduceEquipmentWeightValues(botToUpdate.inventory.equipment);
}));
}
Task.WaitAll(tasks.ToArray());
stopwatch.Stop();
LoggingHelpers.LogToConsole($"Finished processing bot gear. Took {LoggingHelpers.LogTimeTaken(stopwatch.Elapsed.TotalSeconds)} seconds");
return baseBots;
} }
} }
} }

View File

@ -1,6 +1,7 @@
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using Common.Extensions; using Common.Extensions;
using Common.Models;
using Common.Models.Input; using Common.Models.Input;
using Common.Models.Output; using Common.Models.Output;
using Generator.Helpers.Gear; using Generator.Helpers.Gear;
@ -9,107 +10,60 @@ namespace Generator
{ {
public static class BotLootGenerator public static class BotLootGenerator
{ {
internal static IEnumerable<Bot> AddLoot(this IEnumerable<Bot> botsWithGear, Dictionary<string, List<Datum>> rawBots) internal static void AddLoot(Bot botToUpdate, Datum rawBotData)
{ {
var stopwatch = Stopwatch.StartNew(); AddLootToContainers(botToUpdate, rawBotData);
LoggingHelpers.LogToConsole("Started processing bot loot");
var dictionaryLock = new object();
var tasks = new List<Task>(50);
foreach (var botToUpdate in botsWithGear)
{
tasks.Add(Task.Factory.StartNew(() =>
{
var botType = botToUpdate.botType.ToString().ToLower();
List<Datum> rawBotsOfSameType;
lock (dictionaryLock)
{
if (!rawBots.TryGetValue(botType, out rawBotsOfSameType))
{
Console.WriteLine($"(loot) Unable to find {botType} on rawBots data");
return;
}
}
if (rawBotsOfSameType.Count == 0)
{
return;
}
AddLootToContainers(botType, botToUpdate, rawBotsOfSameType);
GearHelpers.ReduceWeightValues(botToUpdate.inventory.equipment.Backpack);
GearHelpers.ReduceWeightValues(botToUpdate.inventory.equipment.Pockets);
GearHelpers.ReduceWeightValues(botToUpdate.inventory.equipment.TacticalVest);
GearHelpers.ReduceWeightValues(botToUpdate.inventory.equipment.SecuredContainer);
//foreach (var rawParsedBot in rawBotsOfSameType)
//{
// AddPocketLoot(botToUpdate, rawParsedBot);
//}
//AddTacticalVestLoot(botToUpdate, rawBotsOfSameType);
//AddBackpackLoot(botToUpdate, rawBotsOfSameType);
//AddSecureContainerLoot(botToUpdate, rawBotsOfSameType);
//AddSpecialLoot(botToUpdate);
}));
}
Task.WaitAll(tasks.ToArray());
stopwatch.Stop();
LoggingHelpers.LogToConsole($"Finished processing bot loot. Took: {LoggingHelpers.LogTimeTaken(stopwatch.Elapsed.TotalSeconds)} seconds");
return botsWithGear;
} }
private static void AddLootToContainers(string botType, Bot botToUpdate, List<Datum> rawBotsOfSameType) private static void AddLootToContainers(Bot botToUpdate, Datum rawBot)
{ {
// Process each bot // Filter out base inventory items and equipment mod items
foreach (var rawBot in rawBotsOfSameType) var rawBotItems = rawBot.Inventory.items.Where(item => item.location != null);
{
// Filter out base inventory items and equipment mod items
var rawBotItems = rawBot.Inventory.items.Where(item => item.location != null).ToList();
var botBackpack = rawBot.Inventory.items.FirstOrDefault(item => item.slotId == "Backpack"); var botBackpack = rawBot.Inventory.items.FirstOrDefault(item => item.slotId == "Backpack");
if (botBackpack != null) if (botBackpack != null)
{ {
AddLootItemsToContainerDictionary(rawBotItems, botBackpack._id, botToUpdate.inventory.items.Backpack); AddLootItemsToContainerDictionary(rawBotItems, botBackpack._id, botToUpdate.inventory.items.Backpack, "backpack", botToUpdate.botType);
} }
var botPockets = rawBot.Inventory.items.FirstOrDefault(item => item.slotId == "Pockets"); var botPockets = rawBot.Inventory.items.FirstOrDefault(item => item.slotId == "Pockets");
if (botPockets != null) if (botPockets != null)
{ {
AddLootItemsToContainerDictionary(rawBotItems, botPockets._id, botToUpdate.inventory.items.Pockets); AddLootItemsToContainerDictionary(rawBotItems, botPockets._id, botToUpdate.inventory.items.Pockets);
} }
var botVest = rawBot.Inventory.items.FirstOrDefault(item => item.slotId == "TacticalVest"); var botVest = rawBot.Inventory.items.FirstOrDefault(item => item.slotId == "TacticalVest");
if (botVest != null) if (botVest != null)
{ {
AddLootItemsToContainerDictionary(rawBotItems, botVest._id, botToUpdate.inventory.items.TacticalVest); AddLootItemsToContainerDictionary(rawBotItems, botVest._id, botToUpdate.inventory.items.TacticalVest);
} }
var botSecure = rawBot.Inventory.items.FirstOrDefault(item => item.slotId == "SecuredContainer"); var botSecure = rawBot.Inventory.items.FirstOrDefault(item => item.slotId == "SecuredContainer");
if (botSecure != null) if (botSecure != null)
{ {
AddLootItemsToContainerDictionary(rawBotItems, botSecure._id, botToUpdate.inventory.items.SecuredContainer); AddLootItemsToContainerDictionary(rawBotItems, botSecure._id, botToUpdate.inventory.items.SecuredContainer);
} }
// Add generic keys to bosses // Add generic keys to bosses
if (botToUpdate.botType.IsBoss()) if (botToUpdate.botType.IsBoss())
{
var keys = SpecialLootHelper.GetGenericBossKeysDictionary();
foreach (var bosskey in keys)
{ {
var keys = SpecialLootHelper.GetGenericBossKeysDictionary(); if (!botToUpdate.inventory.items.Backpack.ContainsKey(bosskey.Key))
foreach (var bosskey in keys)
{ {
if (!botToUpdate.inventory.items.Backpack.ContainsKey(bosskey.Key)) botToUpdate.inventory.items.Backpack.Add(bosskey.Key, bosskey.Value);
{
botToUpdate.inventory.items.Backpack.Add(bosskey.Key, bosskey.Value);
}
} }
} }
AddSpecialLoot(botToUpdate);
} }
AddSpecialLoot(botToUpdate);
// Cleanup of weights
GearHelpers.ReduceWeightValues(botToUpdate.inventory.items.Backpack);
GearHelpers.ReduceWeightValues(botToUpdate.inventory.items.Pockets);
GearHelpers.ReduceWeightValues(botToUpdate.inventory.items.TacticalVest);
GearHelpers.ReduceWeightValues(botToUpdate.inventory.items.SecuredContainer);
} }
/// <summary> /// <summary>
@ -119,16 +73,17 @@ namespace Generator
/// <param name="itemsToFilter">Bots inventory items</param> /// <param name="itemsToFilter">Bots inventory items</param>
/// <param name="containerId"></param> /// <param name="containerId"></param>
/// <param name="dictToAddTo"></param> /// <param name="dictToAddTo"></param>
private static void AddLootItemsToContainerDictionary(List<Item> itemsToFilter, string containerId, Dictionary<string, int> dictToAddTo) private static void AddLootItemsToContainerDictionary(IEnumerable<Common.Models.Input.Item> itemsToFilter, string containerId, Dictionary<string, int> dictToAddTo, string container = "", BotType type = BotType.arenafighterevent)
{ {
var lootItems = itemsToFilter.Where(item => item.parentId == containerId); foreach (var itemToAdd in itemsToFilter)
foreach (var itemToAdd in lootItems)
{ {
if (itemToAdd.parentId != containerId) continue;
if (!dictToAddTo.ContainsKey(itemToAdd._tpl)) if (!dictToAddTo.ContainsKey(itemToAdd._tpl))
{ {
dictToAddTo[itemToAdd._tpl] = 1; dictToAddTo[itemToAdd._tpl] = 1;
return; continue;
} }
dictToAddTo[itemToAdd._tpl]++; dictToAddTo[itemToAdd._tpl]++;

274
Generator/BotParser.cs Normal file
View File

@ -0,0 +1,274 @@
using System.Collections.Concurrent;
using Common.Models.Input;
using Newtonsoft.Json.Linq;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using Common.Models.Output;
using Common.Models;
using Generator;
using Generator.Helpers.Gear;
namespace Common.Bots;
public static class BotParser
{
private static readonly JsonSerializerOptions serialiserOptions = new() { };
public static List<Bot> Parse(string dumpPath, HashSet<string> botTypes)
{
var stopwatch = Stopwatch.StartNew();
// Build the list of base bot data
var baseBots = new HashSet<Bot>();
foreach (var botType in botTypes)
{
var typeToAdd = (BotType)Enum.Parse(typeof(BotType), botType);
baseBots.Add(new Bot(typeToAdd));
}
DiskHelpers.CreateDirIfDoesntExist(dumpPath);
var botFiles = Directory.GetFiles(dumpPath, "*.json", SearchOption.TopDirectoryOnly);
LoggingHelpers.LogToConsole($"{botFiles.Length.ToString()} bot dump files found");
// Store a list of parsed bots so we don't parse the same bot twice
int totalDupeCount = 0;
var parsedBotIds = new HashSet<string>();
int i = 0;
foreach (var filePath in botFiles)
{
i++;
if (i % 500 == 0) Console.WriteLine($"Processing file {i.ToString()}");
ProcessBotFileSync(baseBots, filePath, parsedBotIds, totalDupeCount);
}
// Handle things we can only do once all data has been processed, or only needs to run once per bot type
foreach (var bot in baseBots)
{
// Skip any bots we didn't handle
if (bot.botCount == 0) continue;
BaseBotGenerator.AddDifficulties(bot);
GearChanceHelpers.CalculateModChances(bot);
GearChanceHelpers.CalculateEquipmentModChances(bot);
GearChanceHelpers.CalculateEquipmentChances(bot);
GearChanceHelpers.ApplyModChanceOverrides(bot);
GearChanceHelpers.ApplyEquipmentChanceOverrides(bot);
GearHelpers.ReduceAmmoWeightValues(bot);
GearHelpers.ReduceEquipmentWeightValues(bot.inventory.equipment);
GearHelpers.ReduceWeightValues(bot.appearance.voice);
GearHelpers.ReduceWeightValues(bot.appearance.feet);
GearHelpers.ReduceWeightValues(bot.appearance.body);
GearHelpers.ReduceWeightValues(bot.appearance.head);
GearHelpers.ReduceWeightValues(bot.appearance.hands);
}
stopwatch.Stop();
LoggingHelpers.LogToConsole($"{totalDupeCount.ToString()} dupes were ignored. Took {LoggingHelpers.LogTimeTaken(stopwatch.Elapsed.TotalSeconds)} seconds");
return baseBots.ToList();
}
private static void ProcessBotFileSync(
HashSet<Bot> baseBots,
string filePath,
HashSet<string> parsedBotIds,
int totalDupeCount)
{
var splitFilePath = filePath.Split("\\");
int dupeCount = 0;
List<Datum> bots = new List<Datum>();
try
{
// Parse the bots inside the json file
using (var reader = new StreamReader(filePath))
{
var deSerialisedObject = JsonSerializer.Deserialize<Root>(reader.ReadToEnd(), serialiserOptions);
foreach (var botData in deSerialisedObject.data.ToList())
{
// Bot fucks up something, never allow it in
if (botData._id == "6483938c53cc9087c70eae86")
{
Console.WriteLine("oh no");
continue;
}
var role = botData.Info.Settings.Role;
var botType = Enum.Parse<BotType>(role, true);
Bot baseBot = null;
foreach (var bot in baseBots)
{
if (bot.botType == botType)
{
baseBot = bot;
break;
}
}
if (baseBot == null)
{
Console.WriteLine($"Skipping {botData._id} due to unknown role {botData.Info.Settings.Role}");
continue;
}
// Add bot if not already added
if (!parsedBotIds.Add(botData._id))
{
dupeCount++;
}
baseBot.botCount += 1;
BaseBotGenerator.UpdateBaseDetails(baseBot, botData);
BotGearGenerator.AddGear(baseBot, botData);
BotLootGenerator.AddLoot(baseBot, botData);
BotChancesGenerator.AddChances(baseBot, botData);
}
}
}
catch (Exception)
{
Console.WriteLine($"File parse fucked up: {filePath}");
throw;
}
totalDupeCount += dupeCount;
}
public static async Task<List<Datum>> ParseAsync(string dumpPath, HashSet<string> botTypes)
{
var stopwatch = Stopwatch.StartNew();
DiskHelpers.CreateDirIfDoesntExist(dumpPath);
var botFiles = Directory.GetFiles(dumpPath, "*.json", SearchOption.TopDirectoryOnly);
LoggingHelpers.LogToConsole($"{botFiles.Length.ToString()} bot dump files found");
// key = bot type
// Store bots keyed against their ID so we never get duplicates
var parsedBotsDict = new ConcurrentDictionary<string, Datum>();
int totalDupeCount = 0;
var tasks = new List<Task>();
foreach (var filePath in botFiles)
{
tasks.Add(ProcessBotFile(botTypes, filePath, parsedBotsDict, totalDupeCount));
}
await Task.WhenAll(tasks.ToArray());
stopwatch.Stop();
LoggingHelpers.LogToConsole($"Cleaned and Parsed: {parsedBotsDict.Count.ToString()} bots. {totalDupeCount.ToString()} dupes were ignored. Took {LoggingHelpers.LogTimeTaken(stopwatch.Elapsed.TotalSeconds)} seconds");
return parsedBotsDict.Values.ToList();
}
private static async Task<int> ProcessBotFile(
HashSet<string> botTypes,
string filePath,
ConcurrentDictionary<string, Datum> parsedBotsDict,
int totalDupeCount)
{
var splitFilePath = filePath.Split("\\");
int dupeCount = 0;
List<Datum> bots = new List<Datum>();
try
{
// Parse the bots inside the json file
using var reader = new StreamReader(filePath);
var deSerialisedObject = await JsonSerializer.DeserializeAsync<Root>(reader.BaseStream, serialiserOptions);
var botTypesLower = new HashSet<string>(botTypes, StringComparer.OrdinalIgnoreCase);
var filteredBots = new List<Datum>();
foreach (var botData in deSerialisedObject.data.ToList())
{
var roleLower = botData.Info.Settings.Role.ToLower();
if (botTypesLower.Contains(roleLower))
{
filteredBots.Add(botData);
}
}
bots.AddRange(filteredBots);
}
catch (Exception)
{
Console.WriteLine($"File parse fucked up: {filePath}");
throw;
}
if (bots == null || bots.Count == 0)
{
Console.WriteLine($"Skipping file: {splitFilePath.Last()}. no bots found, ");
return totalDupeCount;
}
//Console.WriteLine($"parsing: {bots.Count} bots in file {splitFilePath.Last()}");
foreach (var bot in bots)
{
// Bot fucks up something, never allow it in
if (bot._id == "6483938c53cc9087c70eae86")
{
Console.WriteLine("oh no");
continue;
}
// null out unnecessary data to save ram
bot.Stats = null;
bot.Encyclopedia = null;
bot.Hideout = null;
bot.TaskConditionCounters = null;
bot.Bonuses = null;
bot.InsuredItems = null;
// Add bot if not already added
if (!parsedBotsDict.TryAdd(bot._id, bot))
{
dupeCount++;
}
}
totalDupeCount += dupeCount;
//Console.WriteLine($"Parsed file: {filePath}");
return totalDupeCount;
}
private static IEnumerable<Datum> ParseJson(string json)
{
var deSerialisedObject = JsonSerializer.Deserialize<Root>(json, serialiserOptions);
return deSerialisedObject.data;
}
private static string PruneMalformedBsgJson(string json, string fileName)
{
// Bsg send json where an item has a location of 1 but it should be an object with x/y/z coords
var o = JObject.Parse(json);
var jItemsToReplace = o.SelectTokens("$.data[*].Inventory.items[?(@.location == 1)].location");
//var jItemsToReplace = o.SelectTokens("$.data[*].Inventory.items[?(@.location == 1 && @.slotId == 'cartridges')].location");
if (jItemsToReplace != null && jItemsToReplace.Any())
{
LoggingHelpers.LogToConsole($"file {fileName} has {jItemsToReplace.Count().ToString()} json issues, cleaning up.", ConsoleColor.Yellow);
var jItemsToReplaceList = jItemsToReplace.ToList();
foreach (var item in jItemsToReplaceList)
{
var obj = new { x = 1, y = 0, r = 0 };
item.Replace(JToken.FromObject(obj));
}
}
var returnString = o.ToString();
o = null;
jItemsToReplace = null;
return returnString;
}
}

View File

@ -2,7 +2,7 @@
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<ServerGarbageCollection>true</ServerGarbageCollection> <ServerGarbageCollection>true</ServerGarbageCollection>
<ConcurrentGarbageCollection>true</ConcurrentGarbageCollection> <ConcurrentGarbageCollection>true</ConcurrentGarbageCollection>

View File

@ -7,75 +7,102 @@ namespace Generator.Helpers.Gear
{ {
public static class GearChanceHelpers public static class GearChanceHelpers
{ {
public static void CalculateModChances(Bot bot, List<Datum> baseBots) private static Dictionary<string, Dictionary<string, int>> weaponModCount = new Dictionary<string, Dictionary<string, int>>();
private static Dictionary<string, Dictionary<string, int>> weaponSlotCount = new Dictionary<string, Dictionary<string, int>>();
private static Dictionary<string, Dictionary<string, int>> equipmentModCount = new Dictionary<string, Dictionary<string, int>>();
private static Dictionary<string, Dictionary<string, int>> equipmentSlotCount = new Dictionary<string, Dictionary<string, int>>();
public static void AddModChances(Bot bot, Datum baseBot)
{ {
// TODO: Further split these counts by equipment slot? (ex. "FirstPrimaryWeapon", "Holster", etc.) // TODO: Further split these counts by equipment slot? (ex. "FirstPrimaryWeapon", "Holster", etc.)
var validSlots = new List<string> { "FirstPrimaryWeapon", "SecondPrimaryWeapon", "Holster" }; var validSlots = new List<string> { "FirstPrimaryWeapon", "SecondPrimaryWeapon", "Holster" };
var modCounts = new Dictionary<string, int>(); if (!weaponModCount.TryGetValue(baseBot.Info.Settings.Role.ToLower(), out var modCounts))
var slotCounts = new Dictionary<string, int>();
foreach (var baseBot in baseBots)
{ {
var validParents = new List<string>(); modCounts = new Dictionary<string, int>();
foreach (var inventoryItem in baseBot.Inventory.items) weaponModCount.Add(baseBot.Info.Settings.Role.ToLower(), modCounts);
}
if (!weaponSlotCount.TryGetValue(baseBot.Info.Settings.Role.ToLower(), out var slotCounts))
{
slotCounts = new Dictionary<string, int>();
weaponSlotCount.Add(baseBot.Info.Settings.Role.ToLower(), slotCounts);
}
var validParents = new List<string>();
foreach (var inventoryItem in baseBot.Inventory.items)
{
if (validSlots.Contains(inventoryItem.slotId))
{ {
if (validSlots.Contains(inventoryItem.slotId)) validParents.Add(inventoryItem._id);
}
else if (validParents.Contains(inventoryItem.parentId))
{
validParents.Add(inventoryItem._id);
}
else
{
continue;
}
var template = ItemTemplateHelper.GetTemplateById(inventoryItem._tpl);
var parentTemplate = ItemTemplateHelper.GetTemplateById(baseBot.Inventory.items.Single(i => i._id == inventoryItem.parentId)._tpl);
if (!(parentTemplate?._props?.Slots?.FirstOrDefault(slot => slot._name == inventoryItem.slotId)?._required ?? false))
{
if (modCounts.ContainsKey(inventoryItem.slotId.ToLower()))
{ {
validParents.Add(inventoryItem._id); modCounts[inventoryItem.slotId.ToLower()]++;
}
else if (validParents.Contains(inventoryItem.parentId))
{
validParents.Add(inventoryItem._id);
} }
else else
{ {
continue; modCounts.Add(inventoryItem.slotId.ToLower(), 1);
}
var template = ItemTemplateHelper.GetTemplateById(inventoryItem._tpl);
var parentTemplate = ItemTemplateHelper.GetTemplateById(baseBot.Inventory.items.Single(i => i._id == inventoryItem.parentId)._tpl);
if (!(parentTemplate?._props?.Slots?.FirstOrDefault(slot => slot._name == inventoryItem.slotId)?._required ?? false))
{
if (modCounts.ContainsKey(inventoryItem.slotId.ToLower()))
{
modCounts[inventoryItem.slotId.ToLower()]++;
}
else
{
modCounts.Add(inventoryItem.slotId.ToLower(), 1);
}
}
if ((template?._props?.Slots?.Count ?? 0) < 1)
{
// Item has no slots, nothing to count here
continue;
}
foreach (var slot in template._props.Slots)
{
if (slot._required)
{
continue;
}
if (slot._name.StartsWith("camora"))
{
continue;
}
if (slotCounts.ContainsKey(slot._name.ToLower()))
{
slotCounts[slot._name.ToLower()]++;
}
else
{
slotCounts.Add(slot._name.ToLower(), 1);
}
} }
} }
if ((template?._props?.Slots?.Count ?? 0) < 1)
{
// Item has no slots, nothing to count here
continue;
}
foreach (var slot in template._props.Slots)
{
if (slot._required)
{
continue;
}
if (slot._name.StartsWith("camora"))
{
continue;
}
if (slotCounts.ContainsKey(slot._name.ToLower()))
{
slotCounts[slot._name.ToLower()]++;
}
else
{
slotCounts.Add(slot._name.ToLower(), 1);
}
}
}
}
public static void CalculateModChances(Bot bot)
{
if (!weaponModCount.TryGetValue(bot.botType.ToString(), out var modCounts))
{
modCounts = new Dictionary<string, int>();
weaponModCount.Add(bot.botType.ToString(), modCounts);
}
if (!weaponSlotCount.TryGetValue(bot.botType.ToString(), out var slotCounts))
{
slotCounts = new Dictionary<string, int>();
weaponSlotCount.Add(bot.botType.ToString(), slotCounts);
} }
bot.chances.weaponMods = slotCounts.ToDictionary( bot.chances.weaponMods = slotCounts.ToDictionary(
@ -83,76 +110,97 @@ namespace Generator.Helpers.Gear
kvp => GetPercent(kvp.Value, modCounts.GetValueOrDefault(kvp.Key))); kvp => GetPercent(kvp.Value, modCounts.GetValueOrDefault(kvp.Key)));
} }
public static void CalculateEquipmentModChances(Bot bot, List<Datum> baseBots) public static void AddEquipmentModChances(Bot bot, Datum baseBot)
{ {
if (!equipmentModCount.TryGetValue(baseBot.Info.Settings.Role.ToLower(), out var modCounts))
{
modCounts = new Dictionary<string, int>();
equipmentModCount.Add(baseBot.Info.Settings.Role.ToLower(), modCounts);
}
if (!equipmentSlotCount.TryGetValue(baseBot.Info.Settings.Role.ToLower(), out var slotCounts))
{
slotCounts = new Dictionary<string, int>();
equipmentSlotCount.Add(baseBot.Info.Settings.Role.ToLower(), slotCounts);
}
// TODO: Further split these counts by equipment slot? (ex. "FirstPrimaryWeapon", "Holster", etc.) // TODO: Further split these counts by equipment slot? (ex. "FirstPrimaryWeapon", "Holster", etc.)
var validSlots = new List<string> { "Headwear", "ArmorVest", "TacticalVest" }; var validSlots = new List<string> { "Headwear", "ArmorVest", "TacticalVest" };
var modCounts = new Dictionary<string, int>(); var validParents = new List<string>();
var slotCounts = new Dictionary<string, int>();
foreach (var baseBot in baseBots) foreach (var inventoryItem in baseBot.Inventory.items)
{ {
var validParents = new List<string>(); if (validSlots.Contains(inventoryItem.slotId))
foreach (var inventoryItem in baseBot.Inventory.items)
{ {
if (validSlots.Contains(inventoryItem.slotId)) validParents.Add(inventoryItem._id);
}
else if (validParents.Contains(inventoryItem.parentId))
{
validParents.Add(inventoryItem._id);
}
else
{
continue;
}
var template = ItemTemplateHelper.GetTemplateById(inventoryItem._tpl);
var parentTemplate = ItemTemplateHelper.GetTemplateById(baseBot.Inventory.items.Single(i => i._id == inventoryItem.parentId)._tpl);
if (!(parentTemplate?._props?.Slots?.FirstOrDefault(slot => slot._name == inventoryItem.slotId)?._required ?? false))
{
if (modCounts.ContainsKey(inventoryItem.slotId.ToLower()))
{ {
validParents.Add(inventoryItem._id); modCounts[inventoryItem.slotId.ToLower()]++;
}
else if (validParents.Contains(inventoryItem.parentId))
{
validParents.Add(inventoryItem._id);
} }
else else
{ {
continue; modCounts.Add(inventoryItem.slotId.ToLower(), 1);
}
var template = ItemTemplateHelper.GetTemplateById(inventoryItem._tpl);
var parentTemplate = ItemTemplateHelper.GetTemplateById(baseBot.Inventory.items.Single(i => i._id == inventoryItem.parentId)._tpl);
if (!(parentTemplate?._props?.Slots?.FirstOrDefault(slot => slot._name == inventoryItem.slotId)?._required ?? false))
{
if (modCounts.ContainsKey(inventoryItem.slotId.ToLower()))
{
modCounts[inventoryItem.slotId.ToLower()]++;
}
else
{
modCounts.Add(inventoryItem.slotId.ToLower(), 1);
}
}
if ((template?._props?.Slots?.Count ?? 0) < 1)
{
// Item has no slots, nothing to count here
continue;
}
foreach (var slot in template._props.Slots)
{
if (slot._required)
{
continue;
}
if (slot._name.StartsWith("camora"))
{
continue;
}
if (slotCounts.ContainsKey(slot._name.ToLower()))
{
slotCounts[slot._name.ToLower()]++;
}
else
{
slotCounts.Add(slot._name.ToLower(), 1);
}
} }
} }
if ((template?._props?.Slots?.Count ?? 0) < 1)
{
// Item has no slots, nothing to count here
continue;
}
foreach (var slot in template._props.Slots)
{
if (slot._required)
{
continue;
}
if (slot._name.StartsWith("camora"))
{
continue;
}
if (slotCounts.ContainsKey(slot._name.ToLower()))
{
slotCounts[slot._name.ToLower()]++;
}
else
{
slotCounts.Add(slot._name.ToLower(), 1);
}
}
}
}
public static void CalculateEquipmentModChances(Bot bot)
{
if (!equipmentModCount.TryGetValue(bot.botType.ToString(), out var modCounts))
{
modCounts = new Dictionary<string, int>();
equipmentModCount.Add(bot.botType.ToString(), modCounts);
}
if (!equipmentSlotCount.TryGetValue(bot.botType.ToString(), out var slotCounts))
{
slotCounts = new Dictionary<string, int>();
equipmentSlotCount.Add(bot.botType.ToString(), slotCounts);
} }
bot.chances.equipmentMods = slotCounts.ToDictionary( bot.chances.equipmentMods = slotCounts.ToDictionary(
@ -245,47 +293,47 @@ namespace Generator.Helpers.Gear
weightsData["grenades"]); weightsData["grenades"]);
} }
public static void CalculateEquipmentChances(Bot bot, List<Datum> baseBots) public static void AddEquipmentChances(Bot bot, Datum baseBot)
{ {
// TODO: Convert to dynamic? bot.chances.equipment.Headwear += baseBot.Inventory.items.Count(x => x.slotId == "Headwear");
var totalBotsCount = baseBots.Count; bot.chances.equipment.Earpiece += baseBot.Inventory.items.Count(x => x.slotId == "Earpiece");
int headwearCount = 0, earCount = 0, faceCoverCount = 0, armorVestCount = 0, eyeWearCount = 0, armBandCount = 0, bot.chances.equipment.FaceCover += baseBot.Inventory.items.Count(x => x.slotId == "FaceCover");
tacticalVestCount = 0, backpackCount = 0, firstPrimaryCount = 0, secondPrimaryCount = 0, holsterCount = 0, bot.chances.equipment.ArmorVest += baseBot.Inventory.items.Count(x => x.slotId == "ArmorVest");
scabbardCount = 0, pocketsCount = 0, securedContainerCount = 0; bot.chances.equipment.Eyewear += baseBot.Inventory.items.Count(x => x.slotId == "Eyewear");
bot.chances.equipment.ArmBand += baseBot.Inventory.items.Count(x => x.slotId == "ArmBand");
bot.chances.equipment.TacticalVest += baseBot.Inventory.items.Count(x => x.slotId == "TacticalVest");
bot.chances.equipment.Backpack += baseBot.Inventory.items.Count(x => x.slotId == "Backpack");
bot.chances.equipment.FirstPrimaryWeapon += baseBot.Inventory.items.Count(x => x.slotId == "FirstPrimaryWeapon");
bot.chances.equipment.SecondPrimaryWeapon += baseBot.Inventory.items.Count(x => x.slotId == "SecondPrimaryWeapon");
bot.chances.equipment.Holster += baseBot.Inventory.items.Count(x => x.slotId == "Holster");
bot.chances.equipment.Scabbard += baseBot.Inventory.items.Count(x => x.slotId == "Scabbard");
bot.chances.equipment.Pockets += baseBot.Inventory.items.Count(x => x.slotId == "Pockets");
bot.chances.equipment.SecuredContainer += baseBot.Inventory.items.Count(x => x.slotId == "SecuredContainer");
}
foreach (var baseBot in baseBots) public static void CalculateEquipmentChances(Bot bot)
{
if (bot.botCount == 0)
{ {
headwearCount += baseBot.Inventory.items.Count(x => x.slotId == "Headwear"); // No bots, don't do anything
earCount += baseBot.Inventory.items.Count(x => x.slotId == "Earpiece"); return;
faceCoverCount += baseBot.Inventory.items.Count(x => x.slotId == "FaceCover");
armorVestCount += baseBot.Inventory.items.Count(x => x.slotId == "ArmorVest");
eyeWearCount += baseBot.Inventory.items.Count(x => x.slotId == "Eyewear");
armBandCount += baseBot.Inventory.items.Count(x => x.slotId == "ArmBand");
tacticalVestCount += baseBot.Inventory.items.Count(x => x.slotId == "TacticalVest");
backpackCount += baseBot.Inventory.items.Count(x => x.slotId == "Backpack");
firstPrimaryCount += baseBot.Inventory.items.Count(x => x.slotId == "FirstPrimaryWeapon");
secondPrimaryCount += baseBot.Inventory.items.Count(x => x.slotId == "SecondPrimaryWeapon");
holsterCount += baseBot.Inventory.items.Count(x => x.slotId == "Holster");
scabbardCount += baseBot.Inventory.items.Count(x => x.slotId == "Scabbard");
pocketsCount += baseBot.Inventory.items.Count(x => x.slotId == "Pockets");
securedContainerCount += baseBot.Inventory.items.Count(x => x.slotId == "SecuredContainer");
} }
bot.chances.equipment = new EquipmentChances( bot.chances.equipment = new EquipmentChances(
GetPercent(totalBotsCount, headwearCount), GetPercent(bot.botCount, bot.chances.equipment.Headwear),
GetPercent(totalBotsCount, earCount), GetPercent(bot.botCount, bot.chances.equipment.Earpiece),
GetPercent(totalBotsCount, faceCoverCount), GetPercent(bot.botCount, bot.chances.equipment.FaceCover),
GetPercent(totalBotsCount, armorVestCount), GetPercent(bot.botCount, bot.chances.equipment.ArmorVest),
GetPercent(totalBotsCount, eyeWearCount), GetPercent(bot.botCount, bot.chances.equipment.Eyewear),
GetPercent(totalBotsCount, armBandCount), GetPercent(bot.botCount, bot.chances.equipment.ArmBand),
GetPercent(totalBotsCount, tacticalVestCount), GetPercent(bot.botCount, bot.chances.equipment.TacticalVest),
GetPercent(totalBotsCount, backpackCount), GetPercent(bot.botCount, bot.chances.equipment.Backpack),
GetPercent(totalBotsCount, firstPrimaryCount), GetPercent(bot.botCount, bot.chances.equipment.FirstPrimaryWeapon),
GetPercent(totalBotsCount, secondPrimaryCount), GetPercent(bot.botCount, bot.chances.equipment.SecondPrimaryWeapon),
GetPercent(totalBotsCount, holsterCount), GetPercent(bot.botCount, bot.chances.equipment.Holster),
GetPercent(totalBotsCount, scabbardCount), GetPercent(bot.botCount, bot.chances.equipment.Scabbard),
GetPercent(totalBotsCount, pocketsCount), GetPercent(bot.botCount, bot.chances.equipment.Pockets),
GetPercent(totalBotsCount, securedContainerCount)); GetPercent(bot.botCount, bot.chances.equipment.SecuredContainer));
} }
private static int GetPercent(int total, int count) private static int GetPercent(int total, int count)

View File

@ -65,7 +65,6 @@ namespace Generator.Helpers.Gear
internal static void AddAmmo(Bot botToUpdate, Datum bot) internal static void AddAmmo(Bot botToUpdate, Datum bot)
{ {
//var weightService = new WeightingService();
foreach (var ammo in bot.Inventory.items.Where( foreach (var ammo in bot.Inventory.items.Where(
x => x.slotId != null x => x.slotId != null
&& (x.slotId == "patron_in_weapon" && (x.slotId == "patron_in_weapon"
@ -117,7 +116,6 @@ namespace Generator.Helpers.Gear
public static void AddEquippedGear(Bot botToUpdate, Datum bot) public static void AddEquippedGear(Bot botToUpdate, Datum bot)
{ {
// add equipped gear // add equipped gear
var weightService = new WeightingService();
foreach (var inventoryItem in bot.Inventory.items.Where(x=>x.slotId != null)) foreach (var inventoryItem in bot.Inventory.items.Where(x=>x.slotId != null))
{ {
switch (inventoryItem.slotId?.ToLower()) switch (inventoryItem.slotId?.ToLower())
@ -298,7 +296,13 @@ namespace Generator.Helpers.Gear
foreach (var cartridge in botToUpdate.inventory.Ammo.Keys) foreach (var cartridge in botToUpdate.inventory.Ammo.Keys)
{ {
var cartridgeWithWeights = botToUpdate.inventory.Ammo[cartridge]; var cartridgeWithWeights = botToUpdate.inventory.Ammo[cartridge];
foreach (var cartridgeKvP in cartridgeWithWeights)
{
cartridgeWithWeights[cartridgeKvP.Key] = ReduceValueAccuracy(cartridgeKvP.Value);
}
var weights = cartridgeWithWeights.Values.Select(x => x).ToList(); var weights = cartridgeWithWeights.Values.Select(x => x).ToList();
var commonAmmoDivisor = CommonDivisor(weights); var commonAmmoDivisor = CommonDivisor(weights);
foreach (var cartridgeWeightKvP in cartridgeWithWeights) foreach (var cartridgeWeightKvP in cartridgeWithWeights)
@ -306,7 +310,7 @@ namespace Generator.Helpers.Gear
botToUpdate.inventory.Ammo[cartridge][cartridgeWeightKvP.Key] /= commonAmmoDivisor; botToUpdate.inventory.Ammo[cartridge][cartridgeWeightKvP.Key] /= commonAmmoDivisor;
} }
} }
} }
} }
public static void ReduceEquipmentWeightValues(Equipment equipment) public static void ReduceEquipmentWeightValues(Equipment equipment)
@ -343,6 +347,11 @@ namespace Generator.Helpers.Gear
return; return;
} }
foreach (var itemWeightKvp in equipmentDict)
{
equipmentDict[itemWeightKvp.Key] = ReduceValueAccuracy(itemWeightKvp.Value, 4);
}
var weights = equipmentDict.Values.Select(x => x).ToList(); var weights = equipmentDict.Values.Select(x => x).ToList();
var commonAmmoDivisor = CommonDivisor(weights); var commonAmmoDivisor = CommonDivisor(weights);
@ -357,5 +366,13 @@ namespace Generator.Helpers.Gear
equipmentDict[itemTplWithWeight.Key] /= commonAmmoDivisor; equipmentDict[itemTplWithWeight.Key] /= commonAmmoDivisor;
} }
} }
private static int ReduceValueAccuracy(long x, int digits = 3)
{
int i = (int)Math.Log10(x);
i = Math.Max(0, i - (digits - 1));
i = (int)Math.Pow(10, i);
return (int)(x / i * i);
}
} }
} }

View File

@ -1,5 +1,7 @@
using Common.Models.Input; using Common.Models.Input;
using Common.Models.Output;
using Generator.Helpers; using Generator.Helpers;
using System.Diagnostics;
namespace Generator; namespace Generator;
@ -7,6 +9,9 @@ internal static class Program
{ {
internal static async Task Main(string[] args) internal static async Task Main(string[] args)
{ {
var stopwatch = Stopwatch.StartNew();
LoggingHelpers.LogToConsole("Started processing bots");
// Create list of bots we want to process // Create list of bots we want to process
string[] botTypes = { string[] botTypes = {
"assault", "assault",
@ -60,44 +65,31 @@ internal static class Program
"spiritwinter", "spiritwinter",
"skier", "skier",
"peacemaker" "peacemaker",
"infectedassault",
"infectedpmc",
"infectedcivil",
"infectedlaborant",
"infectedtagilla",
}; };
// Read raw bot dumps and turn into c# objects // Read raw bot dumps and turn into c# objects
var workingPath = Directory.GetCurrentDirectory(); var workingPath = Directory.GetCurrentDirectory();
var dumpPath = $"{workingPath}//dumps"; var dumpPath = $"{workingPath}//dumps";
var parsedBots = await BotParser.ParseAsync(dumpPath, botTypes.ToHashSet()); List<Bot> bots = BotParser.Parse(dumpPath, botTypes.ToHashSet());
// Put in dictionary for better use later on if (bots.Count == 0)
var rawBotsCache = new Dictionary<string, List<Datum>>(45);
foreach (var rawBot in parsedBots)
{
if (rawBotsCache.TryGetValue(rawBot.Info.Settings.Role.ToLower(), out var botList))
{
botList.Add(rawBot);
continue;
}
// Doesnt exist, add key and bot
rawBotsCache.Add(rawBot.Info.Settings.Role.ToLower(), new List<Datum> { rawBot });
}
if (parsedBots.Count == 0)
{ {
LoggingHelpers.LogToConsole("No bots found, unable to continue"); LoggingHelpers.LogToConsole("No bots found, unable to continue");
LoggingHelpers.LogToConsole("Check your dumps are in 'Generator\\bin\\Debug\\net6.0\\dumps' and start with 'resp.' NOT 'req.'"); LoggingHelpers.LogToConsole("Check your dumps are in 'Generator\\bin\\Debug\\net6.0\\dumps' and start with 'resp.' NOT 'req.'");
return; return;
} }
// Generate the base bot class with basic details (health/body part hp etc) and then append everything else
var bots = BaseBotGenerator.GenerateBaseDetails(parsedBots, workingPath, botTypes)
.AddGear(rawBotsCache) // Add weapons/armor
.AddLoot(rawBotsCache)
.AddChances(rawBotsCache); // Add mod/equipment chances
// Output bot to json file
var jsonWriter = new JsonWriter(workingPath, "output"); var jsonWriter = new JsonWriter(workingPath, "output");
jsonWriter.WriteJson(bots.ToList()); jsonWriter.WriteJson(bots);
stopwatch.Stop();
LoggingHelpers.LogToConsole($"Finished processing bots. Took {LoggingHelpers.LogTimeTaken(stopwatch.Elapsed.TotalSeconds)} seconds");
} }
} }

View File

@ -14,11 +14,14 @@ namespace Generator.Weighting
public class WeightingService public class WeightingService
{ {
private readonly Dictionary<BotType, Weightings> _weights; private static Dictionary<BotType, Weightings> _weights = null;
private readonly Dictionary<string, Dictionary<string, GenerationWeightData>> _generationWeights; private static Dictionary<string, Dictionary<string, GenerationWeightData>> _generationWeights = null;
public WeightingService() public WeightingService()
{ {
// Cache the loaded data
if (_weights != null && _generationWeights != null) return;
var assetsPath = $"{Directory.GetCurrentDirectory()}\\Assets"; var assetsPath = $"{Directory.GetCurrentDirectory()}\\Assets";
var weightsFilePath = $"{assetsPath}\\weights.json"; var weightsFilePath = $"{assetsPath}\\weights.json";
if (!File.Exists(weightsFilePath)) if (!File.Exists(weightsFilePath))

View File

@ -2,11 +2,11 @@
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net9.0</TargetFramework>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>