diff --git a/Core/Models/Eft/Common/Tables/Achievement.cs b/Core/Models/Eft/Common/Tables/Achievement.cs index 127965a1..4cf81a8c 100644 --- a/Core/Models/Eft/Common/Tables/Achievement.cs +++ b/Core/Models/Eft/Common/Tables/Achievement.cs @@ -49,3 +49,21 @@ public class Achievement [JsonPropertyName("index")] public int? Index { get; set; } } + +public class AchievementQuestConditionTypes +{ + [JsonPropertyName("started")] + public List? Started { get; set; } + + [JsonPropertyName("availableForFinish")] + public List? AvailableForFinish { get; set; } + + [JsonPropertyName("availableForStart")] + public List? AvailableForStart { get; set; } + + [JsonPropertyName("success")] + public List? Success { get; set; } + + [JsonPropertyName("fail")] + public List? Fail { get; set; } +} diff --git a/Core/Models/Eft/Common/Tables/BotBase.cs b/Core/Models/Eft/Common/Tables/BotBase.cs index 46fc153b..2ca32223 100644 --- a/Core/Models/Eft/Common/Tables/BotBase.cs +++ b/Core/Models/Eft/Common/Tables/BotBase.cs @@ -2,6 +2,7 @@ using System.Text.Json.Serialization; using Core.Models.Eft.Ragfair; using Core.Models.Enums; +using Core.Utils.Json; using Core.Utils.Json.Converters; namespace Core.Models.Eft.Common.Tables; @@ -12,6 +13,7 @@ public class BotBase public string? Id { get; set; } [JsonPropertyName("aid")] + [JsonConverter(typeof(StringToNumberFactoryConverter))] public double? Aid { get; set; } /** SPT property - use to store player id - TODO - move to AID ( account id as guid of choice) */ @@ -293,6 +295,9 @@ public class BotBaseInventory [JsonPropertyName("favoriteItems")] public List? FavoriteItems { get; set; } + + [JsonPropertyName("hideoutCustomizationStashId")] + public string? HideoutCustomizationStashId { get; set; } } public class BaseJsonSkills @@ -304,8 +309,7 @@ public class BaseJsonSkills public class Skills { - [JsonConverter(typeof(ArrayToObjectFactoryConverter))] - public Dictionary? Common { get; set; } + public DictionaryOrList? Common { get; set; } [JsonConverter(typeof(ArrayToObjectFactoryConverter))] public Dictionary? Mastering { get; set; } @@ -681,7 +685,7 @@ public class LastCompleted public class Notes { - [JsonPropertyName("notes")] + [JsonPropertyName("Notes")] public List? DataNotes { get; set; } } diff --git a/Core/Models/Eft/Common/Tables/ProfileTemplate.cs b/Core/Models/Eft/Common/Tables/ProfileTemplate.cs index 172f2d3c..9e48cd39 100644 --- a/Core/Models/Eft/Common/Tables/ProfileTemplate.cs +++ b/Core/Models/Eft/Common/Tables/ProfileTemplate.cs @@ -61,6 +61,12 @@ public class TemplateSide [JsonPropertyName("trader")] public ProfileTraderTemplate? Trader { get; set; } + + [JsonPropertyName("equipmentBuilds")] + public object? EquipmentBuilds { get; set; } + + [JsonPropertyName("weaponbuilds")] + public object? WeaponBuilds { get; set; } } public class ProfileTraderTemplate @@ -69,7 +75,7 @@ public class ProfileTraderTemplate public Dictionary? InitialLoyaltyLevel { get; set; } [JsonPropertyName("initialStanding")] - public Dictionary? InitialStanding { get; set; } + public Dictionary? InitialStanding { get; set; } [JsonPropertyName("setQuestsAvailableForStart")] public bool? SetQuestsAvailableForStart { get; set; } @@ -94,4 +100,4 @@ public class ProfileTraderTemplate /** What traders should have their clothing unlocked/purchased on creation */ [JsonPropertyName("purchaseAllClothingByDefaultForTrader")] public List? PurchaseAllClothingByDefaultForTrader { get; set; } -} \ No newline at end of file +} diff --git a/Core/Models/Eft/Hideout/QteData.cs b/Core/Models/Eft/Hideout/QteData.cs index 90ed9c94..2cdb8931 100644 --- a/Core/Models/Eft/Hideout/QteData.cs +++ b/Core/Models/Eft/Hideout/QteData.cs @@ -1,3 +1,4 @@ +using System.Runtime.CompilerServices; using System.Text.Json.Serialization; using Core.Models.Eft.Health; using Core.Models.Enums; @@ -166,6 +167,7 @@ public class SkillRequirement : QteRequirement public RequirementType? Type { get; set; } = Models.Enums.Hideout.RequirementType.Skill; [JsonPropertyName("skillName")] + [JsonConverter(typeof(JsonStringEnumConverter))] public SkillTypes? SkillName { get; set; } [JsonPropertyName("skillLevel")] @@ -254,4 +256,4 @@ public class BodyPartBuffRequirement : QteRequirement [JsonPropertyName("excluded")] public bool? Excluded { get; set; } -} \ No newline at end of file +} diff --git a/Core/Utils/ImporterUtil.cs b/Core/Utils/ImporterUtil.cs index 35291092..b257b7e3 100644 --- a/Core/Utils/ImporterUtil.cs +++ b/Core/Utils/ImporterUtil.cs @@ -2,6 +2,7 @@ using System.Reflection; using System.Text.Json; using System.Text.Json.Serialization; using Core.Annotations; +using Core.Utils.Json.Converters; namespace Core.Utils; @@ -12,6 +13,11 @@ public class ImporterUtil private readonly HashSet filesToIgnore = ["bearsuits.json", "usecsuits.json", "archivedquests.json"]; + private readonly JsonSerializerOptions jsonSerializerOptions = new JsonSerializerOptions + { + UnmappedMemberHandling = JsonUnmappedMemberHandling.Disallow, Converters = { new ListOrTConverterFactory(), new DictionaryOrListConverter() } + }; + public ImporterUtil(FileUtil fileUtil) { _fileUtil = fileUtil; @@ -58,8 +64,7 @@ public class ImporterUtil ); try { - var fileDeserialized = JsonSerializer.Deserialize(fileData, propertyType, - new JsonSerializerOptions { UnmappedMemberHandling = JsonUnmappedMemberHandling.Disallow }); + var fileDeserialized = JsonSerializer.Deserialize(fileData, propertyType, jsonSerializerOptions); if (onObjectDeserialized != null) onObjectDeserialized(file, fileDeserialized); @@ -77,13 +82,17 @@ public class ImporterUtil // deep tree search foreach (var directory in directories) { + var dictionaryLock = new object(); tasks.Add( Task.Factory.StartNew(() => { var setMethod = GetSetMethod(directory.Split("/").Last().Replace("_", ""), loadedType, out var matchedProperty, out var isDictionary); var loadTask = LoadRecursiveAsync($"{directory}/", matchedProperty); loadTask.Wait(); - setMethod.Invoke(result, isDictionary ? [directory, loadTask.Result] : [loadTask.Result]); + lock (dictionaryLock) + { + setMethod.Invoke(result, isDictionary ? [directory, loadTask.Result] : [loadTask.Result]); + } }) ); } diff --git a/Core/Utils/Json/Converters/DictionaryOfListOrTConverter.cs b/Core/Utils/Json/Converters/DictionaryOfListOrTConverter.cs new file mode 100644 index 00000000..aeb0c695 --- /dev/null +++ b/Core/Utils/Json/Converters/DictionaryOfListOrTConverter.cs @@ -0,0 +1,43 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Core.Utils.Json.Converters; + +public class DictionaryOfListOrTConverter : JsonConverterFactory +{ + public override bool CanConvert(Type typeToConvert) + { + return typeToConvert.IsGenericType && typeToConvert.GetGenericTypeDefinition() == typeof(Dictionary<,>) && + typeToConvert.GenericTypeArguments[1].IsGenericType && typeToConvert.GenericTypeArguments[1].GetGenericTypeDefinition() == typeof(ListOrT<>); + } + + public override JsonConverter? CreateConverter(Type typeToConvert, JsonSerializerOptions options) + { + return (JsonConverter)Activator.CreateInstance(typeof(DictionaryOfListOrTConverter<,>).MakeGenericType(typeToConvert.GenericTypeArguments[0], typeToConvert.GenericTypeArguments[1].GenericTypeArguments[0])); + } +} + +public class DictionaryOfListOrTConverter : JsonConverter>?> +{ + public override Dictionary>? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType == JsonTokenType.StartArray) + { + reader.Read(); + return default; + } + else + { + using (var jsonDocument = JsonDocument.ParseValue(ref reader)) + { + var jsonText = jsonDocument.RootElement.GetRawText(); + return JsonSerializer.Deserialize>>(jsonText, options); + } + } + } + + public override void Write(Utf8JsonWriter writer, Dictionary> value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value, options); + } +} diff --git a/Core/Utils/Json/Converters/DictionaryOrListConverter.cs b/Core/Utils/Json/Converters/DictionaryOrListConverter.cs new file mode 100644 index 00000000..c6913deb --- /dev/null +++ b/Core/Utils/Json/Converters/DictionaryOrListConverter.cs @@ -0,0 +1,55 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Core.Utils.Json.Converters; + +public class DictionaryOrListConverter: JsonConverterFactory +{ + public override bool CanConvert(Type typeToConvert) + { + return typeToConvert.IsGenericType && typeToConvert.GetGenericTypeDefinition() == typeof(DictionaryOrList<,>); + } + + public override JsonConverter? CreateConverter(Type typeToConvert, JsonSerializerOptions options) + { + return (JsonConverter)Activator.CreateInstance(typeof(DictionaryOrListConverter<,>).MakeGenericType(typeToConvert.GenericTypeArguments[0], typeToConvert.GenericTypeArguments[1])); + } +} + +public class DictionaryOrListConverter : JsonConverter?> +{ + public override DictionaryOrList? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + switch (reader.TokenType) + { + case JsonTokenType.StartArray: + using (var jsonDocument = JsonDocument.ParseValue(ref reader)) + { + var jsonText = jsonDocument.RootElement.GetRawText(); + var list = JsonSerializer.Deserialize>(jsonText, options); + return new DictionaryOrList(null, list); + } + case JsonTokenType.StartObject: + using (var jsonDocument = JsonDocument.ParseValue(ref reader)) + { + var jsonText = jsonDocument.RootElement.GetRawText(); + var dictionary = JsonSerializer.Deserialize>(jsonText, options); + return new DictionaryOrList(dictionary, null); + } + default: + throw new Exception($"Unable to translate object type {reader.TokenType} to ListOrT."); + } + } + + public override void Write(Utf8JsonWriter writer, DictionaryOrList value, JsonSerializerOptions options) + { + if (value.IsList) + { + JsonSerializer.Serialize(writer, value.List, options); + } + else + { + JsonSerializer.Serialize(writer, value.Dictionary, options); + } + } +} diff --git a/Core/Utils/Json/Converters/ListOrTConverter.cs b/Core/Utils/Json/Converters/ListOrTConverter.cs new file mode 100644 index 00000000..fca1deea --- /dev/null +++ b/Core/Utils/Json/Converters/ListOrTConverter.cs @@ -0,0 +1,55 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Core.Utils.Json.Converters; + +public class ListOrTConverterFactory : JsonConverterFactory +{ + public override bool CanConvert(Type typeToConvert) + { + return typeToConvert.IsGenericType && typeToConvert.GetGenericTypeDefinition() == typeof(ListOrT<>); + } + + public override JsonConverter? CreateConverter(Type typeToConvert, JsonSerializerOptions options) + { + return (JsonConverter)Activator.CreateInstance(typeof(ListOrTConverter<>).MakeGenericType(typeToConvert.GenericTypeArguments[0])); + } +} + +public class ListOrTConverter : JsonConverter?> +{ + public override ListOrT? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + switch (reader.TokenType) + { + case JsonTokenType.StartArray: + using (var jsonDocument = JsonDocument.ParseValue(ref reader)) + { + var jsonText = jsonDocument.RootElement.GetRawText(); + var list = JsonSerializer.Deserialize>(jsonText, options); + return new ListOrT(list, default); + } + case JsonTokenType.StartObject: + using (var jsonDocument = JsonDocument.ParseValue(ref reader)) + { + var jsonText = jsonDocument.RootElement.GetRawText(); + var obj = JsonSerializer.Deserialize(jsonText, options); + return new ListOrT(null, obj); + } + default: + throw new Exception($"Unable to translate object type {reader.TokenType} to ListOrT."); + } + } + + public override void Write(Utf8JsonWriter writer, ListOrT value, JsonSerializerOptions options) + { + if (value.IsItem) + { + JsonSerializer.Serialize(writer, value.Item, options); + } + else + { + JsonSerializer.Serialize(writer, value.List, options); + } + } +} diff --git a/Core/Utils/Json/DictionaryOrList.cs b/Core/Utils/Json/DictionaryOrList.cs new file mode 100644 index 00000000..bdcb6ceb --- /dev/null +++ b/Core/Utils/Json/DictionaryOrList.cs @@ -0,0 +1,10 @@ +namespace Core.Utils.Json; + +public class DictionaryOrList(Dictionary? dictionary, List? list) +{ + public Dictionary? Dictionary { get; } = dictionary; + public List? List { get; } = list; + + public bool IsList => List != null; + public bool IsDictionary => Dictionary != null; +} diff --git a/Core/Utils/Json/ListOrT.cs b/Core/Utils/Json/ListOrT.cs new file mode 100644 index 00000000..bfa0aae4 --- /dev/null +++ b/Core/Utils/Json/ListOrT.cs @@ -0,0 +1,10 @@ +namespace Core.Utils.Json; + +public class ListOrT(List? list, T? item) +{ + public List? List { get; } = list; + public T? Item { get; } = item; + + public bool IsItem => Item != null; + public bool IsList => List != null; +}