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 Quads Craft From Chests v1.5.2
plugins/CraftFromChests.dll
Decompiled 13 hours agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using DHack.Shared; using HarmonyLib; using Microsoft.CodeAnalysis; using TMPro; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyMetadata("AI_Assisted_Creation", "This assembly was partially or fully created with the assistance of Generative AI (Claude Code).")] [assembly: AssemblyMetadata("AI_Model_Vendor", "Anthropic")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyVersion("0.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace CraftFromChests { internal class ChestOverlay : MonoBehaviour { private class ModButton { public GameObject Root; public TMP_Text[] Tmp; public Text[] Legacy; public Func<string> Label; public void Refresh() { string text = Label(); TMP_Text[] tmp = Tmp; foreach (TMP_Text val in tmp) { if ((Object)(object)val != (Object)null && val.text != text) { val.text = text; } } Text[] legacy = Legacy; foreach (Text val2 in legacy) { if ((Object)(object)val2 != (Object)null && val2.text != text) { val2.text = text; } } } } [CompilerGenerated] private static class <>O { public static UnityAction <0>__ToggleLines; public static UnityAction <1>__CycleRange; } private static readonly float[] RangeSteps = new float[5] { 5f, 10f, 15f, 20f, 30f }; private readonly List<LineRenderer> _lines = new List<LineRenderer>(); private Material _lineMaterial; private ModButton _linesButton; private ModButton _rangeButton; private bool _buttonsFailed; private float[] _layoutKey; private void Update() { //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) if (InventoryGui.IsVisible()) { EnsureButtons(); } ChestScanner.RefreshWhenSettled(); Player localPlayer = Player.m_localPlayer; CraftingStation val = (((Object)(object)localPlayer != (Object)null) ? localPlayer.GetCurrentCraftingStation() : null); bool flag = (Object)(object)localPlayer != (Object)null && InventoryGui.IsVisible() && Plugin.Enabled.Value && ((Object)(object)val != (Object)null || ChestScanner.HandCrafting()) && OtherChestMods.Which == null; if (Alive(_linesButton)) { _linesButton.Root.SetActive(flag); } if (Alive(_rangeButton)) { _rangeButton.Root.SetActive(flag); } Vector3? origin = ((!flag || !Plugin.ShowLines.Value) ? ((Vector3?)null) : new Vector3?(((Object)(object)val != (Object)null) ? ((Component)val).transform.position : ((Component)localPlayer).transform.position)); DrawLines(origin); } private void EnsureButtons() { //IL_0129: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Expected O, but got Unknown //IL_0139: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_0155: Unknown result type (might be due to invalid IL or missing references) //IL_0163: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Expected O, but got Unknown //IL_01ce: Unknown result type (might be due to invalid IL or missing references) //IL_01d9: Unknown result type (might be due to invalid IL or missing references) //IL_01e4: Unknown result type (might be due to invalid IL or missing references) //IL_01f1: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Expected O, but got Unknown InventoryGui instance = InventoryGui.instance; if (_buttonsFailed || (Object)(object)instance == (Object)null || (Object)(object)instance.m_tabUpgrade == (Object)null) { return; } if (!Alive(_linesButton) || !Alive(_rangeButton)) { DestroyButtons(); _layoutKey = null; try { object obj = <>O.<0>__ToggleLines; if (obj == null) { UnityAction val = ToggleLines; <>O.<0>__ToggleLines = val; obj = (object)val; } _linesButton = CreateButton(instance, "CFC_LineToggle", (UnityAction)obj, () => Translations.T("craftfromchests_lines", Localization.instance.Localize(Plugin.ShowLines.Value ? "$hud_on" : "$hud_off").ToUpperInvariant())); object obj2 = <>O.<1>__CycleRange; if (obj2 == null) { UnityAction val2 = CycleRange; <>O.<1>__CycleRange = val2; obj2 = (object)val2; } _rangeButton = CreateButton(instance, "CFC_RangeToggle", (UnityAction)obj2, () => Translations.T("craftfromchests_range", Plugin.Radius.Value.ToString("0.#"))); } catch (Exception ex) { Plugin.Log.LogError((object)("Could not create the mod buttons: " + ex)); DestroyButtons(); _buttonsFailed = true; return; } } RectTransform val3 = (RectTransform)((Component)instance.m_tabUpgrade).transform; float[] array = new float[10] { val3.anchoredPosition.x, val3.anchoredPosition.y, val3.sizeDelta.x, val3.sizeDelta.y, ((Transform)val3).localScale.x, Plugin.ToggleWidth.Value, Plugin.RangeButtonWidth.Value, Plugin.ToggleOffsetX.Value, Plugin.ToggleOffsetY.Value, Plugin.ToggleScale.Value }; if (!SameLayout(array, _layoutKey)) { float leftEdge = val3.anchoredPosition.x - val3.pivot.x * val3.sizeDelta.x + val3.sizeDelta.x; leftEdge = Place(_linesButton, val3, leftEdge, Plugin.ToggleWidth.Value); Place(_rangeButton, val3, leftEdge, Plugin.RangeButtonWidth.Value); _layoutKey = array; } _linesButton.Refresh(); _rangeButton.Refresh(); } private static bool SameLayout(float[] a, float[] b) { if (b == null || a.Length != b.Length) { return false; } for (int i = 0; i < a.Length; i++) { if (a[i] != b[i]) { return false; } } return true; } private ModButton CreateButton(InventoryGui gui, string name, UnityAction onClick, Func<string> label) { Transform parent = ((Component)gui.m_tabUpgrade).transform.parent; Transform val = parent.Find(name); if ((Object)(object)val != (Object)null) { Object.Destroy((Object)(object)((Component)val).gameObject); } GameObject val2 = Object.Instantiate<GameObject>(((Component)gui.m_tabUpgrade).gameObject, parent); ((Object)val2).name = name; Image component = val2.GetComponent<Image>(); MonoBehaviour[] components = val2.GetComponents<MonoBehaviour>(); foreach (MonoBehaviour val3 in components) { if (val3 is Button || ((object)val3).GetType().Name == "ButtonTextColor") { Object.DestroyImmediate((Object)(object)val3); } } Button val4 = val2.AddComponent<Button>(); ((Selectable)val4).targetGraphic = (Graphic)(object)component; ((UnityEvent)val4.onClick).AddListener(onClick); TMP_Text[] componentsInChildren = val2.GetComponentsInChildren<TMP_Text>(true); TMP_Text[] array = componentsInChildren; foreach (TMP_Text val5 in array) { val5.fontSizeMax = val5.fontSize; val5.fontSizeMin = val5.fontSize * 0.6f; val5.enableAutoSizing = true; val5.textWrappingMode = (TextWrappingModes)0; } return new ModButton { Root = val2, Tmp = componentsInChildren, Legacy = val2.GetComponentsInChildren<Text>(true), Label = label }; } private static float Place(ModButton b, RectTransform tab, float leftEdge, float widthMultiple) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0037: 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) //IL_0052: 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_0069: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) RectTransform val = (RectTransform)b.Root.transform; val.anchorMin = tab.anchorMin; val.anchorMax = tab.anchorMax; val.pivot = tab.pivot; ((Transform)val).localScale = ((Transform)tab).localScale * Plugin.ToggleScale.Value; val.sizeDelta = new Vector2(tab.sizeDelta.x * widthMultiple, tab.sizeDelta.y); float num = leftEdge + 8f; val.anchoredPosition = new Vector2(num + val.pivot.x * val.sizeDelta.x + Plugin.ToggleOffsetX.Value, tab.anchoredPosition.y + Plugin.ToggleOffsetY.Value); return num + val.sizeDelta.x; } private static void ToggleLines() { Plugin.ShowLines.Value = !Plugin.ShowLines.Value; } private static void CycleRange() { if (Plugin.Synced != null && Plugin.Synced.FromServer) { Player localPlayer = Player.m_localPlayer; if (localPlayer != null) { ((Character)localPlayer).Message((MessageType)1, Translations.T("craftfromchests_server_range"), 0, (Sprite)null, false); } return; } float value = Plugin.Radius.Value; float[] rangeSteps = RangeSteps; foreach (float num in rangeSteps) { if (num > value + 0.01f) { Plugin.Radius.Value = num; return; } } Plugin.Radius.Value = RangeSteps[0]; } private static bool Alive(ModButton b) { if (b != null) { return (Object)(object)b.Root != (Object)null; } return false; } private void DestroyButtons() { if (Alive(_linesButton)) { Object.Destroy((Object)(object)_linesButton.Root); } if (Alive(_rangeButton)) { Object.Destroy((Object)(object)_rangeButton.Root); } _linesButton = (_rangeButton = null); } private void OnDestroy() { DestroyButtons(); if ((Object)(object)_lineMaterial != (Object)null) { Object.Destroy((Object)(object)_lineMaterial); } } private void DrawLines(Vector3? origin) { //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) List<Container> list = (origin.HasValue ? ChestScanner.GetNearby() : null); int num = list?.Count ?? 0; while (_lines.Count < num) { _lines.Add(CreateLine()); } float num2 = 0.65f + 0.35f * Mathf.Sin(Time.time * 3f); Color val = default(Color); ((Color)(ref val))..ctor(0.3f, 0.9f, 1f, num2); for (int i = 0; i < _lines.Count; i++) { LineRenderer val2 = _lines[i]; ((Component)val2).gameObject.SetActive(i < num); if (i < num) { Color startColor = (val2.endColor = val); val2.startColor = startColor; val2.SetPosition(0, origin.Value + Vector3.up * 1f); val2.SetPosition(1, ((Component)list[i]).transform.position + Vector3.up * 0.6f); } } } private LineRenderer CreateLine() { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Expected O, but got Unknown //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Expected O, but got Unknown if ((Object)(object)_lineMaterial == (Object)null) { Shader val = Shader.Find("Sprites/Default") ?? Shader.Find("UI/Default") ?? Shader.Find("Hidden/Internal-Colored"); _lineMaterial = new Material(val); } GameObject val2 = new GameObject("CFC_Line"); val2.transform.SetParent(((Component)this).transform, false); LineRenderer val3 = val2.AddComponent<LineRenderer>(); ((Renderer)val3).material = _lineMaterial; val3.positionCount = 2; val3.useWorldSpace = true; float startWidth = (val3.endWidth = 0.05f); val3.startWidth = startWidth; val3.numCapVertices = 4; return val3; } } internal static class ChestScanner { private class Claim { public float At; public float RetryAt = -1f; } internal struct Need { public string Name; public int Amount; public int Quality; } private static readonly List<Container> AllContainers = new List<Container>(); private static readonly List<Container> Nearby = new List<Container>(); private static readonly Func<Container, long, bool> CheckAccess = AccessTools.MethodDelegate<Func<Container, long, bool>>(AccessTools.Method(typeof(Container), "CheckAccess", (Type[])null, (Type[])null), (object)null, true); private static readonly FieldInfo NView = AccessTools.Field(typeof(Container), "m_nview"); private static readonly MethodInfo Load = AccessTools.Method(typeof(Container), "Load", (Type[])null, (Type[])null); internal static bool Suspend; internal static bool Consuming; private static int _contextFrame = -1; private static bool _contextActive; private static float _nearbyAt = -10f; private static Vector3 _nearbyOrigin; private const float Settle = 1f; private const float Retry = 5f; private const float Forget = 10f; private static readonly Dictionary<(long, uint), Claim> Claims = new Dictionary<(long, uint), Claim>(); private static float _prunedAt; private static float _refreshAt = -1f; private static readonly MethodInfo UpdateCraftingPanel = AccessTools.Method(typeof(InventoryGui), "UpdateCraftingPanel", (Type[])null, (Type[])null); private static int _countFrame = -1; private static readonly Dictionary<string, Dictionary<int, int>> Counts = new Dictionary<string, Dictionary<int, int>>(); internal static void Register(Container c) { if ((Object)(object)c != (Object)null && !((Object)c).name.StartsWith("piece_recycler") && !AllContainers.Contains(c)) { AllContainers.Add(c); } } private static bool CraftingNow() { if (_contextFrame == Time.frameCount) { return _contextActive; } _contextFrame = Time.frameCount; Player localPlayer = Player.m_localPlayer; _contextActive = (Object)(object)localPlayer != (Object)null && ((Object)(object)localPlayer.GetCurrentCraftingStation() != (Object)null || HandCrafting()); return _contextActive; } internal static bool Applies(Inventory inv) { if (Suspend || !Plugin.Enabled.Value) { return false; } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || inv != ((Humanoid)localPlayer).GetInventory()) { return false; } if (CraftingNow()) { return OtherChestMods.Which == null; } return false; } internal static bool HandCrafting() { InventoryGui instance = InventoryGui.instance; if ((Object)(object)instance != (Object)null && InventoryGui.IsVisible()) { return instance.InCraftTab(); } return false; } internal static List<Container> GetNearby() { //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_003c: 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_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_017f: Unknown result type (might be due to invalid IL or missing references) Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null || !CraftingNow()) { Nearby.Clear(); return Nearby; } CraftingStation currentCraftingStation = localPlayer.GetCurrentCraftingStation(); Vector3 val = (((Object)(object)currentCraftingStation != (Object)null) ? ((Component)currentCraftingStation).transform.position : ((Component)localPlayer).transform.position); Vector3 val2; if (Time.unscaledTime - _nearbyAt < 0.25f) { val2 = val - _nearbyOrigin; if (((Vector3)(ref val2)).sqrMagnitude < 0.04f) { Nearby.RemoveAll((Container c) => (Object)(object)c == (Object)null); return Nearby; } } _nearbyAt = Time.unscaledTime; _nearbyOrigin = val; InvalidateCounts(); Nearby.Clear(); float num = Plugin.Radius.Value * Plugin.Radius.Value; long playerID = Game.instance.GetPlayerProfile().GetPlayerID(); AllContainers.RemoveAll((Container c) => (Object)(object)c == (Object)null); foreach (Container allContainer in AllContainers) { val2 = ((Component)allContainer).transform.position - val; if (!(((Vector3)(ref val2)).sqrMagnitude > num) && allContainer.GetInventory() != null && IsStorage(allContainer) && !InUse(allContainer) && (!allContainer.m_checkGuardStone || PrivateArea.CheckAccess(((Component)allContainer).transform.position, 0f, false, false)) && CheckAccess(allContainer, playerID)) { Nearby.Add(allContainer); } } return Nearby; } private static bool IsStorage(Container c) { if ((Object)(object)((Component)c).GetComponentInParent<Piece>() == (Object)null || (Object)(object)((Component)c).GetComponent<TombStone>() != (Object)null) { return false; } if ((Object)(object)((Component)c).GetComponentInParent<Character>() != (Object)null || (Object)(object)((Component)c).GetComponentInParent<Vagon>() != (Object)null || (Object)(object)((Component)c).GetComponentInParent<Ship>() != (Object)null) { return false; } if (SpecialContainers.NotStorage(c)) { return false; } object? value = NView.GetValue(c); ZNetView val = (ZNetView)((value is ZNetView) ? value : null); if ((Object)(object)val != (Object)null && val.IsValid()) { return val.GetZDO().GetLong("dhc_home", 0L) == 0; } return false; } private static bool InUse(Container c) { if (c.IsInUse()) { return true; } object? value = NView.GetValue(c); ZNetView val = (ZNetView)((value is ZNetView) ? value : null); if ((Object)(object)val != (Object)null && val.IsValid() && !val.IsOwner()) { return val.GetZDO().GetInt(ZDOVars.s_inUse, 0) == 1; } return false; } private static (long, uint) Id(ZDOID uid) { return (((ZDOID)(ref uid)).UserID, ((ZDOID)(ref uid)).ID); } private static Claim ClaimOn((long, uint) id, float now) { if (!Claims.TryGetValue(id, out var value)) { return null; } if ((value.RetryAt >= 0f) ? (now < value.RetryAt) : (now - value.At < 10f)) { return value; } Claims.Remove(id); return null; } private static bool Settled(Container c) { //IL_0030: Unknown result type (might be due to invalid IL or missing references) object? value = NView.GetValue(c); ZNetView val = (ZNetView)((value is ZNetView) ? value : null); if ((Object)(object)val == (Object)null || !val.IsValid()) { return false; } float unscaledTime = Time.unscaledTime; Claim claim = ClaimOn(Id(val.GetZDO().m_uid), unscaledTime); if (!val.IsOwner()) { if (claim != null && claim.RetryAt < 0f) { claim.RetryAt = unscaledTime + 5f; } return false; } if (claim != null) { return unscaledTime - claim.At >= 1f; } return true; } private static bool Ready(Container c) { if (!Settled(c)) { return false; } Load?.Invoke(c, null); return true; } private static void Request(Container c) { //IL_0096: Unknown result type (might be due to invalid IL or missing references) object? value = NView.GetValue(c); ZNetView val = (ZNetView)((value is ZNetView) ? value : null); if ((Object)(object)val == (Object)null || !val.IsValid() || val.IsOwner() || InUse(c)) { return; } float unscaledTime = Time.unscaledTime; if (unscaledTime - _prunedAt > 30f) { _prunedAt = unscaledTime; foreach (var item in new List<(long, uint)>(Claims.Keys)) { ClaimOn(item, unscaledTime); } } (long, uint) tuple = Id(val.GetZDO().m_uid); Claim claim = ClaimOn(tuple, unscaledTime); if (claim != null) { if (claim.RetryAt < 0f) { claim.RetryAt = unscaledTime + 5f; } } else { val.ClaimOwnership(); Claims[tuple] = new Claim { At = unscaledTime }; _refreshAt = unscaledTime + 1f + 0.1f; } } internal static void RefreshWhenSettled() { if (_refreshAt < 0f || Time.unscaledTime < _refreshAt) { return; } _refreshAt = -1f; if (UpdateCraftingPanel == null || (Object)(object)InventoryGui.instance == (Object)null || !InventoryGui.IsVisible() || !CraftingNow()) { return; } try { UpdateCraftingPanel.Invoke(InventoryGui.instance, new object[1] { false }); } catch (Exception ex) { Plugin.Log.LogWarning((object)("Could not refresh the recipe list: " + ex.Message)); } } private static void InvalidateCounts() { _countFrame = -1; } internal static int CountInChests(string name, int quality, bool matchWorldLevel) { if (_countFrame != Time.frameCount) { Counts.Clear(); _countFrame = Time.frameCount; } if (!Counts.TryGetValue(name, out var value)) { value = (Counts[name] = new Dictionary<int, int>()); } int key = quality * 2 + (matchWorldLevel ? 1 : 0); if (value.TryGetValue(key, out var value2)) { return value2; } int num = 0; foreach (Container item in GetNearby()) { int num2 = item.GetInventory().CountItems(name, quality, matchWorldLevel); if (num2 > 0 && !InUse(item)) { if (Settled(item)) { num += num2; } else { Request(item); } } } value[key] = num; return num; } internal static int OwnCount(Inventory inv, string name, int quality, bool matchWorldLevel) { int num = 0; foreach (ItemData allItem in inv.GetAllItems()) { if (allItem.m_shared.m_name == name && (quality < 0 || allItem.m_quality == quality) && (!matchWorldLevel || allItem.m_worldLevel >= Game.m_worldLevel)) { num += allItem.m_stack; } } return num; } internal static bool Charged(Requirement req, CraftingStation station) { if ((Object)(object)req.m_resItem == (Object)null) { return false; } if (!((Object)(object)station != (Object)null)) { return !req.m_upgraderResource; } return station.m_upgrader == req.m_upgraderResource; } internal static string FindShortfall(Inventory inv, List<Need> needs) { foreach (Need need in needs) { Suspend = true; int num; try { num = inv.CountItems(need.Name, need.Quality, true); } finally { Suspend = false; } int num2 = need.Amount - num; if (num2 <= 0) { continue; } int num3 = 0; foreach (Container item in GetNearby()) { if (num3 >= num2) { break; } if ((Object)(object)item == (Object)null || InUse(item)) { continue; } Inventory inventory = item.GetInventory(); if (inventory.CountItems(need.Name, need.Quality, true) > 0) { if (!Ready(item)) { Request(item); } else { num3 += inventory.CountItems(need.Name, need.Quality, true); } } } InvalidateCounts(); if (num3 < num2) { return need.Name; } } return null; } internal static ItemData FindInChests(string name, int quality) { foreach (Container item in GetNearby()) { ItemData val = (((Object)(object)item != (Object)null && Settled(item)) ? item.GetInventory().GetItem(name, quality, false) : null); if (val != null) { return val; } } return null; } internal static int RemoveFromChests(string name, int amount, int quality, bool matchWorldLevel) { foreach (Container item in GetNearby()) { if (amount <= 0) { break; } if ((Object)(object)item == (Object)null || InUse(item)) { continue; } Inventory inventory = item.GetInventory(); if (inventory.CountItems(name, quality, matchWorldLevel) <= 0) { continue; } if (!Ready(item)) { Request(item); continue; } int num = Math.Min(inventory.CountItems(name, quality, matchWorldLevel), amount); if (num > 0) { inventory.RemoveItem(name, num, quality, matchWorldLevel); amount -= num; } } InvalidateCounts(); return amount; } } internal static class HaveLabel { private const string LabelName = "CFC_Have"; internal static void Show(Transform elementRoot, Requirement req, int quality, int craftMultiplier) { //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) Transform val = elementRoot.Find("res_amount"); if ((Object)(object)val == (Object)null) { return; } TMP_Text orCreate = GetOrCreate(elementRoot, val); if (!((Object)(object)orCreate == (Object)null)) { string name = req.m_resItem.m_itemData.m_shared.m_name; Inventory inventory = ((Humanoid)Player.m_localPlayer).GetInventory(); ChestScanner.Suspend = true; int num; try { num = inventory.CountItems(name, -1, true); } finally { ChestScanner.Suspend = false; } int num2 = ChestScanner.CountInChests(name, -1, matchWorldLevel: true); int num3 = num + num2; int num4 = req.GetAmount(quality) * craftMultiplier; ((Component)orCreate).gameObject.SetActive(true); orCreate.text = num3.ToString(); ((Graphic)orCreate).color = ((num3 >= num4) ? new Color(0.55f, 1f, 0.6f) : new Color(1f, 0.62f, 0.45f)); UITooltip component = ((Component)elementRoot).GetComponent<UITooltip>(); if ((Object)(object)component != (Object)null && !string.IsNullOrEmpty(component.m_text)) { component.m_text = component.m_text + "\n" + Translations.T("craftfromchests_have", num3, num, num2); } } } internal static void Hide(Transform elementRoot) { Transform val = (((Object)(object)elementRoot != (Object)null) ? elementRoot.Find("CFC_Have") : null); if ((Object)(object)val != (Object)null) { ((Component)val).gameObject.SetActive(false); } } private static TMP_Text GetOrCreate(Transform elementRoot, Transform amount) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Expected O, but got Unknown //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Expected O, but got Unknown //IL_004e: 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_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_0081: 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_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00e0: Unknown result type (might be due to invalid IL or missing references) Transform val = elementRoot.Find("CFC_Have"); if ((Object)(object)val != (Object)null) { return ((Component)val).GetComponent<TMP_Text>(); } GameObject val2 = Object.Instantiate<GameObject>(((Component)amount).gameObject, elementRoot); ((Object)val2).name = "CFC_Have"; RectTransform val3 = (RectTransform)amount; RectTransform val4 = (RectTransform)val2.transform; val4.anchorMin = new Vector2(1f - val3.anchorMax.x, val3.anchorMin.y); val4.anchorMax = new Vector2(1f - val3.anchorMin.x, val3.anchorMax.y); val4.pivot = new Vector2(1f - val3.pivot.x, val3.pivot.y); val4.anchoredPosition = new Vector2(0f - val3.anchoredPosition.x, val3.anchoredPosition.y); val4.sizeDelta = val3.sizeDelta; TMP_Text component = val2.GetComponent<TMP_Text>(); component.horizontalAlignment = (HorizontalAlignmentOptions)1; component.enableAutoSizing = false; component.fontSize = ((Component)amount).GetComponent<TMP_Text>().fontSize * 0.9f; return component; } internal static void DestroyAll() { if ((Object)(object)InventoryGui.instance == (Object)null) { return; } TMP_Text[] componentsInChildren = ((Component)InventoryGui.instance).GetComponentsInChildren<TMP_Text>(true); foreach (TMP_Text val in componentsInChildren) { if ((Object)(object)val != (Object)null && ((Object)((Component)val).gameObject).name == "CFC_Have") { Object.Destroy((Object)(object)((Component)val).gameObject); } } } } [HarmonyPatch(typeof(InventoryGui), "SetupRequirement")] internal static class InventoryGui_SetupRequirement { private static void Postfix(Transform elementRoot, Requirement req, Player player, bool craft, int quality, int craftMultiplier, bool __result) { try { bool flag = (Object)(object)InventoryGui.instance != (Object)null && elementRoot.IsChildOf(((Component)InventoryGui.instance).transform); if (flag && __result && Plugin.Enabled.Value && Plugin.ShowHaveCounts.Value && (Object)(object)req.m_resItem != (Object)null && (Object)(object)player != (Object)null && (Object)(object)player == (Object)(object)Player.m_localPlayer && ChestScanner.Applies(((Humanoid)player).GetInventory())) { HaveLabel.Show(elementRoot, req, quality, craftMultiplier); } else { HaveLabel.Hide(elementRoot); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Could not show the 'you have' count: " + ex.Message)); } } } [HarmonyPatch(typeof(InventoryGui), "HideRequirement")] internal static class InventoryGui_HideRequirement { private static void Postfix(Transform elementRoot) { HaveLabel.Hide(elementRoot); } } internal static class OtherChestMods { private static readonly string[][] Always = new string[7][] { new string[2] { "Azumatt.AzuCraftyBoxes", "AzuCraftyBoxes" }, new string[2] { "aedenthorn.CraftFromContainers", "CraftFromContainers" }, new string[2] { "toxo.craftfromchests", "Craft From Chests (Toxo)" }, new string[2] { "poneis.valheim.CraftFromChestsPlus", "CraftFromChestsPlus" }, new string[2] { "com.teflonted.valheim.craftfromchests", "Teflon Ted's Craft From Chests" }, new string[2] { "com.morda.storeandcraft", "StoreAndCraft" }, new string[2] { "com.zellds.smartcraftstorage", "SmartCraft-Storage" } }; private const string ValheimPlus = "org.bepinex.plugins.valheim_plus"; private static bool _looked; private static string _always; private static PropertyInfo _vpCurrent; private static PropertyInfo _vpSection; private static PropertyInfo _vpEnabled; private static float _vpReadAt = -10f; private static bool _vpOn; private static string _said; internal static string Which { get { if (!_looked) { Look(); } string text = _always ?? (ValheimPlusOn() ? "ValheimPlus (its CraftFromChest section is on)" : null); if (text != _said) { _said = text; if (text != null) { Plugin.Log.LogWarning((object)(text + " also lets you craft from chests; CraftFromChests stands down so chests aren't counted twice")); } else { Plugin.Log.LogInfo((object)"CraftFromChests crafts from chests again (no other chest-crafting mod is doing it)"); } } return text; } } private static void Look() { _looked = true; string[][] always = Always; foreach (string[] array in always) { if (_always == null && Chainloader.PluginInfos.ContainsKey(array[0])) { _always = array[1]; } } if (!Chainloader.PluginInfos.TryGetValue("org.bepinex.plugins.valheim_plus", out var value) || !((Object)(object)value.Instance != (Object)null)) { return; } try { Type type = ((object)value.Instance).GetType().Assembly.GetType("ValheimPlus.Configurations.Configuration"); _vpCurrent = type?.GetProperty("Current", BindingFlags.Static | BindingFlags.Public); _vpSection = type?.GetProperty("CraftFromChest", BindingFlags.Instance | BindingFlags.Public); _vpEnabled = _vpSection?.PropertyType.GetProperty("IsEnabled", BindingFlags.Instance | BindingFlags.Public); if (_vpEnabled == null) { Plugin.Log.LogWarning((object)"ValheimPlus is installed but its CraftFromChest setting can't be read; if you turn it on, turn this mod off"); } } catch (Exception ex) { Plugin.Log.LogWarning((object)("Could not read ValheimPlus' settings: " + ex.Message)); } } private static bool ValheimPlusOn() { if (_vpEnabled == null) { return false; } if (Time.unscaledTime - _vpReadAt < 3f) { return _vpOn; } _vpReadAt = Time.unscaledTime; try { object value = _vpCurrent.GetValue(null, null); object obj = ((value != null) ? _vpSection.GetValue(value, null) : null); _vpOn = obj != null && (bool)_vpEnabled.GetValue(obj, null); } catch (Exception) { _vpOn = false; } return _vpOn; } } [HarmonyPatch(typeof(Player), "OnSpawned")] internal static class Player_OnSpawned { private static void Postfix(Player __instance) { if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer) { Plugin.Announce(reloaded: false); } } } [HarmonyPatch(typeof(Container), "Awake")] internal static class Container_Awake { private static void Postfix(Container __instance) { ChestScanner.Register(__instance); } } [HarmonyPatch(typeof(Inventory), "CountItems", new Type[] { typeof(string), typeof(int), typeof(bool) })] internal static class Inventory_CountItems { private static void Postfix(Inventory __instance, string name, int quality, bool matchWorldLevel, ref int __result) { if (name != null && ChestScanner.Applies(__instance)) { __result += ChestScanner.CountInChests(name, quality, matchWorldLevel); } } } [HarmonyPatch(typeof(Inventory), "HaveItem", new Type[] { typeof(string), typeof(bool) })] internal static class Inventory_HaveItem { private static void Postfix(Inventory __instance, string name, bool matchWorldLevel, ref bool __result) { if (!__result && ChestScanner.Applies(__instance)) { __result = ChestScanner.CountInChests(name, -1, matchWorldLevel) > 0; } } } [HarmonyPatch(typeof(Player), "ConsumeResources")] internal static class Player_ConsumeResources { private static void Prefix(out bool __state) { __state = ChestScanner.Consuming; ChestScanner.Consuming = true; } private static void Finalizer(bool __state) { ChestScanner.Consuming = __state; } } [HarmonyPatch(typeof(InventoryGui), "DoCrafting")] internal static class InventoryGui_DoCrafting { [HarmonyPriority(0)] private static bool Prefix(Player player, Recipe ___m_craftRecipe, ItemData ___m_craftUpgradeItem, bool ___m_multiCrafting, int ___m_multiCraftAmount, bool __runOriginal, out bool __state) { __state = ChestScanner.Consuming; if (!__runOriginal) { return false; } if ((Object)(object)player == (Object)null || (Object)(object)___m_craftRecipe == (Object)null || !ChestScanner.Applies(((Humanoid)player).GetInventory())) { return true; } if (player.NoCostCheat() || ZoneSystem.instance.GetGlobalKey((GlobalKeys)25)) { return true; } int num = ((___m_craftUpgradeItem == null) ? 1 : (___m_craftUpgradeItem.m_quality + 1)); int num2 = ((!___m_multiCrafting) ? 1 : ___m_multiCraftAmount); List<ChestScanner.Need> list = new List<ChestScanner.Need>(); if (___m_craftRecipe.m_requireOnlyOneIngredient) { int amount = default(int); ItemData val = default(ItemData); try { ___m_craftRecipe.GetAmount(num, ref amount, ref val, num2); } catch (Exception) { return true; } if (val == null) { return true; } list.Add(new ChestScanner.Need { Name = val.m_shared.m_name, Amount = amount, Quality = val.m_quality }); } else { CraftingStation currentCraftingStation = player.GetCurrentCraftingStation(); Requirement[] resources = ___m_craftRecipe.m_resources; foreach (Requirement val2 in resources) { if (ChestScanner.Charged(val2, currentCraftingStation)) { int num3 = val2.GetAmount(num) * num2; if (num3 > 0) { list.Add(new ChestScanner.Need { Name = val2.m_resItem.m_itemData.m_shared.m_name, Amount = num3, Quality = -1 }); } } } } string text = ChestScanner.FindShortfall(((Humanoid)player).GetInventory(), list); if (text != null) { ((Character)player).Message((MessageType)2, Translations.T("craftfromchests_short", Localization.instance.Localize(text)), 0, (Sprite)null, false); return false; } ChestScanner.Consuming = true; return true; } private static void Finalizer(bool __state) { ChestScanner.Consuming = __state; } } [HarmonyPatch(typeof(Player), "GetFirstRequiredItem")] internal static class Player_GetFirstRequiredItem { [HarmonyPriority(0)] private static void Postfix(Player __instance, Inventory inventory, Recipe recipe, int qualityLevel, int craftMultiplier, ref int amount, ref int extraAmount, ref ItemData __result) { if (__result != null || (Object)(object)recipe == (Object)null || !ChestScanner.Applies(inventory)) { return; } CraftingStation currentCraftingStation = __instance.GetCurrentCraftingStation(); Requirement[] resources = recipe.m_resources; foreach (Requirement val in resources) { if (!ChestScanner.Charged(val, currentCraftingStation)) { continue; } int num = val.GetAmount(qualityLevel) * craftMultiplier; string name = val.m_resItem.m_itemData.m_shared.m_name; for (int j = 1; j <= val.m_resItem.m_itemData.m_shared.m_maxQuality; j++) { if (inventory.CountItems(name, j, true) >= num) { ItemData val2 = ChestScanner.FindInChests(name, j); if (val2 != null) { amount = num; extraAmount = val.m_extraAmountOnlyOneIngredient; __result = val2; return; } } } } } } [HarmonyPatch(typeof(Inventory), "RemoveItem", new Type[] { typeof(string), typeof(int), typeof(int), typeof(bool) })] internal static class Inventory_RemoveItem { [HarmonyPriority(0)] private static bool Prefix(Inventory __instance, string name, ref int amount, int itemQuality, bool worldLevelBased, bool __runOriginal) { if (!__runOriginal) { return false; } if (!ChestScanner.Consuming || !ChestScanner.Applies(__instance)) { return true; } int num = ChestScanner.OwnCount(__instance, name, itemQuality, worldLevelBased); if (num >= amount) { return true; } int num2 = ChestScanner.RemoveFromChests(name, amount - num, itemQuality, worldLevelBased); if (num2 > 0) { Plugin.Log.LogWarning((object)$"Crafting: the chests were {num2} {name} short after all"); } amount = num; return num > 0; } } [BepInPlugin("com.quad.craftfromchests", "CraftFromChests", "1.5.2")] public class Plugin : BaseUnityPlugin { public const string Guid = "com.quad.craftfromchests"; public const string OldGuid = "com.dhack.craftfromchests"; public const string Name = "CraftFromChests"; public const string Version = "1.5.2"; internal static ConfigEntry<bool> Enabled; internal static ConfigEntry<float> Radius; internal static ConfigEntry<bool> ShowHaveCounts; internal static ConfigEntry<bool> ShowLines; internal static ConfigEntry<float> ToggleOffsetX; internal static ConfigEntry<float> ToggleOffsetY; internal static ConfigEntry<float> ToggleScale; internal static ConfigEntry<float> ToggleWidth; internal static ConfigEntry<float> RangeButtonWidth; internal static ManualLogSource Log; internal static ServerSettings Synced; private Harmony _harmony; private static string BuildTime { get { try { string path = "CraftFromChests.dll"; string text = Path.Combine(Paths.BepInExRootPath, "scripts", path); string text2 = Path.Combine(Paths.PluginPath, "CraftFromChests", path); string path2 = (File.Exists(text) ? text : text2); return File.GetLastWriteTime(path2).ToString("HH:mm:ss"); } catch { return "?"; } } } private void Awake() { //IL_018a: Unknown result type (might be due to invalid IL or missing references) //IL_0194: Expected O, but got Unknown //IL_01a4: Unknown result type (might be due to invalid IL or missing references) //IL_01ae: Expected O, but got Unknown Migration.FromOldGuid((BaseUnityPlugin)(object)this, "com.dhack.craftfromchests"); Translations.Load((BaseUnityPlugin)(object)this); Log = ((BaseUnityPlugin)this).Logger; Synced = new ServerSettings("com.quad.craftfromchests", ((BaseUnityPlugin)this).Config, ((BaseUnityPlugin)this).Logger); ShowHaveCounts = ((BaseUnityPlugin)this).Config.Bind<bool>("Display", "ShowHaveCounts", true, "In the crafting window, show how many of each material you HAVE (inventory + chests) next to how many you need."); ShowLines = ((BaseUnityPlugin)this).Config.Bind<bool>("Display", "ShowLines", false, "Draw lines from the crafting station to every chest it can use."); ToggleOffsetX = ((BaseUnityPlugin)this).Config.Bind<float>("Display", "ToggleOffsetX", 0f, "Move the 'Chest lines' button horizontally from its default spot beside the Upgrade tab (UI pixels)."); ToggleOffsetY = ((BaseUnityPlugin)this).Config.Bind<float>("Display", "ToggleOffsetY", 0f, "Move the 'Chest lines' button vertically (UI pixels). Negative = down."); ToggleScale = ((BaseUnityPlugin)this).Config.Bind<float>("Display", "ToggleScale", 1f, "Size multiplier for the 'Chest lines' button."); ToggleWidth = ((BaseUnityPlugin)this).Config.Bind<float>("Display", "ToggleWidth", 2f, "'Chest lines' button width as a multiple of the Upgrade tab's width."); RangeButtonWidth = ((BaseUnityPlugin)this).Config.Bind<float>("Display", "RangeButtonWidth", 1.4f, "'Range' button width as a multiple of the Upgrade tab's width."); ((Component)this).gameObject.AddComponent<ChestOverlay>(); Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Turn the mod on or off."); Radius = Synced.Add<float>(((BaseUnityPlugin)this).Config.Bind<float>("General", "Radius", 20f, new ConfigDescription("How far (in meters) from the crafting station a chest can be and still be used. In multiplayer the server's value applies.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(2f, 50f), Array.Empty<object>()))); _harmony = new Harmony("com.quad.craftfromchests"); _harmony.PatchAll(); Container[] array = Object.FindObjectsOfType<Container>(); foreach (Container c in array) { ChestScanner.Register(c); } ((BaseUnityPlugin)this).Logger.LogInfo((object)"CraftFromChests 1.5.2 loaded"); if ((Object)(object)Player.m_localPlayer != (Object)null) { Announce(reloaded: true); } } internal static void Announce(bool reloaded) { string text = Localization.instance.Localize(Enabled.Value ? "$hud_on" : "$hud_off").ToLowerInvariant(); string text2 = Translations.T(reloaded ? "craftfromchests_chat_reloaded" : "craftfromchests_chat_loaded", "CraftFromChests", "1.5.2", text, Radius.Value.ToString("0.#"), BuildTime); if ((Object)(object)Chat.instance != (Object)null) { ((Terminal)Chat.instance).AddString("[Mod]", text2, (Type)1, false); AccessTools.Field(typeof(Chat), "m_hideTimer").SetValue(Chat.instance, 0f); ((Component)((Terminal)Chat.instance).m_chatWindow).gameObject.SetActive(true); } if ((Object)(object)Player.m_localPlayer != (Object)null) { ((Character)Player.m_localPlayer).Message((MessageType)1, Translations.T(reloaded ? "craftfromchests_reloaded" : "craftfromchests_loaded", "CraftFromChests"), 0, (Sprite)null, false); } } private void Update() { Synced?.Update(); } private void OnDestroy() { Translations.Unload(); Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } Synced?.Dispose(); ChestScanner.Suspend = false; ChestScanner.Consuming = false; HaveLabel.DestroyAll(); } } } namespace DHack.Shared { internal static class Migration { private sealed class Notice : MonoBehaviour { public string Text; private float _next; private void Update() { if (!(Time.unscaledTime < _next) && !((Object)(object)Player.m_localPlayer == (Object)null) && !((Object)(object)MessageHud.instance == (Object)null)) { ((Character)Player.m_localPlayer).Message((MessageType)2, Text, 0, (Sprite)null, false); _next = Time.unscaledTime + 60f; } } } public static void FromOldGuid(BaseUnityPlugin plugin, string oldGuid) { BepInPlugin metadata = MetadataHelper.GetMetadata((object)plugin); string text = ((metadata != null) ? metadata.GUID : null); if (string.IsNullOrEmpty(oldGuid) || string.IsNullOrEmpty(text) || oldGuid == text) { return; } ManualLogSource val = Logger.CreateLogSource(text); try { bool flag = false; if (Directory.Exists(Paths.ConfigPath)) { string[] files = Directory.GetFiles(Paths.ConfigPath, oldGuid + ".*"); foreach (string text2 in files) { string fileName = Path.GetFileName(text2); if (!fileName.EndsWith(".cfg") && !fileName.EndsWith(".json") && !fileName.EndsWith(".txt")) { continue; } string text3 = Path.Combine(Paths.ConfigPath, text + fileName.Substring(oldGuid.Length)); if (!File.Exists(text3)) { File.Copy(text2, text3); val.LogInfo((object)("Settings moved over from " + fileName + " (the mod's id is now " + text + "; the old file is kept as a backup)")); if (fileName == oldGuid + ".cfg") { flag = true; } } } } if (flag) { plugin.Config.Reload(); } } catch (Exception ex) { val.LogWarning((object)("Couldn't move the settings over from " + oldGuid + ".cfg: " + ex.Message)); } finally { Logger.Sources.Remove((ILogSource)(object)val); } ((MonoBehaviour)plugin).StartCoroutine(StandDownIfTwin(plugin, oldGuid)); } private static IEnumerator StandDownIfTwin(BaseUnityPlugin plugin, string oldGuid) { yield return null; yield return null; if (!((Object)(object)plugin == (Object)null) && Chainloader.PluginInfos.TryGetValue(oldGuid, out var value) && !((Object)(object)value.Instance == (Object)null) && !((Object)(object)value.Instance == (Object)(object)plugin)) { BepInPlugin metadata = MetadataHelper.GetMetadata((object)plugin); string text = ((metadata != null) ? metadata.Name : null) ?? oldGuid; string text2 = (string.IsNullOrEmpty(value.Location) ? "an older copy" : value.Location); string text3 = text + ": two copies are installed, an old one (" + text2 + ") and this one. Both would run and do everything twice, so this one stands down. Delete the old copy, then restart the game."; Debug.LogError((object)("[" + text + "] " + text3)); Object.Destroy((Object)(object)plugin); GameObject val = new GameObject("Migration_TwoCopies_" + text); Object.DontDestroyOnLoad((Object)(object)val); val.AddComponent<Notice>().Text = text3; } } } internal static class Translations { public static readonly string[] Shipped = new string[9] { "English", "Russian", "German", "Chinese", "Portuguese_Brazilian", "Spanish", "French", "Polish", "Turkish" }; private static readonly Dictionary<string, Dictionary<string, string>> Languages = new Dictionary<string, Dictionary<string, string>>(StringComparer.OrdinalIgnoreCase); private static Dictionary<string, string> _english = new Dictionary<string, string>(); private static string _overrideDir; private static bool _hooked; private static readonly MethodInfo AddWord = AccessTools.Method(typeof(Localization), "AddWord", (Type[])null, (Type[])null); private static readonly FieldInfo Cache = AccessTools.Field(typeof(Localization), "m_cache"); public static void Load(BaseUnityPlugin plugin) { Languages.Clear(); Assembly assembly = ((object)plugin).GetType().Assembly; foreach (string item in from n in assembly.GetManifestResourceNames() where n.StartsWith("translations/") && n.EndsWith(".json") select n) { using StreamReader streamReader = new StreamReader(assembly.GetManifestResourceStream(item)); Languages[Path.GetFileNameWithoutExtension(item.Substring("translations/".Length))] = Parse(streamReader.ReadToEnd()); } _english = (Languages.TryGetValue("English", out var value) ? value : new Dictionary<string, string>()); _overrideDir = Path.Combine(Paths.ConfigPath, "translations", plugin.Info.Metadata.GUID); if (!_hooked) { Localization.OnLanguageChange = (Action)Delegate.Combine(Localization.OnLanguageChange, new Action(Apply)); _hooked = true; } Apply(); } public static void Unload() { if (_hooked) { Localization.OnLanguageChange = (Action)Delegate.Remove(Localization.OnLanguageChange, new Action(Apply)); _hooked = false; } } public static string T(string key, params object[] args) { string text = ((Localization.instance != null) ? Localization.instance.Localize("$" + key) : null); if (string.IsNullOrEmpty(text) || text == "[" + key + "]") { text = (_english.TryGetValue(key, out var value) ? value : key); } if (args == null || args.Length == 0) { return text; } try { return string.Format(text, args); } catch (FormatException) { string value2; return _english.TryGetValue(key, out value2) ? SafeFormat(value2, args) : text; } } private static string SafeFormat(string text, object[] args) { try { return string.Format(text, args); } catch (FormatException) { return text; } } private static void Apply() { Localization instance = Localization.instance; if (instance == null || AddWord == null) { return; } string selectedLanguage = instance.GetSelectedLanguage(); Dictionary<string, string> dictionary = new Dictionary<string, string>(_english); if (Languages.TryGetValue(selectedLanguage, out var value)) { foreach (KeyValuePair<string, string> item in value) { dictionary[item.Key] = item.Value; } } try { string path = Path.Combine(_overrideDir, selectedLanguage + ".json"); if (File.Exists(path)) { foreach (KeyValuePair<string, string> item2 in Parse(File.ReadAllText(path))) { dictionary[item2.Key] = item2.Value; } } } catch (Exception ex) { Debug.LogWarning((object)("Translations: couldn't read the player's " + selectedLanguage + " file: " + ex.Message)); } foreach (KeyValuePair<string, string> item3 in dictionary) { AddWord.Invoke(instance, new object[2] { item3.Key, item3.Value }); } object obj = Cache?.GetValue(instance); obj?.GetType().GetMethod("EvictAll")?.Invoke(obj, null); } private static Dictionary<string, string> Parse(string json) { Dictionary<string, string> dictionary = new Dictionary<string, string>(); foreach (Match item in Regex.Matches(json, "\"((?:[^\"\\\\]|\\\\.)*)\"\\s*:\\s*\"((?:[^\"\\\\]|\\\\.)*)\"")) { dictionary[Unescape(item.Groups[1].Value)] = Unescape(item.Groups[2].Value); } return dictionary; } private static string Unescape(string s) { return Regex.Replace(s, "\\\\(u[0-9a-fA-F]{4}|.)", delegate(Match m) { string value = m.Groups[1].Value; return value[0] switch { 'n' => "\n", 't' => "\t", 'u' => ((char)Convert.ToInt32(value.Substring(1), 16)).ToString(), _ => value, }; }); } } internal sealed class ServerSettings : IDisposable { private readonly string _send; private readonly string _ask; private readonly ConfigFile _config; private readonly ManualLogSource _log; private readonly List<ConfigEntryBase> _entries = new List<ConfigEntryBase>(); private readonly Dictionary<ConfigEntryBase, object> _own = new Dictionary<ConfigEntryBase, object>(); private readonly Dictionary<ConfigEntryBase, object> _server = new Dictionary<ConfigEntryBase, object>(); private ZRoutedRpc _registeredOn; private bool _applying; private bool _changePending; private float _nextChangeSend; private const float ChangeSendGap = 0.5f; public bool FromServer => _server.Count > 0; private static bool IsServer { get { if ((Object)(object)ZNet.instance != (Object)null) { return ZNet.instance.IsServer(); } return false; } } public ServerSettings(string guid, ConfigFile config, ManualLogSource log) { _send = guid + ".settings"; _ask = guid + ".settings.ask"; _config = config; _log = log; } public ConfigEntry<T> Add<T>(ConfigEntry<T> entry) { if (entry == null || _entries.Contains((ConfigEntryBase)(object)entry)) { return entry; } _entries.Add((ConfigEntryBase)(object)entry); entry.SettingChanged += OnChanged; return entry; } public void Update() { ZRoutedRpc instance = ZRoutedRpc.instance; if (instance != _registeredOn) { Unregister(); Restore(); _changePending = false; if (instance != null) { Register(instance); } } if (_changePending && Time.unscaledTime >= _nextChangeSend) { SendChange(); } } public void Dispose() { Unregister(); Restore(); foreach (ConfigEntryBase entry in _entries) { Unsubscribe(entry); } _entries.Clear(); } private void Register(ZRoutedRpc rpc) { Forget(rpc, _send); Forget(rpc, _ask); rpc.Register<ZPackage>(_send, (Action<long, ZPackage>)OnValues); rpc.Register(_ask, (Action<long>)OnAsk); rpc.m_onNewPeer = (Action<long>)Delegate.Combine(rpc.m_onNewPeer, new Action<long>(OnNewPeer)); _registeredOn = rpc; } private void Unregister() { if (_registeredOn != null) { ZRoutedRpc registeredOn = _registeredOn; registeredOn.m_onNewPeer = (Action<long>)Delegate.Remove(registeredOn.m_onNewPeer, new Action<long>(OnNewPeer)); Forget(_registeredOn, _send); Forget(_registeredOn, _ask); _registeredOn = null; } } private static void Forget(ZRoutedRpc rpc, string name) { if (AccessTools.Field(typeof(ZRoutedRpc), "m_functions")?.GetValue(rpc) is IDictionary dictionary) { dictionary.Remove(StringExtensionMethods.GetStableHashCode(name)); } } private void OnNewPeer(long peer) { if (IsServer) { Send(peer); } else if ((Object)(object)ZNet.instance != (Object)null) { ZNetPeer serverPeer = ZNet.instance.GetServerPeer(); if (serverPeer != null && serverPeer.m_uid == peer) { Ask(); } } } private void OnAsk(long sender) { if (IsServer) { Send(sender); } } private void Ask() { try { ZRoutedRpc instance = ZRoutedRpc.instance; if (instance != null) { instance.InvokeRoutedRPC(ZNet.instance.GetServerPeer().m_uid, _ask, Array.Empty<object>()); } } catch (Exception) { } } private void Send(long target) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected O, but got Unknown if (ZRoutedRpc.instance == null || _entries.Count == 0) { return; } ZPackage val = new ZPackage(); val.Write(_entries.Count); foreach (ConfigEntryBase entry in _entries) { val.Write(entry.Definition.Section); val.Write(entry.Definition.Key); val.Write(TomlTypeConverter.ConvertToString(entry.BoxedValue, entry.SettingType)); } ZRoutedRpc.instance.InvokeRoutedRPC(target, _send, new object[1] { val }); } private void OnValues(long sender, ZPackage pkg) { if (IsServer) { return; } ZNetPeer val = (((Object)(object)ZNet.instance != (Object)null) ? ZNet.instance.GetServerPeer() : null); if (val == null || val.m_uid != sender) { return; } try { int num = pkg.ReadInt(); List<string> list = new List<string>(); for (int i = 0; i < num; i++) { string section = pkg.ReadString(); string key = pkg.ReadString(); string text = pkg.ReadString(); ConfigEntryBase val2 = ((IEnumerable<ConfigEntryBase>)_entries).FirstOrDefault((Func<ConfigEntryBase, bool>)((ConfigEntryBase x) => x.Definition.Section == section && x.Definition.Key == key)); if (val2 != null) { object obj; try { obj = TomlTypeConverter.ConvertToValue(text, val2.SettingType); } catch (Exception) { continue; } if (!_own.ContainsKey(val2)) { _own[val2] = val2.BoxedValue; } _server[val2] = obj; if (!object.Equals(val2.BoxedValue, obj)) { Apply(val2, obj); list.Add(section + "/" + key + " = " + text); } } } ManualLogSource log = _log; if (log != null) { log.LogInfo((object)($"Settings from the server apply while you play here ({_server.Count})" + ((list.Count > 0) ? (": " + string.Join(", ", list.ToArray())) : ""))); } } catch (Exception ex2) { ManualLogSource log2 = _log; if (log2 != null) { log2.LogWarning((object)("Could not read the server's settings: " + ex2.Message)); } } } private void Apply(ConfigEntryBase e, object value) { bool saveOnConfigSet = _config.SaveOnConfigSet; _applying = true; try { _config.SaveOnConfigSet = false; e.BoxedValue = value; } finally { _config.SaveOnConfigSet = saveOnConfigSet; _applying = false; } } private void Restore() { if (_server.Count == 0) { return; } foreach (KeyValuePair<ConfigEntryBase, object> item in _own) { Apply(item.Key, item.Value); } _own.Clear(); _server.Clear(); ManualLogSource log = _log; if (log != null) { log.LogInfo((object)"Your own settings apply again"); } } private void OnChanged(object sender, EventArgs args) { if (_applying) { return; } ConfigEntryBase val = (ConfigEntryBase)((sender is ConfigEntryBase) ? sender : null); if (val == null) { return; } if (_server.TryGetValue(val, out var value)) { _own[val] = val.BoxedValue; Apply(val, value); } else if (IsServer) { _changePending = true; if (Time.unscaledTime >= _nextChangeSend) { SendChange(); } } } private void SendChange() { _changePending = false; _nextChangeSend = Time.unscaledTime + 0.5f; if (IsServer && ZNet.instance.GetPeers().Count > 0) { Send(0L); } } private void Unsubscribe(ConfigEntryBase e) { ((object)e).GetType().GetEvent("SettingChanged")?.RemoveEventHandler(e, new EventHandler(OnChanged)); } } internal static class SpecialContainers { private static readonly string[] Prefabs = new string[2] { "piece_ledgerchest", "piece_recycler" }; public static bool NotStorage(Container c) { if ((Object)(object)c == (Object)null) { return false; } Piece componentInParent = ((Component)c).GetComponentInParent<Piece>(); string text = (((Object)(object)componentInParent != (Object)null) ? ((Object)((Component)componentInParent).gameObject).name : ((Object)((Component)c).gameObject).name); string[] prefabs = Prefabs; foreach (string value in prefabs) { if (text.StartsWith(value, StringComparison.Ordinal) || ((Object)((Component)c).gameObject).name.StartsWith(value, StringComparison.Ordinal)) { return true; } } if ((Object)(object)(((Object)(object)componentInParent != (Object)null) ? ((Component)componentInParent).GetComponentInChildren<Incinerator>(true) : ((Component)c).GetComponentInParent<Incinerator>()) != (Object)null) { return true; } ZNetView component = ((Component)c).GetComponent<ZNetView>(); ZDO val = (((Object)(object)component != (Object)null && component.IsValid()) ? component.GetZDO() : null); if (val != null) { if (val.GetInt("dh_arena_chest", 0) == 0) { return val.GetBool("dh_notstorage", false); } return true; } return false; } } }