From bafaeb8c587ad1cc923480d32e22774db04a18cd Mon Sep 17 00:00:00 2001 From: Citrinate Date: Sun, 18 Feb 2024 18:07:47 -0500 Subject: [PATCH 1/3] Remove ValveKeyValue --- .github/workflows/publish.yml | 1 - CS2Interface/CS2Interface.csproj | 1 - CS2Interface/Data/GameDataItems.cs | 17 ++++--- CS2Interface/Data/GameDataItemsCDN.cs | 2 +- CS2Interface/Data/GameDataResource.cs | 11 +++-- CS2Interface/Data/GameDataText.cs | 10 ++-- CS2Interface/Data/InventoryItem.cs | 21 ++++---- CS2Interface/Data/Item.cs | 66 +++++++++++++------------- CS2Interface/Data/ItemData.cs | 10 ++-- CS2Interface/Data/ItemDef.cs | 18 +++---- CS2Interface/Helpers/KVConverter.cs | 38 +++++++++------ CS2Interface/Helpers/UtilExtensions.cs | 13 ++--- build.sh | 1 - 13 files changed, 111 insertions(+), 98 deletions(-) diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 4f4ac00..10e0fe1 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -31,7 +31,6 @@ jobs: dotnet publish ${{ env.PLUGIN_NAME }} -c "Release" -o "out/generic" -p:ContinuousIntegrationBuild=true --nologo mkdir -p ./out/dist/${{ env.PLUGIN_NAME }} cp ./out/generic/${{ env.PLUGIN_NAME }}.dll ./out/dist/${{ env.PLUGIN_NAME }} - cp ./out/generic/ValveKeyValue.dll ./out/dist/${{ env.PLUGIN_NAME }} - name: Create README uses: docker://pandoc/core:3.1 diff --git a/CS2Interface/CS2Interface.csproj b/CS2Interface/CS2Interface.csproj index 931f894..c0e809b 100644 --- a/CS2Interface/CS2Interface.csproj +++ b/CS2Interface/CS2Interface.csproj @@ -11,7 +11,6 @@ - diff --git a/CS2Interface/Data/GameDataItems.cs b/CS2Interface/Data/GameDataItems.cs index 3d7630c..fe71f4b 100644 --- a/CS2Interface/Data/GameDataItems.cs +++ b/CS2Interface/Data/GameDataItems.cs @@ -1,16 +1,18 @@ using System; +using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using ArchiSteamFarm.Core; -using ValveKeyValue; +using SteamKit2; namespace CS2Interface { internal class GameDataItems : GameDataResource { - private KVObject? Data; + private KeyValue? Data; internal GameDataItems(string url) : base(url) {} internal async Task Update() { - KVObject? data = await FetchKVResource().ConfigureAwait(false); + KeyValue? data = await FetchKVResource().ConfigureAwait(false); if (data == null) { ASF.ArchiLogger.LogGenericError(String.Format("Couldn't load game data from: {0}", Url)); @@ -23,7 +25,7 @@ internal async Task Update() { return true; } - internal KVObject? this[string? key] { + internal List? this[string? key] { get { if (key == null || Data == null) { return null; @@ -33,12 +35,13 @@ internal KVObject? this[string? key] { } } - internal KVObject? GetDef(string value, string index, bool suppressErrorLogs = false) { + internal KeyValue? GetDef(string value, string index, bool suppressErrorLogs = false) { if (Data == null) { return null; } - var def = this[value]?[index]; + KeyValue? def = this[value]?.Where(x => x.Name?.ToUpper().Trim() == index.ToUpper().Trim()).FirstOrDefault(); + if (def == null) { if (!suppressErrorLogs) { ASF.ArchiLogger.LogGenericError(String.Format("Couldn't find definition: {0}[{1}]", value, index)); @@ -47,7 +50,7 @@ internal KVObject? this[string? key] { return null; } - return new KVObject(value, def); + return def; } } } \ No newline at end of file diff --git a/CS2Interface/Data/GameDataItemsCDN.cs b/CS2Interface/Data/GameDataItemsCDN.cs index bbedb91..85ad8ab 100644 --- a/CS2Interface/Data/GameDataItemsCDN.cs +++ b/CS2Interface/Data/GameDataItemsCDN.cs @@ -25,7 +25,7 @@ internal async Task Update() { internal string? this[string? key] { get { - if (key == null || Data == null) { + if (key == null || Data == null || !Data.ContainsKey(key)) { return null; } diff --git a/CS2Interface/Data/GameDataResource.cs b/CS2Interface/Data/GameDataResource.cs index 8e99a6f..914f351 100644 --- a/CS2Interface/Data/GameDataResource.cs +++ b/CS2Interface/Data/GameDataResource.cs @@ -3,7 +3,7 @@ using System.IO; using System.Net.Http; using System.Threading.Tasks; -using ValveKeyValue; +using SteamKit2; namespace CS2Interface { internal class GameDataResource { @@ -14,12 +14,15 @@ internal GameDataResource(string url) { Url = new Uri(url); } - protected async Task FetchKVResource(KVSerializerOptions? options = null) { + protected async Task FetchKVResource() { HttpClient httpClient = new(); using (Stream response = await httpClient.GetStreamAsync(Url).ConfigureAwait(false)) { - KVSerializer serializer = KVSerializer.Create(KVSerializationFormat.KeyValues1Text); + KeyValue kv = new KeyValue(); + if (!kv.ReadAsText(response)) { + return null; + } - return serializer.Deserialize(response, options); + return kv; } } diff --git a/CS2Interface/Data/GameDataText.cs b/CS2Interface/Data/GameDataText.cs index c0a3bba..280766f 100644 --- a/CS2Interface/Data/GameDataText.cs +++ b/CS2Interface/Data/GameDataText.cs @@ -1,16 +1,18 @@ using System; +using System.Collections.Generic; +using System.Linq; using System.Threading.Tasks; using ArchiSteamFarm.Core; -using ValveKeyValue; +using SteamKit2; namespace CS2Interface { internal class GameDataText : GameDataResource { - private KVObject? Data; + private List? Data; internal GameDataText(string url) : base(url) {} internal async Task Update() { - KVObject? data = (await FetchKVResource(new KVSerializerOptions { HasEscapeSequences = true, EnableValveNullByteBugBehavior = true }).ConfigureAwait(false))?.Search("Tokens"); + List? data = (await FetchKVResource().ConfigureAwait(false))?.Search("Tokens"); if (data == null) { ASF.ArchiLogger.LogGenericError(String.Format("Couldn't load game data from: {0}", Url)); @@ -29,7 +31,7 @@ internal string? this[string? key] { return null; } - return Data.SearchFirst(key)?.Value.ToString(); + return Data.Where(x => x.Name?.ToUpper().Trim() == key.ToUpper().Trim()).FirstOrDefault()?.Value; } } } diff --git a/CS2Interface/Data/InventoryItem.cs b/CS2Interface/Data/InventoryItem.cs index 60e24d1..7d117c4 100644 --- a/CS2Interface/Data/InventoryItem.cs +++ b/CS2Interface/Data/InventoryItem.cs @@ -1,10 +1,11 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Text; using ArchiSteamFarm.Core; using Newtonsoft.Json; +using SteamKit2; using SteamKit2.GC.CSGO.Internal; -using ValveKeyValue; namespace CS2Interface { internal sealed class InventoryItem : Item { @@ -90,7 +91,7 @@ private bool CanBeMoved() { } if (GetAttribute("cannot trade")?.ToUInt32() == 1 - || ItemData!.ItemDef.GetValue("attributes", "cannot trade")?.ToString() == "1" + || ItemData!.ItemDef.GetValue("attributes", "cannot trade") == "1" ) { return false; } @@ -103,7 +104,7 @@ private bool CanBeMoved() { // Apparently certain case keys can't be put in storage units? untested, might not be necessary // https://github.com/nombersDev/casemove/blob/8289ea35cb6d76c553ee4955adecdf9a02622764/src/main/helpers/classes/steam/items/index.js#L506 // https://dev.doctormckay.com/topic/4086-inventory-and-music-kits/#comment-10610 - if (ItemInfo.flags == 10 && (ItemData!.ItemDef.GetValue("prefab")?.ToString() == "valve weapon_case_key" || ItemData!.ItemDef.GetValue("prefab")?.ToString() == "weapon_case_key")) { + if (ItemInfo.flags == 10 && (ItemData!.ItemDef.GetValue("prefab") == "valve weapon_case_key" || ItemData!.ItemDef.GetValue("prefab") == "weapon_case_key")) { return false; } @@ -130,31 +131,31 @@ private bool ParseAttributes(List? attributes) { } foreach (CSOEconItemAttribute attribute in attributes) { - KVObject? attribute_def = GameData.ItemsGame.GetDef("attributes", attribute.def_index.ToString()); + KeyValue? attribute_def = GameData.ItemsGame.GetDef("attributes", attribute.def_index.ToString()); if (attribute_def == null) { return false; } - string? attribute_name = attribute_def["name"].ToString(); + string? attribute_name = attribute_def["name"].Value; if (attribute_name == null) { ASF.ArchiLogger.LogGenericError(String.Format("Missing name for attribute: {0}", attribute.def_index.ToString())); return false; } - if (attribute_def["attribute_type"]?.ToString() == "vector") { + if (attribute_def["attribute_type"].Value == "vector") { ASF.ArchiLogger.LogGenericError(String.Format("zzzz vector for: {0}", attribute_name)); } - switch (attribute_def["attribute_type"]?.ToString()) { + switch (attribute_def["attribute_type"].Value) { case "uint32": - case null when attribute_def["stored_as_integer"]?.ToString() == "1": Attributes.Add(attribute_name, new Attribute(attribute_name, BitConverter.ToUInt32(attribute.value_bytes))); + case null when attribute_def["stored_as_integer"].Value == "1": break; case "float": - case null when attribute_def["stored_as_integer"]?.ToString() == "0": Attributes.Add(attribute_name, new Attribute(attribute_name, BitConverter.ToSingle(attribute.value_bytes))); + case null when attribute_def["stored_as_integer"].Value == "0": break; case "string": @@ -163,7 +164,7 @@ private bool ParseAttributes(List? attributes) { case "vector": default: - ASF.ArchiLogger.LogGenericError(String.Format("Unknown attribute type: {0}, value: {1}", attribute_def["attribute_type"].ToString(), Convert.ToBase64String(attribute.value_bytes))); + ASF.ArchiLogger.LogGenericError(String.Format("Unknown attribute type: {0}, value: {1}", attribute_def["attribute_type"].Value, Convert.ToBase64String(attribute.value_bytes))); return false; } } diff --git a/CS2Interface/Data/Item.cs b/CS2Interface/Data/Item.cs index 018756b..bee1645 100644 --- a/CS2Interface/Data/Item.cs +++ b/CS2Interface/Data/Item.cs @@ -1,7 +1,7 @@ using System; using System.Linq; using Newtonsoft.Json; -using ValveKeyValue; +using SteamKit2; namespace CS2Interface { internal class Item { @@ -117,7 +117,7 @@ internal static void SetSerializationProperties(bool should_serialize_additional protected bool SetAdditionalProperties() { try { - ItemData = new(this); + ItemData = new ItemData(this); } catch (Exception) { GameData.Update(true); @@ -126,25 +126,25 @@ protected bool SetAdditionalProperties() { { // Set rarity name, which differs based on the type of item string locKey = "loc_key"; // General rarities - if (ItemData.ItemDef.GetValue("taxonomy", "weapon")?.ToString() == "1") { + if (ItemData.ItemDef.GetValue("taxonomy", "weapon") == "1") { locKey = "loc_key_weapon"; // Weapon skin rarities - } else if (ItemData.ItemDef.GetValue("item_slot")?.ToString() == "customplayer") { + } else if (ItemData.ItemDef.GetValue("item_slot") == "customplayer") { locKey = "loc_key_character"; // Agent rarities } - RarityName = GameData.CsgoEnglish[GameData.ItemsGame["rarities"]?.FirstOrDefault(x => (uint) x["value"] == Rarity)?[locKey].ToString()]; + RarityName = GameData.CsgoEnglish[GameData.ItemsGame["rarities"]?.FirstOrDefault(x => x["value"].Value == Rarity.ToString())?[locKey].Value]; } - TypeName = GameData.CsgoEnglish[ItemData.ItemDef.GetValue("item_type_name")?.ToString()?.Substring(1)]; - QualityName = GameData.CsgoEnglish[GameData.ItemsGame["qualities"]?.FirstOrDefault(x => (uint) x["value"] == Quality)?.Name]; + TypeName = GameData.CsgoEnglish[ItemData.ItemDef.GetValue("item_type_name")?.Substring(1)]; + QualityName = GameData.CsgoEnglish[GameData.ItemsGame["qualities"]?.FirstOrDefault(x => x["value"].Value == Quality.ToString())?.Name]; OriginName = GameData.GetOriginName(Origin); // Set the item name, which will be something like: what kind of sticker it is, or the name of the weapon skin, or the type of pin/coin // If an item has a wear value, but uses the default paint_kit (vanilla knives for example), this will be "-" - ItemName = GameData.CsgoEnglish[(ItemData.MusicDef?.GetValue("loc_name") ?? ItemData.StickerKitDef?.GetValue("item_name") ?? ItemData.PaintKitDef?.GetValue("description_tag") ?? ItemData.ItemDef.GetValue("item_name"))?.ToString()?.Substring(1)]; + ItemName = GameData.CsgoEnglish[(ItemData.MusicDef?.GetValue("loc_name") ?? ItemData.StickerKitDef?.GetValue("item_name") ?? ItemData.PaintKitDef?.GetValue("description_tag") ?? ItemData.ItemDef.GetValue("item_name"))?.Substring(1)]; // Set the tool named, used for various things like differentiating between Graffiti and Sealed Graffiti - if (ItemData.ItemDef.GetValue("prefab")?.ToString() == "csgo_tool") { - ToolName = GameData.CsgoEnglish[ItemData.ItemDef.GetValue("item_name")?.ToString()?.Substring(1)]; + if (ItemData.ItemDef.GetValue("prefab") == "csgo_tool") { + ToolName = GameData.CsgoEnglish[ItemData.ItemDef.GetValue("item_name")?.Substring(1)]; } // Set the graffiti color, ignore if tint_id is 0 (Multicolor) @@ -153,8 +153,8 @@ protected bool SetAdditionalProperties() { } // Set various weapon-only attributes - if (ItemData.ItemDef.GetValue("taxonomy", "weapon")?.ToString() == "1") { - WeaponName = GameData.CsgoEnglish[ItemData.ItemDef.GetValue("item_name")?.ToString()?.Substring(1)]; + if (ItemData.ItemDef.GetValue("taxonomy", "weapon") == "1") { + WeaponName = GameData.CsgoEnglish[ItemData.ItemDef.GetValue("item_name")?.Substring(1)]; if (Wear != null) { WearName = GameData.GetWearName(Wear.Value); @@ -165,9 +165,9 @@ protected bool SetAdditionalProperties() { // Set the weapon image url string? cdnNameID; if (PaintIndex == 0) { - cdnNameID = ItemData.ItemDef.GetValue("name")?.ToString(); // Vanilla Knives + cdnNameID = ItemData.ItemDef.GetValue("name"); // Vanilla Knives } else { - cdnNameID = String.Format("{0}_{1}", ItemData.ItemDef.GetValue("name")?.ToString(), ItemData.PaintKitDef!.GetValue("name")?.ToString()); // Everything else + cdnNameID = String.Format("{0}_{1}", ItemData.ItemDef.GetValue("name"), ItemData.PaintKitDef!.GetValue("name")); // Everything else } WeaponImageURL = GameData.ItemsGameCdn[cdnNameID]; } @@ -190,29 +190,31 @@ protected bool SetAdditionalProperties() { // Set the name id, used for determining related set and crate if (PaintIndex == 0 && ItemData.StickerKitDef == null && ItemData.MusicDef == null) { - NameID = ItemData.ItemDef.GetValue("name")?.ToString(); // Collectibles, Vanilla Knives + NameID = ItemData.ItemDef.GetValue("name"); // Collectibles, Vanilla Knives } else { - NameID = String.Format("[{0}]{1}", (ItemData.MusicDef ?? ItemData.StickerKitDef ?? ItemData.PaintKitDef)?.GetValue("name")?.ToString(), ItemData.ItemDef.GetValue("name")?.ToString()); // Everything else + NameID = String.Format("[{0}]{1}", (ItemData.MusicDef ?? ItemData.StickerKitDef ?? ItemData.PaintKitDef)?.GetValue("name"), ItemData.ItemDef.GetValue("name")); // Everything else } - { // Determine what set, if any, this item belongs to - KVObject? setItemDef = GameData.ItemsGame["item_sets"]?.FirstOrDefault(x => x["items"][NameID] != null); - if (setItemDef != null) { - SetNameID = setItemDef.Name; - SetName = GameData.CsgoEnglish[setItemDef["name"]?.ToString()?.Substring(1)]; + if (NameID != null) { + { // Determine what set, if any, this item belongs to + KeyValue? setItemDef = GameData.ItemsGame["item_sets"]?.FirstOrDefault(x => x["items"][NameID] != KeyValue.Invalid); + if (setItemDef != null) { + SetNameID = setItemDef.Name; + SetName = GameData.CsgoEnglish[setItemDef["name"].Value?.Substring(1)]; + } } - } - { // Determine what crate, if any, this item belongs to. Doesn't work for souvenir skins, knives, or gloves - string? lootListName = GameData.ItemsGame["client_loot_lists"]?.FirstOrDefault(x => x[NameID] != null)?.Name; - lootListName = lootListName == null ? null : GameData.ItemsGame["client_loot_lists"]?.FirstOrDefault(x => x[lootListName] != null)?.Name ?? lootListName; // Some lists in client_loot_lists are nested (1 or 2 layers), we want the top-most layer - string? lootListID = lootListName == null ? null : GameData.ItemsGame["revolving_loot_lists"]?.FirstOrDefault(x => x.Value.ToString() == lootListName)?.Name; - KVObject? crateItemDef = lootListID == null ? null : GameData.ItemsGame["items"]?.FirstOrDefault(x => x["attributes"]?["set supply crate series"]?["value"]?.ToString() == lootListID); - if (crateItemDef != null) { - CrateNameID = crateItemDef["name"]?.ToString(); - CrateDefIndex = uint.Parse(crateItemDef.Name); - CrateSupplySeries = uint.Parse(lootListID!); - CrateName = GameData.CsgoEnglish[crateItemDef["item_name"]?.ToString()?.Substring(1)]; + { // Determine what crate, if any, this item belongs to. Doesn't work for souvenir skins, knives, or gloves + string? lootListName = GameData.ItemsGame["client_loot_lists"]?.FirstOrDefault(x => x[NameID] != KeyValue.Invalid)?.Name; + lootListName = lootListName == null ? null : GameData.ItemsGame["client_loot_lists"]?.FirstOrDefault(x => x[lootListName] != KeyValue.Invalid)?.Name ?? lootListName; // Some lists in client_loot_lists are nested (1 or 2 layers), we want the top-most layer + string? lootListID = lootListName == null ? null : GameData.ItemsGame["revolving_loot_lists"]?.FirstOrDefault(x => x.Value == lootListName)?.Name; + KeyValue? crateItemDef = lootListID == null ? null : GameData.ItemsGame["items"]?.FirstOrDefault(x => x["attributes"]["set supply crate series"]["value"].Value == lootListID); + if (crateItemDef != null && crateItemDef.Name != null) { + CrateNameID = crateItemDef["name"].Value; + CrateDefIndex = uint.Parse(crateItemDef.Name); + CrateSupplySeries = uint.Parse(lootListID!); + CrateName = GameData.CsgoEnglish[crateItemDef["item_name"].Value?.Substring(1)]; + } } } diff --git a/CS2Interface/Data/ItemData.cs b/CS2Interface/Data/ItemData.cs index db33ff7..cc65669 100644 --- a/CS2Interface/Data/ItemData.cs +++ b/CS2Interface/Data/ItemData.cs @@ -1,7 +1,7 @@ using System; using ArchiSteamFarm.Core; using Newtonsoft.Json; -using ValveKeyValue; +using SteamKit2; namespace CS2Interface { internal class ItemData { @@ -33,7 +33,7 @@ private ItemDef CreateItemDef(Item item) { ItemDef itemDef = new(GameData.ItemsGame.GetDef("items", item.DefIndex.ToString())); // Add prefab values - if (!MergePrefab(itemDef, itemDef.GetValue("prefab")?.ToString())) { + if (!MergePrefab(itemDef, itemDef.GetValue("prefab"))) { throw new InvalidOperationException(); } @@ -48,6 +48,8 @@ private bool MergePrefab(ItemDef itemDef, string? prefab) { return true; } + // STACK OVERFLOW HERE + // Some items have multiple prefabs separated by a space, but only one is valid (it has an entry in ItemsGame) // Ex: "valve weapon_case_key": "valve" isn't valid, but "weapon_case_key" is // Ex: "antwerp2022_sticker_capsule_prefab antwerp2022_sellable_item_with_payment_rules": "antwerp2022_sticker_capsule_prefab" is valid, but "antwerp2022_sellable_item_with_payment_rules" isn't @@ -56,14 +58,14 @@ private bool MergePrefab(ItemDef itemDef, string? prefab) { bool foundValid = false; foreach (string prefabName in prefabNames) { - KVObject? prefabDef = GameData.ItemsGame.GetDef("prefabs", prefabName, suppressErrorLogs: true); + KeyValue? prefabDef = GameData.ItemsGame.GetDef("prefabs", prefabName, suppressErrorLogs: true); if (prefabDef == null) { continue; } foundValid = true; itemDef.AddDef(prefabDef); - if (!MergePrefab(itemDef, prefabDef["prefab"]?.ToString())) { + if (!MergePrefab(itemDef, prefabDef["prefab"].Value)) { return false; }; } diff --git a/CS2Interface/Data/ItemDef.cs b/CS2Interface/Data/ItemDef.cs index 0c060f8..49162e9 100644 --- a/CS2Interface/Data/ItemDef.cs +++ b/CS2Interface/Data/ItemDef.cs @@ -2,18 +2,18 @@ using System.Collections.Generic; using System.Linq; using Newtonsoft.Json; -using ValveKeyValue; +using SteamKit2; namespace CS2Interface { internal class ItemDef { [JsonProperty(PropertyName = "defs", ItemConverterType = typeof(KVConverter))] - internal List Defs = new(); + internal List Defs = new(); - internal ItemDef(KVObject? def) { + internal ItemDef(KeyValue? def) { AddDef(def); } - internal void AddDef(KVObject? def) { + internal void AddDef(KeyValue? def) { if (def == null) { throw new ArgumentNullException(); } @@ -21,11 +21,11 @@ internal void AddDef(KVObject? def) { Defs.Add(def); } - internal KVValue? GetValue(params string[] keys) { - return GetValue(Defs, keys); + internal string? GetValue(params string[] keys) { + return GetValue(Defs, keys)?.Value; } - private KVValue? GetValue(IEnumerable defs, IEnumerable keys) { + private KeyValue? GetValue(IEnumerable defs, IEnumerable keys) { if (defs.Count() == 0) { return null; } @@ -33,10 +33,10 @@ internal void AddDef(KVObject? def) { string key = keys.First(); if (keys.Count() == 1) { - return defs.FirstOrDefault(def => def[key] != null)?[key]; + return defs.FirstOrDefault(def => def[key] != KeyValue.Invalid)?[key]; } - return GetValue(defs.Where(def => def[key] != null).Select(def => new KVObject(key, def[key])), keys.Skip(1).ToArray()); + return GetValue(defs.Where(def => def[key] != KeyValue.Invalid).Select(def => def[key]), keys.Skip(1).ToArray()); } } } \ No newline at end of file diff --git a/CS2Interface/Helpers/KVConverter.cs b/CS2Interface/Helpers/KVConverter.cs index 3c9e6ad..ceebb16 100644 --- a/CS2Interface/Helpers/KVConverter.cs +++ b/CS2Interface/Helpers/KVConverter.cs @@ -1,18 +1,19 @@ using System; +using System.Globalization; using ArchiSteamFarm.Core; using Newtonsoft.Json; using Newtonsoft.Json.Linq; -using ValveKeyValue; +using SteamKit2; namespace CS2Interface { public sealed class KVConverter : JsonConverter { public override bool CanConvert(Type objectType) { - return objectType == typeof(KVObject); + return objectType == typeof(KeyValue); } public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) { JToken? json = null; - if (value is KVObject vdf) { + if (value is KeyValue vdf) { json = ConvertKVObjectToJson(vdf); } @@ -25,10 +26,14 @@ public override void WriteJson(JsonWriter writer, object? value, JsonSerializer json.WriteTo(writer); } - private JToken? ConvertKVObjectToJson (KVObject vdf) { - if (vdf.Value.ValueType == KVValueType.Collection) { + private JToken? ConvertKVObjectToJson (KeyValue vdf) { + if (vdf.Children.Count > 0) { JObject json = new(); - foreach (KVObject child in vdf.Children) { + foreach (KeyValue child in vdf.Children) { + if (child.Name == null) { + continue; + } + try { json.Add(child.Name, ConvertKVObjectToJson(child)); } catch (Exception e) { @@ -40,14 +45,19 @@ public override void WriteJson(JsonWriter writer, object? value, JsonSerializer return json; } - return vdf.Value.GetTypeCode() switch { - TypeCode.Int32 => (int) vdf.Value, - TypeCode.Int64 => (long) vdf.Value, - TypeCode.UInt64 => vdf.Value.ToString(), - TypeCode.Single => (float) vdf.Value, - TypeCode.String => vdf.Value.ToString(), - _ => null - }; + if (int.TryParse(vdf.Value, out int intValue)) { + return intValue; + } + + if (long.TryParse(vdf.Value, out long longValue)) { + return longValue; + } + + if (float.TryParse(vdf.Value, NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out float floatValue)) { + return floatValue; + } + + return vdf.Value; } public override bool CanRead { diff --git a/CS2Interface/Helpers/UtilExtensions.cs b/CS2Interface/Helpers/UtilExtensions.cs index 346c095..a5214df 100644 --- a/CS2Interface/Helpers/UtilExtensions.cs +++ b/CS2Interface/Helpers/UtilExtensions.cs @@ -1,18 +1,11 @@ using System.Collections.Generic; using System.Linq; -using ValveKeyValue; +using SteamKit2; namespace CS2Interface { public static class UtilExtensions { - public static KVObject Search(this IEnumerable obj, string value) { - var objs = obj.Where(x => x.Name.ToUpper().Trim() == value.ToUpper().Trim()).SelectMany(x => x.Children); - var result = new KVObject(value, objs); - - return result; - } - - public static KVObject? SearchFirst(this IEnumerable obj, string value) { - return obj.Where(x => x.Name.ToUpper().Trim() == value.ToUpper().Trim()).FirstOrDefault(); + public static List Search(this KeyValue obj, string value) { + return obj.Children.Where(x => x.Name == value).SelectMany(x => x.Children).ToList(); } } } \ No newline at end of file diff --git a/build.sh b/build.sh index 6ec5aa0..151c28a 100755 --- a/build.sh +++ b/build.sh @@ -48,7 +48,6 @@ sync dotnet publish CS2Interface -c "Release" -f net8.0 -o "out/generic" "/p:LinkDuringPublish=false" mkdir ./out/$plugin_name cp ./out/generic/$plugin_name.dll ./out/$plugin_name -cp ./out/generic/ValveKeyValue.dll ./out/$plugin_name if [[ -f "README.md" ]]; then if ! command -v pandoc &> /dev/null; then cp README.md ./out/$plugin_name From 26fbf8f4d6cf3aa87d2bc7b47a8c5dbbf988b5b0 Mon Sep 17 00:00:00 2001 From: Citrinate Date: Sun, 18 Feb 2024 18:08:34 -0500 Subject: [PATCH 2/3] Fix method not found --- CS2Interface/Data/InventoryItem.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CS2Interface/Data/InventoryItem.cs b/CS2Interface/Data/InventoryItem.cs index 7d117c4..da33283 100644 --- a/CS2Interface/Data/InventoryItem.cs +++ b/CS2Interface/Data/InventoryItem.cs @@ -149,13 +149,13 @@ private bool ParseAttributes(List? attributes) { switch (attribute_def["attribute_type"].Value) { case "uint32": - Attributes.Add(attribute_name, new Attribute(attribute_name, BitConverter.ToUInt32(attribute.value_bytes))); case null when attribute_def["stored_as_integer"].Value == "1": + Attributes.Add(attribute_name, new Attribute(attribute_name, BitConverter.ToUInt32(attribute.value_bytes.ToArray(), 0))); break; case "float": - Attributes.Add(attribute_name, new Attribute(attribute_name, BitConverter.ToSingle(attribute.value_bytes))); case null when attribute_def["stored_as_integer"].Value == "0": + Attributes.Add(attribute_name, new Attribute(attribute_name, BitConverter.ToSingle(attribute.value_bytes.ToArray(), 0))); break; case "string": From fc0f846c7d3e1bd0d279cd88980a643e1cae0cf0 Mon Sep 17 00:00:00 2001 From: Citrinate Date: Tue, 20 Feb 2024 15:35:39 -0500 Subject: [PATCH 3/3] Cleanup --- CS2Interface/Data/GameDataItems.cs | 2 +- CS2Interface/Data/GameDataText.cs | 4 ++-- CS2Interface/Data/InventoryItem.cs | 4 ---- CS2Interface/Helpers/UtilExtensions.cs | 11 ----------- 4 files changed, 3 insertions(+), 18 deletions(-) delete mode 100644 CS2Interface/Helpers/UtilExtensions.cs diff --git a/CS2Interface/Data/GameDataItems.cs b/CS2Interface/Data/GameDataItems.cs index fe71f4b..a8f5211 100644 --- a/CS2Interface/Data/GameDataItems.cs +++ b/CS2Interface/Data/GameDataItems.cs @@ -31,7 +31,7 @@ internal List? this[string? key] { return null; } - return Data.Search(key); + return Data.Children.Where(x => x.Name == key).SelectMany(x => x.Children).ToList(); } } diff --git a/CS2Interface/Data/GameDataText.cs b/CS2Interface/Data/GameDataText.cs index 280766f..256d002 100644 --- a/CS2Interface/Data/GameDataText.cs +++ b/CS2Interface/Data/GameDataText.cs @@ -12,14 +12,14 @@ internal class GameDataText : GameDataResource { internal GameDataText(string url) : base(url) {} internal async Task Update() { - List? data = (await FetchKVResource().ConfigureAwait(false))?.Search("Tokens"); + KeyValue? data = await FetchKVResource().ConfigureAwait(false); if (data == null) { ASF.ArchiLogger.LogGenericError(String.Format("Couldn't load game data from: {0}", Url)); return false; } - Data = data; + Data = data.Children.Where(x => x.Name == "Tokens").SelectMany(x => x.Children).ToList(); Updated = true; return true; diff --git a/CS2Interface/Data/InventoryItem.cs b/CS2Interface/Data/InventoryItem.cs index da33283..9cd624e 100644 --- a/CS2Interface/Data/InventoryItem.cs +++ b/CS2Interface/Data/InventoryItem.cs @@ -143,10 +143,6 @@ private bool ParseAttributes(List? attributes) { return false; } - if (attribute_def["attribute_type"].Value == "vector") { - ASF.ArchiLogger.LogGenericError(String.Format("zzzz vector for: {0}", attribute_name)); - } - switch (attribute_def["attribute_type"].Value) { case "uint32": case null when attribute_def["stored_as_integer"].Value == "1": diff --git a/CS2Interface/Helpers/UtilExtensions.cs b/CS2Interface/Helpers/UtilExtensions.cs deleted file mode 100644 index a5214df..0000000 --- a/CS2Interface/Helpers/UtilExtensions.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using SteamKit2; - -namespace CS2Interface { - public static class UtilExtensions { - public static List Search(this KeyValue obj, string value) { - return obj.Children.Where(x => x.Name == value).SelectMany(x => x.Children).ToList(); - } - } -} \ No newline at end of file