Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of ItemDrawers v0.5.8
itemdrawers.dll
Decompiled a year agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using AuthoritativeConfig; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using SimpleJSON; using UnityEngine; using UnityEngine.Events; using UnityEngine.InputSystem; using UnityEngine.Serialization; using UnityEngine.UI; using itemdrawers; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.0.0.0")] [module: UnverifiableCode] public class AssetManager { public static AssetBundle AssetBundle; public static Object[] AssetBundleObjects; public static UnityAction<ZNetScene> OnSceneAwake; public static UnityAction<ObjectDB> OnObjectDBAwake; public static void Create(Harmony harmony, string assetBundle) { harmony.PatchAll(typeof(AssetManager)); LoadAssetsFromBundle(assetBundle); } public static void LoadAssetsFromBundle(string bundleName) { AssetBundle = GetAssetBundleFromResources(bundleName); AssetBundleObjects = AssetBundle.LoadAllAssets(); } public static AssetBundle GetAssetBundleFromResources(string fileName) { Assembly executingAssembly = Assembly.GetExecutingAssembly(); string name = executingAssembly.GetManifestResourceNames().Single((string str) => str.EndsWith(fileName)); using Stream stream = executingAssembly.GetManifestResourceStream(name); return AssetBundle.LoadFromStream(stream); } public static IEnumerable<T> GetGameObjects<T>() where T : Component { Object[] assetBundleObjects = AssetBundleObjects; foreach (Object obj in assetBundleObjects) { GameObject val = (GameObject)(object)((obj is GameObject) ? obj : null); if (val != null) { T component = val.GetComponent<T>(); if (component != null) { yield return component; } } } } public static T GetGameObject<T>(string id) where T : Component { Object[] assetBundleObjects = AssetBundleObjects; foreach (Object obj in assetBundleObjects) { GameObject val = (GameObject)(object)((obj is GameObject) ? obj : null); if (val != null && ((Object)val).name == id) { T component = val.GetComponent<T>(); if (component != null) { return component; } } } return default(T); } public static GameObject GetGameObject(string id) { Object[] assetBundleObjects = AssetBundleObjects; foreach (Object obj in assetBundleObjects) { GameObject val = (GameObject)(object)((obj is GameObject) ? obj : null); if (val != null && ((Object)val).name == id) { return val; } } return null; } public static IEnumerable<T> GetAssets<T>() { Object[] assetBundleObjects = AssetBundleObjects; foreach (Object val in assetBundleObjects) { if (val is T) { yield return (T)(object)((val is T) ? val : null); } } } public static T GetAsset<T>(string name) { Object[] assetBundleObjects = AssetBundleObjects; foreach (Object val in assetBundleObjects) { if (val.name == name && val is T) { return (T)(object)((val is T) ? val : null); } } return default(T); } public static T LoadAsset<T>(string name) where T : Object { return AssetBundle.LoadAsset<T>(name); } public static void LoadItems(ObjectDB db) { IEnumerable<ItemDrop> gameObjects = AssetManager.GetGameObjects<ItemDrop>(); db.m_items.AddRange(gameObjects.Select((ItemDrop x) => ((Component)x).gameObject)); db.UpdateRegisters(); } public static void RegisterRecipes(ObjectDB db) { db.m_recipes.AddRange(from x in GetAssets<SkyheimRecipe>() select x.Convert()); db.m_recipes.AddRange(GetAssets<Recipe>()); } public static void RegisterStatusEffects(ObjectDB db) { db.m_StatusEffects.AddRange(GetAssets<StatusEffect>()); } public static void RegisterPieces() { foreach (SkyheimPieceData gameObject in AssetManager.GetGameObjects<SkyheimPieceData>()) { ItemDrop tool = gameObject.Tool; if (tool != null) { List<GameObject> pieces = tool.m_itemData.m_shared.m_buildPieces.m_pieces; if (!pieces.Contains(((Component)gameObject).gameObject)) { gameObject.Convert(); pieces.Add(((Component)gameObject).gameObject); } } } } [HarmonyPatch(typeof(ObjectDB), "Awake")] [HarmonyPostfix] public static void ObjectDB_Awake_Postfix(ObjectDB __instance) { if ((Object)(object)ZNetScene.instance != (Object)null) { OnObjectDBAwake?.Invoke(__instance); LoadItems(__instance); RegisterRecipes(__instance); RegisterStatusEffects(__instance); } } [HarmonyPatch(typeof(FejdStartup), "SetupObjectDB")] [HarmonyPostfix] public static void FejdStartup_SetupObjectDB_Postfix(FejdStartup __instance) { LoadItems(((Component)__instance).GetComponent<ObjectDB>()); } [HarmonyPatch(typeof(ZNetScene), "Awake")] [HarmonyPostfix] public static void ZNetScene_Awake_Postfix(ZNetScene __instance) { foreach (ZNetView gameObject in AssetManager.GetGameObjects<ZNetView>()) { __instance.m_namedPrefabs.Add(StringExtensionMethods.GetStableHashCode(((Object)gameObject).name), ((Component)gameObject).gameObject); } RegisterPieces(); OnSceneAwake?.Invoke(__instance); } } public class AssetType : PropertyAttribute { public Type Type { get; protected set; } public AssetType(Type type) { Type = type; } } public static class CommonUtils { [Serializable] public class SerializableWrapper<T> { public T Value; public SerializableWrapper(T v) { Value = v; } } public struct DistanceSort : IComparer<Collider> { private Vector3 _target; public DistanceSort(Vector3 target) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) _target = target; } public int Compare(Collider a, Collider b) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) Vector3 position = ((Component)a).transform.position; Vector3 position2 = ((Component)b).transform.position; return Vector3.Distance(position, _target).CompareTo(Vector3.Distance(position2, _target)); } } public static long PlayerID => Game.instance.GetPlayerProfile().GetPlayerID(); [Conditional("DEBUG")] public static void Log(string str) { Debug.Log((object)str); } [Conditional("DEBUG")] public static void LogFields(object obj) { FieldInfo[] fields = obj.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public); foreach (FieldInfo obj2 in fields) { _ = obj2.Name; obj2.GetValue(obj); } } public static T ConvertPrefabReference<T>(string prefabReference) where T : Component { if (Object.op_Implicit((Object)(object)ZNetScene.instance) && !string.IsNullOrEmpty(prefabReference)) { GameObject prefab = ZNetScene.instance.GetPrefab(prefabReference); if ((Object)(object)prefab != (Object)null) { return prefab.GetComponent<T>(); } } return default(T); } public static GameObject ConvertPrefabReference(string prefabReference) { if (Object.op_Implicit((Object)(object)ZNetScene.instance) && !string.IsNullOrEmpty(prefabReference)) { return ZNetScene.instance.GetPrefab(prefabReference); } return null; } public static T GetItemComponent<T>(ItemData item) where T : Component { if (item == null || !Object.op_Implicit((Object)(object)item.m_dropPrefab)) { return default(T); } return item.m_dropPrefab.GetComponent<T>(); } public static T Clone<T>(T obj) { return (T)obj.GetType().GetMethod("MemberwiseClone", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(obj, null); } public static T Get<T>(this IDictionary dict, string key, T defaultValue = default(T)) { if (dict.Contains(key)) { return (T)Convert.ChangeType(dict[key], typeof(T)); } return defaultValue; } public static JSONNode ToJSON<T>(T obj) { Type type = obj.GetType(); if (obj is IList) { JSONArray jSONArray = new JSONArray(); { foreach (object item in obj as IList) { jSONArray.Add(ToJSON(item)); } return jSONArray; } } if (type.IsClass && !(obj is string)) { Type type2 = obj.GetType(); JSONObject jSONObject = new JSONObject(); FieldInfo[] fields = type2.GetFields(BindingFlags.Instance | BindingFlags.Public); foreach (FieldInfo fieldInfo in fields) { jSONObject.Add(fieldInfo.Name, ToJSON(fieldInfo.GetValue(obj))); } return jSONObject; } return (JSONNode)typeof(JSONNode).GetMethod("op_Implicit", new Type[1] { obj.GetType() }).Invoke(null, new object[1] { obj }); } } public class NameAttribute : PropertyAttribute { public string Name { get; private set; } public NameAttribute(string name) { Name = name; } } public class PrefabReferenceType : PropertyAttribute { public Type ComponentType { get; protected set; } public PrefabReferenceType(Type componentType) { ComponentType = componentType; } } public class SkyheimPieceData : MonoBehaviour, ISerializationCallbackReceiver { [Header("Requirements")] [SerializeField] [PrefabReferenceType(typeof(CraftingStation))] [FormerlySerializedAs("m_craftingStation")] private string _craftingStation; [SerializeField] [PrefabReferenceType(typeof(ItemDrop))] [FormerlySerializedAs("m_craftingTool")] private string _craftingTool; [SerializeField] [PrefabReferenceType(typeof(Piece))] [FormerlySerializedAs("m_template")] private string _template; [SerializeField] [FormerlySerializedAs("m_resources")] private List<SkyheimRecipe.SkyheimRequirement> _resources; [SerializeField] [HideInInspector] [FormerlySerializedAs("m_resources_internal")] private string _resourcesInternal; public ItemDrop Tool => CommonUtils.ConvertPrefabReference<ItemDrop>(_craftingTool); public void Convert() { RuntimeDeserialize(); Piece component = ((Component)this).GetComponent<Piece>(); component.m_craftingStation = CommonUtils.ConvertPrefabReference<CraftingStation>(_craftingStation); component.m_resources = ((IEnumerable<SkyheimRecipe.SkyheimRequirement>)_resources).Select((Func<SkyheimRecipe.SkyheimRequirement, Requirement>)((SkyheimRecipe.SkyheimRequirement x) => x)).ToArray(); Piece val = (string.IsNullOrEmpty(_template) ? null : CommonUtils.ConvertPrefabReference<Piece>(_template)); if (!((Object)(object)val != (Object)null)) { return; } component.m_placeEffect = val.m_placeEffect; WearNTear component2 = ((Component)component).GetComponent<WearNTear>(); if (Object.op_Implicit((Object)(object)component2)) { WearNTear component3 = ((Component)val).GetComponent<WearNTear>(); if (component3 != null) { component2.m_hitEffect = component3.m_hitEffect; component2.m_destroyedEffect = component3.m_destroyedEffect; component2.m_switchEffect = component3.m_switchEffect; } } } public void RuntimeDeserialize() { _resources = new List<SkyheimRecipe.SkyheimRequirement>(); JSONNode.Enumerator enumerator = JSONNode.Parse(_resourcesInternal).AsArray.GetEnumerator(); while (enumerator.MoveNext()) { KeyValuePair<string, JSONNode> current = enumerator.Current; _resources.Add(new SkyheimRecipe.SkyheimRequirement(current.Value)); } } public void OnBeforeSerialize() { JSONNode jSONNode = CommonUtils.ToJSON(_resources); _resourcesInternal = jSONNode.ToString(); } public void OnAfterDeserialize() { } } [CreateAssetMenu(menuName = "Skyheim/Recipe")] public class SkyheimRecipe : ScriptableObject, ISerializationCallbackReceiver { [Serializable] public class SkyheimRequirement { [PrefabReferenceType(typeof(ItemDrop))] [FormerlySerializedAs("ResItem")] public string ResourceItem; [FormerlySerializedAs("m_amount")] public int Amount = 1; [FormerlySerializedAs("m_amountPerLevel")] public int AmountPerLevel = 1; [FormerlySerializedAs("m_recover")] public bool Recover = true; public SkyheimRequirement() { } public SkyheimRequirement(JSONNode j) { ResourceItem = j["ResourceItem"]; Amount = j["Amount"]; AmountPerLevel = j["AmountPerLevel"]; Recover = j["Recover"]; } public static implicit operator Requirement(SkyheimRequirement r) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Expected O, but got Unknown return new Requirement { m_resItem = CommonUtils.ConvertPrefabReference<ItemDrop>(r.ResourceItem), m_amount = r.Amount, m_amountPerLevel = r.AmountPerLevel, m_recover = r.Recover }; } } [SerializeField] [PrefabReferenceType(typeof(ItemDrop))] [FormerlySerializedAs("m_item")] private string _item; [SerializeField] [FormerlySerializedAs("m_amount")] private int _amount = 1; [SerializeField] [FormerlySerializedAs("m_enabled")] private bool _enabled = true; [Header("Requirements")] [SerializeField] [PrefabReferenceType(typeof(CraftingStation))] [FormerlySerializedAs("m_craftingStation")] private string _craftingStation; [SerializeField] [PrefabReferenceType(typeof(CraftingStation))] [FormerlySerializedAs("m_repairStation")] private string _repairStation; [SerializeField] [FormerlySerializedAs("m_minStationLevel")] private int _minStationLevel = 1; [SerializeField] [FormerlySerializedAs("m_resources")] private List<SkyheimRequirement> _resources; [SerializeField] [HideInInspector] [FormerlySerializedAs("m_resources_internal")] private string _resourcesInternal; public Recipe Convert() { RuntimeDeserialize(); Recipe obj = ScriptableObject.CreateInstance<Recipe>(); ((Object)obj).name = ((Object)this).name; obj.m_item = CommonUtils.ConvertPrefabReference<ItemDrop>(_item); obj.m_craftingStation = CommonUtils.ConvertPrefabReference<CraftingStation>(_craftingStation); obj.m_repairStation = CommonUtils.ConvertPrefabReference<CraftingStation>(_repairStation); obj.m_resources = ((IEnumerable<SkyheimRequirement>)_resources).Select((Func<SkyheimRequirement, Requirement>)((SkyheimRequirement x) => x)).ToArray(); obj.m_amount = _amount; obj.m_enabled = _enabled; obj.m_minStationLevel = _minStationLevel; return obj; } public void RuntimeDeserialize() { JSONNode jSONNode = JSONNode.Parse(_resourcesInternal); _resources = new List<SkyheimRequirement>(); JSONNode.Enumerator enumerator = jSONNode.AsArray.GetEnumerator(); while (enumerator.MoveNext()) { KeyValuePair<string, JSONNode> current = enumerator.Current; _resources.Add(new SkyheimRequirement(current.Value)); } } public void OnBeforeSerialize() { JSONNode jSONNode = CommonUtils.ToJSON(_resources); _resourcesInternal = jSONNode.ToString(); } public void OnAfterDeserialize() { } } public class DrawerContainer : Container, Hoverable, Interactable { public Image _image; public Text _text; private const int CurrentVersion = 0; private uint? _lastRevision; private ZNetView _nview; private Piece _piece; private ItemData _item; private int _quantity; private bool _loading; private int _pickupDelay = 10; private int _pickupMask; public void Awake_Drawer() { //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Expected O, but got Unknown _pickupMask = LayerMask.GetMask(new string[1] { "item" }); _nview = ((Component)this).GetComponent<ZNetView>(); _piece = ((Component)this).GetComponent<Piece>(); ((Component)_image).gameObject.SetActive(false); if ((Object)(object)_nview != (Object)null && _nview.GetZDO() != null) { ((Object)this).name = $"Container_{_nview.GetZDO().m_uid}"; base.m_name = "piece_chest_drawer"; base.m_nview = _nview; base.m_inventory = new Inventory(((Object)this).name, (Sprite)null, 1, 1); base.m_inventory.m_onChanged = (Action)Delegate.Combine(base.m_inventory.m_onChanged, new Action(OnInventoryChanged)); _nview.Register<int>("Drop", (Action<long, int>)RPC_Drop); _nview.Register<ZDOID>("DropResponse", (Action<long, ZDOID>)RPC_DropResponse); _nview.Register("Clear", (Action<long>)RPC_Clear); _nview.Register<string, int>("AddItem", (Action<long, string, int>)RPC_AddItem); _nview.Register<string, int>("AddItemResponse", (Action<long, string, int>)RPC_AddItemResponse); Load(); } WearNTear component = ((Component)this).GetComponent<WearNTear>(); if (component != null) { component.m_onDestroyed = (Action)Delegate.Combine(component.m_onDestroyed, new Action(base.OnDestroyed)); } ((MonoBehaviour)this).InvokeRepeating("CheckForChanges", 0f, 1f); } public void SetFontColor(Color fontColor, Color outlineColor, bool outlineEnabled) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) ((Graphic)_text).color = fontColor; Outline component = ((Component)_text).GetComponent<Outline>(); ((Behaviour)component).enabled = outlineEnabled; if (((Behaviour)component).enabled) { ((Shadow)component).effectColor = outlineColor; } } private void RetreiveItems() { //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) if (!ItemDrawerPlugin.RetreiveEnabled || _item == null || !Object.op_Implicit((Object)(object)_nview) || !_nview.IsOwner()) { return; } if (_pickupDelay > 0) { _pickupDelay--; return; } Collider[] array = Physics.OverlapSphere(((Component)this).transform.position + Vector3.up, ItemDrawerPlugin.RetreiveRadius, _pickupMask); foreach (Collider val in array) { if (!Object.op_Implicit((Object)(object)val.attachedRigidbody)) { continue; } ItemDrop component = ((Component)val.attachedRigidbody).GetComponent<ItemDrop>(); if ((Object)(object)component == (Object)null || (Object)(object)component.m_itemData.m_dropPrefab != (Object)(object)_item.m_dropPrefab) { continue; } ZNetView component2 = ((Component)component).GetComponent<ZNetView>(); if (Object.op_Implicit((Object)(object)component2) && component2.IsValid()) { if (!component.CanPickup(true)) { component.RequestOwn(); continue; } AddItem(((Object)component.m_itemData.m_dropPrefab).name, component.m_itemData.m_stack); ZNetScene.instance.Destroy(((Component)component).gameObject); } } } private void CheckForChanges() { if (Object.op_Implicit((Object)(object)_nview) && _nview.IsValid()) { Load(); RetreiveItems(); UpdateVisuals(); } } public string GetHoverName() { if (!Object.op_Implicit((Object)(object)_piece)) { return string.Empty; } return Localization.instance.Localize(_piece.m_name); } public string GetHoverText() { string text = ((_item != null) ? $"<color=#00FFFF>{_item.m_shared.m_name}</color> x {_quantity}" : "$piece_container_empty"); if (!ZInput.IsGamepadActive()) { string boundKeyString = ZInput.instance.GetBoundKeyString("drawer_mod_deposit_all", false); if (_quantity > 0) { text += "\n[<color=yellow><b>$KEY_Use</b></color>] Take Stack"; string boundKeyString2 = ZInput.instance.GetBoundKeyString("drawer_mod_withdraw_one", false); text = text + "\n[<color=yellow><b>" + boundKeyString2 + "+$KEY_Use</b></color>] Take One"; } else { string boundKeyString3 = ZInput.instance.GetBoundKeyString("drawer_mod_clear", false); text = text + "\n[<color=yellow><b>" + boundKeyString3 + "+$KEY_Use</b></color>] Clear Item"; } text = text + "\n[<color=yellow><b>" + boundKeyString + "+$KEY_Use</b></color>] Deposit All"; } return Localization.instance.Localize(text); } public bool Interact(Humanoid user, bool hold, bool alt) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) if (_item == null || (Object)(object)user != (Object)(object)Player.m_localPlayer || !PrivateArea.CheckAccess(((Component)this).transform.position, 0f, true, false)) { return false; } if (!ZInput.IsGamepadActive()) { return OnKeyboardInteract(Player.m_localPlayer); } return OnGamepadInteract(Player.m_localPlayer); } private bool OnGamepadInteract(Player player) { bool blocking = ((Character)player).m_blocking; bool crouchToggled = player.m_crouchToggled; return ProcessInputInternal(player, blocking, crouchToggled); } private bool OnKeyboardInteract(Player player) { bool button = ZInput.GetButton("drawer_mod_deposit_all"); bool button2 = ZInput.GetButton((_quantity > 0) ? "drawer_mod_withdraw_one" : "drawer_mod_clear"); return ProcessInputInternal(player, button, button2); } private bool ProcessInputInternal(Player player, bool mod0, bool mod1) { if (mod0) { return UseItem((Humanoid)(object)player, _item); } if (mod1 && _quantity <= 0) { _nview.InvokeRPC("Clear", Array.Empty<object>()); } else if (_quantity > 0) { _nview.InvokeRPC("Drop", new object[1] { mod1 ? 1 : _item.m_shared.m_maxStackSize }); } return true; } public bool UseItem(Humanoid user, ItemData item) { if (item.m_shared.m_maxStackSize <= 1) { return false; } if (_item == null || (Object)(object)_item.m_dropPrefab == (Object)(object)item.m_dropPrefab) { int num = user.GetInventory().CountItems(item.m_shared.m_name, -1, true); if (num > 0) { _nview.InvokeRPC("AddItem", new object[2] { ((Object)item.m_dropPrefab).name, num }); return true; } } return false; } private void UpdateInventory() { if (base.m_inventory.m_inventory.Count > 0) { ItemData val = base.m_inventory.m_inventory[0]; if (val.m_shared.m_name == _item.m_shared.m_name) { val.m_stack = _quantity; val.m_shared.m_maxStackSize = ItemDrawerPlugin.MaxItems; } else { _addItem(); } } else { _addItem(); } void _addItem() { base.m_inventory.m_inventory.Clear(); ItemData val2 = _item.Clone(); val2.m_stack = _quantity; val2.m_shared = CommonUtils.Clone<SharedData>(val2.m_shared); val2.m_shared.m_maxStackSize = ItemDrawerPlugin.MaxItems; base.m_inventory.m_inventory.Add(val2); } } private ItemDrop Drop(int quantity) { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) if (_quantity > 0) { GameObject dropPrefab = _item.m_dropPrefab; int num = Mathf.Min(quantity, _quantity); _quantity -= num; _pickupDelay = 5; UpdateInventory(); Vector3 val = ((Component)this).transform.position + ((Component)this).transform.forward * -0.5f; GameObject obj = Object.Instantiate<GameObject>(dropPrefab, val, Quaternion.identity); obj.GetComponent<Rigidbody>().velocity = Vector3.up * 4f + ((Component)this).transform.forward * 4f; ItemDrop component = obj.GetComponent<ItemDrop>(); component.SetStack(num); return component; } return null; } public int AddItem(string name, int stack) { //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(name); if ((Object)(object)itemPrefab == (Object)null) { ZLog.Log((object)("Failed to find item prefab " + name)); return 0; } ItemDrop component = itemPrefab.GetComponent<ItemDrop>(); if ((Object)(object)component == (Object)null) { ZLog.Log((object)("Invalid item " + name)); return 0; } if (_item != null && (Object)(object)_item.m_dropPrefab != (Object)(object)itemPrefab) { ZLog.Log((object)("Cannot add to container " + name)); return 0; } stack = Math.Min(stack, ItemDrawerPlugin.MaxItems - _quantity); _item = component.m_itemData; _item.m_dropPrefab = itemPrefab; _item.m_gridPos = Vector2i.zero; _quantity += stack; UpdateInventory(); OnContainerChanged(); return stack; } private void OnContainerChanged() { if (!_loading && _nview.IsOwner()) { Save(); } } private void OnInventoryChanged() { if (base.m_inventory.m_inventory.Count > 0) { ItemData val = base.m_inventory.m_inventory[0]; int num = _quantity - val.m_stack; _quantity = Math.Max(0, _quantity - num); } else { _quantity = 0; } OnContainerChanged(); UpdateVisuals(); } private void RPC_AddItem(long playerId, string prefabName, int quantity) { if (_nview.IsOwner()) { int num = AddItem(prefabName, quantity); if (num > 0) { _nview.InvokeRPC(playerId, "AddItemResponse", new object[2] { prefabName, num }); } } } private void RPC_AddItemResponse(long _, string prefabName, int quantity) { if (Object.op_Implicit((Object)(object)Player.m_localPlayer) && quantity > 0) { ItemDrop component = ObjectDB.instance.GetItemPrefab(prefabName).GetComponent<ItemDrop>(); if (component != null) { string name = component.m_itemData.m_shared.m_name; ((Humanoid)Player.m_localPlayer).GetInventory().RemoveItem(name, quantity, -1, true); } } } private void RPC_Clear(long _) { if (_nview.IsOwner() && _item != null) { Clear(); OnContainerChanged(); } } private void RPC_Drop(long playerId, int quantity) { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) if (_nview.IsOwner() && _item != null) { ItemDrop val = Drop(quantity); if (val != null) { OnContainerChanged(); ZDOID uid = val.m_nview.GetZDO().m_uid; _nview.InvokeRPC(playerId, "DropResponse", new object[1] { uid }); } } } private void RPC_DropResponse(long _, ZDOID drop) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) GameObject val = ZNetScene.instance.FindInstance(drop); if ((Object)(object)val != (Object)null) { ItemDrop component = val.GetComponent<ItemDrop>(); if (!((Object)(object)component == (Object)null) && ((Humanoid)Player.m_localPlayer).GetInventory().AddItem(component.m_itemData)) { ((Character)Player.m_localPlayer).ShowPickupMessage(component.m_itemData, component.m_itemData.m_stack); ZNetScene.instance.Destroy(val); } } } private void UpdateVisuals() { if ((Object)(object)_image != (Object)null) { bool flag = _item != null && _item.m_shared.m_icons.Count() > 0; ((Component)_image).gameObject.SetActive(flag); if (flag) { _image.sprite = _item.m_shared.m_icons[0]; } } if ((Object)(object)_text != (Object)null) { _text.text = ((_quantity > 0) ? _quantity.ToString() : string.Empty); } } private void Clear() { _quantity = 0; _item = null; base.m_inventory.m_inventory.Clear(); UpdateVisuals(); } private void Save() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown //IL_00a6: Unknown result type (might be due to invalid IL or missing references) ZPackage val = new ZPackage(); val.Write(0); if (_item != null) { val.Write(((Object)(object)_item.m_dropPrefab == (Object)null) ? "" : ((Object)_item.m_dropPrefab).name); val.Write(_quantity); } else { val.Write(""); } string @base = val.GetBase64(); _nview.GetZDO().Set("items", @base); _lastRevision = _nview.GetZDO().DataRevision; ZDOMan.instance.ForceSendZDO(_nview.GetZDO().m_uid); } private void Load() { //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Expected O, but got Unknown if (_nview.GetZDO().DataRevision == _lastRevision && _lastRevision.HasValue) { return; } Clear(); string @string = _nview.GetZDO().GetString("items", ""); if (!string.IsNullOrEmpty(@string)) { ZPackage val = new ZPackage(@string); val.ReadInt(); string text = val.ReadString(); if (!string.IsNullOrEmpty(text)) { int stack = val.ReadInt(); _loading = true; AddItem(text, stack); _loading = false; _lastRevision = _nview.GetZDO().DataRevision; } } } public void OnDestroyed_Drawer() { if (_nview.IsOwner() && _item != null) { while (_quantity > 0 && Object.op_Implicit((Object)(object)Drop(_item.m_shared.m_maxStackSize))) { } } } } namespace itemdrawers { [BepInPlugin("mkz.itemdrawers", "Item Drawers", "0.5.7")] public class ItemDrawerPlugin : BaseUnityPlugin { public static ItemDrawerPlugin Instance; private Harmony _harmony; private static ConfigEntry<bool> _retreiveEnabled; private static ConfigEntry<float> _retreiveRadius; private static ConfigEntry<bool> _enabled; private static ConfigEntry<int> _maxItems; private static ConfigEntry<Color> _configFontColor; private static ConfigEntry<Color> _configOutlineColor; private static ConfigEntry<bool> _configOutlineEnabled; private static ConfigEntry<string> _configKeyDepositAll; private static ConfigEntry<string> _configKeyWithdrawOne; private static ConfigEntry<string> _configKeyClear; public const string KeyDepositAll = "drawer_mod_deposit_all"; public const string KeyWithdrawOne = "drawer_mod_withdraw_one"; public const string KeyClear = "drawer_mod_clear"; public static float RetreiveRadius => _retreiveRadius.Value; public static bool RetreiveEnabled => _retreiveEnabled.Value; public static bool Enabled => _enabled.Value; public static int MaxItems => _maxItems.Value; public static bool OutlineEnabled => _configOutlineEnabled.Value; public static Color OutlineColor => _configOutlineColor.Value; public static Color FontColor => _configFontColor.Value; private Config AuthoritativeConfig => Config.Instance; public void Awake() { Instance = this; _harmony = Harmony.CreateAndPatchAll(typeof(ItemDrawerPlugin), (string)null); AssetManager.Create(_harmony, "itemdrawers"); LoadConfig(); } public void OnDestroy() { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchAll((string)null); } } [HarmonyPatch(typeof(Container), "Awake")] [HarmonyPrefix] private static bool Container_Awake_Prefix(Container __instance) { if (__instance is DrawerContainer drawerContainer) { drawerContainer.Awake_Drawer(); return false; } return true; } [HarmonyPatch(typeof(Container), "OnDestroyed")] [HarmonyPrefix] private static bool Container_OnDestroyed_Prefix(Container __instance) { if (__instance is DrawerContainer drawerContainer) { drawerContainer.OnDestroyed_Drawer(); return false; } return true; } [HarmonyPatch(typeof(Container), "Save")] [HarmonyPrefix] private static bool Container_Save_Prefix(Container __instance) { return !(__instance is DrawerContainer); } [HarmonyPatch(typeof(Container), "Load")] [HarmonyPrefix] private static bool Container_Load_Prefix(Container __instance) { return !(__instance is DrawerContainer); } [HarmonyPatch(typeof(ZNetScene), "Awake")] [HarmonyPostfix] private static void ZNetScene_Awake_Postfix() { Instance.ApplyConfig(); } [HarmonyPatch(typeof(Player), "IsOverlappingOtherPiece")] [HarmonyPostfix] private static void Player_IsOverlappingOtherPiece_PostFix(string pieceName, ref bool __result) { if (pieceName == "piece_drawer") { __result = false; } } [HarmonyPatch(typeof(ZInput), "Load")] [HarmonyPostfix] private static void ZInput_Load_PostFix(ZInput __instance) { __instance.AddButton("drawer_mod_deposit_all", parseKeyConfig(_configKeyDepositAll, (Key)51), false, true, false, 0f, 0f); __instance.AddButton("drawer_mod_withdraw_one", parseKeyConfig(_configKeyWithdrawOne, (Key)53), false, true, false, 0f, 0f); __instance.AddButton("drawer_mod_clear", parseKeyConfig(_configKeyClear, (Key)53), false, true, false, 0f, 0f); static string parseKeyConfig(ConfigEntry<string> configString, Key @default) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) if (Enum.TryParse<Key>(configString.Value, out Key result)) { return ZInput.KeyToPath(result); } return ZInput.KeyToPath(@default); } } private void LoadConfig() { //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_018f: Unknown result type (might be due to invalid IL or missing references) //IL_01e1: Unknown result type (might be due to invalid IL or missing references) //IL_01eb: Expected O, but got Unknown AuthoritativeConfig.Init((BaseUnityPlugin)(object)this, defaultBindServerAuthority: true); _enabled = AuthoritativeConfig.Bind("General", "Enabled", defaultValue: true, "Enable creation of Item Drawers"); _maxItems = AuthoritativeConfig.Bind("General", "MaxItems", 9999, "The maximum number of items that can be stored in a drawer"); _retreiveEnabled = AuthoritativeConfig.Bind("Item Retreival", "Enabled", defaultValue: true, "Drawers will retreive dropped items matching their item"); _retreiveRadius = AuthoritativeConfig.Bind("Item Retreival", "Radius", 5f, "The distance drawers will check for dropped items"); _configKeyDepositAll = AuthoritativeConfig.Bind("Hotkeys", "Deposit All", "LeftShift", "Hold while interacting to deposit all", false); _configKeyWithdrawOne = AuthoritativeConfig.Bind("Hotkeys", "Withdraw One", "LeftAlt", "Hold while interacting to withdraw one", false); _configKeyClear = AuthoritativeConfig.Bind("Hotkeys", "Clear", "LeftAlt", "Hold while interacting to clear contents (only if 0 quantity)", false); _configFontColor = AuthoritativeConfig.Bind<Color>("Font", "Color", new Color(1f, 0.5f, 0f), "Drawer font color", false); _configOutlineColor = AuthoritativeConfig.Bind<Color>("Font", "Outline Color", new Color(0f, 0f, 0f), "Drawer outline color", false); _configOutlineEnabled = AuthoritativeConfig.Bind("Font", "Outline Enabled", defaultValue: true, "Enabled font outline on drawers", false); AuthoritativeConfig.OnConfigReceived.AddListener(new UnityAction(ApplyConfig)); } private void ApplyConfig() { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) Piece gameObject = AssetManager.GetGameObject<Piece>("piece_drawer"); if (gameObject != null) { gameObject.m_enabled = Enabled; DrawerContainer component = ((Component)gameObject).GetComponent<DrawerContainer>(); component._text.text = MaxItems.ToString(); component.SetFontColor(_configFontColor.Value, _configOutlineColor.Value, _configOutlineEnabled.Value); } } } } namespace SimpleJSON { public enum JSONNodeType { Array = 1, Object = 2, String = 3, Number = 4, NullValue = 5, Boolean = 6, None = 7, Custom = 255 } public enum JSONTextMode { Compact, Indent } public abstract class JSONNode { public struct Enumerator { private enum Type { None, Array, Object } private Type type; private Dictionary<string, JSONNode>.Enumerator m_Object; private List<JSONNode>.Enumerator m_Array; public bool IsValid => type != Type.None; public KeyValuePair<string, JSONNode> Current { get { if (type == Type.Array) { return new KeyValuePair<string, JSONNode>(string.Empty, m_Array.Current); } if (type == Type.Object) { return m_Object.Current; } return new KeyValuePair<string, JSONNode>(string.Empty, null); } } public Enumerator(List<JSONNode>.Enumerator aArrayEnum) { type = Type.Array; m_Object = default(Dictionary<string, JSONNode>.Enumerator); m_Array = aArrayEnum; } public Enumerator(Dictionary<string, JSONNode>.Enumerator aDictEnum) { type = Type.Object; m_Object = aDictEnum; m_Array = default(List<JSONNode>.Enumerator); } public bool MoveNext() { if (type == Type.Array) { return m_Array.MoveNext(); } if (type == Type.Object) { return m_Object.MoveNext(); } return false; } } public struct ValueEnumerator { private Enumerator m_Enumerator; public JSONNode Current => m_Enumerator.Current.Value; public ValueEnumerator(List<JSONNode>.Enumerator aArrayEnum) : this(new Enumerator(aArrayEnum)) { } public ValueEnumerator(Dictionary<string, JSONNode>.Enumerator aDictEnum) : this(new Enumerator(aDictEnum)) { } public ValueEnumerator(Enumerator aEnumerator) { m_Enumerator = aEnumerator; } public bool MoveNext() { return m_Enumerator.MoveNext(); } public ValueEnumerator GetEnumerator() { return this; } } public struct KeyEnumerator { private Enumerator m_Enumerator; public string Current => m_Enumerator.Current.Key; public KeyEnumerator(List<JSONNode>.Enumerator aArrayEnum) : this(new Enumerator(aArrayEnum)) { } public KeyEnumerator(Dictionary<string, JSONNode>.Enumerator aDictEnum) : this(new Enumerator(aDictEnum)) { } public KeyEnumerator(Enumerator aEnumerator) { m_Enumerator = aEnumerator; } public bool MoveNext() { return m_Enumerator.MoveNext(); } public KeyEnumerator GetEnumerator() { return this; } } public class LinqEnumerator : IEnumerator<KeyValuePair<string, JSONNode>>, IDisposable, IEnumerator, IEnumerable<KeyValuePair<string, JSONNode>>, IEnumerable { private JSONNode m_Node; private Enumerator m_Enumerator; public KeyValuePair<string, JSONNode> Current => m_Enumerator.Current; object IEnumerator.Current => m_Enumerator.Current; internal LinqEnumerator(JSONNode aNode) { m_Node = aNode; if (m_Node != null) { m_Enumerator = m_Node.GetEnumerator(); } } public bool MoveNext() { return m_Enumerator.MoveNext(); } public void Dispose() { m_Node = null; m_Enumerator = default(Enumerator); } public IEnumerator<KeyValuePair<string, JSONNode>> GetEnumerator() { return new LinqEnumerator(m_Node); } public void Reset() { if (m_Node != null) { m_Enumerator = m_Node.GetEnumerator(); } } IEnumerator IEnumerable.GetEnumerator() { return new LinqEnumerator(m_Node); } } public static bool forceASCII = false; public static bool longAsString = false; public static bool allowLineComments = true; [ThreadStatic] private static StringBuilder m_EscapeBuilder; public abstract JSONNodeType Tag { get; } public virtual JSONNode this[int aIndex] { get { return null; } set { } } public virtual JSONNode this[string aKey] { get { return null; } set { } } public virtual string Value { get { return ""; } set { } } public virtual int Count => 0; public virtual bool IsNumber => false; public virtual bool IsString => false; public virtual bool IsBoolean => false; public virtual bool IsNull => false; public virtual bool IsArray => false; public virtual bool IsObject => false; public virtual bool Inline { get { return false; } set { } } public virtual IEnumerable<JSONNode> Children { get { yield break; } } public IEnumerable<JSONNode> DeepChildren { get { foreach (JSONNode child in Children) { foreach (JSONNode deepChild in child.DeepChildren) { yield return deepChild; } } } } public IEnumerable<KeyValuePair<string, JSONNode>> Linq => new LinqEnumerator(this); public KeyEnumerator Keys => new KeyEnumerator(GetEnumerator()); public ValueEnumerator Values => new ValueEnumerator(GetEnumerator()); public virtual double AsDouble { get { double result = 0.0; if (double.TryParse(Value, NumberStyles.Float, CultureInfo.InvariantCulture, out result)) { return result; } return 0.0; } set { Value = value.ToString(CultureInfo.InvariantCulture); } } public virtual int AsInt { get { return (int)AsDouble; } set { AsDouble = value; } } public virtual float AsFloat { get { return (float)AsDouble; } set { AsDouble = value; } } public virtual bool AsBool { get { bool result = false; if (bool.TryParse(Value, out result)) { return result; } return !string.IsNullOrEmpty(Value); } set { Value = (value ? "true" : "false"); } } public virtual long AsLong { get { long result = 0L; if (long.TryParse(Value, out result)) { return result; } return 0L; } set { Value = value.ToString(); } } public virtual ulong AsULong { get { ulong result = 0uL; if (ulong.TryParse(Value, out result)) { return result; } return 0uL; } set { Value = value.ToString(); } } public virtual JSONArray AsArray => this as JSONArray; public virtual JSONObject AsObject => this as JSONObject; internal static StringBuilder EscapeBuilder { get { if (m_EscapeBuilder == null) { m_EscapeBuilder = new StringBuilder(); } return m_EscapeBuilder; } } public virtual decimal AsDecimal { get { if (!decimal.TryParse(Value, out var result)) { result = default(decimal); } return result; } set { Value = value.ToString(); } } public virtual char AsChar { get { if (IsString && Value.Length > 0) { return Value[0]; } if (IsNumber) { return (char)AsInt; } return '\0'; } set { if (IsString) { Value = value.ToString(); } else if (IsNumber) { AsInt = value; } } } public virtual uint AsUInt { get { return (uint)AsDouble; } set { AsDouble = value; } } public virtual byte AsByte { get { return (byte)AsInt; } set { AsInt = value; } } public virtual sbyte AsSByte { get { return (sbyte)AsInt; } set { AsInt = value; } } public virtual short AsShort { get { return (short)AsInt; } set { AsInt = value; } } public virtual ushort AsUShort { get { return (ushort)AsInt; } set { AsInt = value; } } public virtual DateTime AsDateTime { get { if (!DateTime.TryParse(Value, CultureInfo.InvariantCulture, DateTimeStyles.None, out var result)) { result = new DateTime(0L); } return result; } set { Value = value.ToString(CultureInfo.InvariantCulture); } } public virtual TimeSpan AsTimeSpan { get { if (!TimeSpan.TryParse(Value, CultureInfo.InvariantCulture, out var result)) { result = new TimeSpan(0L); } return result; } set { Value = value.ToString(); } } public virtual Guid AsGuid { get { Guid.TryParse(Value, out var result); return result; } set { Value = value.ToString(); } } public virtual byte[] AsByteArray { get { if (IsNull || !IsArray) { return null; } int count = Count; byte[] array = new byte[count]; for (int i = 0; i < count; i++) { array[i] = this[i].AsByte; } return array; } set { if (IsArray && value != null) { Clear(); for (int i = 0; i < value.Length; i++) { Add(value[i]); } } } } public virtual List<byte> AsByteList { get { if (IsNull || !IsArray) { return null; } int count = Count; List<byte> list = new List<byte>(count); for (int i = 0; i < count; i++) { list.Add(this[i].AsByte); } return list; } set { if (IsArray && value != null) { Clear(); for (int i = 0; i < value.Count; i++) { Add(value[i]); } } } } public virtual string[] AsStringArray { get { if (IsNull || !IsArray) { return null; } int count = Count; string[] array = new string[count]; for (int i = 0; i < count; i++) { array[i] = this[i].Value; } return array; } set { if (IsArray && value != null) { Clear(); for (int i = 0; i < value.Length; i++) { Add(value[i]); } } } } public virtual List<string> AsStringList { get { if (IsNull || !IsArray) { return null; } int count = Count; List<string> list = new List<string>(count); for (int i = 0; i < count; i++) { list.Add(this[i].Value); } return list; } set { if (IsArray && value != null) { Clear(); for (int i = 0; i < value.Count; i++) { Add(value[i]); } } } } public virtual void Add(string aKey, JSONNode aItem) { } public virtual void Add(JSONNode aItem) { Add("", aItem); } public virtual JSONNode Remove(string aKey) { return null; } public virtual JSONNode Remove(int aIndex) { return null; } public virtual JSONNode Remove(JSONNode aNode) { return aNode; } public virtual void Clear() { } public virtual JSONNode Clone() { return null; } public virtual bool HasKey(string aKey) { return false; } public virtual JSONNode GetValueOrDefault(string aKey, JSONNode aDefault) { return aDefault; } public override string ToString() { StringBuilder stringBuilder = new StringBuilder(); WriteToStringBuilder(stringBuilder, 0, 0, JSONTextMode.Compact); return stringBuilder.ToString(); } public virtual string ToString(int aIndent) { StringBuilder stringBuilder = new StringBuilder(); WriteToStringBuilder(stringBuilder, 0, aIndent, JSONTextMode.Indent); return stringBuilder.ToString(); } internal abstract void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode); public abstract Enumerator GetEnumerator(); public static implicit operator JSONNode(string s) { if (s != null) { return new JSONString(s); } return JSONNull.CreateOrGet(); } public static implicit operator string(JSONNode d) { if (!(d == null)) { return d.Value; } return null; } public static implicit operator JSONNode(double n) { return new JSONNumber(n); } public static implicit operator double(JSONNode d) { if (!(d == null)) { return d.AsDouble; } return 0.0; } public static implicit operator JSONNode(float n) { return new JSONNumber(n); } public static implicit operator float(JSONNode d) { if (!(d == null)) { return d.AsFloat; } return 0f; } public static implicit operator JSONNode(int n) { return new JSONNumber(n); } public static implicit operator int(JSONNode d) { if (!(d == null)) { return d.AsInt; } return 0; } public static implicit operator JSONNode(long n) { if (longAsString) { return new JSONString(n.ToString()); } return new JSONNumber(n); } public static implicit operator long(JSONNode d) { if (!(d == null)) { return d.AsLong; } return 0L; } public static implicit operator JSONNode(ulong n) { if (longAsString) { return new JSONString(n.ToString()); } return new JSONNumber(n); } public static implicit operator ulong(JSONNode d) { if (!(d == null)) { return d.AsULong; } return 0uL; } public static implicit operator JSONNode(bool b) { return new JSONBool(b); } public static implicit operator bool(JSONNode d) { if (!(d == null)) { return d.AsBool; } return false; } public static implicit operator JSONNode(KeyValuePair<string, JSONNode> aKeyValue) { return aKeyValue.Value; } public static bool operator ==(JSONNode a, object b) { if ((object)a == b) { return true; } bool flag = a is JSONNull || (object)a == null || a is JSONLazyCreator; bool flag2 = b is JSONNull || b == null || b is JSONLazyCreator; if (flag && flag2) { return true; } if (!flag) { return a.Equals(b); } return false; } public static bool operator !=(JSONNode a, object b) { return !(a == b); } public override bool Equals(object obj) { return (object)this == obj; } public override int GetHashCode() { return base.GetHashCode(); } internal static string Escape(string aText) { StringBuilder escapeBuilder = EscapeBuilder; escapeBuilder.Length = 0; if (escapeBuilder.Capacity < aText.Length + aText.Length / 10) { escapeBuilder.Capacity = aText.Length + aText.Length / 10; } foreach (char c in aText) { switch (c) { case '\\': escapeBuilder.Append("\\\\"); continue; case '"': escapeBuilder.Append("\\\""); continue; case '\n': escapeBuilder.Append("\\n"); continue; case '\r': escapeBuilder.Append("\\r"); continue; case '\t': escapeBuilder.Append("\\t"); continue; case '\b': escapeBuilder.Append("\\b"); continue; case '\f': escapeBuilder.Append("\\f"); continue; } if (c < ' ' || (forceASCII && c > '\u007f')) { ushort num = c; escapeBuilder.Append("\\u").Append(num.ToString("X4")); } else { escapeBuilder.Append(c); } } string result = escapeBuilder.ToString(); escapeBuilder.Length = 0; return result; } private static JSONNode ParseElement(string token, bool quoted) { if (quoted) { return token; } if (token.Length <= 5) { string text = token.ToLower(); switch (text) { case "false": case "true": return text == "true"; case "null": return JSONNull.CreateOrGet(); } } if (double.TryParse(token, NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { return result; } return token; } public static JSONNode Parse(string aJSON) { Stack<JSONNode> stack = new Stack<JSONNode>(); JSONNode jSONNode = null; int i = 0; StringBuilder stringBuilder = new StringBuilder(); string aKey = ""; bool flag = false; bool flag2 = false; bool flag3 = false; for (; i < aJSON.Length; i++) { switch (aJSON[i]) { case '{': if (flag) { stringBuilder.Append(aJSON[i]); break; } stack.Push(new JSONObject()); if (jSONNode != null) { jSONNode.Add(aKey, stack.Peek()); } aKey = ""; stringBuilder.Length = 0; jSONNode = stack.Peek(); flag3 = false; break; case '[': if (flag) { stringBuilder.Append(aJSON[i]); break; } stack.Push(new JSONArray()); if (jSONNode != null) { jSONNode.Add(aKey, stack.Peek()); } aKey = ""; stringBuilder.Length = 0; jSONNode = stack.Peek(); flag3 = false; break; case ']': case '}': if (flag) { stringBuilder.Append(aJSON[i]); break; } if (stack.Count == 0) { throw new Exception("JSON Parse: Too many closing brackets"); } stack.Pop(); if (stringBuilder.Length > 0 || flag2) { jSONNode.Add(aKey, ParseElement(stringBuilder.ToString(), flag2)); } if (jSONNode != null) { jSONNode.Inline = !flag3; } flag2 = false; aKey = ""; stringBuilder.Length = 0; if (stack.Count > 0) { jSONNode = stack.Peek(); } break; case ':': if (flag) { stringBuilder.Append(aJSON[i]); break; } aKey = stringBuilder.ToString(); stringBuilder.Length = 0; flag2 = false; break; case '"': flag = !flag; flag2 = flag2 || flag; break; case ',': if (flag) { stringBuilder.Append(aJSON[i]); break; } if (stringBuilder.Length > 0 || flag2) { jSONNode.Add(aKey, ParseElement(stringBuilder.ToString(), flag2)); } flag2 = false; aKey = ""; stringBuilder.Length = 0; flag2 = false; break; case '\n': case '\r': flag3 = true; break; case '\t': case ' ': if (flag) { stringBuilder.Append(aJSON[i]); } break; case '\\': i++; if (flag) { char c = aJSON[i]; switch (c) { case 't': stringBuilder.Append('\t'); break; case 'r': stringBuilder.Append('\r'); break; case 'n': stringBuilder.Append('\n'); break; case 'b': stringBuilder.Append('\b'); break; case 'f': stringBuilder.Append('\f'); break; case 'u': { string s = aJSON.Substring(i + 1, 4); stringBuilder.Append((char)int.Parse(s, NumberStyles.AllowHexSpecifier)); i += 4; break; } default: stringBuilder.Append(c); break; } } break; case '/': if (allowLineComments && !flag && i + 1 < aJSON.Length && aJSON[i + 1] == '/') { while (++i < aJSON.Length && aJSON[i] != '\n' && aJSON[i] != '\r') { } } else { stringBuilder.Append(aJSON[i]); } break; default: stringBuilder.Append(aJSON[i]); break; case '\ufeff': break; } } if (flag) { throw new Exception("JSON Parse: Quotation marks seems to be messed up."); } if (jSONNode == null) { return ParseElement(stringBuilder.ToString(), flag2); } return jSONNode; } public abstract void SerializeBinary(BinaryWriter aWriter); public void SaveToBinaryStream(Stream aData) { BinaryWriter aWriter = new BinaryWriter(aData); SerializeBinary(aWriter); } public void SaveToCompressedStream(Stream aData) { throw new Exception("Can't use compressed functions. You need include the SharpZipLib and uncomment the define at the top of SimpleJSON"); } public void SaveToCompressedFile(string aFileName) { throw new Exception("Can't use compressed functions. You need include the SharpZipLib and uncomment the define at the top of SimpleJSON"); } public string SaveToCompressedBase64() { throw new Exception("Can't use compressed functions. You need include the SharpZipLib and uncomment the define at the top of SimpleJSON"); } public void SaveToBinaryFile(string aFileName) { Directory.CreateDirectory(new FileInfo(aFileName).Directory.FullName); using FileStream aData = File.OpenWrite(aFileName); SaveToBinaryStream(aData); } public string SaveToBinaryBase64() { using MemoryStream memoryStream = new MemoryStream(); SaveToBinaryStream(memoryStream); memoryStream.Position = 0L; return Convert.ToBase64String(memoryStream.ToArray()); } public static JSONNode DeserializeBinary(BinaryReader aReader) { JSONNodeType jSONNodeType = (JSONNodeType)aReader.ReadByte(); switch (jSONNodeType) { case JSONNodeType.Array: { int num2 = aReader.ReadInt32(); JSONArray jSONArray = new JSONArray(); for (int j = 0; j < num2; j++) { jSONArray.Add(DeserializeBinary(aReader)); } return jSONArray; } case JSONNodeType.Object: { int num = aReader.ReadInt32(); JSONObject jSONObject = new JSONObject(); for (int i = 0; i < num; i++) { string aKey = aReader.ReadString(); JSONNode aItem = DeserializeBinary(aReader); jSONObject.Add(aKey, aItem); } return jSONObject; } case JSONNodeType.String: return new JSONString(aReader.ReadString()); case JSONNodeType.Number: return new JSONNumber(aReader.ReadDouble()); case JSONNodeType.Boolean: return new JSONBool(aReader.ReadBoolean()); case JSONNodeType.NullValue: return JSONNull.CreateOrGet(); default: throw new Exception("Error deserializing JSON. Unknown tag: " + jSONNodeType); } } public static JSONNode LoadFromCompressedFile(string aFileName) { throw new Exception("Can't use compressed functions. You need include the SharpZipLib and uncomment the define at the top of SimpleJSON"); } public static JSONNode LoadFromCompressedStream(Stream aData) { throw new Exception("Can't use compressed functions. You need include the SharpZipLib and uncomment the define at the top of SimpleJSON"); } public static JSONNode LoadFromCompressedBase64(string aBase64) { throw new Exception("Can't use compressed functions. You need include the SharpZipLib and uncomment the define at the top of SimpleJSON"); } public static JSONNode LoadFromBinaryStream(Stream aData) { using BinaryReader aReader = new BinaryReader(aData); return DeserializeBinary(aReader); } public static JSONNode LoadFromBinaryFile(string aFileName) { using FileStream aData = File.OpenRead(aFileName); return LoadFromBinaryStream(aData); } public static JSONNode LoadFromBinaryBase64(string aBase64) { return LoadFromBinaryStream(new MemoryStream(Convert.FromBase64String(aBase64)) { Position = 0L }); } public static implicit operator JSONNode(decimal aDecimal) { return new JSONString(aDecimal.ToString()); } public static implicit operator decimal(JSONNode aNode) { return aNode.AsDecimal; } public static implicit operator JSONNode(char aChar) { return new JSONString(aChar.ToString()); } public static implicit operator char(JSONNode aNode) { return aNode.AsChar; } public static implicit operator JSONNode(uint aUInt) { return new JSONNumber(aUInt); } public static implicit operator uint(JSONNode aNode) { return aNode.AsUInt; } public static implicit operator JSONNode(byte aByte) { return new JSONNumber((int)aByte); } public static implicit operator byte(JSONNode aNode) { return aNode.AsByte; } public static implicit operator JSONNode(sbyte aSByte) { return new JSONNumber(aSByte); } public static implicit operator sbyte(JSONNode aNode) { return aNode.AsSByte; } public static implicit operator JSONNode(short aShort) { return new JSONNumber(aShort); } public static implicit operator short(JSONNode aNode) { return aNode.AsShort; } public static implicit operator JSONNode(ushort aUShort) { return new JSONNumber((int)aUShort); } public static implicit operator ushort(JSONNode aNode) { return aNode.AsUShort; } public static implicit operator JSONNode(DateTime aDateTime) { return new JSONString(aDateTime.ToString(CultureInfo.InvariantCulture)); } public static implicit operator DateTime(JSONNode aNode) { return aNode.AsDateTime; } public static implicit operator JSONNode(TimeSpan aTimeSpan) { return new JSONString(aTimeSpan.ToString()); } public static implicit operator TimeSpan(JSONNode aNode) { return aNode.AsTimeSpan; } public static implicit operator JSONNode(Guid aGuid) { return new JSONString(aGuid.ToString()); } public static implicit operator Guid(JSONNode aNode) { return aNode.AsGuid; } public static implicit operator JSONNode(byte[] aByteArray) { return new JSONArray { AsByteArray = aByteArray }; } public static implicit operator byte[](JSONNode aNode) { return aNode.AsByteArray; } public static implicit operator JSONNode(List<byte> aByteList) { return new JSONArray { AsByteList = aByteList }; } public static implicit operator List<byte>(JSONNode aNode) { return aNode.AsByteList; } public static implicit operator JSONNode(string[] aStringArray) { return new JSONArray { AsStringArray = aStringArray }; } public static implicit operator string[](JSONNode aNode) { return aNode.AsStringArray; } public static implicit operator JSONNode(List<string> aStringList) { return new JSONArray { AsStringList = aStringList }; } public static implicit operator List<string>(JSONNode aNode) { return aNode.AsStringList; } public static implicit operator JSONNode(int? aValue) { if (!aValue.HasValue) { return JSONNull.CreateOrGet(); } return new JSONNumber(aValue.Value); } public static implicit operator int?(JSONNode aNode) { if (aNode == null || aNode.IsNull) { return null; } return aNode.AsInt; } public static implicit operator JSONNode(float? aValue) { if (!aValue.HasValue) { return JSONNull.CreateOrGet(); } return new JSONNumber(aValue.Value); } public static implicit operator float?(JSONNode aNode) { if (aNode == null || aNode.IsNull) { return null; } return aNode.AsFloat; } public static implicit operator JSONNode(double? aValue) { if (!aValue.HasValue) { return JSONNull.CreateOrGet(); } return new JSONNumber(aValue.Value); } public static implicit operator double?(JSONNode aNode) { if (aNode == null || aNode.IsNull) { return null; } return aNode.AsDouble; } public static implicit operator JSONNode(bool? aValue) { if (!aValue.HasValue) { return JSONNull.CreateOrGet(); } return new JSONBool(aValue.Value); } public static implicit operator bool?(JSONNode aNode) { if (aNode == null || aNode.IsNull) { return null; } return aNode.AsBool; } public static implicit operator JSONNode(long? aValue) { if (!aValue.HasValue) { return JSONNull.CreateOrGet(); } return new JSONNumber(aValue.Value); } public static implicit operator long?(JSONNode aNode) { if (aNode == null || aNode.IsNull) { return null; } return aNode.AsLong; } public static implicit operator JSONNode(short? aValue) { if (!aValue.HasValue) { return JSONNull.CreateOrGet(); } return new JSONNumber(aValue.Value); } public static implicit operator short?(JSONNode aNode) { if (aNode == null || aNode.IsNull) { return null; } return aNode.AsShort; } } public class JSONArray : JSONNode { private List<JSONNode> m_List = new List<JSONNode>(); private bool inline; public override bool Inline { get { return inline; } set { inline = value; } } public override JSONNodeType Tag => JSONNodeType.Array; public override bool IsArray => true; public override JSONNode this[int aIndex] { get { if (aIndex < 0 || aIndex >= m_List.Count) { return new JSONLazyCreator(this); } return m_List[aIndex]; } set { if (value == null) { value = JSONNull.CreateOrGet(); } if (aIndex < 0 || aIndex >= m_List.Count) { m_List.Add(value); } else { m_List[aIndex] = value; } } } public override JSONNode this[string aKey] { get { return new JSONLazyCreator(this); } set { if (value == null) { value = JSONNull.CreateOrGet(); } m_List.Add(value); } } public override int Count => m_List.Count; public override IEnumerable<JSONNode> Children { get { foreach (JSONNode item in m_List) { yield return item; } } } public override Enumerator GetEnumerator() { return new Enumerator(m_List.GetEnumerator()); } public override void Add(string aKey, JSONNode aItem) { if (aItem == null) { aItem = JSONNull.CreateOrGet(); } m_List.Add(aItem); } public override JSONNode Remove(int aIndex) { if (aIndex < 0 || aIndex >= m_List.Count) { return null; } JSONNode result = m_List[aIndex]; m_List.RemoveAt(aIndex); return result; } public override JSONNode Remove(JSONNode aNode) { m_List.Remove(aNode); return aNode; } public override void Clear() { m_List.Clear(); } public override JSONNode Clone() { JSONArray jSONArray = new JSONArray(); jSONArray.m_List.Capacity = m_List.Capacity; foreach (JSONNode item in m_List) { if (item != null) { jSONArray.Add(item.Clone()); } else { jSONArray.Add(null); } } return jSONArray; } internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) { aSB.Append('['); int count = m_List.Count; if (inline) { aMode = JSONTextMode.Compact; } for (int i = 0; i < count; i++) { if (i > 0) { aSB.Append(','); } if (aMode == JSONTextMode.Indent) { aSB.AppendLine(); } if (aMode == JSONTextMode.Indent) { aSB.Append(' ', aIndent + aIndentInc); } m_List[i].WriteToStringBuilder(aSB, aIndent + aIndentInc, aIndentInc, aMode); } if (aMode == JSONTextMode.Indent) { aSB.AppendLine().Append(' ', aIndent); } aSB.Append(']'); } public override void SerializeBinary(BinaryWriter aWriter) { aWriter.Write((byte)1); aWriter.Write(m_List.Count); for (int i = 0; i < m_List.Count; i++) { m_List[i].SerializeBinary(aWriter); } } } public class JSONObject : JSONNode { private Dictionary<string, JSONNode> m_Dict = new Dictionary<string, JSONNode>(); private bool inline; public override bool Inline { get { return inline; } set { inline = value; } } public override JSONNodeType Tag => JSONNodeType.Object; public override bool IsObject => true; public override JSONNode this[string aKey] { get { if (m_Dict.ContainsKey(aKey)) { return m_Dict[aKey]; } return new JSONLazyCreator(this, aKey); } set { if (value == null) { value = JSONNull.CreateOrGet(); } if (m_Dict.ContainsKey(aKey)) { m_Dict[aKey] = value; } else { m_Dict.Add(aKey, value); } } } public override JSONNode this[int aIndex] { get { if (aIndex < 0 || aIndex >= m_Dict.Count) { return null; } return m_Dict.ElementAt(aIndex).Value; } set { if (value == null) { value = JSONNull.CreateOrGet(); } if (aIndex >= 0 && aIndex < m_Dict.Count) { string key = m_Dict.ElementAt(aIndex).Key; m_Dict[key] = value; } } } public override int Count => m_Dict.Count; public override IEnumerable<JSONNode> Children { get { foreach (KeyValuePair<string, JSONNode> item in m_Dict) { yield return item.Value; } } } public override Enumerator GetEnumerator() { return new Enumerator(m_Dict.GetEnumerator()); } public override void Add(string aKey, JSONNode aItem) { if (aItem == null) { aItem = JSONNull.CreateOrGet(); } if (aKey != null) { if (m_Dict.ContainsKey(aKey)) { m_Dict[aKey] = aItem; } else { m_Dict.Add(aKey, aItem); } } else { m_Dict.Add(Guid.NewGuid().ToString(), aItem); } } public override JSONNode Remove(string aKey) { if (!m_Dict.ContainsKey(aKey)) { return null; } JSONNode result = m_Dict[aKey]; m_Dict.Remove(aKey); return result; } public override JSONNode Remove(int aIndex) { if (aIndex < 0 || aIndex >= m_Dict.Count) { return null; } KeyValuePair<string, JSONNode> keyValuePair = m_Dict.ElementAt(aIndex); m_Dict.Remove(keyValuePair.Key); return keyValuePair.Value; } public override JSONNode Remove(JSONNode aNode) { try { KeyValuePair<string, JSONNode> keyValuePair = m_Dict.Where((KeyValuePair<string, JSONNode> k) => k.Value == aNode).First(); m_Dict.Remove(keyValuePair.Key); return aNode; } catch { return null; } } public override void Clear() { m_Dict.Clear(); } public override JSONNode Clone() { JSONObject jSONObject = new JSONObject(); foreach (KeyValuePair<string, JSONNode> item in m_Dict) { jSONObject.Add(item.Key, item.Value.Clone()); } return jSONObject; } public override bool HasKey(string aKey) { return m_Dict.ContainsKey(aKey); } public override JSONNode GetValueOrDefault(string aKey, JSONNode aDefault) { if (m_Dict.TryGetValue(aKey, out var value)) { return value; } return aDefault; } internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) { aSB.Append('{'); bool flag = true; if (inline) { aMode = JSONTextMode.Compact; } foreach (KeyValuePair<string, JSONNode> item in m_Dict) { if (!flag) { aSB.Append(','); } flag = false; if (aMode == JSONTextMode.Indent) { aSB.AppendLine(); } if (aMode == JSONTextMode.Indent) { aSB.Append(' ', aIndent + aIndentInc); } aSB.Append('"').Append(JSONNode.Escape(item.Key)).Append('"'); if (aMode == JSONTextMode.Compact) { aSB.Append(':'); } else { aSB.Append(" : "); } item.Value.WriteToStringBuilder(aSB, aIndent + aIndentInc, aIndentInc, aMode); } if (aMode == JSONTextMode.Indent) { aSB.AppendLine().Append(' ', aIndent); } aSB.Append('}'); } public override void SerializeBinary(BinaryWriter aWriter) { aWriter.Write((byte)2); aWriter.Write(m_Dict.Count); foreach (string key in m_Dict.Keys) { aWriter.Write(key); m_Dict[key].SerializeBinary(aWriter); } } } public class JSONString : JSONNode { private string m_Data; public override JSONNodeType Tag => JSONNodeType.String; public override bool IsString => true; public override string Value { get { return m_Data; } set { m_Data = value; } } public override Enumerator GetEnumerator() { return default(Enumerator); } public JSONString(string aData) { m_Data = aData; } public override JSONNode Clone() { return new JSONString(m_Data); } internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) { aSB.Append('"').Append(JSONNode.Escape(m_Data)).Append('"'); } public override bool Equals(object obj) { if (base.Equals(obj)) { return true; } if (obj is string text) { return m_Data == text; } JSONString jSONString = obj as JSONString; if (jSONString != null) { return m_Data == jSONString.m_Data; } return false; } public override int GetHashCode() { return m_Data.GetHashCode(); } public override void Clear() { m_Data = ""; } public override void SerializeBinary(BinaryWriter aWriter) { aWriter.Write((byte)3); aWriter.Write(m_Data); } } public class JSONNumber : JSONNode { private double m_Data; public override JSONNodeType Tag => JSONNodeType.Number; public override bool IsNumber => true; public override string Value { get { return m_Data.ToString(CultureInfo.InvariantCulture); } set { if (double.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out var result)) { m_Data = result; } } } public override double AsDouble { get { return m_Data; } set { m_Data = value; } } public override long AsLong { get { return (long)m_Data; } set { m_Data = value; } } public override ulong AsULong { get { return (ulong)m_Data; } set { m_Data = value; } } public override Enumerator GetEnumerator() { return default(Enumerator); } public JSONNumber(double aData) { m_Data = aData; } public JSONNumber(string aData) { Value = aData; } public override JSONNode Clone() { return new JSONNumber(m_Data); } internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) { aSB.Append(Value); } private static bool IsNumeric(object value) { if (!(value is int) && !(value is uint) && !(value is float) && !(value is double) && !(value is decimal) && !(value is long) && !(value is ulong) && !(value is short) && !(value is ushort) && !(value is sbyte)) { return value is byte; } return true; } public override bool Equals(object obj) { if (obj == null) { return false; } if (base.Equals(obj)) { return true; } JSONNumber jSONNumber = obj as JSONNumber; if (jSONNumber != null) { return m_Data == jSONNumber.m_Data; } if (IsNumeric(obj)) { return Convert.ToDouble(obj) == m_Data; } return false; } public override int GetHashCode() { return m_Data.GetHashCode(); } public override void Clear() { m_Data = 0.0; } public override void SerializeBinary(BinaryWriter aWriter) { aWriter.Write((byte)4); aWriter.Write(m_Data); } } public class JSONBool : JSONNode { private bool m_Data; public override JSONNodeType Tag => JSONNodeType.Boolean; public override bool IsBoolean => true; public override string Value { get { return m_Data.ToString(); } set { if (bool.TryParse(value, out var result)) { m_Data = result; } } } public override bool AsBool { get { return m_Data; } set { m_Data = value; } } public override Enumerator GetEnumerator() { return default(Enumerator); } public JSONBool(bool aData) { m_Data = aData; } public JSONBool(string aData) { Value = aData; } public override JSONNode Clone() { return new JSONBool(m_Data); } internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) { aSB.Append(m_Data ? "true" : "false"); } public override bool Equals(object obj) { if (obj == null) { return false; } if (obj is bool) { return m_Data == (bool)obj; } return false; } public override int GetHashCode() { return m_Data.GetHashCode(); } public override void Clear() { m_Data = false; } public override void SerializeBinary(BinaryWriter aWriter) { aWriter.Write((byte)6); aWriter.Write(m_Data); } } public class JSONNull : JSONNode { private static JSONNull m_StaticInstance = new JSONNull(); public static bool reuseSameInstance = true; public override JSONNodeType Tag => JSONNodeType.NullValue; public override bool IsNull => true; public override string Value { get { return "null"; } set { } } public override bool AsBool { get { return false; } set { } } public static JSONNull CreateOrGet() { if (reuseSameInstance) { return m_StaticInstance; } return new JSONNull(); } private JSONNull() { } public override Enumerator GetEnumerator() { return default(Enumerator); } public override JSONNode Clone() { return CreateOrGet(); } public override bool Equals(object obj) { if ((object)this == obj) { return true; } return obj is JSONNull; } public override int GetHashCode() { return 0; } internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) { aSB.Append("null"); } public override void SerializeBinary(BinaryWriter aWriter) { aWriter.Write((byte)5); } } internal class JSONLazyCreator : JSONNode { private JSONNode m_Node; private string m_Key; public override JSONNodeType Tag => JSONNodeType.None; public override JSONNode this[int aIndex] { get { return new JSONLazyCreator(this); } set { Set(new JSONArray()).Add(value); } } public override JSONNode this[string aKey] { get { return new JSONLazyCreator(this, aKey); } set { Set(new JSONObject()).Add(aKey, value); } } public override int AsInt { get { Set(new JSONNumber(0.0)); return 0; } set { Set(new JSONNumber(value)); } } public override float AsFloat { get { Set(new JSONNumber(0.0)); return 0f; } set { Set(new JSONNumber(value)); } } public override double AsDouble { get { Set(new JSONNumber(0.0)); return 0.0; } set { Set(new JSONNumber(value)); } } public override long AsLong { get { if (JSONNode.longAsString) { Set(new JSONString("0")); } else { Set(new JSONNumber(0.0)); } return 0L; } set { if (JSONNode.longAsString) { Set(new JSONString(value.ToString())); } else { Set(new JSONNumber(value)); } } } public override ulong AsULong { get { if (JSONNode.longAsString) { Set(new JSONString("0")); } else { Set(new JSONNumber(0.0)); } return 0uL; } set { if (JSONNode.longAsString) { Set(new JSONString(value.ToString())); } else { Set(new JSONNumber(value)); } } } public override bool AsBool { get { Set(new JSONBool(aData: false)); return false; } set { Set(new JSONBool(value)); } } public override JSONArray AsArray => Set(new JSONArray()); public override JSONObject AsObject => Set(new JSONObject()); public override Enumerator GetEnumerator() { return default(Enumerator); } public JSONLazyCreator(JSONNode aNode) { m_Node = aNode; m_Key = null; } public JSONLazyCreator(JSONNode aNode, string aKey) { m_Node = aNode; m_Key = aKey; } private T Set<T>(T aVal) where T : JSONNode { if (m_Key == null) { m_Node.Add(aVal); } else { m_Node.Add(m_Key, aVal); } m_Node = null; return aVal; } public override void Add(JSONNode aItem) { Set(new JSONArray()).Add(aItem); } public override void Add(string aKey, JSONNode aItem) { Set(new JSONObject()).Add(aKey, aItem); } public static bool operator ==(JSONLazyCreator a, object b) { if (b == null) { return true; } return (object)a == b; } public static bool operator !=(JSONLazyCreator a, object b) { return !(a == b); } public override bool Equals(object obj) { if (obj == null) { return true; } return (object)this == obj; } public override int GetHashCode() { return 0; } internal override void WriteToStringBuilder(StringBuilder aSB, int aIndent, int aIndentInc, JSONTextMode aMode) { aSB.Append("null"); } public override void SerializeBinary(BinaryWriter aWriter) { } } public static class JSON { public static JSONNode Parse(string aJSON) { return JSONNode.Parse(aJSON); } } } namespace AuthoritativeConfig { public class Config { private static Config _instance; public Dictionary<string, ConfigBaseEntry> _configEntries; public BaseUnityPlugin _mod; private static ConfigEntry<bool> _serverIsAuthoritative; private static bool _defaultBindAuthority; public static ManualLogSource Logger; public UnityEvent OnConfigReceived = new UnityEvent(); public static Config Instance => _instance ?? (_instance = new Config()); public static ZNet ZNet => ZNet.instance; public static string GUID => Instance._mod.Info.Metadata.GUID; public static string RPC_SYNC_GUID => "AuthoritativeConfig_" + GUID; public static int RPC_SYNC_HASH => StringExtensionMethods.GetStableHashCode(RPC_SYNC_GUID); public void Init(BaseUnityPlugin mod, bool defaultBindServerAuthority = false) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Expected O, but got Unknown _mod = mod; Logger = new ManualLogSource(RPC_SYNC_GUID); Logger.Sources.Add((ILogSource)(object)Logger); _configEntries = new Dictionary<string, ConfigBaseEntry>(); _defaultBindAuthority = defaultBindServerAuthority; _serverIsAuthoritative = _mod.Config.Bind<bool>("ServerAuthoritativeConfig", "ServerIsAuthoritative", true, "<Server Only> Forces Clients to use Server defined configs."); Harmony.CreateAndPatchAll(typeof(Config), (string)null); Logger.LogInfo((object)"Initialized Server Authoritative Config Manager."); } [HarmonyPatch(typeof(Game), "Start")] [HarmonyPostfix] protected static void RegisterSyncConfigRPC() { if (!ZRoutedRpc.instance.m_functions.ContainsKey(RPC_SYNC_HASH)) { Logger.LogInfo((object)("Authoritative Config Registered -> " + RPC_SYNC_GUID)); ZRoutedRpc.instance.Register<ZPackage>(RPC_SYNC_GUID, (Action<long, ZPackage>)RPC_SyncServerConfig); } foreach (ConfigBaseEntry value in Instance._configEntries.Values) { value.ClearServerValue(); } } [HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")] [HarmonyPostfix] protected static void RequestConfigFromServer() { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Invalid comparison between Unknown and I4 //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Expected O, but got Unknown if (!ZNet.IsServer() && (int)ZNet.GetConnectionStatus() == 2) { long? num = AccessTools.Method(typeof(ZRoutedRpc), "GetServerPeerID", (Type[])null, (Type[])null).Invoke(ZRoutedRpc.instance, null) as long?; ZRoutedRpc.instance.InvokeRoutedRPC(num.Value, RPC_SYNC_GUID, new object[1] { (object)new ZPackage() }); Logger.LogInfo((object)("Authoritative Config Registered -> " + RPC_SYNC_GUID)); Debug.Log((object)(Instance._mod.Info.Metadata.Name + ": Authoritative Config Requested -> " + RPC_SYNC_GUID)); } else if (!ZNet.IsServer()) { Logger.LogWarning((object)"Failed to Request Configs. Bad Peer? Too Early?"); } } public ConfigEntry<T> Bind<T>(string section, string key, T defaultValue, ConfigDescription configDescription = null, bool? serverAuthoritative = null) { ConfigEntry<T> configEntry = new ConfigEntry<T>(_mod.Config.Bind<T>(section, key, defaultValue, configDescription), serverAuthoritative.HasValue ? serverAuthoritative.Value : _defaultBindAuthority); _configEntries[((object)configEntry.BaseEntry.Definition).ToString()] = configEntry; return configEntry; } public ConfigEntry<T> Bind<T>(ConfigDefinition configDefinition, T defaultValue, ConfigDescription configDescription = null, bool? serverAuthoritative = null) { ConfigEntry<T> configEntry = new ConfigEntry<T>(_mod.Config.Bind<T>(configDefinition, defaultValue, configDescription), serverAuthoritative.HasValue ? serverAuthoritative.Value : _defaultBindAuthority); _configEntries[((object)configEntry.BaseEntry.Definition).ToString()] = configEntry; return configEntry; } public ConfigEntry<T> Bind<T>(string section, string key, T defaultValue, string description, bool? serverAuthoritative = null) { ConfigEntry<T> configEntry = new ConfigEntry<T>(_mod.Config.Bind<T>(section, key, defaultValue, description), serverAuthoritative.HasValue ? serverAuthoritative.Value : _defaultBindAuthority); _configEntries[((object)configEntry.BaseEntry.Definition).ToString()] = configEntry; return configEntry; } public static void SendConfigToClient(long sender) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Expected O, but got Unknown if (!ZNet.IsServer()) { return; } ZPackage val = new ZPackage(); int num = 0; foreach (KeyValuePair<string, ConfigBaseEntry> configEntry in Instance._configEntries) { if (configEntry.Value.ServerAuthoritative) { val.Write(configEntry.Key); val.Write(configEntry.Value.BaseEntry.GetSerializedValue()); num++; Logger.LogInfo((object)("Sending Config " + configEntry.Key + ": " + configEntry.Value.BaseEntry.GetSerializedValue())); } } ZRoutedRpc.instance.InvokeRoutedRPC(sender, RPC_SYNC_GUID, new object[1] { val }); Logger.LogInfo((object)$"Sent {num} config pairs to client {sender}"); } public static void ReadConfigPkg(ZPackage pkg) { if (ZNet.IsServer()) { return; } int num = 0; while (pkg.GetPos() != pkg.Size()) { string text = pkg.ReadString(); string text2 = pkg.ReadString(); num++; if (Instance._configEntries.ContainsKey(text)) { Instance._configEntries[text].SetSerializedValue(text2); Logger.LogInfo((object)("Applied Server Authoritative config pair => " + text + ": " + text2)); } else { Logger.LogError((object)("Recieved config key we dont have locally. Possible Version Mismatch. " + text + ": " + text2)); } } Logger.LogInfo((object)$"Applied {num} config pairs"); UnityEvent onConfigReceived = Instance.OnConfigReceived; if (onConfigReceived != null) { onConfigReceived.Invoke(); } } public static void RPC_SyncServerConfig(long sender, ZPackage pkg) { if (ZNet.IsServer() && _serverIsAuthoritative.Value) { SendConfigToClient(sender); } else if (!ZNet.IsServer() && pkg != null && pkg.Size() > 0 && AccessTools.Method(typeof(ZRoutedRpc), "GetServerPeerID", (Type[])null, (Type[])null).Invoke(ZRoutedRpc.instance, null) as long? == sender) { ReadConfigPkg(pkg); } } } public class ConfigBaseEntry { protected object _serverValue; public ConfigEntryBase BaseEntry; public bool ServerAuthoritative; protected bool _didError; internal ConfigBaseEntry(ConfigEntryBase configEntry, bool serverAuthoritative) { BaseEntry = configEntry; ServerAuthoritative = serverAuthoritative; } public void SetSerializedValue(string value) { try { _serverValue = TomlTypeConverter.ConvertToValue(value, BaseEntry.SettingType); _didError = false; } catch (Exception ex) { Config.Logger.LogWarning((object)$"Config value of setting \"{BaseEntry.Definition}\" could not be parsed and will be ignored. Reason: {ex.Message}; Value: {value}"); } } public void ClearServerValue() { _serverValue = null; _didError = false; } } public class ConfigEntry<T> : ConfigBaseEntry { private readonly ConfigEntry<T> _configEntry; public T ServerValue => (T)_serverValue; public T Value { get { if (ServerAuthoritative && (Object)(object)Config.ZNet != (Object)null && !Config.ZNet.IsServer()) { if (_serverValue != null) { return ServerValue; } if (!_didError) { Config.Logger.LogWarning((object)$"No Recieved value for Server Authoritative Config. {BaseEntry.Definition}. Falling Back to Client Config."); _didError = true; } return _configEntry.Value; } return _configEntry.Value; } set { _configEntry.Value = value; } } internal ConfigEntry(ConfigEntry<T> configEntry, bool serverAuthoritative) : base((ConfigEntryBase)(object)configEntry, serverAuthoritative) { _configEntry = configEntry; } } }