From 27422ff33a7e3acd30fbcc1a7b757da3abfdefdb Mon Sep 17 00:00:00 2001 From: Alex Date: Tue, 7 Jan 2025 20:00:37 +0000 Subject: [PATCH] more db progress --- Core/Controllers/BotController.cs | 2 +- Core/Models/Eft/Common/Tables/BotBase.cs | 78 ++++++++++--------- Core/Models/Eft/Common/Tables/BotCore.cs | 5 +- Core/Models/Eft/Common/Tables/BotType.cs | 41 ++++++++++ Core/Models/Spt/Bots/Bots.cs | 11 ++- Core/Models/Spt/Config/QuestConfig.cs | 2 +- Core/Models/Spt/Config/WeatherConfig.cs | 8 +- Core/Utils/ImporterUtil.cs | 41 +++++++--- .../ArrayToObjectFactoryConverter.cs | 46 +++++++++++ .../Converters/NotNullObjectToIntConverter.cs | 36 --------- .../NullableObjectToLongConverter.cs | 38 --------- .../StringToNumberFactoryConverter.cs | 49 ++++++++++++ Server/Assets/database/bots/base.json | 4 +- 13 files changed, 228 insertions(+), 133 deletions(-) create mode 100644 Core/Utils/Json/Converters/ArrayToObjectFactoryConverter.cs delete mode 100644 Core/Utils/Json/Converters/NotNullObjectToIntConverter.cs delete mode 100644 Core/Utils/Json/Converters/NullableObjectToLongConverter.cs create mode 100644 Core/Utils/Json/Converters/StringToNumberFactoryConverter.cs diff --git a/Core/Controllers/BotController.cs b/Core/Controllers/BotController.cs index cd8a2c94..1092eae2 100644 --- a/Core/Controllers/BotController.cs +++ b/Core/Controllers/BotController.cs @@ -22,7 +22,7 @@ public class BotController throw new NotImplementedException(); } - public BotCore GetBotCoreDifficulty() + public Dictionary GetBotCoreDifficulty() { throw new NotImplementedException(); } diff --git a/Core/Models/Eft/Common/Tables/BotBase.cs b/Core/Models/Eft/Common/Tables/BotBase.cs index c6ffc216..8f7cc0d1 100644 --- a/Core/Models/Eft/Common/Tables/BotBase.cs +++ b/Core/Models/Eft/Common/Tables/BotBase.cs @@ -1,6 +1,8 @@ -using System.Text.Json.Serialization; +using System.Reflection; +using System.Text.Json.Serialization; using Core.Models.Eft.Ragfair; using Core.Models.Enums; +using Core.Utils.Json.Converters; namespace Core.Models.Eft.Common.Tables; @@ -10,7 +12,7 @@ public class BotBase public string Id { get; set; } [JsonPropertyName("aid")] - public int Aid { get; set; } + public double Aid { get; set; } /** SPT property - use to store player id - TODO - move to AID ( account id as guid of choice) */ [JsonPropertyName("sessionId")] @@ -20,7 +22,7 @@ public class BotBase public string? Savage { get; set; } [JsonPropertyName("karmaValue")] - public int KarmaValue { get; set; } + public double KarmaValue { get; set; } [JsonPropertyName("Info")] public Info Info { get; set; } @@ -87,6 +89,7 @@ public class BotBase public SurvivorClass SurvivorClass { get; set; } [JsonPropertyName("WishList")] + [JsonConverter(typeof(ArrayToObjectFactoryConverter))] public Dictionary WishList { get; set; } [JsonPropertyName("moneyTransferLimitData")] @@ -147,11 +150,11 @@ public class Info public bool HasCoopExtension { get; set; } public bool HasPveGame { get; set; } public string Voice { get; set; } - public int Level { get; set; } - public int Experience { get; set; } + public double Level { get; set; } + public double Experience { get; set; } public long RegistrationDate { get; set; } public string GameVersion { get; set; } - public int AccountType { get; set; } + public double AccountType { get; set; } public MemberCategory MemberCategory { get; set; } public MemberCategory SelectedMemberCategory { get; set; } @@ -183,9 +186,9 @@ public class BotInfoSettings { public string Role { get; set; } public string BotDifficulty { get; set; } - public int Experience { get; set; } - public int StandingForKill { get; set; } - public int AggressorBonus { get; set; } + public double Experience { get; set; } + public double StandingForKill { get; set; } + public double AggressorBonus { get; set; } public bool UseSimpleAnimator { get; set; } } @@ -223,7 +226,7 @@ public class BotBaseHealth public CurrentMax Energy { get; set; } public CurrentMax Temperature { get; set; } public BodyPartsHealth BodyParts { get; set; } - public int UpdateTime { get; set; } + public double UpdateTime { get; set; } public bool? Immortal { get; set; } } @@ -248,13 +251,13 @@ public class BodyPartEffectProperties { // TODO: this was any, what actual type is it? public object? ExtraData { get; set; } - public int Time { get; set; } + public double Time { get; set; } } public class CurrentMax { - public int Current { get; set; } - public int Maximum { get; set; } + public double Current { get; set; } + public double Maximum { get; set; } } public class BotBaseInventory @@ -292,20 +295,20 @@ public class BaseJsonSkills { public Dictionary Common { get; set; } public Dictionary Mastering { get; set; } - public int Points { get; set; } + public double Points { get; set; } } public class Skills { public List Common { get; set; } public List Mastering { get; set; } - public int Points { get; set; } + public double Points { get; set; } } public class BaseSkill { public string Id { get; set; } - public int Progress { get; set; } + public double Progress { get; set; } [JsonPropertyName("max")] public int? Max { get; set; } @@ -333,7 +336,7 @@ public class EftStats { public List CarriedQuestItems { get; set; } public List Victims { get; set; } - public int TotalSessionExperience { get; set; } + public double TotalSessionExperience { get; set; } public long LastSessionDate { get; set; } public SessionCounters SessionCounters { get; set; } public OverallCounters OverallCounters { get; set; } @@ -345,7 +348,7 @@ public class EftStats public DamageHistory? DamageHistory { get; set; } public DeathCause? DeathCause { get; set; } public LastPlayerState? LastPlayerState { get; set; } - public int TotalInGameTime { get; set; } + public double TotalInGameTime { get; set; } public string? SurvivorClass { get; set; } [JsonPropertyName("sptLastRaidFenceRepChange")] @@ -375,7 +378,7 @@ public class Victim public string BodyPart { get; set; } public string Time { get; set; } public float Distance { get; set; } - public int Level { get; set; } + public double Level { get; set; } public string Weapon { get; set; } public string Role { get; set; } public string Location { get; set; } @@ -394,7 +397,7 @@ public class OverallCounters public class CounterKeyValue { public List Key { get; set; } - public int Value { get; set; } + public double Value { get; set; } } public class Aggressor @@ -414,18 +417,19 @@ public class DamageHistory { public string LethalDamagePart { get; set; } public LethalDamage LethalDamage { get; set; } + [JsonConverter(typeof(ArrayToObjectFactoryConverter))] public BodyPartsDamageHistory BodyParts { get; set; } } // TODO: this class seems exactly the same as DamageStats, why have it? public class LethalDamage { - public int Amount { get; set; } + public double Amount { get; set; } public string Type { get; set; } public string SourceId { get; set; } public string OverDamageFrom { get; set; } public bool Blunt { get; set; } - public int ImpactsCount { get; set; } + public double ImpactsCount { get; set; } } public class BodyPartsDamageHistory @@ -442,12 +446,12 @@ public class BodyPartsDamageHistory public class DamageStats { - public int Amount { get; set; } + public double Amount { get; set; } public string Type { get; set; } public string SourceId { get; set; } public string OverDamageFrom { get; set; } public bool Blunt { get; set; } - public int ImpactsCount { get; set; } + public double ImpactsCount { get; set; } } public class DeathCause @@ -471,7 +475,7 @@ public class LastPlayerStateInfo { public string Nickname { get; set; } public string Side { get; set; } - public int Level { get; set; } + public double Level { get; set; } public MemberCategory MemberCategory { get; set; } } @@ -484,7 +488,7 @@ public class BackendCounter public string? QId { get; set; } [JsonPropertyName("value")] - public int Value { get; set; } + public double Value { get; set; } } public class InsuredItem @@ -503,7 +507,7 @@ public class Hideout public List Areas { get; set; } public Dictionary Improvements { get; set; } public HideoutCounters HideoutCounters { get; set; } - public int Seed { get; set; } + public double Seed { get; set; } public List MannequinPoses { get; set; } [JsonPropertyName("sptUpdateLastRunTimestamp")] @@ -513,16 +517,16 @@ public class Hideout public class HideoutCounters { [JsonPropertyName("fuelCounter")] - public int FuelCounter { get; set; } + public double FuelCounter { get; set; } [JsonPropertyName("airFilterCounter")] - public int AirFilterCounter { get; set; } + public double AirFilterCounter { get; set; } [JsonPropertyName("waterFilterCounter")] - public int WaterFilterCounter { get; set; } + public double WaterFilterCounter { get; set; } [JsonPropertyName("craftingTimeCounter")] - public int CraftingTimeCounter { get; set; } + public double CraftingTimeCounter { get; set; } } public class HideoutImprovement @@ -613,7 +617,7 @@ public class BotHideoutArea public HideoutAreas Type { get; set; } [JsonPropertyName("level")] - public int Level { get; set; } + public double Level { get; set; } [JsonPropertyName("active")] public bool Active { get; set; } @@ -623,7 +627,7 @@ public class BotHideoutArea /** Must be integer */ [JsonPropertyName("completeTime")] - public int CompleteTime { get; set; } + public double CompleteTime { get; set; } [JsonPropertyName("constructing")] public bool Constructing { get; set; } @@ -641,7 +645,7 @@ public class HideoutSlot /// SPT specific value to keep track of what index this slot is (0,1,2,3 etc) /// [JsonPropertyName("locationIndex")] - public int LocationIndex { get; set; } + public double LocationIndex { get; set; } [JsonPropertyName("item")] public List? Items { get; set; } @@ -708,13 +712,13 @@ public class TraderInfo public int? LoyaltyLevel { get; set; } [JsonPropertyName("salesSum")] - public int SalesSum { get; set; } + public double SalesSum { get; set; } [JsonPropertyName("standing")] - public int Standing { get; set; } + public double Standing { get; set; } [JsonPropertyName("nextResupply")] - public int NextResupply { get; set; } + public double NextResupply { get; set; } [JsonPropertyName("unlocked")] public bool Unlocked { get; set; } diff --git a/Core/Models/Eft/Common/Tables/BotCore.cs b/Core/Models/Eft/Common/Tables/BotCore.cs index 53fcdf60..7d5cbee0 100644 --- a/Core/Models/Eft/Common/Tables/BotCore.cs +++ b/Core/Models/Eft/Common/Tables/BotCore.cs @@ -2,7 +2,7 @@ namespace Core.Models.Eft.Common.Tables; - +/* public class BotCore { [JsonPropertyName("SAVAGE_KILL_DIST")] @@ -397,4 +397,5 @@ public class BotCore [JsonPropertyName("AXE_MAN_KILLS_END")] public double AxeManKillsEnd { get; set; } -} \ No newline at end of file +} +*/ \ No newline at end of file diff --git a/Core/Models/Eft/Common/Tables/BotType.cs b/Core/Models/Eft/Common/Tables/BotType.cs index c836b4a0..3634e5f2 100644 --- a/Core/Models/Eft/Common/Tables/BotType.cs +++ b/Core/Models/Eft/Common/Tables/BotType.cs @@ -1,5 +1,6 @@ using System.Text.Json.Serialization; using Core.Models.Common; +using Core.Utils.Json.Converters; namespace Core.Models.Eft.Common.Tables; @@ -188,6 +189,9 @@ public class ModsChances [JsonPropertyName("mod_stock_000")] public double ModStock000 { get; set; } + [JsonPropertyName("mod_stock_002")] + public double ModStock002 { get; set; } + [JsonPropertyName("mod_stock_akms")] public double ModStockAkms { get; set; } @@ -203,18 +207,52 @@ public class ModsChances [JsonPropertyName("mod_tactical_002")] public double ModTactical002 { get; set; } + [JsonPropertyName("mod_tactical_2")] + public double ModTactical2 { get; set; } + [JsonPropertyName("mod_tactical_003")] public double ModTactical003 { get; set; } [JsonPropertyName("mod_handguard")] public double ModHandguard { get; set; } + + [JsonPropertyName("back_plate")] + public double BackPlate { get; set; } + + [JsonPropertyName("front_plate")] + public double FrontPlate { get; set; } + + [JsonPropertyName("left_side_plate")] + public double LeftSidePlate { get; set; } + + [JsonPropertyName("right_side_plate")] + public double RightSidePlate { get; set; } + + [JsonPropertyName("mod_mount_002")] + public double ModMount002 { get; set; } + + [JsonPropertyName("mod_mount_003")] + public double ModMount003 { get; set; } + + [JsonPropertyName("mod_mount_004")] + public double ModMount004 { get; set; } + + [JsonPropertyName("mod_muzzle_000")] + public double ModMuzzle000 { get; set; } + + [JsonPropertyName("mod_muzzle_001")] + public double ModMuzzle001 { get; set; } } public class Difficulties { + [JsonPropertyName("easy")] public DifficultyCategories Easy { get; set; } + [JsonPropertyName("normal")] public DifficultyCategories Normal { get; set; } + [JsonPropertyName("hard")] public DifficultyCategories Hard { get; set; } + [JsonPropertyName("impossible")] public DifficultyCategories Impossible { get; set; } } @@ -242,6 +280,7 @@ public class Experience [JsonPropertyName("aggressorBonus")] public Dictionary AggressorBonus { get; set; } + [JsonPropertyName("level")] public MinMax Level { get; set; } /** key = bot difficulty */ @@ -309,6 +348,7 @@ public class GenerationData /** Array of item tpls */ [JsonPropertyName("whitelist")] + [JsonConverter(typeof(ArrayToObjectFactoryConverter))] public Dictionary Whitelist { get; set; } } @@ -341,6 +381,7 @@ public class BotTypeInventory [JsonPropertyName("items")] public ItemPools Items { get; set; } + [JsonPropertyName("mods")] public GlobalMods Mods { get; set; } } diff --git a/Core/Models/Spt/Bots/Bots.cs b/Core/Models/Spt/Bots/Bots.cs index 2fc13d43..a296ff9b 100644 --- a/Core/Models/Spt/Bots/Bots.cs +++ b/Core/Models/Spt/Bots/Bots.cs @@ -5,8 +5,13 @@ namespace Core.Models.Spt.Bots; public class Bots { - public Dictionary types { get; } + + [JsonPropertyName("types")] + public Dictionary Types { get; set; } + [JsonPropertyName("base")] - public BotBase Base { get; } - public BotCore core { get; } + public BotBase Base { get; set; } + + [JsonPropertyName("core")] + public Dictionary Core { get; set; } } \ No newline at end of file diff --git a/Core/Models/Spt/Config/QuestConfig.cs b/Core/Models/Spt/Config/QuestConfig.cs index 29fa0175..c7830a02 100644 --- a/Core/Models/Spt/Config/QuestConfig.cs +++ b/Core/Models/Spt/Config/QuestConfig.cs @@ -78,7 +78,7 @@ public class EventQuestData public long StartTimestamp { get; set; } [JsonPropertyName("endTimestamp")] - [JsonConverter(typeof(NullableObjectToLongConverter))] + [JsonConverter(typeof(StringToNumberFactoryConverter))] public long? EndTimestamp { get; set; } [JsonPropertyName("yearly")] diff --git a/Core/Models/Spt/Config/WeatherConfig.cs b/Core/Models/Spt/Config/WeatherConfig.cs index be4a8ec0..3f8f1478 100644 --- a/Core/Models/Spt/Config/WeatherConfig.cs +++ b/Core/Models/Spt/Config/WeatherConfig.cs @@ -32,19 +32,19 @@ public class SeasonDateTimes public string Name { get; set; } [JsonPropertyName("startDay")] - [JsonConverter(typeof(NotNullObjectToIntConverter))] + [JsonConverter(typeof(StringToNumberFactoryConverter))] public int StartDay { get; set; } [JsonPropertyName("startMonth")] - [JsonConverter(typeof(NotNullObjectToIntConverter))] + [JsonConverter(typeof(StringToNumberFactoryConverter))] public int StartMonth { get; set; } [JsonPropertyName("endDay")] - [JsonConverter(typeof(NotNullObjectToIntConverter))] + [JsonConverter(typeof(StringToNumberFactoryConverter))] public int EndDay { get; set; } [JsonPropertyName("endMonth")] - [JsonConverter(typeof(NotNullObjectToIntConverter))] + [JsonConverter(typeof(StringToNumberFactoryConverter))] public int EndMonth { get; set; } } diff --git a/Core/Utils/ImporterUtil.cs b/Core/Utils/ImporterUtil.cs index 40c2bc9f..fb43f071 100644 --- a/Core/Utils/ImporterUtil.cs +++ b/Core/Utils/ImporterUtil.cs @@ -1,3 +1,4 @@ +using System.Reflection; using System.Text; using System.Text.Json; using System.Text.Json.Serialization; @@ -42,24 +43,44 @@ public class ImporterUtil if (onReadCallback != null) onReadCallback(file, fileData); - var matchedProperty = loadedType.GetProperties().FirstOrDefault(prop => prop.Name.ToLower() == Path.GetFileNameWithoutExtension(file).ToLower()); - if (matchedProperty == null) - throw new Exception($"Unable to find property '{Path.GetFileNameWithoutExtension(file)}' for type '{loadedType.Name}'"); - var propertyType = matchedProperty.PropertyType; - var fileDeserialized = JsonSerializer.Deserialize(fileData, propertyType, new JsonSerializerOptions { UnmappedMemberHandling = JsonUnmappedMemberHandling.Disallow }); - if (onObjectDeserialized != null) - onObjectDeserialized(file, fileDeserialized); + Type propertyType; + MethodInfo setMethod; + bool isDictionary = false; + if (loadedType.IsGenericType && loadedType.GetGenericTypeDefinition() == typeof(Dictionary<,>)) + { + propertyType = loadedType.GetGenericArguments()[1]; + setMethod = loadedType.GetMethod("Add"); + isDictionary = true; + } + else + { + var matchedProperty = loadedType.GetProperties().FirstOrDefault(prop => prop.Name.ToLower() == Path.GetFileNameWithoutExtension(file).ToLower()); + if (matchedProperty == null) + throw new Exception($"Unable to find property '{Path.GetFileNameWithoutExtension(file)}' for type '{loadedType.Name}'"); + propertyType = matchedProperty.PropertyType; + setMethod = matchedProperty.GetSetMethod(); + } + try + { + var fileDeserialized = JsonSerializer.Deserialize(fileData, propertyType, new JsonSerializerOptions { UnmappedMemberHandling = JsonUnmappedMemberHandling.Disallow }); + if (onObjectDeserialized != null) + onObjectDeserialized(file, fileDeserialized); - matchedProperty.SetValue(result, fileDeserialized); + setMethod.Invoke(result, isDictionary ? [Path.GetFileNameWithoutExtension(file), fileDeserialized] : [fileDeserialized]); + } + catch (Exception e) + { + throw new Exception($"Unable to deserialize or set properties for file '{file}'", e); + } } // deep tree search foreach (var directory in directories) { - var matchedProperty = loadedType.GetProperties().FirstOrDefault(prop => prop.Name.ToLower() == directory.ToLower()); + var matchedProperty = loadedType.GetProperties().FirstOrDefault(prop => prop.Name.ToLower() == directory.Split("/").Last().ToLower()); if (matchedProperty == null) throw new Exception($"Unable to find property '{directory}' for type '{loadedType.Name}'"); - matchedProperty.GetSetMethod().Invoke(result, [await LoadRecursiveAsync($"{filepath}{directory}/", matchedProperty.PropertyType)]); + matchedProperty.GetSetMethod().Invoke(result, [await LoadRecursiveAsync($"{directory}/", matchedProperty.PropertyType)]); } // return the result of all async fetch diff --git a/Core/Utils/Json/Converters/ArrayToObjectFactoryConverter.cs b/Core/Utils/Json/Converters/ArrayToObjectFactoryConverter.cs new file mode 100644 index 00000000..27e11ce8 --- /dev/null +++ b/Core/Utils/Json/Converters/ArrayToObjectFactoryConverter.cs @@ -0,0 +1,46 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Core.Utils.Json.Converters; + +public class ArrayToObjectFactoryConverter : JsonConverterFactory +{ + public override bool CanConvert(Type typeToConvert) + { + return true; + } + + public override JsonConverter? CreateConverter(Type typeToConvert, JsonSerializerOptions options) + { + return (JsonConverter) Activator.CreateInstance(typeof(ArrayToObjectConverter<>).MakeGenericType(typeToConvert)); + } + + class ArrayToObjectConverter : JsonConverter + { + public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + switch (reader.TokenType) + { + case JsonTokenType.StartArray: + // start array + reader.Read(); + return default; + case JsonTokenType.StartObject: + using (var jsonDocument = JsonDocument.ParseValue(ref reader)) + { + var jsonText = jsonDocument.RootElement.GetRawText(); + return JsonSerializer.Deserialize(jsonText); + } + } + + return default; + } + + public override void Write(Utf8JsonWriter writer, T? value, JsonSerializerOptions options) + { + if (value == null) + value = default; + JsonSerializer.Serialize(writer, value, options); + } + } +} \ No newline at end of file diff --git a/Core/Utils/Json/Converters/NotNullObjectToIntConverter.cs b/Core/Utils/Json/Converters/NotNullObjectToIntConverter.cs deleted file mode 100644 index 85fe83db..00000000 --- a/Core/Utils/Json/Converters/NotNullObjectToIntConverter.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Text.Json; -using System.Text.Json.Serialization; - -namespace Core.Utils.Json.Converters; - -public class NotNullObjectToIntConverter : JsonConverter -{ - public override int Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - int result; - switch (reader.TokenType) - { - case JsonTokenType.String: - var value = reader.GetString(); - if (string.IsNullOrWhiteSpace(value) || !int.TryParse(value, out result)) - return 0; - break; - case JsonTokenType.Number: - result = reader.GetInt32(); - break; - case JsonTokenType.Null: - return 0; - default: - throw new ArgumentOutOfRangeException(); - } - return result; - } - - public override void Write(Utf8JsonWriter writer, int value, JsonSerializerOptions options) - { - if (value == null) - writer.WriteStringValue(""); - else - writer.WriteStringValue($"{value}"); - } -} \ No newline at end of file diff --git a/Core/Utils/Json/Converters/NullableObjectToLongConverter.cs b/Core/Utils/Json/Converters/NullableObjectToLongConverter.cs deleted file mode 100644 index 36b8baf2..00000000 --- a/Core/Utils/Json/Converters/NullableObjectToLongConverter.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System.Text.Json; -using System.Text.Json.Serialization; - -namespace Core.Utils.Json.Converters; - -public class NullableObjectToLongConverter : JsonConverter -{ - public override long? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - long result; - switch (reader.TokenType) - { - case JsonTokenType.String: - var value = reader.GetString(); - if (string.IsNullOrWhiteSpace(value) || !long.TryParse(value, out result)) - return null; - break; - case JsonTokenType.Number: - result = reader.GetInt64(); - break; - case JsonTokenType.Null: - return null; - default: - throw new ArgumentOutOfRangeException(); - } - return result; - } - - public override void Write(Utf8JsonWriter writer, long? value, JsonSerializerOptions options) - { - if (value == null) - writer.WriteStringValue(""); - else if (value is long longValue) - writer.WriteNumberValue(longValue); - else - throw new Exception("Cannot convert the object valur to a long."); - } -} \ No newline at end of file diff --git a/Core/Utils/Json/Converters/StringToNumberFactoryConverter.cs b/Core/Utils/Json/Converters/StringToNumberFactoryConverter.cs new file mode 100644 index 00000000..4fe35603 --- /dev/null +++ b/Core/Utils/Json/Converters/StringToNumberFactoryConverter.cs @@ -0,0 +1,49 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Core.Utils.Json.Converters; + +public class StringToNumberFactoryConverter : JsonConverterFactory +{ + public override bool CanConvert(Type typeToConvert) + { + return true; + } + + public override JsonConverter? CreateConverter(Type typeToConvert, JsonSerializerOptions options) + { + return (JsonConverter) Activator.CreateInstance(typeof(StringToNumberConverter<>).MakeGenericType(typeToConvert)); + } + + class StringToNumberConverter : JsonConverter + { + public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + switch (reader.TokenType) + { + case JsonTokenType.String: + var value = reader.GetString(); + if (string.IsNullOrWhiteSpace(value)) + return default; + goto case JsonTokenType.Number; + case JsonTokenType.Number: + using (var jsonDocument = JsonDocument.ParseValue(ref reader)) + { + var jsonText = jsonDocument.RootElement.GetRawText().Replace("\"", ""); + return JsonSerializer.Deserialize(jsonText); + } + case JsonTokenType.Null: + return default; + default: + throw new ArgumentOutOfRangeException(); + } + } + + public override void Write(Utf8JsonWriter writer, T? value, JsonSerializerOptions options) + { + if (value == null) + value = default; + JsonSerializer.Serialize(writer, value, options); + } + } +} \ No newline at end of file diff --git a/Server/Assets/database/bots/base.json b/Server/Assets/database/bots/base.json index 49fb4a29..9c168991 100644 --- a/Server/Assets/database/bots/base.json +++ b/Server/Assets/database/bots/base.json @@ -76,7 +76,9 @@ "DamageHistory": { "LethalDamagePart": "Head", "LethalDamage": null, - "BodyParts": [] + "BodyParts": { + "Head": [] + } }, "LastPlayerState": null, "TotalInGameTime": 0,