Decompiled source of RossItemDrawers v1.0.7

plugins/ItemDrawers.Core.dll

Decompiled 4 hours 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 Microsoft.CodeAnalysis;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("ItemDrawers.Core")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+2fb60d9584e8b8b8ea0c17c4691cc1491e88cacf")]
[assembly: AssemblyProduct("ItemDrawers.Core")]
[assembly: AssemblyTitle("ItemDrawers.Core")]
[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 ItemDrawers.Core
{
	public readonly struct IconSize
	{
		public readonly string Name;

		public readonly int Width;

		public readonly int Height;

		public IconSize(string name, int width, int height)
		{
			Name = name;
			Width = width;
			Height = height;
		}
	}
	public readonly struct AtlasRect
	{
		public readonly int X;

		public readonly int Y;

		public readonly int Width;

		public readonly int Height;

		public AtlasRect(int x, int y, int width, int height)
		{
			X = x;
			Y = y;
			Width = width;
			Height = height;
		}
	}
	public sealed class AtlasLayout
	{
		public int Width { get; }

		public int Height { get; }

		public IReadOnlyDictionary<string, AtlasRect> Rects { get; }

		public AtlasLayout(int width, int height, IReadOnlyDictionary<string, AtlasRect> rects)
		{
			Width = width;
			Height = height;
			Rects = rects;
		}

		public bool TryGetUv(string name, out float u0, out float v0, out float u1, out float v1)
		{
			u0 = (v0 = (u1 = (v1 = 0f)));
			if (name == null || !Rects.TryGetValue(name, out var value))
			{
				return false;
			}
			u0 = (float)value.X / (float)Width;
			v0 = (float)value.Y / (float)Height;
			u1 = (float)(value.X + value.Width) / (float)Width;
			v1 = (float)(value.Y + value.Height) / (float)Height;
			return true;
		}
	}
	public static class ChunkSplitter
	{
		public static IReadOnlyList<int> Chunks(int amount, int stackSize)
		{
			List<int> list = new List<int>();
			if (amount <= 0)
			{
				return list;
			}
			int num = ((stackSize < 1) ? 1 : stackSize);
			int num2 = amount;
			while (num2 > 0)
			{
				int num3 = ((num2 < num) ? num2 : num);
				list.Add(num3);
				num2 -= num3;
			}
			return list;
		}
	}
	public static class DrawerMeshBuilder
	{
		private const float UvScale = 1f;

		private static readonly int[] Signs = new int[2] { -1, 1 };

		public static MeshData Build(DrawerProportions p)
		{
			MeshData.Builder builder = new MeshData.Builder();
			float width = p.Width;
			float height = p.Height;
			float depth = p.Depth;
			float num = Math.Min(p.FrameThickness, Math.Min(width, height) / 2f - 0.02f);
			float num2 = Math.Min(p.RecessDepth, depth * 0.5f);
			float bevel = p.Bevel;
			AddChamferBox(builder, 0f, 0f, (0f - num2) / 2f, width, height, depth - num2, bevel);
			float cz = depth / 2f - num2 / 2f;
			AddChamferBox(builder, 0f, height / 2f - num / 2f, cz, width, num, num2, bevel);
			AddChamferBox(builder, 0f, (0f - height) / 2f + num / 2f, cz, width, num, num2, bevel);
			float num3 = height - 2f * num;
			if (num3 > 0.01f)
			{
				AddChamferBox(builder, (0f - width) / 2f + num / 2f, 0f, cz, num, num3, num2, bevel);
				AddChamferBox(builder, width / 2f - num / 2f, 0f, cz, num, num3, num2, bevel);
			}
			AddHandle(builder, p, num, num2);
			return builder.Build();
		}

		private static void AddHandle(MeshData.Builder gb, DrawerProportions p, float frame, float recess)
		{
			float num = p.Depth / 2f - recess;
			float num2 = p.Height - 2f * frame;
			switch (p.Handle)
			{
			case HandleStyle.Bar:
			{
				float w2 = Math.Min(p.HandleWidth, p.Width - 2f * frame - 0.02f);
				float cy = (0f - num2) / 2f + p.HandleSection * 1.4f;
				AddChamferBox(gb, 0f, cy, num + p.HandleProud / 2f, w2, p.HandleSection, p.HandleProud, Math.Min(p.Bevel, p.HandleSection / 2.5f));
				break;
			}
			case HandleStyle.Knobs:
			{
				float num3 = Math.Min(p.HandleWidth / 2f, p.Width / 2f - frame - p.HandleSection);
				for (int i = -1; i <= 1; i += 2)
				{
					AddChamferBox(gb, num3 * (float)i, 0f, num + p.HandleProud / 2f, p.HandleSection, p.HandleSection, p.HandleProud, p.HandleSection / 3f);
				}
				break;
			}
			case HandleStyle.Pull:
			{
				float w = Math.Min(p.HandleWidth, p.Width - 2f * frame);
				AddChamferBox(gb, 0f, (0f - p.Height) / 2f + frame + p.HandleSection / 2f, p.Depth / 2f - p.HandleProud / 2f, w, p.HandleSection, p.HandleProud, p.Bevel);
				break;
			}
			case HandleStyle.None:
				break;
			}
		}

		public static MeshData ChamferBox(float cx, float cy, float cz, float w, float h, float d, float chamfer)
		{
			MeshData.Builder builder = new MeshData.Builder();
			AddChamferBox(builder, cx, cy, cz, w, h, d, chamfer);
			return builder.Build();
		}

		private static void AddChamferBox(MeshData.Builder gb, float cx, float cy, float cz, float w, float h, float d, float chamfer)
		{
			float num = w / 2f;
			float num2 = h / 2f;
			float num3 = d / 2f;
			float num4 = Math.Max(0f, Math.Min(chamfer, Math.Min(num, Math.Min(num2, num3)) * 0.98f));
			float[] extent = new float[3] { num, num2, num3 };
			float[] inner = new float[3]
			{
				num - num4,
				num2 - num4,
				num3 - num4
			};
			float[] centre = new float[3] { cx, cy, cz };
			int[][] array = new int[4][]
			{
				new int[2] { -1, -1 },
				new int[2] { 1, -1 },
				new int[2] { 1, 1 },
				new int[2] { -1, 1 }
			};
			int[] signs;
			for (int i = 0; i < 3; i++)
			{
				signs = Signs;
				foreach (int num5 in signs)
				{
					int num6 = (i + 1) % 3;
					int num7 = (i + 2) % 3;
					float[][] array2 = new float[4][];
					for (int k = 0; k < 4; k++)
					{
						int[] array3 = new int[3];
						array3[i] = num5;
						array3[num6] = array[k][0];
						array3[num7] = array[k][1];
						array2[k] = Point(i, array3);
					}
					float[] array4 = new float[3];
					array4[i] = num5;
					gb.Quad(array2[0], array2[1], array2[2], array2[3], array4, 1f);
				}
			}
			for (int l = 0; l < 3; l++)
			{
				int num8 = (l + 1) % 3;
				int num9 = (l + 2) % 3;
				signs = Signs;
				foreach (int num10 in signs)
				{
					int[] signs2 = Signs;
					foreach (int num11 in signs2)
					{
						float[][] array5 = new float[2][];
						float[][] array6 = new float[2][];
						for (int n = 0; n < 2; n++)
						{
							int[] array7 = new int[3];
							array7[l] = Signs[n];
							array7[num8] = num10;
							array7[num9] = num11;
							array5[n] = Point(num8, array7);
							array6[n] = Point(num9, array7);
						}
						float[] array8 = new float[3];
						array8[num8] = num10;
						array8[num9] = num11;
						gb.Quad(array5[0], array5[1], array6[1], array6[0], array8, 1f);
					}
				}
			}
			signs = Signs;
			foreach (int num12 in signs)
			{
				int[] signs2 = Signs;
				foreach (int num13 in signs2)
				{
					int[] signs3 = Signs;
					foreach (int num15 in signs3)
					{
						int[] s = new int[3] { num12, num13, num15 };
						float[] hint = new float[3] { num12, num13, num15 };
						gb.TriangleHinted(Point(0, s), Point(1, s), Point(2, s), hint, 1f);
					}
				}
			}
			float[] Point(int axis, int[] array10)
			{
				float[] array9 = new float[3];
				for (int num16 = 0; num16 < 3; num16++)
				{
					array9[num16] = centre[num16] + (float)array10[num16] * ((num16 == axis) ? extent[num16] : inner[num16]);
				}
				return array9;
			}
		}
	}
	public readonly struct DrawerOutcome
	{
		public readonly bool Accepted;

		public readonly DrawerSnapshot Result;

		public readonly int MovedToDrawer;

		public readonly int MovedToPlayer;

		public readonly string Rejection;

		private DrawerOutcome(bool accepted, DrawerSnapshot result, int toDrawer, int toPlayer, string rejection)
		{
			Accepted = accepted;
			Result = result;
			MovedToDrawer = toDrawer;
			MovedToPlayer = toPlayer;
			Rejection = rejection;
		}

		public static DrawerOutcome Deposited(DrawerSnapshot result, int moved)
		{
			return new DrawerOutcome(accepted: true, result, moved, 0, null);
		}

		public static DrawerOutcome Withdrew(DrawerSnapshot result, int moved)
		{
			return new DrawerOutcome(accepted: true, result, 0, moved, null);
		}

		public static DrawerOutcome Changed(DrawerSnapshot result)
		{
			return new DrawerOutcome(accepted: true, result, 0, 0, null);
		}

		public static DrawerOutcome Refused(DrawerSnapshot unchanged, string why)
		{
			return new DrawerOutcome(accepted: false, unchanged, 0, 0, why);
		}
	}
	public enum HandleStyle
	{
		None,
		Bar,
		Knobs,
		Pull
	}
	public sealed class DrawerProportions
	{
		public float Width = 0.66f;

		public float Height = 0.66f;

		public float Depth = 0.66f;

		public float FrameThickness = 0.033f;

		public float RecessDepth = 0.0198f;

		public float Bevel = 0.00396f;

		public float HandleWidth = 0.264f;

		public float HandleSection = 0.0429f;

		public float HandleProud = 0.0231f;

		public HandleStyle Handle = HandleStyle.Bar;

		public float LabelScale = 0.98f;

		public float LabelSize
		{
			get
			{
				float num = Width - 2f * FrameThickness;
				float num2 = Height - 2f * FrameThickness;
				if (Handle == HandleStyle.Bar)
				{
					num2 -= HandleSection * 3f;
				}
				float num3 = ((num < num2) ? num : num2) * LabelScale;
				if (!(num3 < 0.05f))
				{
					return num3;
				}
				return 0.05f;
			}
		}
	}
	public readonly struct DrawerSnapshot : IEquatable<DrawerSnapshot>
	{
		public readonly string ItemName;

		public readonly int Amount;

		public bool IsAssigned => !string.IsNullOrEmpty(ItemName);

		public bool IsEmpty => Amount <= 0;

		public DrawerSnapshot(string itemName, int amount)
		{
			ItemName = itemName ?? "";
			Amount = ((amount >= 0) ? amount : 0);
		}

		public bool Equals(DrawerSnapshot other)
		{
			if (string.Equals(ItemName, other.ItemName, StringComparison.Ordinal))
			{
				return Amount == other.Amount;
			}
			return false;
		}

		public override bool Equals(object obj)
		{
			if (obj is DrawerSnapshot other)
			{
				return Equals(other);
			}
			return false;
		}

		public override int GetHashCode()
		{
			return ((ItemName?.GetHashCode() ?? 0) * 397) ^ Amount;
		}

		public override string ToString()
		{
			if (!IsAssigned)
			{
				return "(empty drawer)";
			}
			return $"{ItemName} x{Amount}";
		}
	}
	public static class DrawerState
	{
		public const string WrongItem = "This drawer holds a different item";

		public const string DrawerFull = "This drawer is full";

		public const string DrawerEmpty = "This drawer has been drained";

		public const string NotAssigned = "This drawer has not been assigned";

		public const string BadCapacity = "This drawer has an invalid capacity";

		public const string NotEmptyYet = "Empty the drawer before clearing it";

		public const string NothingOffered = "Nothing to deposit";

		public static DrawerOutcome Deposit(DrawerSnapshot current, int capacity, string itemName, int offered)
		{
			if (offered <= 0)
			{
				return DrawerOutcome.Refused(current, "Nothing to deposit");
			}
			if (string.IsNullOrEmpty(itemName))
			{
				return DrawerOutcome.Refused(current, "Nothing to deposit");
			}
			if (capacity <= 0)
			{
				return DrawerOutcome.Refused(current, "This drawer has an invalid capacity");
			}
			if (current.IsAssigned && current.ItemName != itemName)
			{
				return DrawerOutcome.Refused(current, "This drawer holds a different item");
			}
			int num = capacity - current.Amount;
			if (num <= 0)
			{
				return DrawerOutcome.Refused(current, "This drawer is full");
			}
			int num2 = ((offered < num) ? offered : num);
			return DrawerOutcome.Deposited(new DrawerSnapshot(itemName, current.Amount + num2), num2);
		}

		public static DrawerOutcome WithdrawStack(DrawerSnapshot current, int maxStackSize)
		{
			if (!current.IsAssigned)
			{
				return DrawerOutcome.Refused(current, "This drawer has not been assigned");
			}
			if (current.IsEmpty)
			{
				return DrawerOutcome.Refused(current, "This drawer has been drained");
			}
			int num = ((maxStackSize < 1) ? 1 : maxStackSize);
			int num2 = ((current.Amount < num) ? current.Amount : num);
			return DrawerOutcome.Withdrew(new DrawerSnapshot(current.ItemName, current.Amount - num2), num2);
		}

		public static DrawerOutcome WithdrawOne(DrawerSnapshot current)
		{
			if (!current.IsAssigned)
			{
				return DrawerOutcome.Refused(current, "This drawer has not been assigned");
			}
			if (current.IsEmpty)
			{
				return DrawerOutcome.Refused(current, "This drawer has been drained");
			}
			return DrawerOutcome.Withdrew(new DrawerSnapshot(current.ItemName, current.Amount - 1), 1);
		}

		public static DrawerOutcome WithdrawExact(DrawerSnapshot current, int requested)
		{
			if (requested <= 0 || !current.IsAssigned || current.IsEmpty)
			{
				return DrawerOutcome.Withdrew(current, 0);
			}
			int num = ((requested < current.Amount) ? requested : current.Amount);
			return DrawerOutcome.Withdrew(new DrawerSnapshot(current.ItemName, current.Amount - num), num);
		}

		public static DrawerOutcome Clear(DrawerSnapshot current)
		{
			if (!current.IsEmpty)
			{
				return DrawerOutcome.Refused(current, "Empty the drawer before clearing it");
			}
			return DrawerOutcome.Changed(new DrawerSnapshot("", 0));
		}

		public static int RefundShortfall(int removed, int accepted)
		{
			int num = removed - accepted;
			if (num <= 0)
			{
				return 0;
			}
			return num;
		}
	}
	public static class DrawerTextureGenerator
	{
		private readonly struct Knot
		{
			public readonly float X;

			public readonly float Y;

			public readonly float Radius;

			public readonly float Strength;

			public Knot(float x, float y, float radius, float strength)
			{
				X = x;
				Y = y;
				Radius = radius;
				Strength = strength;
			}
		}

		public static TextureData Generate(TextureTier tier, int width, int height, int seed)
		{
			if (width < 2 || height < 2)
			{
				throw new ArgumentException($"Texture must be at least 2x2, got {width}x{height}.");
			}
			float[] heights = BuildHeightField(tier, width, height, seed);
			byte[] albedo = BuildAlbedo(tier, heights, width, height);
			byte[] normal = BuildNormal(heights, width, height, NormalStrength(tier));
			return new TextureData(width, height, albedo, normal);
		}

		private static float[] BuildHeightField(TextureTier tier, int width, int height, int seed)
		{
			return tier switch
			{
				TextureTier.Wood => BuildWoodHeight(width, height, seed), 
				TextureTier.Stone => BuildStoneHeight(width, height, seed), 
				TextureTier.BlackMarble => BuildMarbleHeight(width, height, seed), 
				_ => BuildWoodHeight(width, height, seed), 
			};
		}

		private static float[] BuildWoodHeight(int width, int height, int seed)
		{
			float[] array = new float[width * height];
			Knot[] knots = MakeKnots(seed, width, height, 3, 6f, 14f);
			for (int i = 0; i < height; i++)
			{
				for (int j = 0; j < width; j++)
				{
					float num = PeriodicNoise.Fbm(j, i, width, height, seed + 11, 2, 7, 2f, 0.6f) * 1.3f;
					float num2 = (float)i * 7f / (float)height + num;
					float num3 = num2 - (float)Math.Floor(num2);
					float num4 = Math.Min(num3, 1f - num3);
					float num5 = 1f - Clamp01(num4 / 0.08f);
					float num6 = PeriodicNoise.Fbm(j, i, width, height, seed + 23, 4, 48, 2.1f, 0.55f);
					float num7 = PeriodicNoise.Fbm(j, i, width, height, seed + 37, 3, 5);
					float num8 = KnotContribution(knots, j, i, width, height);
					float v = 0.55f + num7 * 0.08f + num6 * 0.018f - num5 * 0.14f + num8;
					array[i * width + j] = Clamp01(v);
				}
			}
			return array;
		}

		private static float[] BuildStoneHeight(int width, int height, int seed)
		{
			float[] array = new float[width * height];
			for (int i = 0; i < height; i++)
			{
				for (int j = 0; j < width; j++)
				{
					float num = PeriodicNoise.Fbm(j, i, width, height, seed + 5, 3, 6);
					float num2 = PeriodicNoise.Fbm(j, i, width, height, seed + 61, 3, 34);
					float v = 0.5f + num * 0.15f + num2 * 0.045f;
					array[i * width + j] = Clamp01(v);
				}
			}
			return array;
		}

		private static float[] BuildMarbleHeight(int width, int height, int seed)
		{
			float[] array = new float[width * height];
			for (int i = 0; i < height; i++)
			{
				for (int j = 0; j < width; j++)
				{
					float value = PeriodicNoise.Fbm(j, i, width, height, seed + 7, 3, 5, 2.3f);
					float num = (float)Math.Pow(Clamp01((1f - Math.Abs(value) - 0.82f) / 0.18f), 2.0);
					float num2 = PeriodicNoise.Fbm(j, i, width, height, seed + 89, 2, 40);
					float v = 0.16f + num * 0.22f + num2 * 0.02f;
					array[i * width + j] = Clamp01(v);
				}
			}
			return array;
		}

		private static Knot[] MakeKnots(int seed, int width, int height, int count, float minRadius, float maxRadius)
		{
			Random random = new Random(seed + 999);
			Knot[] array = new Knot[count];
			float num = (float)width / 2f;
			float num2 = (float)height / 2f;
			float num3 = (float)width * 0.22f;
			float num4 = (float)height * 0.22f;
			for (int i = 0; i < count; i++)
			{
				float num5 = 0f;
				float num6 = 0f;
				for (int j = 0; j < 20; j++)
				{
					num5 = (float)(random.NextDouble() * (double)width);
					num6 = (float)(random.NextDouble() * (double)height);
					if (!(Math.Abs(num5 - num) < num3) || !(Math.Abs(num6 - num2) < num4))
					{
						break;
					}
				}
				float radius = minRadius + (float)random.NextDouble() * (maxRadius - minRadius);
				float strength = 0.03f + (float)random.NextDouble() * 0.03f;
				array[i] = new Knot(num5, num6, radius, strength);
			}
			return array;
		}

		private static float KnotContribution(Knot[] knots, int x, int y, int width, int height)
		{
			float num = 0f;
			for (int i = 0; i < knots.Length; i++)
			{
				Knot knot = knots[i];
				float num2 = PeriodicNoise.ToroidalDistance(x, y, knot.X, knot.Y, width, height);
				if (!(num2 >= knot.Radius))
				{
					float num3 = num2 / knot.Radius;
					float num4 = (float)Math.Pow(1f - num3, 2.0);
					num -= knot.Strength * num4;
				}
			}
			return num;
		}

		private static (float r, float g, float b) HueRatio(TextureTier tier)
		{
			return tier switch
			{
				TextureTier.Wood => (r: 1f, g: 0.84f, b: 0.66f), 
				TextureTier.Stone => (r: 0.97f, g: 0.98f, b: 1f), 
				TextureTier.BlackMarble => (r: 0.86f, g: 0.95f, b: 1f), 
				_ => (r: 1f, g: 0.8f, b: 0.56f), 
			};
		}

		public static float MidLightness(TextureTier tier)
		{
			var (num, num2) = LightnessRange(tier);
			return (num + num2) / 2f;
		}

		private static (float min, float max) LightnessRange(TextureTier tier)
		{
			return tier switch
			{
				TextureTier.Wood => (min: 0.24f, max: 0.6f), 
				TextureTier.Stone => (min: 0.34f, max: 0.64f), 
				TextureTier.BlackMarble => (min: 0.04f, max: 0.3f), 
				_ => (min: 0.28f, max: 0.66f), 
			};
		}

		private static byte[] BuildAlbedo(TextureTier tier, float[] heights, int width, int height)
		{
			(float r, float g, float b) tuple = HueRatio(tier);
			float item = tuple.r;
			float item2 = tuple.g;
			float item3 = tuple.b;
			(float min, float max) tuple2 = LightnessRange(tier);
			float item4 = tuple2.min;
			float item5 = tuple2.max;
			byte[] array = new byte[width * height * 4];
			for (int i = 0; i < heights.Length; i++)
			{
				float num = item4 + heights[i] * (item5 - item4);
				int num2 = i * 4;
				array[num2] = ToByte(num * item);
				array[num2 + 1] = ToByte(num * item2);
				array[num2 + 2] = ToByte(num * item3);
				array[num2 + 3] = byte.MaxValue;
			}
			return array;
		}

		private static float NormalStrength(TextureTier tier)
		{
			return tier switch
			{
				TextureTier.Wood => 3f, 
				TextureTier.Stone => 1.6f, 
				TextureTier.BlackMarble => 1.1f, 
				_ => 2f, 
			};
		}

		private static byte[] BuildNormal(float[] heights, int width, int height, float strength)
		{
			byte[] array = new byte[width * height * 4];
			for (int i = 0; i < height; i++)
			{
				for (int j = 0; j < width; j++)
				{
					float num = At(heights, j - 1, i - 1, width, height);
					float num2 = At(heights, j, i - 1, width, height);
					float num3 = At(heights, j + 1, i - 1, width, height);
					float num4 = At(heights, j - 1, i, width, height);
					float num5 = At(heights, j + 1, i, width, height);
					float num6 = At(heights, j - 1, i + 1, width, height);
					float num7 = At(heights, j, i + 1, width, height);
					float num8 = At(heights, j + 1, i + 1, width, height);
					float num9 = num3 + 2f * num5 + num8 - (num + 2f * num4 + num6);
					float num10 = num6 + 2f * num7 + num8 - (num + 2f * num2 + num3);
					float num11 = (0f - num9) * strength;
					float num12 = (0f - num10) * strength;
					float num13 = 1f;
					float num14 = (float)Math.Sqrt(num11 * num11 + num12 * num12 + num13 * num13);
					num11 /= num14;
					num12 /= num14;
					num13 /= num14;
					int num15 = (i * width + j) * 4;
					array[num15] = ToByte(num11 * 0.5f + 0.5f);
					array[num15 + 1] = ToByte(num12 * 0.5f + 0.5f);
					array[num15 + 2] = ToByte(num13 * 0.5f + 0.5f);
					array[num15 + 3] = byte.MaxValue;
				}
			}
			return array;
		}

		private static float At(float[] field, int x, int y, int width, int height)
		{
			int num = (x % width + width) % width;
			int num2 = (y % height + height) % height;
			return field[num2 * width + num];
		}

		private static float Clamp01(float v)
		{
			if (!(v < 0f))
			{
				if (!(v > 1f))
				{
					return v;
				}
				return 1f;
			}
			return 0f;
		}

		private static byte ToByte(float v)
		{
			v = Clamp01(v);
			return (byte)Math.Round(v * 255f);
		}
	}
	public sealed class HandledRequestCache<T>
	{
		private readonly struct Entry
		{
			public readonly T Result;

			public readonly float RecordedAt;

			public Entry(T result, float recordedAt)
			{
				Result = result;
				RecordedAt = recordedAt;
			}
		}

		private readonly Dictionary<(long Sender, long Id), Entry> _entries = new Dictionary<(long, long), Entry>();

		private readonly float _lifetimeSeconds;

		public int Count => _entries.Count;

		public HandledRequestCache(float lifetimeSeconds)
		{
			_lifetimeSeconds = lifetimeSeconds;
		}

		public bool TryGet(long sender, long id, out T result)
		{
			if (_entries.TryGetValue((sender, id), out var value))
			{
				result = value.Result;
				return true;
			}
			result = default(T);
			return false;
		}

		public void Record(long sender, long id, T result, float now)
		{
			_entries[(sender, id)] = new Entry(result, now);
		}

		public void Prune(float now)
		{
			if (_entries.Count == 0)
			{
				return;
			}
			List<(long, long)> list = null;
			foreach (KeyValuePair<(long, long), Entry> entry in _entries)
			{
				if (now - entry.Value.RecordedAt > _lifetimeSeconds)
				{
					(list ?? (list = new List<(long, long)>())).Add(entry.Key);
				}
			}
			if (list == null)
			{
				return;
			}
			foreach (var item in list)
			{
				_entries.Remove(item);
			}
		}
	}
	public static class IconAtlasPacker
	{
		private const int Padding = 2;

		public static AtlasLayout Pack(IReadOnlyList<IconSize> icons, int maxDimension = 4096)
		{
			if (icons == null)
			{
				throw new ArgumentNullException("icons");
			}
			if (icons.Count == 0)
			{
				return new AtlasLayout(1, 1, new Dictionary<string, AtlasRect>());
			}
			int num = maxDimension - 2;
			foreach (IconSize icon in icons)
			{
				if (icon.Width <= 0 || icon.Height <= 0)
				{
					throw new ArgumentException($"Icon '{icon.Name}' has invalid dimensions {icon.Width}×{icon.Height}. " + "Icon width and height must be positive.");
				}
				if (icon.Width > num || icon.Height > num)
				{
					throw new ArgumentException($"Icon '{icon.Name}' has dimensions {icon.Width}×{icon.Height}, " + $"but maximum icon size for maxDimension={maxDimension} is {num}×{num} " + $"(atlas dimension - {2} pixel padding).");
				}
			}
			List<IconSize> sorted = icons.OrderByDescending((IconSize i) => i.Height).ToList();
			for (int num2 = 64; num2 <= maxDimension; num2 *= 2)
			{
				if (TryPackInto(sorted, num2, out var rects))
				{
					return new AtlasLayout(num2, num2, rects);
				}
			}
			throw new InvalidOperationException($"Cannot fit {icons.Count} icons into a {maxDimension}×{maxDimension} atlas.");
		}

		private static bool TryPackInto(List<IconSize> sorted, int size, out Dictionary<string, AtlasRect> rects)
		{
			rects = new Dictionary<string, AtlasRect>(sorted.Count);
			int num = 0;
			int num2 = 0;
			int num3 = 0;
			foreach (IconSize item in sorted)
			{
				int num4 = item.Width + 2;
				int num5 = item.Height + 2;
				if (num4 > size || num5 > size)
				{
					return false;
				}
				if (num3 + num4 > size)
				{
					num += num2;
					num2 = 0;
					num3 = 0;
				}
				if (num + num5 > size)
				{
					return false;
				}
				rects[item.Name] = new AtlasRect(num3, num, item.Width, item.Height);
				num3 += num4;
				if (num5 > num2)
				{
					num2 = num5;
				}
			}
			return true;
		}
	}
	public sealed class MeshData
	{
		internal sealed class Builder
		{
			private readonly List<float> _pos = new List<float>(4096);

			private readonly List<float> _nor = new List<float>(4096);

			private readonly List<float> _uv = new List<float>(2731);

			public void Triangle(float[] a, float[] b, float[] c, float uvScale)
			{
				float num = b[0] - a[0];
				float num2 = b[1] - a[1];
				float num3 = b[2] - a[2];
				float num4 = c[0] - a[0];
				float num5 = c[1] - a[1];
				float num6 = c[2] - a[2];
				float num7 = num2 * num6 - num3 * num5;
				float num8 = num3 * num4 - num * num6;
				float num9 = num * num5 - num2 * num4;
				float num10 = (float)Math.Sqrt(num7 * num7 + num8 * num8 + num9 * num9);
				if (!(num10 < 1E-09f))
				{
					num7 /= num10;
					num8 /= num10;
					num9 /= num10;
					float num11 = Math.Abs(num7);
					float num12 = Math.Abs(num8);
					float num13 = Math.Abs(num9);
					int num14;
					int num15;
					if (num11 >= num12 && num11 >= num13)
					{
						num14 = 2;
						num15 = 1;
					}
					else if (num12 >= num13)
					{
						num14 = 0;
						num15 = 2;
					}
					else
					{
						num14 = 0;
						num15 = 1;
					}
					float[][] array = new float[3][] { a, b, c };
					foreach (float[] array2 in array)
					{
						_pos.Add(array2[0]);
						_pos.Add(array2[1]);
						_pos.Add(array2[2]);
						_nor.Add(num7);
						_nor.Add(num8);
						_nor.Add(num9);
						_uv.Add(array2[num14] * uvScale);
						_uv.Add(array2[num15] * uvScale);
					}
				}
			}

			public void Quad(float[] a, float[] b, float[] c, float[] d, float[] hint, float uvScale)
			{
				float num = b[0] - a[0];
				float num2 = b[1] - a[1];
				float num3 = b[2] - a[2];
				float num4 = c[0] - a[0];
				float num5 = c[1] - a[1];
				float num6 = c[2] - a[2];
				float num7 = num2 * num6 - num3 * num5;
				float num8 = num3 * num4 - num * num6;
				float num9 = num * num5 - num2 * num4;
				if (num7 * hint[0] + num8 * hint[1] + num9 * hint[2] < 0f)
				{
					float[] array = b;
					b = d;
					d = array;
				}
				Triangle(a, b, c, uvScale);
				Triangle(a, c, d, uvScale);
			}

			public void TriangleHinted(float[] a, float[] b, float[] c, float[] hint, float uvScale)
			{
				float num = b[0] - a[0];
				float num2 = b[1] - a[1];
				float num3 = b[2] - a[2];
				float num4 = c[0] - a[0];
				float num5 = c[1] - a[1];
				float num6 = c[2] - a[2];
				float num7 = num2 * num6 - num3 * num5;
				float num8 = num3 * num4 - num * num6;
				float num9 = num * num5 - num2 * num4;
				if (num7 * hint[0] + num8 * hint[1] + num9 * hint[2] < 0f)
				{
					float[] array = b;
					b = c;
					c = array;
				}
				Triangle(a, b, c, uvScale);
			}

			public MeshData Build()
			{
				return new MeshData(_pos.ToArray(), _nor.ToArray(), _uv.ToArray());
			}
		}

		public float[] Vertices { get; }

		public float[] Normals { get; }

		public float[] Uvs { get; }

		public int VertexCount => Vertices.Length / 3;

		public int TriangleCount => VertexCount / 3;

		public MeshData(float[] vertices, float[] normals, float[] uvs)
		{
			Vertices = vertices;
			Normals = normals;
			Uvs = uvs;
		}
	}
	public sealed class PendingRequest<T>
	{
		public readonly long Id;

		public readonly T Payload;

		public int Attempts;

		public float Deadline;

		public PendingRequest(long id, T payload, int attempts, float deadline)
		{
			Id = id;
			Payload = payload;
			Attempts = attempts;
			Deadline = deadline;
		}
	}
	public sealed class PendingRequestLedger<T>
	{
		private readonly Dictionary<long, PendingRequest<T>> _pending = new Dictionary<long, PendingRequest<T>>();

		private readonly float _timeoutSeconds;

		private readonly int _maxAttempts;

		public int Count => _pending.Count;

		public PendingRequestLedger(float timeoutSeconds, int maxAttempts)
		{
			_timeoutSeconds = timeoutSeconds;
			_maxAttempts = maxAttempts;
		}

		public void Add(long id, T payload, float now)
		{
			_pending[id] = new PendingRequest<T>(id, payload, 1, now + _timeoutSeconds);
		}

		public bool TryComplete(long id, out T payload)
		{
			if (_pending.TryGetValue(id, out var value))
			{
				payload = value.Payload;
				_pending.Remove(id);
				return true;
			}
			payload = default(T);
			return false;
		}

		public bool Remove(long id, out T payload)
		{
			if (_pending.TryGetValue(id, out var value))
			{
				payload = value.Payload;
				_pending.Remove(id);
				return true;
			}
			payload = default(T);
			return false;
		}

		public IEnumerable<KeyValuePair<long, T>> All()
		{
			foreach (KeyValuePair<long, PendingRequest<T>> item in _pending)
			{
				yield return new KeyValuePair<long, T>(item.Key, item.Value.Payload);
			}
		}

		public void Tick(float now, Action<long, T> retry, Action<long, T> giveUp)
		{
			List<long> list = null;
			foreach (KeyValuePair<long, PendingRequest<T>> item in _pending)
			{
				if (!(now < item.Value.Deadline))
				{
					(list ?? (list = new List<long>())).Add(item.Key);
				}
			}
			if (list == null)
			{
				return;
			}
			foreach (long item2 in list)
			{
				PendingRequest<T> pendingRequest = _pending[item2];
				if (pendingRequest.Attempts >= _maxAttempts)
				{
					_pending.Remove(item2);
					giveUp(item2, pendingRequest.Payload);
				}
				else
				{
					pendingRequest.Attempts++;
					pendingRequest.Deadline = now + _timeoutSeconds;
					retry(item2, pendingRequest.Payload);
				}
			}
		}
	}
	public static class PeriodicNoise
	{
		public static float Sample(float x, float y, int cells, int seed)
		{
			if (cells < 1)
			{
				cells = 1;
			}
			float num = (float)Math.Floor(x);
			float num2 = (float)Math.Floor(y);
			int num3 = Mod((int)num, cells);
			int num4 = Mod((int)num2, cells);
			int x2 = Mod(num3 + 1, cells);
			int y2 = Mod(num4 + 1, cells);
			float t = x - num;
			float t2 = y - num2;
			float t3 = Fade(t);
			float t4 = Fade(t2);
			float a = HashValue(num3, num4, seed);
			float b = HashValue(x2, num4, seed);
			float a2 = HashValue(num3, y2, seed);
			float b2 = HashValue(x2, y2, seed);
			float a3 = Lerp(a, b, t3);
			float b3 = Lerp(a2, b2, t3);
			return Lerp(a3, b3, t4);
		}

		public static float Fbm(float px, float py, int width, int height, int seed, int octaves, int baseCells, float lacunarity = 2f, float gain = 0.5f)
		{
			float num = 0f;
			float num2 = 1f;
			float num3 = 0f;
			int num4 = Math.Max(1, baseCells);
			for (int i = 0; i < octaves; i++)
			{
				float x = px * (float)num4 / (float)width;
				float y = py * (float)num4 / (float)height;
				num += Sample(x, y, num4, seed + i * 101) * num2;
				num3 += num2;
				num2 *= gain;
				num4 = Math.Max(1, (int)Math.Round((float)num4 * lacunarity));
			}
			if (!(num3 > 0f))
			{
				return 0f;
			}
			return num / num3;
		}

		public static float ToroidalDistance(float ax, float ay, float bx, float by, int width, int height)
		{
			float num = WrapDelta(ax - bx, width);
			float num2 = WrapDelta(ay - by, height);
			return (float)Math.Sqrt(num * num + num2 * num2);
		}

		private static float WrapDelta(float d, int period)
		{
			d %= (float)period;
			if (d > (float)period / 2f)
			{
				d -= (float)period;
			}
			if (d < (float)(-period) / 2f)
			{
				d += (float)period;
			}
			return d;
		}

		private static int Mod(int a, int m)
		{
			int num = a % m;
			if (num >= 0)
			{
				return num;
			}
			return num + m;
		}

		private static float Fade(float t)
		{
			return t * t * t * (t * (t * 6f - 15f) + 10f);
		}

		private static float Lerp(float a, float b, float t)
		{
			return a + t * (b - a);
		}

		private static float HashValue(int x, int y, int seed)
		{
			int num = x * 374761393 + y * 668265263 + seed * 1274126177;
			int num2 = (num ^ (num >>> 13)) * 1274126177;
			return (float)(uint)(num2 ^ (num2 >>> 16)) / 4.2949673E+09f * 2f - 1f;
		}
	}
	public sealed class RecipeSpec
	{
		public IReadOnlyList<(string Item, int Amount)> Requirements { get; }

		public string Error { get; }

		public bool Ok => Error == null;

		private RecipeSpec(IReadOnlyList<(string, int)> requirements, string error)
		{
			Requirements = requirements;
			Error = error;
		}

		public static RecipeSpec Parse(string text)
		{
			if (string.IsNullOrWhiteSpace(text))
			{
				return Fail("recipe is empty");
			}
			List<(string, int)> list = new List<(string, int)>();
			HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			string[] array = text.Split(',');
			for (int i = 0; i < array.Length; i++)
			{
				string text2 = array[i].Trim();
				if (text2.Length != 0)
				{
					int num = text2.IndexOf(':');
					if (num <= 0)
					{
						return Fail("'" + text2 + "' is not Item:Count");
					}
					string text3 = text2.Substring(0, num).Trim();
					string s = text2.Substring(num + 1).Trim();
					if (text3.Length == 0)
					{
						return Fail("'" + text2 + "' has no item name");
					}
					if (!int.TryParse(s, out var result))
					{
						return Fail("'" + text2 + "' has a non-numeric count");
					}
					if (result <= 0)
					{
						return Fail("'" + text2 + "' must have a count above zero");
					}
					if (!hashSet.Add(text3))
					{
						return Fail("'" + text3 + "' is listed more than once");
					}
					list.Add((text3, result));
				}
			}
			if (list.Count == 0)
			{
				return Fail("recipe is empty");
			}
			return new RecipeSpec(list, null);
		}

		public static string Format(IReadOnlyList<(string Item, int Amount)> requirements)
		{
			List<string> list = new List<string>(requirements.Count);
			foreach (var requirement in requirements)
			{
				list.Add($"{requirement.Item}:{requirement.Amount}");
			}
			return string.Join(",", list);
		}

		private static RecipeSpec Fail(string error)
		{
			return new RecipeSpec(Array.Empty<(string, int)>(), error);
		}
	}
	public sealed class RequestIdGenerator
	{
		private long _next;

		public RequestIdGenerator(long seed)
		{
			_next = seed;
		}

		public long Next()
		{
			return ++_next;
		}
	}
	public sealed class SpatialGrid<T>
	{
		private readonly struct Cell : IEquatable<Cell>
		{
			public readonly int X;

			public readonly int Y;

			public readonly int Z;

			public Cell(int x, int y, int z)
			{
				X = x;
				Y = y;
				Z = z;
			}

			public bool Equals(Cell o)
			{
				if (X == o.X && Y == o.Y)
				{
					return Z == o.Z;
				}
				return false;
			}

			public override bool Equals(object o)
			{
				if (o is Cell o2)
				{
					return Equals(o2);
				}
				return false;
			}

			public override int GetHashCode()
			{
				return (X * 73856093) ^ (Y * 19349663) ^ (Z * 83492791);
			}
		}

		private readonly float _cellSize;

		private readonly Dictionary<Cell, List<T>> _cells = new Dictionary<Cell, List<T>>();

		private readonly Dictionary<T, (Cell cell, float x, float y, float z)> _index = new Dictionary<T, (Cell, float, float, float)>();

		public int Count => _index.Count;

		public SpatialGrid(float cellSize)
		{
			if (cellSize <= 0f)
			{
				throw new ArgumentOutOfRangeException("cellSize");
			}
			_cellSize = cellSize;
		}

		private Cell CellOf(float x, float y, float z)
		{
			return new Cell((int)Math.Floor(x / _cellSize), (int)Math.Floor(y / _cellSize), (int)Math.Floor(z / _cellSize));
		}

		public void Insert(T item, float x, float y, float z)
		{
			if (_index.ContainsKey(item))
			{
				Move(item, x, y, z);
				return;
			}
			Cell cell = CellOf(x, y, z);
			if (!_cells.TryGetValue(cell, out var value))
			{
				value = new List<T>(4);
				_cells[cell] = value;
			}
			value.Add(item);
			_index[item] = (cell, x, y, z);
		}

		public bool Remove(T item)
		{
			if (!_index.TryGetValue(item, out (Cell, float, float, float) value))
			{
				return false;
			}
			if (_cells.TryGetValue(value.Item1, out var value2))
			{
				value2.Remove(item);
				if (value2.Count == 0)
				{
					_cells.Remove(value.Item1);
				}
			}
			_index.Remove(item);
			return true;
		}

		public void Move(T item, float x, float y, float z)
		{
			Remove(item);
			Insert(item, x, y, z);
		}

		public void Query(float x, float y, float z, float radius, List<T> results)
		{
			if (results == null)
			{
				throw new ArgumentNullException("results");
			}
			if (radius <= 0f)
			{
				return;
			}
			int num = (int)Math.Ceiling(radius / _cellSize);
			Cell cell = CellOf(x, y, z);
			float num2 = radius * radius;
			for (int i = -num; i <= num; i++)
			{
				for (int j = -num; j <= num; j++)
				{
					for (int k = -num; k <= num; k++)
					{
						Cell key = new Cell(cell.X + i, cell.Y + j, cell.Z + k);
						if (!_cells.TryGetValue(key, out var value))
						{
							continue;
						}
						for (int l = 0; l < value.Count; l++)
						{
							T val = value[l];
							(Cell cell, float x, float y, float z) tuple = _index[val];
							float num3 = tuple.x - x;
							float num4 = tuple.y - y;
							float num5 = tuple.z - z;
							if (num3 * num3 + num4 * num4 + num5 * num5 <= num2)
							{
								results.Add(val);
							}
						}
					}
				}
			}
		}
	}
	public readonly struct StackSplitPlan
	{
		public readonly int[] TopUps;

		public readonly int[] NewStacks;

		public readonly int Remainder;

		public int Placed
		{
			get
			{
				int num = 0;
				int[] topUps = TopUps;
				foreach (int num2 in topUps)
				{
					num += num2;
				}
				topUps = NewStacks;
				foreach (int num3 in topUps)
				{
					num += num3;
				}
				return num;
			}
		}

		public StackSplitPlan(int[] topUps, int[] newStacks, int remainder)
		{
			TopUps = topUps;
			NewStacks = newStacks;
			Remainder = remainder;
		}
	}
	public static class StackSplit
	{
		public static StackSplitPlan Plan(IReadOnlyList<int> partialFreeSpace, int emptySlots, int incoming, int maxStackSize, bool firstStackToTarget)
		{
			int num = ((maxStackSize < 1) ? 1 : maxStackSize);
			int num2 = partialFreeSpace?.Count ?? 0;
			int[] array = new int[num2];
			List<int> list = new List<int>();
			int num3 = ((incoming >= 0) ? incoming : 0);
			int num4 = ((emptySlots >= 0) ? emptySlots : 0);
			if (firstStackToTarget && num3 > 0 && num4 > 0)
			{
				int num5 = ((num3 < num) ? num3 : num);
				list.Add(num5);
				num3 -= num5;
				num4--;
			}
			for (int i = 0; i < num2; i++)
			{
				if (num3 <= 0)
				{
					break;
				}
				int num6 = partialFreeSpace[i];
				if (num6 > 0)
				{
					num3 -= (array[i] = ((num3 < num6) ? num3 : num6));
				}
			}
			while (num3 > 0 && num4 > 0)
			{
				int num7 = ((num3 < num) ? num3 : num);
				list.Add(num7);
				num3 -= num7;
				num4--;
			}
			return new StackSplitPlan(array, list.ToArray(), num3);
		}
	}
	public sealed class TextureData
	{
		public int Width { get; }

		public int Height { get; }

		public byte[] Albedo { get; }

		public byte[] Normal { get; }

		public TextureData(int width, int height, byte[] albedo, byte[] normal)
		{
			Width = width;
			Height = height;
			Albedo = albedo;
			Normal = normal;
		}
	}
	public enum TextureTier
	{
		Wood,
		Stone,
		BlackMarble
	}
	public enum ViewFlushAction
	{
		Wait,
		Claim,
		Reconcile
	}
	public static class ViewFlushPolicy
	{
		public const float SettleSeconds = 1f;

		public const float ClaimRetrySeconds = 2f;

		public static bool MayWriteAfterOwnerChange(float ownerRevisionAge, long previousOwner, long thisPeer)
		{
			if (previousOwner == 0L)
			{
				return true;
			}
			if (previousOwner == thisPeer)
			{
				return true;
			}
			return ownerRevisionAge >= 1f;
		}

		public static ViewFlushAction Decide(bool isOwner, float ownerRevisionAge, bool claimAlreadyMade, float jitterSeconds)
		{
			if (isOwner)
			{
				if (!(ownerRevisionAge >= 1f))
				{
					return ViewFlushAction.Wait;
				}
				return ViewFlushAction.Reconcile;
			}
			if (!claimAlreadyMade)
			{
				return ViewFlushAction.Claim;
			}
			if (!(ownerRevisionAge >= 2f + jitterSeconds))
			{
				return ViewFlushAction.Wait;
			}
			return ViewFlushAction.Claim;
		}
	}
	public static class ViewLayout
	{
		public const int DefaultSlotCount = 8;

		public static int[] Compute(bool assigned, int amount, int capacity, int maxStackSize, int slotCount = 8)
		{
			if (!assigned || amount <= 0 || slotCount <= 0)
			{
				return Array.Empty<int>();
			}
			int num = ((maxStackSize < 1) ? 1 : maxStackSize);
			long num2 = (long)capacity - (long)amount;
			long num3 = ((num2 < 0) ? 0 : num2);
			for (int num4 = ((amount < slotCount) ? amount : slotCount); num4 >= 2; num4--)
			{
				int[] array = Distribute(amount, num, num4, num3);
				if (array != null && Room(array, num) <= num3)
				{
					return array;
				}
			}
			return new int[1] { amount };
		}

		public static long Room(IReadOnlyList<int> slots, int maxStackSize)
		{
			int num = ((maxStackSize < 1) ? 1 : maxStackSize);
			long num2 = 0L;
			for (int i = 0; i < slots.Count; i++)
			{
				if (slots[i] < num)
				{
					num2 += num - slots[i];
				}
			}
			return num2;
		}

		private static int[] Distribute(int amount, int max, int n, long allowedRoom)
		{
			int num = n - 1;
			long num2 = (long)num * (long)max - allowedRoom;
			long num3 = ((num2 > num) ? num2 : num);
			if (num3 > amount - 1)
			{
				return null;
			}
			int num4 = (int)(num3 / num);
			int num5 = (int)(num3 % num);
			int[] array = new int[n];
			array[0] = amount - (int)num3;
			for (int i = 1; i < n; i++)
			{
				array[i] = num4 + ((i - 1 < num5) ? 1 : 0);
			}
			return array;
		}
	}
	public readonly struct ViewReconcileOutcome
	{
		public readonly DrawerSnapshot Result;

		public readonly int Credited;

		public readonly int Debited;

		public readonly int SpillDrawerItem;

		public readonly int SpillForeign;

		public readonly int Unbacked;

		public bool AmountChanged
		{
			get
			{
				if (Credited <= 0)
				{
					return Debited > 0;
				}
				return true;
			}
		}

		public ViewReconcileOutcome(DrawerSnapshot result, int credited, int debited, int spillDrawerItem, int spillForeign, int unbacked)
		{
			Result = result;
			Credited = credited;
			Debited = debited;
			SpillDrawerItem = spillDrawerItem;
			SpillForeign = spillForeign;
			Unbacked = unbacked;
		}
	}
	public static class ViewReconciliation
	{
		public static ViewReconcileOutcome Apply(DrawerSnapshot current, int capacity, int baseline, int viewItemTotal, int foreignCount)
		{
			int spillForeign = ((foreignCount >= 0) ? foreignCount : 0);
			long num = (long)((viewItemTotal >= 0) ? viewItemTotal : 0) - (long)((baseline >= 0) ? baseline : 0);
			if (num > 0)
			{
				int num2 = (int)((num > int.MaxValue) ? int.MaxValue : num);
				DrawerOutcome drawerOutcome = DrawerState.Deposit(current, capacity, current.ItemName, num2);
				int num3 = (drawerOutcome.Accepted ? drawerOutcome.MovedToDrawer : 0);
				return new ViewReconcileOutcome(drawerOutcome.Accepted ? drawerOutcome.Result : current, num3, 0, DrawerState.RefundShortfall(num2, num3), spillForeign, 0);
			}
			if (num < 0)
			{
				int num4 = (int)((-num > int.MaxValue) ? int.MaxValue : (-num));
				DrawerOutcome drawerOutcome2 = DrawerState.WithdrawExact(current, num4);
				int movedToPlayer = drawerOutcome2.MovedToPlayer;
				return new ViewReconcileOutcome(drawerOutcome2.Result, 0, movedToPlayer, 0, spillForeign, num4 - movedToPlayer);
			}
			return new ViewReconcileOutcome(current, 0, 0, 0, spillForeign, 0);
		}
	}
}

plugins/ItemDrawers.dll

Decompiled 4 hours 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.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using ItemDrawers.Core;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("assembly_valheim")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("ItemDrawers")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+2fb60d9584e8b8b8ea0c17c4691cc1491e88cacf")]
[assembly: AssemblyProduct("ItemDrawers")]
[assembly: AssemblyTitle("ItemDrawers")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.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.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace ItemDrawers.Game
{
	internal static class ContainerBridge
	{
		[HarmonyPatch(typeof(Container), "Awake")]
		private static class AwakePatch
		{
			private static bool Prepare()
			{
				return ValheimCompat.RequireMethod(typeof(Container), "Awake");
			}

			private static bool Prefix(Container __instance)
			{
				return !(__instance is DrawerComponent);
			}
		}

		[HarmonyPatch(typeof(Container), "GetInventory")]
		private static class GetInventoryPatch
		{
			private static bool Prepare()
			{
				return ValheimCompat.RequireMethod(typeof(Container), "GetInventory");
			}

			private static bool Prefix(Container __instance, ref Inventory __result)
			{
				if (!(__instance is DrawerComponent drawerComponent))
				{
					return true;
				}
				__result = drawerComponent.GetViewInventory();
				return false;
			}
		}

		[HarmonyPatch(typeof(Container), "Save")]
		private static class SavePatch
		{
			private static bool Prepare()
			{
				return ValheimCompat.RequireMethod(typeof(Container), "Save");
			}

			private static bool Prefix(Container __instance)
			{
				if (!(__instance is DrawerComponent drawerComponent))
				{
					return true;
				}
				drawerComponent.SaveView();
				return false;
			}
		}

		[HarmonyPatch(typeof(Container), "Load")]
		private static class LoadPatch
		{
			private static bool Prepare()
			{
				return ValheimCompat.RequireMethod(typeof(Container), "Load");
			}

			private static bool Prefix(Container __instance, ref bool __result)
			{
				if (!(__instance is DrawerComponent drawerComponent))
				{
					return true;
				}
				__result = drawerComponent.LoadView();
				return false;
			}
		}

		private static bool? _viewPatchesApplied;

		internal static bool ViewPatchesApplied
		{
			get
			{
				if (!_viewPatchesApplied.HasValue)
				{
					_viewPatchesApplied = HasOwnPrefix("GetInventory") && HasOwnPrefix("Save") && HasOwnPrefix("Load");
				}
				return _viewPatchesApplied.Value;
			}
		}

		private static bool HasOwnPrefix(string methodName)
		{
			MethodInfo methodInfo = AccessTools.Method(typeof(Container), methodName, (Type[])null, (Type[])null);
			Patches val = ((methodInfo == null) ? null : Harmony.GetPatchInfo((MethodBase)methodInfo));
			if (val == null)
			{
				return false;
			}
			foreach (Patch prefix in val.Prefixes)
			{
				if (prefix.owner == "com.rossdwest.itemdrawers")
				{
					return true;
				}
			}
			return false;
		}
	}
	internal static class DebugCommands
	{
		[Serializable]
		[CompilerGenerated]
		private sealed class <>c
		{
			public static readonly <>c <>9 = new <>c();

			public static ConsoleEvent <>9__2_0;

			public static ConsoleEvent <>9__2_1;

			public static ConsoleEvent <>9__2_2;

			public static ConsoleEvent <>9__2_3;

			public static ConsoleEvent <>9__2_4;

			internal void <Register>b__2_0(ConsoleEventArgs args)
			{
				//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
				//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
				//IL_00c5: 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_00cf: Unknown result type (might be due to invalid IL or missing references)
				//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
				//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
				//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
				//IL_00e4: Expected O, but got Unknown
				//IL_0112: 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_0127: Unknown result type (might be due to invalid IL or missing references)
				//IL_012c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0131: Unknown result type (might be due to invalid IL or missing references)
				//IL_014a: Unknown result type (might be due to invalid IL or missing references)
				//IL_014f: 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_0157: 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_0160: Unknown result type (might be due to invalid IL or missing references)
				//IL_0165: 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)
				if ((Object)(object)Player.m_localPlayer == (Object)null)
				{
					return;
				}
				int num = ((args.Length > 1) ? int.Parse(args[1]) : 10);
				int num2 = ((args.Length > 2) ? int.Parse(args[2]) : 10);
				DrawerTier drawerTier = DrawerTier.Wood;
				if (args.Length > 3)
				{
					if (args[3].Equals("stone", StringComparison.OrdinalIgnoreCase))
					{
						drawerTier = DrawerTier.Stone;
					}
					else if (args[3].StartsWith("black", StringComparison.OrdinalIgnoreCase))
					{
						drawerTier = DrawerTier.BlackMarble;
					}
				}
				GameObject prefab = ZNetScene.instance.GetPrefab(DrawerTiers.PrefabName(drawerTier));
				if ((Object)(object)prefab == (Object)null)
				{
					args.Context.AddString("Drawer prefab not found");
					return;
				}
				Transform transform = ((Component)Player.m_localPlayer).transform;
				Vector3 val = transform.position + transform.forward * 6f;
				Vector3 right = transform.right;
				int num3 = 0;
				DrawerProportions val2 = new DrawerProportions();
				float num4 = val2.Width + 0.02f;
				float num5 = val2.Height + 0.02f;
				for (int i = 0; i < num2; i++)
				{
					for (int j = 0; j < num; j++)
					{
						Vector3 val3 = val + right * (((float)j - (float)(num - 1) / 2f) * num4) + Vector3.up * ((float)i * num5 + val2.Height / 2f);
						DrawerComponent component = Object.Instantiate<GameObject>(prefab, val3, Quaternion.LookRotation(-transform.forward, Vector3.up)).GetComponent<DrawerComponent>();
						if ((Object)(object)component != (Object)null)
						{
							string itemName = SampleItems[(j + i * num) % SampleItems.Length];
							component.TryDepositExternally(itemName, 250 + (j * 7 + i * 13) % 500, out var _);
						}
						num3++;
					}
				}
				args.Context.AddString($"Built {num3} drawers ({num}x{num2}, {drawerTier})");
			}

			internal void <Register>b__2_1(ConsoleEventArgs args)
			{
				//IL_0023: Unknown result type (might be due to invalid IL or missing references)
				//IL_0028: Unknown result type (might be due to invalid IL or missing references)
				//IL_0038: Unknown result type (might be due to invalid IL or missing references)
				int count = DrawerComponent.All.Count;
				int num = 0;
				int num2 = 0;
				foreach (DrawerComponent item in DrawerComponent.All)
				{
					DrawerSnapshot snapshot = item.Snapshot;
					if (((DrawerSnapshot)(ref snapshot)).IsAssigned)
					{
						num++;
					}
					num2 += snapshot.Amount;
				}
				args.Context.AddString($"drawers loaded : {count}");
				args.Context.AddString($"assigned       : {num}");
				args.Context.AddString($"items held     : {num2}");
				args.Context.AddString($"atlas built    : {DrawerIconAtlas.IsBuilt}");
			}

			internal void <Register>b__2_2(ConsoleEventArgs args)
			{
				//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
				//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
				//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
				//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
				//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
				//IL_0134: Unknown result type (might be due to invalid IL or missing references)
				//IL_01dd: 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_0236: Unknown result type (might be due to invalid IL or missing references)
				Player localPlayer = Player.m_localPlayer;
				if ((Object)(object)localPlayer == (Object)null)
				{
					args.Context.AddString("no local player");
					return;
				}
				float num = 20f;
				if (args.Length > 1 && float.TryParse(args[1], out var result))
				{
					num = result;
				}
				long sessionID = ZDOMan.GetSessionID();
				args.Context.AddString($"my session id : {sessionID}");
				args.Context.AddString("name  zdoAmount  owner  isOwner  viewCount");
				int num2 = 0;
				int num3 = 0;
				foreach (DrawerComponent item in DrawerComponent.All)
				{
					if ((Object)(object)item == (Object)null)
					{
						continue;
					}
					Vector3 val = ((Component)item).transform.position - ((Component)localPlayer).transform.position;
					if (((Vector3)(ref val)).sqrMagnitude > num * num)
					{
						continue;
					}
					DrawerSnapshot snapshot = item.Snapshot;
					ZNetView component = ((Component)item).GetComponent<ZNetView>();
					ZDO val2 = (((Object)(object)component != (Object)null) ? component.GetZDO() : null);
					long num4 = ((val2 != null) ? val2.GetOwner() : (-1));
					bool flag = (Object)(object)component != (Object)null && component.IsValid() && component.IsOwner();
					bool flag2 = item.View != null && item.View.CannotResolve(snapshot);
					Inventory inventory = ((Container)item).GetInventory();
					int num5 = 0;
					if (inventory != null)
					{
						foreach (ItemData allItem in inventory.GetAllItems())
						{
							num5 += allItem.m_stack;
						}
					}
					string text = ((num4 == 0L) ? "0 (nobody)" : ((num4 == sessionID) ? $"{num4} (me)" : num4.ToString()));
					args.Context.AddString(string.Format("{0}  {1}  {2}  {3}  ", ((DrawerSnapshot)(ref snapshot)).IsAssigned ? snapshot.ItemName : "<empty>", snapshot.Amount, text, flag) + (flag2 ? $"{num5} (item unresolved on this client)" : num5.ToString()));
					if (!flag2 && num5 != snapshot.Amount)
					{
						num3++;
					}
					num2++;
				}
				args.Context.AddString($"-- {num2} drawer(s) within {num}m, {num3} with a view change not yet reconciled");
			}

			internal void <Register>b__2_3(ConsoleEventArgs args)
			{
				//IL_007d: 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_0240: Unknown result type (might be due to invalid IL or missing references)
				//IL_024c: Unknown result type (might be due to invalid IL or missing references)
				//IL_0329: Unknown result type (might be due to invalid IL or missing references)
				//IL_056b: Unknown result type (might be due to invalid IL or missing references)
				<>c__DisplayClass2_0 <>c__DisplayClass2_ = default(<>c__DisplayClass2_0);
				<>c__DisplayClass2_.args = args;
				Player localPlayer = Player.m_localPlayer;
				if ((Object)(object)localPlayer == (Object)null)
				{
					<>c__DisplayClass2_.args.Context.AddString("no local player");
					return;
				}
				float num = 40f;
				if (<>c__DisplayClass2_.args.Length > 1 && float.TryParse(<>c__DisplayClass2_.args[1], out var result))
				{
					num = result;
				}
				Smelter val = null;
				float num2 = float.MaxValue;
				Smelter[] array = Object.FindObjectsByType<Smelter>((FindObjectsSortMode)0);
				foreach (Smelter val2 in array)
				{
					float num3 = Vector3.Distance(((Component)val2).transform.position, ((Component)localPlayer).transform.position);
					if (num3 < num2)
					{
						num2 = num3;
						val = val2;
					}
				}
				if ((Object)(object)val == (Object)null)
				{
					<Register>g__Out|2_5("no smelter/kiln loaded", ref <>c__DisplayClass2_);
					return;
				}
				ZNetView component = ((Component)val).GetComponent<ZNetView>();
				<Register>g__Out|2_5($"smelter {((Object)val).name} at {num2:F1}m  isOwner={(Object)(object)component != (Object)null && component.IsValid() && component.IsOwner()}  " + $"maxOre={val.m_maxOre} queue={val.GetQueueSize()}  Game.m_worldLevel={Game.m_worldLevel}", ref <>c__DisplayClass2_);
				foreach (ItemConversion item in val.m_conversion)
				{
					<Register>g__Out|2_5("  conversion from " + ((Object)item.m_from).name + " (" + item.m_from.m_itemData.m_shared.m_name + ")", ref <>c__DisplayClass2_);
				}
				IList list = null;
				list = (Type.GetType("OttoFuel.OttoFuelPlugin, OttoFuel")?.GetField("ContainerList", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic))?.GetValue(null) as IList;
				<Register>g__Out|2_5((list == null) ? "OttoFuel ContainerList: not found" : $"OttoFuel ContainerList: {list.Count} entries", ref <>c__DisplayClass2_);
				Container[] array2 = Object.FindObjectsByType<Container>((FindObjectsSortMode)0);
				foreach (Container val3 in array2)
				{
					float num4 = Vector3.Distance(((Component)val).transform.position, ((Component)val3).transform.position);
					if (num4 >= num)
					{
						continue;
					}
					Inventory inventory = val3.GetInventory();
					ZNetView component2 = ((Component)val3).GetComponent<ZNetView>();
					<Register>g__Out|2_5($"-- {((Object)val3).name} at {num4:F1}m from smelter", ref <>c__DisplayClass2_);
					<Register>g__Out|2_5("   inOttoList=" + ((list == null) ? "?" : list.Contains(val3).ToString()) + "  " + $"pieceInParent={(Object)(object)((Component)val3).GetComponentInParent<Piece>() != (Object)null}  inventoryNull={inventory == null}  " + $"checkAccess={val3.CheckAccess(localPlayer.GetPlayerID())}  inUse={val3.IsInUse()}  " + $"wardAccess={PrivateArea.CheckAccess(((Component)val3).transform.position, 0f, false, false)}  " + $"zdoValid={(Object)(object)component2 != (Object)null && component2.IsValid()}  isOwner={(Object)(object)component2 != (Object)null && component2.IsValid() && component2.IsOwner()}", ref <>c__DisplayClass2_);
					if (inventory == null)
					{
						continue;
					}
					<Register>g__Out|2_5($"   grid {inventory.GetWidth()}x{inventory.GetHeight()}  items={inventory.GetAllItems().Count}", ref <>c__DisplayClass2_);
					val3.Load();
					foreach (ItemConversion item2 in val.m_conversion)
					{
						List<ItemData> list2 = new List<ItemData>();
						inventory.GetAllItems(item2.m_from.m_itemData.m_shared.m_name, list2);
						if (list2.Count == 0)
						{
							continue;
						}
						foreach (ItemData item3 in list2)
						{
							<Register>g__Out|2_5($"   GetAllItems({item2.m_from.m_itemData.m_shared.m_name}): stack={item3.m_stack} worldLevel={item3.m_worldLevel} " + string.Format("dropPrefab={0} quality={1}", ((Object)(object)item3.m_dropPrefab != (Object)null) ? ((Object)item3.m_dropPrefab).name : "NULL", item3.m_quality), ref <>c__DisplayClass2_);
						}
					}
					foreach (ItemData allItem in inventory.GetAllItems())
					{
						<Register>g__Out|2_5($"   item {allItem.m_shared.m_name} stack={allItem.m_stack} worldLevel={allItem.m_worldLevel} " + string.Format("dropPrefab={0} pos={1}", ((Object)(object)allItem.m_dropPrefab != (Object)null) ? ((Object)allItem.m_dropPrefab).name : "NULL", allItem.m_gridPos), ref <>c__DisplayClass2_);
					}
				}
			}

			internal void <Register>b__2_4(ConsoleEventArgs args)
			{
				<>c__DisplayClass2_1 <>c__DisplayClass2_ = default(<>c__DisplayClass2_1);
				<>c__DisplayClass2_.args = args;
				if (<>c__DisplayClass2_.args.Length > 1 && <>c__DisplayClass2_.args[1] == "reset")
				{
					DrawerDiagnostics.Reset();
					<>c__DisplayClass2_.args.Context.AddString("rid_diag: counters reset");
					return;
				}
				<Register>g__Line|2_6($"refused (ownership unsettled) : {DrawerDiagnostics.RefusedUnsettled}", ref <>c__DisplayClass2_);
				<Register>g__Line|2_6($"ownership changes seen       : {DrawerDiagnostics.OwnershipChanges}", ref <>c__DisplayClass2_);
				<Register>g__Line|2_6($"claims made by this client   : {DrawerDiagnostics.ClaimsMade}", ref <>c__DisplayClass2_);
				<Register>g__Line|2_6($"unowned drawers claimed     : {DrawerDiagnostics.UnownedClaims}", ref <>c__DisplayClass2_);
				<Register>g__Line|2_6($"withdrawals deferred        : {DrawerDiagnostics.DeferredWithdraws}", ref <>c__DisplayClass2_);
				<Register>g__Line|2_6($"claims held for the player  : {DrawerDiagnostics.ClaimsHeldForPlayer}", ref <>c__DisplayClass2_);
				<Register>g__Line|2_6($"withdraw requests sent       : {DrawerDiagnostics.RequestsSent}", ref <>c__DisplayClass2_);
				<Register>g__Line|2_6($"  granted                    : {DrawerDiagnostics.GrantsReceived}", ref <>c__DisplayClass2_);
				<Register>g__Line|2_6($"  gave up                    : {DrawerDiagnostics.GrantsGivenUp}", ref <>c__DisplayClass2_);
				<Register>g__Line|2_6($"  still outstanding          : {DrawerDiagnostics.OutstandingRequests}", ref <>c__DisplayClass2_);
				<Register>g__Line|2_6($"grant latency mean/max ms    : {DrawerDiagnostics.MeanGrantLatencyMs:F0} / {DrawerDiagnostics.MaxGrantLatencyMs:F0}", ref <>c__DisplayClass2_);
			}
		}

		[StructLayout(LayoutKind.Auto)]
		[CompilerGenerated]
		private struct <>c__DisplayClass2_0
		{
			public ConsoleEventArgs args;
		}

		[StructLayout(LayoutKind.Auto)]
		[CompilerGenerated]
		private struct <>c__DisplayClass2_1
		{
			public ConsoleEventArgs args;
		}

		private static readonly string[] SampleItems = new string[6] { "Wood", "Stone", "Coal", "Iron", "Resin", "Flint" };

		private const float WallGap = 0.02f;

		public static void Register()
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Expected O, but got Unknown
			//IL_006c: 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)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Expected O, but got Unknown
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Expected O, but got Unknown
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Expected O, but got Unknown
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_0107: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Expected O, but got Unknown
			object obj = <>c.<>9__2_0;
			if (obj == null)
			{
				ConsoleEvent val = delegate(ConsoleEventArgs args)
				{
					//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
					//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
					//IL_00c5: 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_00cf: Unknown result type (might be due to invalid IL or missing references)
					//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
					//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
					//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
					//IL_00e4: Expected O, but got Unknown
					//IL_0112: 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_0127: Unknown result type (might be due to invalid IL or missing references)
					//IL_012c: Unknown result type (might be due to invalid IL or missing references)
					//IL_0131: Unknown result type (might be due to invalid IL or missing references)
					//IL_014a: Unknown result type (might be due to invalid IL or missing references)
					//IL_014f: 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_0157: 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_0160: Unknown result type (might be due to invalid IL or missing references)
					//IL_0165: 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)
					if (!((Object)(object)Player.m_localPlayer == (Object)null))
					{
						int num = ((args.Length > 1) ? int.Parse(args[1]) : 10);
						int num2 = ((args.Length > 2) ? int.Parse(args[2]) : 10);
						DrawerTier drawerTier = DrawerTier.Wood;
						if (args.Length > 3)
						{
							if (args[3].Equals("stone", StringComparison.OrdinalIgnoreCase))
							{
								drawerTier = DrawerTier.Stone;
							}
							else if (args[3].StartsWith("black", StringComparison.OrdinalIgnoreCase))
							{
								drawerTier = DrawerTier.BlackMarble;
							}
						}
						GameObject prefab = ZNetScene.instance.GetPrefab(DrawerTiers.PrefabName(drawerTier));
						if ((Object)(object)prefab == (Object)null)
						{
							args.Context.AddString("Drawer prefab not found");
						}
						else
						{
							Transform transform = ((Component)Player.m_localPlayer).transform;
							Vector3 val6 = transform.position + transform.forward * 6f;
							Vector3 right = transform.right;
							int num3 = 0;
							DrawerProportions val7 = new DrawerProportions();
							float num4 = val7.Width + 0.02f;
							float num5 = val7.Height + 0.02f;
							for (int i = 0; i < num2; i++)
							{
								for (int j = 0; j < num; j++)
								{
									Vector3 val8 = val6 + right * (((float)j - (float)(num - 1) / 2f) * num4) + Vector3.up * ((float)i * num5 + val7.Height / 2f);
									DrawerComponent component = Object.Instantiate<GameObject>(prefab, val8, Quaternion.LookRotation(-transform.forward, Vector3.up)).GetComponent<DrawerComponent>();
									if ((Object)(object)component != (Object)null)
									{
										string itemName = SampleItems[(j + i * num) % SampleItems.Length];
										component.TryDepositExternally(itemName, 250 + (j * 7 + i * 13) % 500, out var _);
									}
									num3++;
								}
							}
							args.Context.AddString($"Built {num3} drawers ({num}x{num2}, {drawerTier})");
						}
					}
				};
				<>c.<>9__2_0 = val;
				obj = (object)val;
			}
			new ConsoleCommand("rid_wall", "rid_wall <cols> <rows> [wood|stone|blackmarble] - build a test wall of drawers", (ConsoleEvent)obj, true, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
			object obj2 = <>c.<>9__2_1;
			if (obj2 == null)
			{
				ConsoleEvent val2 = delegate(ConsoleEventArgs args)
				{
					//IL_0023: Unknown result type (might be due to invalid IL or missing references)
					//IL_0028: Unknown result type (might be due to invalid IL or missing references)
					//IL_0038: Unknown result type (might be due to invalid IL or missing references)
					int count = DrawerComponent.All.Count;
					int num = 0;
					int num2 = 0;
					foreach (DrawerComponent item in DrawerComponent.All)
					{
						DrawerSnapshot snapshot = item.Snapshot;
						if (((DrawerSnapshot)(ref snapshot)).IsAssigned)
						{
							num++;
						}
						num2 += snapshot.Amount;
					}
					args.Context.AddString($"drawers loaded : {count}");
					args.Context.AddString($"assigned       : {num}");
					args.Context.AddString($"items held     : {num2}");
					args.Context.AddString($"atlas built    : {DrawerIconAtlas.IsBuilt}");
				};
				<>c.<>9__2_1 = val2;
				obj2 = (object)val2;
			}
			new ConsoleCommand("rid_stats", "rid_stats - report drawer counts and rendering cost", (ConsoleEvent)obj2, true, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
			object obj3 = <>c.<>9__2_2;
			if (obj3 == null)
			{
				ConsoleEvent val3 = delegate(ConsoleEventArgs args)
				{
					//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
					//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
					//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
					//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
					//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
					//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
					//IL_0134: Unknown result type (might be due to invalid IL or missing references)
					//IL_01dd: 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_0236: Unknown result type (might be due to invalid IL or missing references)
					Player localPlayer = Player.m_localPlayer;
					if ((Object)(object)localPlayer == (Object)null)
					{
						args.Context.AddString("no local player");
					}
					else
					{
						float num = 20f;
						if (args.Length > 1 && float.TryParse(args[1], out var result))
						{
							num = result;
						}
						long sessionID = ZDOMan.GetSessionID();
						args.Context.AddString($"my session id : {sessionID}");
						args.Context.AddString("name  zdoAmount  owner  isOwner  viewCount");
						int num2 = 0;
						int num3 = 0;
						foreach (DrawerComponent item2 in DrawerComponent.All)
						{
							if (!((Object)(object)item2 == (Object)null))
							{
								Vector3 val6 = ((Component)item2).transform.position - ((Component)localPlayer).transform.position;
								if (!(((Vector3)(ref val6)).sqrMagnitude > num * num))
								{
									DrawerSnapshot snapshot = item2.Snapshot;
									ZNetView component = ((Component)item2).GetComponent<ZNetView>();
									ZDO val7 = (((Object)(object)component != (Object)null) ? component.GetZDO() : null);
									long num4 = ((val7 != null) ? val7.GetOwner() : (-1));
									bool flag = (Object)(object)component != (Object)null && component.IsValid() && component.IsOwner();
									bool flag2 = item2.View != null && item2.View.CannotResolve(snapshot);
									Inventory inventory = ((Container)item2).GetInventory();
									int num5 = 0;
									if (inventory != null)
									{
										foreach (ItemData allItem in inventory.GetAllItems())
										{
											num5 += allItem.m_stack;
										}
									}
									string text = ((num4 == 0L) ? "0 (nobody)" : ((num4 == sessionID) ? $"{num4} (me)" : num4.ToString()));
									args.Context.AddString(string.Format("{0}  {1}  {2}  {3}  ", ((DrawerSnapshot)(ref snapshot)).IsAssigned ? snapshot.ItemName : "<empty>", snapshot.Amount, text, flag) + (flag2 ? $"{num5} (item unresolved on this client)" : num5.ToString()));
									if (!flag2 && num5 != snapshot.Amount)
									{
										num3++;
									}
									num2++;
								}
							}
						}
						args.Context.AddString($"-- {num2} drawer(s) within {num}m, {num3} with a view change not yet reconciled");
					}
				};
				<>c.<>9__2_2 = val3;
				obj3 = (object)val3;
			}
			new ConsoleCommand("rid_owners", "rid_owners [radius] - who owns nearby drawers, and what automation sees", (ConsoleEvent)obj3, true, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
			object obj4 = <>c.<>9__2_3;
			if (obj4 == null)
			{
				ConsoleEvent val4 = delegate(ConsoleEventArgs args)
				{
					//IL_007d: 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_0240: Unknown result type (might be due to invalid IL or missing references)
					//IL_024c: Unknown result type (might be due to invalid IL or missing references)
					//IL_0329: Unknown result type (might be due to invalid IL or missing references)
					//IL_056b: Unknown result type (might be due to invalid IL or missing references)
					Player localPlayer = Player.m_localPlayer;
					if ((Object)(object)localPlayer == (Object)null)
					{
						args.Context.AddString("no local player");
					}
					else
					{
						float num = 40f;
						if (args.Length > 1 && float.TryParse(args[1], out var result))
						{
							num = result;
						}
						Smelter val6 = null;
						float num2 = float.MaxValue;
						Smelter[] array = Object.FindObjectsByType<Smelter>((FindObjectsSortMode)0);
						foreach (Smelter val7 in array)
						{
							float num3 = Vector3.Distance(((Component)val7).transform.position, ((Component)localPlayer).transform.position);
							if (num3 < num2)
							{
								num2 = num3;
								val6 = val7;
							}
						}
						if ((Object)(object)val6 == (Object)null)
						{
							Out("no smelter/kiln loaded");
						}
						else
						{
							ZNetView component = ((Component)val6).GetComponent<ZNetView>();
							Out($"smelter {((Object)val6).name} at {num2:F1}m  isOwner={(Object)(object)component != (Object)null && component.IsValid() && component.IsOwner()}  " + $"maxOre={val6.m_maxOre} queue={val6.GetQueueSize()}  Game.m_worldLevel={Game.m_worldLevel}");
							foreach (ItemConversion item3 in val6.m_conversion)
							{
								Out("  conversion from " + ((Object)item3.m_from).name + " (" + item3.m_from.m_itemData.m_shared.m_name + ")");
							}
							IList list = null;
							list = (Type.GetType("OttoFuel.OttoFuelPlugin, OttoFuel")?.GetField("ContainerList", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic))?.GetValue(null) as IList;
							Out((list == null) ? "OttoFuel ContainerList: not found" : $"OttoFuel ContainerList: {list.Count} entries");
							Container[] array2 = Object.FindObjectsByType<Container>((FindObjectsSortMode)0);
							foreach (Container val8 in array2)
							{
								float num4 = Vector3.Distance(((Component)val6).transform.position, ((Component)val8).transform.position);
								if (!(num4 >= num))
								{
									Inventory inventory = val8.GetInventory();
									ZNetView component2 = ((Component)val8).GetComponent<ZNetView>();
									Out($"-- {((Object)val8).name} at {num4:F1}m from smelter");
									Out("   inOttoList=" + ((list == null) ? "?" : list.Contains(val8).ToString()) + "  " + $"pieceInParent={(Object)(object)((Component)val8).GetComponentInParent<Piece>() != (Object)null}  inventoryNull={inventory == null}  " + $"checkAccess={val8.CheckAccess(localPlayer.GetPlayerID())}  inUse={val8.IsInUse()}  " + $"wardAccess={PrivateArea.CheckAccess(((Component)val8).transform.position, 0f, false, false)}  " + $"zdoValid={(Object)(object)component2 != (Object)null && component2.IsValid()}  isOwner={(Object)(object)component2 != (Object)null && component2.IsValid() && component2.IsOwner()}");
									if (inventory != null)
									{
										Out($"   grid {inventory.GetWidth()}x{inventory.GetHeight()}  items={inventory.GetAllItems().Count}");
										val8.Load();
										foreach (ItemConversion item4 in val6.m_conversion)
										{
											List<ItemData> list2 = new List<ItemData>();
											inventory.GetAllItems(item4.m_from.m_itemData.m_shared.m_name, list2);
											if (list2.Count != 0)
											{
												foreach (ItemData item5 in list2)
												{
													Out($"   GetAllItems({item4.m_from.m_itemData.m_shared.m_name}): stack={item5.m_stack} worldLevel={item5.m_worldLevel} " + string.Format("dropPrefab={0} quality={1}", ((Object)(object)item5.m_dropPrefab != (Object)null) ? ((Object)item5.m_dropPrefab).name : "NULL", item5.m_quality));
												}
											}
										}
										foreach (ItemData allItem2 in inventory.GetAllItems())
										{
											Out($"   item {allItem2.m_shared.m_name} stack={allItem2.m_stack} worldLevel={allItem2.m_worldLevel} " + string.Format("dropPrefab={0} pos={1}", ((Object)(object)allItem2.m_dropPrefab != (Object)null) ? ((Object)allItem2.m_dropPrefab).name : "NULL", allItem2.m_gridPos));
										}
									}
								}
							}
						}
					}
					void Out(string line)
					{
						args.Context.AddString(line);
						Debug.Log((object)("[rid_probe] " + line));
					}
				};
				<>c.<>9__2_3 = val4;
				obj4 = (object)val4;
			}
			new ConsoleCommand("rid_probe", "rid_probe [range] - replay OttoFuel's container checks for the nearest smelter/kiln", (ConsoleEvent)obj4, true, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
			object obj5 = <>c.<>9__2_4;
			if (obj5 == null)
			{
				ConsoleEvent val5 = delegate(ConsoleEventArgs args)
				{
					if (args.Length > 1 && args[1] == "reset")
					{
						DrawerDiagnostics.Reset();
						args.Context.AddString("rid_diag: counters reset");
					}
					else
					{
						Line($"refused (ownership unsettled) : {DrawerDiagnostics.RefusedUnsettled}");
						Line($"ownership changes seen       : {DrawerDiagnostics.OwnershipChanges}");
						Line($"claims made by this client   : {DrawerDiagnostics.ClaimsMade}");
						Line($"unowned drawers claimed     : {DrawerDiagnostics.UnownedClaims}");
						Line($"withdrawals deferred        : {DrawerDiagnostics.DeferredWithdraws}");
						Line($"claims held for the player  : {DrawerDiagnostics.ClaimsHeldForPlayer}");
						Line($"withdraw requests sent       : {DrawerDiagnostics.RequestsSent}");
						Line($"  granted                    : {DrawerDiagnostics.GrantsReceived}");
						Line($"  gave up                    : {DrawerDiagnostics.GrantsGivenUp}");
						Line($"  still outstanding          : {DrawerDiagnostics.OutstandingRequests}");
						Line($"grant latency mean/max ms    : {DrawerDiagnostics.MeanGrantLatencyMs:F0} / {DrawerDiagnostics.MaxGrantLatencyMs:F0}");
					}
					void Line(string text)
					{
						args.Context.AddString(text);
						DrawerPlugin.Log.LogInfo((object)("rid_diag: " + text));
					}
				};
				<>c.<>9__2_4 = val5;
				obj5 = (object)val5;
			}
			new ConsoleCommand("rid_diag", "rid_diag [reset] - withdraw contention counters since the last reset", (ConsoleEvent)obj5, false, false, false, false, false, false, (ConsoleOptionsFetcher)null, false, false, false);
		}

		[CompilerGenerated]
		internal static void <Register>g__Out|2_5(string line, ref <>c__DisplayClass2_0 P_1)
		{
			P_1.args.Context.AddString(line);
			Debug.Log((object)("[rid_probe] " + line));
		}

		[CompilerGenerated]
		internal static void <Register>g__Line|2_6(string text, ref <>c__DisplayClass2_1 P_1)
		{
			P_1.args.Context.AddString(text);
			DrawerPlugin.Log.LogInfo((object)("rid_diag: " + text));
		}
	}
	public class DrawerComponent : Container, Interactable, Hoverable
	{
		internal enum DepositRoute
		{
			Owned,
			Claiming,
			Foreign,
			Unavailable
		}

		public static readonly List<DrawerComponent> All = new List<DrawerComponent>();

		private const string KeyPrefab = "Prefab";

		private const string KeyAmount = "Amount";

		private static readonly int KeyPrefabHash = StringExtensionMethods.GetStableHashCode("Prefab");

		private static readonly int KeyAmountHash = StringExtensionMethods.GetStableHashCode("Amount");

		private static readonly int CreatorHash = StringExtensionMethods.GetStableHashCode("creator");

		internal const string CommitFailedMessage = "Try again";

		internal const string RpcReqWithdraw = "RID_ReqWithdraw";

		internal const string RpcGrantWithdraw = "RID_GrantWithdraw";

		internal const string RpcReqDeposit = "RID_ReqDeposit";

		internal const string RpcGrantDeposit = "RID_GrantDeposit";

		private const string RpcReqClear = "RID_ReqClear";

		internal const float RequestTimeoutSeconds = 5f;

		internal const int MaxRequestAttempts = 3;

		private const float HandledRequestLifetimeSeconds = 60f;

		private readonly HandledRequestCache<(string ItemName, int Amount)> _handledWithdrawals = new HandledRequestCache<(string, int)>(60f);

		private readonly HandledRequestCache<int> _handledDeposits = new HandledRequestCache<int>(60f);

		private ZNetView _view;

		private ZDOID _zdoId = ZDOID.None;

		private bool _loggedUnresolvedPending;

		private const float DeferredWithdrawTimeout = 3f;

		internal const float OwnershipSettleSeconds = 1f;

		private ushort _ownerRevisionSeen;

		private float _ownerRevisionSince;

		private long _previousOwner;

		private float _lastPlayerActionAt = float.NegativeInfinity;

		private float _viewClaimHeldSince = float.NegativeInfinity;

		private const float PlayerPriorityWindow = 2f;

		private const float ViewClaimHoldCap = 6f;

		private long _ownerSeen;

		private bool _viewClaimMade;

		private bool _reconcileDeferred;

		private string _deferredPriorItemName;

		internal ZDOID ZdoId => _zdoId;

		internal DrawerTier Tier { get; private set; }

		internal DrawerView View { get; private set; }

		public DrawerRenderer Face { get; private set; }

		public DrawerSnapshot Snapshot
		{
			get
			{
				//IL_0021: 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 (!((Object)(object)_view != (Object)null) || !_view.IsValid())
				{
					return new DrawerSnapshot("", 0);
				}
				return new DrawerSnapshot(_view.GetZDO().GetString(KeyPrefabHash, ""), _view.GetZDO().GetInt(KeyAmountHash, 0));
			}
		}

		public int Capacity => DrawerConfig.CapacityFor(Tier);

		internal string DiagId
		{
			get
			{
				//IL_004c: Unknown result type (might be due to invalid IL or missing references)
				if (!((Object)(object)_view != (Object)null) || !_view.IsValid())
				{
					return ((Object)((Component)this).gameObject).name + "#(no ZDO)";
				}
				return $"{((Object)((Component)this).gameObject).name}#{_view.GetZDO().m_uid}";
			}
		}

		internal string ViewDiag
		{
			get
			{
				if (View == null)
				{
					return "no view";
				}
				return View.Describe();
			}
		}

		private void Awake()
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			_view = ((Component)this).GetComponent<ZNetView>();
			Face = ((Component)this).GetComponentInChildren<DrawerRenderer>();
			if ((Object)(object)_view == (Object)null || !_view.IsValid())
			{
				return;
			}
			_zdoId = _view.GetZDO().m_uid;
			Tier = TierFromPrefabName(((Object)((Component)this).gameObject).name);
			ResetOwnershipClock();
			try
			{
				AccessTools.FieldRefAccess<Container, ZNetView>((Container)(object)this, "m_nview") = _view;
			}
			catch (Exception ex)
			{
				DrawerPlugin.Log.LogError((object)("Could not set Container.m_nview; this drawer stays inert. See the compatibility check at startup. " + ex.GetType().Name + ": " + ex.Message));
				return;
			}
			if (InventoryAccess.Available)
			{
				View = new DrawerView(this, _view);
			}
			if (View != null && ContainerBridge.ViewPatchesApplied)
			{
				try
				{
					AccessTools.FieldRefAccess<Container, Inventory>((Container)(object)this, "m_inventory") = View.Inventory;
				}
				catch (Exception ex2)
				{
					DrawerPlugin.Log.LogError((object)("Could not set Container.m_inventory; mods that read it directly will not see this drawer. " + ex2.GetType().Name + ": " + ex2.Message));
				}
			}
			EnsureCreator();
			((Container)this).Awake();
			WearNTear component = ((Component)this).GetComponent<WearNTear>();
			if ((Object)(object)component != (Object)null)
			{
				component.m_onDestroyed = (Action)Delegate.Combine(component.m_onDestroyed, new Action(OnDrawerDestroyed));
			}
			_view.Register<long, int>("RID_ReqWithdraw", (Action<long, long, int>)RPC_RequestWithdraw);
			_view.Register<long, string, int>("RID_GrantWithdraw", (Action<long, long, string, int>)RPC_GrantWithdraw);
			_view.Register<long, string, int>("RID_ReqDeposit", (Action<long, long, string, int>)RPC_RequestDeposit);
			_view.Register<long, int>("RID_GrantDeposit", (Action<long, long, int>)RPC_GrantDeposit);
			_view.Register("RID_ReqClear", (Action<long>)RPC_RequestClear);
			All.Add(this);
			DrawerManager.Instance?.Register(this);
		}

		private void EnsureCreator()
		{
			if ((Object)(object)_view == (Object)null || !_view.IsValid())
			{
				return;
			}
			ZDO zDO = _view.GetZDO();
			if (_view.IsOwner() && zDO.GetLong(CreatorHash, 0L) == 0L)
			{
				PlayerProfile val = (((Object)(object)Game.instance != (Object)null) ? Game.instance.GetPlayerProfile() : null);
				if (val != null)
				{
					zDO.Set(CreatorHash, val.GetPlayerID());
				}
			}
		}

		private void OnDestroy()
		{
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			FlushView(teardown: true);
			All.Remove(this);
			DrawerManager.Instance?.Unregister(this);
			if (!((ZDOID)(ref _zdoId)).IsNone())
			{
				DrawerManager.Instance?.ResolvePendingForDrawer(_zdoId);
			}
		}

		private void OnDrawerDestroyed()
		{
			//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_0034: 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_0048: 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)
			if (!((Object)(object)_view == (Object)null) && _view.IsOwner())
			{
				ReconcileView(null, force: true);
				DrawerSnapshot snapshot = Snapshot;
				if (((DrawerSnapshot)(ref snapshot)).IsAssigned && snapshot.Amount > 0)
				{
					ItemFacts.SpillAtPosition(((Component)this).transform.position, snapshot.ItemName, snapshot.Amount);
				}
			}
		}

		private static DrawerTier TierFromPrefabName(string name)
		{
			DrawerTier[] all = DrawerTiers.All;
			foreach (DrawerTier drawerTier in all)
			{
				if (name.StartsWith(DrawerTiers.PrefabName(drawerTier)))
				{
					return drawerTier;
				}
			}
			return DrawerTier.Wood;
		}

		private bool WriteOwned(DrawerSnapshot expected, DrawerSnapshot next)
		{
			//IL_005b: 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_006d: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_view == (Object)null || !_view.IsValid() || !_view.IsOwner())
			{
				return false;
			}
			ZDO zDO = _view.GetZDO();
			DrawerSnapshot val = default(DrawerSnapshot);
			((DrawerSnapshot)(ref val))..ctor(zDO.GetString(KeyPrefabHash, ""), zDO.GetInt(KeyAmountHash, 0));
			if (!((DrawerSnapshot)(ref val)).Equals(expected))
			{
				return false;
			}
			WriteState(next);
			ReconcileView(val.ItemName);
			return true;
		}

		private void WriteState(DrawerSnapshot next)
		{
			//IL_0011: 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)
			ZDO zDO = _view.GetZDO();
			zDO.Set(KeyPrefabHash, next.ItemName);
			zDO.Set(KeyAmountHash, next.Amount, false);
			DrawerManager.Instance?.MarkDirty(this);
			RefreshFace();
		}

		public void RefreshFace()
		{
			//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_001c: 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)
			if (!((Object)(object)Face == (Object)null))
			{
				DrawerSnapshot snapshot = Snapshot;
				Face.Show(snapshot.ItemName, snapshot.Amount);
			}
		}

		public bool Interact(Humanoid user, bool hold, bool alt)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			if (hold)
			{
				return false;
			}
			Player val = (Player)(object)((user is Player) ? user : null);
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			NotePlayerAction();
			DrawerSnapshot snapshot = Snapshot;
			bool button = ZInput.GetButton("Crouch");
			bool button2 = ZInput.GetButton("Run");
			if (button)
			{
				if (!((DrawerSnapshot)(ref snapshot)).IsAssigned)
				{
					return true;
				}
				if (((DrawerSnapshot)(ref snapshot)).IsEmpty)
				{
					RequestClear(snapshot);
					return true;
				}
				BeginWithdraw(val, snapshot, DrawerState.WithdrawOne(snapshot));
				return true;
			}
			if (button2)
			{
				return DepositEverythingMatching(val, snapshot);
			}
			int num = ItemFacts.MaxStackSize(snapshot.ItemName);
			BeginWithdraw(val, snapshot, DrawerState.WithdrawStack(snapshot, num));
			return true;
		}

		public bool UseItem(Humanoid user, ItemData item)
		{
			//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_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			Player val = (Player)(object)((user is Player) ? user : null);
			if ((Object)(object)val == (Object)null || item == null)
			{
				return false;
			}
			NotePlayerAction();
			DrawerSnapshot snapshot = Snapshot;
			if (((DrawerSnapshot)(ref snapshot)).IsAssigned)
			{
				return false;
			}
			if ((Object)(object)_view == (Object)null || !_view.IsValid())
			{
				((Character)val).Message((MessageType)2, "Try again", 0, (Sprite)null, false);
				return true;
			}
			if (RefuseWhileUnsettled(val))
			{
				return true;
			}
			if (item.m_shared.m_maxStackSize <= 1)
			{
				((Character)val).Message((MessageType)2, "Drawers only hold stackable items", 0, (Sprite)null, false);
				return true;
			}
			string text = ItemFacts.PrefabNameOf(item);
			if (text == null)
			{
				((Character)val).Message((MessageType)2, "Cannot identify this item", 0, (Sprite)null, false);
				return true;
			}
			DrawerOutcome val2 = DrawerState.Deposit(Snapshot, Capacity, text, item.m_stack);
			if (!val2.Accepted)
			{
				((Character)val).Message((MessageType)2, val2.Rejection, 0, (Sprite)null, false);
				return true;
			}
			if (!((Humanoid)val).GetInventory().RemoveItem(item, val2.MovedToDrawer))
			{
				((Character)val).Message((MessageType)2, "Could not remove item", 0, (Sprite)null, false);
				return true;
			}
			RequestDeposit(val, text, val2.MovedToDrawer, announce: false);
			return true;
		}

		private bool DepositEverythingMatching(Player player, DrawerSnapshot current)
		{
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: 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)
			if ((Object)(object)_view == (Object)null || !_view.IsValid())
			{
				((Character)player).Message((MessageType)2, "Try again", 0, (Sprite)null, false);
				return true;
			}
			if (RefuseWhileUnsettled(player))
			{
				return true;
			}
			if (!((DrawerSnapshot)(ref current)).IsAssigned)
			{
				((Character)player).Message((MessageType)2, "This drawer has not been assigned", 0, (Sprite)null, false);
				return true;
			}
			Inventory inventory = ((Humanoid)player).GetInventory();
			List<ItemData> list = new List<ItemData>();
			int num = 0;
			foreach (ItemData allItem in inventory.GetAllItems())
			{
				if (!(ItemFacts.PrefabNameOf(allItem) != current.ItemName))
				{
					list.Add(allItem);
					num += allItem.m_stack;
				}
			}
			if (num <= 0)
			{
				((Character)player).Message((MessageType)2, "You have none of those", 0, (Sprite)null, false);
				return true;
			}
			DrawerOutcome val = DrawerState.Deposit(current, Capacity, current.ItemName, num);
			if (!val.Accepted)
			{
				((Character)player).Message((MessageType)2, val.Rejection, 0, (Sprite)null, false);
				return true;
			}
			int movedToDrawer = val.MovedToDrawer;
			int num2 = 0;
			foreach (ItemData item in list)
			{
				if (num2 >= movedToDrawer)
				{
					break;
				}
				int num3 = ((item.m_stack < movedToDrawer - num2) ? item.m_stack : (movedToDrawer - num2));
				if (inventory.RemoveItem(item, num3))
				{
					num2 += num3;
				}
			}
			if (num2 <= 0)
			{
				((Character)player).Message((MessageType)2, "You have none of those", 0, (Sprite)null, false);
				return true;
			}
			RequestDeposit(player, current.ItemName, num2, announce: true);
			return true;
		}

		private void BeginWithdraw(Player player, DrawerSnapshot current, DrawerOutcome outcome)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: 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_004e: Unknown result type (might be due to invalid IL or missing references)
			if (!outcome.Accepted)
			{
				((Character)player).Message((MessageType)2, outcome.Rejection, 0, (Sprite)null, false);
			}
			else if (outcome.MovedToPlayer > 0)
			{
				if ((Object)(object)ItemFacts.Drop(current.ItemName) == (Object)null)
				{
					((Character)player).Message((MessageType)2, "Cannot identify this item", 0, (Sprite)null, false);
				}
				else
				{
					RequestWithdraw(player, current.ItemName, outcome.MovedToPlayer);
				}
			}
		}

		private void RequestWithdraw(Player player, string itemName, int requested)
		{
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_view == (Object)null || !_view.IsValid() || requested <= 0)
			{
				return;
			}
			NotePlayerAction();
			if (_view.IsOwner())
			{
				if (!OwnershipSettled())
				{
					DrawerManager instance = DrawerManager.Instance;
					if ((Object)(object)instance != (Object)null)
					{
						DrawerDiagnostics.DeferredWithdraws++;
						instance.DeferWithdraw(this, player, requested, 3f);
						return;
					}
				}
				WithdrawAsOwner(player, requested);
				return;
			}
			DrawerManager instance2 = DrawerManager.Instance;
			if ((Object)(object)instance2 == (Object)null)
			{
				return;
			}
			long owner = _view.GetZDO().GetOwner();
			if (owner == 0L)
			{
				DrawerDiagnostics.UnownedClaims++;
				_view.ClaimOwnership();
				if (_view.IsOwner())
				{
					WithdrawAsOwner(player, requested);
				}
				else
				{
					((Character)player).Message((MessageType)2, "Try again", 0, (Sprite)null, false);
				}
			}
			else
			{
				long id = instance2.NextRequestId();
				instance2.AddPendingWithdrawal(id, new PendingWithdrawal(_zdoId, owner, player, requested, ((Component)player).transform.position));
				SendWithdrawRequestRpc(id, owner, requested);
			}
		}

		internal bool TryCompleteDeferredWithdraw(Player player, int requested)
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_view == (Object)null || !_view.IsValid())
			{
				return true;
			}
			if (!_view.IsOwner())
			{
				RequestWithdraw(player, Snapshot.ItemName, requested);
				return true;
			}
			if (!OwnershipSettled())
			{
				return false;
			}
			WithdrawAsOwner(player, requested);
			return true;
		}

		private void WithdrawAsOwner(Player player, int requested)
		{
			//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)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: 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_0025: 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_0034: 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)
			if (!RefuseWhileUnsettled(player))
			{
				DrawerSnapshot snapshot = Snapshot;
				DrawerOutcome val = DrawerState.WithdrawExact(snapshot, requested);
				if (val.MovedToPlayer > 0 && WriteOwned(snapshot, val.Result))
				{
					ItemFacts.GiveToPlayer(player, snapshot.ItemName, val.MovedToPlayer);
				}
			}
		}

		internal void SendWithdrawRequestRpc(long id, long targetOwner, int requested)
		{
			if ((Object)(object)_view != (Object)null && _view.IsValid())
			{
				DrawerDiagnostics.RequestSent(id, Time.time);
				_view.InvokeRPC(targetOwner, "RID_ReqWithdraw", new object[2] { id, requested });
			}
		}

		private void RPC_RequestWithdraw(long sender, long id, int requested)
		{
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_00df: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: 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_00c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_view == (Object)null || !_view.IsValid() || !_view.IsOwner())
			{
				return;
			}
			_handledWithdrawals.Prune(Time.time);
			(string, int) tuple = default((string, int));
			if (_handledWithdrawals.TryGet(sender, id, ref tuple))
			{
				_view.InvokeRPC(sender, "RID_GrantWithdraw", new object[3] { id, tuple.Item1, tuple.Item2 });
			}
			else
			{
				if (!OwnershipSettled())
				{
					return;
				}
				DrawerSnapshot snapshot = Snapshot;
				int num = 0;
				if (((DrawerSnapshot)(ref snapshot)).IsAssigned && (Object)(object)ItemFacts.Drop(snapshot.ItemName) != (Object)null)
				{
					DrawerOutcome val = DrawerState.WithdrawExact(snapshot, requested);
					if (val.MovedToPlayer > 0 && WriteOwned(snapshot, val.Result))
					{
						num = val.MovedToPlayer;
					}
				}
				_handledWithdrawals.Record(sender, id, (snapshot.ItemName, num), Time.time);
				_view.InvokeRPC(sender, "RID_GrantWithdraw", new object[3] { id, snapshot.ItemName, num });
			}
		}

		private void RPC_GrantWithdraw(long sender, long id, string itemName, int granted)
		{
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			DrawerManager instance = DrawerManager.Instance;
			if ((Object)(object)instance == (Object)null || !instance.TryCompleteWithdrawal(id, out var payload))
			{
				return;
			}
			DrawerDiagnostics.GrantReceived(id, Time.time);
			if (sender != payload.TargetOwner)
			{
				DrawerPlugin.Log.LogWarning((object)$"RPC_GrantWithdraw: reply for request {id} came from peer {sender}, not the pinned target {payload.TargetOwner}.");
			}
			int num = ((granted >= 0) ? Math.Min(granted, payload.Requested) : 0);
			if (granted != num)
			{
				DrawerPlugin.Log.LogWarning((object)$"RPC_GrantWithdraw: request {id} asked for {payload.Requested} but was granted {granted}; clamped to {num}.");
			}
			if (num > 0)
			{
				if ((Object)(object)payload.Player != (Object)null)
				{
					ItemFacts.GiveToPlayer(payload.Player, itemName, num);
				}
				else
				{
					ItemFacts.SpillAtPosition(payload.PlayerPosition, itemName, num);
				}
			}
		}

		private void RequestDeposit(Player player, string itemName, int removed, bool announce)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: 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)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Unknown result type (might be due to invalid IL or missing references)
			//IL_014b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: 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_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			if (removed <= 0)
			{
				return;
			}
			if ((Object)(object)_view == (Object)null || !_view.IsValid())
			{
				ItemFacts.SpillAtPosition(((Component)player).transform.position, itemName, removed);
				((Character)player).Message((MessageType)2, "Try again", 0, (Sprite)null, false);
				return;
			}
			if (_view.IsOwner())
			{
				DrawerSnapshot snapshot = Snapshot;
				DrawerOutcome val = DrawerState.Deposit(snapshot, Capacity, itemName, removed);
				bool num = OwnershipSettled();
				if (!num)
				{
					((Character)player).Message((MessageType)2, "Try again", 0, (Sprite)null, false);
				}
				int num2 = ((num && val.Accepted && WriteOwned(snapshot, val.Result)) ? val.MovedToDrawer : 0);
				int num3 = DrawerState.RefundShortfall(removed, num2);
				if (num3 > 0)
				{
					ItemFacts.GiveToPlayer(player, itemName, num3);
				}
				if (announce)
				{
					AnnounceDeposit(player, itemName, num2);
				}
				return;
			}
			DrawerManager instance = DrawerManager.Instance;
			if ((Object)(object)instance == (Object)null)
			{
				ItemFacts.SpillAtPosition(((Component)player).transform.position, itemName, removed);
				((Character)player).Message((MessageType)2, "Try again", 0, (Sprite)null, false);
				return;
			}
			long owner = _view.GetZDO().GetOwner();
			if (owner == 0L)
			{
				ItemFacts.SpillAtPosition(((Component)player).transform.position, itemName, removed);
				((Character)player).Message((MessageType)2, "Try again", 0, (Sprite)null, false);
			}
			else
			{
				long id = instance.NextRequestId();
				instance.AddPendingDeposit(id, new PendingDeposit(_zdoId, owner, player, itemName, removed, ((Component)player).transform.position, announce));
				SendDepositRequestRpc(id, owner, itemName, removed);
			}
		}

		internal void SendDepositRequestRpc(long id, long targetOwner, string itemName, int removed)
		{
			if ((Object)(object)_view != (Object)null && _view.IsValid())
			{
				_view.InvokeRPC(targetOwner, "RID_ReqDeposit", new object[3] { id, itemName, removed });
			}
		}

		private void RPC_RequestDeposit(long sender, long id, string itemName, int amount)
		{
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: 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_009d: 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_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_view == (Object)null) && _view.IsValid() && _view.IsOwner())
			{
				_handledDeposits.Prune(Time.time);
				int num = default(int);
				if (_handledDeposits.TryGet(sender, id, ref num))
				{
					_view.InvokeRPC(sender, "RID_GrantDeposit", new object[2] { id, num });
				}
				else if (OwnershipSettled())
				{
					DrawerSnapshot snapshot = Snapshot;
					DrawerOutcome val = DrawerState.Deposit(snapshot, Capacity, itemName, amount);
					int num2 = ((val.Accepted && WriteOwned(snapshot, val.Result)) ? val.MovedToDrawer : 0);
					_handledDeposits.Record(sender, id, num2, Time.time);
					_view.InvokeRPC(sender, "RID_GrantDeposit", new object[2] { id, num2 });
				}
			}
		}

		private void RPC_GrantDeposit(long sender, long id, int accepted)
		{
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			DrawerManager instance = DrawerManager.Instance;
			if ((Object)(object)instance == (Object)null || !instance.TryCompleteDeposit(id, out var payload))
			{
				return;
			}
			if (sender != payload.TargetOwner)
			{
				DrawerPlugin.Log.LogWarning((object)$"RPC_GrantDeposit: reply for request {id} came from peer {sender}, not the pinned target {payload.TargetOwner}.");
			}
			int num = ((accepted >= 0) ? Math.Min(accepted, payload.Removed) : 0);
			if (accepted != num)
			{
				DrawerPlugin.Log.LogWarning((object)$"RPC_GrantDeposit: request {id} offered {payload.Removed} but {accepted} was reported accepted; clamped to {num}.");
			}
			int num2 = DrawerState.RefundShortfall(payload.Removed, num);
			if (num2 > 0)
			{
				if ((Object)(object)payload.Player != (Object)null)
				{
					ItemFacts.GiveToPlayer(payload.Player, payload.ItemName, num2);
				}
				else
				{
					ItemFacts.SpillAtPosition(payload.PlayerPosition, payload.ItemName, num2);
				}
			}
			if (payload.Announce && (Object)(object)payload.Player != (Object)null)
			{
				AnnounceDeposit(payload.Player, payload.ItemName, num);
			}
		}

		private static void AnnounceDeposit(Player player, string itemName, int accepted)
		{
			if (accepted > 0)
			{
				((Character)player).Message((MessageType)1, $"Stored {accepted} {ItemFacts.LocalizedName(itemName)}", 0, (Sprite)null, false);
			}
		}

		private void RequestClear(DrawerSnapshot current)
		{
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: 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_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_view == (Object)null || !_view.IsValid())
			{
				return;
			}
			if (_view.IsOwner())
			{
				if (!OwnershipSettled())
				{
					Player localPlayer = Player.m_localPlayer;
					if (localPlayer != null)
					{
						((Character)localPlayer).Message((MessageType)2, "Try again", 0, (Sprite)null, false);
					}
				}
				else
				{
					DrawerOutcome val = DrawerState.Clear(current);
					if (val.Accepted)
					{
						WriteOwned(current, val.Result);
					}
				}
			}
			else
			{
				_view.InvokeRPC("RID_ReqClear", Array.Empty<object>());
			}
		}

		private void RPC_RequestClear(long sender)
		{
			//IL_0032: 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_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: 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_0048: 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_004a: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)_view == (Object)null) && _view.IsValid() && _view.IsOwner() && OwnershipSettled())
			{
				DrawerSnapshot snapshot = Snapshot;
				DrawerOutcome val = DrawerState.Clear(snapshot);
				if (val.Accepted)
				{
					WriteOwned(snapshot, val.Result);
				}
			}
		}

		public bool TryWithdrawExternally(int requested, out int taken)
		{
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//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_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			taken = 0;
			if ((Object)(object)_view == (Object)null || !_view.IsValid())
			{
				return false;
			}
			if (!_view.IsOwner())
			{
				_view.ClaimOwnership();
				return false;
			}
			if (!OwnershipSettled())
			{
				return false;
			}
			DrawerSnapshot snapshot = Snapshot;
			DrawerOutcome val = DrawerState.WithdrawExact(snapshot, requested);
			if (val.MovedToPlayer <= 0)
			{
				return false;
			}
			if (!WriteOwned(snapshot, val.Result))
			{
				return false;
			}
			taken = val.MovedToPlayer;
			return true;
		}

		internal DepositRoute ResolveDepositRoute()
		{
			if ((Object)(object)_view == (Object)null || !_view.IsValid())
			{
				return DepositRoute.Unavailable;
			}
			if (_view.IsOwner())
			{
				if (!OwnershipSettled())
				{
					return DepositRoute.Claiming;
				}
				return DepositRoute.Owned;
			}
			ZDO zDO = _view.GetZDO();
			if (zDO == null)
			{
				return DepositRoute.Unavailable;
			}
			if (zDO.GetOwner() == 0L)
			{
				_view.ClaimOwnership();
				return DepositRoute.Claiming;
			}
			return DepositRoute.Foreign;
		}

		internal bool TrySubmitForeignDeposit(string itemName, int amount, Vector3 refundPosition, out int submitted)
		{
			//IL_0049: 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)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: 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_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: 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)
			submitted = 0;
			if (!ItemFacts.IsStorable(itemName))
			{
				return false;
			}
			if ((Object)(object)_view == (Object)null || !_view.IsValid())
			{
				return false;
			}
			ZDO zDO = _view.GetZDO();
			if (zDO == null)
			{
				return false;
			}
			long owner = zDO.GetOwner();
			if (owner == 0L)
			{
				return false;
			}
			DrawerOutcome val = DrawerState.Deposit(Snapshot, Capacity, itemName, amount);
			if (!val.Accepted || val.MovedToDrawer <= 0)
			{
				return false;
			}
			DrawerManager instance = DrawerManager.Instance;
			if ((Object)(object)instance == (Object)null)
			{
				return false;
			}
			long id = instance.NextRequestId();
			instance.AddPendingDeposit(id, new PendingDeposit(_zdoId, owner, null, itemName, val.MovedToDrawer, refundPosition, announce: false));
			SendDepositRequestRpc(id, owner, itemName, val.MovedToDrawer);
			submitted = val.MovedToDrawer;
			return true;
		}

		public bool TryDepositExternally(string itemName, int amount, out int accepted)
		{
			//IL_0038: 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_003e: 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_004d: 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_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			accepted = 0;
			if (!ItemFacts.IsStorable(itemName))
			{
				return false;
			}
			if ((Object)(object)_view == (Object)null || !_view.IsValid() || !_view.IsOwner())
			{
				return false;
			}
			DrawerSnapshot snapshot = Snapshot;
			DrawerOutcome val = DrawerState.Deposit(snapshot, Capacity, itemName, amount);
			if (!val.Accepted || val.MovedToDrawer <= 0)
			{
				return false;
			}
			if (!WriteOwned(snapshot, val.Result))
			{
				return false;
			}
			accepted = val.MovedToDrawer;
			return true;
		}

		internal Inventory GetViewInventory()
		{
			return View?.GetForCaller();
		}

		internal void SaveView()
		{
			if (View != null && (View.IsDirty || View.HasUnflushedChange()))
			{
				View.MarkDirty();
			}
		}

		internal bool LoadView()
		{
			if (View != null)
			{
				return View.RefreshFromZdo();
			}
			return false;
		}

		private float ClaimJitterSeconds()
		{
			return Mathf.Abs((float)((ZDOMan.GetSessionID() ^ (((ZDOID)(ref _zdoId)).UserID * 31) ^ ((ZDOID)(ref _zdoId)).ID) % 500)) / 1000f;
		}

		private void NotePlayerAction()
		{
			_lastPlayerActionAt = Time.time;
		}

		private void ResetOwnershipClock()
		{
			_ownerRevisionSeen = (ushort)(((Object)(object)_view != (Object)null && _view.IsValid()) ? _view.GetZDO().OwnerRevision : 0);
			_ownerSeen = (((Object)(object)_view != (Object)null && _view.IsValid()) ? _view.GetZDO().GetOwner() : 0);
			_previousOwner = _ownerSeen;
			_ownerRevisionSince = Time.time;
		}

		private float OwnerRevisionAge()
		{
			ushort ownerRevision = _view.GetZDO().OwnerRevision;
			if (ownerRevision != _ownerRevisionSeen)
			{
				if (_ownerRevisionSeen != 0)
				{
					DrawerDiagnostics.OwnershipChanges++;
				}
				_previousOwner = _ownerSeen;
				_ownerSeen = _view.GetZDO().GetOwner();
				_ownerRevisionSeen = ownerRevision;
				_ownerRevisionSince = Time.time;
			}
			return Time.time - _ownerRevisionSince;
		}

		private bool RefuseWhileUnsettled(Player player)
		{
			if ((Object)(object)_view == (Object)null || !_view.IsValid() || !_view.IsOwner() || OwnershipSettled())
			{
				return false;
			}
			DrawerDiagnostics.RefusedUnsettled++;
			DrawerDiagnostics.LogRefusal(_previousOwner, ZDOMan.GetSessionID(), OwnerRevisionAge());
			if (player != null)
			{
				((Character)player).Message((MessageType)2, "Try again", 0, (Sprite)null, false);
			}
			return true;
		}

		internal bool OwnershipSettled()
		{
			if ((Object)(object)_view == (Object)null || !_view.IsValid() || !_view.IsOwner())
			{
				return false;
			}
			return ViewFlushPolicy.MayWriteAfterOwnerChange(OwnerRevisionAge(), _previousOwner, ZDOMan.GetSessionID());
		}

		internal void FlushView(bool teardown = false)
		{
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Invalid comparison between Unknown and I4
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Invalid comparison between Unknown and I4
			if (View == null || !View.IsDirty || (Object)(object)_view == (Object)null || !_view.IsValid())
			{
				return;
			}
			if (!View.HasUnflushedChange())
			{
				View.ClearDirty();
				_viewClaimMade = false;
				return;
			}
			if (teardown)
			{
				View.WriteDeltaToZdo();
				_viewClaimMade = false;
				return;
			}
			DrawerManager instance = DrawerManager.Instance;
			ViewFlushAction val = ViewFlushPolicy.Decide(_view.IsOwner(), OwnerRevisionAge(), _viewClaimMade, ClaimJitterSeconds());
			if ((int)val == 1 && Time.time - _lastPlayerActionAt < 2f)
			{
				if (float.IsNegativeInfinity(_viewClaimHeldSince))
				{
					_viewClaimHeldSince = Time.time;
				}
				if (Time.time - _viewClaimHeldSince < 6f)
				{
					DrawerDiagnostics.ClaimsHeldForPlayer++;
					instance?.MarkViewDirty(this);
					return;
				}
			}
			else
			{
				_viewClaimHeldSince = float.NegativeInfinity;
			}
			if ((int)val != 0)
			{
				if ((int)val == 1)
				{
					DrawerDiagnostics.ClaimsMade++;
					_view.ClaimOwnership();
					_viewClaimMade = true;
					instance?.MarkViewDirty(this);
				}
				else
				{
					ReconcileView();
				}
			}
			else
			{
				instance?.MarkViewDirty(this);
			}
		}

		internal bool ViewNeedsReconcile()
		{
			if (View != null && (Object)(object)_view != (Object)null && _view.IsValid() && _view.IsOwner())
			{
				if (!_reconcileDeferred)
				{
					return View.NeedsReconcile();
				}
				return true;
			}
			return false;
		}

		internal void ReconcileView(string priorItemName = null, bool force = false)
		{
			//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_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0121: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0131: 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_0155: 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)
			//IL_0167: Unknown result type (might be due to invalid IL or missing references)
			//IL_016c: Unknown result type (might be due to invalid IL or missing references)
			//IL_016e: 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_0145: Unknown result type (might be due to invalid IL or missing references)
			//IL_0178: 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_01af: Unknown result type (might be due to invalid IL or missing references)
			//IL_020c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0226: Unknown result type (might be due to invalid IL or missing references)
			//IL_0232: Unknown result type (might be due to invalid IL or missing references)
			if (View == null || (Object)(object)_view == (Object)null || !_view.IsValid() || !_view.IsOwner())
			{
				return;
			}
			if (!force && !OwnershipSettled())
			{
				_reconcileDeferred = true;
				if (!string.IsNullOrEmpty(priorItemName) && _deferredPriorItemName == null)
				{
					_deferredPriorItemName = priorItemName;
				}
				return;
			}
			if (priorItemName == null)
			{
				priorItemName = _deferredPriorItemName;
			}
			_reconcileDeferred = false;
			_deferredPriorItemName = null;
			_viewClaimMade = false;
			DrawerSnapshot snapshot = Snapshot;
			if (View.CannotResolve(snapshot))
			{
				ReconcileUnresolved(snapshot);
				return;
			}
			_loggedUnresolvedPending = false;
			string itemName = ((!((DrawerSnapshot)(ref snapshot)).IsAssigned && !string.IsNullOrEmpty(priorItemName)) ? priorItemName : snapshot.ItemName);
			if (!View.TryReadForReconcile(itemName, out var itemTotal, out var baseline, out var foreign))
			{
				View.Publish(snapshot);
				return;
			}
			if (itemTotal < 0)
			{
				baseline -= itemTotal;
				itemTotal = 0;
			}
			int num = 0;
			foreach (KeyValuePair<string, int> item in foreign)
			{
				num += item.Value;
			}
			ViewReconcileOutcome val = ViewReconciliation.Apply(snapshot, Capacity, baseline, itemTotal, num);
			if (!((DrawerSnapshot)(ref val.Result)).Equals(snapshot))
			{
				WriteState(val.Result);
			}
			View.Publish(val.Result);
			Vector3 position = ((Component)this).transform.position;
			if (val.SpillDrawerItem > 0)
			{
				ItemFacts.SpillAtPosition(position, itemName, val.SpillDrawerItem);
			}
			foreach (KeyValuePair<string, int> item2 in foreign)
			{
				if ((Object)(object)ItemFacts.Prefab(item2.Key) != (Object)null)
				{
					ItemFacts.SpillAtPosition(position, item2.Key, item2.Value);
				}
				else
				{
					DrawerPlugin.Log.LogWarning((object)$"{DiagId}: {item2.Value} of unidentifiable item '{item2.Key}' left in the drawer's view could not be returned.");
				}
			}
			if (val.Unbacked > 0)
			{
				DrawerPlugin.Log.LogWarning((object)($"{DiagId}: {val.Unbacked} {snapshot.ItemName} were taken through the view beyond what the drawer held " + "(another player changed it at the same moment)."));
			}
		}

		private void ReconcileUnresolved(DrawerSnapshot current)
		{
			//IL_0006: 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)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			switch (View.InspectForUnresolvedOwner(current.Amount))
			{
			case DrawerView.UnresolvedViewState.PendingChanges:
				if (!_loggedUnresolvedPending)
				{
					_loggedUnresolvedPending = true;
					DrawerPlugin.Log.LogWarning((object)(DiagId + ": this client cannot resolve item '" + current.ItemName + "', so a change made through the drawer's container view is left unreconciled for an owner that can."));
				}
				break;
			case DrawerView.UnresolvedViewState.AlreadyPublished:
				break;
			default:
				View.Publish(current);
				break;
			}
		}

		public string GetHoverText()
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			DrawerSnapshot snapshot = Snapshot;
			if (!((DrawerSnapshot)(ref snapshot)).IsAssigned)
			{
				return Localization.instance.Localize(DrawerTiers.DisplayName(Tier) + "\n[<color=yellow><b>1-8</b></color>] Store an item");
			}
			string arg = ItemFacts.LocalizedName(snapshot.ItemName);
			string text = ((snapshot.Amount > 0) ? "[<color=yellow><b>E</b></color>] Take stack\n[<color=yellow><b>Ctrl+E</b></color>] Take one\n[<color=yellow><b>Shift+E</b></color>] Store all" : "[<color=yellow><b>Ctrl+E</b></color>] Unassign\n[<color=yellow><b>Shift+E</b></color>] Store all");
			return Localization.instance.Localize($"{arg}  <color=orange>{snapshot.Amount}</color>/{Capacity}\n" + text);
		}

		public string GetHoverName()
		{
			return DrawerTiers.DisplayName(Tier);
		}
	}
	[HarmonyPatch(typeof(Container), "CanBeRemoved")]
	internal static class DrawerCanBeRemovedPatch
	{
		private static bool Prepare()
		{
			return ValheimCompat.RequireMethod(typeof(Container), "CanBeRemoved");
		}

		private static bool Prefix(Container __instance, ref bool __result)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			if (!(__instance is DrawerComponent drawerComponent))
			{
				return true;
			}
			DrawerSnapshot snapshot = drawerComponent.Snapshot;
			__result = ((DrawerSnapshot)(ref snapshot)).IsEmpty;
			return false;
		}
	}
	[HarmonyPatch(typeof(Piece), "SetCreator")]
	internal static class PieceSetCreatorPatch
	{
		private static bool Prepare()
		{
			return ValheimCompat.RequireMethod(typeof(Piece), "SetCreator");
		}

		private static void Prefix(Piece __instance, long uid)
		{
			if (__instance.m_creator == uid && (Object)(object)((Component)__instance).GetComponent<DrawerComponent>() != (Object)null)
			{
				__instance.m_creator = 0L;
			}
		}
	}
	public static class DrawerConfig
	{
		public static ConfigEntry<int> WoodCapacity;

		public static ConfigEntry<int> StoneCapacity;

		public static ConfigEntry<int> BlackMarbleCapacity;

		public static ConfigEntry<bool> AutoPickupEnabled;

		public static ConfigEntry<float> PickupRadius;

		public static ConfigEntry<float> PickupScanRange;

		public static ConfigEntry<float> PickupInterval;

		public static ConfigEntry<float> LabelDistance;

		private static readonly Dictionary<DrawerTier, ConfigEntry<string>> Recipes = new Dictionary<DrawerTier, ConfigEntry<string>>();

		public static void Bind(ConfigFile config)
		{
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Expected O, but got Unknown
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Expected O, but got Unknown
			//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)
			//IL_0065: Expected O, but got Unknown
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006f: Expected O, but got Unknown
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Expected O, but got Unknown
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Expected O, but got Unknown
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Expected O, but got Unknown
			//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Expected O, but got Unknown
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0133: Unknown result type (might be due to invalid IL or missing references)
			//IL_013b: Expected O, but got Unknown
			//IL_013b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Expected O, but got Unknown
			//IL_0168: 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_0175: Expected O, but got Unknown
			//IL_0175: Unknown result type (might be due to invalid IL or missing references)
			//IL_017f: Expected O, but got Unknown
			//IL_01a2: 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_01af: Expected O, but got Unknown
			//IL_01af: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b9: Expected O, but got Unknown
			//IL_01dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e9: Expected O, but got Unknown
			//IL_01e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f3: Expected O, but got Unknown
			WoodCapacity = config.Bind<int>("Capacity", "Wood", 1000, new ConfigDescription("How many items a wood drawer holds.", (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = true
			} }));
			StoneCapacity = config.Bind<int>("Capacity", "Stone", 2000, new ConfigDescription("How many items a stone drawer holds.", (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = true
			} }));
			BlackMarbleCapacity = config.Bind<int>("Capacity", "BlackMarble", 10000, new ConfigDescription("How many items a black marble drawer holds.", (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = true
			} }));
			DrawerTier[] all = DrawerTiers.All;
			for (int i = 0; i < all.Length; i++)
			{
				DrawerTier drawerTier = all[i];
				Recipes[drawerTier] = config.Bind<string>("Recipe", drawerTier.ToString(), RecipeSpec.Format(DrawerTiers.Recipe(drawerTier)), new ConfigDescription("Build cost, as Item:Count separated by commas -- e.g. FineWood:5,Stone:10. Names are PREFAB names (FineWood, BlackMarble, RoundLog), not the names shown in game. An unparseable or unknown-item recipe is logged and the default is used instead. Takes effect on restart.", (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes
				{
					IsAdminOnly = true
				} }));
			}
			AutoPickupEnabled = config.Bind<bool>("Pickup", "Enabled", true, new ConfigDescription("Drawers absorb matching items dropped nearby.", (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = true
			} }));
			PickupRadius = config.Bind<float>("Pickup", "Radius", 40f, new ConfigDescription("How far from a drawer a dropped item is absorbed, in metres.", (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = true
			} }));
			PickupScanRange = config.Bind<float>("Pickup", "ScanRange", 40f, new ConfigDescription("How far around you dropped items are considered at all. One query covers every drawer, so this is cheap.", (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = true
			} }));
			PickupInterval = config.Bind<float>("Pickup", "Interval", 0.5f, new ConfigDescription("Seconds between pickup passes.", (AcceptableValueBase)null, new object[1] { (object)new ConfigurationManagerAttributes
			{
				IsAdminOnly = true
			} }));
			LabelDistance = config.Bind<float>("Display", "LabelDistance", 30f, "Beyond this distance drawer icons and counts switch off. A wall of text nobody can read is wasted work.");
		}

		public static IReadOnlyList<(string Item, int Amount)> RecipeFor(DrawerTier tier)
		{
			IReadOnlyList<(string, int)> readOnlyList = DrawerTiers.Recipe(tier);
			if (!Recipes.TryGetValue(tier, out var value) || value == null)
			{
				return readOnlyList;
			}
			RecipeSpec val = RecipeSpec.Parse(value.Value);
			string text = (val.Ok ? UnknownItem(val.Requirements) : val.Error);
			if (text == null)
			{
				return val.Requirements;
			}
			DrawerPlugin.Log.LogWarning((object)($"Recipe.{tier} is not usable ({text}); using the default " + RecipeSpec.Format(readOnlyList) + " instead."));
			return readOnlyList;
		}

		private static string UnknownItem(IReadOnlyList<(string Item, int Amount)> requirements)
		{
			PrefabManager instance = PrefabManager.Instance;
			if (instance == null)
			{
				return null;
			}
			foreach (var requirement in requirements)
			{
				if ((Object)(object)instance.GetPrefab(requirement.Item) == (Object)null)
				{
					return "no item named '" + requirement.Item + "'";
				}
			}
			return null;
		}

		public static int CapacityFor(DrawerTier tier)
		{
			return tier switch
			{
				DrawerTier.Wood => WoodCapacity?.Value ?? DrawerTiers.DefaultCapacity(tier), 
				DrawerTier.Stone => StoneCapacity?.Value ?? DrawerTiers.DefaultCapacity(tier), 
				DrawerTier.BlackMarble => BlackMarbleCapacity?.Value ?? DrawerTiers.DefaultCapacity(tier), 
				_ => DrawerTiers.DefaultCapacity(tier), 
			};
		}
	}
	internal static class DrawerDiagnostics
	{
		public static int RefusedUnsettled;

		public static int OwnershipChanges;

		public static int ClaimsMade;

		public static int RequestsSent;

		public static int UnownedClaims;

		public static int DeferredWithdraws;

		public static int ClaimsHeldForPlayer;

		public static int GrantsReceived;

		public static int GrantsGivenUp;

		private static double _grantLatencyTotalMs;

		private static double _grantLatencyMaxMs;

		private static readonly Dictionary<long, float> _sentAt = new Dictionary<long, float>();

		private const int RefusalLogLimit = 15;

		private static int _refusalsLogged;

		public static double MeanGrantLatencyMs
		{
			get
			{
				if (GrantsReceived <= 0)
				{
					return 0.0;
				}
				return _grantLatencyTotalMs / (double)GrantsReceived;
			}
		}

		public static double MaxGrantLatencyMs => _grantLatencyMaxMs;

		public static int OutstandingRequests => _sentAt.Count;

		public static void RequestSent(long id, float now)
		{
			RequestsSent++;
			_sentAt[id] = now;
		}

		public static void GrantReceived(long id, float now)
		{
			GrantsReceived++;
			if (_sentAt.TryGetValue(id, out var value))
			{
				_sentAt.Remove(id);
				double num = (double)(now - value) * 1000.0;
				_grantLatencyTotalMs += num;
				if (num > _grantLatencyMaxMs)
				{
					_grantLatencyMaxMs = num;
				}
			}
		}

		public static void RequestClosed(long id)
		{
			_sentAt.Remove(id);
		}

		public static void LogRefusal(long previousOwner, long thisPeer, float ownerRevisionAge)
		{
			if (_refusalsLogged < 15)
			{
				_refusalsLogged++;
				string arg = ((previousOwner == 0L) ? "nobody" : ((previousOwner == thisPeer) ? "this client" : $"peer {previousOwner}"));
				DrawerPlugin.Log.LogInfo((object)($"Withdraw refused ({RefusedUnsettled} so far): ownership came from {arg} " + $"{ownerRevisionAge:F2}s ago." + ((_refusalsLogged == 15) ? " Further refusals will not be logged; use rid_diag for totals." : "")));
			}
		}

		public static void Reset()
		{
			RefusedUnsettled = 0;
			OwnershipChanges = 0;
			ClaimsMade = 0;
			RequestsSent = 0;
			UnownedClaims = 0;
			DeferredWithdraws = 0;
			ClaimsHeldForPlayer = 0;
			GrantsReceived = 0;
			GrantsGivenUp = 0;
			_grantLatencyTotalMs = 0.0;
			_grantLatencyMaxMs = 0.0;
			_sentAt.Clear();
			_refusalsLogged = 0;
		}
	}
	internal static class DrawerFont
	{
		private static TMP_FontAsset _cached;

		private static bool _loggedFailure;

		public static TMP_FontAsset Shared
		{
			get
			{
				if ((Object)(object)_cached != (Object)null)
				{
					return _cached;
				}
				TMP_FontAsset val = ((GUIManager.Instance != null) ? GUIManager.Instance.TMP_AveriaSansLibre : null);
				if ((Object)(object)val != (Object)null)
				{
					_cached = val;
					return _cached;
				}
				TMP_Text[] array = Resources.FindObjectsOfTypeAll<TMP_Text>();
				foreach (TMP_Text val2 in array)
				{
					if ((Object)(object)val2 != (Object)null && (Object)(object)val2.font != (Object)null)
					{
						_cached = val2.font;
						return _cached;
					}
				}
				TMP_FontAsset[] array2 = Resources.FindObjectsOfTypeAll<TMP_FontAsset>();
				if (array2.Length != 0)
				{
					_cached = array2[0];
					return _cached;
				}
				try
				{
					TMP_FontAsset defaultFontAsset = TMP_Settings.defaultFontAsset;
					if ((Object)(object)defaultFontAsset != (Object)null)
					{
						_cached = defaultFontAsset;
						return _cached;
					}
				}
				catch (Exception)
				{
				}
				if (!_loggedFailure)
				{
					_loggedFailure = true;
					DrawerPlugin.Log.LogError((object)"No TMP font asset found anywhere (checked Jotunn.GUIManager.TMP_AveriaSansLibre, every live TMP_Text, Resources.FindObjectsOfTypeAll<TMP_FontAsset>, and TMP_Settings.defaultFontAsset). Drawer count labels will not render any text until this resolves. This mod ships no font of its own by design.");
				}
				return null;
			}
		}
	}
	public static class DrawerIconAtlas
	{
		private static AtlasLayout _layout;

		private static Texture2D _texture;

		private static bool _buildFailureLogged;

		private static readonly string[] ShaderCandidates = new string[3] { "Unlit/Transparent", "Sprites/Default", "UI/Default" };

		public static Material SharedMaterial { get; private set; }

		public static bool IsBuilt
		{
			get
			{
				if (_layout != null)
				{
					return (Object)(object)SharedMaterial != (Object)null;
				}
				return false;
			}
		}

		public static void Build()
		{
			if (IsBuilt)
			{
				return;
			}
			try
			{
				BuildInternal();
			}
			catch (Exception arg)
			{
				_layout = null;
				SharedMaterial = null;
				if (!_buildFailureLogged)
				{
					_buildFailureLogged = true;
					DrawerPlugin.Log.LogError((object)("Icon atlas build threw; leaving it unbuilt. Will keep retrying quietly on " + $"later calls, but will not log this again: {arg}"));
				}
			}
		}

		private static void BuildInternal()
		{
			//IL_01cd: 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)
			//IL_01dd: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f0: Expected O, but got Unknown
			//IL_0259: Unknown result type (might be due to invalid IL or missing references)
			//IL_0265: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0301: Unknown result type (might be due to invalid IL or missing references)
			//IL_0311: Expected O, but got Unknown
			//IL_0149: 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_015a: Unknown result type (might be due to invalid IL or missing references)
			//IL_015f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0169: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)ObjectDB.instance == (Object)null)
			{
				DrawerPlugin.Log.LogWarning((object)"ObjectDB not ready; icon atlas deferred.");
				return;
			}
			List<IconSize> list = new List<IconSize>();
			Dictionary<string, Sprite> dictionary = new Dictionary<string, Sprite>();
			List<string> list2 = new List<string>();
			foreach (GameObject item in ObjectDB.instance.m_items)
			{
				ItemDrop val = (((Object)(object)item == (Object)null) ? null : item.GetComponent<ItemDrop>());
				if ((Object)(object)val == (Object)null || val.m_itemData.m_shared.m_maxStackSize <= 1)
				{
					continue;
				}
				Sprite val2;
				try
				{
					val2 = ItemFacts.SafeIcon(val.m_itemData, out var variantOutOfRange);
					if (variantOutOfRange)
					{
						list2.Add(((Object)item).name);
					}
				}
				catch (Exception ex)
				{
					DrawerPlugin.Log.LogWarning((object)("Skipping '" + ((Object)item).name + "' in the icon atlas: reading its icon threw (" + ex.GetType().Name + ": " + ex.Message + ")."));
					continue;
				}
				if (!((Object)(object)val2 == (Object)null) && !((Object)(object)val2.texture == (Object)null) && !dictionary.ContainsKey(((Object)item).name))
				{
					dictionary[((Object)item).name] = val2;
					string name = ((Object)item).name;
					Rect textureRect = val2.textureRect;
					int num = (int)((Rect)(ref textureRect)).width;
					textureRect = val2.textureRect;
					list.Add(new IconSize(name, num, (int)((Rect)(ref textureRect)).height));
				}
			}
			if (list.Count == 0)
			{
				DrawerPlugin.Log.LogError((object)"Icon atlas build found 0 storable items in ObjectDB.m_items -- called too early, before item registration finished. Not marking the atlas built; a later call to Build() (see DrawerIconAtlas.TryGetUv's retry) will try again.");
				return;
			}
			_layout = IconAtlasPacker