Decompiled source of ValheimPlusPlus v1.5.1

patchers\GC_ModSchalter_VPP.dll

Decompiled 3 days ago
using System;
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.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Mono.Cecil;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("assembly_guiutils")]
[assembly: IgnoresAccessChecksTo("assembly_utils")]
[assembly: IgnoresAccessChecksTo("assembly_valheim")]
[assembly: TargetFramework(".NETFramework,Version=v4.8.1", FrameworkDisplayName = ".NET Framework 4.8.1")]
[assembly: AssemblyCompany("Gorduan")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.1.0.0")]
[assembly: AssemblyInformationalVersion("1.1.0+7c8bde9391283b12125175d68f4632c4cf8f93b3")]
[assembly: AssemblyProduct("GC_ModSchalter")]
[assembly: AssemblyTitle("GC_ModSchalter_VPP")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.1.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 GC.Patcher
{
	public static class ModSchalter
	{
		public const string KernGuid = "gorduan.gc.kern";

		private static readonly string[] OldPackages = new string[3] { "gorduan.gc_server", "gorduan.gc_client", "gorduan.valheimplusplus" };

		public const string DataInstalled = "GC.ModSchalter.Installed";

		public const string DataBest = "GC.ModSchalter.Best";

		public const string DataAll = "GC.ModSchalter.All";

		public const string DataSkipped = "GC.ModSchalter.Skipped";

		public const string DataProtected = "GC.ModSchalter.Protected";

		public const string DataFile = "GC.ModSchalter.File";

		private static readonly ManualLogSource Log = Logger.CreateLogSource("GC_ModSchalter");

		public static IEnumerable<string> TargetDLLs { get; } = Array.Empty<string>();

		public static string ListFile => Path.Combine(Path.Combine(Paths.ConfigPath, "GC_Kern"), "aus.txt");

		private static Version Own => typeof(ModSchalter).Assembly.GetName().Version;

		public static void Patch(AssemblyDefinition assembly)
		{
		}

		public static void Initialize()
		{
			AppDomain currentDomain = AppDomain.CurrentDomain;
			if (!(currentDomain.GetData("GC.ModSchalter.Best") is Version version) || Own > version)
			{
				currentDomain.SetData("GC.ModSchalter.Best", Own);
			}
		}

		public static void Finish()
		{
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Expected O, but got Unknown
			AppDomain currentDomain = AppDomain.CurrentDomain;
			object data = currentDomain.GetData("GC.ModSchalter.Installed");
			bool flag = default(bool);
			int num;
			if (data is bool)
			{
				flag = (bool)data;
				num = 1;
			}
			else
			{
				num = 0;
			}
			if (((uint)num & (flag ? 1u : 0u)) != 0 || (currentDomain.GetData("GC.ModSchalter.Best") is Version version && Own < version))
			{
				return;
			}
			currentDomain.SetData("GC.ModSchalter.Installed", true);
			try
			{
				MethodInfo methodInfo = AccessTools.Method(typeof(TypeLoader), "FindPluginTypes", (Type[])null, (Type[])null).MakeGenericMethod(typeof(PluginInfo));
				new Harmony("gorduan.gc.modschalter").Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(ModSchalter), "FindPluginTypesPostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				currentDomain.SetData("GC.ModSchalter.File", ListFile);
			}
			catch (Exception arg)
			{
				Log.LogError((object)$"Konnte sich nicht in BepInEx einhängen, alle Mods laden: {arg}");
			}
		}

		private static void FindPluginTypesPostfix(string cacheName, Dictionary<string, List<PluginInfo>> __result)
		{
			if (cacheName != "chainloader" || __result == null)
			{
				return;
			}
			try
			{
				List<PluginInfo> list = __result.Values.SelectMany((List<PluginInfo> result) => result).ToList();
				AppDomain.CurrentDomain.SetData("GC.ModSchalter.All", list);
				HashSet<string> hashSet = ReadList(ListFile);
				if (hashSet.Count == 0)
				{
					return;
				}
				if (!list.Any((PluginInfo info) => string.Equals(info.Metadata.GUID, "gorduan.gc.kern", StringComparison.OrdinalIgnoreCase)))
				{
					Log.LogWarning((object)"GC_Kern fehlt: aus.txt gilt nicht, alle Mods laden.");
					return;
				}
				if (list.Any((PluginInfo info) => OldPackages.Contains<string>(info.Metadata.GUID, StringComparer.OrdinalIgnoreCase)))
				{
					Log.LogWarning((object)"Ein altes GC-Sammelpaket liegt noch im Ordner, GC_Kern startet dann nicht: aus.txt gilt nicht, alle Mods laden.");
					return;
				}
				HashSet<string> hashSet2 = Protected(list);
				List<PluginInfo> list2 = new List<PluginInfo>();
				PropertyInfo propertyInfo = AccessTools.Property(typeof(PluginInfo), "Location");
				foreach (KeyValuePair<string, List<PluginInfo>> item in __result.ToList())
				{
					foreach (PluginInfo item2 in item.Value.ToList())
					{
						if (hashSet.Contains(item2.Metadata.GUID) && !hashSet2.Contains(item2.Metadata.GUID))
						{
							propertyInfo?.SetValue(item2, item.Key, null);
							item.Value.Remove(item2);
							list2.Add(item2);
							Log.LogMessage((object)$"Im Menü GC Option ausgeschaltet, wird nicht geladen: [{item2}]");
						}
					}
					if (item.Value.Count == 0)
					{
						__result.Remove(item.Key);
					}
				}
				AppDomain.CurrentDomain.SetData("GC.ModSchalter.Skipped", list2);
				AppDomain.CurrentDomain.SetData("GC.ModSchalter.Protected", hashSet2.ToArray());
			}
			catch (Exception arg)
			{
				Log.LogError((object)$"Ausgeschaltete Mods nicht gefiltert, alle Mods laden: {arg}");
			}
		}

		private static HashSet<string> Protected(List<PluginInfo> all)
		{
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			Dictionary<string, PluginInfo> dictionary = new Dictionary<string, PluginInfo>(StringComparer.OrdinalIgnoreCase);
			foreach (PluginInfo item in all)
			{
				dictionary[item.Metadata.GUID] = item;
			}
			HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "gorduan.gc.kern" };
			Queue<string> queue = new Queue<string>(hashSet);
			while (queue.Count > 0)
			{
				if (!dictionary.TryGetValue(queue.Dequeue(), out var value))
				{
					continue;
				}
				foreach (BepInDependency dependency in value.Dependencies)
				{
					if ((dependency.Flags & 1) != 0 && hashSet.Add(dependency.DependencyGUID))
					{
						queue.Enqueue(dependency.DependencyGUID);
					}
				}
			}
			return hashSet;
		}

		public static HashSet<string> ReadList(string file)
		{
			HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			if (!File.Exists(file))
			{
				return hashSet;
			}
			string[] array = File.ReadAllLines(file);
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				if (text.Length > 0 && !text.StartsWith("#", StringComparison.Ordinal))
				{
					hashSet.Add(text);
				}
			}
			return hashSet;
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}

GC_Baeume.dll

Decompiled 3 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using GC.Shared;
using GC.ValheimPlusPlus.TreeRegrow;
using HarmonyLib;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("assembly_guiutils")]
[assembly: IgnoresAccessChecksTo("assembly_utils")]
[assembly: IgnoresAccessChecksTo("assembly_valheim")]
[assembly: TargetFramework(".NETFramework,Version=v4.8.1", FrameworkDisplayName = ".NET Framework 4.8.1")]
[assembly: AssemblyCompany("Gorduan")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.5.1.0")]
[assembly: AssemblyInformationalVersion("1.5.1+7c8bde9391283b12125175d68f4632c4cf8f93b3")]
[assembly: AssemblyProduct("GC_Baeume")]
[assembly: AssemblyTitle("GC_Baeume")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.5.1.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 GC.ValheimPlusPlus
{
	internal static class VppPlugin
	{
		internal static ManualLogSource Log;

		internal static GcModule? Module;

		internal static bool Allowed
		{
			get
			{
				if (Module != null)
				{
					return Module.Allowed;
				}
				return true;
			}
		}

		internal static bool OnServer
		{
			get
			{
				if (Module != null)
				{
					return Module.OnServer;
				}
				return false;
			}
		}
	}
}
namespace GC.ValheimPlusPlus.TreeRegrow
{
	internal static class Plugin
	{
		public const string Guid = "gorduan.gc_treeregrow";

		public const string ModName = "GC_TreeRegrow";

		internal const string TreeKey = "gc_regrow_tree";

		internal const string TimeKey = "gc_regrow_at";

		internal static ManualLogSource Log = null;

		internal static ConfigEntry<bool> Enabled = null;

		internal static ConfigEntry<float> OverrideMinutes = null;

		internal static ConfigEntry<float> FallbackMinutes = null;

		internal static ConfigEntry<float> Spread = null;

		internal static ConfigEntry<bool> ShowHover = null;

		internal static readonly Dictionary<string, List<string>> StubToTrees = new Dictionary<string, List<string>>();

		internal static readonly Dictionary<string, Plant> TreeToSapling = new Dictionary<string, Plant>();

		internal static string? PendingTree;

		internal static Vector3 PendingPosition;

		internal static bool Blocked => Chainloader.PluginInfos.ContainsKey("gorduan.gc_treeregrow");

		internal static bool On
		{
			get
			{
				if (Enabled.Value)
				{
					return !Blocked;
				}
				return false;
			}
		}

		internal static bool HoverOn
		{
			get
			{
				if (ShowHover.Value)
				{
					return !Blocked;
				}
				return false;
			}
		}

		internal static void Init(ConfigFile Config, Harmony harmony)
		{
			Log = VppPlugin.Log;
			Enabled = Gc.BindSynced<bool>(Config, "5 - Tree Regrow", "Enabled", true, "Baumstümpfe wachsen wieder zum Baum nach (wird vom Server vorgegeben).", (AcceptableValueBase)null);
			OverrideMinutes = Gc.BindSynced<float>(Config, "5 - Tree Regrow", "OverrideMinutes", 0f, "0 = Wachstumszeit des passenden Setzlings aus dem Spiel. Größer 0 = feste Minuten Spielzeit für alle Bäume (ein Valheim-Tag dauert 30 Minuten).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10080f));
			FallbackMinutes = Gc.BindSynced<float>(Config, "5 - Tree Regrow", "FallbackMinutes", 60f, "Minuten für Bäume, die keinen Setzling haben.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 10080f));
			Spread = Gc.BindSynced<float>(Config, "5 - Tree Regrow", "RandomSpread", 0.25f, "Zufällige Abweichung für feste Zeiten (Override und Fallback), 0.25 = plus/minus 25 %. Setzling-Zeiten haben schon eine eigene Spanne.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 0.9f));
			ShowHover = Gc.Bind<bool>(Config, "5 - Tree Regrow - Interface", "ShowHover", true, "Restzeit anzeigen, wenn man auf einen Stumpf schaut.", (AcceptableValueBase)null);
		}

		internal static double RegrowSeconds(string? tree)
		{
			if (OverrideMinutes.Value > 0f)
			{
				return (double)OverrideMinutes.Value * 60.0 * (1.0 + (double)Random.Range(0f - Spread.Value, Spread.Value));
			}
			if (tree != null && TreeToSapling.TryGetValue(tree, out Plant value) && (Object)(object)value != (Object)null)
			{
				float num = Mathf.Min(value.m_growTime, value.m_growTimeMax);
				float num2 = Mathf.Max(value.m_growTime, value.m_growTimeMax);
				return Mathf.Lerp(num, num2, Random.value);
			}
			return (double)FallbackMinutes.Value * 60.0 * (1.0 + (double)Random.Range(0f - Spread.Value, Spread.Value));
		}
	}
	[HarmonyPatch(typeof(ZNetScene), "Awake")]
	internal static class ZNetSceneAwakePatch
	{
		private static void Postfix(ZNetScene __instance)
		{
			if (Plugin.Blocked)
			{
				return;
			}
			Plugin.StubToTrees.Clear();
			Plugin.TreeToSapling.Clear();
			foreach (GameObject prefab in __instance.m_prefabs)
			{
				if ((Object)(object)prefab == (Object)null)
				{
					continue;
				}
				Plant component = prefab.GetComponent<Plant>();
				if ((Object)(object)component != (Object)null && component.m_grownPrefabs != null)
				{
					GameObject[] grownPrefabs = component.m_grownPrefabs;
					foreach (GameObject val in grownPrefabs)
					{
						if ((Object)(object)val != (Object)null && (Object)(object)val.GetComponent<TreeBase>() != (Object)null && !Plugin.TreeToSapling.ContainsKey(((Object)val).name))
						{
							Plugin.TreeToSapling[((Object)val).name] = component;
						}
					}
				}
				TreeBase component2 = prefab.GetComponent<TreeBase>();
				if ((Object)(object)component2 == (Object)null || (Object)(object)component2.m_stubPrefab == (Object)null)
				{
					continue;
				}
				string name = ((Object)component2.m_stubPrefab).name;
				if (!Plugin.StubToTrees.TryGetValue(name, out List<string> value))
				{
					value = (Plugin.StubToTrees[name] = new List<string>());
				}
				value.Add(((Object)prefab).name);
				if (!((Object)(object)component2.m_stubPrefab.GetComponent<RegrowStub>() == (Object)null))
				{
					continue;
				}
				component2.m_stubPrefab.AddComponent<RegrowStub>();
				MonoBehaviour[] componentsInChildren = component2.m_stubPrefab.GetComponentsInChildren<MonoBehaviour>(true);
				foreach (MonoBehaviour val2 in componentsInChildren)
				{
					if (val2 is Hoverable && !(val2 is RegrowStub))
					{
						Plugin.Log.LogInfo((object)("Stumpf " + name + " hat schon " + ((object)val2).GetType().Name + " an " + ((Object)val2).name + ": Status kommt dort hin."));
					}
				}
			}
			string arg = string.Join(", ", from pair in Plugin.TreeToSapling
				orderby pair.Key
				select $"{pair.Key} {pair.Value.m_growTime / 60f:0}-{pair.Value.m_growTimeMax / 60f:0} Min.");
			Plugin.Log.LogInfo((object)$"Bäume wachsen nach: {Plugin.StubToTrees.Count} Stumpf-Arten, Setzling-Zeiten für {Plugin.TreeToSapling.Count} Bäume: {arg}");
		}
	}
	[HarmonyPatch(typeof(TreeBase), "SpawnLog")]
	internal static class SpawnLogPatch
	{
		private static void Prefix(TreeBase __instance)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			Plugin.PendingTree = Utils.GetPrefabName(((Component)__instance).gameObject);
			Plugin.PendingPosition = ((Component)__instance).transform.position;
		}

		private static void Finalizer()
		{
			Plugin.PendingTree = null;
		}
	}
	public class RegrowStub : MonoBehaviour, Hoverable
	{
		private ZNetView? nview;

		private string? fellenTree;

		private float hoverCheckUntil;

		private bool hoverBlocked;

		private static int pieceMask;

		private long Now
		{
			get
			{
				if (!((Object)(object)ZNet.instance != (Object)null))
				{
					return 0L;
				}
				return ZNet.instance.GetTime().Ticks;
			}
		}

		private void Awake()
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			if (Plugin.PendingTree != null && Vector3.Distance(Plugin.PendingPosition, ((Component)this).transform.position) < 1f)
			{
				fellenTree = Plugin.PendingTree;
			}
		}

		private void Start()
		{
			nview = ((Component)this).GetComponent<ZNetView>();
			if (!((Object)(object)nview == (Object)null) && nview.GetZDO() != null)
			{
				if (fellenTree != null && nview.IsOwner())
				{
					nview.GetZDO().Set("gc_regrow_tree", fellenTree);
				}
				((MonoBehaviour)this).InvokeRepeating("Check", Random.Range(3f, 10f), 15f);
			}
		}

		private void Check()
		{
			if (!Plugin.On || (Object)(object)nview == (Object)null || !nview.IsValid() || !nview.IsOwner() || (Object)(object)ZNet.instance == (Object)null)
			{
				return;
			}
			ZDO zDO = nview.GetZDO();
			string text = zDO.GetString("gc_regrow_tree", "");
			if (string.IsNullOrEmpty(text))
			{
				return;
			}
			long num = zDO.GetLong("gc_regrow_at", 0L);
			if (num == 0L)
			{
				zDO.Set("gc_regrow_at", Now + (long)(Plugin.RegrowSeconds(text) * 10000000.0));
			}
			else if (Now >= num)
			{
				if (Blocked())
				{
					zDO.Set("gc_regrow_at", Now + 6000000000L);
				}
				else
				{
					Regrow(zDO);
				}
			}
		}

		private bool Blocked()
		{
			//IL_004d: 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_0057: Unknown result type (might be due to invalid IL or missing references)
			if (pieceMask == 0)
			{
				pieceMask = LayerMask.GetMask(new string[6] { "piece", "piece_nonsolid", "Default", "static_solid", "Default_small", "vehicle" });
			}
			Collider[] array = Physics.OverlapSphere(((Component)this).transform.position + Vector3.up, 1.2f, pieceMask, (QueryTriggerInteraction)1);
			for (int i = 0; i < array.Length; i++)
			{
				Piece componentInParent = ((Component)array[i]).GetComponentInParent<Piece>();
				if ((Object)(object)componentInParent != (Object)null && componentInParent.IsPlacedByPlayer())
				{
					return true;
				}
			}
			return false;
		}

		private void Regrow(ZDO zdo)
		{
			//IL_007c: 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_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: 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)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			string text = zdo.GetString("gc_regrow_tree", "");
			GameObject val = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab(text) : null);
			if ((Object)(object)val == (Object)null)
			{
				Plugin.Log.LogWarning((object)("Kein Baum für Stumpf " + Utils.GetPrefabName(((Component)this).gameObject) + " gefunden, Nachwachsen übersprungen."));
				zdo.Set("gc_regrow_at", Now + 36000000000L);
				return;
			}
			Vector3 position = ((Component)this).transform.position;
			Quaternion rotation = ((Component)this).transform.rotation;
			Vector3 localScale = ((Component)this).transform.localScale;
			ZNetScene.instance.Destroy(((Component)this).gameObject);
			GameObject obj = Object.Instantiate<GameObject>(val, position, rotation);
			ZNetView component = obj.GetComponent<ZNetView>();
			if ((Object)(object)component != (Object)null)
			{
				component.SetLocalScale(localScale);
			}
			TreeBase component2 = obj.GetComponent<TreeBase>();
			if ((Object)(object)component2 != (Object)null)
			{
				component2.Grow();
			}
		}

		public string GetHoverText()
		{
			if (!Plugin.On || !Plugin.HoverOn || (Object)(object)nview == (Object)null || !nview.IsValid() || (Object)(object)ZNet.instance == (Object)null)
			{
				return "";
			}
			if (string.IsNullOrEmpty(nview.GetZDO().GetString("gc_regrow_tree", "")))
			{
				return "";
			}
			long num = nview.GetZDO().GetLong("gc_regrow_at", 0L);
			if (num == 0L)
			{
				return GcLoc.Get("gctr_hover_pending", Array.Empty<object>());
			}
			if (Time.time >= hoverCheckUntil)
			{
				hoverCheckUntil = Time.time + 1f;
				hoverBlocked = Blocked();
			}
			if (hoverBlocked)
			{
				return GcLoc.Get("gctr_hover_blocked", Array.Empty<object>());
			}
			long num2 = num - Now;
			if (num2 <= 0)
			{
				return GcLoc.Get("gctr_hover_soon", Array.Empty<object>());
			}
			long num3 = (long)Math.Ceiling((double)num2 / 10000000.0);
			string text = (num3 / 3600).ToString("00") + ":" + (num3 / 60 % 60).ToString("00") + ":" + (num3 % 60).ToString("00");
			return GcLoc.Get("gctr_hover_time", new object[1] { text });
		}

		public string GetHoverName()
		{
			return GcLoc.Get("gctr_name", Array.Empty<object>());
		}

		public float GetHoverOffset()
		{
			return 0f;
		}
	}
	[HarmonyPatch(typeof(HoverText), "GetHoverText")]
	internal static class HoverTextPatch
	{
		private static void Postfix(HoverText __instance, ref string __result)
		{
			RegrowStub regrowStub = (Plugin.On ? ((Component)__instance).GetComponentInParent<RegrowStub>() : null);
			if ((Object)(object)regrowStub != (Object)null)
			{
				string hoverText = regrowStub.GetHoverText();
				if (hoverText.Length > 0)
				{
					__result = hoverText;
				}
			}
		}
	}
}
namespace GC.ValheimPlusPlus.ModBaeume
{
	[BepInPlugin("gorduan.gc.baeume", "GC_Baeume", "1.5.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		internal static GcModule Module;

		private void Awake()
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Expected O, but got Unknown
			Module = GcKern.Register((BaseUnityPlugin)(object)this, ((BaseUnityPlugin)this).Logger, GcMod.Info);
			VppPlugin.Log = ((BaseUnityPlugin)this).Logger;
			VppPlugin.Module = Module;
			Harmony val = new Harmony("gorduan.gc.baeume");
			GC.ValheimPlusPlus.TreeRegrow.Plugin.Init(((BaseUnityPlugin)this).Config, val);
			Module.TakeOverOldSettings();
			val.PatchAll(typeof(Plugin).Assembly);
			Module.MenuGroup((Func<string, string>)((string key) => GcLoc.Get("vpp_" + key, Array.Empty<object>())), (string)null).Main("treeregrow", (ConfigEntryBase)(object)GC.ValheimPlusPlus.TreeRegrow.Plugin.Enabled, (Func<bool>)(() => GC.ValheimPlusPlus.TreeRegrow.Plugin.Blocked), "GC_TreeRegrow", (Func<string>)null, 0.0, "", (Func<bool>)null, (Func<bool>)null).Sub("treeregrow_override", (ConfigEntryBase)(object)GC.ValheimPlusPlus.TreeRegrow.Plugin.OverrideMinutes, 10.0, " min", (Func<bool>)null, "", (Func<bool>)null, (Func<bool>)null)
				.Sub("treeregrow_fallback", (ConfigEntryBase)(object)GC.ValheimPlusPlus.TreeRegrow.Plugin.FallbackMinutes, 10.0, " min", (Func<bool>)null, "", (Func<bool>)null, (Func<bool>)null)
				.Sub("treeregrow_spread", (ConfigEntryBase)(object)GC.ValheimPlusPlus.TreeRegrow.Plugin.Spread, 0.05, "", (Func<bool>)null, "", (Func<bool>)null, (Func<bool>)null)
				.Sub("treeregrowhover", (ConfigEntryBase)(object)GC.ValheimPlusPlus.TreeRegrow.Plugin.ShowHover, 0.0, "", (Func<bool>)null, "", (Func<bool>)null, (Func<bool>)null);
		}
	}
	internal static class GcMod
	{
		public const string Id = "baeume";

		public const string Guid = "gorduan.gc.baeume";

		public const string Name = "GC_Baeume";

		public const string Version = "1.5.1";

		public const int Stufe = 4;

		public static GcModInfo Info => new GcModInfo
		{
			Id = "baeume",
			Guid = "gorduan.gc.baeume",
			Name = "GC_Baeume",
			Version = "1.5.1",
			Stufe = 4,
			Seite = (GcSeite)2,
			Braucht = Array.Empty<string>(),
			Partner = Array.Empty<string>(),
			Fremd = Array.Empty<string>(),
			AlteConfig = "gorduan.valheimplusplus.cfg",
			Ausgleich = 0f
		};
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}

GC_BasisEssen.dll

Decompiled 3 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using GC.Shared;
using GC.ValheimPlusPlus.BaseFood;
using HarmonyLib;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("assembly_guiutils")]
[assembly: IgnoresAccessChecksTo("assembly_utils")]
[assembly: IgnoresAccessChecksTo("assembly_valheim")]
[assembly: TargetFramework(".NETFramework,Version=v4.8.1", FrameworkDisplayName = ".NET Framework 4.8.1")]
[assembly: AssemblyCompany("Gorduan")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.5.1.0")]
[assembly: AssemblyInformationalVersion("1.5.1+7c8bde9391283b12125175d68f4632c4cf8f93b3")]
[assembly: AssemblyProduct("GC_BasisEssen")]
[assembly: AssemblyTitle("GC_BasisEssen")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.5.1.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 GC.ValheimPlusPlus
{
	internal static class VppPlugin
	{
		internal static ManualLogSource Log;

		internal static GcModule? Module;

		internal static bool Allowed
		{
			get
			{
				if (Module != null)
				{
					return Module.Allowed;
				}
				return true;
			}
		}

		internal static bool OnServer
		{
			get
			{
				if (Module != null)
				{
					return Module.OnServer;
				}
				return false;
			}
		}
	}
}
namespace GC.ValheimPlusPlus.ModBasisEssen
{
	[BepInPlugin("gorduan.gc.basisessen", "GC_BasisEssen", "1.5.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		internal static GcModule Module;

		private void Awake()
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Expected O, but got Unknown
			Module = GcKern.Register((BaseUnityPlugin)(object)this, ((BaseUnityPlugin)this).Logger, GcMod.Info);
			VppPlugin.Log = ((BaseUnityPlugin)this).Logger;
			VppPlugin.Module = Module;
			Harmony val = new Harmony("gorduan.gc.basisessen");
			GC.ValheimPlusPlus.BaseFood.Plugin.Init(((BaseUnityPlugin)this).Config, val);
			Module.TakeOverOldSettings();
			Module.IsOn = () => GC.ValheimPlusPlus.BaseFood.Plugin.On;
			val.PatchAll(typeof(Plugin).Assembly);
			Module.MenuGroup((Func<string, string>)((string key) => GcLoc.Get("vpp_" + key, Array.Empty<object>())), (string)null).Main("basefood", (ConfigEntryBase)(object)GC.ValheimPlusPlus.BaseFood.Plugin.Enabled, (Func<bool>)null, "", (Func<string>)null, 0.0, "", (Func<bool>)null, (Func<bool>)null).Sub("basefood_workbench", (ConfigEntryBase)(object)GC.ValheimPlusPlus.BaseFood.Plugin.WorkbenchPercent, 10.0, " %", (Func<bool>)null, "", (Func<bool>)null, (Func<bool>)null)
				.Sub("basefood_ward", (ConfigEntryBase)(object)GC.ValheimPlusPlus.BaseFood.Plugin.WardPercent, 10.0, " %", (Func<bool>)null, "", (Func<bool>)null, (Func<bool>)null)
				.Sub("basefood_access", (ConfigEntryBase)(object)GC.ValheimPlusPlus.BaseFood.Plugin.WardNeedsAccess, 0.0, "", (Func<bool>)null, "", (Func<bool>)null, (Func<bool>)null);
		}
	}
	internal static class GcMod
	{
		public const string Id = "basisessen";

		public const string Guid = "gorduan.gc.basisessen";

		public const string Name = "GC_BasisEssen";

		public const string Version = "1.5.1";

		public const int Stufe = 4;

		public static GcModInfo Info => new GcModInfo
		{
			Id = "basisessen",
			Guid = "gorduan.gc.basisessen",
			Name = "GC_BasisEssen",
			Version = "1.5.1",
			Stufe = 4,
			Seite = (GcSeite)2,
			Braucht = Array.Empty<string>(),
			Partner = Array.Empty<string>(),
			Fremd = Array.Empty<string>(),
			AlteConfig = "gorduan.valheimplusplus.cfg",
			Ausgleich = 1f
		};
	}
}
namespace GC.ValheimPlusPlus.BaseFood
{
	internal static class Plugin
	{
		internal static ManualLogSource Log = null;

		internal static ConfigEntry<bool> Enabled = null;

		internal static ConfigEntry<int> WorkbenchPercent = null;

		internal static ConfigEntry<int> WardPercent = null;

		internal static ConfigEntry<string> Stations = null;

		internal static ConfigEntry<bool> WardNeedsAccess = null;

		private static float nextCheck;

		private static float cached;

		private static HashSet<string>? stationSet;

		private static string stationText = "";

		internal static bool On => Enabled.Value;

		internal static void Init(ConfigFile Config, Harmony harmony)
		{
			Log = VppPlugin.Log;
			Enabled = Gc.BindSynced<bool>(Config, "6 - Base Hunger", "Enabled", true, "Weniger Hungerverlust im Bereich von Werkbänken und Wächtersteinen (wird vom Server vorgegeben).", (AcceptableValueBase)null);
			WorkbenchPercent = Gc.BindSynced<int>(Config, "6 - Base Hunger", "WorkbenchPercent", 50, "Um wie viel Prozent der Hungerverlust im Bereich einer Werkbank sinkt. 0 = wie Vanilla, 100 = kein Hungerverlust.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100));
			WardPercent = Gc.BindSynced<int>(Config, "6 - Base Hunger", "WardPercent", 50, "Um wie viel Prozent der Hungerverlust im Bereich eines aktiven Wächtersteins sinkt. 0 = wie Vanilla, 100 = kein Hungerverlust.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100));
			Stations = Gc.BindSynced<string>(Config, "6 - Base Hunger", "Stations", "piece_workbench", "Kommagetrennte Prefab-Namen der Stationen, deren Bereich zählt (z. B. piece_workbench, forge, piece_stonecutter, piece_artisanstation, blackforge, piece_magetable).", (AcceptableValueBase)null);
			WardNeedsAccess = Gc.BindSynced<bool>(Config, "6 - Base Hunger", "WardNeedsAccess", true, "Nur Wächtersteine zählen, auf die der Spieler Zugriff hat (eigene oder freigegebene).", (AcceptableValueBase)null);
		}

		internal static float Reduction(Player player)
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			if (Time.time < nextCheck)
			{
				return cached;
			}
			nextCheck = Time.time + 1f;
			Vector3 position = ((Component)player).transform.position;
			int num = 0;
			if (WorkbenchPercent.Value > num && NearStation(position))
			{
				num = WorkbenchPercent.Value;
			}
			if (WardPercent.Value > num && InWard(position))
			{
				num = WardPercent.Value;
			}
			cached = Mathf.Clamp01((float)num / 100f);
			return cached;
		}

		private static bool NearStation(Vector3 position)
		{
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			if (stationSet == null || stationText != Stations.Value)
			{
				stationText = Stations.Value;
				stationSet = Gc.ParseList(stationText);
			}
			foreach (CraftingStation allStation in CraftingStation.m_allStations)
			{
				if (!((Object)(object)allStation == (Object)null) && stationSet.Contains(Utils.GetPrefabName(((Component)allStation).gameObject)))
				{
					Vector3 val = position;
					val.y = ((Component)allStation).transform.position.y;
					if (Vector3.Distance(((Component)allStation).transform.position, val) < allStation.GetStationBuildRange())
					{
						return true;
					}
				}
			}
			return false;
		}

		private static bool InWard(Vector3 position)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			foreach (PrivateArea allArea in PrivateArea.m_allAreas)
			{
				if ((Object)(object)allArea != (Object)null && allArea.IsEnabled() && allArea.IsInside(position, 0f) && (!WardNeedsAccess.Value || allArea.HaveLocalAccess()))
				{
					return true;
				}
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(Player), "UpdateFood")]
	internal static class UpdateFoodPatch
	{
		private static readonly List<KeyValuePair<Food, float>> Before = new List<KeyValuePair<Food, float>>();

		private static void Prefix(Player __instance, out bool __state)
		{
			__state = false;
			if (!Plugin.On || (Object)(object)__instance != (Object)(object)Player.m_localPlayer || __instance.m_foods.Count == 0 || Plugin.Reduction(__instance) <= 0f)
			{
				return;
			}
			Before.Clear();
			foreach (Food food in __instance.m_foods)
			{
				Before.Add(new KeyValuePair<Food, float>(food, food.m_time));
			}
			__state = true;
		}

		private static void Postfix(Player __instance, bool __state)
		{
			if (!__state)
			{
				return;
			}
			float num = Plugin.Reduction(__instance);
			foreach (KeyValuePair<Food, float> item in Before)
			{
				Food key = item.Key;
				float num2 = item.Value - key.m_time;
				if (!(num2 <= 0f) && __instance.m_foods.Contains(key))
				{
					key.m_time = Mathf.Min(item.Value, key.m_time + num2 * num);
				}
			}
			Before.Clear();
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}

GC_Kern.dll

Decompiled 3 days ago
using System;
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 BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using GC.Kern;
using GC.Kern.ModCheck;
using GC.Kern.Progression;
using GC.Shared;
using HarmonyLib;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.Rendering;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("assembly_guiutils")]
[assembly: IgnoresAccessChecksTo("assembly_utils")]
[assembly: IgnoresAccessChecksTo("assembly_valheim")]
[assembly: TargetFramework(".NETFramework,Version=v4.8.1", FrameworkDisplayName = ".NET Framework 4.8.1")]
[assembly: AssemblyCompany("Gorduan")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.1.1.0")]
[assembly: AssemblyInformationalVersion("1.1.1+7c8bde9391283b12125175d68f4632c4cf8f93b3")]
[assembly: AssemblyProduct("GC_Kern")]
[assembly: AssemblyTitle("GC_Kern")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.1.1.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 GC.Kern
{
	[HarmonyPatch(typeof(Localization), "SetLanguage")]
	internal static class LanguageKeepPatch
	{
		private static void Prefix(Localization __instance, out Dictionary<string, string>? __state)
		{
			__state = ((__instance.m_translations != null) ? new Dictionary<string, string>(__instance.m_translations) : null);
		}

		private static void Postfix(Localization __instance, Dictionary<string, string>? __state)
		{
			if (__state == null || __instance.m_translations == null)
			{
				return;
			}
			int num = 0;
			foreach (KeyValuePair<string, string> item in __state)
			{
				if (!__instance.m_translations.ContainsKey(item.Key))
				{
					__instance.m_translations[item.Key] = item.Value;
					num++;
				}
			}
			if (num > 0)
			{
				__instance.m_cache.EvictAll();
				KernPlugin.Log.LogInfo((object)$"Sprachwechsel: {num} Texte anderer Mods wiederhergestellt.");
			}
		}
	}
	internal static class ModSwitch
	{
		internal enum State
		{
			Loaded,
			Failed,
			Off,
			OtherProcess,
			NotLoaded
		}

		internal sealed class Mod
		{
			public string Guid = "";

			public string Name = "";

			public string Version = "";

			public string Location = "";

			public string Package = "";

			public string PackageVersion = "";

			public string Namespace = "";

			public string PackageName = "";

			public State State;

			public string Reason = "";

			public string[] HardDeps = Array.Empty<string>();

			public bool Protected;

			public string Search = "";
		}

		internal sealed class Profile
		{
			public string Name = "";

			public string File = "";

			public HashSet<string> Off = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
		}

		[Serializable]
		private sealed class ManifestJson
		{
			public string name = "";

			public string version_number = "";
		}

		private const string DataAll = "GC.ModSchalter.All";

		private const string DataSkipped = "GC.ModSchalter.Skipped";

		private const string DataProtected = "GC.ModSchalter.Protected";

		private const string DataFile = "GC.ModSchalter.File";

		private const string KernGuid = "gorduan.gc.kern";

		private static List<Mod>? catalog;

		private static HashSet<string>? wanted;

		private static HashSet<string> skippedAtStart = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		internal static bool PatcherActive => AppDomain.CurrentDomain.GetData("GC.ModSchalter.File") is string;

		internal static string Folder => Path.Combine(Paths.ConfigPath, "GC_Kern");

		private static string ListFile => (AppDomain.CurrentDomain.GetData("GC.ModSchalter.File") as string) ?? Path.Combine(Folder, "aus.txt");

		private static string ProfileFolder => Path.Combine(Folder, "profile");

		internal static IReadOnlyList<Mod> Catalog => catalog ?? (catalog = BuildCatalog());

		private static HashSet<string> Wanted => wanted ?? (wanted = ReadList(ListFile));

		internal static bool RestartNeeded
		{
			get
			{
				if (!PatcherActive)
				{
					return false;
				}
				HashSet<string> hashSet = new HashSet<string>(from m in Catalog
					where !m.Protected
					select m.Guid, StringComparer.OrdinalIgnoreCase);
				hashSet.IntersectWith(Wanted);
				return !hashSet.SetEquals(skippedAtStart);
			}
		}

		internal static string LastError { get; private set; } = "";

		internal static bool IsWantedOff(Mod mod)
		{
			if (!mod.Protected)
			{
				return Wanted.Contains(mod.Guid);
			}
			return false;
		}

		internal static Mod? Find(string guid)
		{
			return Catalog.FirstOrDefault((Mod m) => string.Equals(m.Guid, guid, StringComparison.OrdinalIgnoreCase));
		}

		private static List<Mod> BuildCatalog()
		{
			ImportModMenu();
			AppDomain currentDomain = AppDomain.CurrentDomain;
			List<PluginInfo> list = (currentDomain.GetData("GC.ModSchalter.All") as List<PluginInfo>) ?? new List<PluginInfo>();
			List<PluginInfo> list2 = (currentDomain.GetData("GC.ModSchalter.Skipped") as List<PluginInfo>) ?? new List<PluginInfo>();
			skippedAtStart = new HashSet<string>(list2.Select((PluginInfo info) => info.Metadata.GUID), StringComparer.OrdinalIgnoreCase);
			Dictionary<string, Mod> byGuid = new Dictionary<string, Mod>(StringComparer.OrdinalIgnoreCase);
			foreach (PluginInfo value in Chainloader.PluginInfos.Values)
			{
				Add(value, (!((Object)(object)value.Instance != (Object)null)) ? State.Failed : State.Loaded, "");
			}
			foreach (PluginInfo item in list2)
			{
				Add(item, State.Off, "");
			}
			foreach (PluginInfo item2 in list)
			{
				if (!byGuid.ContainsKey(item2.Metadata.GUID))
				{
					var (state, reason) = NotLoaded(item2);
					Add(item2, state, reason);
				}
			}
			HashSet<string> hashSet = new HashSet<string>((currentDomain.GetData("GC.ModSchalter.Protected") as string[]) ?? Array.Empty<string>(), StringComparer.OrdinalIgnoreCase);
			if (hashSet.Count == 0)
			{
				hashSet = Protected(byGuid);
			}
			foreach (Mod value2 in byGuid.Values)
			{
				value2.Protected = hashSet.Contains(value2.Guid);
			}
			return byGuid.Values.OrderBy<Mod, string>((Mod m) => m.Name, StringComparer.OrdinalIgnoreCase).ToList();
			void Add(PluginInfo info, State state2, string reason2)
			{
				string gUID = info.Metadata.GUID;
				if (!byGuid.ContainsKey(gUID))
				{
					Mod mod = new Mod
					{
						Guid = gUID,
						Name = info.Metadata.Name,
						Version = info.Metadata.Version.ToString(),
						Location = (info.Location ?? ""),
						State = state2,
						Reason = reason2,
						HardDeps = (from d in info.Dependencies
							where (d.Flags & 1) > 0
							select d.DependencyGUID).ToArray()
					};
					FindPackage(mod);
					mod.Search = string.Join(" ", mod.Name, mod.Guid, mod.Package).ToLowerInvariant();
					byGuid[gUID] = mod;
				}
			}
		}

		private static (State, string) NotLoaded(PluginInfo info)
		{
			string[] array = info.Processes.Select((BepInProcess p) => p.ProcessName).ToArray();
			if (array.Length != 0 && !array.Any((string p) => string.Equals(Path.GetFileNameWithoutExtension(p), Paths.ProcessName, StringComparison.OrdinalIgnoreCase)))
			{
				return (State.OtherProcess, string.Join(", ", array));
			}
			string text = Chainloader.DependencyErrors.FirstOrDefault((string line) => line.Contains(info.Metadata.GUID) || line.Contains("[" + info.Metadata.Name));
			if (text == null)
			{
				return (State.NotLoaded, "");
			}
			return (State.Failed, text);
		}

		private static HashSet<string> Protected(Dictionary<string, Mod> byGuid)
		{
			HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "gorduan.gc.kern" };
			Queue<string> queue = new Queue<string>(hashSet);
			while (queue.Count > 0)
			{
				if (!byGuid.TryGetValue(queue.Dequeue(), out Mod value))
				{
					continue;
				}
				string[] hardDeps = value.HardDeps;
				foreach (string item in hardDeps)
				{
					if (hashSet.Add(item))
					{
						queue.Enqueue(item);
					}
				}
			}
			return hashSet;
		}

		private static void FindPackage(Mod mod)
		{
			try
			{
				string text = Path.GetFullPath(Paths.PluginPath).TrimEnd('\\', '/');
				string text2 = ((mod.Location.Length > 0) ? Path.GetDirectoryName(Path.GetFullPath(mod.Location)) : null);
				while (text2 != null && text2.Length > text.Length && text2.StartsWith(text, StringComparison.OrdinalIgnoreCase))
				{
					string path = Path.Combine(text2, "manifest.json");
					if (File.Exists(path))
					{
						ManifestJson manifestJson = JsonUtility.FromJson<ManifestJson>(File.ReadAllText(path));
						mod.Package = Path.GetFileName(text2);
						mod.PackageName = manifestJson?.name ?? "";
						mod.PackageVersion = manifestJson?.version_number ?? "";
						int num = mod.Package.IndexOf('-');
						if (num > 0 && string.Equals(mod.Package.Substring(num + 1), mod.PackageName, StringComparison.OrdinalIgnoreCase))
						{
							mod.Namespace = mod.Package.Substring(0, num);
						}
						break;
					}
					text2 = Path.GetDirectoryName(text2);
				}
			}
			catch
			{
			}
		}

		internal static List<Mod> SetOff(Mod start, bool off)
		{
			List<Mod> list = new List<Mod>();
			if (!PatcherActive || start.Protected)
			{
				return list;
			}
			Queue<Mod> queue = new Queue<Mod>();
			queue.Enqueue(start);
			while (queue.Count > 0)
			{
				Mod mod = queue.Dequeue();
				if (mod.Protected || Wanted.Contains(mod.Guid) == off)
				{
					continue;
				}
				if (off)
				{
					Wanted.Add(mod.Guid);
				}
				else
				{
					Wanted.Remove(mod.Guid);
				}
				list.Add(mod);
				IEnumerable<Mod> enumerable2;
				if (!off)
				{
					IEnumerable<Mod> enumerable = (from other in mod.HardDeps.Select(Find)
						where other != null
						select other).Cast<Mod>();
					enumerable2 = enumerable;
				}
				else
				{
					enumerable2 = Catalog.Where((Mod other) => other.HardDeps.Contains<string>(mod.Guid, StringComparer.OrdinalIgnoreCase));
				}
				foreach (Mod item in enumerable2)
				{
					queue.Enqueue(item);
				}
			}
			if (!Save())
			{
				list.Clear();
			}
			return list;
		}

		internal static void AllOn()
		{
			Wanted.Clear();
			Save();
		}

		private static HashSet<string> WithDependents(IEnumerable<string> off)
		{
			HashSet<string> hashSet = new HashSet<string>(off, StringComparer.OrdinalIgnoreCase);
			bool flag = true;
			while (flag)
			{
				flag = false;
				foreach (Mod item in Catalog)
				{
					if (!item.Protected && !hashSet.Contains(item.Guid) && item.HardDeps.Any(hashSet.Contains))
					{
						hashSet.Add(item.Guid);
						flag = true;
					}
				}
			}
			return hashSet;
		}

		internal static List<Mod> Dependents(Mod mod)
		{
			return Catalog.Where((Mod other) => !IsWantedOff(other) && other.HardDeps.Contains<string>(mod.Guid, StringComparer.OrdinalIgnoreCase)).ToList();
		}

		internal static List<string> MissingDeps(Mod mod)
		{
			return (from guid in mod.HardDeps.Where(delegate(string guid)
				{
					Mod mod2 = Find(guid);
					return mod2 == null || IsWantedOff(mod2);
				})
				select Find(guid)?.Name ?? guid).ToList();
		}

		private static bool Save()
		{
			try
			{
				Directory.CreateDirectory(Path.GetDirectoryName(ListFile));
				List<string> list = new List<string> { "# Im Menü GC Option ausgeschaltete Mods, eine GUID je Zeile. Datei löschen = alle wieder an." };
				list.AddRange(Wanted.OrderBy<string, string>((string g) => g, StringComparer.OrdinalIgnoreCase));
				File.WriteAllLines(ListFile, list, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
				LastError = "";
				return true;
			}
			catch (Exception ex)
			{
				LastError = ex.Message;
				wanted = ReadList(ListFile);
				return false;
			}
		}

		private static HashSet<string> ReadList(string file)
		{
			HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			try
			{
				if (File.Exists(file))
				{
					string[] array = File.ReadAllLines(file);
					for (int i = 0; i < array.Length; i++)
					{
						string text = array[i].Trim();
						if (text.Length > 0 && !text.StartsWith("#", StringComparison.Ordinal))
						{
							hashSet.Add(text);
						}
					}
				}
			}
			catch (Exception ex)
			{
				Debug.LogWarning((object)("GC_Kern: " + file + " nicht lesbar: " + ex.Message));
			}
			return hashSet;
		}

		internal static List<Profile> Profiles()
		{
			List<Profile> list = new List<Profile>();
			if (!Directory.Exists(ProfileFolder))
			{
				return list;
			}
			foreach (string item in Directory.GetFiles(ProfileFolder, "*.txt").OrderBy<string, string>((string f) => f, StringComparer.OrdinalIgnoreCase))
			{
				list.Add(new Profile
				{
					Name = Path.GetFileNameWithoutExtension(item),
					File = item,
					Off = ReadList(item)
				});
			}
			return list;
		}

		private static HashSet<string> Known(IEnumerable<string> guids)
		{
			HashSet<string> hashSet = new HashSet<string>(from m in Catalog
				where !m.Protected
				select m.Guid, StringComparer.OrdinalIgnoreCase);
			hashSet.IntersectWith(guids);
			return hashSet;
		}

		internal static bool IsCurrent(Profile profile)
		{
			return Known(profile.Off).SetEquals(Known(Wanted));
		}

		internal static int MissingIn(Profile profile)
		{
			return profile.Off.Count((string guid) => Find(guid) == null);
		}

		internal static string CleanName(string name)
		{
			return new string((from c in name.Trim()
				where Array.IndexOf(Path.GetInvalidFileNameChars(), c) < 0
				select c).ToArray()).Trim().TrimEnd('.');
		}

		internal static bool SaveProfile(string name)
		{
			return WriteProfile(name, Wanted);
		}

		internal static bool WriteProfile(string name, IEnumerable<string> off)
		{
			string text = CleanName(name);
			if (text.Length == 0)
			{
				return false;
			}
			try
			{
				Directory.CreateDirectory(ProfileFolder);
				List<string> list = new List<string> { "# GC Option, Mod-Profil „" + text + "“: diese Mods aus, alle anderen an." };
				list.AddRange(off.OrderBy<string, string>((string g) => g, StringComparer.OrdinalIgnoreCase));
				File.WriteAllLines(Path.Combine(ProfileFolder, text + ".txt"), list, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
				LastError = "";
				return true;
			}
			catch (Exception ex)
			{
				LastError = ex.Message;
				return false;
			}
		}

		internal static string FreeName(string name)
		{
			HashSet<string> hashSet = new HashSet<string>(from p in Profiles()
				select p.Name, StringComparer.OrdinalIgnoreCase);
			string text = CleanName(name);
			string text2 = text;
			int num = 2;
			while (hashSet.Contains(text2))
			{
				text2 = $"{text} ({num})";
				num++;
			}
			return text2;
		}

		internal static int ApplyProfile(Profile profile)
		{
			HashSet<string> hashSet = new HashSet<string>(Wanted, StringComparer.OrdinalIgnoreCase);
			Wanted.Clear();
			Wanted.UnionWith(WithDependents(Known(profile.Off)));
			if (!Save())
			{
				wanted = hashSet;
				return -1;
			}
			return MissingIn(profile);
		}

		internal static bool DeleteProfile(Profile profile)
		{
			try
			{
				string text = Path.Combine(ProfileFolder, "geloescht");
				Directory.CreateDirectory(text);
				string destFileName = Path.Combine(text, Path.GetFileNameWithoutExtension(profile.File) + "-" + DateTime.Now.ToString("yyyyMMdd-HHmmss") + ".txt");
				File.Move(profile.File, destFileName);
				LastError = "";
				return true;
			}
			catch (Exception ex)
			{
				LastError = ex.Message;
				return false;
			}
		}

		internal static string ProfileText(Profile profile)
		{
			return "# GC Option, Mod-Profil „" + profile.Name + "“\n" + string.Join("\n", profile.Off.OrderBy<string, string>((string g) => g, StringComparer.OrdinalIgnoreCase)) + "\n";
		}

		internal static HashSet<string> ParseGuids(string text)
		{
			HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			string[] array = text.Replace("\r", "").Split('\n');
			for (int i = 0; i < array.Length; i++)
			{
				string text2 = array[i].Trim();
				if (text2.Length > 0 && !text2.StartsWith("#", StringComparison.Ordinal) && text2.IndexOf(' ') < 0)
				{
					hashSet.Add(text2);
				}
			}
			return hashSet;
		}

		private static void ImportModMenu()
		{
			try
			{
				string text = Path.Combine(Paths.ConfigPath, "ModMenu");
				if (!Directory.Exists(text))
				{
					return;
				}
				string text2 = Path.Combine(text, "disabled.txt");
				if (File.Exists(text2) && !File.Exists(ListFile))
				{
					Directory.CreateDirectory(Path.GetDirectoryName(ListFile));
					File.Copy(text2, ListFile);
					Debug.Log((object)"GC_Kern: ausgeschaltete Mods aus Mod Menu übernommen.");
				}
				string path = Path.Combine(text, "profiles");
				if (Directory.Exists(path) && !Directory.Exists(ProfileFolder))
				{
					Directory.CreateDirectory(ProfileFolder);
					string[] files = Directory.GetFiles(path, "*.txt");
					foreach (string text3 in files)
					{
						File.Copy(text3, Path.Combine(ProfileFolder, Path.GetFileName(text3)));
					}
					Debug.Log((object)"GC_Kern: Profile aus Mod Menu übernommen.");
				}
			}
			catch (Exception ex)
			{
				Debug.LogWarning((object)("GC_Kern: Mod Menu nicht übernommen: " + ex.Message));
			}
		}
	}
	[HarmonyPatch(typeof(Ragdoll), "Awake")]
	internal static class RagdollScalePatch
	{
		private static void Postfix(Ragdoll __instance)
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			ZNetView component = ((Component)__instance).GetComponent<ZNetView>();
			if (!((Object)(object)component == (Object)null) && component.IsValid())
			{
				float num = component.GetZDO().GetFloat(GcKeys.RagdollScaleHash, 0f);
				if (num > 0f)
				{
					((Component)__instance).transform.localScale = Vector3.one * num;
				}
			}
		}
	}
	internal static class VPlusOptions
	{
		private static ConfigFile? config;

		private static GcMenu? menu;

		private static bool dirty;

		private static MethodInfo? reapply;

		private static PropertyInfo? readyForEdit;

		internal static void Register(GcMenu gcMenu)
		{
			if (!Chainloader.PluginInfos.TryGetValue("org.bepinex.plugins.valheim_plus", out var value) || (Object)(object)value.Instance == (Object)null)
			{
				return;
			}
			menu = gcMenu;
			config = value.Instance.Config;
			reapply = AccessTools.Method(AccessTools.TypeByName("ValheimPlus.Configurations.BepInExConfig"), "ReapplyPatches", new Type[1] { typeof(string) }, (Type[])null);
			readyForEdit = AccessTools.Property(AccessTools.TypeByName("ValheimPlus.Configurations.ConfigurationManagerWatcher"), "ReadyForEdit");
			if (reapply == null)
			{
				KernPlugin.Log.LogWarning((object)"ValheimPlus: BepInExConfig.ReapplyPatches nicht gefunden. Änderungen im Menü wirken erst nach dem nächsten Start.");
			}
			config.SettingChanged += delegate(object _, SettingChangedEventArgs e)
			{
				if ((Object)(object)ZNet.instance == (Object)null)
				{
					dirty = true;
				}
				object obj;
				if (e == null)
				{
					obj = null;
				}
				else
				{
					ConfigEntryBase changedSetting = e.ChangedSetting;
					obj = ((changedSetting != null) ? changedSetting.Definition.Key : null);
				}
				if ((string?)obj == "enabled")
				{
					Build();
				}
			};
			Build();
			gcMenu.Closed += OnClosed;
		}

		private static void Build()
		{
			if (config == null || menu == null)
			{
				return;
			}
			GcMenuSection gcMenuSection = menu.Section("gc_vplus", 60, (string key) => GcLoc.Get("gck_vplus_" + key), "");
			int num = 0;
			int num2 = 0;
			foreach (IGrouping<string, ConfigDefinition> item in (from d in config.Keys
				group d by d.Section).OrderBy<IGrouping<string, ConfigDefinition>, string>((IGrouping<string, ConfigDefinition> g) => g.Key, StringComparer.OrdinalIgnoreCase))
			{
				List<ConfigEntryBase> list = item.Select((ConfigDefinition d) => config[d]).Where(Supported).ToList();
				if (list.Count == 0)
				{
					continue;
				}
				Dictionary<string, string> descriptions = list.ToDictionary<ConfigEntryBase, string, string>((ConfigEntryBase e) => e.Definition.Key, delegate(ConfigEntryBase e)
				{
					ConfigDescription description = e.Description;
					return ((description != null) ? description.Description : null) ?? "";
				}, StringComparer.Ordinal);
				string title = item.Key;
				GcMenuGroup gcMenuGroup = gcMenuSection.Group("vplus_" + item.Key, () => title, (string key) => Text(key, descriptions), "");
				ConfigEntryBase enabled = ((IEnumerable<ConfigEntryBase>)list).FirstOrDefault((Func<ConfigEntryBase, bool>)((ConfigEntryBase e) => e.Definition.Key == "enabled" && e.SettingType == typeof(bool)));
				if (enabled != null)
				{
					gcMenuGroup.Main("enabled", enabled, null, "", null, 0.0, "", () => (Object)(object)ZNet.instance == (Object)null || IsOn(enabled), Locked);
					num2++;
					if (!IsOn(enabled))
					{
						num++;
						continue;
					}
				}
				foreach (ConfigEntryBase item2 in list)
				{
					if (item2 != enabled)
					{
						if (enabled != null)
						{
							gcMenuGroup.Sub(item2.Definition.Key, item2, 0.0, "", null, "", null, Locked);
						}
						else
						{
							gcMenuGroup.Main(item2.Definition.Key, item2, null, "", null, 0.0, "", null, Locked);
						}
						num2++;
					}
				}
				num++;
			}
			KernPlugin.Log.LogDebug((object)$"ValheimPlus im Menü GC Option: {num} Abschnitte, {num2} Zeilen.");
		}

		private static bool IsOn(ConfigEntryBase entry)
		{
			object boxedValue = entry.BoxedValue;
			bool flag = default(bool);
			int num;
			if (boxedValue is bool)
			{
				flag = (bool)boxedValue;
				num = 1;
			}
			else
			{
				num = 0;
			}
			return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
		}

		private static bool Locked()
		{
			if ((Object)(object)ZNet.instance != (Object)null)
			{
				return true;
			}
			try
			{
				return readyForEdit != null && readyForEdit.GetValue(null, null) is bool flag && !flag;
			}
			catch
			{
				return false;
			}
		}

		private static bool Supported(ConfigEntryBase entry)
		{
			Type settingType = entry.SettingType;
			if (settingType.IsEnum)
			{
				return !settingType.IsDefined(typeof(FlagsAttribute), inherit: false);
			}
			if (!(settingType == typeof(bool)) && !(settingType == typeof(int)) && !(settingType == typeof(float)) && !(settingType == typeof(KeyboardShortcut)))
			{
				return settingType == typeof(KeyCode);
			}
			return true;
		}

		private static string Text(string key, Dictionary<string, string> descriptions)
		{
			if (key.StartsWith("t_", StringComparison.Ordinal) && key.EndsWith("_name", StringComparison.Ordinal))
			{
				return key.Substring(2, key.Length - 7);
			}
			if (key.StartsWith("t_", StringComparison.Ordinal) && key.EndsWith("_desc", StringComparison.Ordinal))
			{
				string key2 = key.Substring(2, key.Length - 7);
				if (!descriptions.TryGetValue(key2, out string value))
				{
					return "";
				}
				return FirstSentences(value);
			}
			if (key.StartsWith("enum_", StringComparison.Ordinal))
			{
				return key.Substring(5);
			}
			return key;
		}

		private static string FirstSentences(string text)
		{
			string text2 = text.Replace("\r", " ").Replace("\n", " ").Trim();
			int num = -1;
			int i = 0;
			int num2 = 0;
			for (; i < text2.Length; i++)
			{
				if (text2[i] == '.' && (i + 1 == text2.Length || text2[i + 1] == ' ') && ++num2 == 2)
				{
					num = i + 1;
					break;
				}
			}
			if (num <= 0)
			{
				return text2;
			}
			return text2.Substring(0, num);
		}

		private static void OnClosed()
		{
			if (!dirty || (Object)(object)ZNet.instance != (Object)null)
			{
				return;
			}
			dirty = false;
			try
			{
				reapply?.Invoke(null, new object[1] { "Einstellungen in GC Option geändert" });
			}
			catch (Exception ex)
			{
				KernPlugin.Log.LogWarning((object)("ValheimPlus konnte die Änderungen nicht neu anwenden: " + ex.Message));
			}
		}
	}
}
namespace GC.Kern.ModCheck
{
	internal static class Plugin
	{
		public const string Guid = "gorduan.gc_modcheck";

		public const string ModName = "GC_ModCheck";

		public const string Version = "1.0.0";

		internal const string Rpc = "GC_Kern_ServerInfo_v1";

		internal static ManualLogSource Log = null;

		internal static ConfigEntry<bool> Enabled = null;

		internal static ConfigEntry<bool> ShowOnFailure = null;

		internal static ConfigEntry<bool> ShowOnJoinIfMissing = null;

		internal static ConfigEntry<string> HiddenOnServer = null;

		internal static ConfigEntry<string> IgnoreClientOnly = null;

		internal static List<ModInfo>? ServerMods;

		internal static Report? LastReport;

		internal static HashSet<string> OptionalOnServer = new HashSet<string>(StringComparer.Ordinal);

		internal static bool Blocked => Chainloader.PluginInfos.ContainsKey("gorduan.gc_modcheck");

		internal static bool On
		{
			get
			{
				if (Enabled.Value)
				{
					return !Blocked;
				}
				return false;
			}
		}

		internal static void Init(ConfigFile Config)
		{
			Log = KernPlugin.Log;
			Enabled = Gc.Bind(Config, "5 - ModCheck", "Enabled", value: true, "Funktion ein- oder ausschalten (auch im Menü GC Option).");
			ShowOnFailure = Gc.Bind(Config, "5 - ModCheck", "ShowOnFailedConnection", value: true, "Nach einer fehlgeschlagenen Verbindung das Vergleichsfenster im Hauptmenü zeigen.");
			ShowOnJoinIfMissing = Gc.Bind(Config, "5 - ModCheck", "ShowOnJoinIfMissing", value: true, "Nach erfolgreichem Beitritt das Fenster zeigen, wenn dir Mods des Servers fehlen.");
			HiddenOnServer = Gc.Bind(Config, "5 - ModCheck - Server", "HiddenGuids", "", "Nur Server: kommagetrennte Plugin-GUIDs, die nicht an Spieler gemeldet werden (reine Server-Mods).");
			IgnoreClientOnly = Gc.Bind(Config, "5 - ModCheck - Client", "ClientOnlyGuids", "", "Kommagetrennte GUIDs eigener Client-Mods, die nicht als 'nur bei dir' gelistet werden.");
			CommandManager.Instance.AddConsoleCommand((ConsoleCommand)(object)new ConsoleCommand());
		}

		internal static List<ModInfo> LocalMods()
		{
			List<ModInfo> list = new List<ModInfo>();
			foreach (PluginInfo value in Chainloader.PluginInfos.Values)
			{
				string package = "";
				try
				{
					string directoryName = Path.GetDirectoryName(value.Location);
					string text = ((directoryName != null) ? Path.GetFileName(directoryName) : null);
					if (text != null && !text.Equals("plugins", StringComparison.OrdinalIgnoreCase))
					{
						package = text;
					}
				}
				catch
				{
				}
				list.Add(new ModInfo(value.Metadata.GUID, value.Metadata.Name, value.Metadata.Version.ToString(), package));
			}
			return list.OrderBy<ModInfo, string>((ModInfo mod) => mod.Name, StringComparer.OrdinalIgnoreCase).ToList();
		}

		internal static ZPackage BuildPackage()
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Expected O, but got Unknown
			HashSet<string> hidden = Gc.ParseList(HiddenOnServer.Value);
			List<ModInfo> list = (from mod in LocalMods()
				where IsGc(mod.Guid) || (On && !hidden.Contains(mod.Guid))
				select mod).ToList();
			ZPackage val = new ZPackage();
			val.Write(list.Count);
			foreach (ModInfo item in list)
			{
				val.Write(item.Guid);
				val.Write(item.Name);
				val.Write(item.Version);
				val.Write(item.Package);
			}
			val.Write(Math.Max(1, GcKern.HighestLocalStufe));
			val.Write(On);
			val.Write(GcKern.Modules.Count);
			foreach (GcModule module in GcKern.Modules)
			{
				val.Write(module.Info.Guid);
				val.Write((int)module.Info.Seite);
			}
			return val;
		}

		internal static bool IsGc(string guid)
		{
			if (!(guid == "gorduan.gc.kern"))
			{
				return guid.StartsWith("gorduan.gc.", StringComparison.Ordinal);
			}
			return true;
		}

		internal static void Receive(ZPackage pkg)
		{
			List<ModInfo> list = new List<ModInfo>();
			int num = pkg.ReadInt();
			for (int i = 0; i < num; i++)
			{
				list.Add(new ModInfo(pkg.ReadString(), pkg.ReadString(), pkg.ReadString(), pkg.ReadString()));
			}
			int num2 = 5;
			bool flag = true;
			HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
			try
			{
				num2 = pkg.ReadInt();
				flag = pkg.ReadBool();
				int num3 = pkg.ReadInt();
				for (int j = 0; j < num3; j++)
				{
					string item = pkg.ReadString();
					if (pkg.ReadInt() == 0)
					{
						hashSet.Add(item);
					}
				}
			}
			catch
			{
			}
			OptionalOnServer = hashSet;
			GcServerInfo gcServerInfo = new GcServerInfo
			{
				Stufe = num2
			};
			foreach (ModInfo item2 in list)
			{
				gcServerInfo.Guids.Add(item2.Guid);
			}
			GcKern.Server = gcServerInfo;
			Log.LogInfo((object)$"Server: {list.Count} Mods, Stufe {num2} ({GcKern.StufeName(num2)}).");
			ServerMods = list;
			if (!On || !flag)
			{
				return;
			}
			HashSet<string> ignoreClientOnly = Gc.ParseList(IgnoreClientOnly.Value);
			LastReport = Report.Create(list, LocalMods(), ignoreClientOnly, hashSet);
			Log.LogInfo((object)$"Server meldet {list.Count} Mods. Fehlend: {LastReport.Missing.Count}, andere Version: {LastReport.Different.Count}, nur hier: {LastReport.ClientOnly.Count}.");
			string[] array = LastReport.ToText().Split('\n');
			foreach (string text in array)
			{
				if (text.Trim().Length > 0)
				{
					Log.LogInfo((object)text);
				}
			}
		}
	}
	internal sealed class ModInfo
	{
		public string Guid { get; }

		public string Name { get; }

		public string Version { get; }

		public string Package { get; }

		public ModInfo(string guid, string name, string version, string package)
		{
			Guid = guid;
			Name = name;
			Version = version;
			Package = package;
		}

		public string Describe()
		{
			if (Package.Length <= 0)
			{
				return Name + " " + Version + "  (" + Guid + ")";
			}
			return Name + " " + Version + "  [" + Package + "]";
		}
	}
	internal sealed class Report
	{
		public List<ModInfo> Missing = new List<ModInfo>();

		public List<(ModInfo Server, ModInfo Client)> Different = new List<(ModInfo, ModInfo)>();

		public List<ModInfo> ClientOnly = new List<ModInfo>();

		public bool HasProblems
		{
			get
			{
				if (Missing.Count <= 0)
				{
					return Different.Count > 0;
				}
				return true;
			}
		}

		public static Report Create(List<ModInfo> server, List<ModInfo> client, HashSet<string> ignoreClientOnly, HashSet<string> optional)
		{
			Report report = new Report();
			Dictionary<string, ModInfo> dictionary = (from mod in client
				group mod by mod.Guid).ToDictionary((IGrouping<string, ModInfo> group) => group.Key, (IGrouping<string, ModInfo> group) => group.First());
			Dictionary<string, ModInfo> dictionary2 = (from mod in server
				group mod by mod.Guid).ToDictionary((IGrouping<string, ModInfo> group) => group.Key, (IGrouping<string, ModInfo> group) => group.First());
			foreach (ModInfo item in server)
			{
				if (!optional.Contains(item.Guid))
				{
					if (!dictionary.TryGetValue(item.Guid, out var value))
					{
						report.Missing.Add(item);
					}
					else if (value.Version != item.Version)
					{
						report.Different.Add((item, value));
					}
				}
			}
			foreach (ModInfo item2 in client)
			{
				if (!dictionary2.ContainsKey(item2.Guid) && !ignoreClientOnly.Contains(item2.Guid))
				{
					report.ClientOnly.Add(item2);
				}
			}
			return report;
		}

		public string ToText()
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine(GcLoc.Get("gcm_title"));
			stringBuilder.AppendLine();
			stringBuilder.AppendLine(GcLoc.Get("gcm_missing", Missing.Count));
			foreach (ModInfo item in Missing)
			{
				stringBuilder.AppendLine("- " + item.Describe());
			}
			stringBuilder.AppendLine();
			stringBuilder.AppendLine(GcLoc.Get("gcm_different", Different.Count));
			foreach (var (modInfo, modInfo2) in Different)
			{
				stringBuilder.AppendLine("- " + GcLoc.Get("gcm_version_line", modInfo.Name, modInfo.Version, modInfo2.Version) + ((modInfo.Package.Length > 0) ? ("  [" + modInfo.Package + "]") : ""));
			}
			stringBuilder.AppendLine();
			stringBuilder.AppendLine(GcLoc.Get("gcm_client_only", ClientOnly.Count));
			foreach (ModInfo item2 in ClientOnly)
			{
				stringBuilder.AppendLine("- " + item2.Describe());
			}
			return stringBuilder.ToString();
		}
	}
	[HarmonyPatch(typeof(ZNet), "OnNewConnection")]
	internal static class NewConnectionPatch
	{
		private static void Postfix(ZNet __instance, ZNetPeer peer)
		{
			peer.m_rpc.Register<ZPackage>("GC_Kern_ServerInfo_v1", (Action<ZRpc, ZPackage>)delegate(ZRpc _, ZPackage pkg)
			{
				if (!ZNet.instance.IsServer())
				{
					Plugin.Receive(pkg);
				}
			});
			if (__instance.IsServer())
			{
				peer.m_rpc.Invoke("GC_Kern_ServerInfo_v1", new object[1] { Plugin.BuildPackage() });
			}
			else
			{
				Plugin.ServerMods = null;
				Plugin.LastReport = null;
				GcKern.Server = null;
			}
		}
	}
	[HarmonyPatch(typeof(ZNet), "OnDestroy")]
	internal static class ServerInfoResetPatch
	{
		private static void Postfix()
		{
			GcKern.Server = null;
		}
	}
	[HarmonyPatch(typeof(FejdStartup), "ShowConnectError")]
	internal static class ConnectErrorPatch
	{
		private static void Postfix()
		{
			if (Plugin.On && Plugin.ShowOnFailure.Value && Plugin.LastReport != null)
			{
				ReportWindow.Show(Plugin.LastReport);
			}
		}
	}
	[HarmonyPatch(typeof(Player), "OnSpawned")]
	internal static class SpawnPatch
	{
		private static bool shown;

		private static void Postfix(Player __instance)
		{
			if (!((Object)(object)__instance != (Object)(object)Player.m_localPlayer) && !shown && Plugin.On && Plugin.ShowOnJoinIfMissing.Value && Plugin.LastReport != null && Plugin.LastReport.Missing.Count > 0)
			{
				shown = true;
				ReportWindow.Show(Plugin.LastReport);
			}
		}
	}
	internal class ConsoleCommand : ConsoleCommand
	{
		public override string Name => "modcheck";

		public override string Help => "GC_Kern: Mod-Abgleich mit dem Server anzeigen";

		public override void Run(string[] args)
		{
			if (Plugin.LastReport == null)
			{
				Console.instance.Print("Keine Serverliste empfangen (Server ohne GC_Kern, Mod-Abgleich aus oder nicht verbunden).");
			}
			else
			{
				ReportWindow.Show(Plugin.LastReport);
			}
		}
	}
	internal static class ReportWindow
	{
		private static GameObject? window;

		private static Text? header;

		private static Text? body;

		private static string text = "";

		public static void Show(Report report)
		{
			if (!Gc.IsHeadless && GUIManager.Instance != null && !((Object)(object)GUIManager.CustomGUIFront == (Object)null))
			{
				Build();
				text = report.ToText();
				header.text = GcLoc.Get(report.HasProblems ? "gcm_header_problems" : "gcm_header_ok");
				body.text = Colorize(report);
				window.SetActive(true);
				window.transform.SetAsLastSibling();
				GUIManager.BlockInput(true);
			}
		}

		private static string Colorize(Report report)
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine("<color=#ff8866><b>" + GcLoc.Get("gcm_missing", report.Missing.Count) + "</b></color>");
			foreach (ModInfo item in report.Missing)
			{
				stringBuilder.AppendLine("  • " + item.Describe());
			}
			stringBuilder.AppendLine();
			stringBuilder.AppendLine("<color=#ffd166><b>" + GcLoc.Get("gcm_different", report.Different.Count) + "</b></color>");
			foreach (var (modInfo, modInfo2) in report.Different)
			{
				stringBuilder.AppendLine("  • " + GcLoc.Get("gcm_version_line", modInfo.Name, modInfo.Version, modInfo2.Version));
			}
			stringBuilder.AppendLine();
			stringBuilder.AppendLine("<color=#9fd3ff><b>" + GcLoc.Get("gcm_client_only", report.ClientOnly.Count) + "</b></color>");
			foreach (ModInfo item2 in report.ClientOnly)
			{
				stringBuilder.AppendLine("  • " + item2.Describe());
			}
			stringBuilder.AppendLine();
			stringBuilder.AppendLine("<i>" + GcLoc.Get("gcm_note") + "</i>");
			return stringBuilder.ToString();
		}

		private static void Build()
		{
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: 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_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: 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_0157: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)window != (Object)null)
			{
				return;
			}
			window = GcUi.CreateWindow("GC_ModCheck_Window", 900f, 620f, out header, out RectTransform inner);
			RectTransform val = GcUi.CreateScroll((Transform)(object)inner, 820f, 440f);
			body = GUIManager.Instance.CreateText("", (Transform)(object)val, new Vector2(0f, 1f), new Vector2(1f, 1f), Vector2.zero, GUIManager.Instance.AveriaSerifBold, 18, Color.white, true, Color.black, 780f, 0f, true).GetComponent<Text>();
			body.supportRichText = true;
			body.horizontalOverflow = (HorizontalWrapMode)0;
			body.verticalOverflow = (VerticalWrapMode)1;
			GcUi.CreateButton(window.transform, GcLoc.Get("gcm_copy"), new Vector2(0.5f, 0f), new Vector2(-130f, 44f), 240f, 44f, delegate
			{
				GUIUtility.systemCopyBuffer = text;
				Gc.Center(GcLoc.Get("gcm_copied"));
				if ((Object)(object)Player.m_localPlayer == (Object)null)
				{
					header.text = GcLoc.Get("gcm_copied");
				}
			});
			GcUi.CreateButton(window.transform, GcLoc.Get("gcm_close"), new Vector2(0.5f, 0f), new Vector2(130f, 44f), 200f, 44f, delegate
			{
				window.SetActive(false);
				GUIManager.BlockInput(false);
			});
		}
	}
}
namespace GC.Kern.Progression
{
	internal static class BossScaling
	{
		internal static readonly string[] BossKeys = new string[8] { "defeated_eikthyr", "defeated_gdking", "defeated_bonemass", "defeated_dragon", "defeated_goblinking", "defeated_queen", "defeated_fader", "defeated_frozenking_p3" };

		internal const string EpicLootGuid = "randyknapp.mods.epicloot";

		internal const float EpicLootPercent = 10f;

		internal static ConfigEntry<bool> Automatic = null;

		internal static ConfigEntry<float> PercentPerBoss = null;

		private static int count;

		private static float nextCount;

		private static Player? countedFor;

		private static float spawnedAt;

		private static bool announced;

		internal static int Count => count;

		internal static float Factor
		{
			get
			{
				if (!((Object)(object)Player.m_localPlayer != (Object)null))
				{
					return 1f;
				}
				return 1f + Percent() / 100f * (float)count;
			}
		}

		internal static bool OnGcServer
		{
			get
			{
				if ((Object)(object)ZNet.instance != (Object)null)
				{
					if (!ZNet.instance.IsServer())
					{
						return GcKern.Server != null;
					}
					return true;
				}
				return false;
			}
		}

		internal static bool Active
		{
			get
			{
				if (count > 0 && (Object)(object)Player.m_localPlayer != (Object)null && OnGcServer)
				{
					return Percent() > 0f;
				}
				return false;
			}
		}

		internal static void Init(ConfigFile config)
		{
			Automatic = Gc.BindSynced(config, "13 - Boss Progression", "Automatic", value: true, "Prozent je besiegtem Hauptboss automatisch: der höchste Ausgleichswert der laufenden GC-Mods (z. B. Tiere 5), mit EpicLoot mindestens 10, ohne erleichternde Mods 0 (wie Valheim). Aus: es gilt PercentPerBoss.");
			PercentPerBoss = Gc.BindSynced(config, "13 - Boss Progression", "PercentPerBoss", 5f, "Gegner und Bosse haben je besiegtem Hauptboss (Eikthyr, Ältester, Bonemass, Moder, Yagluth, Königin, Fader, Kall Mandefimbul) so viel Prozent mehr Leben und machen so viel mehr Schaden, addiert. Gilt nur, wenn Automatic aus ist. Es zählt der eigene Fortschritt jedes Spielers (mit World Advancement Progression seine privaten Schlüssel, mit Guilds_Progress samt Gilden-Fortschritt, ohne diese Mods die Welt).", (AcceptableValueBase?)(object)new AcceptableValueRange<float>(0f, 100f));
		}

		internal static float Percent()
		{
			if (!Automatic.Value)
			{
				return Mathf.Max(0f, PercentPerBoss.Value);
			}
			float num = (Chainloader.PluginInfos.ContainsKey("randyknapp.mods.epicloot") ? 10f : 0f);
			foreach (GcModule module in GcKern.Modules)
			{
				if (module.Info.Ausgleich > num && module.On)
				{
					num = module.Info.Ausgleich;
				}
			}
			return num;
		}

		internal static string Source()
		{
			if (!Automatic.Value)
			{
				return "";
			}
			float num = 0f;
			string result = "";
			if (Chainloader.PluginInfos.ContainsKey("randyknapp.mods.epicloot"))
			{
				num = 10f;
				result = "EpicLoot";
			}
			foreach (GcModule module in GcKern.Modules)
			{
				if (module.Info.Ausgleich > num && module.On)
				{
					num = module.Info.Ausgleich;
					string text = "gck_mod_" + module.Info.Id;
					string text2 = GcLoc.Get(text);
					result = ((text2 == text) ? module.Info.Name : text2);
				}
			}
			return result;
		}

		internal static bool IsFoe(Character? character)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: 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_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Invalid comparison between Unknown and I4
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Invalid comparison between Unknown and I4
			if ((Object)(object)character == (Object)null || character.IsPlayer() || character.IsTamed())
			{
				return false;
			}
			Faction faction = character.GetFaction();
			if ((int)faction != 0 && (int)faction != 11)
			{
				return (int)faction != 12;
			}
			return false;
		}

		internal static bool Ours(Character? character)
		{
			if ((Object)(object)character == (Object)null)
			{
				return false;
			}
			if ((Object)(object)character == (Object)(object)Player.m_localPlayer)
			{
				return true;
			}
			ZNetView nview = character.m_nview;
			if (!character.IsPlayer() && character.IsTamed() && (Object)(object)nview != (Object)null && nview.IsValid())
			{
				return nview.IsOwner();
			}
			return false;
		}

		private static int CountBosses()
		{
			ZoneSystem instance = ZoneSystem.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return 0;
			}
			int num = 0;
			string[] bossKeys = BossKeys;
			foreach (string text in bossKeys)
			{
				if (instance.GetGlobalKey(text))
				{
					num++;
				}
			}
			return num;
		}

		internal static void Tick()
		{
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null)
			{
				countedFor = null;
				count = 0;
				return;
			}
			float time = Time.time;
			if ((Object)(object)localPlayer != (Object)(object)countedFor)
			{
				countedFor = localPlayer;
				spawnedAt = time;
				announced = false;
				count = CountBosses();
				nextCount = time + 2f;
			}
			else
			{
				if (time < nextCount)
				{
					return;
				}
				nextCount = time + 2f;
				int num = CountBosses();
				bool flag = announced || time - spawnedAt > 15f;
				if (num > count && flag && OnGcServer)
				{
					float num2 = Percent();
					if (num2 > 0f)
					{
						string text = Mathf.RoundToInt(num2 * (float)(num - count)).ToString();
						string text2 = Mathf.RoundToInt(num2 * (float)num).ToString();
						MessageHud instance = MessageHud.instance;
						if (instance != null)
						{
							instance.ShowMessage((MessageType)2, GcLoc.Get("gcbp_stronger", text, text2), 0, (Sprite)null, false, true);
						}
					}
				}
				announced = flag;
				count = num;
			}
		}

		internal static string Note()
		{
			string text = Mathf.RoundToInt(Percent()).ToString();
			string text2 = (((Object)(object)Player.m_localPlayer != (Object)null) ? GcLoc.Get("gcsv_bossscale_now", text, count.ToString(), Mathf.RoundToInt(Percent() * (float)count).ToString()) : GcLoc.Get("gcsv_bossscale_rule", text));
			string text3 = Source();
			if (text3.Length <= 0)
			{
				return text2;
			}
			return text2 + " " + GcLoc.Get("gck_bossscale_source", text3);
		}
	}
	[HarmonyPatch(typeof(Character), "Damage")]
	internal static class BossScalingDealtPatch
	{
		private static void Prefix(Character __instance, HitData hit)
		{
			if (hit != null && BossScaling.Active && BossScaling.IsFoe(__instance))
			{
				Character attacker = hit.GetAttacker();
				if ((Object)(object)attacker != (Object)null && (Object)(object)attacker != (Object)(object)__instance && BossScaling.Ours(attacker))
				{
					hit.ApplyModifier(1f / BossScaling.Factor);
				}
			}
		}
	}
	[HarmonyPatch(typeof(Character), "RPC_Damage")]
	internal static class BossScalingTakenPatch
	{
		private static void Prefix(Character __instance, HitData hit)
		{
			if (hit != null && BossScaling.Active && BossScaling.Ours(__instance))
			{
				Character attacker = hit.GetAttacker();
				if ((Object)(object)attacker != (Object)null && (Object)(object)attacker != (Object)(object)__instance && BossScaling.IsFoe(attacker))
				{
					hit.ApplyModifier(BossScaling.Factor);
				}
			}
		}
	}
}
namespace GC.Shared
{
	public enum GcTaming
	{
		Normal,
		Blocked,
		SimpleAI
	}
	public static class GcHooks
	{
		public static Func<bool>? DungeonMapLargeOpen;

		public static Func<bool>? PortalGuardActive;

		public static Action<TeleportWorld, string>? PortalRename;

		public static Func<int, float>? SpeciesMaxLevel;

		public static Func<float>? SpeciesEffectCap;

		public static Func<Character, Character, bool>? SpeciesEnemyForOrder;

		public static Func<Character, GcTaming>? SpeciesTaming;

		public static Func<bool>? CourierOn;

		public static Func<Player, ItemData, bool>? CourierWhistle;

		public static Func<string>? CourierTooltip;

		public static Func<Procreation, float?>? MateLevel;

		public static int RedCraft;

		public static bool IsDungeonMapLargeOpen()
		{
			try
			{
				return DungeonMapLargeOpen != null && DungeonMapLargeOpen();
			}
			catch
			{
				return false;
			}
		}

		public static bool IsPortalGuardActive()
		{
			try
			{
				return PortalGuardActive != null && PortalGuardActive();
			}
			catch
			{
				return false;
			}
		}

		public static bool IsCourierOn()
		{
			try
			{
				return CourierOn != null && CourierOn();
			}
			catch
			{
				return false;
			}
		}

		public static float? GetMateLevel(Procreation parent)
		{
			try
			{
				return MateLevel?.Invoke(parent);
			}
			catch
			{
				return null;
			}
		}

		public static void Leave(ref int depth)
		{
			depth = Math.Max(0, depth - 1);
		}

		public static int CountUsable(Inventory inventory, string name)
		{
			RedCraft++;
			try
			{
				return inventory.CountItems(name, -1, true);
			}
			finally
			{
				Leave(ref RedCraft);
			}
		}
	}
	public enum GcSeite
	{
		Spieler,
		ServerOptional,
		Alle
	}
	public sealed class GcModInfo
	{
		public string Id = "";

		public string Guid = "";

		public string Name = "";

		public string Version = "";

		public int Stufe = 1;

		public GcSeite Seite;

		public string[] Braucht = Array.Empty<string>();

		public string[] Partner = Array.Empty<string>();

		public string[] Fremd = Array.Empty<string>();

		public string AlteConfig = "";

		public float Ausgleich;
	}
	public sealed class GcServerInfo
	{
		public int Stufe;

		public HashSet<string> Guids = new HashSet<string>(StringComparer.Ordinal);
	}
	public sealed class GcModule
	{
		private readonly HashSet<string> reported = new HashSet<string>();

		public Func<bool>? IsOn;

		public BaseUnityPlugin Plugin { get; }

		public ManualLogSource Log { get; }

		public GcModInfo Info { get; }

		public ConfigFile Config => Plugin.Config;

		public bool FreshConfig { get; }

		public bool Allowed => GcKern.Allowed(Info);

		public bool OnServer => GcKern.ServerHas(Info.Guid);

		public bool On
		{
			get
			{
				try
				{
					return Allowed && (IsOn == null || IsOn());
				}
				catch
				{
					return false;
				}
			}
		}

		internal GcModule(BaseUnityPlugin plugin, ManualLogSource log, GcModInfo info, bool freshConfig)
		{
			Plugin = plugin;
			Log = log;
			Info = info;
			FreshConfig = freshConfig;
		}

		public GcMenuGroup MenuGroup(Func<string, string> text, string? titleKey = null)
		{
			return GcKern.MenuGroup(this, text, titleKey);
		}

		public void TakeOverOldSettings()
		{
			if (!FreshConfig || Info.AlteConfig.Length == 0)
			{
				return;
			}
			string[] array = Info.AlteConfig.Split(new char[2] { ';', ',' }, StringSplitOptions.RemoveEmptyEntries);
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				string path = Path.Combine(Paths.ConfigPath, text);
				if (!File.Exists(path))
				{
					continue;
				}
				bool saveOnConfigSet = Config.SaveOnConfigSet;
				try
				{
					Dictionary<string, string> dictionary = GcCfgFile.Read(path);
					int num = 0;
					Config.SaveOnConfigSet = false;
					foreach (ConfigDefinition item in Config.Keys.ToList())
					{
						if (dictionary.TryGetValue(GcCfgFile.Key(item.Section, item.Key), out var value))
						{
							ConfigEntryBase obj = Config[item];
							string serializedValue = obj.GetSerializedValue();
							obj.SetSerializedValue(value);
							if (obj.GetSerializedValue() != serializedValue)
							{
								num++;
							}
						}
					}
					Config.SaveOnConfigSet = saveOnConfigSet;
					Config.Save();
					Log.LogInfo((object)$"Einstellungen aus {text} übernommen: {num} Werte weichen vom Standard ab.");
				}
				catch (Exception ex)
				{
					Config.SaveOnConfigSet = saveOnConfigSet;
					Log.LogWarning((object)("Einstellungen aus " + text + " konnten nicht übernommen werden: " + ex.Message));
				}
			}
		}

		public void Guard(string part, Action action)
		{
			try
			{
				action();
			}
			catch (Exception arg)
			{
				if (reported.Add(part))
				{
					Log.LogWarning((object)$"{part}: {arg}");
				}
			}
		}

		public bool Has(string id)
		{
			return GcKern.ModLoaded(id);
		}
	}
	public static class GcKern
	{
		public const string Guid = "gorduan.gc.kern";

		public const string Name = "GC_Kern";

		public const string Version = "1.1.1";

		public const string GuidPrefix = "gorduan.gc.";

		public static readonly string[] OldPackages = new string[3] { "gorduan.gc_server", "gorduan.gc_client", "gorduan.valheimplusplus" };

		private static readonly List<GcModule> modules = new List<GcModule>();

		private static readonly Dictionary<int, GcMenuSection> sections = new Dictionary<int, GcMenuSection>();

		internal static ConfigEntry<int>? StufeSetting;

		public static IReadOnlyList<GcModule> Modules => modules;

		public static GcServerInfo? Server { get; internal set; }

		public static int HighestLocalStufe
		{
			get
			{
				if (modules.Count != 0)
				{
					return modules.Max((GcModule m) => m.Info.Stufe);
				}
				return 0;
			}
		}

		public static int ServerStufe
		{
			get
			{
				ZNet instance = ZNet.instance;
				if ((Object)(object)instance == (Object)null || instance.IsServer() || Server == null)
				{
					return 6;
				}
				int num = StufeSetting?.Value ?? 0;
				if (num <= 0)
				{
					return Math.Max(1, Server.Stufe);
				}
				return num;
			}
		}

		public static GcModule Register(BaseUnityPlugin plugin, ManualLogSource log, GcModInfo info)
		{
			bool freshConfig = !File.Exists(plugin.Config.ConfigFilePath);
			GcModule gcModule = new GcModule(plugin, log, info, freshConfig);
			modules.Add(gcModule);
			if (((object)plugin).GetType().Assembly != typeof(GcKern).Assembly)
			{
				GcLoc.AddAssembly(((object)plugin).GetType().Assembly, info.Name, log);
			}
			log.LogInfo((object)$"{info.Name} {info.Version} angemeldet: Stufe {info.Stufe}, {SeiteText(info.Seite)}.");
			return gcModule;
		}

		public static bool IsLoaded(string guid)
		{
			return Chainloader.PluginInfos.ContainsKey(guid);
		}

		public static bool ModLoaded(string id)
		{
			return IsLoaded("gorduan.gc." + id);
		}

		internal static bool Allowed(GcModInfo info)
		{
			if (info.Seite == GcSeite.Alle || info.Stufe <= 0)
			{
				return true;
			}
			if (info.Seite == GcSeite.ServerOptional && ServerHas(info.Guid))
			{
				return true;
			}
			return info.Stufe <= ServerStufe;
		}

		internal static bool ServerHas(string guid)
		{
			ZNet instance = ZNet.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return false;
			}
			if (instance.IsServer())
			{
				return IsLoaded(guid);
			}
			if (Server != null)
			{
				return Server.Guids.Contains(guid);
			}
			return false;
		}

		public static string StufeName(int stufe)
		{
			return GcLoc.Get("gck_stufe" + stufe);
		}

		public static string SeiteText(GcSeite seite)
		{
			return seite switch
			{
				GcSeite.Spieler => "nur Spieler", 
				GcSeite.ServerOptional => "Spieler, Server optional", 
				_ => "Server und alle Spieler", 
			};
		}

		internal static GcMenuSection Section(int stufe)
		{
			if (!sections.TryGetValue(stufe, out GcMenuSection value))
			{
				string id = ((stufe == 0) ? "gc_kern" : ("gc_stufe" + stufe));
				int order = ((stufe == 0) ? 90 : (stufe * 10));
				value = KernPlugin.Menu.Section(id, order, (string key) => GcLoc.Get((stufe == 0) ? ("gck_kern_" + key) : ("gck_stufe" + stufe + "_" + key)), "");
				sections[stufe] = value;
			}
			return value;
		}

		internal static GcMenuGroup MenuGroup(GcModule module, Func<string, string> text, string? ownTitleKey = null)
		{
			GcModInfo info = module.Info;
			string titleKey = ownTitleKey ?? ("gck_mod_" + info.Id);
			Func<string> title = delegate
			{
				string text2 = GcLoc.Get(titleKey);
				return (!(text2 == titleKey)) ? text2 : info.Name;
			};
			return Section(info.Stufe).Group(titleKey, title, text, info.Guid, () => module.Allowed);
		}
	}
	internal static class GcCfgFile
	{
		public static string Key(string section, string key)
		{
			return section.Trim() + "\u0001" + key.Trim();
		}

		public static Dictionary<string, string> Read(string path)
		{
			Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.Ordinal);
			string section = "";
			string[] array = File.ReadAllLines(path);
			for (int i = 0; i < array.Length; i++)
			{
				string text = array[i].Trim();
				if (text.Length == 0 || text.StartsWith("#", StringComparison.Ordinal))
				{
					continue;
				}
				if (text.StartsWith("[", StringComparison.Ordinal) && text.EndsWith("]", StringComparison.Ordinal))
				{
					section = text.Substring(1, text.Length - 2).Trim();
					continue;
				}
				int num = text.IndexOf('=');
				if (num > 0)
				{
					dictionary[Key(section, text.Substring(0, num))] = text.Substring(num + 1).Trim();
				}
			}
			return dictionary;
		}
	}
	[BepInPlugin("gorduan.gc.kern", "GC_Kern", "1.1.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInIncompatibility("gorduan.gc_server")]
	[BepInIncompatibility("gorduan.gc_client")]
	[BepInIncompatibility("gorduan.valheimplusplus")]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public class KernPlugin : BaseUnityPlugin
	{
		internal static ManualLogSource Log;

		internal static GcModule Module;

		internal static GcMenu Menu;

		internal static ConfigEntry<KeyboardShortcut>? MenuKey;

		private void Awake()
		{
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			Log = ((BaseUnityPlugin)this).Logger;
			Module = GcKern.Register((BaseUnityPlugin)(object)this, ((BaseUnityPlugin)this).Logger, new GcModInfo
			{
				Id = "kern",
				Guid = "gorduan.gc.kern",
				Name = "GC_Kern",
				Version = "1.1.1",
				Stufe = 0,
				Seite = GcSeite.Spieler,
				AlteConfig = "gorduan.gc_server.cfg"
			});
			GcLoc.Init("GC_Kern", ((BaseUnityPlugin)this).Logger);
			GcKern.StufeSetting = Gc.BindSynced(((BaseUnityPlugin)this).Config, "1 - Server", "SpielerStufe", 0, "Bis zu welcher Stufe Mods, die nur beim Spieler laufen, auf diesem Server wirken dürfen (1 Sehr Vanilla, 2 Vanilla, 3 Vanilla+, 4 GC leicht, 5 GC). 0 = automatisch: die höchste Stufe der GC-Mods auf dem Server.", (AcceptableValueBase?)(object)new AcceptableValueRange<int>(0, 5));
			Plugin.Init(((BaseUnityPlugin)this).Config);
			BossScaling.Init(((BaseUnityPlugin)this).Config);
			MenuKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("2 - Menue", "Taste", KeyboardShortcut.Empty, "Taste für das Menü GC Option, im Hauptmenü und im Spiel (nicht im Chat oder in der Konsole). Leer = nur über den Knopf.");
			Module.TakeOverOldSettings();
			new Harmony("gorduan.gc.kern").PatchAll(typeof(KernPlugin).Assembly);
			Menu = new GcMenu(Log, GcMenu.UiText(""));
			Module.MenuGroup(KernText).Main("stufe", (ConfigEntryBase)(object)GcKern.StufeSetting, null, "", StufeNote, 1.0).Main("bossscale", (ConfigEntryBase)(object)BossScaling.Automatic, null, "", BossScaling.Note)
				.Sub("bossscale_percent", (ConfigEntryBase)(object)BossScaling.PercentPerBoss, 1.0, " %")
				.Main("modcheck", (ConfigEntryBase)(object)Plugin.Enabled, () => Plugin.Blocked, "GC_ModCheck")
				.Sub("modcheck_failure", (ConfigEntryBase)(object)Plugin.ShowOnFailure)
				.Sub("modcheck_join", (ConfigEntryBase)(object)Plugin.ShowOnJoinIfMissing)
				.Main("menukey", (ConfigEntryBase)(object)MenuKey);
			Log.LogInfo((object)"GC_Kern 1.1.1 geladen.");
		}

		private static string KernText(string key)
		{
			string text = GcLoc.Get("gck_" + key);
			if (!(text != "gck_" + key))
			{
				return GcLoc.Get("gcsv_" + key);
			}
			return text;
		}

		private static string StufeNote()
		{
			ZNet instance = ZNet.instance;
			int num;
			if ((Object)(object)instance != (Object)null && !instance.IsServer())
			{
				num = GcKern.ServerStufe;
			}
			else
			{
				int num2 = GcKern.StufeSetting?.Value ?? 0;
				num = ((num2 > 0) ? num2 : Math.Max(1, GcKern.HighestLocalStufe));
			}
			if (num < 6)
			{
				return GcLoc.Get("gck_stufe_now", num, GcKern.StufeName(num));
			}
			return GcLoc.Get("gck_stufe_now_free");
		}

		private void Start()
		{
			Menu.LogBlocked();
			if (!Gc.IsHeadless)
			{
				Module.Guard("ValheimPlus im Menü", delegate
				{
					VPlusOptions.Register(Menu);
				});
			}
			foreach (GcModule module in GcKern.Modules)
			{
				string[] partner = module.Info.Partner;
				foreach (string text in partner)
				{
					if (!GcKern.ModLoaded(text))
					{
						Log.LogInfo((object)(module.Info.Name + ": Partner gorduan.gc." + text + " fehlt, die Mod läuft ohne dessen Teil."));
					}
				}
			}
		}

		private void Update()
		{
			if (!Gc.IsHeadless)
			{
				Module.Guard("Menü-Taste", delegate
				{
					Menu.CheckHotkey();
				});
				Module.Guard("Menü", delegate
				{
					Menu.Update();
				});
				Module.Guard("Boss-Fortschritt", BossScaling.Tick);
			}
		}
	}
	public sealed class GcMenuSection
	{
		private readonly List<object> groups;

		private readonly Func<string, string> text;

		internal GcMenuSection(Dictionary<string, object> data, Func<string, string> text)
		{
			groups = (List<object>)data["groups"];
			this.text = text;
		}

		public GcMenuGroup Group(string id)
		{
			List<object> list = new List<object>();
			Func<string, string> t = text;
			groups.Add(new Dictionary<string, object>
			{
				["id"] = id,
				["title"] = (Func<string>)(() => t("group_" + id)),
				["items"] = list
			});
			return new GcMenuGroup(list, text);
		}

		public GcMenuGroup Group(string id, Func<string> title, Func<string, string> groupText, string guid, Func<bool>? allowed = null)
		{
			List<object> list = new List<object>();
			Dictionary<string, object> dictionary = new Dictionary<string, object>
			{
				["id"] = id,
				["title"] = title,
				["items"] = list,
				["guid"] = guid
			};
			if (allowed != null)
			{
				dictionary["allowed"] = allowed;
			}
			groups.Add(dictionary);
			return new GcMenuGroup(list, groupText);
		}
	}
	public sealed class GcMenuGroup
	{
		private readonly List<object> items;

		private readonly Func<string, string> text;

		internal GcMenuGroup(List<object> items, Func<string, string> text)
		{
			this.items = items;
			this.text = text;
		}

		public GcMenuGroup Main(string key, ConfigEntryBase entry, Func<bool>? blocked = null, string oldMod = "", Func<string>? note = null, double step = 0.0, string unit = "", Func<bool>? requires = null, Func<bool>? locked = null)
		{
			return Add(key, entry, 0, blocked, oldMod, note, step, unit, requires, locked);
		}

		public GcMenuGroup Sub(string key, ConfigEntryBase entry, double step = 0.0, string unit = "", Func<bool>? blocked = null, string oldMod = "", Func<bool>? requires = null, Func<bool>? locked = null)
		{
			return Add(key, entry, 1, blocked, oldMod, null, step, unit, requires, locked);
		}

		private GcMenuGroup Add(string key, ConfigEntryBase entry, int level, Func<bool>? blocked, string oldMod, Func<string>? note, double step, string unit, Func<bool>? requires, Func<bool>? locked)
		{
			Func<string, string> t = text;
			Dictionary<string, object> dictionary = new Dictionary<string, object>
			{
				["key"] = key,
				["entry"] = entry,
				["name"] = (Func<string>)(() => t("t_" + key + "_name")),
				["desc"] = (Func<string>)(() => t("t_" + key + "_desc")),
				["enumText"] = (Func<string, string>)((string value) => t("enum_" + value)),
				["level"] = level,
				["step"] = step,
				["unit"] = unit,
				["oldMod"] = oldMod
			};
			if (blocked != null)
			{
				dictionary["blocked"] = blocked;
			}
			if (note != null)
			{
				dictionary["note"] = note;
			}
			if (requires != null)
			{
				dictionary["requires"] = requires;
			}
			if (locked != null)
			{
				dictionary["locked"] = locked;
			}
			items.Add(dictionary);
			return this;
		}
	}
	public sealed class GcMenu
	{
		private sealed class Row
		{
			public Dictionary<string, object> Item;

			public ConfigEntryBase Entry;

			public string Guid = "";

			public Func<bool>? Allowed;

			public int Level;

			public bool Server;

			public GameObject Root;

			public Row? Parent;

			public readonly List<Row> Subs = new List<Row>();

			public Text Name;

			public Text Description;

			public Text Scope;

			public string BaseDescription = "";

			public EditorKind Kind;

			public Button? Main;

			public Text? MainLabel;

			public Button? Minus;

			public Button? Plus;

			public InputField? Input;

			public bool InputError;

			public object[]? Choices;

			public bool Foreign;

			public ForeignMod? Mod;

			public SyncKind Sync;

			public bool ReadOnly;

			public Func<bool>? ReadOnlyCheck;

			public object? SyncTag;

			public string Search = "";

			public Button? Reset;

			public bool HideDefault;

			public bool Editable;

			public bool Visible;
		}

		private sealed class Block
		{
			public GameObject Header;

			public readonly List<Row> Rows = new List<Row>();

			public readonly List<Block> Children = new List<Block>();
		}

		private enum SyncKind
		{
			Local,
			Jotunn,
			ServerSync
		}

		private sealed class ForeignEntry
		{
			public ConfigEntryBase Entry;

			public string Name = "";

			public string Section = "";

			public string Description = "";

			public SyncKind Sync;

			public Func<bool>? ReadOnlyCheck;

			public bool HideDefault;

			public bool Advanced;

			public int Order;

			public int Index;

			public string Search = "";
		}

		private sealed class ForeignGroup
		{
			public GameObject Header;

			public readonly List<Row> Rows = new List<Row>();
		}

		private sealed class ForeignMod
		{
			public string Guid = "";

			public string Name = "";

			public string Version = "";

			public bool HasSync;

			public readonly List<ForeignEntry> Entries = new List<ForeignEntry>();

			public string Search = "";

			public GameObject Header;

			public Text HeaderLabel;

			public GameObject Body;

			public bool Built;

			public readonly List<Row> Rows = new List<Row>();

			public GameObject? ClientHeader;

			public GameObject? ServerHeader;

			public readonly List<ForeignGroup> ClientGroups = new List<ForeignGroup>();

			public readonly List<ForeignGroup> ServerGroups = new List<ForeignGroup>();
		}

		private enum View
		{
			Settings,
			Mods,
			Profiles
		}

		private sealed class ModRow
		{
			public ModSwitch.Mod Mod;

			public GameObject Root;

			public Toggle Toggle;

			public Text Title;

			public Text Sub;

			public Text Status;
		}

		private sealed class ProfileRow
		{
			public ModSwitch.Profile Profile;

			public GameObject Root;

			public Text Info;

			public Button Load;

			public Button Overwrite;

			public Button Delete;

			public string Pending = "";

			public float PendingUntil;
		}

		private enum EditorKind
		{
			Toggle,
			Cycle,
			Key,
			Number,
			Text,
			ReadOnly
		}

		public const string SharedSlot = "GC.Options.Registry.v2";

		public const string ButtonName = "GC_OptionButton_v2";

		private static readonly Color OnColor = new Color(0.45f, 0.95f, 0.45f);

		private static readonly Color OffColor = new Color(1f, 0.45f, 0.4f);

		private static readonly Color MutedColor = new Color(0.78f, 0.72f, 0.62f);

		private static readonly Color ValueColor = new Color(1f, 0.85f, 0.55f);

		private static readonly Color LockedColor = new Color(0.6f, 0.58f, 0.55f);

		private static readonly KeyCode[] Modifiers;

		private static KeyCode[]? allKeys;

		private static MethodInfo? syncMethod;

		private readonly ManualLogSource log;

		private readonly Func<string, string> ui;

		private readonly bool canHost;

		private readonly List<object> registry;

		private readonly List<Dictionary<string, object>> ownSections = new List<Dictionary<string, object>>();

		private readonly List<Row> rows = new List<Row>();

		private readonly List<Block> sections = new List<Block>();

		private GameObject? window;

		private RectTransform? content;

		private Toggle? showServer;

		private static bool serverRulesShown;

		private InputField? searchField;

		private string query = "";

		private string pendingQuery = "";

		private float queryAt = -1f;

		private bool focusSearch;

		private InputField? typingField;

		private int typingFrame = -10;

		private Vector2 builtSize;

		private int builtForeign;

		private bool wide;

		private int closedFrame = -10;

		private int pendingUnblocks;

		private GameObject? emptyHint;

		private Text? emptyLabel;

		private Text? hintLabel;

		private Button? menuButton;

		private int builtShape = -1;

		private float nextRefresh;

		private Row? capturing;

		private KeyCode capturedModifier;

		private bool openedFromPause;

		private int captureFrame = -1;

		private string lastError = "";

		private const float WindowMargin = 40f;

		private const float MaxWindowWidth = 1800f;

		private const float WideWidth = 1400f;

		private const float TopBarHeight = 92f;

		private const float SearchWidth = 420f;

		private const float NameWidth = 340f;

		private static readonly Color ErrorColor;

		private Button? mainMenuButton;

		private static readonly Vector3[] MenuCorners;

		private static readonly Color SubHeaderColor;

		private static readonly Color SectionColor;

		private readonly List<ForeignMod> foreignMods = new List<ForeignMod>();

		private GameObject? foreignHeader;

		private static readonly HashSet<string> ExpandedMods;

		private const string VPlusGuid = "org.bepinex.plugins.valheim_plus";

		private static readonly Dictionary<Type, FieldInfo?> LocalBaseFields;

		private readonly HashSet<string> searchClosed = new HashSet<string>(StringComparer.Ordinal);

		private const int AutoOpenLimit = 6;

		private const int DeepSearchLength = 3;

		private const float TabWidth = 190f;

		private static readonly Color GoodColor;

		private static readonly Color WarnColor;

		private static View lastView;

		private View view;

		private RectTransform? innerRect;

		private readonly List<(View View, Text? Label)> tabs = new List<(View, Text)>();

		private GameObject? settingsArea;

		private GameObject? settingsTools;

		private GameObject? modsArea;

		private GameObject? modsTools;

		private RectTransform? modsContent;

		private GameObject? modsNoPatcher;

		private InputField? modsSearch;

		private string modsQuery = "";

		private Text? modsStatus;

		private string modsMessage = "";

		private readonly List<ModRow> modRows = new List<ModRow>();

		private GameObject? profilesArea;

		private GameObject? profilesTools;

		private RectTransform? profilesContent;

		private GameObject? profilesEmpty;

		private InputField? profileName;

		private Text? profilesStatus;

		private Text? profileSaveLabel;

		private string profilesMessage = "";

		private string savePending = "";

		private float savePendingUntil;

		private readonly List<ProfileRow> profileRows = new List<ProfileRow>();

		private bool pausedByKey;

		private static readonly Dictionary<ConfigEntryBase, (bool Has, double Min, double Max)> Ranges;

		public static bool HoldsInput
		{
			get
			{
				if (KernPlugin.Menu != null)
				{
					if (!KernPlugin.Menu.IsOpen)
					{
						return Time.frameCount <= KernPlugin.Menu.closedFrame + 1;
					}
					return true;
				}
				return false;
			}
		}

		public bool IsOpen
		{
			get
			{
				if ((Object)(object)window != (Object)null)
				{
					return window.activeSelf;
				}
				return false;
			}
		}

		public static bool CanChangeServer
		{
			get
			{
				if ((Object)(object)ZNet.instance == (Object)null)
				{
					return false;
				}
				if (ZNet.instance.IsServer())
				{
					return true;
				}
				if (SynchronizationManager.Instance != null)
				{
					return SynchronizationManager.Instance.PlayerIsAdmin;
				}
				return false;
			}
		}

		public event Action? Closed;

		public GcMenu(ManualLogSource log, Func<string, string> ui, bool canHost = true)
		{
			this.log = log;
			this.ui = ui;
			this.canHost = canHost;
			registry = SharedRegistry();
		}

		public static Func<string, string> UiText(string ownPrefix)
		{
			return delegate(string name)
			{
				if (ownPrefix.Length > 0)
				{
					string text = GcLoc.Get(ownPrefix + name);
					if (text != ownPrefix + name)
					{
						return text;
					}
				}
				return GcLoc.Get("gco_" + name);
			};
		}

		public static bool CanChangeServerSetting(string guid)
		{
			if (CanChangeServer)
			{
				return ModOnServer(guid);
			}
			return false;
		}

		private static bool ModOnServer(string guid)
		{
			try
			{
				return guid.Length == 0 || ModCompatibility.IsModuleOnServer(guid);
			}
			catch
			{
				return false;
			}
		}

		private static List<object> SharedRegistry()
		{
			AppDomain currentDomain = AppDomain.CurrentDomain;
			lock (typeof(object))
			{
				List<object> list = currentDomain.GetData("GC.Options.Registry.v2") as List<object>;
				if (list == null)
				{
					list = new List<object>();
					currentDomain.SetData("GC.Options.Registry.v2", list);
				}
				return list;
			}
		}

		public GcMenuSection Section(string id, int order, Func<string, string> text, string guid)
		{
			Dictionary<string, object> dictionary = new Dictionary<string, object>
			{
				["id"] = id,
				["order"] = order,
				["guid"] = guid,
				["title"] = (Func<string>)(() => text("title")),
				["groups"] = new List<object>()
			};
			lock (registry)
			{
				registry.RemoveAll((object o) => o is Dictionary<string, object> dictionary2 && dictionary2.TryGetValue("id", out var value) && value is string text2 && text2 == id);
				registry.Add(dictionary);
			}
			ownSections.Add(dictionary);
			return new GcMenuSection(dictionary, text);
		}

		public void LogBlocked()
		{
			foreach (Dictionary<string, object> ownSection in ownSections)
			{
				foreach (Dictionary<string, object> item in Children(ownSection, "groups"))
				{
					foreach (Dictionary<string, object> item2 in Children(item, "items"))
					{
						if (item2.TryGetValue("blocked", out var value) && value is Func<bool> f && Call(f))
						{
							log.LogWarning((object)("Alte Einzelmod " + Str(item2, "oldMod") + " ist noch installiert. Die Funktion '" + Str(item2, "key") + "' bleibt aus, bis die alte Mod entfernt ist."));
						}
					}
				}
			}
		}

		private static List<Dictionary<string, object>> Children(Dictionary<string, object> data, string key)
		{
			if (data.TryGetValue(key, out object value) && value is List<object> list)
			{
				lock (list)
				{
					return list.OfType<Dictionary<string, object>>().ToList();
				}
			}
			return new List<Dictionary<string, object>>();
		}

		private static string Str(Dictionary<string, object> data, string key)
		{
			if (!data.TryGetValue(key, out object value) || !(value is string result))
			{
				return "";
			}
			return result;
		}

		private static int Int(Dictionary<string, object> data, string key, int fallback)
		{
			if (data.TryGetValue(key, out object value) && value is int)
			{
				return (int)value;
			}
			return fallback;
		}

		private static string TextOf(Dictionary<string, object> data, string key)
		{
			try
			{
				object value;
				return (data.TryGetValue(key, out value) && value is Func<string> func) ? (func() ?? "") : "";
			}
			catch
			{
				return "";
			}
		}

		private static bool Call(Func<bool> f)
		{
			try
			{
				return f();
			}
			catch
			{
				return false;
			}
		}

		private List<Dictionary<string, object>> Sections()
		{
			lock (registry)
			{
				return (from s in registry.OfType<Dictionary<string, object>>()
					orderby Int(s, "order", 100)
					select s).ToList();
			}
		}

		private void LogError(string where, Exception e)
		{
			string text = where + ": " + e.Message;
			if (text != lastError)
			{
				lastError = text;
				log.LogError((object)$"Menü GC Option, {text}\n{e}");
			}
		}

		public void Update()
		{
			if (Gc.IsHeadless || !canHost)
			{
				return;
			}
			while (pendingUnblocks > 0 && Time.frameCount > closedFrame)
			{
				pendingUnblocks--;
				GUIManager.BlockInput(false);
			}
			try
			{
				EnsurePauseButton();
				EnsureMainMenuButton();
				FitMainMenu();
				if (!IsOpen)
				{
					return;
				}
				if (capturing != null)
				{
					Capture();
					return;
				}
				if (focusSearch && (Object)(object)searchField != (Object)null)
				{
					focusSearch = false;
					((Selectable)searchField).Select();
					searchField.ActivateInputField();
				}
				InputField val = Typing();
				if ((Object)(object)val != (Object)null)
				{
					typingField = val;
					typingFrame = Time.frameCount;
				}
				if (Input.GetKeyDown((KeyCode)27) && (Time.frameCount - typingFrame > 1 || !((Object)(object)typingField != (Object)null) || !((Object)(object)typingField != (Object)(object)searchField) || !((Object)(object)typingField != (Object)(object)modsSearch)))
				{
					Hide();
					return;
				}
				if (queryAt >= 0f && Time.unscaledTime >= queryAt)
				{
					queryAt = -1f;
					if (pendingQuery != query)
					{
						query = pendingQuery;
						searchClosed.Clear();
						Refresh();
					}
				}
				if (Time.unscaledTime >= nextRefresh)
				{
					nextRefresh = Time.unscaledTime + 0.5f;
					if (Shape() != builtShape)
					{
						RebuildOpen();
					}
					else
					{
						Refresh();
					}
				}
			}
			catch (Exception e)
			{
				LogError("Update", e);
			}
		}

		public bool Show()
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			if (IsOpen)
			{
				return true;
			}
			if (Gc.IsHeadless || GUIManager.Instance == null || (Object)(object)GUIManager.CustomGUIFront == (Object)null)
			{
				return false;
			}
			capturing = null;
			capturedModifier = (KeyCode)0;
			try
			{
				if ((Object)(object)window != (Object)null && (WindowSize() != builtSize || ForeignCount() != builtForeign))
				{
					Object.Destroy((Object)(object)window);
					window = null;
					builtShape = -1;
				}
				Build();
				ClearSearch();
				SwitchView(lastView);
				focusSearch = view == View.Settings;
			}
			catch (Exception e)
			{
				LogError("Aufbau", e);
				if ((Object)(object)window != (Object)null)
				{
					Object.Destroy((Object)(object)window);
				}
				window = null;
				builtShape = -1;
				return false;
			}
			window.SetActive(true);
			window.transform.SetAsLastSibling();
			GUIManager.BlockInput(true);
			return true;
		}

		public void Hide()
		{
			capturing = null;
			if (IsOpen)
			{
				window.SetActive(false);
				closedFrame = Time.frameCount;
				pendingUnblocks++;
				try
				{
					this.Closed?.Invoke();
				}
				catch (Exception e)
				{
					LogError("Schließen", e);
				}
				if (openedFromPause)
				{
					if ((Object)(object)Menu.instance != (Object)null)
					{
						Menu.instance.Show();
					}
					else
					{
						Game.Unpause();
					}
				}
				else if (pausedByKey)
				{
					Game.Unpause();
				}
			}
			openedFromPause = false;
			pausedByKey = false;
		}

		private int Shape()
		{
			return Sections().Sum((Dictionary<string, object> s) => 1000 + Children(s, "groups").Sum((Dictionary<string, object> g) => 1 + Children(g, "items").Count));
		}

		private void Build()
		{
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: 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_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: 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_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_015d: Expected O, but got Unknown
			//IL_0172: Unknown result type (might be due to invalid IL or missing references)
			//IL_0179: Expected O, but got Unknown
			//IL_017b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0187: Unknown result type (might be due to invalid IL or missing references)
			//IL_0193: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01be: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_0262: Unknown result type (might be due to invalid IL or missing references)
			//IL_04c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_04d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_04e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_04f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_04f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_053f: Unknown result type (might be due to invalid IL or missing references)
			//IL_054e: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ed: Unknown result type (might be due to invalid IL or missing references)
			int num = Shape();
			if ((Object)(object)window != (Object)null && num == builtShape)
			{
				return;
			}
			if ((Object)(object)window != (Object)null)
			{
				Object.Destroy((Object)(object)window);
				window = null;
			}
			builtShape = -1;
			rows.Clear();
			sections.Clear();
			foreignMods.Clear();
			foreignHeader = null;
			Vector2 val = (builtSize = WindowSize());
			builtForeign = ForeignCount();
			wide = val.x >= 1400f;
			window = GcUi.CreateWindow("GC_Option_Window", val.x, val.y, out Text header, out RectTransform inner);
			header.text = ui("title");
			innerRect = inner;
			modsArea = (modsTools = (profilesArea = (profilesTools = null)));
			modsContent = (profilesContent = null);
			modRows.Clear();
			profileRows.Clear();
			BuildTopBar(inner);
			BuildModsTools(inner);
			BuildProfilesTools(inner);
			GameObject val2 = new GameObject("ScrollArea", new Type[1] { typeof(RectTransform) });
			val2.transform.SetParent((Transform)(object)inner, false);
			RectTransform val3 = (RectTransform)val2.transform;
			val3.anchorMin = Vector2.zero;
			val3.anchorMax = Vector2.one;
			val3.offsetMin = Vector2.zero;
			val3.offsetMax = new Vector2(0f, -92f);
			settingsArea = val2;
			content = GcUi.CreateScroll((Transform)(object)val3, val.x - 88f, val.y - 172f - 92f);
			emptyHint = HintRow((Transform)(object)content, ui("nothing"), out Text label);
			emptyLabel = label;
			HashSet<ConfigEntryBase> hashSet = new HashSet<ConfigEntryBase>();
			foreach (Dictionary<string, object> item in Sections())
			{
				string text = Str(item, "guid");
				string text2 = TextOf(item, "title");
				Block block = new Block
				{
					Header = Header((Transform)(object)content, text2, 28, GUIManager.Instance.ValheimOrange, norse: true, 46f)
				};
				sections.Add(block);
				foreach (Dictionary<string, object> item2 in Children(item, "groups"))
				{
					List<Dictionary<string, object>> list = Children(item2, "items");
					if (list.Count == 0)
					{
						continue;
					}
					string text3 = TextOf(item2, "title");
					Block block2 = new Block
					{
						Header = Header((Transform)(object)content, text3, 22, new Color(1f, 0.75f, 0.4f), norse: false, 34f)
					};
					block.Children.Add(block2);
					string text4 = Str(item2, "guid");
					object value;
					Func<bool> allowed = (item2.TryGetValue("allowed", out value) ? (value as Func<bool>) : null);
					Row row = null;
					foreach (Dictionary<string, object> item3 in list)
					{
						Row row2 = CreateRow(item3, (text4.Length > 0) ? text4 : text, (Transform)(object)content);
						if (row2 != null)
						{
							hashSet.Add(row2.Entry);
							row2.Allowed = allowed;
							row2.Search = SearchText(TextOf(item3, "name"), TextOf(item3, "desc"), text3, text2, row2.Entry.Definition.Key, row2.Guid, ModuleName(row2.Guid));
							if (row2.Level == 0)
							{
								row = row2;
							}
							else if (row != null)
							{
								row2.Parent = row;
								row.Subs.Add(row2);
							}
							rows.Add(row2);
							block2.Rows.Add(row2);
						}
					}
				}
			}
			BuildForeign(hashSet);
			hintLabel = GcUi.CreateLabel(window.transform, ui("hint_config"), new Vector2(0f, 0f), new Vector2(1f, 0f), new Vector2(44f, 64f), new Vector2(-44f, 86f), 15, (TextAnchor)4, MutedColor);
			((Graphic)hintLabel).raycastTarget = false;
			GcUi.CreateButton(window.transform, ui("close"), new Vector2(0.5f, 0f), new Vector2(0f, 38f), 200f, 42f, Hide);
			builtShape = num;
		}

		private static string ModuleName(string guid)
		{
			return GcKern.Modules.FirstOrDefault((GcModule m) => m.Info.Guid == guid)?.Info.Name ?? "";
		}

		private static Vector2 WindowSize()
		{
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: 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_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: 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_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			? val2;
			if ((Object)(object)GUIManager.CustomGUIFront != (Object)null)
			{
				Transform transform = GUIManager.CustomGUIFront.transform;
				RectTransform val = (RectTransform)(object)((transform is RectTransform) ? transform : null);
				if (val != null)
				{
					Rect rect = val.rect;
					if (((Rect)(ref rect)).width > 0f)
					{
						rect = val.rect;
						val2 = ((Rect)(ref rect)).size;
						goto IL_0054;
					}
				}
			}
			val2 = new Vector2(1920f, 1080f);
			goto IL_0054;
			IL_0054:
			Vector2 val3 = (Vector2)val2;
			float num = Mathf.Clamp(val3.x - 80f, Mathf.Min(900f, val3.x), 1800f);
			float num2 = Mathf.Max(val3.y - 80f, Mathf.Min(640f, val3.y));
			return new Vector2(Mathf.Round(num), Mathf.Round(num2));
		}

		private void BuildTopBar(RectTransform inner)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Expected O, but got Unknown
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: 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_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_010f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_0128: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0141: 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_0153: Unknown result type (might be due to invalid IL or missing references)
			//IL_0167: Unknown result type (might be due to invalid IL or missing references)
			//IL_019e: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01af: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_021f: Unknown result type (might be due to invalid IL or missing references)
			//IL_024b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0260: Unknown result type (might be due to invalid IL or missing references)
			//IL_0275: Unknown result type (might be due to invalid IL or missing references)
			//IL_028a: Unknown result type (might be due to invalid IL or missing references)
			//IL_029e: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b4: Unknown result type (might be due to invalid IL or missing references)
			BuildTabs(inner);
			settingsTools = ToolRow(inner, "SettingsTools");
			RectTransform val = (RectTransform)settingsTools.transform;
			GameObject val2 = GUIManager.Instance.CreateInputField((Transform)(object)val, new Vector2(0f, 1f), new Vector2(0f, 1f), Vector2.zero, (ContentType)0, ui("search"), 18, 420f, 34f);
			((Object)val2).name = "Search";
			RectTransform val3 = (RectTransform)val2.transform;
			val3.pivot = new Vector2(0f, 1f);
			val3.anchoredPosition = new Vector2(16f, 0f);
			searchField = val2.GetComponent<InputField>();
			searchField.lineType = (LineType)0;
			((UnityEvent<string>)(object)searchField.onValueChanged).AddListener((UnityAction<string>)delegate(string text)
			{
				pendingQuery = NormalizeSearch(text);
				queryAt = Time.unscaledTime + 0.2f;
			});
			GameObject val4 = GUIManager.Instance.CreateToggle((Transform)(object)val, 28f, 28f);
			((Object)val4).name = "ShowServer";
			RectTransform val5 = (RectTransform)val4.transform;
			((Transform)val5).SetParent((Transform)(object)val, false);
			((Transform)val5).localScale = Vector3.one;
			Vector2 val6 = default(Vector2);
			((Vector2)(ref val6))..ctor(0f, 1f);
			val5.anchorMax = val6;
			val5.anchorMin = val6;
			val5.pivot = new Vector2(0f, 1f);
			val5.anchoredPosition = new Vector2(468f, -3f);
			Transform obj = val4.transform.Find("Background");
			RectTransform val7 = (RectTransform)(object)((obj is RectTransform) ? obj : null);
			if (val7 != null)
			{
				Vector2 val8 = default(Vector2);
				((Vector2)(ref val8))..ctor(0.5f, 0.5f);
				val7.pivot = val8;
				val6 = (val7.anchorMin = (val7.anchorMax = val8));
				val7.anchoredPosition = Vector2.zero;
			}
			Transform obj2 = val4.transform.Find("Label");
			Text val11 = ((obj2 != null) ? ((Component)obj2).GetComponent<Text>() : null);
			if ((Object)(object)val11 != (Object)null)
			{
				val11.text = ui("show_server");
				val11.font = GUIManager.Instance.AveriaSerifBold;
				val11.fontSize = 18;
				((Graphic)val11).color = MutedColor;
				val11.alignment = (TextAnchor)3;
				val11.horizontalOverflow = (HorizontalWrapMode)1;
				RectTransform rectTransform = ((Graphic)val11).rectTransform;
				rectTransform.anchorMin = new Vector2(1f, 0f);
				rectTransform.anchorMax = new Vector2(1f, 1f);
				rectTransform.pivot = new Vector2(0f, 0.5f);
				rectTransform.offsetMin = new Vector2(12f, 0f);
				rectTransform.offsetMax = new Vector2(612f, 0f);
				((Shadow)((Component)val11).gameObject.AddComponent<Outline>()).effectColor = Color.black;
			}
			showServer = val4.GetComponent<Toggle>();
			showServer.isOn = serverRulesShown;
			((UnityEvent<bool>)(object)showServer.onValueChanged).AddListener((UnityAction<bool>)delegate(bool on)
			{
				serverRulesShown = on;
				Refresh();
			});
		}

		private void ClearSearch()
		{
			query = (pendingQuery = "");
			queryAt = -1f;
			searchClosed.Clear();
			if ((Object)(object)searchField != (Object)null && searchField.text.Length > 0)
			{
				searchField.text = "";
			}
			queryAt = -1f;
		}

		private static string NormalizeSearch(string text)
		{
			return text.Trim().ToLowerInvariant();
		}

		private static string SearchText(params string[] parts)
		{
			return string.Join(" ", parts).ToLowerInvariant();
		}

		private static InputField? Typing()
		{
			GameObject val = (((Object)(object)EventSystem.current != (Object)null) ? EventSystem.current.currentSelectedGameObject : null);
			InputField val2 = default(InputField);
			if (!((Object)(object)val != (Object)null) || !val.TryGetComponent<InputField>(ref val2) || !val2.isFocused)
			{
				return null;
			}
			return val2;
		}

		private GameObject Header(Transform parent, string text, int size, Color color, bool norse, float height)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: 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_004c: 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)
			//IL_0058: 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_0073: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Expected O, but got Unknown
			GameObject val = new GameObject("Header", new Type[2]
			{
				typeof(RectTransform),
				typeof(LayoutElement)
			});
			val.transform.SetParent(parent, false);
			LayoutElement component = val.GetComponent<LayoutElement>();
			component.minHeight = height;
			component.preferredHeight = height;
			((Graphic)GcUi.CreateLabel(val.transform, text, Vector2.zero, Vector2.one, new Vector2(4f, 0f), Vector2.zero, size, (TextAnchor)6, color, norse)).raycastTarget = false;
			return val;
		}

		private GameObject HintRow(Transform parent, string text, out Text label)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Expected O, but got Unknown
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Expected O, but got Unknown
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = new GameObject("Hint", new Type[2]
			{
				typeof(RectTransform),
				typeof(VerticalLayoutGroup)
			});
			val.transform.SetParent(parent, false);
			VerticalLayoutGroup component = val.GetComponent<VerticalLayoutGroup>();
			((HorizontalOrVerticalLayoutGroup)component).childControlWidth = true;
			((HorizontalOrVerticalLayoutGroup)component).childControlHeight = true;
			((HorizontalOrVerticalLayoutGroup)component).childForceExpandWidth = true;
			((HorizontalOrVerticalLayoutGroup)component).childForceExpandHeight = false;
			((LayoutGroup)component).padding = new RectOffset(8, 8, 16, 16);
			label = FlowText(val.transform, text, 18, MutedColor);
			return val;
		}

		private static Text FlowText(Transform parent, string text, int size, Color color)
		{
			//IL_0013: 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_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			GUIManager instance = GUIManager.Instance;
			Text component = instance.CreateText(text, parent, new Vector2(0f, 1f), new Vector2(1f, 1f), Vector2.zero, instance.AveriaSerifBold, size, color, true, Color.black, 100f, 20f, false).GetComponent<Text>();
			component.alignment = (TextAnchor)0;
			component.horizontalOverflow = (HorizontalWrapMode)0;
			component.verticalOverflow = (VerticalWrapMode)1;
			component.supportRichText = true;
			((Graphic)component).raycastTarget = false;
			return component;
		}

		private Row? CreateRow(Dictionary<string, object> item, string guid, Transform parent, ForeignEntry? foreign = null)
		{
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Expected O, but got Unknown
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Expected O, but got Unknown
			//IL_01d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			//IL_0183: Unknown result type (might be due to invalid IL or missing references)
			//IL_0218: Unknown result type (might be due to invalid IL or missing references)
			//IL_021d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0230: Unknown result type (might be due to invalid IL or missing references)
			//IL_027a: Unknown result type (might be due to invalid IL or missing references)
			//IL_027f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0284: Unknown result type (might be due to invalid IL or missing references)
			//IL_0289: Unknown result type (might be due to invalid IL or missing references)
			//IL_0291: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ed: Expected O, but got Unknown
			//IL_0355: Unknown result type (might be due to invalid IL or missing references)
			//IL_035c: Expected O, but got Unknown
			//IL_04e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_04f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0530: Unknown result type (might be due to invalid IL or missing references)
			//IL_0545: Unknown result type (might be due to invalid IL or missing references)
			//IL_05fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_05ff: Unknown result type (might be due to invalid IL or missing references)
			//IL_0670: Unknown result type (might be due to invalid IL or missing references)
			//IL_0672: Unknown result type (might be due to invalid IL or missing references)
			if (item.TryGetValue("entry", out object value))
			{
				ConfigEntryBase val = (ConfigEntryBase)((value is ConfigEntryBase) ? value : null);
				if (val != null)
				{
					object[] choices = Choices(val);
					EditorKind editorKind = KindOf(val.SettingType, choices);
					int num = ((Int(item, "level", 0) > 0) ? 1 : 0);
					GameObject val2 = new GameObject("Row", new Type[3]
					{
						typeof(RectTransform),
						typeof(HorizontalLayoutGroup),
						typeof(LayoutElement)
					});
					val2.transform.SetParent(parent, false);
					HorizontalLayoutGroup component = val2.GetComponent<HorizontalLayoutGroup>();
					((HorizontalOrVerticalLayoutGroup)component).childControlWidth = true;
					((HorizontalOrVerticalLayoutGroup)component).childControlHeight = true;
					((HorizontalOrVerticalLayoutGroup)component).childForceExpandWidth = false;
					((HorizontalOrVerticalLayoutGroup)component).childForceExpandHeight = false;
					((LayoutGroup)component).childAlignment = (TextAnchor)3;
					((HorizontalOrVerticalLayoutGroup)component).spacing = 12f;
					((LayoutGroup)component).padding = new RectOffset(10 + num * 36, 4, (num == 0) ? 8 : 4, (num == 0) ? 8 : 4);
					val2.GetComponent<LayoutElement>().minHeight = ((num == 0) ? 54f : 44f);
					string text = TextOf(item, "desc");
					Text name;
					Text description;
					if (wide)
					{
						name = FlowText(Cell(val2.transform, 340f - (float)num * 36f, 0f).transform, TextOf(item, "name"), (num == 0) ? 20 : 17, Color.white);
						description = FlowText(Cell(val2.transform, 220f, 1f).transform, text, 15, MutedColor);
					}
					else
					{
						GameObject obj = Cell(val2.transform, 220f, 1f);
						((HorizontalOrVerticalLayoutGroup)obj.GetComponent<VerticalLayoutGroup>()).spacing = 2f;
						name = FlowText(obj.transform, TextOf(item, "name"), (num == 0) ? 20 : 17, Color.white);
						description = FlowText(obj.transform, text, 15, MutedColor);
					}
					GameObject val3 = new GameObject("Scope", new Type[2]
					{
						typeof(RectTransform),
						typeof(LayoutElement)
					});
					val3.transform.SetParent(val2.transform, false);
					LayoutElement component2 = val3.GetComponent<LayoutElement>();
					float minWidth = (component2.preferredWidth = (wide ? 180f : 160f));
					component2.minWidth = minWidth;
					minWidth = (component2.preferredHeight = 40f);
					component2.minHeight = minWidth;
					Text val4 = GcUi.CreateLabel(val3.transform, "", Vector2.zero, Vector2.one, Vector2.zero, Vector2.zero, 14, (TextAnchor)5, MutedColor);
					((Graphic)val4).raycastTarget = false;
					float num4 = (wide ? 260f : 200f);
					GameObject val5 = new GameObject("Control", new Type[2]
					{
						typeof(RectTransform),
						typeof(LayoutElement)
					});
					val5.transform.SetParent(val2.transform, false);
					LayoutElement component3 = val5.GetComponent<LayoutElement>();
					minWidth = (component3.preferredWidth = num4);
					component3.minWidth = minWidth;
					minWidth = (component3.preferredHeight = 40f);
					component3.minHeight = minWidth;
			

GC_Kisten.dll

Decompiled 3 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
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 BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using GC.Shared;
using GC.ValheimPlusPlus.LinkedChests;
using GC.ValheimPlusPlus.StationStorage;
using HarmonyLib;
using Jotunn.Managers;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("assembly_guiutils")]
[assembly: IgnoresAccessChecksTo("assembly_utils")]
[assembly: IgnoresAccessChecksTo("assembly_valheim")]
[assembly: TargetFramework(".NETFramework,Version=v4.8.1", FrameworkDisplayName = ".NET Framework 4.8.1")]
[assembly: AssemblyCompany("Gorduan")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.5.1.0")]
[assembly: AssemblyInformationalVersion("1.5.1+7c8bde9391283b12125175d68f4632c4cf8f93b3")]
[assembly: AssemblyProduct("GC_Kisten")]
[assembly: AssemblyTitle("GC_Kisten")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.5.1.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 GC.ValheimPlusPlus
{
	internal static class VppPlugin
	{
		internal static ManualLogSource Log;

		internal static GcModule? Module;

		internal static bool Allowed
		{
			get
			{
				if (Module != null)
				{
					return Module.Allowed;
				}
				return true;
			}
		}

		internal static bool OnServer
		{
			get
			{
				if (Module != null)
				{
					return Module.OnServer;
				}
				return false;
			}
		}
	}
}
namespace GC.ValheimPlusPlus.StationStorage
{
	internal static class Plugin
	{
		public const string Guid = "gorduan.gc_stationstorage";

		public const string ModName = "GC_StationStorage";

		public const string Version = "1.0.0";

		internal const string RpcTake = "GC_SS_Take";

		internal const string RpcTakeFuel = "GC_SS_TakeFuel";

		internal const string RpcTakeOutput = "GC_SS_TakeOutput";

		internal const string RpcGive = "GC_SS_Give";

		internal const string RpcAssign = "GC_SS_Assign";

		internal const string KeyPrefix = "gc_ss_";

		internal const string OutputKey = "out";

		internal const string FuelKey = "fuel";

		internal static ManualLogSource Log;

		internal static ConfigEntry<bool> MenuEnabled;

		internal static ConfigEntry<bool> ChestsEnabled;

		internal static ConfigEntry<float> MaxChestDistance;

		internal static ConfigEntry<bool> HighlightWhileOpen;

		internal static bool Blocked => Chainloader.PluginInfos.ContainsKey("gorduan.gc_stationstorage");

		internal static bool MenuOn
		{
			get
			{
				if (MenuEnabled.Value && !Blocked && VppPlugin.Allowed)
				{
					return VppPlugin.OnServer;
				}
				return false;
			}
		}

		internal static bool ChestsOn
		{
			get
			{
				if (ChestsEnabled.Value && !Blocked && VppPlugin.Allowed)
				{
					return VppPlugin.OnServer;
				}
				return false;
			}
		}

		internal static void Init(ConfigFile Config, Harmony harmony)
		{
			Log = VppPlugin.Log;
			MenuEnabled = Gc.BindSynced<bool>(Config, "4 - Station Storage", "MenuEnabled", true, "Gebäude-Menü mit Umschalt+E: Inhalt ansehen, zurücknehmen, einfüllen, Kisten wählen (wird vom Server vorgegeben).", (AcceptableValueBase)null);
			ChestsEnabled = Gc.BindSynced<bool>(Config, "4 - Station Storage", "ChestsEnabled", true, "Ein- und Ausgangskisten pro Gebäude für die ValheimPlus-Automatik, auch für Bienenstock, Vogelnest und Saftzapfer (wird vom Server vorgegeben).", (AcceptableValueBase)null);
			MaxChestDistance = Gc.BindSynced<float>(Config, "4 - Station Storage", "MaxChestDistance", 30f, "Größte Entfernung zwischen Gebäude und zugewiesener Kiste in Metern.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(2f, 100f));
			HighlightWhileOpen = Gc.Bind<bool>(Config, "4 - Station Storage - Interface", "HighlightWhileOpen", true, "Zugewiesene Kisten leuchten, solange das Menü offen ist.", (AcceptableValueBase)null);
			VPlusBridge.Apply(harmony);
		}

		internal static void Update()
		{
			if (Gc.IsHeadless)
			{
				return;
			}
			if (!MenuOn)
			{
				if (StationWindow.IsOpen)
				{
					StationWindow.Close();
				}
				if (ChestPicker.Active)
				{
					ChestPicker.Cancel(backToMenu: false);
				}
			}
			else
			{
				ChestPicker.Update();
				StationWindow.Update();
			}
		}

		internal static List<string> Queue(ZDO zdo)
		{
			int num = zdo.GetInt(ZDOVars.s_queued, 0);
			List<string> list = new List<string>(num);
			for (int i = 0; i < num; i++)
			{
				list.Add(zdo.GetString("item" + i, ""));
			}
			return list;
		}

		internal static void WriteQueue(ZDO zdo, List<string> queue)
		{
			for (int i = 0; i < queue.Count; i++)
			{
				zdo.Set("item" + i, queue[i]);
			}
			zdo.Set(ZDOVars.s_queued, queue.Count, false);
		}

		internal static string ItemName(string prefab)
		{
			GameObject val = (((Object)(object)ObjectDB.instance != (Object)null) ? ObjectDB.instance.GetItemPrefab(prefab) : null);
			ItemDrop val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<ItemDrop>() : null);
			if (!((Object)(object)val2 != (Object)null))
			{
				return prefab;
			}
			return Localization.instance.Localize(val2.m_itemData.m_shared.m_name);
		}

		internal static List<string> Assigned(Station station, string key)
		{
			ZDO val = (station.Valid ? station.View.GetZDO() : null);
			if (val == null)
			{
				return new List<string>();
			}
			return val.GetString("gc_ss_" + key, "").Split(new char[1] { ';' }, StringSplitOptions.RemoveEmptyEntries).Distinct()
				.ToList();
		}

		internal static List<Container> Resolve(Station station, IEnumerable<string> ids)
		{
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Invalid comparison between Unknown and I4
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			List<Container> list = new List<Container>();
			HashSet<Container> hashSet = new HashSet<Container>();
			foreach (string id in ids)
			{
				Container val = GcIds.Find(id);
				if ((Object)(object)val == (Object)null)
				{
					continue;
				}
				foreach (Container item in Links.Group(val))
				{
					if (!((Object)(object)item == (Object)null) && LinkedView.Usable(item) && !Gc.IsAutoLocked(item))
					{
						Container val2 = LinkedView.Canonical(item);
						if (!((Object)(object)val2 == (Object)null) && val2.GetInventory() != null && !Gc.IsAutoLocked(val2) && !LinkedView.LockedView(val2) && (int)val2.m_privacy == 2 && GcWard.SameWards(station.Position, ((Component)val2).transform.position) && hashSet.Add(val2))
						{
							list.Add(val2);
						}
					}
				}
			}
			return list;
		}

		internal static List<string> InputKeys(Station station)
		{
			List<string> list = new List<string>();
			Smelter smelter = station.Smelter;
			if ((Object)(object)smelter == (Object)null)
			{
				return list;
			}
			if ((Object)(object)smelter.m_fuelItem != (Object)null)
			{
				list.Add("fuel");
			}
			foreach (ItemConversion item in smelter.m_conversion)
			{
				if ((Object)(object)item.m_from != (Object)null && !list.Contains(((Object)((Component)item.m_from).gameObject).name))
				{
					list.Add(((Object)((Component)item.m_from).gameObject).name);
				}
			}
			return list;
		}

		internal static string KeyForItem(Smelter smelter, ItemData item)
		{
			if ((Object)(object)smelter.m_fuelItem != (Object)null && item.m_shared.m_name == smelter.m_fuelItem.m_itemData.m_shared.m_name)
			{
				return "fuel";
			}
			if (!((Object)(object)item.m_dropPrefab != (Object)null))
			{
				return KeyForSharedName(smelter, item.m_shared.m_name);
			}
			return ((Object)item.m_dropPrefab).name;
		}

		internal static string KeyForSharedName(Smelter smelter, string sharedName)
		{
			if ((Object)(object)smelter.m_fuelItem != (Object)null && smelter.m_fuelItem.m_itemData.m_shared.m_name == sharedName)
			{
				return "fuel";
			}
			foreach (ItemConversion item in smelter.m_conversion)
			{
				if ((Object)(object)item.m_from != (Object)null && item.m_from.m_itemData.m_shared.m_name == sharedName)
				{
					return ((Object)((Component)item.m_from).gameObject).name;
				}
			}
			return sharedName;
		}

		internal static (bool Input, bool Output) Roles(Station station, Container chest)
		{
			bool item = InputKeys(station).Any((string key) => Resolve(station, Assigned(station, key)).Contains(chest));
			bool item2 = Resolve(station, Assigned(station, "out")).Contains(chest);
			return (Input: item, Output: item2);
		}
	}
	internal sealed class Station
	{
		public readonly MonoBehaviour Component;

		public readonly ZNetView View;

		public Smelter? Smelter
		{
			get
			{
				MonoBehaviour component = Component;
				return (Smelter?)(object)((component is Smelter) ? component : null);
			}
		}

		public Beehive? Hive
		{
			get
			{
				MonoBehaviour component = Component;
				return (Beehive?)(object)((component is Beehive) ? component : null);
			}
		}

		public SapCollector? Sap
		{
			get
			{
				MonoBehaviour component = Component;
				return (SapCollector?)(object)((component is SapCollector) ? component : null);
			}
		}

		public bool IsProducer
		{
			get
			{
				if (!((Object)(object)Hive != (Object)null))
				{
					return (Object)(object)Sap != (Object)null;
				}
				return true;
			}
		}

		public bool Valid
		{
			get
			{
				if ((Object)(object)Component != (Object)null && (Object)(object)View != (Object)null)
				{
					return View.IsValid();
				}
				return false;
			}
		}

		public GameObject GameObject => ((Component)Component).gameObject;

		public Vector3 Position => ((Component)Component).transform.position;

		public string Name
		{
			get
			{
				if (!((Object)(object)Smelter != (Object)null))
				{
					if (!((Object)(object)Hive != (Object)null))
					{
						if (!((Object)(object)Sap != (Object)null))
						{
							return "";
						}
						return Sap.m_name;
					}
					return Hive.m_name;
				}
				return Smelter.m_name;
			}
		}

		public (int Level, int Max) Level
		{
			get
			{
				if (!((Object)(object)Hive != (Object)null))
				{
					if (!((Object)(object)Sap != (Object)null))
					{
						return (Level: 0, Max: 0);
					}
					return (Level: Sap.GetLevel(), Max: Sap.m_maxLevel);
				}
				return (Level: Hive.GetHoneyLevel(), Max: Hive.m_maxHoney);
			}
		}

		public string ProductName
		{
			get
			{
				ItemDrop val = (((Object)(object)Hive != (Object)null) ? Hive.m_honeyItem : (((Object)(object)Sap != (Object)null) ? Sap.m_spawnItem : null));
				if (!((Object)(object)val != (Object)null))
				{
					return "";
				}
				return Localization.instance.Localize(val.m_itemData.m_shared.m_name);
			}
		}

		private Station(MonoBehaviour component, ZNetView view)
		{
			Component = component;
			View = view;
		}

		public static Station? Of(Component? component)
		{
			if ((Object)(object)component == (Object)null)
			{
				return null;
			}
			Smelter componentInParent = component.GetComponentInParent<Smelter>();
			if ((Object)(object)componentInParent != (Object)null && (Object)(object)componentInParent.m_nview != (Object)null)
			{
				return new Station((MonoBehaviour)(object)componentInParent, componentInParent.m_nview);
			}
			Beehive componentInParent2 = component.GetComponentInParent<Beehive>();
			if ((Object)(object)componentInParent2 != (Object)null && (Object)(object)componentInParent2.m_nview != (Object)null)
			{
				return new Station((MonoBehaviour)(object)componentInParent2, componentInParent2.m_nview);
			}
			SapCollector componentInParent3 = component.GetComponentInParent<SapCollector>();
			if ((Object)(object)componentInParent3 != (Object)null && (Object)(object)componentInParent3.m_nview != (Object)null)
			{
				return new Station((MonoBehaviour)(object)componentInParent3, componentInParent3.m_nview);
			}
			return null;
		}

		public void Extract()
		{
			if ((Object)(object)Hive != (Object)null)
			{
				Hive.Extract();
			}
			else if ((Object)(object)Sap != (Object)null)
			{
				Sap.Extract();
			}
		}

		public override bool Equals(object? obj)
		{
			if (obj is Station station)
			{
				return (Object)(object)station.Component == (Object)(object)Component;
			}
			return false;
		}

		public override int GetHashCode()
		{
			if (!((Object)(object)Component != (Object)null))
			{
				return 0;
			}
			return ((object)Component).GetHashCode();
		}
	}
	[HarmonyPatch(typeof(Smelter), "Awake")]
	internal static class SmelterAwakePatch
	{
		private static void Postfix(Smelter __instance)
		{
			ZNetView nview = __instance.m_nview;
			if (!((Object)(object)nview == (Object)null) && nview.GetZDO() != null && !Plugin.Blocked)
			{
				Smelter smelter = __instance;
				nview.Register<string, int>("GC_SS_Take", (Action<long, string, int>)delegate(long sender, string item, int amount)
				{
					Rpcs.Take(smelter, sender, item, amount);
				});
				nview.Register<int>("GC_SS_TakeFuel", (Action<long, int>)delegate(long sender, int amount)
				{
					Rpcs.TakeFuel(smelter, sender, amount);
				});
				nview.Register("GC_SS_TakeOutput", (Action<long>)delegate(long sender)
				{
					Rpcs.TakeOutput(smelter, sender);
				});
				nview.Register<string, int>("GC_SS_Give", (Action<long, string, int>)delegate(long sender, string item, int amount)
				{
					Rpcs.Give(item, amount);
				});
				nview.Register<string, string>("GC_SS_Assign", (Action<long, string, string>)delegate(long sender, string key, string value)
				{
					Rpcs.Assign(nview, key, value);
				});
			}
		}
	}
	[HarmonyPatch(typeof(Beehive), "Awake")]
	internal static class BeehiveAwakePatch
	{
		private static void Postfix(Beehive __instance)
		{
			ProducerRpcs.Register(__instance.m_nview);
		}
	}
	[HarmonyPatch(typeof(SapCollector), "Awake")]
	internal static class SapAwakePatch
	{
		private static void Postfix(SapCollector __instance)
		{
			ProducerRpcs.Register(__instance.m_nview);
		}
	}
	internal static class ProducerRpcs
	{
		public static void Register(ZNetView? nview)
		{
			if (!((Object)(object)nview == (Object)null) && nview.GetZDO() != null && !Plugin.Blocked)
			{
				nview.Register<string, string>("GC_SS_Assign", (Action<long, string, string>)delegate(long sender, string key, string value)
				{
					Rpcs.Assign(nview, key, value);
				});
			}
		}
	}
	internal static class Rpcs
	{
		public static void Take(Smelter smelter, long sender, string item, int amount)
		{
			ZNetView nview = smelter.m_nview;
			if (!nview.IsOwner())
			{
				return;
			}
			ZDO zDO = nview.GetZDO();
			List<string> list = Plugin.Queue(zDO);
			List<string> list2 = new List<string>();
			int num = 0;
			foreach (string item2 in list)
			{
				if (item2 == item && num < amount)
				{
					num++;
				}
				else
				{
					list2.Add(item2);
				}
			}
			if (num != 0)
			{
				Plugin.WriteQueue(zDO, list2);
				nview.InvokeRPC(sender, "GC_SS_Give", new object[2] { item, num });
			}
		}

		public static void TakeFuel(Smelter smelter, long sender, int amount)
		{
			ZNetView nview = smelter.m_nview;
			if (nview.IsOwner() && !((Object)(object)smelter.m_fuelItem == (Object)null))
			{
				ZDO zDO = nview.GetZDO();
				float num = zDO.GetFloat(ZDOVars.s_fuel, 0f);
				int num2 = Mathf.Min(amount, Mathf.FloorToInt(num));
				if (num2 > 0)
				{
					zDO.Set(ZDOVars.s_fuel, num - (float)num2);
					nview.InvokeRPC(sender, "GC_SS_Give", new object[2]
					{
						((Object)((Component)smelter.m_fuelItem).gameObject).name,
						num2
					});
				}
			}
		}

		public static void TakeOutput(Smelter smelter, long sender)
		{
			ZNetView nview = smelter.m_nview;
			if (nview.IsOwner())
			{
				ZDO zDO = nview.GetZDO();
				int num = zDO.GetInt(ZDOVars.s_spawnAmount, 0);
				string text = zDO.GetString(ZDOVars.s_spawnOre, "");
				ItemConversion itemConversion = smelter.GetItemConversion(text);
				if (num > 0 && !((Object)(object)itemConversion?.m_to == (Object)null))
				{
					zDO.Set(ZDOVars.s_spawnOre, "");
					zDO.Set(ZDOVars.s_spawnAmount, 0, false);
					nview.InvokeRPC(sender, "GC_SS_Give", new object[2]
					{
						((Object)((Component)itemConversion.m_to).gameObject).name,
						num
					});
				}
			}
		}

		public static void Give(string item, int amount)
		{
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_0121: Unknown result type (might be due to invalid IL or missing references)
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0130: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			GameObject val = (((Object)(object)ObjectDB.instance != (Object)null) ? ObjectDB.instance.GetItemPrefab(item) : null);
			ItemDrop val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<ItemDrop>() : null);
			if ((Object)(object)localPlayer == (Object)null || (Object)(object)val2 == (Object)null || amount <= 0)
			{
				return;
			}
			Inventory inventory = ((Humanoid)localPlayer).GetInventory();
			int num = Mathf.Max(1, val2.m_itemData.m_shared.m_maxStackSize);
			int num2 = amount;
			int num3 = 0;
			while (num2 > 0)
			{
				int num4 = Mathf.Min(num2, num);
				num2 -= num4;
				ItemData val3 = val2.m_itemData.Clone();
				val3.m_dropPrefab = val;
				val3.m_stack = num4;
				val3.m_durability = val3.GetMaxDurability();
				int num5 = inventory.NrOfItemsIncludingStacks();
				if (inventory.CanAddItem(val3, num4))
				{
					inventory.AddItem(val3);
				}
				int num6 = inventory.NrOfItemsIncludingStacks() - num5;
				int num7 = num4 - num6;
				if (num7 > 0)
				{
					ItemData obj = val2.m_itemData.Clone();
					obj.m_dropPrefab = val;
					obj.m_durability = obj.GetMaxDurability();
					Vector3 val4 = ((Component)localPlayer).transform.position + ((Component)localPlayer).transform.forward * 0.7f + Vector3.up;
					ItemDrop.DropItem(obj, num7, val4, Quaternion.identity);
					num3 += num7;
				}
			}
			Gc.TopLeft(GcLoc.Get("gcss_received", new object[2]
			{
				amount,
				Plugin.ItemName(item)
			}));
			if (num3 > 0)
			{
				Gc.Center(GcLoc.Get("gcss_dropped", new object[1] { num3 }));
			}
			StationWindow.Refresh();
		}

		public static void Assign(ZNetView nview, string key, string value)
		{
			if (nview.IsOwner())
			{
				nview.GetZDO().Set("gc_ss_" + key, value);
			}
		}
	}
	[HarmonyPatch(typeof(Switch), "Interact")]
	internal static class SwitchInteractPatch
	{
		private static bool Prefix(Switch __instance, Humanoid character, bool hold, bool alt, ref bool __result)
		{
			if (!Plugin.MenuOn || !alt || hold || (Object)(object)character != (Object)(object)Player.m_localPlayer)
			{
				return true;
			}
			Smelter componentInParent = ((Component)__instance).GetComponentInParent<Smelter>();
			if ((Object)(object)componentInParent == (Object)null)
			{
				return true;
			}
			return OpenMenu.Try(Station.Of((Component?)(object)componentInParent), ref __result);
		}
	}
	[HarmonyPatch(typeof(Beehive), "Interact")]
	internal static class BeehiveInteractPatch
	{
		private static bool Prefix(Beehive __instance, Humanoid character, bool repeat, bool alt, ref bool __result)
		{
			if (!Plugin.MenuOn || !alt || repeat || (Object)(object)character != (Object)(object)Player.m_localPlayer)
			{
				return true;
			}
			return OpenMenu.Try(Station.Of((Component?)(object)__instance), ref __result);
		}
	}
	[HarmonyPatch(typeof(SapCollector), "Interact")]
	internal static class SapInteractPatch
	{
		private static bool Prefix(SapCollector __instance, Humanoid character, bool repeat, bool alt, ref bool __result)
		{
			if (!Plugin.MenuOn || !alt || repeat || (Object)(object)character != (Object)(object)Player.m_localPlayer)
			{
				return true;
			}
			return OpenMenu.Try(Station.Of((Component?)(object)__instance), ref __result);
		}
	}
	internal static class OpenMenu
	{
		public static string Hover => Localization.instance.Localize("[<color=yellow><b>$KEY_AltPlace + $KEY_Use</b></color>] ") + GcLoc.Get("gcss_hover", Array.Empty<object>());

		public static bool Try(Station? station, ref bool result)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			if (station == null || !station.Valid)
			{
				return true;
			}
			if (!PrivateArea.CheckAccess(station.Position, 0f, true, false))
			{
				return true;
			}
			StationWindow.Open(station);
			result = true;
			return false;
		}
	}
	[HarmonyPatch(typeof(Switch), "GetHoverText")]
	internal static class SwitchHoverPatch
	{
		private static void Postfix(Switch __instance, ref string __result)
		{
			if (Plugin.MenuOn && !string.IsNullOrEmpty(__result) && (Object)(object)((Component)__instance).GetComponentInParent<Smelter>() != (Object)null)
			{
				__result = __result + "\n" + OpenMenu.Hover;
			}
		}
	}
	[HarmonyPatch(typeof(Beehive), "GetHoverText")]
	internal static class BeehiveHoverPatch
	{
		private static void Postfix(ref string __result)
		{
			if (Plugin.MenuOn && !string.IsNullOrEmpty(__result))
			{
				__result = __result + "\n" + OpenMenu.Hover;
			}
		}
	}
	[HarmonyPatch(typeof(SapCollector), "GetHoverText")]
	internal static class SapHoverPatch
	{
		private static void Postfix(ref string __result)
		{
			if (Plugin.MenuOn && !string.IsNullOrEmpty(__result))
			{
				__result = __result + "\n" + OpenMenu.Hover;
			}
		}
	}
	[HarmonyPatch(typeof(Humanoid), "StartAttack")]
	internal static class AttackBlockPatch
	{
		private static bool Prefix(Humanoid __instance)
		{
			if (ChestPicker.Active)
			{
				return !((Object)(object)__instance == (Object)(object)Player.m_localPlayer);
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(Player), "UpdatePlacement")]
	internal static class PlacementBlockPatch
	{
		private static void Prefix(Player __instance, ref bool takeInput)
		{
			if (ChestPicker.Active && (Object)(object)__instance == (Object)(object)Player.m_localPlayer)
			{
				takeInput = false;
			}
		}
	}
	internal static class VPlusBridge
	{
		private enum Mode
		{
			None,
			Input,
			Output
		}

		private static readonly Stack<(Mode Mode, Smelter Station)> Context = new Stack<(Mode, Smelter)>();

		private static List<Container> original = new List<Container>();

		private static Mode mode
		{
			get
			{
				if (Context.Count <= 0)
				{
					return Mode.None;
				}
				return Context.Peek().Mode;
			}
		}

		private static Smelter? current
		{
			get
			{
				if (Context.Count <= 0)
				{
					return null;
				}
				return Context.Peek().Station;
			}
		}

		internal static void Apply(Harmony harmony)
		{
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: 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_0080: Expected O, but got Unknown
			//IL_0080: Expected O, but got Unknown
			//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)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Expected O, but got Unknown
			//IL_00d5: Expected O, but got Unknown
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: Expected O, but got Unknown
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_014c: Expected O, but got Unknown
			//IL_01c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d7: Expected O, but got Unknown
			//IL_020e: Unknown result type (might be due to invalid IL or missing references)
			//IL_021c: Expected O, but got Unknown
			if (!Gc.VPlusLoaded)
			{
				return;
			}
			Type type = AccessTools.TypeByName("ValheimPlus.InventoryAssistant");
			if (type == null)
			{
				Plugin.Log.LogWarning((object)"ValheimPlus gefunden, aber InventoryAssistant nicht. Kistenzuweisung wirkt nicht.");
				return;
			}
			harmony.Patch((MethodBase)AccessTools.Method(typeof(Smelter), "UpdateSmelter", (Type[])null, (Type[])null), new HarmonyMethod(typeof(VPlusBridge), "BeginInput", (Type[])null)
			{
				priority = 800
			}, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(VPlusBridge), "End", (Type[])null), (HarmonyMethod)null);
			harmony.Patch((MethodBase)AccessTools.Method(typeof(Smelter), "Spawn", (Type[])null, (Type[])null), new HarmonyMethod(typeof(VPlusBridge), "BeginOutput", (Type[])null)
			{
				priority = 800
			}, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(typeof(VPlusBridge), "End", (Type[])null), (HarmonyMethod)null);
			MethodInfo methodInfo = AccessTools.Method(type, "GetNearbyChestsForMachine", (Type[])null, (Type[])null);
			if (methodInfo != null)
			{
				harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(VPlusBridge), "MachineChests", (Type[])null)
				{
					priority = 0
				}, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			MethodInfo methodInfo2 = AccessTools.Method(type, "RemoveItemInAmountFromChests", (Type[])null, (Type[])null);
			if (methodInfo2 != null)
			{
				harmony.Patch((MethodBase)methodInfo2, new HarmonyMethod(typeof(VPlusBridge), "FilterChestList", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			foreach (MethodInfo item in from m in type.GetMethods(BindingFlags.Static | BindingFlags.Public)
				where m.Name == "RemoveItemFromChest"
				select m)
			{
				ParameterInfo[] parameters = item.GetParameters();
				if (parameters.Length == 3 && parameters[1].ParameterType == typeof(ItemData))
				{
					harmony.Patch((MethodBase)item, new HarmonyMethod(typeof(VPlusBridge), "FilterSingleItem", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				}
				else if (parameters.Length == 3 && parameters[1].ParameterType == typeof(string))
				{
					harmony.Patch((MethodBase)item, new HarmonyMethod(typeof(VPlusBridge), "FilterSingleName", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				}
			}
			Plugin.Log.LogInfo((object)"ValheimPlus erkannt: Ein- und Ausgangskisten pro Gebäude aktiv (auch Bienenstock, Vogelnest, Saftzapfer).");
		}

		private static void BeginInput(Smelter __instance)
		{
			Context.Push((Mode.Input, __instance));
		}

		private static void BeginOutput(Smelter __instance)
		{
			Context.Push((Mode.Output, __instance));
		}

		private static void End()
		{
			if (Context.Count > 0)
			{
				Context.Pop();
			}
			if (Context.Count == 0)
			{
				original = new List<Container>();
			}
		}

		private static void MachineChests(GameObject machine, ref List<Container> __result)
		{
			if (!Plugin.ChestsOn || (Object)(object)machine == (Object)null)
			{
				return;
			}
			if (mode == Mode.None)
			{
				Station station = Station.Of((Component?)(object)machine.transform);
				if (station != null && station.IsProducer && (Object)(object)station.GameObject == (Object)(object)machine)
				{
					List<Container> list = Plugin.Resolve(station, Plugin.Assigned(station, "out"));
					if (list.Count > 0)
					{
						__result = list;
					}
				}
			}
			else
			{
				if ((Object)(object)current == (Object)null || (Object)(object)machine != (Object)(object)((Component)current).gameObject)
				{
					return;
				}
				Station station2 = Station.Of((Component?)(object)current);
				if (mode == Mode.Output)
				{
					List<Container> list2 = Plugin.Resolve(station2, Plugin.Assigned(station2, "out"));
					if (list2.Count > 0)
					{
						__result = list2;
					}
					return;
				}
				original = new List<Container>(__result);
				List<Container> list3 = Plugin.Resolve(station2, Plugin.InputKeys(station2).SelectMany((string key) => Plugin.Assigned(station2, key)).Distinct());
				if (list3.Count == 0)
				{
					return;
				}
				List<Container> list4 = new List<Container>(__result);
				HashSet<Container> hashSet = new HashSet<Container>(list4.Where((Container c) => (Object)(object)c != (Object)null));
				foreach (Container item in list3)
				{
					if (hashSet.Add(item))
					{
						list4.Add(item);
					}
				}
				__result = list4;
			}
		}

		private static List<Container>? AllowedFor(string key)
		{
			if (mode != Mode.Input || (Object)(object)current == (Object)null)
			{
				return null;
			}
			Station station = Station.Of((Component?)(object)current);
			List<string> list = Plugin.Assigned(station, key);
			if (list.Count == 0)
			{
				return original;
			}
			List<Container> list2 = Plugin.Resolve(station, list);
			if (list2.Count <= 0)
			{
				return original;
			}
			return list2;
		}

		private static void FilterChestList(ref List<Container> chests, ItemData needle)
		{
			if (!Plugin.ChestsOn || (Object)(object)current == (Object)null || mode != Mode.Input || needle == null)
			{
				return;
			}
			List<Container> list = AllowedFor(Plugin.KeyForItem(current, needle));
			if (list != null)
			{
				HashSet<Container> set = new HashSet<Container>(list);
				chests = chests.Where((Container c) => (Object)(object)c != (Object)null && set.Contains(c)).ToList();
			}
		}

		private static bool FilterSingleItem(Container chest, ItemData needle, ref int __result)
		{
			if (!Plugin.ChestsOn || (Object)(object)current == (Object)null || mode != Mode.Input || needle == null)
			{
				return true;
			}
			List<Container> list = AllowedFor(Plugin.KeyForItem(current, needle));
			if (list != null && (Object)(object)chest != (Object)null && !list.Contains(chest))
			{
				__result = 0;
				return false;
			}
			return true;
		}

		private static bool FilterSingleName(Container chest, string needle, ref int __result)
		{
			if (!Plugin.ChestsOn || (Object)(object)current == (Object)null || mode != Mode.Input || needle == null)
			{
				return true;
			}
			List<Container> list = AllowedFor(Plugin.KeyForSharedName(current, needle));
			if (list != null && (Object)(object)chest != (Object)null && !list.Contains(chest))
			{
				__result = 0;
				return false;
			}
			return true;
		}
	}
	internal static class Highlighter
	{
		private static readonly Dictionary<GameObject, Color> Active = new Dictionary<GameObject, Color>();

		private static float nextRefresh;

		public static readonly Color Hover = new Color(1f, 0.9f, 0.2f);

		public static readonly Color Input = new Color(0.3f, 1f, 0.35f);

		public static readonly Color Output = new Color(1f, 0.3f, 0.25f);

		public static readonly Color Both = new Color(0.35f, 0.6f, 1f);

		public static readonly Color Station = new Color(1f, 0.55f, 0.15f);

		public static Color For(bool input, bool output)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			if (!(input && output))
			{
				if (!output)
				{
					return Input;
				}
				return Output;
			}
			return Both;
		}

		public static void Apply(Dictionary<GameObject, Color> wanted)
		{
			//IL_00df: 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_00c6: 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)
			bool flag = Time.time >= nextRefresh;
			if (flag)
			{
				nextRefresh = Time.time + 0.5f;
			}
			foreach (GameObject item in Active.Keys.ToList())
			{
				if ((Object)(object)item == (Object)null)
				{
					Active.Remove(item);
				}
				else if (!wanted.ContainsKey(item))
				{
					Gc.ResetHighlight(item);
					Active.Remove(item);
				}
			}
			foreach (KeyValuePair<GameObject, Color> item2 in wanted)
			{
				if (!((Object)(object)item2.Key == (Object)null) && (flag || !Active.TryGetValue(item2.Key, out var value) || value != item2.Value))
				{
					Gc.Highlight(item2.Key, item2.Value);
					Active[item2.Key] = item2.Value;
				}
			}
		}

		public static void Clear()
		{
			Apply(new Dictionary<GameObject, Color>());
		}

		public static Dictionary<GameObject, Color> Assigned(Station station)
		{
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			HashSet<Container> hashSet = new HashSet<Container>();
			foreach (string item in Plugin.InputKeys(station))
			{
				hashSet.UnionWith(Plugin.Resolve(station, Plugin.Assigned(station, item)));
			}
			HashSet<Container> hashSet2 = new HashSet<Container>(Plugin.Resolve(station, Plugin.Assigned(station, "out")));
			Dictionary<GameObject, Color> dictionary = new Dictionary<GameObject, Color>();
			foreach (Container item2 in hashSet.Union(hashSet2))
			{
				if ((Object)(object)item2 != (Object)null)
				{
					dictionary[((Component)item2).gameObject] = For(hashSet.Contains(item2), hashSet2.Contains(item2));
				}
			}
			return dictionary;
		}
	}
	internal static class ChestPicker
	{
		private static Station? station;

		private static string key = "";

		public static bool Active { get; private set; }

		public static void Begin(Station target, string assignKey)
		{
			station = target;
			key = assignKey;
			Active = true;
			Gc.Center(GcLoc.Get("gcss_pick_help", Array.Empty<object>()));
		}

		public static void Update()
		{
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			if (!Active)
			{
				return;
			}
			if ((Object)(object)Player.m_localPlayer == (Object)null || station == null || !station.Valid)
			{
				Cancel(backToMenu: false);
				return;
			}
			Container val = Gc.Hovered<Container>();
			if ((Object)(object)val != (Object)null && !GcWard.IsPlayerChest(val))
			{
				val = null;
			}
			Dictionary<GameObject, Color> dictionary = Highlighter.Assigned(station);
			dictionary[station.GameObject] = Highlighter.Station;
			if ((Object)(object)val != (Object)null)
			{
				dictionary[((Component)val).gameObject] = Highlighter.Hover;
			}
			Highlighter.Apply(dictionary);
			if (Gc.TextInputActive() || InventoryGui.IsVisible() || Minimap.IsOpen() || Menu.IsVisible() || StoreGui.IsVisible() || ((Object)(object)TextViewer.instance != (Object)null && TextViewer.instance.IsVisible()))
			{
				return;
			}
			if (Input.GetMouseButtonDown(0) && (Object)(object)val != (Object)null)
			{
				if (Toggle(val))
				{
					Cancel(backToMenu: true);
				}
			}
			else if (Input.GetMouseButtonDown(1))
			{
				Cancel(backToMenu: true);
			}
		}

		private static bool Toggle(Container chest)
		{
			//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_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Invalid comparison between Unknown and I4
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			List<string> list = Plugin.Assigned(station, key);
			string text = GcIds.Read(chest.m_nview);
			if (text.Length > 0 && list.Remove(text))
			{
				Save(list);
				Gc.TopLeft(GcLoc.Get("gcss_pick_removed", new object[1] { list.Count }));
				return true;
			}
			if (Vector3.Distance(((Component)chest).transform.position, station.Position) > Plugin.MaxChestDistance.Value)
			{
				Gc.Center(GcLoc.Get("gcss_pick_too_far", new object[1] { Plugin.MaxChestDistance.Value.ToString("0") }));
				return false;
			}
			if (!PrivateArea.CheckAccess(((Component)chest).transform.position, 0f, true, false))
			{
				return false;
			}
			if ((int)chest.m_privacy != 2)
			{
				Gc.Center(GcLoc.Get("gcss_pick_not_public", Array.Empty<object>()));
				return false;
			}
			if (!GcWard.SameWards(((Component)chest).transform.position, station.Position))
			{
				Gc.Center(GcLoc.Get("gcss_pick_wards", Array.Empty<object>()));
				return false;
			}
			string text2 = GcIds.Ensure(chest, chest.m_nview);
			if (text2 == null)
			{
				Gc.Center(GcLoc.Get("gcss_pick_busy", Array.Empty<object>()));
				return false;
			}
			list.Add(text2);
			Save(list);
			int count = Links.Group(chest).Count;
			Gc.TopLeft(GcLoc.Get((count > 1) ? "gcss_pick_added_group" : "gcss_pick_added", new object[2] { list.Count, count }));
			return true;
		}

		private static void Save(List<string> selected)
		{
			station.View.InvokeRPC("GC_SS_Assign", new object[2]
			{
				key,
				string.Join(";", selected.Distinct())
			});
		}

		public static void Cancel(bool backToMenu)
		{
			Station station = ChestPicker.station;
			Active = false;
			ChestPicker.station = null;
			Highlighter.Clear();
			if (backToMenu && station != null && station.Valid)
			{
				StationWindow.Open(station);
			}
		}
	}
	internal static class StationWindow
	{
		private static GameObject? window;

		private static Text? header;

		private static RectTransform? content;

		private static Station? station;

		private static string signature = "";

		private static float nextCheck;

		private static int openedFrame = -1;

		public static bool IsOpen
		{
			get
			{
				if ((Object)(object)window != (Object)null)
				{
					return window.activeSelf;
				}
				return false;
			}
		}

		public static void Open(Station target)
		{
			if (!ChestPicker.Active)
			{
				Build();
				station = target;
				signature = "";
				openedFrame = Time.frameCount;
				window.SetActive(true);
				GUIManager.BlockInput(true);
				Refresh();
			}
		}

		public static void Close()
		{
			if ((Object)(object)window != (Object)null && window.activeSelf)
			{
				window.SetActive(false);
				GUIManager.BlockInput(false);
			}
			station = null;
			if (!ChestPicker.Active)
			{
				Highlighter.Clear();
			}
		}

		public static void Update()
		{
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			if (!IsOpen)
			{
				return;
			}
			Player localPlayer = Player.m_localPlayer;
			bool flag = Input.GetKeyDown((KeyCode)27) && Time.frameCount != openedFrame;
			if ((Object)(object)localPlayer == (Object)null || station == null || !station.Valid || Vector3.Distance(((Component)localPlayer).transform.position, station.Position) > 10f || flag)
			{
				Close();
				return;
			}
			if (Plugin.HighlightWhileOpen.Value)
			{
				Highlighter.Apply(Highlighter.Assigned(station));
			}
			if (Time.time >= nextCheck)
			{
				nextCheck = Time.time + 0.5f;
				Refresh();
			}
		}

		public static void Refresh()
		{
			if (!IsOpen || station == null || !station.Valid)
			{
				return;
			}
			ZDO zDO = station.View.GetZDO();
			Inventory inventory = ((Humanoid)Player.m_localPlayer).GetInventory();
			StringBuilder stringBuilder = new StringBuilder();
			foreach (string item in Plugin.InputKeys(station).Append("out"))
			{
				stringBuilder.Append('|').Append(zDO.GetString("gc_ss_" + item, ""));
			}
			Smelter smelter = station.Smelter;
			if ((Object)(object)smelter != (Object)null)
			{
				stringBuilder.Append(string.Join(",", Plugin.Queue(zDO))).Append('|').Append(Mathf.FloorToInt(zDO.GetFloat(ZDOVars.s_fuel, 0f)))
					.Append('|')
					.Append(zDO.GetInt(ZDOVars.s_spawnAmount, 0))
					.Append(zDO.GetString(ZDOVars.s_spawnOre, ""));
				foreach (ItemConversion item2 in smelter.m_conversion)
				{
					if ((Object)(object)item2.m_from != (Object)null)
					{
						stringBuilder.Append('|').Append(GcHooks.CountUsable(inventory, item2.m_from.m_itemData.m_shared.m_name));
					}
				}
				if ((Object)(object)smelter.m_fuelItem != (Object)null)
				{
					stringBuilder.Append('|').Append(GcHooks.CountUsable(inventory, smelter.m_fuelItem.m_itemData.m_shared.m_name));
				}
			}
			else
			{
				stringBuilder.Append('|').Append(station.Level.Level);
			}
			if (!(stringBuilder.ToString() == signature))
			{
				signature = stringBuilder.ToString();
				GcUi.ClearChildren((Transform)(object)content);
				header.text = Localization.instance.Localize(station.Name);
				if ((Object)(object)smelter != (Object)null)
				{
					BuildSmelter(smelter, zDO, inventory);
				}
				else
				{
					BuildProducer(station);
				}
				BuildChests(station);
			}
		}

		private static void BuildSmelter(Smelter smelter, ZDO zdo, Inventory inventory)
		{
			List<string> list = Plugin.Queue(zdo);
			float num = zdo.GetFloat(ZDOVars.s_fuel, 0f);
			int num2 = zdo.GetInt(ZDOVars.s_spawnAmount, 0);
			string text = zdo.GetString(ZDOVars.s_spawnOre, "");
			Section(GcLoc.Get("gcss_section_queue", new object[2] { list.Count, smelter.m_maxOre }));
			if (list.Count == 0)
			{
				Row(GcLoc.Get("gcss_empty", Array.Empty<object>()));
			}
			foreach (IGrouping<string, string> item2 in from name in list
				group name by name)
			{
				string item = item2.Key;
				int count = item2.Count();
				Row($"{Plugin.ItemName(item)}  x{count}", GcLoc.Get("gcss_take", Array.Empty<object>()), delegate
				{
					smelter.m_nview.InvokeRPC("GC_SS_Take", new object[2] { item, count });
				});
			}
			if ((Object)(object)smelter.m_fuelItem != (Object)null)
			{
				int whole = Mathf.FloorToInt(num);
				Row(GcLoc.Get("gcss_fuel", new object[3]
				{
					Localization.instance.Localize(smelter.m_fuelItem.m_itemData.m_shared.m_name),
					Mathf.CeilToInt(num),
					smelter.m_maxFuel
				}), (whole > 0) ? GcLoc.Get("gcss_take", Array.Empty<object>()) : null, delegate
				{
					smelter.m_nview.InvokeRPC("GC_SS_TakeFuel", new object[1] { whole });
				});
			}
			if (num2 > 0)
			{
				ItemConversion itemConversion = smelter.GetItemConversion(text);
				string text2 = (((Object)(object)itemConversion?.m_to != (Object)null) ? Plugin.ItemName(((Object)((Component)itemConversion.m_to).gameObject).name) : text);
				Row(GcLoc.Get("gcss_ready", new object[2] { text2, num2 }), GcLoc.Get("gcss_take", Array.Empty<object>()), delegate
				{
					smelter.m_nview.InvokeRPC("GC_SS_TakeOutput", Array.Empty<object>());
				});
			}
			Section(GcLoc.Get("gcss_section_add", Array.Empty<object>()));
			bool flag = false;
			foreach (ItemConversion item3 in smelter.m_conversion)
			{
				if ((Object)(object)item3.m_from == (Object)null)
				{
					continue;
				}
				ItemData from = item3.m_from.m_itemData;
				int num3 = GcHooks.CountUsable(inventory, from.m_shared.m_name);
				if (num3 > 0)
				{
					flag = true;
					string prefab = ((Object)((Component)item3.m_from).gameObject).name;
					Row(GcLoc.Get("gcss_have", new object[2]
					{
						Localization.instance.Localize(from.m_shared.m_name),
						num3
					}), GcLoc.Get("gcss_add_all", Array.Empty<object>()), delegate
					{
						AddOre(smelter, prefab, from.m_shared.m_name);
					});
				}
			}
			if ((Object)(object)smelter.m_fuelItem != (Object)null)
			{
				string fuelName = smelter.m_fuelItem.m_itemData.m_shared.m_name;
				int num4 = GcHooks.CountUsable(inventory, fuelName);
				if (num4 > 0)
				{
					flag = true;
					Row(GcLoc.Get("gcss_have", new object[2]
					{
						Localization.instance.Localize(fuelName),
						num4
					}), GcLoc.Get("gcss_add_all", Array.Empty<object>()), delegate
					{
						AddFuel(smelter, fuelName);
					});
				}
			}
			if (!flag)
			{
				Row(GcLoc.Get("gcss_nothing_to_add", Array.Empty<object>()));
			}
		}

		private static void BuildProducer(Station producer)
		{
			var (num, num2) = producer.Level;
			Section(GcLoc.Get("gcss_section_yield", Array.Empty<object>()));
			Row(GcLoc.Get("gcss_level", new object[3] { producer.ProductName, num, num2 }), (num > 0) ? GcLoc.Get("gcss_take", Array.Empty<object>()) : null, delegate
			{
				producer.Extract();
			});
		}

		private static void BuildChests(Station target)
		{
			if (!Gc.VPlusLoaded || !Plugin.ChestsOn)
			{
				return;
			}
			Section(GcLoc.Get("gcss_section_chests", Array.Empty<object>()));
			Smelter smelter = target.Smelter;
			foreach (string item in Plugin.InputKeys(target))
			{
				string text = ((item == "fuel") ? Localization.instance.Localize(smelter.m_fuelItem.m_itemData.m_shared.m_name) : Plugin.ItemName(item));
				ChestRow(target, item, GcLoc.Get("gcss_input_chests", new object[2]
				{
					text,
					Plugin.Assigned(target, item).Count
				}));
			}
			ChestRow(target, "out", GcLoc.Get("gcss_output_chests", new object[1] { Plugin.Assigned(target, "out").Count }));
			Row("<i>" + GcLoc.Get(target.IsProducer ? "gcss_chests_note_producer" : "gcss_chests_note", Array.Empty<object>()) + "</i>");
		}

		private static void ChestRow(Station target, string key, string text)
		{
			List<string> list = Plugin.Assigned(target, key);
			int num = list.Count((string id) => (Object)(object)GcIds.Find(id) != (Object)null);
			Row(text + ((num < list.Count) ? (" " + GcLoc.Get("gcss_found", new object[1] { num })) : ""), GcLoc.Get("gcss_pick", Array.Empty<object>()), delegate
			{
				Pick(target, key);
			}, (list.Count > 0) ? GcLoc.Get("gcss_clear", Array.Empty<object>()) : null, delegate
			{
				target.View.InvokeRPC("GC_SS_Assign", new object[2] { key, "" });
			});
		}

		private static void Pick(Station target, string key)
		{
			Close();
			ChestPicker.Begin(target, key);
		}

		private static void AddOre(Smelter smelter, string prefab, string sharedName)
		{
			GcHooks.RedCraft++;
			try
			{
				AddOreInner(smelter, prefab, sharedName);
			}
			finally
			{
				GcHooks.Leave(ref GcHooks.RedCraft);
			}
		}

		private static void AddOreInner(Smelter smelter, string prefab, string sharedName)
		{
			Inventory inventory = ((Humanoid)Player.m_localPlayer).GetInventory();
			int num = Mathf.Min(smelter.m_maxOre - smelter.GetQueueSize(), inventory.CountItems(sharedName, -1, true));
			if (num <= 0)
			{
				Gc.Center("$msg_itsfull");
				return;
			}
			for (int i = 0; i < num; i++)
			{
				ItemData item = inventory.GetItem(sharedName, -1, false);
				if (item == null)
				{
					break;
				}
				bool cheated = item.m_cheated;
				inventory.RemoveItem(item, 1);
				smelter.m_nview.InvokeRPC("RPC_AddOre", new object[2] { prefab, cheated });
			}
			Gc.TopLeft(GcLoc.Get("gcss_added", new object[2]
			{
				num,
				Plugin.ItemName(prefab)
			}));
		}

		private static void AddFuel(Smelter smelter, string sharedName)
		{
			GcHooks.RedCraft++;
			try
			{
				AddFuelInner(smelter, sharedName);
			}
			finally
			{
				GcHooks.Leave(ref GcHooks.RedCraft);
			}
		}

		private static void AddFuelInner(Smelter smelter, string sharedName)
		{
			Inventory inventory = ((Humanoid)Player.m_localPlayer).GetInventory();
			int num = Mathf.Min(Mathf.FloorToInt((float)smelter.m_maxFuel - smelter.GetFuel()), inventory.CountItems(sharedName, -1, true));
			if (num <= 0)
			{
				Gc.Center("$msg_itsfull");
				return;
			}
			inventory.RemoveItem(sharedName, num, -1, true);
			for (int i = 0; i < num; i++)
			{
				smelter.m_nview.InvokeRPC("RPC_AddFuel", Array.Empty<object>());
			}
			Gc.TopLeft(GcLoc.Get("gcss_added", new object[2]
			{
				num,
				Localization.instance.Localize(sharedName)
			}));
		}

		private static void Section(string text)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: 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)
			GcUi.CreateLabel((Transform)(object)GcUi.CreateRow((Transform)(object)content, 34f), text, Vector2.zero, Vector2.one, new Vector2(4f, 0f), Vector2.zero, 22, (TextAnchor)6, (Color?)GUIManager.Instance.ValheimOrange, true);
		}

		private static void Row(string text, string? button1 = null, Action? action1 = null, string? button2 = null, Action? action2 = null)
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: 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_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			RectTransform val = GcUi.CreateRow((Transform)(object)content, 40f);
			float num = ((button1 == null) ? 0f : ((button2 == null) ? 150f : 300f));
			GcUi.CreateLabel((Transform)(object)val, text, Vector2.zero, Vector2.one, new Vector2(12f, 0f), new Vector2(0f - num, 0f), 19, (TextAnchor)3, (Color?)null, false);
			if (button1 != null && action1 != null)
			{
				GcUi.CreateButton((Transform)(object)val, button1, new Vector2(1f, 0.5f), new Vector2((button2 == null) ? (-72f) : (-222f), 0f), 140f, 34f, action1);
			}
			if (button2 != null && action2 != null)
			{
				GcUi.CreateButton((Transform)(object)val, button2, new Vector2(1f, 0.5f), new Vector2(-72f, 0f), 140f, 34f, action2);
			}
		}

		private static void Build()
		{
			//IL_0066: 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)
			if (!((Object)(object)window != (Object)null))
			{
				RectTransform val = default(RectTransform);
				window = GcUi.CreateWindow("GC_StationStorage_Window", 860f, 640f, ref header, ref val);
				content = GcUi.CreateScroll((Transform)(object)val, 780f, 470f);
				GcUi.CreateButton(window.transform, GcLoc.Get("gcss_close", Array.Empty<object>()), new Vector2(0.5f, 0f), new Vector2(0f, 44f), 200f, 44f, (Action)Close);
			}
		}
	}
}
namespace GC.ValheimPlusPlus.ModKisten
{
	[BepInPlugin("gorduan.gc.kisten", "GC_Kisten", "1.5.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		internal static GcModule Module;

		private void Awake()
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Expected O, but got Unknown
			Module = GcKern.Register((BaseUnityPlugin)(object)this, ((BaseUnityPlugin)this).Logger, GcMod.Info);
			VppPlugin.Log = ((BaseUnityPlugin)this).Logger;
			VppPlugin.Module = Module;
			Harmony val = new Harmony("gorduan.gc.kisten");
			GC.ValheimPlusPlus.LinkedChests.Plugin.Init(((BaseUnityPlugin)this).Config, val);
			GC.ValheimPlusPlus.StationStorage.Plugin.Init(((BaseUnityPlugin)this).Config, val);
			Module.TakeOverOldSettings();
			val.PatchAll(typeof(Plugin).Assembly);
			Module.MenuGroup((Func<string, string>)((string key) => GcLoc.Get("vpp_" + key, Array.Empty<object>())), (string)null).Main("linked", (ConfigEntryBase)(object)GC.ValheimPlusPlus.LinkedChests.Plugin.Enabled, (Func<bool>)(() => GC.ValheimPlusPlus.LinkedChests.Plugin.Blocked), "GC_LinkedChests", (Func<string>)null, 0.0, "", (Func<bool>)null, (Func<bool>)null).Sub("linked_key", (ConfigEntryBase)(object)GC.ValheimPlusPlus.LinkedChests.Plugin.LinkKey, 0.0, "", (Func<bool>)null, "", (Func<bool>)null, (Func<bool>)null)
				.Sub("linked_distance", (ConfigEntryBase)(object)GC.ValheimPlusPlus.LinkedChests.Plugin.MaxDistance, 5.0, " m", (Func<bool>)null, "", (Func<bool>)null, (Func<bool>)null)
				.Main("stationmenu", (ConfigEntryBase)(object)GC.ValheimPlusPlus.StationStorage.Plugin.MenuEnabled, (Func<bool>)(() => GC.ValheimPlusPlus.StationStorage.Plugin.Blocked), "GC_StationStorage", (Func<string>)null, 0.0, "", (Func<bool>)null, (Func<bool>)null)
				.Main("stationchests", (ConfigEntryBase)(object)GC.ValheimPlusPlus.StationStorage.Plugin.ChestsEnabled, (Func<bool>)(() => GC.ValheimPlusPlus.StationStorage.Plugin.Blocked), "GC_StationStorage", (Func<string>)null, 0.0, "", (Func<bool>)(() => Gc.VPlusLoaded), (Func<bool>)null)
				.Sub("stationchests_distance", (ConfigEntryBase)(object)GC.ValheimPlusPlus.StationStorage.Plugin.MaxChestDistance, 5.0, " m", (Func<bool>)null, "", (Func<bool>)null, (Func<bool>)null)
				.Sub("stationchests_highlight", (ConfigEntryBase)(object)GC.ValheimPlusPlus.StationStorage.Plugin.HighlightWhileOpen, 0.0, "", (Func<bool>)null, "", (Func<bool>)null, (Func<bool>)null);
		}

		private void Update()
		{
			if (!Gc.IsHeadless)
			{
				Module.Guard("Verbundene Kisten", (Action)GC.ValheimPlusPlus.LinkedChests.Plugin.Update);
				Module.Guard("Gebäude-Menü", (Action)GC.ValheimPlusPlus.StationStorage.Plugin.Update);
			}
		}
	}
	internal static class GcMod
	{
		public const string Id = "kisten";

		public const string Guid = "gorduan.gc.kisten";

		public const string Name = "GC_Kisten";

		public const string Version = "1.5.1";

		public const int Stufe = 3;

		public static GcModInfo Info
		{
			get
			{
				//IL_0000: Unknown result type (might be due to invalid IL or missing references)
				//IL_0006: Expected O, but got Unknown
				//IL_003b: Unknown result type (might be due to invalid IL or missing references)
				GcModInfo val = new GcModInfo();
				val.Id = "kisten";
				val.Guid = "gorduan.gc.kisten";
				val.Name = "GC_Kisten";
				val.Version = "1.5.1";
				val.Stufe = 3;
				val.Seite = (GcSeite)1;
				val.Braucht = Array.Empty<string>();
				val.Partner = new string[1] { "schloesser" };
				val.Fremd = new string[1] { "org.bepinex.plugins.valheim_plus" };
				val.AlteConfig = "gorduan.valheimplusplus.cfg";
				val.Ausgleich = 0f;
				return val;
			}
		}
	}
}
namespace GC.ValheimPlusPlus.LinkedChests
{
	internal static class Plugin
	{
		public const string Guid = "gorduan.gc_linkedchests";

		public const string ModName = "GC_LinkedChests";

		public const string Version = "1.0.0";

		internal static ManualLogSource Log;

		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<KeyboardShortcut> LinkKey;

		internal static ConfigEntry<float> MaxDistance;

		private static Container? selectedMaster;

		internal static bool Blocked => Chainloader.PluginInfos.ContainsKey("gorduan.gc_linkedchests");

		internal static bool On
		{
			get
			{
				if (Enabled.Value && !Blocked && VppPlugin.Allowed)
				{
					return VppPlugin.OnServer;
				}
				return false;
			}
		}

		internal static void Init(ConfigFile Config, Harmony harmony)
		{
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			Log = VppPlugin.Log;
			Enabled = Gc.BindSynced<bool>(Config, "3 - Linked Chests", "Enabled", true, "Verbundene Kisten: eine Kiste öffnen zeigt den Inhalt aller verbundenen Kisten (wird vom Server vorgegeben).", (AcceptableValueBase)null);
			LinkKey = Gc.Bind<KeyboardShortcut>(Config, "3 - Linked Chests", "LinkKey", new KeyboardShortcut((KeyCode)107, (KeyCode[])(object)new KeyCode[1] { (KeyCode)308 }), "Auf eine Kiste schauen und diese Tasten drücken: Hauptkiste wählen, Kiste verbinden oder lösen.", (AcceptableValueBase)null);
			MaxDistance = Gc.BindSynced<float>(Config, "3 - Linked Chests", "MaxDistance", 50f, "Größte Entfernung zwischen Hauptkiste und verbundener Kiste in Metern.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(2f, 200f));
			VPlusDedupe.Apply(harmony);
			AppDomain.CurrentDomain.SetData("GC.LinkedChests.Healthy.v1", new Func<bool>(LinkedView.EnsureHealthy));
		}

		internal static void Update()
		{
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0152: Unknown result type (might be due to invalid IL or missing references)
			if (Gc.IsHeadless)
			{
				return;
			}
			LinkedView.Tick();
			if ((Object)(object)Player.m_localPlayer == (Object)null)
			{
				return;
			}
			if (!On)
			{
				selectedMaster = null;
				GroupHighlight.Update(null, null);
				return;
			}
			if (StationWindow.IsOpen || ChestPicker.Active)
			{
				GroupHighlight.Update(null, null);
				return;
			}
			Container val = Gc.Hovered<Container>();
			if (!GcWard.IsPlayerChest(val))
			{
				val = null;
			}
			GroupHighlight.Update(val, selectedMaster);
			if ((Object)(object)val == (Object)null || !Gc.Down(LinkKey) || InventoryGui.IsVisible() || !PrivateArea.CheckAccess(((Component)val).transform.position, 0f, true, false))
			{
				return;
			}
			if (Links.HasLink(val))
			{
				if (Links.Unlink(val))
				{
					Gc.Center(GcLoc.Get("gclc_unlinked", Array.Empty<object>()));
				}
				return;
			}
			long playerID = Player.m_localPlayer.GetPlayerID();
			if ((Object)(object)selectedMaster == (Object)(object)val)
			{
				selectedMaster = null;
				Gc.Center(GcLoc.Get("gclc_master_cleared", Array.Empty<object>()));
			}
			else if ((Object)(object)selectedMaster == (Object)null || !selectedMaster.m_nview.IsValid())
			{
				if (!val.CheckAccess(playerID))
				{
					Gc.Center("$msg_cantopen");
					return;
				}
				selectedMaster = val;
				Gc.Center(GcLoc.Get("gclc_master_selected", new object[2]
				{
					Localization.instance.Localize(val.m_name),
					((object)LinkKey.Value/*cast due to .constrained prefix*/).ToString()
				}));
			}
			else
			{
				TryLink(val, selectedMaster, playerID);
			}
		}

		private static void TryLink(Container proxy, Container master, long playerId)
		{
			//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_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			if (Vector3.Distance(((Component)master).transform.position, ((Component)proxy).transform.position) > MaxDistance.Value)
			{
				Gc.Center(GcLoc.Get("gclc_too_far", new object[1] { MaxDistance.Value.ToString("0") }));
			}
			else if (Links.IsMaster(proxy))
			{
				Gc.Center(GcLoc.Get("gclc_is_master", Array.Empty<object>()));
			}
			else if (Links.HasLink(master))
			{
				selectedMaster = null;
				Gc.Center(GcLoc.Get("gclc_master_linked", Array.Empty<object>()));
			}
			else if (proxy.m_privacy != master.m_privacy || !proxy.CheckAccess(playerId) || !master.CheckAccess(playerId))
			{
				Gc.Center(GcLoc.Get("gclc_privacy", Array.Empty<object>()));
			}
			else if (!PrivateArea.CheckAccess(((Component)master).transform.position, 0f, true, false) || !GcWard.SameWards(((Component)master).transform.position, ((Component)proxy).transform.position))
			{
				Gc.Center(GcLoc.Get("gclc_wards", Array.Empty<object>()));
			}
			else if (Links.Link(proxy, master))
			{
				Gc.Center(GcLoc.Get("gclc_linked", new object[1] { Links.Group(master).Count }));
			}
		}
	}
	internal static class Links
	{
		private static readonly HashSet<Container> Known = new HashSet<Container>();

		public static void Register(Container c)
		{
			GcIds.Track(c, c.m_nview);
			Known.Add(c);
		}

		public static void Forget(Container c)
		{
			Known.Remove(c);
			GcIds.Untrack(c);
		}

		public static string OwnId(Container c)
		{
			return GcIds.Read(c.m_nview);
		}

		public static string MasterId(Container c)
		{
			if (!((Object)(object)c != (Object)null) || !((Object)(object)c.m_nview != (Object)null) || !c.m_nview.IsValid())
			{
				return "";
			}
			return c.m_nview.GetZDO().GetString("gc_link_master_uid", "");
		}

		public static bool HasLink(Container c)
		{
			return MasterId(c).Length > 0;
		}

		private static string RootId(Container c)
		{
			string text = MasterId(c);
			if (text.Length <= 0)
			{
				return OwnId(c);
			}
			return text;
		}

		public static bool IsMaster(Container c)
		{
			string id = OwnId(c);
			if (id.Length > 0)
			{
				return Known.Any((Container k) => (Object)(object)k != (Object)null && (Object)(object)k != (Object)(object)c && MasterId(k) == id);
			}
			return false;
		}

		public static List<Container> Group(Container c)
		{
			List<Container> result = new List<Container> { c };
			if (!Plugin.On || (Object)(object)c == (Object)null || !GcWard.IsPlayerChest(c))
			{
				return result;
			}
			string root = RootId(c);
			if (root.Length == 0)
			{
				return result;
			}
			Known.RemoveWhere((Container k) => (Object)(object)k == (Object)null);
			List<Container> list = Known.Where((Container k) => (Object)(object)k.m_nview != (Object)null && k.m_nview.IsValid() && MasterId(k) == root).OrderBy<Container, string>(OwnId, StringComparer.Ordinal).ToList();
			Container val = GcIds.Find(root);
			if ((Object)(object)val != (Object)null && !list.Contains(val))
			{
				list.Insert(0, val);
			}
			if (list.Count <= 1 || !list.Contains(c))
			{
				return result;
			}
			return list;
		}

		public static bool Link(Container proxy, Container master)
		{
			string text = GcIds.Ensure(master, master.m_nview);
			ZNetView nview = proxy.m_nview;
			if (text == null || GcIds.InUse(nview) || !Gc.Own(nview) || GcIds.Ensure(proxy, nview) == null)
			{
				Gc.Center(GcLoc.Get("gclc_busy", Array.Empty<object>()));
				return false;
			}
			nview.GetZDO().Set("gc_link_master_uid", text);
			return true;
		}

		public static bool Unlink(Container proxy)
		{
			ZNetView nview = proxy.m_nview;
			if (GcIds.InUse(nview) || proxy.m_inUse || !Gc.Own(nview))
			{
				Gc.Center(GcLoc.Get("gclc_busy", Array.Empty<object>()));
				return false;
			}
			nview.GetZDO().Set("gc_link_master_uid", "");
			return true;
		}
	}
	internal static class LinkedView
	{
		private sealed class Member
		{
			public Container Chest;

			public Inventory Real;

			public int Order;

			public int Offset;

			public int Width = 1;

			public int Capacity;

			public int Synced;

			public bool InView;
		}

		internal static Container? Shown;

		internal static Inventory? Virtual;

		private static readonly List<Container> GroupOrder = new List<Container>();

		private static readonly List<Member> Members = new List<Member>();

		private static readonly HashSet<Container> Pending = new HashSet<Container>();

		private static readonly HashSet<Container> Granted = new HashSet<Container>();

		private static readonly List<ItemData> Homeless = new List<ItemData>();

		private static readonly Dictionary<Container, float> Recent = new Dictionary<Container, float>();

		private const float LateAnswerWindow = 30f;

		private static int width = 1;

		private static int total;

		private static float startedAt;

		private static bool applying;

		private static bool rebuildWanted;

		private static bool built;

		private static bool reportedBusy;

		private static int batchDepth;

		private static bool batchDirty;

		private static readonly List<Container> NoContainers = new List<Container>();

		public static bool Active
		{
			get
			{
				if ((Object)(object)Shown != (Object)null && Virtual != null)
				{
					return built;
				}
				return false;
			}
		}

		public static int Capacity => total;

		public static bool IsVirtual(Inventory? inventory)
		{
			if (inventory != null && Virtual != null)
			{
				return inventory == Virtual;
			}
			return false;
		}

		public static Inventory? RealOf(Container c)
		{
			foreach (Member member in Members)
			{
				if (member.InView && (Object)(object)member.Chest == (Object)(object)c)
				{
					return member.Real;
				}
			}
			return null;
		}

		public static Container Canonical(Container c)
		{
			if (!Active || RealOf(c) == null)
			{
				return c;
			}
			return Shown;
		}

		public static void CanonicalizeList(ref List<Container> list)
		{
			if (list == null || list.Count == 0)
			{
				return;
			}
			EnsureHealthy();
			bool flag = ViewLocked();
			List<Container> list2 = new List<Container>(list.Count);
			HashSet<Container> hashSet = new HashSet<Container>();
			HashSet<Inventory> hashSet2 = new HashSet<Inventory>();
			foreach (Container item in list)
			{
				if ((Object)(object)item == (Object)null || !Usable(item))
				{
					continue;
				}
				Container val = Canonical(item);
				if (!flag || !((Object)(object)val == (Object)(object)Shown))
				{
					Inventory inventory = val.GetInventory();
					if (inventory != null && hashSet.Add(val) && hashSet2.Add(inventory))
					{
						list2.Add(val);
					}
				}
			}
			list = list2;
		}

		public static bool Usable(Container c)
		{
			ZNetView nview = c.m_nview;
			if ((Object)(object)nview == (Object)null || !nview.IsValid() || c.m_lastRevision == uint.MaxValue)
			{
				return false;
			}
			if (!nview.IsOwner())
			{
				return nview.GetZDO().GetInt(ZDOVars.s_inUse, 0) != 1;
			}
			return true;
		}

		public static bool ViewLocked()
		{
			if (!Active)
			{
				return false;
			}
			foreach (Member member in Members)
			{
				if (member.InView && Gc.IsAutoLocked(member.Chest))
				{
					return true;
				}
			}
			return false;
		}

		public static bool LockedView(Container c)
		{
			if (Active && (Object)(object)c == (Object)(object)Shown)
			{
				return ViewLocked();
			}
			return false;
		}

		public static bool EnsureHealthy()
		{
			if ((Object)(object)Shown == (Object)null)
			{
				return true;
			}
			foreach (Member member in Members)
			{
				if ((Object)(object)member.Chest == (Object)null || (Object)(object)member.Chest.m_nview == (Object)null || !member.Chest.m_nview.IsValid() || !member.Chest.m_nview.IsOwner())
				{
					Gc.Center(GcLoc.Get("gclc_view_lost", Array.Empty<object>()));
					Close();
					return false;
				}
			}
			return true;
		}

		private static void Close()
		{
			InventoryGui instance = InventoryGui.instance;
			if ((Object)(object)instance != (Object)null && (Object)(object)Shown != (Object)null && (Object)(object)instance.m_currentContainer == (Object)(object)Shown)
			{
				instance.CloseContainer();
			}
			End();
		}

		public static void Release(Container c)
		{
			if ((Object)(object)Shown != (Object)null && ((Object)(object)c == (Object)(object)Shown || Pending.Contains(c) || Granted.Contains(c) || Members.Any((Member m) => (Object)(object)m.Chest == (Object)(object)c)))
			{
				Close();
			}
		}

		public static void UserRequested(Container c)
		{
			Recent.Remove(c);
		}

		public static void BeginBatch()
		{
			batchDepth++;
		}

		public static void EndBatch()
		{
			if (batchDepth != 0 && --batchDepth <= 0 && batchDirty)
			{
				batchDirty = false;
				if (Active)
				{
					Distribute();
				}
			}
		}

		public static void Begin(Container shown)
		{
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Expected O, but got Unknown
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			End();
			List<Container> list = Links.Group(shown);
			if (list.Count < 2 || (Object)(object)Game.instance == (Object)null)
			{
				return;
			}
			Shown = shown;
			GroupOrder.AddRange(list);
			Inventory view = new Inventory(shown.m_name, shown.m_bkg, 1, 1);
			view.m_onChanged = (Action)Delegate.Combine(view.m_onChanged, (Action)delegate
			{
				OnVirtualChanged(view);
			});
			Virtual = view;
			long playerID = Game.instance.GetPlayerProfile().GetPlayerID();
			bool flag = false;
			foreach (KeyValuePair<Container, float> item in Recent.Where<KeyValuePair<Container, float>>((KeyValuePair<Container, float> r) => (Object)(object)r.Key == (Object)null || Time.time - r.Value > 30f).ToList())
			{
				Recent.Remove(item.Key);
			}
			foreach (Container item2 in list)
			{
				if (!((Object)(object)item2 == (Object)(object)shown) && !((Object)(object)item2.m_nview == (Object)null) && item2.m_nview.IsValid())
				{
					if (!PrivateArea.CheckAccess(((Component)item2).transform.position, 0f, false, false))
					{
						flag = true;
						continue;
					}
					Pending.Add(item2);
					Recent[item2] = Time.time;
					item2.m_nview.InvokeRPC("RPC_RequestOpen", new object[1] { playerID });
				}
			}
			if (flag)
			{
				Gc.Center(GcLoc.Get("gclc_view_ward", Array.Empty<object>()));
			}
			startedAt = Time.time;
			rebuildWanted = true;
		}

		public static bool HandleOpenResponse(Container c, bool granted)
		{
			if (Pending.Remove(c))
			{
				Recent.Remove(c);
				if (granted)
				{
					Granted.Add(c);
				}
				else
				{
					ReportBusy();
				}
				return true;
			}
			if ((Object)(object)Shown != (Object)null && (Object)(object)c != (Object)(object)Shown && (Granted.Contains(c) || Members.Any((Member m) => (Object)(object)m.Chest == (Object)(object)c)))
			{
				return true;
			}
			if (Recent.TryGetValue(c, out var value))
			{
				Recent.Remove(c);
				return Time.time - value < 30f;
			}
			return false;
		}

		private static void ReportBusy()
		{
			if (!reportedBusy)
			{
				reportedBusy = true;
				Gc.Center(GcLoc.Get("gclc_view_busy", Array.Empty<object>()));
			}
		}

		public static void Tick()
		{
			if ((Object)(object)Shown == (Object)null)
			{
				return;
			}
			InventoryGui instance = InventoryGui.instance;
			if ((Object)(object)instance == (Object)null || (Object)(object)instance.m_currentContainer != (Object)(object)Shown || (Object)(object)Shown.m_nview == (Object)null || !Shown.m_nview.IsValid())
			{
				End();
				return;
			}
			if (Shown.m_nview.IsOwner())
			{
				Acquire(Shown, load: false);
			}
			foreach (Container item in (List<Container>)((Granted.Count > 0) ? ((IList)Granted.ToList()) : ((IList)NoContainers)))
			{
				if ((Object)(object)item == (Object)null || (Object)(object)item.m_nview == (Object)null || !item.m_nview.IsValid())
				{
					Granted.Remove(item);
				}
				else if (item.m_nview.IsOwner())
				{
					Granted.Remove(item);
					Acquire(item, load: true);
				}
			}
			if (Pending.Count > 0 && Time.time - startedAt > 4f)
			{
				Pending.Clear();
				ReportBusy();
			}
			if (Granted.Count > 0 && Time.time - startedAt > 10f)
			{
				Granted.Clear();
				ReportBusy();
			}
			if (!EnsureHealthy())
			{
				return;
			}
			foreach (Member member in Members)
			{
				if ((Object)(object)member.Chest != (Object)(object)Shown)
				{
					member.Chest.SetInUse(true);
				}
				if (built && member.InView && Signature(member.Real.m_inventory) != member.Synced)
				{
					rebuildWanted = true;
				}
			}
			if (rebuildWanted && HasMember(Shown) && CanRebuild(instance))
			{
				rebuildWanted = false;
				Rebuild();
			}
		}

		private static bool HasMember(Container c)
		{
			foreach (Member member in Members)
			{
				if ((Object)(object)member.Chest == (Object)(object)c)
				{
					return true;
				}
			}
			return false;
		}

		private static void Acquire(Container c, bool load)
		{
			if (!HasMember(c) && !IsVirtual(c.m_inventory))
			{
				if (load)
				{
					c.Load();
					c.SetInUse(true);
				}
				Members.Add(new Member
				{
					Chest = c,
					Real = c.m_inventory,
					Order = Math.Max(0, GroupOrder.IndexOf(c))
				});
				rebuildWanted = true;
			}
		}

		private static bool CanRebuild(InventoryGui gui)
		{
			if (gui.m_dragItem == null && (Object)(object)gui.m_dragGo == (Object)null)
			{
				if (!((Object)(object)gui.m_splitDialog == (Object)null))
				{
					return !gui.m_splitDialog.IsActive;
				}
				return true;
			}
			return false;
		}

		public static void End()
		{
			if ((Object)(object)Shown == (Object)null && Members.Count == 0)
			{
				return;
			}
			Inventory val = Virtual;
			InventoryGui instance = InventoryGui.instance;
			if ((Object)(object)instance != (Object)null && val != null)
			{
				if (instance.m_dragInventory == val)
				{
					instance.SetupDragItem((ItemData)null, (Inventory)null, 1);
				}
				if (instance.m_splitInventory == val)
				{
					instance.OnSplitCancel();
				}
			}
			bool flag = (Object)(object)instance != (Object)null && (Object)(object)instance.m_currentContainer == (Object)(object)Shown;
			foreach (Member member in Members)
			{
				if ((Object)(object)member.Chest == (Object)null)
				{
					continue;
				}
				if (IsVirtual(member.Chest.m_inventory))
				{
					member.Chest.m_inventory = member.Real;
				}
				if (!((Object)(object)member.Chest == (Object)(object)Shown && flag))
				{
					if ((Object)(object)member.Chest.m_nview != (Object)null && member.Chest.m_nview.IsValid() && member.Chest.m_nview.IsOwner())
					{
						member.Chest.SetInUse(false);
					}
					else
					{
						member.Chest.m_inUse = false;
					}
				}
			}
			val?.m_inventory.Clear();
			DropHomeless();
			Members.Clear();
			GroupOrder.Clear();
			Pending.Clear();
			Granted.Clear();
			Shown = null;
			Virtual = null;
			built = false;
			rebuildWanted = false;
			reportedBusy = false;
			batchDepth = 0;
			batchDirty = false;
			total = 0;
			width = 1;
		}

		private static void DropHomeless()
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: 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)
			Player localPlayer = Player.m_localPlayer;
			foreach (ItemData item in Homeless)
			{
				if ((Object)(object)localPlayer != (Object)null)
				{
					Vector3 val = ((Component)localPlayer).transform.position + ((Component)localPlayer).transform.forward + Vector3.up;
					ItemDrop.DropItem(item, item.m_stack, val, Quaternion.identity);
					Plugin.Log.LogWarning((object)$"Verbundene Kisten: {item.m_shared.m_name} x{item.m_stack} hatte keinen Platz und liegt jetzt am Boden.");
				}
			}
			Homeless.Clear();
		}

		private static int Signature(List<ItemData> items)
		{
			int num = items.Count * 7919;
			foreach (ItemData item in items)
			{
				int a = Mix(item.m_shared.m_name?.GetHashCode() ?? 0, item.m_gridPos.x);
				a = Mix(a, item.m_gridPos.y);
				a = Mix(a, item.m_stack);
				a = Mix(a, item.m_quality);
				a = Mix(a, item.m_variant);
				a = Mix(a, item.m_durability.GetHashCode());
				if (item.m_customData != null)
				{
					foreach (KeyValuePair<string, string> customDatum in item.m_customData)
					{
						a += Mix(customDatum.Key?.GetHashCode() ?? 0, customDatum.Value?.GetHashCode() ?? 0);
					}
				}
				num += Mix(a, 625341585);
			}
			return num;
		}

		private static int Mix(int a, int b)
		{
			int num = (a * -1640531535) ^ b;
			int num2 = (num ^ (num >>> 15)) * -2048144777;
			return num2 ^ (num2 >>> 13);
		}

		private static bool SameContent(List<ItemData> a, List<ItemData> b)
		{
			if (a.Count != b.Count)
			{
				return false;
			}
			Dictionary<long, ItemData> dictionary = new Dictionary<long, ItemData>(a.Count);
			foreach (ItemData item in a)
			{
				long key = ((long)item.m_gridPos.y << 32) | (uint)item.m_gridPos.x;
				if (dictionary.ContainsKey(key))
				{
					return false;
				}
				dictionary[key] = item;
			}
			foreach (ItemData item2 in b)
			{
				long key2 = ((long)item2.m_gridPos.y << 32) | (uint)item2.m_gridPos.x;
				if (!dictionary.TryGetValue(key2, out var value) || !SameItem(item2, value))
				{
					return false;
				}
			}
			return true;
		}

		private static bool SameItem(ItemData a, ItemData b)
		{
			if (a.m_shared.m_name != b.m_shared.m_name || a.m_stack != b.m_stack || a.m_quality != b.m_quality || a.m_variant != b.m_variant || !Mathf.Approximately(a.m_durability, b.m_durability) || a.m_worldLevel != b.m_worldLevel || a.m_crafterID != b.m_crafterID || a.m_crafterName != b.m_crafterName || a.m_pickedUp != b.m_pickedUp || (Object)(object)a.m_dropPrefab != (Object)(object)b.m_dropPrefab)
			{
				return false;
			}
			int num = a.m_customData?.Count ?? 0;
			int num2 = b.m_customData?.Count ?? 0;
			if (num != num2)
			{
				return false;
			}
			if (num > 0)
			{
				foreach (KeyValuePair<string, string> customDatum in a.m_customData)
				{
					if (!b.m_customData.TryGetValue(customDatum.Key, out var value) || value != customDatum.Value)
					{
						return false;
					}
				}
			}
			return true;
		}

		private static void Rebuild()
		{
			//IL_0238: Unknown result type (might be due to invalid IL or missing references)
			//IL_023d: Unknown result type (might be due to invalid IL or missing references)
			Inventory val = Virtual;
			Members.Sort((Member a, Member b) => a.Order.CompareTo(b.Order));
			width = 1;
			total = 0;
			foreach (Member member in Members)
			{
				int num = Math.Max(1, member.Real.GetWidth());
				int num2 = Math.Max(1, member.Real.GetHeight());
				member.Width = num;
				member.Offset = total;
				member.Capacity = num * num2;
				total += member.Capacity;
				width = Math.Max(width, num);
			}
			val.m_width = width;
			val.m_height = Math.Max(1, (total + width - 1) / width);
			val.m_name = GcLoc.Get("gclc_view_title", new object[2] { Shown.m_name, Members.Count });
			val.m_inventory.Clear();
			foreach (Member member2 in Members)
			{
				foreach (ItemData item in member2.Real.m_inventory)
				{
					int num3 = item.m_gridPos.y * member2.Width + item.m_gridPos.x;
					if (num3 < 0 || num3 >= member2.Capacity || item.m_gridPos.x >= member2.Width)
					{
						Plugin.Log.LogWarning((object)$"Verbundene Kisten: {item.m_shared.m_name} liegt außerhalb der Kiste ({item.m_gridPos.x},{item.m_gridPos.y}).");
						num3 = Math.Min(Math.Max(num3, 0), member2.Capacity - 1);
					}
					int num4 = member2.Offset + num3;
					ItemData val2 = item.Clone();
					val2.m_gridPos = new Vector2i(num4 % width, num4 / width);
					val.m_inventory.Add(val2);
				}
				if (!member2.InView)
				{
					member2.InView = true;
					member2.Chest.m_inventory = val;
				}
				member2.Synced = Signature(member2.Real.m_inventory);
			}
			applying = true;
			try
			{
				val.Changed(false, false);
			}
			finally
			{
				applying = false;
			}
			built = true;
		}

		private static void OnVirtualChanged(Inventory source)
		{
			if (!applying && Active && source == Virtual)
			{
				if (batchDepth > 0)
				{
					batchDirty = true;
				}
				else
				{
					Distribute();
				}
			}
		}

		private static int MemberAt(int index)
		{
			for (int i = 0; i < Members.Count; i++)
			{
				Member member = Members[i];
				if (member.InView && index >= member.Offset && index < member.Offset + member.Capacity)
				{
					return i;
				}
			}
			return -1;
		}

		public static bool IsDead(int x, int y)
		{
			if (x >= 0 && y >= 0 && x < width)
			{
				return y * width + x >= total;
			}
			return true;
		}

		private static void Distribute()
		{
			Inventory val = Virtual;
			applying = true;
			try
			{
				List<List<ItemData>> list = Members.Select((Member _) => new List<ItemData>()).ToList();
				List<HashSet<int>> list2 = Members.Select((Member _) => new HashSet<int>()).ToList();
				List<ItemData> list3 = new List<ItemData>();
				foreach (ItemData item in val.m_inventory)
				{
					int num = item.m_gridPos.y * width + item.m_gridPos.x;
					int num2 = ((item.m_gridPos.x < width) ? MemberAt(num) : (-1));
					if (num2 < 0 || !list2[num2].Add(num - Members[num2].Offset))
					{
						list3.Add(item);
					}
					else
					{
						Place(list[num2], Members[num2], num - Members[num2].Offset, item);
					}
				}
				foreach (ItemData item2 in list3)
				{
					if (!PlaceAnywhere(list, list2, item2))
					{
						val.m_inventory.Remove(item2);
						Homeless.Add(item2);
					}
				}
				for (int num3 = 0; num3 < Members.Count; num3++)
				{
					Member member = Members[num3];
					if (member.InView && !((Object)(object)member.Chest == (Object)null) && !((Object)(object)member.Chest.m_nview == (Object)null) && member.Chest.m_nview.IsValid() && member.Chest.m_nview.IsOwner())
					{
						if (Signature(member.Real.m_inventory) != member.Synced)
						{
							Plugin.Log.LogWarning((object)("Verbundene Kisten: " + member.Chest.m_name + " wurde an der Ansicht vorbei verändert, die Ansicht gilt."));
						}
						if (SameContent(member.Real.m_inventory, list[num3]))
						{
							member.Synced = Signature(member.Real.m_inventory);
							continue;
						}
						member.Real.m_inventory.Clear();
						member.Real.m_inventory.AddRange(list[num3]);
						member.Real.Changed(false, false);
						member.Synced = Signature(member.Real.m_inventory);
					}
				}
			}
			finally
			{
				applying = false;
			}
			if (Homeless.Count > 0)
			{
				DropHomeless();
			}
		}

		private static void Place(List<ItemData> bucket, Member m, int local, ItemData item)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			ItemData val = item.Clone();
			val.m_gridPos = new Vector2i(local % m.Width, local / m.Width);
			bucket.Add(val);
		}

		private static bool PlaceAnywhere(List<List<ItemData>> buckets, List<HashSet<int>> used, ItemData item)
		{
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			for (int i = 0; i < Members.Count; i++)
			{
				Member member = Members[i];
				if (!member.InView)
				{
					continue;
				}
				for (int j = 0; j < member.Capacity; j++)
				{
					if (used[i].Add(j))
					{
						int num = member.Offset + j;
						item.m_gridPos = new Vector2i(num % width, num / width);
						Place(buckets[i], member, j, item);
						return true;
					}
				}
			}
			return false;
		}

		public static Vector2i FirstFree(Inventory view, bool topFirst)
		{
			//IL_00ba: 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_009e: Unknown result type (might be due to invalid IL or missing references)
			HashSet<int> hashSet = new HashSet<int>(view.m_inventory.Select((ItemData item) => item.m_gridPos.y * width + item.m_gridPos.x));
			if (topFirst)
			{
				for (int num = 0; num < total; num++)
				{
					if (!hashSet.Contains(num))
					{
						return new Vector2i(num % width, num / width);
					}
				}
			}
			else
			{
				for (int num2 = (total + width - 1) / width - 1; num2 >= 0; num2--)
				{
					for (int num3 = 0; num3 < width; num3++)
					{
						int num4 = num2 * width + num3;
						if (num4 < total && !hashSet.Contains(num4))
						{
							return new Vector2i(num3, num2);
						}
					}
				}
			}
			return new Vector2i(-1, -1);
		}
	}
	[HarmonyPatch(typeof(Container), "Awake")]
	internal static class AwakePatch
	{
		[HarmonyPriority(800)]
		private static void Postfix(Container __instance)
		{
			if (!((Object)(object)__instance.m_nview == (Object)null) && __instance.m_nview.GetZDO() != null)
			{
				((Component)__instance).gameObject.AddComponent<LinkTracker>().Container = __instance;
				Links.Register(__instance);
			}
		}
	}
	public class LinkTracker : MonoBehaviour
	{
		internal Container? Container;

		private void OnDestroy()
		{
			if ((Object)(object)Container != (Object)null)
			{
				Links.Forget(Container);
			}
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "Show")]
	internal static class ShowPatch
	{
		private static void Postfix(InventoryGui __instance, Container container)
		{
			if (Plugin.On && !((Object)(object)container == (Object)null) && !((Object)(object)__instance.m_currentContainer != (Object)(object)container) && !((Object)(object)LinkedView.Shown == (Object)(object)container))
			{
				LinkedView.Begin(container);
			}
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "CloseContainer")]
	internal static class CloseContainerPatch
	{
		private static void Postfix()
		{
			LinkedView.End();
		}
	}
	[HarmonyPatch(typeof(Container), "RPC_OpenResponse")]
	internal static class OpenResponsePatch
	{
		[HarmonyPriority(800)]
		private static bool Prefix(Container __instance, bool granted)
		{
			return !LinkedView.HandleOpenResponse(__instance, granted);
		}
	}
	[HarmonyPatch(typeof(Container), "Save")]
	internal static class SavePatch
	{
		private static bool Prefix(Container __instance)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Expected O, but got Unknown
			Inventory val = LinkedView.RealOf(__instance);
			if (val == null)
			{
				return true;
			}
			ZPackage val2 = new ZPackage();
			val.Save(val2);
			__instance.m_nview.GetZDO().Set(ZDOVars.s_items, val2.GetArray());
			__instance.m_lastRevision = __instance.m_nview.GetZDO().DataRevision;
			return false;
		}
	}
	[HarmonyPatch(typeof(Container), "Load")]
	internal static class LoadPatch
	{
		private static bool Prefix(Container __instance, ref bool __result)
		{
			if (LinkedView.RealOf(__instance) == null)
			{
				return true;
			}
			__result = false;
			return false;
		}
	}
	[HarmonyPatch(typeof(Container), "OnDestroyed")]
	internal static class DestroyedPatch
	{
		[HarmonyPriority(800)]
		private static void Prefix(Container __instance)
		{
			LinkedView.Release(__instance);
		}
	}
	[HarmonyPatch(typeof(Container), "Interact")]
	internal static class InteractPatch
	{
		[HarmonyPriority(0)]
		private static void Prefix(Container __instance, Humanoid character, bool hold, bool __runOriginal)
		{
			if (__runOriginal && !hold && (Object)(object)character == (Object)(object)Player.m_localPlayer)
			{
				LinkedView.UserRequested(__instance);
			}
		}
	}
	[HarmonyPatch]
	internal static class GuardPatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			yield return AccessTools.Method(typeof(InventoryGui), "OnSelectedItem", (Type[])null, (Type[])null);
			yield return AccessTools.Method(typeof(InventoryGui), "OnRightClickItem", (Type[])null, (Type[])null);
			yield return AccessTools.Method(typeof(InventoryGui), "OnTakeAll", (Type[])null, (Type[])null);
			yield return AccessTools.Method(typeof(InventoryGui), "OnStackAll", (Type[])null, (Type[])null);
			yield return AccessTools.Method(typeof(InventoryGui), "OnDropOutside", (Type[])null, (Type[])null);
		}

		[HarmonyPriority(800)]
		private static bool Prefix()
		{
			return LinkedView.EnsureHealthy();
		}
	}
	[HarmonyPatch(typeof(Container), "RPC_StackResponse")]
	internal static class StackGuardPatch
	{
		[HarmonyPriority(800)]
		private static bool Prefix()
		{
			return LinkedView.EnsureHealthy();
		}
	}
	[HarmonyPatch]
	internal static class BatchPatch
	{
		private static IEnumerable<MethodBase> TargetMethods()
		{
			yield return AccessTools.Method(typeof(Inventory), "MoveAll", (Type[])null, (Type[])null);
			yield return AccessTools.Method(typeof(Inventory), "StackAll", (Type[])null, (Type[])null);
		}

		[HarmonyPriority(900)]
		private static void Prefix(Inventory __instance, Inventory fromInventory, out bool __state)
		{
			__state = LinkedView.IsVirtual(__instance) || LinkedView.IsVirtual(fromInventory);
			if (__state)
			{
				LinkedView.BeginBatch();
			}
		}

		[HarmonyPriority(-1)]
		private static void Finalizer(bool __state)
		{
			if (__state)
			{
				LinkedView.EndBatch();
			}
		}
	}
	[HarmonyPatch(typeof(Inventory), "FindEmptySlot")]
	internal static class FindEmptySlotPatch
	{
		private static void Postfix(Inventory __instance, bool topFirst, ref Vector2i __result)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			if (LinkedView.IsVirtual(__instance))
			{
				__result = LinkedView.FirstFree(__instance, topFirst);
			}
		}
	}
	[HarmonyPatch(typeof(Inventory), "HaveEmptySlot")]
	internal static class HaveEmptySlotPatch
	{
		private static void Postfix(Inventory __instance, ref bool __result)
		{
			if (LinkedView.IsVirtual(__instance))
			{
				__result = __instance.m_inventory.Count < LinkedView.Capacity;
			}
		}
	}
	[HarmonyPatch(typeof(Inventory), "GetEmptySlots")]
	internal static class GetEmptySlotsPatch
	{
		private static void Postfix(Inventory __instance, ref int __result)
		{
			if (LinkedView.IsVirtual(__instance))
			{
				__result = Math.Max(0, LinkedView.Capacity - __instance.m_inventory.Count);
			}
		}
	}
	[HarmonyPatch(typeof(Inventory), "CanAddItem", new Type[]
	{
		typeof(ItemData),
		typeof(int)
	})]
	internal static class CanAddItemPatch
	{
		private static void Postfix(Inventory __instance, ItemData item, int stack, ref bool __result)
		{
			if (LinkedView.IsVirtual(__instance) && item != null)
			{
				if (stack <= 0)
				{
					stack = item.m_stack;
				}
				int num = __instance.FindFreeStackSpace(item.m_shared.m_name, (float)item.m_worldLevel) + Math.Max(0, LinkedView.Capacity - __instance.m_inventory.Count) * item.m_shared.m_maxStackSize;
				__result = num >= stack;
			}
		}
	}
	[HarmonyPatch(typeof(Inventory), "AddItem", new Type[]
	{
		typeof(ItemData),
		typeof(int),
		typeof(int),
		typeof(int),
		typeof(bool)
	})]
	internal static class AddAtPatch
	{
		[HarmonyPriority(800)]
		private static bool Prefix(Inventory __instance, int x, int y, ref bool __result)
		{
			if (LinkedView.IsVirtual(__instance) && LinkedView.IsDead(x, y))
			{
				__result = false;
				return false;
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(Inventory), "AddItem", new Type[]
	{
		typeof(ItemData),
		typeof(Vector2i)
	})]
	internal static class AddAtPosPatch
	{
		[HarmonyPriority(800)]
		private static bool Prefix(Inventory __instance, Vector2i pos, ref bool __result)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			if (LinkedView.IsVirtual(__instance) && LinkedView.IsDead(pos.x, pos.y))
			{
				__result = false;
				return false;
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(InventoryGrid), "UpdateGui")]
	internal static class GridPatch
	{
		private static readonly HashSet<InventoryGrid> Hidden = new HashSet<InventoryGrid>();

		private static void Postfix(InventoryGrid __instance)
		{
			bool flag = LinkedView.IsVirtual(__instance.m_inventory);
			if (!flag && !Hidden.Contains(__instance))
			{
				return;
			}
			List<InventoryElement> elements = __instance.m_elements;
			for (int i = 0; i < elements.Count; i++)
			{
				if (!((Object)(object)elements[i] == (Object)null))
				{
					bool flag2 = !flag || i < LinkedView.Capacity;
					if (((Component)elements[i]).gameObject.activeSelf != flag2)
					{
						((Component)elements[i]).gameObject.SetActive(flag2);
					}
				}
			}
			if (flag)
			{
				Hidden.Add(__instance);
			}
			else
			{
				Hidden.Remove(__instance);
			}
		}
	}
	[HarmonyPatch(typeof(Container), "GetHoverText")]
	internal static class HoverPatch
	{
		private static void Postfix(Container __instance, ref string __result)
		{
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Unknown result type (might be due to invalid IL or missing references)
			if (Plugin.On && !string.IsNullOrEmpty(__result) && GcWard.IsPlayerChest(__instance))
			{
				int count = Links.Group(__instance).Count;
				if (count > 1)
				{
					__result = __result + "\n" + GcLoc.Get("gclc_hover_group", new object[1] { count });
				}
				__result = __result + "\n" + GcLoc.Get("gclc_hover_key", new object[1] { ((object)Plugin.LinkKey.Value/*cast due to .constrained prefix*/).ToString() });
			}
		}
	}
	internal static class GroupHighlight
	{
		private static readonly List<(GameObject, Color)> Wanted = new List<(GameObject, Color)>();

		private static readonly List<GameObject> Marked = new List<GameObject>();

		public static void Update(Container? hovered, Container? selected)
		{
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_016b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0170: Unknown result type (might be due to invalid IL or missing references)
			//IL_0174: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			List<(GameObject, Color)> wanted = Wanted;
			wanted.Clear();
			bool num = Input.GetKey((KeyCode)308) || Input.GetKey((KeyCode)307);
			if ((Object)(object)selected != (Object)null)
			{
				wanted.Add((((Component)selected).gameObject, new Color(1f, 0.55f, 0.15f)));
			}
			if (num && (Object)(object)hovered != (Object)null)
			{
				List<Container> list = Links.Group(hovered);
				if (list.Count > 1)
				{
					for (int i = 0; i < list.Count; i++)
					{
						bool flag = i == 0 && !Links.HasLink(list[i]);
						wanted.Add((((Component)list[i]).gameObject, flag ? new Color(1f, 0.55f, 0.15f) : new Color(0.3f, 1f, 0.35f)));
					}
				}
			}
			foreach (GameObject go in Marked)
			{
				if ((Object)(object)go != (Object)null && wanted.All(((GameObject, Color) w) => (Object)(object)w.Item1 != (Object)(object)go))
				{
					Gc.ResetHighlight(go);
				}
			}
			Marked.Clear();
			foreach (var (val, val2) in wanted)
			{
				Gc.Highlight(val, val2);
				Marked.Add(val);
			}
		}
	}
	internal static class VPlusDedupe
	{
		internal static void Apply(Harmony harmony)
		{
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Expected O, but got Unknown
			//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00db: Expected O, but got Unknown
			if (!Gc.VPlusLoaded)
			{
				return;
			}
			Type type = AccessTools.TypeByName("ValheimPlus.InventoryAssistant");
			if (type == null)
			{
				Plugin.Log.LogWarning((object)"ValheimPlus gefunden, aber InventoryAssistant nicht. Offene verbundene Kisten könnten dort doppelt zählen.");
				return;
			}
			string[] array = new string[2] { "GetNearbyChests", "GetNearbyChestsForMachine" };
			foreach (string text in array)
			{
				MethodInfo methodInfo = AccessTools.Method(type, text, (Type[])null, (Type[])null);
				if (methodInfo != null)
				{
					harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(VPlusDedupe), "Canonicalize", (Type[])null)
					{
						priority = 200
					}, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				}
			}
			MethodInfo methodInfo2 = AccessTools.Method(type, "GetNearbyChestItemsByContainerList", (Type[])null, (Type[])null);
			if (methodInfo2 != null)
			{
				harmony.Patch((MethodBase)methodInfo2, new HarmonyMethod(typeof(VPlusDedupe), "CanonicalizeArgument", (Type[])null)
				{
					priority = 800
				}, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			Plugin.Log.LogInfo((object)"ValheimPlus erkannt: offene verbundene Kisten zählen dort als eine Kiste, fremd geöffnete und nicht geladene Kisten werden ausgelassen.");
		}

		private static void Canonicalize(ref List<Container> __result)
		{
			LinkedView.CanonicalizeList(ref __result);
		}

		private static void CanonicalizeArgument(ref List<Container> nearbyChests)
		{
			LinkedView.CanonicalizeList(ref nearbyChests);
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}

GC_Schloesser.dll

Decompiled 3 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using GC.Shared;
using GC.ValheimPlusPlus.ItemLock;
using HarmonyLib;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("assembly_guiutils")]
[assembly: IgnoresAccessChecksTo("assembly_utils")]
[assembly: IgnoresAccessChecksTo("assembly_valheim")]
[assembly: TargetFramework(".NETFramework,Version=v4.8.1", FrameworkDisplayName = ".NET Framework 4.8.1")]
[assembly: AssemblyCompany("Gorduan")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.5.1.0")]
[assembly: AssemblyInformationalVersion("1.5.1+7c8bde9391283b12125175d68f4632c4cf8f93b3")]
[assembly: AssemblyProduct("GC_Schloesser")]
[assembly: AssemblyTitle("GC_Schloesser")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.5.1.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 GC.ValheimPlusPlus
{
	internal static class VppPlugin
	{
		internal static ManualLogSource Log;

		internal static GcModule? Module;

		internal static bool Allowed
		{
			get
			{
				if (Module != null)
				{
					return Module.Allowed;
				}
				return true;
			}
		}

		internal static bool OnServer
		{
			get
			{
				if (Module != null)
				{
					return Module.OnServer;
				}
				return false;
			}
		}
	}
}
namespace GC.ValheimPlusPlus.ModSchloesser
{
	[BepInPlugin("gorduan.gc.schloesser", "GC_Schloesser", "1.5.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		internal static GcModule Module;

		private void Awake()
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Expected O, but got Unknown
			Module = GcKern.Register((BaseUnityPlugin)(object)this, ((BaseUnityPlugin)this).Logger, GcMod.Info);
			VppPlugin.Log = ((BaseUnityPlugin)this).Logger;
			VppPlugin.Module = Module;
			Harmony val = new Harmony("gorduan.gc.schloesser");
			GC.ValheimPlusPlus.ItemLock.Plugin.Init(((BaseUnityPlugin)this).Config, val);
			Module.TakeOverOldSettings();
			val.PatchAll(typeof(Plugin).Assembly);
			Module.MenuGroup((Func<string, string>)((string key) => GcLoc.Get("vpp_" + key, Array.Empty<object>())), (string)null).Main("itemlock", (ConfigEntryBase)(object)GC.ValheimPlusPlus.ItemLock.Plugin.ItemsEnabled, (Func<bool>)(() => GC.ValheimPlusPlus.ItemLock.Plugin.Blocked), "GC_ItemLock", (Func<string>)null, 0.0, "", (Func<bool>)null, (Func<bool>)null).Sub("itemlock_key", (ConfigEntryBase)(object)GC.ValheimPlusPlus.ItemLock.Plugin.ItemModifier, 0.0, "", (Func<bool>)null, "", (Func<bool>)null, (Func<bool>)null)
				.Sub("itemlock_stacking", (ConfigEntryBase)(object)GC.ValheimPlusPlus.ItemLock.Plugin.ProtectFromStacking, 0.0, "", (Func<bool>)null, "", (Func<bool>)null, (Func<bool>)null)
				.Sub("itemlock_takeall", (ConfigEntryBase)(object)GC.ValheimPlusPlus.ItemLock.Plugin.ProtectFromTakeAll, 0.0, "", (Func<bool>)null, "", (Func<bool>)null, (Func<bool>)null)
				.Sub("itemlock_grave", (ConfigEntryBase)(object)GC.ValheimPlusPlus.ItemLock.Plugin.RestoreAfterDeath, 0.0, "", (Func<bool>)null, "", (Func<bool>)null, (Func<bool>)null)
				.Sub("itemlock_white_new", (ConfigEntryBase)(object)GC.ValheimPlusPlus.ItemLock.Plugin.WhiteStacksTakeNew, 0.0, "", (Func<bool>)null, "", (Func<bool>)null, (Func<bool>)null)
				.Sub("itemlock_red_new", (ConfigEntryBase)(object)GC.ValheimPlusPlus.ItemLock.Plugin.RedStacksTakeNew, 0.0, "", (Func<bool>)null, "", (Func<bool>)null, (Func<bool>)null)
				.Main("chestlock", (ConfigEntryBase)(object)GC.ValheimPlusPlus.ItemLock.Plugin.ChestsEnabled, (Func<bool>)(() => GC.ValheimPlusPlus.ItemLock.Plugin.Blocked), "GC_ItemLock", (Func<string>)null, 0.0, "", (Func<bool>)(() => Gc.VPlusLoaded), (Func<bool>)null)
				.Sub("chestlock_key", (ConfigEntryBase)(object)GC.ValheimPlusPlus.ItemLock.Plugin.ChestKey, 0.0, "", (Func<bool>)null, "", (Func<bool>)null, (Func<bool>)null);
		}

		private void Update()
		{
			if (!Gc.IsHeadless)
			{
				Module.Guard("Schlösser", (Action)GC.ValheimPlusPlus.ItemLock.Plugin.Update);
			}
		}
	}
	internal static class GcMod
	{
		public const string Id = "schloesser";

		public const string Guid = "gorduan.gc.schloesser";

		public const string Name = "GC_Schloesser";

		public const string Version = "1.5.1";

		public const int Stufe = 2;

		public static GcModInfo Info
		{
			get
			{
				//IL_0000: Unknown result type (might be due to invalid IL or missing references)
				//IL_0006: Expected O, but got Unknown
				//IL_003b: Unknown result type (might be due to invalid IL or missing references)
				GcModInfo val = new GcModInfo();
				val.Id = "schloesser";
				val.Guid = "gorduan.gc.schloesser";
				val.Name = "GC_Schloesser";
				val.Version = "1.5.1";
				val.Stufe = 2;
				val.Seite = (GcSeite)1;
				val.Braucht = Array.Empty<string>();
				val.Partner = Array.Empty<string>();
				val.Fremd = new string[1] { "org.bepinex.plugins.valheim_plus" };
				val.AlteConfig = "gorduan.valheimplusplus.cfg";
				val.Ausgleich = 0f;
				return val;
			}
		}
	}
}
namespace GC.ValheimPlusPlus.ItemLock
{
	internal static class Plugin
	{
		public const string Guid = "gorduan.gc_itemlock";

		public const string ModName = "GC_ItemLock";

		public const string Version = "1.0.0";

		internal static ManualLogSource Log = null;

		internal static ConfigEntry<bool> ItemsEnabled = null;

		internal static ConfigEntry<bool> ChestsEnabled = null;

		internal static ConfigEntry<KeyCode> ItemModifier = null;

		internal static ConfigEntry<KeyboardShortcut> ChestKey = null;

		internal static ConfigEntry<bool> ProtectFromStacking = null;

		internal static ConfigEntry<bool> ProtectFromTakeAll = null;

		internal static ConfigEntry<bool> RestoreAfterDeath = null;

		internal static ConfigEntry<bool> WhiteStacksTakeNew = null;

		internal static ConfigEntry<bool> RedStacksTakeNew = null;

		internal static Sprite? LockIcon;

		internal static readonly Color WhiteLock = new Color(1f, 1f, 1f, 0.95f);

		internal static readonly Color RedLock = new Color(1f, 0.28f, 0.22f, 1f);

		internal static bool Blocked => Chainloader.PluginInfos.ContainsKey("gorduan.gc_itemlock");

		internal static bool ItemsOn
		{
			get
			{
				if (ItemsEnabled.Value && !Blocked)
				{
					return VppPlugin.Allowed;
				}
				return false;
			}
		}

		internal static bool ChestsOn
		{
			get
			{
				if (ChestsEnabled.Value && !Blocked && VppPlugin.Allowed)
				{
					return VppPlugin.OnServer;
				}
				return false;
			}
		}

		internal static bool AnyClosed
		{
			get
			{
				if (ItemsOn)
				{
					if (RedStacksTakeNew.Value)
					{
						return !WhiteStacksTakeNew.Value;
					}
					return true;
				}
				return false;
			}
		}

		internal static void Init(ConfigFile Config, Harmony harmony)
		{
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			Log = VppPlugin.Log;
			ItemsEnabled = Gc.Bind<bool>(Config, "2 - Item Lock", "ItemsEnabled", true, "Items fixieren: Alt+Linksklick = weißes Schloss (nicht einlagern, nicht sortieren, Platz nach dem Tod), Alt+Rechtsklick = rotes Schloss (zusätzlich nie zum Craften oder Bauen verbrauchen). Im Inventar und in Kisten.", (AcceptableValueBase)null);
			ChestsEnabled = Gc.BindSynced<bool>(Config, "2 - Item Lock", "ChestsEnabled", true, "Kisten mit Alt+L für Automatik sperren (wird vom Server vorgegeben).", (AcceptableValueBase)null);
			ItemModifier = Gc.Bind<KeyCode>(Config, "2 - Item Lock", "ItemLockModifier", (KeyCode)308, "Diese Taste halten: Linksklick auf ein Item setzt oder löst das weiße Schloss, Rechtsklick das rote.", (AcceptableValueBase)null);
			ChestKey = Gc.Bind<KeyboardShortcut>(Config, "2 - Item Lock", "ChestLockKey", new KeyboardShortcut((KeyCode)108, (KeyCode[])(object)new KeyCode[1] { (KeyCode)308 }), "Auf eine Kiste schauen und diese Tasten drücken: Kiste für Automatik sperren oder freigeben.", (AcceptableValueBase)null);
			ProtectFromStacking = Gc.Bind<bool>(Config, "2 - Item Lock - Rules", "ProtectFromStacking", true, "Fixierte Items nie in Kisten stapeln (Stapel ablegen, E halten, V+ Auto-Stack).", (AcceptableValueBase)null);
			ProtectFromTakeAll = Gc.Bind<bool>(Config, "2 - Item Lock - Rules", "ProtectFromTakeAll", true, "Fixierte Items in Kisten und Fahrzeugen bleiben bei 'Alles nehmen' liegen (Grabsteine ausgenommen).", (AcceptableValueBase)null);
			WhiteStacksTakeNew = Gc.Bind<bool>(Config, "2 - Item Lock - Rules", "WhiteStacksTakeNewItems", true, "Neue Items gleicher Art (Aufheben, Alles nehmen, Craften) dürfen in weiß fixierte Stapel. Aus = sie bekommen einen eigenen Stapel.", (AcceptableValueBase)null);
			RedStacksTakeNew = Gc.Bind<bool>(Config, "2 - Item Lock - Rules", "RedStacksTakeNewItems", false, "Neue Items gleicher Art dürfen in rot fixierte Stapel und sind dann ebenfalls gesperrt. Aus = sie bekommen einen eigenen Stapel.", (AcceptableValueBase)null);
			RestoreAfterDeath = Gc.Bind<bool>(Config, "2 - Item Lock - Rules", "RestoreAfterDeath", true, "Beim Leeren des eigenen Grabsteins kommen fixierte Items auf ihren alten Platz. Was dort liegt, wandert auf einen freien Platz oder auf den Boden.", (AcceptableValueBase)null);
			VPlusFilter.Apply(harmony);
		}

		internal static void Update()
		{
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			if (!ChestsOn || Gc.IsHeadless || (Object)(object)Player.m_localPlayer == (Object)null || InventoryGui.IsVisible() || !Gc.Down(ChestKey))
			{
				return;
			}
			Container val = Gc.Hovered<Container>();
			if (GcWard.IsPlayerChest(val) && PrivateArea.CheckAccess(((Component)val).transform.position, 0f, true, false))
			{
				ZNetView nview = val.m_nview;
				if (GcIds.InUse(nview) || !Gc.Own(nview))
				{
					Gc.Center(GcLoc.Get("gcl_chest_busy", Array.Empty<object>()));
					return;
				}
				bool flag = !nview.GetZDO().GetBool("gc_noauto", false);
				nview.GetZDO().Set("gc_noauto", flag);
				Gc.Center(GcLoc.Get(flag ? "gcl_chest_locked" : "gcl_chest_unlocked", Array.Empty<object>()));
			}
		}

		internal static bool Closed(ItemData? item)
		{
			if (!ItemsOn || !Gc.IsLocked(item))
			{
				return false;
			}
			if (!Gc.IsHardLocked(item))
			{
				return !WhiteStacksTakeNew.Value;
			}
			return !RedStacksTakeNew.Value;
		}

		internal static int Level(ItemData? item)
		{
			if (!Gc.IsHardLocked(item))
			{
				return Gc.IsLocked(item) ? 1 : 0;
			}
			return 2;
		}

		internal static void SetLevel(ItemData item, int level)
		{
			if (level <= 0)
			{
				item.m_customData.Remove("gc_locked");
				item.m_customData.Remove("gc_locked_red");
				return;
			}
			item.m_customData["gc_locked"] = "1";
			if (level >= 2)
			{
				item.m_customData["gc_locked_red"] = "1";
			}
			else
			{
				item.m_customData.Remove("gc_locked_red");
			}
		}

		internal static void Toggle(ItemData item, bool red)
		{
			int num = Level(item);
			int num2 = ((!red) ? ((num != 1) ? 1 : 0) : ((num != 2) ? 2 : 0));
			SetLevel(item, num2);
			Gc.TopLeft(GcLoc.Get(num2 switch
			{
				1 => "gcl_item_locked", 
				0 => "gcl_item_unlocked", 
				_ => "gcl_item_locked_red", 
			}, new object[1] { Localization.instance.Localize(item.m_shared.m_name) }));
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "OnSelectedItem")]
	internal static class SelectPatch
	{
		private static bool Prefix(InventoryGui __instance, InventoryGrid grid, ItemData item)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			if (!Plugin.ItemsOn || item == null || (Object)(object)__instance.m_dragGo != (Object)null || !Input.GetKey(Plugin.ItemModifier.Value))
			{
				return true;
			}
			Plugin.Toggle(item, red: false);
			grid.GetInventory().Changed(false, false);
			return false;
		}
	}
	[HarmonyPatch(typeof(InventoryGui), "OnRightClickItem")]
	internal static class RightClickPatch
	{
		private static bool Prefix(InventoryGrid grid, ItemData item)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			if (!Plugin.ItemsOn || item == null || !Input.GetKey(Plugin.ItemModifier.Value))
			{
				return true;
			}
			Plugin.Toggle(item, red: true);
			grid.GetInventory().Changed(false, false);
			return false;
		}
	}
	[HarmonyPatch(typeof(InventoryGrid), "UpdateGui")]
	internal static class GridIconPatch
	{
		private const string IconName = "GC_LockIcon";

		private static readonly Dictionary<int, ItemData> Locked = new Dictionary<int, ItemData>();

		private static void Postfix(InventoryGrid __instance)
		{
			//IL_00ad: 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_011f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: 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_014e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0153: Unknown result type (might be due to invalid IL or missing references)
			//IL_0154: Unknown result type (might be due to invalid IL or missing references)
			//IL_015b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0162: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0177: Unknown result type (might be due to invalid IL or missing references)
			//IL_0182: Unknown result type (might be due to invalid IL or missing references)
			//IL_0196: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a0: 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_01d2: Unknown result type (might be due to invalid IL or missing references)
			Inventory inventory = __instance.m_inventory;
			if (inventory == null)
			{
				return;
			}
			if (Plugin.LockIcon == null)
			{
				Plugin.LockIcon = GcUi.LockSprite();
			}
			Locked.Clear();
			if (Plugin.ItemsOn)
			{
				foreach (ItemData item in inventory.m_inventory)
				{
					if (Gc.IsLocked(item))
					{
						Locked[item.m_gridPos.y * 1000 + item.m_gridPos.x] = item;
					}
				}
			}
			foreach (InventoryElement element in __instance.m_elements)
			{
				ItemData value;
				bool flag = Locked.TryGetValue(element.Position.y * 1000 + element.Position.x, out value);
				Transform val = ((Component)element).transform.Find("GC_LockIcon");
				if ((Object)(object)val == (Object)null)
				{
					if (!flag)
					{
						continue;
					}
					GameObject val2 = new GameObject("GC_LockIcon", new Type[2]
					{
						typeof(RectTransform),
						typeof(Image)
					});
					val2.transform.SetParent(((Component)element).transform, false);
					RectTransform val3 = (RectTransform)val2.transform;
					Vector2 anchorMin = (val3.anchorMax = new Vector2(0f, 1f));
					val3.anchorMin = anchorMin;
					val3.pivot = new Vector2(0f, 1f);
					val3.anchoredPosition = new Vector2(3f, -3f);
					val3.sizeDelta = new Vector2(16f, 16f);
					Image component = val2.GetComponent<Image>();
					component.sprite = Plugin.LockIcon;
					((Graphic)component).raycastTarget = false;
					val = val2.transform;
				}
				if (flag)
				{
					((Graphic)((Component)val).GetComponent<Image>()).color = (Gc.IsHardLocked(value) ? Plugin.RedLock : Plugin.WhiteLock);
				}
				if (((Component)val).gameObject.activeSelf != flag)
				{
					((Component)val).gameObject.SetActive(flag);
				}
			}
		}
	}
	[HarmonyPatch]
	internal static class StackMergePatches
	{
		private static int moveAllDepth;

		[HarmonyPatch(typeof(Inventory), "FindFreeStackItem")]
		[HarmonyPostfix]
		private static void FindItem(Inventory __instance, string name, int quality, float worldLevel, ref ItemData? __result)
		{
			if (__result == null || !Plugin.Closed(__result))
			{
				return;
			}
			__result = null;
			foreach (ItemData item in __instance.m_inventory)
			{
				if (item.m_shared.m_name == name && item.m_quality == quality && item.m_stack < item.m_shared.m_maxStackSize && (float)item.m_worldLevel == worldLevel && !Plugin.Closed(item))
				{
					__result = item;
					break;
				}
			}
		}

		[HarmonyPatch(typeof(Inventory), "MoveAll")]
		[HarmonyPrefix]
		private static void MoveAllEnter()
		{
			moveAllDepth++;
		}

		[HarmonyPatch(typeof(Inventory), "MoveAll")]
		[HarmonyFinalizer]
		private static void MoveAllLeave()
		{
			moveAllDepth = Math.Max(0, moveAllDepth - 1);
		}

		[HarmonyPatch(typeof(Inventory), "AddItem", new Type[]
		{
			typeof(ItemData),
			typeof(int),
			typeof(int),
			typeof(int),
			typeof(bool)
		})]
		[HarmonyPrefix]
		private static bool AddAtSlot(Inventory __instance, int x, int y, ref bool __result)
		{
			if (moveAllDepth <= 0 || !Plugin.AnyClosed || !Plugin.Closed(__instance.GetItemAt(x, y)))
			{
				return true;
			}
			__result = false;
			return false;
		}

		[HarmonyPatch(typeof(Inventory), "FindFreeStackSpace")]
		[HarmonyPostfix]
		private static void FindSpace(Inventory __instance, string name, float worldLevel, ref int __result)
		{
			if (__result <= 0 || !Plugin.AnyClosed)
			{
				return;
			}
			foreach (ItemData item in __instance.m_inventory)
			{
				if (item.m_shared.m_name == name && item.m_stack < item.m_shared.m_maxStackSize && (float)item.m_worldLevel == worldLevel && Plugin.Closed(item))
				{
					__result -= item.m_shared.m_maxStackSize - item.m_stack;
				}
			}
			__result = Math.Max(0, __result);
		}
	}
	[HarmonyPatch(typeof(ItemData), "GetTooltip", new Type[] { typeof(int) })]
	internal static class TooltipPatch
	{
		private static void Postfix(ItemData __instance, ref string __result)
		{
			if (Plugin.ItemsOn && Gc.IsLocked(__instance))
			{
				__result = __result + "\n\n" + GcLoc.Get(Gc.IsHardLocked(__instance) ? "gcl_tooltip_red" : "gcl_tooltip_white", Array.Empty<object>());
			}
		}
	}
	[HarmonyPatch(typeof(Inventory), "StackAll")]
	internal static class StackAllPatch
	{
		[HarmonyPriority(800)]
		private static void Prefix(Inventory fromInventory, out List<ItemData>? __state)
		{
			__state = null;
			if (Plugin.ItemsOn && Plugin.ProtectFromStacking.Value)
			{
				__state = fromInventory.m_inventory.FindAll((Predicate<ItemData>)Gc.IsLocked);
				if (__state.Count == 0)
				{
					__state = null;
				}
				else
				{
					fromInventory.m_inventory.RemoveAll((Predicate<ItemData>)Gc.IsLocked);
				}
			}
		}

		[HarmonyPriority(0)]
		private static void Finalizer(Inventory fromInventory, List<ItemData>? __state)
		{
			if (__state != null)
			{
				fromInventory.m_inventory.AddRange(__state);
				fromInventory.Changed(false, false);
			}
		}
	}
	[HarmonyPatch]
	internal static class TakeAllContext
	{
		internal static int Depth;

		internal static int GraveDepth;

		private const int None = 0;

		private const int Chest = 1;

		private const int OwnGrave = 2;

		private static int Kind(Container? container)
		{
			if ((Object)(object)container == (Object)null)
			{
				return 0;
			}
			TombStone component = ((Component)container).GetComponent<TombStone>();
			if ((Object)(object)component == (Object)null)
			{
				return 1;
			}
			if (!((Object)(object)component.m_nview != (Object)null) || !component.m_nview.IsValid() || !component.IsOwner())
			{
				return 0;
			}
			return 2;
		}

		private static void Enter(int kind)
		{
			switch (kind)
			{
			case 1:
				Depth++;
				break;
			case 2:
				GraveDepth++;
				break;
			}
		}

		private static void Leave(int kind)
		{
			switch (kind)
			{
			case 1:
				Depth = Math.Max(0, Depth - 1);
				break;
			case 2:
				GraveDepth = Math.Max(0, GraveDepth - 1);
				break;
			}
		}

		[HarmonyPatch(typeof(InventoryGui), "OnTakeAll")]
		[HarmonyPrefix]
		private static void GuiPrefix(InventoryGui __instance, out int __state)
		{
			__state = Kind(__instance.m_currentContainer);
			Enter(__state);
		}

		[HarmonyPatch(typeof(InventoryGui), "OnTakeAll")]
		[HarmonyFinalizer]
		private static void GuiFinalizer(int __state)
		{
			Leave(__state);
		}

		[HarmonyPatch(typeof(Container), "RPC_TakeAllResponse")]
		[HarmonyPrefix]
		private static void RpcPrefix(Container __instance, out int __state)
		{
			__state = Kind(__instance);
			Enter(__state);
		}

		[HarmonyPatch(typeof(Container), "RPC_TakeAllResponse")]
		[HarmonyFinalizer]
		private static void RpcFinalizer(int __state)
		{
			Leave(__state);
		}
	}
	[HarmonyPatch(typeof(Inventory), "MoveAll")]
	internal static class MoveAllPatch
	{
		[HarmonyPriority(800)]
		private static void Prefix(Inventory fromInventory, out List<ItemData>? __state)
		{
			__state = null;
			if (Plugin.ItemsOn && Plugin.ProtectFromTakeAll.Value && TakeAllContext.Depth > 0)
			{
				__state = fromInventory.m_inventory.FindAll((Predicate<ItemData>)Gc.IsLocked);
				if (__state.Count == 0)
				{
					__state = null;
				}
				else
				{
					fromInventory.m_inventory.RemoveAll((Predicate<ItemData>)Gc.IsLocked);
				}
			}
		}

		[HarmonyPriority(0)]
		private static void Finalizer(Inventory fromInventory, List<ItemData>? __state)
		{
			if (__state != null)
			{
				fromInventory.m_inventory.AddRange(__state);
				fromInventory.Changed(false, false);
			}
		}
	}
	[HarmonyPatch(typeof(Inventory), "MoveAll")]
	internal static class GraveRestorePatch
	{
		internal sealed class State
		{
			public int Restored;

			public int Moved;

			public int Dropped;
		}

		[HarmonyPriority(800)]
		private static void Prefix(Inventory __instance, Inventory fromInventory, out State? __state)
		{
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0111: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0121: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_016a: 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_017b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0180: Unknown result type (might be due to invalid IL or missing references)
			//IL_0185: Unknown result type (might be due to invalid IL or missing references)
			//IL_018a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0191: Unknown result type (might be due to invalid IL or missing references)
			__state = null;
			Player localPlayer = Player.m_localPlayer;
			if (!Plugin.ItemsOn || !Plugin.RestoreAfterDeath.Value || TakeAllContext.GraveDepth <= 0 || (Object)(object)localPlayer == (Object)null || __instance != ((Humanoid)localPlayer).GetInventory())
			{
				return;
			}
			List<ItemData> list = fromInventory.m_inventory.FindAll((ItemData item) => Gc.IsLocked(item) && item.m_gridPos.x >= 0 && item.m_gridPos.y >= 0 && item.m_gridPos.x < __instance.m_width && item.m_gridPos.y < __instance.m_height);
			if (list.Count == 0)
			{
				return;
			}
			HashSet<int> hashSet = new HashSet<int>();
			foreach (ItemData item in list)
			{
				hashSet.Add(Key(item.m_gridPos));
			}
			__state = new State();
			foreach (ItemData item2 in list)
			{
				ItemData itemAt = __instance.GetItemAt(item2.m_gridPos.x, item2.m_gridPos.y);
				if (itemAt != null)
				{
					Vector2i val = FreeSlot(__instance, hashSet, __instance.TopFirst(itemAt));
					if (val.x >= 0)
					{
						itemAt.m_gridPos = val;
						__state.Moved++;
					}
					else
					{
						if (((Humanoid)localPlayer).IsItemEquiped(itemAt))
						{
							hashSet.Remove(Key(item2.m_gridPos));
							continue;
						}
						__instance.m_inventory.Remove(itemAt);
						Transform transform = ((Component)localPlayer).transform;
						ItemDrop.DropItem(itemAt, itemAt.m_stack, transform.position + transform.forward * 0.8f + Vector3.up, transform.rotation);
						__state.Dropped++;
					}
				}
				fromInventory.m_inventory.Remove(item2);
				__instance.m_inventory.Add(item2);
				__state.Restored++;
			}
			__instance.Changed(false, false);
			fromInventory.Changed(false, false);
		}

		[HarmonyPriority(0)]
		private static void Finalizer(State? __state)
		{
			if (__state != null && __state.Restored != 0)
			{
				Gc.TopLeft(GcLoc.Get("gcl_grave_moved", new object[2] { __state.Restored, __state.Moved }));
				if (__state.Dropped > 0)
				{
					Gc.Center(GcLoc.Get("gcl_grave_dropped", new object[1] { __state.Dropped }));
				}
			}
		}

		private static int Key(Vector2i pos)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			return pos.y * 1000 + pos.x;
		}

		private static Vector2i FreeSlot(Inventory inventory, HashSet<int> reserved, bool topFirst)
		{
			//IL_0058: 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)
			for (int i = 0; i < inventory.m_height; i++)
			{
				int num = (topFirst ? i : (inventory.m_height - 1 - i));
				for (int j = 0; j < inventory.m_width; j++)
				{
					if (!reserved.Contains(num * 1000 + j) && inventory.GetItemAt(j, num) == null)
					{
						return new Vector2i(j, num);
					}
				}
			}
			return new Vector2i(-1, -1);
		}
	}
	internal static class RedContext
	{
		internal static int Chests;

		internal static bool Applies(Inventory inventory)
		{
			if ((GcHooks.RedCraft <= 0 && Chests <= 0) || !Plugin.ItemsOn)
			{
				return false;
			}
			if (GcHooks.RedCraft <= 0 && (Object)(object)Player.m_localPlayer != (Object)null && inventory == ((Humanoid)Player.m_localPlayer).GetInventory())
			{
				return false;
			}
			return inventory.m_inventory.Exists((Predicate<ItemData>)Gc.IsHardLocked);
		}

		internal static void Leave(ref int depth)
		{
			depth = Math.Max(0, depth - 1);
		}

		internal static bool Enter(ref int depth)
		{
			depth++;
			return true;
		}

		internal static void Exit(ref int depth, bool entered)
		{
			if (entered)
			{
				Leave(ref depth);
			}
		}

		internal static int CountUsable(Inventory inventory, string name)
		{
			GcHooks.RedCraft++;
			try
			{
				return inventory.CountItems(name, -1, true);
			}
			finally
			{
				Leave(ref GcHooks.RedCraft);
			}
		}
	}
	[HarmonyPatch]
	internal static class RedInventoryPatches
	{
		[HarmonyPatch(typeof(Inventory), "CountItems", new Type[]
		{
			typeof(string),
			typeof(int),
			typeof(bool)
		})]
		[HarmonyPrefix]
		[HarmonyPriority(800)]
		private static bool CountItems(Inventory __instance, string name, int quality, bool matchWorldLevel, ref int __result)
		{
			if (!RedContext.Applies(__instance))
			{
				return true;
			}
			int num = 0;
			foreach (ItemData item in __instance.m_inventory)
			{
				if (!Gc.IsHardLocked(item) && (name == null || item.m_shared.m_name == name) && (quality < 0 || quality == item.m_quality) && (!matchWorldLevel || item.m_worldLevel >= Game.m_worldLevel))
				{
					num += item.m_stack;
				}
			}
			__result = num;
			return false;
		}

		[HarmonyPatch(typeof(Inventory), "HaveItem", new Type[]
		{
			typeof(string),
			typeof(bool)
		})]
		[HarmonyPrefix]
		[HarmonyPriority(800)]
		private static bool HaveItem(Inventory __instance, string name, bool matchWorldLevel, ref bool __result)
		{
			if (!RedContext.Applies(__instance))
			{
				return true;
			}
			__result = __instance.m_inventory.Exists((ItemData item) => !Gc.IsHardLocked(item) && item.m_shared.m_name == name && (!matchWorldLevel || item.m_worldLevel >= Game.m_worldLevel));
			return false;
		}

		[HarmonyPatch(typeof(Inventory), "GetItem", new Type[]
		{
			typeof(string),
			typeof(int),
			typeof(bool)
		})]
		[HarmonyPrefix]
		[HarmonyPriority(800)]
		private static bool GetItem(Inventory __instance, string name, int quality, bool isPrefabName, ref ItemData? __result)
		{
			if (!RedContext.Applies(__instance))
			{
				return true;
			}
			__result = null;
			foreach (ItemData item in __instance.m_inventory)
			{
				bool num;
				if (!isPrefabName)
				{
					num = item.m_shared.m_name == name;
				}
				else
				{
					if (!((Object)(object)item.m_dropPrefab != (Object)null))
					{
						continue;
					}
					num = ((Object)item.m_dropPrefab).name == name;
				}
				if (num && !Gc.IsHardLocked(item) && (quality < 0 || quality == item.m_quality) && item.m_worldLevel >= Game.m_worldLevel)
				{
					__result = item;
					break;
				}
			}
			return false;
		}

		[HarmonyPatch(typeof(Inventory), "RemoveItem", new Type[]
		{
			typeof(string),
			typeof(int),
			typeof(int),
			typeof(bool)
		})]
		[HarmonyPrefix]
		[HarmonyPriority(800)]
		private static bool RemoveItem(Inventory __instance, string name, int amount, int itemQuality, bool worldLevelBased)
		{
			if (!RedContext.Applies(__instance))
			{
				return true;
			}
			foreach (ItemData item in __instance.m_inventory)
			{
				if (!Gc.IsHardLocked(item) && !(item.m_shared.m_name != name) && (itemQuality < 0 || item.m_quality == itemQuality) && (!worldLevelBased || item.m_worldLevel >= Game.m_worldLevel))
				{
					int num = Mathf.Min(item.m_stack, amount);
					item.m_stack -= num;
					amount -= num;
					if (amount <= 0)
					{
						break;
					}
				}
			}
			__instance.m_inventory.RemoveAll((ItemData item) => item.m_stack <= 0);
			__instance.Changed(false, false);
			return false;
		}
	}
	[HarmonyPatch]
	internal static class RedContextPatches
	{
		internal sealed class UpgradeLock
		{
			public ItemData Item { get; }

			public string Name { get; }

			public Vector2i Position { get; }

			public int Level { get; }

			public UpgradeLock(ItemData item, int level)
			{
				//IL_0020: 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)
				Item = item;
				Name = item.m_shared.m_name;
				Position = item.m_gridPos;
				Level = level;
			}
		}

		[HarmonyPatch(typeof(Player), "HaveRequirements", new Type[]
		{
			typeof(Recipe),
			typeof(bool),
			typeof(int),
			typeof(int)
		})]
		[HarmonyPrefix]
		private static void RecipePrefix(bool discover, out bool __state)
		{
			__state = !discover && RedContext.Enter(ref GcHooks.RedCraft);
		}

		[HarmonyPatch(typeof(Player), "HaveRequirements", new Type[]
		{
			typeof(Recipe),
			typeof(bool),
			typeof(int),
			typeof(int)
		})]
		[HarmonyFinalizer]
		private static void RecipeFinalizer(bool __state)
		{
			RedContext.Exit(ref GcHooks.RedCraft, __state);
		}

		[HarmonyPatch(typeof(Player), "HaveRequirements", new Type[]
		{
			typeof(Piece),
			typeof(RequirementMode)
		})]
		[HarmonyPrefix]
		private static void PiecePrefix(RequirementMode mode, out bool __state)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Invalid comparison between Unknown and I4
			__state = (int)mode != 1 && RedContext.Enter(ref GcHooks.RedCraft);
		}

		[HarmonyPatch(typeof(Player), "HaveRequirements", new Type[]
		{
			typeof(Piece),
			typeof(RequirementMode)
		})]
		[HarmonyFinalizer]
		private static void PieceFinalizer(bool __state)
		{
			RedContext.Exit(ref GcHooks.RedCraft, __state);
		}

		[HarmonyPatch(typeof(Player), "GetFirstRequiredItem")]
		[HarmonyPrefix]
		private static void FirstPrefix(out bool __state)
		{
			__state = RedContext.Enter(ref GcHooks.RedCraft);
		}

		[HarmonyPatch(typeof(Player), "GetFirstRequiredItem")]
		[HarmonyFinalizer]
		private static void FirstFinalizer(bool __state)
		{
			RedContext.Exit(ref GcHooks.RedCraft, __state);
		}

		[HarmonyPatch(typeof(Player), "ConsumeResources")]
		[HarmonyPrefix]
		private static void ConsumePrefix(out bool __state)
		{
			__state = RedContext.Enter(ref GcHooks.RedCraft);
		}

		[HarmonyPatch(typeof(Player), "ConsumeResources")]
		[HarmonyFinalizer]
		private static void ConsumeFinalizer(bool __state)
		{
			RedContext.Exit(ref GcHooks.RedCraft, __state);
		}

		[HarmonyPatch(typeof(InventoryGui), "SetupRequirement")]
		[HarmonyPrefix]
		[HarmonyPriority(800)]
		private static void RequirementPrefix(out bool __state)
		{
			__state = RedContext.Enter(ref GcHooks.RedCraft);
		}

		[HarmonyPatch(typeof(InventoryGui), "SetupRequirement")]
		[HarmonyFinalizer]
		private static void RequirementFinalizer(bool __state)
		{
			RedContext.Exit(ref GcHooks.RedCraft, __state);
		}

		[HarmonyPatch(typeof(Recipe), "GetAmount")]
		[HarmonyPrefix]
		private static void AmountPrefix(out bool __state)
		{
			__state = RedContext.Enter(ref GcHooks.RedCraft);
		}

		[HarmonyPatch(typeof(Recipe), "GetAmount")]
		[HarmonyFinalizer]
		private static void AmountFinalizer(bool __state)
		{
			RedContext.Exit(ref GcHooks.RedCraft, __state);
		}

		[HarmonyPatch(typeof(Fireplace), "Interact")]
		[HarmonyPrefix]
		private static void FireplacePrefix(out bool __state)
		{
			__state = RedContext.Enter(ref RedContext.Chests);
		}

		[HarmonyPatch(typeof(Fireplace), "Interact")]
		[HarmonyFinalizer]
		private static void FireplaceFinalizer(bool __state)
		{
			RedContext.Exit(ref RedContext.Chests, __state);
		}

		[HarmonyPatch(typeof(InventoryGui), "DoCrafting")]
		[HarmonyPrefix]
		[HarmonyPriority(800)]
		private static void CraftPrefix(InventoryGui __instance, out UpgradeLock? __state)
		{
			GcHooks.RedCraft++;
			ItemData craftUpgradeItem = __instance.m_craftUpgradeItem;
			int num = ((craftUpgradeItem != null) ? Plugin.Level(craftUpgradeItem) : 0);
			__state = ((num > 0) ? new UpgradeLock(craftUpgradeItem, num) : null);
		}

		[HarmonyPatch(typeof(InventoryGui), "DoCrafting")]
		[HarmonyFinalizer]
		private static void CraftFinalizer(Player player, UpgradeLock? __state)
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			RedContext.Leave(ref GcHooks.RedCraft);
			if (__state == null || (Object)(object)player == (Object)null)
			{
				return;
			}
			Inventory inventory = ((Humanoid)player).GetInventory();
			if (!inventory.ContainsItem(__state.Item))
			{
				ItemData itemAt = inventory.GetItemAt(__state.Position.x, __state.Position.y);
				if (itemAt != null && itemAt.m_shared.m_name == __state.Name && Plugin.Level(itemAt) == 0)
				{
					Plugin.SetLevel(itemAt, __state.Level);
				}
			}
		}
	}
	[HarmonyPatch(typeof(Container), "GetHoverText")]
	internal static class HoverPatch
	{
		private static void Postfix(Container __instance, ref string __result)
		{
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			if (Plugin.ChestsOn && !string.IsNullOrEmpty(__result) && GcWard.IsPlayerChest(__instance))
			{
				if (Gc.IsAutoLocked(__instance))
				{
					__result = __result + "\n" + GcLoc.Get("gcl_hover_locked", Array.Empty<object>());
				}
				__result = __result + "\n" + GcLoc.Get("gcl_hover_key", new object[1] { ((object)Plugin.ChestKey.Value/*cast due to .constrained prefix*/).ToString() });
			}
		}
	}
	internal static class VPlusFilter
	{
		private static PropertyInfo? configCurrent;

		internal static void Apply(Harmony harmony)
		{
			if (!Gc.VPlusLoaded)
			{
				return;
			}
			Type type = AccessTools.TypeByName("ValheimPlus.InventoryAssistant");
			if (type == null)
			{
				Plugin.Log.LogWarning((object)"ValheimPlus gefunden, aber InventoryAssistant nicht. Kistensperre und rotes Schloss wirken nur ohne V+-Automatik.");
				return;
			}
			int num = 0;
			num += Patch(harmony, type, "GetNearbyChests", null, "FilterChests");
			num += Patch(harmony, type, "GetNearbyChestsForMachine", null, "FilterChests");
			num += Patch(harmony, type, "GetNearbyChestItemsByContainerList", null, "FilterItems");
			num += Patch(harmony, type, "GetNearbyChestItems", null, "FilterItems");
			num += Patch(harmony, type, "RemoveItemFromChest", new Type[3]
			{
				typeof(Container),
				typeof(ItemData),
				typeof(int)
			}, "RemoveByItem", prefix: true, 200);
			num += Patch(harmony, type, "RemoveItemFromChest", new Type[3]
			{
				typeof(Container),
				typeof(string),
				typeof(int)
			}, "RemoveByName", prefix: true, 200);
			num += Patch(harmony, type, "ChestContainsItem", new Type[2]
			{
				typeof(Container),
				typeof(ItemData)
			}, "ContainsByItem");
			num += Patch(harmony, type, "ChestContainsItem", new Type[2]
			{
				typeof(Container),
				typeof(string)
			}, "ContainsByName");
			Type type2 = AccessTools.TypeByName("ValheimPlus.GameClasses.CookingStation_FindCookableItem_Transpiler");
			if (type2 != null)
			{
				num += Patch(harmony, type2, "PullCookableItemFromNearbyChests", null, "ChestsEnter", prefix: true);
				num += Patch(harmony, type2, "PullCookableItemFromNearbyChests", null, "ChestsLeave", prefix: false, 400, finalizer: true);
			}
			Type type3 = AccessTools.TypeByName("ValheimPlus.GameClasses.Inventory_MoveAll_Patch");
			if (type3 != null)
			{
				num += Patch(harmony, type3, "Prefix", null, "MergePrefix", prefix: true);
			}
			Type type4 = AccessTools.TypeByName("ValheimPlus.GameClasses.Fermenter_SlowUpdate_Transpiler");
			if (type4 != null)
			{
				num += Patch(harmony, type4, "AddItemFromNearbyChests", null, "ChestsEnter", prefix: true);
				num += Patch(harmony, type4, "AddItemFromNearbyChests", null, "ChestsLeave", prefix: false, 400, finalizer: true);
			}
			Plugin.Log.LogInfo((object)$"ValheimPlus erkannt: Kistensperre und rotes Schloss an {num} von 13 Stellen eingehängt.");
		}

		private static int Patch(Harmony harmony, Type type, string name, Type[]? args, string handler, bool prefix = false, int priority = 400, bool finalizer = false)
		{
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Expected O, but got Unknown
			MethodInfo methodInfo = ((args == null) ? AccessTools.Method(type, name, (Type[])null, (Type[])null) : AccessTools.Method(type, name, args, (Type[])null));
			if (methodInfo == null)
			{
				Plugin.Log.LogWarning((object)("ValheimPlus: " + name + " nicht gefunden."));
				return 0;
			}
			HarmonyMethod val = new HarmonyMethod(typeof(VPlusFilter), handler, (Type[])null)
			{
				priority = priority
			};
			if (finalizer)
			{
				harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, val, (HarmonyMethod)null);
			}
			else if (prefix)
			{
				harmony.Patch((MethodBase)methodInfo, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			else
			{
				harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, val, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			return 1;
		}

		private static bool MergePrefix(ref Inventory __0, ref Inventory __1)
		{
			if (!Plugin.AnyClosed || __0 == null || __1 == null || !VPlusMergeEnabled())
			{
				return true;
			}
			Inventory val = __0;
			Inventory val2 = __1;
			foreach (ItemData item in new List<ItemData>(val2.GetAllItems()))
			{
				if (item.m_shared.m_maxStackSize <= 1)
				{
					continue;
				}
				foreach (ItemData item2 in val.m_inventory)
				{
					if (!(item2.m_shared.m_name != item.m_shared.m_name) && item2.m_quality == item.m_quality && !Plugin.Closed(item2))
					{
						int num = Math.Min(item2.m_shared.m_maxStackSize - item2.m_stack, item.m_stack);
						item2.m_stack += num;
						if (item.m_stack == num)
						{
							val2.RemoveItem(item);
							break;
						}
						item.m_stack -= num;
					}
				}
			}
			return false;
		}

		private static bool VPlusMergeEnabled()
		{
			try
			{
				if ((object)configCurrent == null)
				{
					configCurrent = AccessTools.Property(AccessTools.TypeByName("ValheimPlus.Configurations.Configuration"), "Current");
				}
				object obj = configCurrent?.GetValue(null);
				object obj2 = ((obj == null) ? null : AccessTools.Property(obj.GetType(), "Inventory")?.GetValue(obj));
				if (obj2 == null)
				{
					return false;
				}
				object obj3 = AccessTools.Property(obj2.GetType(), "IsEnabled")?.GetValue(obj2);
				bool num = obj3 is bool && (bool)obj3;
				obj3 = AccessTools.Property(obj2.GetType(), "mergeWithExistingStacks")?.GetValue(obj2);
				bool flag = obj3 is bool && (bool)obj3;
				return num && flag;
			}
			catch
			{
				return false;
			}
		}

		private static void ChestsEnter(out bool __state)
		{
			__state = RedContext.Enter(ref RedContext.Chests);
		}

		private static void ChestsLeave(bool __state)
		{
			RedContext.Exit(ref RedContext.Chests, __state);
		}

		private static void FilterChests(ref List<Container> __result)
		{
			if (Plugin.ChestsOn && __result != null && __result.Count > 0)
			{
				__result.RemoveAll((Predicate<Container>)Gc.IsAutoLocked);
			}
		}

		private static void FilterItems(ref List<ItemData> __result)
		{
			if (Plugin.ItemsOn && __result != null && __result.Count > 0)
			{
				__result.RemoveAll((Predicate<ItemData>)Gc.IsHardLocked);
			}
		}

		private static bool RemoveByItem(Container chest, ItemData needle, int amount, ref int __result, bool __runOriginal)
		{
			if (__runOriginal)
			{
				return Remove(chest, needle?.m_shared.m_name, amount, ref __result);
			}
			return false;
		}

		private static bool RemoveByName(Container chest, string needle, int amount, ref int __result, bool __runOriginal)
		{
			if (__runOriginal)
			{
				return Remove(chest, needle, amount, ref __result);
			}
			return false;
		}

		private static bool Remove(Container chest, string? name, int amount, ref int __result)
		{
			Inventory val = (((Object)(object)chest != (Object)null) ? chest.GetInventory() : null);
			if (!Plugin.ItemsOn || val == null || name == null || !val.m_inventory.Exists((ItemData i) => i.m_shared.m_name == name && Gc.IsHardLocked(i)))
			{
				return true;
			}
			int num = 0;
			foreach (ItemData item in val.m_inventory)
			{
				if (amount <= 0)
				{
					break;
				}
				if (!(item.m_shared.m_name != name) && !Gc.IsHardLocked(item))
				{
					int num2 = Mathf.Min(item.m_stack, amount);
					item.m_stack -= num2;
					amount -= num2;
					num += num2;
				}
			}
			if (num > 0)
			{
				val.m_inventory.RemoveAll((ItemData i) => i.m_stack <= 0);
				chest.Save();
				val.Changed(false, false);
			}
			__result = num;
			return false;
		}

		private static void ContainsByItem(Container chest, ItemData needle, ref bool __result)
		{
			if (__result && needle != null)
			{
				__result = HasFree(chest, needle.m_shared.m_name);
			}
		}

		private static void ContainsByName(Container chest, string needle, ref bool __result)
		{
			if (__result)
			{
				__result = HasFree(chest, needle);
			}
		}

		private static bool HasFree(Container chest, string name)
		{
			if (Plugin.ItemsOn && !((Object)(object)chest == (Object)null) && chest.GetInventory() != null)
			{
				return chest.GetInventory().m_inventory.Exists((ItemData i) => i.m_shared.m_name == name && !Gc.IsHardLocked(i));
			}
			return true;
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}

GC_StackFix.dll

Decompiled 3 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using GC.Shared;
using GC.ValheimPlusPlus.StackFix;
using HarmonyLib;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("assembly_guiutils")]
[assembly: IgnoresAccessChecksTo("assembly_utils")]
[assembly: IgnoresAccessChecksTo("assembly_valheim")]
[assembly: TargetFramework(".NETFramework,Version=v4.8.1", FrameworkDisplayName = ".NET Framework 4.8.1")]
[assembly: AssemblyCompany("Gorduan")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.5.1.0")]
[assembly: AssemblyInformationalVersion("1.5.1+7c8bde9391283b12125175d68f4632c4cf8f93b3")]
[assembly: AssemblyProduct("GC_StackFix")]
[assembly: AssemblyTitle("GC_StackFix")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.5.1.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 GC.ValheimPlusPlus
{
	internal static class VppPlugin
	{
		internal static ManualLogSource Log;

		internal static GcModule? Module;

		internal static bool Allowed
		{
			get
			{
				if (Module != null)
				{
					return Module.Allowed;
				}
				return true;
			}
		}

		internal static bool OnServer
		{
			get
			{
				if (Module != null)
				{
					return Module.OnServer;
				}
				return false;
			}
		}
	}
}
namespace GC.ValheimPlusPlus.StackFix
{
	internal static class Plugin
	{
		public const string Guid = "gorduan.gc_stackfix";

		public const string ModName = "GC_StackFix";

		public const string Version = "1.0.0";

		internal static ManualLogSource Log;

		internal static ConfigEntry<bool> Enabled;

		internal static ConfigEntry<bool> ProtectHotbar;

		internal static bool Blocked => Chainloader.PluginInfos.ContainsKey("gorduan.gc_stackfix");

		internal static bool On
		{
			get
			{
				if (Enabled.Value && !Blocked)
				{
					return VppPlugin.Allowed;
				}
				return false;
			}
		}

		internal static bool HotbarOn
		{
			get
			{
				if (ProtectHotbar.Value && !Blocked)
				{
					return VppPlugin.Allowed;
				}
				return false;
			}
		}

		internal static void Init(ConfigFile Config, Harmony harmony)
		{
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Expected O, but got Unknown
			Log = VppPlugin.Log;
			Enabled = Gc.Bind<bool>(Config, "1 - Auto-Stack Fix", "Enabled", true, "V+ Auto-Stack lagert Rüstung, Schilde und Schmuck (auch EpicLoot) und EpicLoot-Materialien wieder ein, wenn die Kiste dasselbe Item enthält. Waffen, Werkzeuge und echte Munition folgen der V+-Einstellung.", (AcceptableValueBase)null);
			ProtectHotbar = Gc.Bind<bool>(Config, "1 - Auto-Stack Fix", "ProtectHotbar", true, "Items in der Schnellleiste (oberste Inventarreihe) nie in Kisten stapeln.", (AcceptableValueBase)null);
			if (Gc.VPlusLoaded)
			{
				Type type = AccessTools.TypeByName("ValheimPlus.GameClasses.Inventory_StackAll_Patch");
				MethodInfo methodInfo = ((type == null) ? null : AccessTools.Method(type, "ContainsItemByName", new Type[2]
				{
					typeof(Inventory),
					typeof(string)
				}, (Type[])null));
				if (methodInfo != null)
				{
					harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(Plugin), "ContainsItemByNamePostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					Log.LogInfo((object)"ValheimPlus erkannt: Auto-Stack lagert Rüstung, Schilde, Schmuck und EpicLoot-Materialien wieder ein (Waffen, Werkzeuge und Munition nach V+-Einstellung).");
				}
				else
				{
					Log.LogWarning((object)"ValheimPlus gefunden, aber Inventory_StackAll_Patch.ContainsItemByName nicht. Ausrüstungs-Fix inaktiv.");
				}
			}
		}

		private static void ContainsItemByNamePostfix(Inventory inventory, string name, ref bool __result)
		{
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Invalid comparison between Unknown and I4
			if (__result || !On)
			{
				return;
			}
			foreach (ItemData item in inventory.m_inventory)
			{
				if (!(item.m_shared.m_name != name) && (TaggedMaterial(item) || (item.IsEquipable() && !IsAmmo(item) && !item.IsWeapon() && (int)item.m_shared.m_itemType != 19)))
				{
					__result = true;
					break;
				}
			}
		}

		private static bool TaggedMaterial(ItemData item)
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Invalid comparison between Unknown and I4
			SharedData shared = item.m_shared;
			if (!string.IsNullOrEmpty(shared.m_ammoType) && !IsAmmo(item) && !item.IsEquipable() && !shared.m_isDrink)
			{
				if ((int)shared.m_itemType == 2)
				{
					if (!(shared.m_food > 0f) && !(shared.m_foodStamina > 0f))
					{
						return !(shared.m_foodEitr > 0f);
					}
					return false;
				}
				return true;
			}
			return false;
		}

		private static bool IsAmmo(ItemData item)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Invalid comparison between Unknown and I4
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Invalid comparison between Unknown and I4
			if ((int)item.m_shared.m_itemType != 9)
			{
				return (int)item.m_shared.m_itemType == 23;
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(Inventory), "StackAll")]
	internal static class StackAllPatch
	{
		[HarmonyPriority(800)]
		private static void Prefix(Inventory fromInventory, out List<ItemData>? __state)
		{
			__state = null;
			if (Plugin.HotbarOn && !((Object)(object)Player.m_localPlayer == (Object)null) && fromInventory == ((Humanoid)Player.m_localPlayer).GetInventory())
			{
				__state = fromInventory.m_inventory.FindAll(Protect);
				if (__state.Count == 0)
				{
					__state = null;
				}
				else
				{
					fromInventory.m_inventory.RemoveAll(Protect);
				}
			}
			static bool Protect(ItemData item)
			{
				return item.m_gridPos.y == 0;
			}
		}

		[HarmonyPriority(0)]
		private static void Finalizer(Inventory fromInventory, List<ItemData>? __state)
		{
			if (__state != null)
			{
				fromInventory.m_inventory.AddRange(__state);
				fromInventory.Changed(false, false);
			}
		}
	}
}
namespace GC.ValheimPlusPlus.ModStackFix
{
	[BepInPlugin("gorduan.gc.stackfix", "GC_StackFix", "1.5.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public class Plugin : BaseUnityPlugin
	{
		internal static GcModule Module;

		private void Awake()
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Expected O, but got Unknown
			Module = GcKern.Register((BaseUnityPlugin)(object)this, ((BaseUnityPlugin)this).Logger, GcMod.Info);
			VppPlugin.Log = ((BaseUnityPlugin)this).Logger;
			VppPlugin.Module = Module;
			Harmony val = new Harmony("gorduan.gc.stackfix");
			GC.ValheimPlusPlus.StackFix.Plugin.Init(((BaseUnityPlugin)this).Config, val);
			Module.TakeOverOldSettings();
			val.PatchAll(typeof(Plugin).Assembly);
			Module.MenuGroup((Func<string, string>)((string key) => GcLoc.Get("vpp_" + key, Array.Empty<object>())), (string)null).Main("stackfix", (ConfigEntryBase)(object)GC.ValheimPlusPlus.StackFix.Plugin.Enabled, (Func<bool>)(() => GC.ValheimPlusPlus.StackFix.Plugin.Blocked), "GC_StackFix", (Func<string>)null, 0.0, "", (Func<bool>)(() => Gc.VPlusLoaded), (Func<bool>)null).Sub("hotbar", (ConfigEntryBase)(object)GC.ValheimPlusPlus.StackFix.Plugin.ProtectHotbar, 0.0, "", (Func<bool>)(() => GC.ValheimPlusPlus.StackFix.Plugin.Blocked), "GC_StackFix", (Func<bool>)null, (Func<bool>)null);
		}
	}
	internal static class GcMod
	{
		public const string Id = "stackfix";

		public const string Guid = "gorduan.gc.stackfix";

		public const string Name = "GC_StackFix";

		public const string Version = "1.5.1";

		public const int Stufe = 3;

		public static GcModInfo Info
		{
			get
			{
				//IL_0000: Unknown result type (might be due to invalid IL or missing references)
				//IL_0006: Expected O, but got Unknown
				//IL_003b: Unknown result type (might be due to invalid IL or missing references)
				GcModInfo val = new GcModInfo();
				val.Id = "stackfix";
				val.Guid = "gorduan.gc.stackfix";
				val.Name = "GC_StackFix";
				val.Version = "1.5.1";
				val.Stufe = 3;
				val.Seite = (GcSeite)0;
				val.Braucht = Array.Empty<string>();
				val.Partner = Array.Empty<string>();
				val.Fremd = new string[1] { "org.bepinex.plugins.valheim_plus" };
				val.AlteConfig = "gorduan.valheimplusplus.cfg";
				val.Ausgleich = 0f;
				return val;
			}
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}