Decompiled source of Prime Chest Sizes v1.0.0

BepInEx/plugins/PrimeChestSizes/PrimeChestSizes.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("Prime Chest Sizes")]
[assembly: AssemblyDescription("Configurable chest capacities for Valheim")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace PrimeChestSizes
{
	internal readonly struct GridSize
	{
		public readonly int Width;

		public readonly int Height;

		public GridSize(int width, int height)
		{
			Width = width;
			Height = height;
		}
	}
	internal static class SizePolicy
	{
		public static GridSize Fit(int width, int height, int vanillaWidth, int vanillaHeight, IEnumerable<GridSize> occupied)
		{
			int num = Math.Max(vanillaWidth, Math.Max(1, Math.Min(8, width)));
			int num2 = Math.Max(vanillaHeight, Math.Max(1, Math.Min(20, height)));
			foreach (GridSize item in occupied)
			{
				if (item.Width >= 0)
				{
					num = Math.Max(num, item.Width + 1);
				}
				if (item.Height >= 0)
				{
					num2 = Math.Max(num2, item.Height + 1);
				}
			}
			return new GridSize(num, num2);
		}

		public static string PrefabName(string name)
		{
			if (!name.EndsWith("(Clone)", StringComparison.Ordinal))
			{
				return name;
			}
			return name.Substring(0, name.Length - "(Clone)".Length).TrimEnd();
		}
	}
	[BepInPlugin("prime.valheim.chestsizes", "Prime Chest Sizes", "1.0.0")]
	public sealed class Plugin : BaseUnityPlugin
	{
		private sealed class ChestOptions
		{
			public ConfigEntry<bool> Enabled;

			public ConfigEntry<int> Columns;

			public ConfigEntry<int> Rows;
		}

		private sealed class OriginalSize
		{
			public int Width;

			public int Height;

			public ChestOptions Options;
		}

		public const string Guid = "prime.valheim.chestsizes";

		private static Plugin instance;

		private Harmony harmony;

		private bool errorLogged;

		private readonly Dictionary<string, ChestOptions> options = new Dictionary<string, ChestOptions>(StringComparer.Ordinal);

		private static readonly ConditionalWeakTable<object, OriginalSize> originals = new ConditionalWeakTable<object, OriginalSize>();

		private static Type containerType;

		private static Type viewType;

		private static FieldInfo containerWidth;

		private static FieldInfo containerHeight;

		private static FieldInfo containerInventory;

		private static FieldInfo inventoryWidth;

		private static FieldInfo inventoryHeight;

		private static FieldInfo gridPosition;

		private static FieldInfo positionX;

		private static FieldInfo positionY;

		private static MethodInfo getItems;

		private static MethodInfo getZdo;

		private static MethodInfo isOwner;

		private static MethodInfo zdoGet;

		private static MethodInfo zdoSet;

		private static bool stringKeys;

		private void Awake()
		{
			//IL_0089: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Expected O, but got Unknown
			instance = this;
			Add("Wooden Chest", "piece_chest_wood", 8, 4);
			Add("Personal Chest", "piece_chest_private", 6, 4);
			Add("Reinforced Chest", "piece_chest", 8, 6);
			Add("Black Metal Chest", "piece_chest_blackmetal", 8, 8);
			Add("Barrel", "piece_chest_barrel", 8, 4);
			Add("Grausten Chest", "piece_chest_grausten", 8, 10);
			((BaseUnityPlugin)this).Config.Save();
			try
			{
				ResolveApi();
				harmony = new Harmony("prime.valheim.chestsizes");
				Patch("Awake", "BeforeAwake", "AfterLoad");
				Patch("Load", "BeforeLoad", "AfterLoad");
				Patch("Save", "BeforeSave", null);
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Prime Chest Sizes 1.0.0 loaded. Edit prime.valheim.chestsizes.cfg with the game closed. Multiplayer: use identical configs on all players and the server.");
			}
			catch (Exception ex)
			{
				Harmony obj = harmony;
				if (obj != null)
				{
					obj.UnpatchSelf();
				}
				((BaseUnityPlugin)this).Logger.LogError((object)("Prime Chest Sizes could not initialize against this Valheim version; patches were removed. " + ex));
			}
		}

		private void Add(string section, string prefab, int columns, int rows)
		{
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Expected O, but got Unknown
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Expected O, but got Unknown
			options.Add(prefab, new ChestOptions
			{
				Enabled = ((BaseUnityPlugin)this).Config.Bind<bool>(section, "Enabled", true, "Use custom capacity for " + prefab + ". False requests vanilla size while protecting occupied slots. Restart after editing."),
				Columns = ((BaseUnityPlugin)this).Config.Bind<int>(section, "Columns", columns, new ConfigDescription("Horizontal slots (1-8). Never reduced below vanilla width or occupied slots. Restart after editing.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 8), Array.Empty<object>())),
				Rows = ((BaseUnityPlugin)this).Config.Bind<int>(section, "Rows", rows, new ConfigDescription("Vertical slots (1-20). Never reduced below vanilla height or occupied slots. Extra rows use Valheim's container scrolling. Restart after editing.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 20), Array.Empty<object>()))
			});
		}

		private static Type RequireType(string name)
		{
			return AccessTools.TypeByName(name) ?? throw new MissingMemberException(name);
		}

		private static FieldInfo Field(Type type, string name)
		{
			return AccessTools.Field(type, name) ?? throw new MissingFieldException(type.FullName, name);
		}

		private static MethodInfo Method(Type type, string name, params Type[] args)
		{
			return AccessTools.Method(type, name, args, (Type[])null) ?? throw new MissingMethodException(type.FullName, name);
		}

		private static void ResolveApi()
		{
			containerType = RequireType("Container");
			viewType = RequireType("ZNetView");
			Type type = RequireType("Inventory");
			Type type2 = RequireType("ZDO");
			Type type3 = RequireType("ItemDrop").GetNestedType("ItemData", BindingFlags.Public | BindingFlags.NonPublic) ?? throw new MissingMemberException("ItemDrop.ItemData");
			containerWidth = Field(containerType, "m_width");
			containerHeight = Field(containerType, "m_height");
			containerInventory = Field(containerType, "m_inventory");
			inventoryWidth = Field(type, "m_width");
			inventoryHeight = Field(type, "m_height");
			getItems = Method(type, "GetAllItems");
			gridPosition = Field(type3, "m_gridPos");
			positionX = Field(gridPosition.FieldType, "x");
			positionY = Field(gridPosition.FieldType, "y");
			getZdo = Method(viewType, "GetZDO");
			isOwner = Method(viewType, "IsOwner");
			zdoGet = AccessTools.Method(type2, "GetInt", new Type[2]
			{
				typeof(string),
				typeof(int)
			}, (Type[])null);
			zdoSet = AccessTools.Method(type2, "Set", new Type[2]
			{
				typeof(string),
				typeof(int)
			}, (Type[])null);
			stringKeys = zdoGet != null && zdoSet != null;
			if (!stringKeys)
			{
				zdoGet = Method(type2, "GetInt", typeof(int), typeof(int));
				zdoSet = Method(type2, "Set", typeof(int), typeof(int));
			}
			Method(containerType, "Awake");
			Method(containerType, "Load");
			Method(containerType, "Save");
		}

		private void Patch(string original, string prefix, string postfix)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			harmony.Patch((MethodBase)Method(containerType, original), (prefix == null) ? ((HarmonyMethod)null) : new HarmonyMethod(typeof(Plugin), prefix, (Type[])null), (postfix == null) ? ((HarmonyMethod)null) : new HarmonyMethod(typeof(Plugin), postfix, (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
		}

		private static bool TryOriginal(object container, out OriginalSize original)
		{
			if (originals.TryGetValue(container, out original))
			{
				return true;
			}
			Component val = (Component)((container is Component) ? container : null);
			if ((Object)(object)val == (Object)null || !instance.options.TryGetValue(SizePolicy.PrefabName(((Object)val.gameObject).name), out var value))
			{
				return false;
			}
			original = new OriginalSize
			{
				Width = (int)containerWidth.GetValue(container),
				Height = (int)containerHeight.GetValue(container),
				Options = value
			};
			originals.Add(container, original);
			return true;
		}

		private static GridSize Requested(OriginalSize original)
		{
			return SizePolicy.Fit(original.Options.Enabled.Value ? original.Options.Columns.Value : original.Width, original.Options.Enabled.Value ? original.Options.Rows.Value : original.Height, original.Width, original.Height, Array.Empty<GridSize>());
		}

		private static object View(object container)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			return ((Component)container).GetComponent(viewType);
		}

		private static object Zdo(object container)
		{
			object obj = View(container);
			if (obj != null)
			{
				return getZdo.Invoke(obj, null);
			}
			return null;
		}

		private static object Key(string key)
		{
			if (!stringKeys)
			{
				return StableHash(key);
			}
			return key;
		}

		private static int StableHash(string text)
		{
			int num = 5381;
			int num2 = num;
			for (int i = 0; i < text.Length; i += 2)
			{
				num = ((num << 5) + num) ^ text[i];
				if (i == text.Length - 1)
				{
					break;
				}
				num2 = ((num2 << 5) + num2) ^ text[i + 1];
			}
			return num + num2 * 1566083941;
		}

		private static void Prepare(object container)
		{
			if (TryOriginal(container, out var original))
			{
				GridSize gridSize = Requested(original);
				int num = gridSize.Width;
				int num2 = gridSize.Height;
				object obj = Zdo(container);
				if (obj != null)
				{
					num = Math.Max(num, (int)zdoGet.Invoke(obj, new object[2]
					{
						Key("prime.valheim.chestsizes.width"),
						num
					}));
					num2 = Math.Max(num2, (int)zdoGet.Invoke(obj, new object[2]
					{
						Key("prime.valheim.chestsizes.height"),
						num2
					}));
				}
				object value = containerInventory.GetValue(container);
				if (value != null)
				{
					num = Math.Max(num, (int)inventoryWidth.GetValue(value));
					num2 = Math.Max(num2, (int)inventoryHeight.GetValue(value));
					inventoryWidth.SetValue(value, num);
					inventoryHeight.SetValue(value, num2);
				}
				containerWidth.SetValue(container, num);
				containerHeight.SetValue(container, num2);
			}
		}

		private static IEnumerable<GridSize> Positions(object inventory)
		{
			foreach (object item in (IEnumerable)getItems.Invoke(inventory, null))
			{
				object value = gridPosition.GetValue(item);
				yield return new GridSize((int)positionX.GetValue(value), (int)positionY.GetValue(value));
			}
		}

		private static void Resize(object container)
		{
			if (TryOriginal(container, out var original))
			{
				object value = containerInventory.GetValue(container);
				if (value != null)
				{
					GridSize gridSize = Requested(original);
					GridSize gridSize2 = SizePolicy.Fit(gridSize.Width, gridSize.Height, original.Width, original.Height, Positions(value));
					containerWidth.SetValue(container, gridSize2.Width);
					containerHeight.SetValue(container, gridSize2.Height);
					inventoryWidth.SetValue(value, gridSize2.Width);
					inventoryHeight.SetValue(value, gridSize2.Height);
				}
			}
		}

		private static void Remember(object container)
		{
			if (!TryOriginal(container, out var _))
			{
				return;
			}
			object value = containerInventory.GetValue(container);
			object obj = View(container);
			if (value != null && obj != null && (bool)isOwner.Invoke(obj, null))
			{
				object obj2 = getZdo.Invoke(obj, null);
				if (obj2 != null)
				{
					zdoSet.Invoke(obj2, new object[2]
					{
						Key("prime.valheim.chestsizes.width"),
						inventoryWidth.GetValue(value)
					});
					zdoSet.Invoke(obj2, new object[2]
					{
						Key("prime.valheim.chestsizes.height"),
						inventoryHeight.GetValue(value)
					});
				}
			}
		}

		private static void Guard(Action action)
		{
			try
			{
				action();
			}
			catch (Exception ex)
			{
				if (!instance.errorLogged)
				{
					instance.errorLogged = true;
					((BaseUnityPlugin)instance).Logger.LogError((object)("Chest resize failed. Exit without further chest changes and check compatibility. " + ex));
				}
			}
		}

		private static void BeforeAwake(object __instance)
		{
			Guard(delegate
			{
				Prepare(__instance);
			});
		}

		private static void BeforeLoad(object __instance)
		{
			Guard(delegate
			{
				Prepare(__instance);
			});
		}

		private static void AfterLoad(object __instance)
		{
			Guard(delegate
			{
				Resize(__instance);
			});
		}

		private static void BeforeSave(object __instance)
		{
			Guard(delegate
			{
				Remember(__instance);
			});
		}

		private void OnDestroy()
		{
			Harmony obj = harmony;
			if (obj != null)
			{
				obj.UnpatchSelf();
			}
		}
	}
}