Decompiled source of ModerWarehousePlus v1.0.1

plugins/ModerWarehousePlus.dll

Decompiled 4 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using Microsoft.CodeAnalysis;
using ServersideQoL;
using ServersideQoL.Processors;
using ServersideQoL.Utilities;
using UnityEngine;
using YamlDotNet.Serialization;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyCompany("Tie")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright © 2026 Cris Haani; fork changes © 2026 Tie")]
[assembly: AssemblyDescription("Server-only automatic warehouse for Valheim. Fork of Moder Warehouse 2.0.0 by Cris Haani.")]
[assembly: AssemblyProduct("Moder Warehouse Plus")]
[assembly: AssemblyTitle("ModerWarehousePlus")]
[assembly: InternalsVisibleTo("ModerWarehousePlus.Tests")]
[assembly: AssemblyFileVersion("1.0.1")]
[assembly: AssemblyInformationalVersion("1.0.1")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.1.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace ModerWarehouse
{
	internal static class RequestDisplayPolicy
	{
		internal const float MinimumResultSeconds = 2f;

		internal const float MaximumResultSeconds = 8f;

		internal static float ClampResultSeconds(float configuredSeconds)
		{
			if (float.IsNaN(configuredSeconds) || float.IsInfinity(configuredSeconds))
			{
				return 8f;
			}
			return Math.Max(2f, Math.Min(8f, configuredSeconds));
		}
	}
	internal static class SleepDisplayPolicy
	{
		internal sealed class Layout
		{
			internal string Text { get; }

			internal int LabelLength { get; }

			internal Layout(string text, int labelLength)
			{
				Text = text;
				LabelLength = labelLength;
			}
		}

		internal static Layout Create(string localizedLabel, int cellCount)
		{
			if (cellCount <= 0)
			{
				return new Layout(string.Empty, 0);
			}
			string text = (localizedLabel ?? string.Empty).Trim().TrimEnd('Z', 'z');
			return new Layout(labelLength: Math.Min(text.Length, cellCount), text: (text + new string('Z', cellCount)).Substring(0, cellCount));
		}
	}
	internal sealed class SpatialLookup<T> where T : class
	{
		private sealed class Entry
		{
			internal T Item;

			internal float X;

			internal float Y;

			internal float Z;

			internal int Order;
		}

		private const float CellSize = 16f;

		private readonly Dictionary<Tuple<int, int, int>, List<Entry>> cells = new Dictionary<Tuple<int, int, int>, List<Entry>>();

		internal SpatialLookup(IEnumerable<T> items, Func<T, float> x, Func<T, float> y, Func<T, float> z)
		{
			int num = 0;
			foreach (T item in items)
			{
				Entry entry = new Entry
				{
					Item = item,
					X = x(item),
					Y = y(item),
					Z = z(item),
					Order = num++
				};
				Tuple<int, int, int> key = Tuple.Create(Cell(entry.X), Cell(entry.Y), Cell(entry.Z));
				if (!cells.TryGetValue(key, out var value))
				{
					List<Entry> list = (cells[key] = new List<Entry>());
					value = list;
				}
				value.Add(entry);
			}
		}

		private static int Cell(float value)
		{
			return (int)Math.Floor(value / 16f);
		}

		internal T Nearest(float x, float y, float z, float radiusSquared, Func<T, bool> include = null, bool preferLastOnTie = false)
		{
			if (radiusSquared < 0f || float.IsNaN(radiusSquared))
			{
				return null;
			}
			if (radiusSquared > 4096f)
			{
				Entry entry = null;
				float num = radiusSquared;
				foreach (List<Entry> value2 in cells.Values)
				{
					foreach (Entry item in value2)
					{
						float num2 = item.X - x;
						float num3 = item.Y - y;
						float num4 = item.Z - z;
						float num5 = num2 * num2 + num3 * num3 + num4 * num4;
						if (!(num5 > num) && (num5 != num || entry == null || !(preferLastOnTie ? (item.Order < entry.Order) : (item.Order > entry.Order))) && (include == null || include(item.Item)))
						{
							entry = item;
							num = num5;
						}
					}
				}
				if (entry == null)
				{
					return null;
				}
				return entry.Item;
			}
			float num6 = (float)Math.Sqrt(radiusSquared);
			Entry entry2 = null;
			float num7 = radiusSquared;
			for (int i = Cell(x - num6); i <= Cell(x + num6); i++)
			{
				for (int j = Cell(y - num6); j <= Cell(y + num6); j++)
				{
					for (int k = Cell(z - num6); k <= Cell(z + num6); k++)
					{
						if (!cells.TryGetValue(Tuple.Create(i, j, k), out var value))
						{
							continue;
						}
						foreach (Entry item2 in value)
						{
							float num8 = item2.X - x;
							float num9 = item2.Y - y;
							float num10 = item2.Z - z;
							float num11 = num8 * num8 + num9 * num9 + num10 * num10;
							if (!(num11 > num7) && (num11 != num7 || entry2 == null || !(preferLastOnTie ? (item2.Order < entry2.Order) : (item2.Order > entry2.Order))) && (include == null || include(item2.Item)))
							{
								entry2 = item2;
								num7 = num11;
							}
						}
					}
				}
			}
			if (entry2 == null)
			{
				return null;
			}
			return entry2.Item;
		}
	}
	internal static class TranslationGate
	{
		internal sealed class Report
		{
			internal readonly List<string> Missing = new List<string>();

			internal readonly List<string> Unknown = new List<string>();

			internal readonly List<string> InvalidPlaceholders = new List<string>();

			internal bool IsValid => InvalidPlaceholders.Count == 0;
		}

		private static readonly Regex LanguageCode = new Regex("^[a-z]{2,3}(?:-[a-z0-9]{2,8})*$", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);

		private static readonly Regex Placeholder = new Regex("\\{([0-9]+)(?:,[^{}]+)?(?::[^{}]+)?\\}", RegexOptions.CultureInvariant);

		internal static bool IsValidLanguageCode(string value)
		{
			string input = (value ?? string.Empty).Trim().Replace('_', '-');
			return LanguageCode.IsMatch(input);
		}

		internal static Report Validate(IDictionary<string, string> reference, IDictionary<string, string> supplied)
		{
			Report report = new Report();
			if (reference == null || supplied == null)
			{
				return report;
			}
			foreach (KeyValuePair<string, string> item in reference)
			{
				if (!supplied.TryGetValue(item.Key, out var value) || string.IsNullOrWhiteSpace(value))
				{
					report.Missing.Add(item.Key);
				}
				else if (!PlaceholderSet(item.Value).SequenceEqual(PlaceholderSet(value)))
				{
					report.InvalidPlaceholders.Add(item.Key);
				}
			}
			foreach (string key in supplied.Keys)
			{
				if (!reference.ContainsKey(key))
				{
					report.Unknown.Add(key);
				}
			}
			report.Missing.Sort(StringComparer.OrdinalIgnoreCase);
			report.Unknown.Sort(StringComparer.OrdinalIgnoreCase);
			report.InvalidPlaceholders.Sort(StringComparer.OrdinalIgnoreCase);
			return report;
		}

		private static string[] PlaceholderSet(string value)
		{
			return (from Match match in Placeholder.Matches(value ?? string.Empty)
				select match.Groups[1].Value).OrderBy<string, string>((string index) => index, StringComparer.Ordinal).ToArray();
		}
	}
	internal static class WarehouseClock
	{
		internal static Func<DateTime> Source = () => DateTime.UtcNow;

		internal static DateTime UtcNow => Source();
	}
	public sealed class WarehouseConfig : ConfigBase<WarehouseConfig>
	{
		public override ConfigEntry<bool> Enabled { get; }

		public ConfigEntry<string> Language { get; }

		public ConfigEntry<float> Radius { get; }

		public ConfigEntry<float> MinimumRadius { get; }

		public ConfigEntry<float> MinimumWarehouseDistance { get; }

		public ConfigEntry<float> MarkerDistance { get; }

		public ConfigEntry<float> ScanInterval { get; }

		public ConfigEntry<int> MaxMoves { get; }

		public ConfigEntry<float> StaleInputLockSeconds { get; }

		public ConfigEntry<float> StaleInputLockPlayerDistance { get; }

		public ConfigEntry<float> StaleInputLockMaxSeconds { get; }

		public ConfigEntry<bool> AutoCreateLabels { get; }

		public ConfigEntry<float> LabelDistance { get; }

		public ConfigEntry<float> AutoLabelDelay { get; }

		public ConfigEntry<float> RequestResultSeconds { get; }

		public ConfigEntry<float> BoardPageSeconds { get; }

		public ConfigEntry<float> BoardWakeDistance { get; }

		public ConfigEntry<float> DeliveryNoticeSeconds { get; }

		public ConfigEntry<float> VisitSummarySeconds { get; }

		public ConfigEntry<int> CapacityWarningPercent { get; }

		public ConfigEntry<int> CapacityCriticalPercent { get; }

		public WarehouseConfig(ConfigFile file, Logger logger)
			: base(file, logger)
		{
			Enabled = ConfigBase<WarehouseConfig>.BindEx<bool>(file, "Warehouse", true, "Enable the warehouse.", (IAcceptableValueBase<bool>)null, (Deprecated<WarehouseConfig>)null, "Enabled");
			Language = ConfigBase<WarehouseConfig>.BindEx<string>(file, "Localization", "English", "Default display language. Built in: English, German. External JSON translations may add more.", (IAcceptableValueBase<string>)null, (Deprecated<WarehouseConfig>)null, "Language");
			Radius = ConfigBase<WarehouseConfig>.BindEx<float>(file, "Warehouse", 100f, "Storage radius around the input chest.", (IAcceptableValueBase<float>)null, (Deprecated<WarehouseConfig>)null, "Radius");
			MinimumRadius = ConfigBase<WarehouseConfig>.BindEx<float>(file, "Warehouse", 10f, "Smallest radius selectable on an IN sign.", (IAcceptableValueBase<float>)null, (Deprecated<WarehouseConfig>)null, "MinimumRadius");
			MinimumWarehouseDistance = ConfigBase<WarehouseConfig>.BindEx<float>(file, "Warehouse", 210f, "Minimum distance between warehouse IN chests.", (IAcceptableValueBase<float>)null, (Deprecated<WarehouseConfig>)null, "MinimumWarehouseDistance");
			MarkerDistance = ConfigBase<WarehouseConfig>.BindEx<float>(file, "Warehouse", 3f, "Maximum sign-to-chest distance.", (IAcceptableValueBase<float>)null, (Deprecated<WarehouseConfig>)null, "MarkerDistance");
			ScanInterval = ConfigBase<WarehouseConfig>.BindEx<float>(file, "Warehouse", 3f, "Seconds between scans.", (IAcceptableValueBase<float>)null, (Deprecated<WarehouseConfig>)null, "ScanInterval");
			MaxMoves = ConfigBase<WarehouseConfig>.BindEx<int>(file, "Safety", 250, "Maximum stack moves per scan.", (IAcceptableValueBase<int>)null, (Deprecated<WarehouseConfig>)null, "MaxMovesPerCycle");
			StaleInputLockSeconds = ConfigBase<WarehouseConfig>.BindEx<float>(file, "Safety", 30f, "Seconds the IN chest may stay marked in use after its opener left or disconnected before the lock is released. 0 disables.", (IAcceptableValueBase<float>)null, (Deprecated<WarehouseConfig>)null, "StaleInputLockSeconds");
			StaleInputLockPlayerDistance = ConfigBase<WarehouseConfig>.BindEx<float>(file, "Safety", 4f, "The player who opened the IN chest keeps its lock while within this distance (m).", (IAcceptableValueBase<float>)null, (Deprecated<WarehouseConfig>)null, "StaleInputLockPlayerDistance");
			StaleInputLockMaxSeconds = ConfigBase<WarehouseConfig>.BindEx<float>(file, "Safety", 90f, "Hard limit (s): the IN chest lock is released after this long even if its opener is still nearby.", (IAcceptableValueBase<float>)null, (Deprecated<WarehouseConfig>)null, "StaleInputLockMaxSeconds");
			AutoCreateLabels = ConfigBase<WarehouseConfig>.BindEx<bool>(file, "Labels", true, "Create persistent localized white labels for homogeneous storage chests.", (IAcceptableValueBase<bool>)null, (Deprecated<WarehouseConfig>)null, "AutoCreateLabels");
			LabelDistance = ConfigBase<WarehouseConfig>.BindEx<float>(file, "Labels", 1f, "Maximum distance for ordinary storage labels and blank adoption signs.", (IAcceptableValueBase<float>)null, (Deprecated<WarehouseConfig>)null, "LabelDistance");
			AutoLabelDelay = ConfigBase<WarehouseConfig>.BindEx<float>(file, "Labels", 90f, "Seconds an unlabeled homogeneous chest must remain unchanged before a sign is created.", (IAcceptableValueBase<float>)null, (Deprecated<WarehouseConfig>)null, "AutoLabelDelay");
			RequestResultSeconds = ConfigBase<WarehouseConfig>.BindEx<float>(file, "Requests", 8f, "Seconds withdrawal results remain visible before the REQ sign returns to its target-stock view (maximum 8 seconds).", (IAcceptableValueBase<float>)null, (Deprecated<WarehouseConfig>)null, "ResultDisplaySeconds");
			BoardPageSeconds = ConfigBase<WarehouseConfig>.BindEx<float>(file, "Display", 3f, "Seconds each warehouse board page remains visible.", (IAcceptableValueBase<float>)null, (Deprecated<WarehouseConfig>)null, "BoardPageSeconds");
			BoardWakeDistance = ConfigBase<WarehouseConfig>.BindEx<float>(file, "Display", 5f, "Player distance in metres at which the stock board wakes up.", (IAcceptableValueBase<float>)null, (Deprecated<WarehouseConfig>)null, "BoardWakeDistance");
			DeliveryNoticeSeconds = ConfigBase<WarehouseConfig>.BindEx<float>(file, "Display", 6f, "Seconds a completed delivery remains visible on the stock board.", (IAcceptableValueBase<float>)null, (Deprecated<WarehouseConfig>)null, "DeliveryNoticeSeconds");
			VisitSummarySeconds = ConfigBase<WarehouseConfig>.BindEx<float>(file, "Display", 12f, "Seconds stock changes since the last visit remain visible after waking the board.", (IAcceptableValueBase<float>)null, (Deprecated<WarehouseConfig>)null, "VisitSummarySeconds");
			CapacityWarningPercent = ConfigBase<WarehouseConfig>.BindEx<int>(file, "Display", 80, "Occupied chest percentage at which the capacity display becomes orange.", (IAcceptableValueBase<int>)null, (Deprecated<WarehouseConfig>)null, "CapacityWarningPercent");
			CapacityCriticalPercent = ConfigBase<WarehouseConfig>.BindEx<int>(file, "Display", 95, "Occupied chest percentage at which the capacity display becomes red and requests expansion.", (IAcceptableValueBase<int>)null, (Deprecated<WarehouseConfig>)null, "CapacityCriticalPercent");
		}
	}
	public static class WarehouseContainerPolicy
	{
		public static bool IsPrivatePrefab(string prefabName)
		{
			string text = (prefabName ?? string.Empty).ToLowerInvariant();
			if (!text.Contains("chest_private"))
			{
				return text.Contains("personal");
			}
			return true;
		}

		public static bool CanManage(bool endpoint, bool ignored, bool isPrivate, bool inUse, bool hasStorageSign)
		{
			return !endpoint && !ignored && !isPrivate && !inUse && hasStorageSign;
		}

		public static int TransferAmount(int sourceStack, int wanted, int maxStack, int matchingStackSpace, int emptySlots)
		{
			int num = Math.Max(1, maxStack);
			int val = Math.Max(0, matchingStackSpace) + Math.Max(0, emptySlots) * num;
			return Math.Min(Math.Min(Math.Max(0, sourceStack), Math.Max(0, wanted)), val);
		}
	}
	public static class WarehouseDistancePolicy
	{
		public static bool IsTooClose(float distance, float minimum)
		{
			return distance < minimum;
		}
	}
	public static class WarehouseDuplicatePolicy
	{
		public static int Compare(bool leftCanonical, int leftInfrastructure, string leftStableId, bool rightCanonical, int rightInfrastructure, string rightStableId)
		{
			int num = rightCanonical.CompareTo(leftCanonical);
			if (num != 0)
			{
				return num;
			}
			int num2 = rightInfrastructure.CompareTo(leftInfrastructure);
			if (num2 != 0)
			{
				return num2;
			}
			return StringComparer.Ordinal.Compare(leftStableId ?? string.Empty, rightStableId ?? string.Empty);
		}
	}
	internal static class WarehouseLocalization
	{
		public sealed class TranslationFile
		{
			public string language { get; set; }

			public string language_name { get; set; }

			public string translator { get; set; }

			public Dictionary<string, string> strings { get; set; }
		}

		private static readonly Dictionary<string, Dictionary<string, string>> Languages = new Dictionary<string, Dictionary<string, string>>(StringComparer.OrdinalIgnoreCase);

		private static readonly Dictionary<string, string> Aliases = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

		private static Action<string> _log;

		public static void Initialize(Action<string> log)
		{
			_log = log;
			Languages.Clear();
			Aliases.Clear();
			AddBuiltIn("en", English());
			AddBuiltIn("de", German());
			Aliases["english"] = "en";
			Aliases["eng"] = "en";
			Aliases["german"] = "de";
			Aliases["deutsch"] = "de";
			Aliases["ger"] = "de";
			string text = Path.Combine(Paths.ConfigPath, "ModerWarehouse", "localization");
			Directory.CreateDirectory(text);
			WriteTemplate(Path.Combine(text, "TranslationTemplate.json"));
			foreach (string item in Directory.GetFiles(text, "*.json").OrderBy<string, string>((string x) => x, StringComparer.OrdinalIgnoreCase))
			{
				if (!Path.GetFileName(item).Equals("TranslationTemplate.json", StringComparison.OrdinalIgnoreCase))
				{
					LoadFile(item);
				}
			}
			_log?.Invoke($"Moder Warehouse localization: {Languages.Count} language(s) available: " + string.Join(", ", Languages.Keys.OrderBy((string x) => x)) + ".");
		}

		public static string Resolve(string requested)
		{
			string text = (requested ?? string.Empty).Trim();
			if (Aliases.TryGetValue(text, out var value))
			{
				text = value;
			}
			text = text.Replace('_', '-').ToLowerInvariant();
			if (Languages.ContainsKey(text))
			{
				return text;
			}
			string text2 = text.Split(new char[1] { '-' })[0];
			if (Languages.ContainsKey(text2))
			{
				return text2;
			}
			return "en";
		}

		public static string Text(string language, string key)
		{
			language = Resolve(language);
			if (Languages.TryGetValue(language, out var value) && value.TryGetValue(key, out var value2) && !string.IsNullOrWhiteSpace(value2))
			{
				return value2;
			}
			if (Languages["en"].TryGetValue(key, out value2))
			{
				return value2;
			}
			return "[" + key + "]";
		}

		public static string Format(string language, string key, params object[] args)
		{
			try
			{
				return string.Format(CultureInfo.InvariantCulture, Text(language, key), args);
			}
			catch (FormatException)
			{
				string value;
				return Languages["en"].TryGetValue(key, out value) ? string.Format(CultureInfo.InvariantCulture, value, args) : ("[" + key + "]");
			}
		}

		public static bool StartsWithAnyTranslation(string text, params string[] keys)
		{
			string text2 = (text ?? string.Empty).TrimStart(Array.Empty<char>());
			if (text2.Length == 0 || keys == null || keys.Length == 0)
			{
				return false;
			}
			foreach (Dictionary<string, string> value2 in Languages.Values)
			{
				foreach (string text3 in keys)
				{
					if (!string.IsNullOrWhiteSpace(text3) && value2.TryGetValue(text3, out var value) && !string.IsNullOrWhiteSpace(value))
					{
						int num = value.IndexOfAny(new char[3] { '{', '\n', '\r' });
						string text4 = ((num >= 0) ? value.Substring(0, num) : value).Trim();
						if (text4.Length != 0 && text2.StartsWith(text4, StringComparison.OrdinalIgnoreCase) && (text2.Length == text4.Length || char.IsWhiteSpace(text2[text4.Length]) || text2[text4.Length] == ':'))
						{
							return true;
						}
					}
				}
			}
			return false;
		}

		private static void AddBuiltIn(string code, Dictionary<string, string> strings)
		{
			Languages[code] = strings;
		}

		private static void LoadFile(string path)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				TranslationFile translationFile = new DeserializerBuilder().IgnoreUnmatchedProperties().Build().Deserialize<TranslationFile>(File.ReadAllText(path));
				string text = (translationFile?.language ?? string.Empty).Trim().Replace('_', '-').ToLowerInvariant();
				if (string.IsNullOrWhiteSpace(text) || translationFile.strings == null || translationFile.strings.Count == 0)
				{
					throw new InvalidDataException("'language' and a non-empty 'strings' object are required");
				}
				if (!TranslationGate.IsValidLanguageCode(text))
				{
					throw new InvalidDataException("'" + text + "' is not a valid language code; use a code such as 'es' or 'sr-Latn'");
				}
				if (string.IsNullOrWhiteSpace(translationFile.language_name))
				{
					throw new InvalidDataException("'language_name' is required (for example 'Español')");
				}
				if (text == "en" || text == "de")
				{
					throw new InvalidDataException("'" + text + "' is built in and cannot be replaced by an external file");
				}
				if (Languages.ContainsKey(text))
				{
					throw new InvalidDataException("language code '" + text + "' is already loaded; keep one file per language code");
				}
				if (!string.IsNullOrWhiteSpace(translationFile.language_name) && Aliases.TryGetValue(translationFile.language_name.Trim(), out var value) && !string.Equals(value, text, StringComparison.OrdinalIgnoreCase))
				{
					throw new InvalidDataException("language_name '" + translationFile.language_name + "' is already used by '" + value + "'");
				}
				Dictionary<string, string> dictionary = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
				foreach (KeyValuePair<string, string> @string in translationFile.strings)
				{
					if (!string.IsNullOrWhiteSpace(@string.Key) && @string.Value != null)
					{
						dictionary[@string.Key] = @string.Value;
					}
				}
				TranslationGate.Report report = TranslationGate.Validate(Languages["en"], dictionary);
				if (!report.IsValid)
				{
					throw new InvalidDataException("placeholder mismatch in key(s): " + string.Join(", ", report.InvalidPlaceholders) + "; preserve every {0}, {1}, etc. exactly as in TranslationTemplate.json");
				}
				foreach (string item in report.Unknown)
				{
					dictionary.Remove(item);
				}
				Languages[text] = dictionary;
				Aliases[text] = text;
				if (!string.IsNullOrWhiteSpace(translationFile.language_name))
				{
					Aliases[translationFile.language_name.Trim()] = text;
				}
				_log?.Invoke(string.Format("Loaded warehouse translation '{0}' ({1}/{2} strings) from {3}.", text, dictionary.Count, Languages["en"].Count, Path.GetFileName(path)) + GateSummary(report));
			}
			catch (Exception ex)
			{
				_log?.Invoke("Ignored invalid warehouse translation '" + Path.GetFileName(path) + "': " + ex.Message);
			}
		}

		private static string GateSummary(TranslationGate.Report report)
		{
			List<string> list = new List<string>();
			if (report.Missing.Count > 0)
			{
				list.Add($" {report.Missing.Count} missing/empty key(s) use English fallback: " + string.Join(", ", report.Missing.Take(8)) + ((report.Missing.Count > 8) ? ", …" : string.Empty) + ".");
			}
			if (report.Unknown.Count > 0)
			{
				list.Add($" Ignored {report.Unknown.Count} unknown key(s): " + string.Join(", ", report.Unknown.Take(8)) + ((report.Unknown.Count > 8) ? ", …" : string.Empty) + ".");
			}
			if (list.Count != 0)
			{
				return string.Join(" ", list);
			}
			return " Translation gate: complete; placeholders valid.";
		}

		private static void WriteTemplate(string path)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			TranslationFile translationFile;
			if (File.Exists(path))
			{
				try
				{
					translationFile = new DeserializerBuilder().IgnoreUnmatchedProperties().Build().Deserialize<TranslationFile>(File.ReadAllText(path));
					if (translationFile?.strings == null)
					{
						_log?.Invoke("TranslationTemplate.json was not updated because its strings object could not be read.");
						return;
					}
					int num = 0;
					foreach (KeyValuePair<string, string> item in English())
					{
						if (!translationFile.strings.ContainsKey(item.Key))
						{
							translationFile.strings[item.Key] = item.Value;
							num++;
						}
					}
					if (num == 0)
					{
						return;
					}
					_log?.Invoke($"Updated TranslationTemplate.json with {num} new key(s); existing entries were preserved.");
				}
				catch (Exception ex)
				{
					_log?.Invoke("TranslationTemplate.json was not updated: " + ex.Message);
					return;
				}
			}
			else
			{
				translationFile = new TranslationFile
				{
					language = "xx",
					language_name = "Your language",
					translator = "Your name",
					strings = English()
				};
			}
			WriteTemplateFile(path, translationFile);
		}

		private static void WriteTemplateFile(string path, TranslationFile file)
		{
			StringBuilder stringBuilder = new StringBuilder("{\n  \"language\": \"").Append(JsonEscape(file.language)).Append("\",\n  \"language_name\": \"").Append(JsonEscape(file.language_name))
				.Append("\",\n  \"translator\": \"")
				.Append(JsonEscape(file.translator))
				.Append("\",\n  \"strings\": {\n");
			List<KeyValuePair<string, string>> list = file.strings.OrderBy((KeyValuePair<string, string> x) => x.Key).ToList();
			for (int num = 0; num < list.Count; num++)
			{
				KeyValuePair<string, string> keyValuePair = list[num];
				stringBuilder.Append("    \"").Append(JsonEscape(keyValuePair.Key)).Append("\": \"")
					.Append(JsonEscape(keyValuePair.Value))
					.Append('"');
				if (num + 1 < list.Count)
				{
					stringBuilder.Append(',');
				}
				stringBuilder.AppendLine();
			}
			stringBuilder.Append("  }\n}\n");
			File.WriteAllText(path, stringBuilder.ToString(), new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
		}

		private static string JsonEscape(string value)
		{
			return (value ?? string.Empty).Replace("\\", "\\\\").Replace("\"", "\\\"").Replace("\r", "\\r")
				.Replace("\n", "\\n");
		}

		internal static Dictionary<string, string> English()
		{
			return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
			{
				["help.title"] = "WAREHOUSE HELP",
				["help.in"] = "IN: DEPOSIT ALL",
				["help.req"] = "REQ: WOOD 100",
				["help.out"] = "OUT: COLLECT",
				["help.board"] = "BOARD: E = FILTER",
				["request.title"] = "REQUEST",
				["request.example_item"] = "Wood",
				["request.updating"] = "UPDATING",
				["request.target"] = "TARGET",
				["request.error_example"] = "ERROR: e.g. Tin 20",
				["request.done"] = "DONE {0} x{1}",
				["request.partial"] = "PARTIAL {0} x{1}/{2}",
				["request.output_full"] = "OUTPUT FULL {0}",
				["request.not_found"] = "NOT FOUND {0}",
				["request.wildcard_too_many"] = "TOO MANY MATCHES {0}: {1} (MAX {2})",
				["request.craft_header"] = "CRAFTING {0} x{1}:",
				["request.craft_header_level"] = "CRAFTING {0} LVL {1} x{2}:",
				["request.craft_in_output"] = "IN OUT {0} x{1}",
				["request.craft_unknown"] = "NO RECIPE {0}",
				["request.craft_max_level"] = "MAX LEVEL {0}: {1}",
				["request.error"] = "ERROR: {0}",
				["request.suggestions"] = "AVAILABLE",
				["request.shown_on_board"] = "SEARCH {0}\n{1} HIT(S) ON MAIN BOARD",
				["request.stock_live"] = "LIVE",
				["request.stock_seen"] = "AS OF {0}",
				["request.stock_unknown"] = "UNKNOWN",
				["request.idle"] = "REQ",
				["shopping.title"] = "NEEDED",
				["shopping.fulfilled"] = "TARGETS MET",
				["board.warehouse"] = "WAREHOUSE",
				["board.stock"] = "STOCK",
				["board.page"] = "PAGE",
				["board.chests"] = "CHESTS",
				["board.display_sleeping"] = "DISPLAY\nSLEEPING",
				["board.wake"] = "{0} METRES\nWAKE UP",
				["board.sleep_word"] = "SLEEPMODE",
				["board.expand"] = "EXPAND!",
				["board.deposited"] = "DEPOSITED",
				["board.since_visit"] = "SINCE VISIT",
				["board.rejected"] = "REJECTED AT IN",
				["board.internal_item"] = "INTERNAL / CHEAT ITEM",
				["board.positions"] = "ITEMS",
				["board.no_matches"] = "NO MATCHES",
				["info.free"] = "FREE",
				["info.empty_reserved"] = "EMPTY RESERVED",
				["info.occupied"] = "OCCUPIED",
				["common.auto"] = "AUTO",
				["stats.new"] = "NEW",
				["network.active"] = "NETWORK\nACTIVE",
				["network.local"] = "LOCAL",
				["network.statistics"] = "NETWORK\nSTATISTICS",
				["network.warehouses"] = "WAREHOUSES",
				["network.chests_world"] = "CHESTS WORLD",
				["network.stock_world"] = "STOCK WORLD",
				["network.types_world"] = "ITEM TYPES WORLD",
				["stats.title"] = "STATISTICS",
				["stats.period"] = "PERIOD",
				["stats.today"] = "TODAY",
				["stats.days"] = "{0} DAYS",
				["stats.sleep_word"] = "STATISTICS",
				["stats.input"] = "INPUT",
				["stats.output"] = "OUTPUT",
				["stats.balance"] = "BALANCE",
				["stats.per_day"] = "MOVES / DAY",
				["stats.top_input"] = "TOP INPUT",
				["stats.top_output"] = "TOP OUTPUT",
				["stats.current"] = "CURRENT TOTAL",
				["stats.item_types"] = "ITEM TYPES",
				["stats.chests_now"] = "CHESTS NOW",
				["stats.avg_use"] = "AVG USE",
				["stats.peak"] = "PEAK",
				["stats.low"] = "LOW",
				["stats.rising"] = "RISING",
				["stats.falling"] = "FALLING",
				["stats.empty_first"] = "EMPTY FIRST",
				["stats.ratio"] = "IN / OUT",
				["stats.busy_in"] = "BUSIEST INPUT",
				["stats.busy_out"] = "BUSIEST OUTPUT",
				["stats.tracked_since"] = "TRACKED SINCE",
				["stats.days_tracked"] = "DAYS TRACKED",
				["stats.days_short"] = "DAYS",
				["stats.eta"] = "DAYS LEFT",
				["stats.input_change"] = "INPUT VS 7D",
				["stats.output_change"] = "OUTPUT VS 7D",
				["stats.confidence"] = "DATA BASIS",
				["stats.not_enough"] = "TOO LITTLE DATA",
				["stats.net_day"] = "NET / DAY",
				["stats.date"] = "DATE",
				["stats.amount"] = "AMOUNT",
				["stats.previous"] = "PREVIOUS",
				["stats.control"] = "STAT CONTROL",
				["stats.manual"] = "MANUAL",
				["stats.auto_in"] = "AUTO IN {0}s",
				["stats.page_1"] = "OVERVIEW",
				["stats.page_2"] = "COMPARE",
				["stats.page_3"] = "TREND",
				["stats.page_4"] = "RECORDS"
			};
		}

		internal static Dictionary<string, string> German()
		{
			return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
			{
				["help.title"] = "LAGERHILFE",
				["help.in"] = "IN: ALLES REIN",
				["help.req"] = "REQ: HOLZ 100",
				["help.out"] = "OUT: ABHOLEN",
				["help.board"] = "TAFEL: E = FILTER",
				["request.title"] = "BESTELLUNG",
				["request.example_item"] = "Holz",
				["request.updating"] = "WIRD AKTUALISIERT",
				["request.target"] = "SOLL",
				["request.error_example"] = "FEHLER: z.B. Zinn 20",
				["request.done"] = "FERTIG {0} x{1}",
				["request.partial"] = "TEILWEISE {0} x{1}/{2}",
				["request.output_full"] = "AUSGABE VOLL {0}",
				["request.not_found"] = "NICHT GEFUNDEN {0}",
				["request.wildcard_too_many"] = "ZU VIELE TREFFER {0}: {1} (MAX. {2})",
				["request.craft_header"] = "HERSTELLUNG {0} x{1}:",
				["request.craft_header_level"] = "HERSTELLUNG {0} STUFE {1} x{2}:",
				["request.craft_in_output"] = "IN AUSGABE {0} x{1}",
				["request.craft_unknown"] = "KEIN REZEPT {0}",
				["request.craft_max_level"] = "MAX. STUFE {0}: {1}",
				["request.error"] = "FEHLER: {0}",
				["request.suggestions"] = "VERFÜGBAR",
				["request.shown_on_board"] = "SUCHE {0}\n{1} TREFFER AM LAGERPANEL",
				["request.stock_live"] = "LIVE",
				["request.stock_seen"] = "STAND {0}",
				["request.stock_unknown"] = "UNBEKANNT",
				["request.idle"] = "FRAGE",
				["shopping.title"] = "BEDARF",
				["shopping.fulfilled"] = "SOLL ERFÜLLT",
				["board.warehouse"] = "LAGER",
				["board.stock"] = "BESTAND",
				["board.page"] = "SEITE",
				["board.chests"] = "KISTEN",
				["board.display_sleeping"] = "ANZEIGE\nSCHLÄFT",
				["board.wake"] = "{0} METER\nAUFWECKEN",
				["board.sleep_word"] = "RUHEMODUS",
				["board.expand"] = "AUSBAUEN!",
				["board.deposited"] = "EINGELAGERT",
				["board.since_visit"] = "SEIT BESUCH",
				["board.rejected"] = "BEI IN ABGEWIESEN",
				["board.internal_item"] = "INTERNES / CHEAT-OBJEKT",
				["board.positions"] = "POSITIONEN",
				["board.no_matches"] = "KEINE TREFFER",
				["info.free"] = "FREI",
				["info.empty_reserved"] = "LEER RESERVIERT",
				["info.occupied"] = "BELEGT",
				["common.auto"] = "AUTO",
				["stats.new"] = "NEU",
				["network.active"] = "LAGERNETZ\nAKTIV",
				["network.local"] = "LOKAL",
				["network.statistics"] = "LAGERNETZ\nSTATISTIK",
				["network.warehouses"] = "LAGER",
				["network.chests_world"] = "KISTEN WELT",
				["network.stock_world"] = "BESTAND WELT",
				["network.types_world"] = "ARTEN WELT",
				["stats.title"] = "STATISTIK",
				["stats.period"] = "ZEITRAUM",
				["stats.today"] = "HEUTE",
				["stats.days"] = "{0} TAGE",
				["stats.sleep_word"] = "ZAHLENRUHE",
				["stats.input"] = "EINGANG",
				["stats.output"] = "AUSGANG",
				["stats.balance"] = "SALDO",
				["stats.per_day"] = "BEWEGT / TAG",
				["stats.top_input"] = "TOP EINGANG",
				["stats.top_output"] = "TOP AUSGANG",
				["stats.current"] = "BESTAND TOTAL",
				["stats.item_types"] = "ARTIKELARTEN",
				["stats.chests_now"] = "KISTEN JETZT",
				["stats.avg_use"] = "Ø AUSLASTUNG",
				["stats.peak"] = "HÖCHSTSTAND",
				["stats.low"] = "TIEFSTSTAND",
				["stats.rising"] = "STEIGEND",
				["stats.falling"] = "FALLEND",
				["stats.empty_first"] = "ZUERST LEER",
				["stats.ratio"] = "EIN / AUS",
				["stats.busy_in"] = "MAX EINGANG",
				["stats.busy_out"] = "MAX AUSGANG",
				["stats.tracked_since"] = "ERFASST SEIT",
				["stats.days_tracked"] = "TAGE ERFASST",
				["stats.days_short"] = "TAGE",
				["stats.eta"] = "NOCH TAGE",
				["stats.input_change"] = "EIN VS 7T",
				["stats.output_change"] = "AUS VS 7T",
				["stats.confidence"] = "DATENBASIS",
				["stats.not_enough"] = "ZU WENIG DATEN",
				["stats.net_day"] = "NETTO / TAG",
				["stats.date"] = "DATUM",
				["stats.amount"] = "MENGE",
				["stats.previous"] = "VORPERIODE",
				["stats.control"] = "STAT-STEUERUNG",
				["stats.manual"] = "MANUELL",
				["stats.auto_in"] = "AUTO IN {0}s",
				["stats.page_1"] = "ÜBERSICHT",
				["stats.page_2"] = "VERGLEICH",
				["stats.page_3"] = "TREND",
				["stats.page_4"] = "REKORDE"
			};
		}
	}
	[BepInPlugin("Tie.ModerWarehousePlus", "Moder Warehouse Plus", "1.0.1")]
	[BepInIncompatibility("ch.cris.valheim.moderwarehouse")]
	public sealed class WarehousePlugin : ServersideQoLPluginBase<WarehousePlugin, WarehouseConfig>
	{
		public const string Guid = "Tie.ModerWarehousePlus";

		public const string Name = "Moder Warehouse Plus";

		public const string Version = "1.0.1";

		private const string OriginalGuid = "ch.cris.valheim.moderwarehouse";

		protected override WarehouseConfig CreateConfigSingleton(ConfigFile file, Logger logger)
		{
			CopyOriginalSettings(file, delegate(string x)
			{
				logger.LogInfo((object)x);
			}, delegate(string x)
			{
				logger.LogWarning((object)x);
			});
			return new WarehouseConfig(file, logger);
		}

		internal static void CopyOriginalSettings(ConfigFile file, Action<string> info, Action<string> warning)
		{
			if (Path.GetFileName(file.ConfigFilePath) != "Tie.ModerWarehousePlus.cfg")
			{
				return;
			}
			try
			{
				if (CopyIfMissing(Path.Combine(Paths.ConfigPath, "ch.cris.valheim.moderwarehouse.cfg"), file.ConfigFilePath))
				{
					file.Reload();
					info("Settings copied from ch.cris.valheim.moderwarehouse.cfg to Tie.ModerWarehousePlus.cfg.");
				}
			}
			catch (IOException ex)
			{
				warning("Could not copy the settings from ch.cris.valheim.moderwarehouse.cfg: " + ex.Message);
			}
		}

		internal static bool CopyIfMissing(string source, string destination)
		{
			if (File.Exists(destination) || !File.Exists(source))
			{
				return false;
			}
			Directory.CreateDirectory(Path.GetDirectoryName(destination));
			File.Copy(source, destination);
			return true;
		}

		protected override void RegisterProcessors(IProcessorCollection processors)
		{
			processors.Add<WarehouseProcessor>();
		}
	}
	[Processor("1f3823e7-1db0-4857-b8b8-e377bd8cd191")]
	[RunAfter<ContainerRegistryProcessor>]
	public sealed class WarehouseProcessor : Processor<ProcessorPrefabInfo<Sign>>
	{
		private sealed class PersistedContainerSpatialLookup
		{
			private readonly SpatialLookup<ZDO> lookup;

			private readonly HashSet<ZDO> checkedObjects = new HashSet<ZDO>();

			private readonly HashSet<ZDO> containers = new HashSet<ZDO>();

			internal PersistedContainerSpatialLookup(List<ZDO> objects)
			{
				lookup = new SpatialLookup<ZDO>(objects, (ZDO zdo) => zdo.GetPosition().x, (ZDO zdo) => zdo.GetPosition().y, (ZDO zdo) => zdo.GetPosition().z);
			}

			internal ZDO Nearest(ZDO sign, float range2)
			{
				//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_000d: 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_0019: Unknown result type (might be due to invalid IL or missing references)
				Vector3 position = sign.GetPosition();
				return lookup.Nearest(position.x, position.y, position.z, range2, IsContainer, preferLastOnTie: true);
			}

			private bool IsContainer(ZDO candidate)
			{
				//IL_0034: Unknown result type (might be due to invalid IL or missing references)
				//IL_003f: Expected O, but got Unknown
				//IL_0052: Unknown result type (might be due to invalid IL or missing references)
				//IL_005d: Expected O, but got Unknown
				if (containers.Contains(candidate))
				{
					return true;
				}
				if (checkedObjects.Contains(candidate))
				{
					return false;
				}
				checkedObjects.Add(candidate);
				ZNetScene instance = ZNetScene.instance;
				GameObject val = (((Object)instance != (Object)null) ? instance.GetPrefab(candidate.GetPrefab()) : null);
				if ((Object)(object)(((Object)val != (Object)null) ? val.GetComponent<Container>() : null) == (Object)null)
				{
					return false;
				}
				containers.Add(candidate);
				return true;
			}
		}

		private sealed class Request
		{
			public ServersideQoLZDO Sign { get; }

			public string Payload { get; }

			public Request(ServersideQoLZDO sign, string payload)
			{
				Sign = sign;
				Payload = payload;
			}
		}

		private sealed class RequestSuggestion
		{
			public string Token { get; set; }

			public string Name { get; set; }

			public int Score { get; set; }

			public Dictionary<string, int> Amounts { get; } = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);

			public Dictionary<string, string> Statuses { get; } = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
		}

		private sealed class RequestSearchNotice
		{
			public string Query { get; }

			public List<RequestSuggestion> Matches { get; }

			public DateTime StartedAt { get; }

			public DateTime Until { get; }

			public RequestSearchNotice(string query, List<RequestSuggestion> matches, DateTime startedAt, DateTime until)
			{
				Query = query;
				Matches = matches;
				StartedAt = startedAt;
				Until = until;
			}
		}

		private sealed class PersistedSign
		{
			public ZDO Zdo { get; }

			public string Text { get; }

			public PersistedSign(ZDO zdo, string text)
			{
				Zdo = zdo;
				Text = text;
			}
		}

		private sealed class PersistedContainer
		{
			public ZDO Zdo { get; }

			public List<ItemData> Items { get; }

			public PersistedContainer(ZDO zdo, List<ItemData> items)
			{
				Zdo = zdo;
				Items = items ?? new List<ItemData>();
			}
		}

		private sealed class PersistedWarehouse
		{
			public string Id { get; }

			public ZDO Sign { get; }

			public ZDO Input { get; }

			public float Radius { get; }

			public PersistedWarehouse(string id, ZDO sign, ZDO input, float radius)
			{
				Id = id;
				Sign = sign;
				Input = input;
				Radius = radius;
			}
		}

		private sealed class PersistedInputCandidate
		{
			public string Id { get; }

			public ZDO Sign { get; }

			public ZDO Input { get; }

			public int Infrastructure { get; }

			public bool Canonical { get; }

			public PersistedInputCandidate(string id, ZDO sign, ZDO input, int infrastructure, bool canonical)
			{
				Id = id;
				Sign = sign;
				Input = input;
				Infrastructure = infrastructure;
				Canonical = canonical;
			}
		}

		private sealed class WarehouseNode
		{
			public string Id { get; }

			public ServersideQoLZDO Sign { get; }

			public ServersideQoLZDO Input { get; }

			public float Radius { get; }

			public string Language { get; }

			public bool Valid { get; set; } = true;

			public string Error { get; set; } = string.Empty;

			public WarehouseNode(string id, ServersideQoLZDO sign, ServersideQoLZDO input, float radius, string language)
			{
				Id = id;
				Sign = sign;
				Input = input;
				Radius = radius;
				Language = language;
			}
		}

		private sealed class NetworkOverview
		{
			public string Signature { get; }

			public List<BoardMessage> Rows { get; }

			public NetworkOverview(string signature, List<BoardMessage> rows)
			{
				Signature = signature;
				Rows = rows;
			}
		}

		private sealed class PerfWindow
		{
			public DateTime Since { get; set; }

			public int Cycles { get; set; }

			public int Skipped { get; set; }

			public int AwakeCycles { get; set; }

			public int SleepingCycles { get; set; }

			public long TotalMs { get; set; }

			public long MaxMs { get; set; }

			public long TopologyMs { get; set; }

			public long ClassifyMs { get; set; }

			public long DisplayMs { get; set; }

			public long TransferMs { get; set; }

			public int Containers { get; set; }

			public int Signs { get; set; }

			public int Stores { get; set; }

			public int InventoryReads { get; set; }

			public int Peers { get; set; }

			public int PingAverage { get; set; }

			public int PingMaximum { get; set; }

			public float LocalQualityMinimum { get; set; } = 1f;

			public float RemoteQualityMinimum { get; set; } = 1f;

			public float OutBytesPerSecond { get; set; }

			public float InBytesPerSecond { get; set; }
		}

		private sealed class PendingLabel
		{
			public string ItemType { get; }

			public DateTime Since { get; }

			public PendingLabel(string itemType, DateTime since)
			{
				ItemType = itemType;
				Since = since;
			}
		}

		private sealed class StockEntry
		{
			public string Token { get; }

			public string Name { get; }

			public int Amount { get; }

			public StockEntry(string token, string name, int amount)
			{
				Token = token;
				Name = name;
				Amount = amount;
			}
		}

		private sealed class BoardControl
		{
			public string Prefix { get; set; }

			public int? Page { get; set; }

			public DateTime ChangedAt { get; set; }
		}

		private sealed class StatsControl
		{
			public int? Days { get; set; }

			public int? Page { get; set; }

			public DateTime ChangedAt { get; set; }
		}

		private sealed class TargetStatus
		{
			public string Name { get; }

			public int Current { get; }

			public int Target { get; }

			public TargetStatus(string name, int current, int target)
			{
				Name = name;
				Current = current;
				Target = target;
			}
		}

		private sealed class DeliveryNotice
		{
			public Dictionary<string, int> Items { get; }

			public DateTime Until { get; }

			public DeliveryNotice(Dictionary<string, int> items, DateTime until)
			{
				Items = items;
				Until = until;
			}
		}

		private sealed class BoardVisitState
		{
			public bool Awake { get; set; }

			public Dictionary<string, StockEntry> Baseline { get; set; } = new Dictionary<string, StockEntry>(StringComparer.OrdinalIgnoreCase);

			public List<StockChange> Changes { get; set; } = new List<StockChange>();

			public DateTime SummaryUntil { get; set; }
		}

		private sealed class StockChange
		{
			public string Name { get; }

			public int Amount { get; }

			public StockChange(string name, int amount)
			{
				Name = name;
				Amount = amount;
			}
		}

		private sealed class BoardMessage
		{
			public string Name { get; }

			public string Value { get; }

			public string Color { get; }

			public BoardMessage(string name, string value, string color)
			{
				Name = name;
				Value = value;
				Color = color;
			}
		}

		private sealed class CraftTarget
		{
			public string Name;

			public string Token;

			public ItemData Item;

			public Recipe Recipe;

			public Requirement[] Resources;

			public int MaxLevel;

			public int Priority;

			public HashSet<string> Aliases;

			public string DisplayName(string language)
			{
				if (Item != null)
				{
					return LocalizedItemName(Item, language);
				}
				if (!((WarehouseLocalization.Resolve(language) == "de") ? PieceGermanNames : PieceEnglishNames).TryGetValue(Token ?? string.Empty, out var value) || string.IsNullOrWhiteSpace(value))
				{
					return Name;
				}
				return value;
			}
		}

		internal static readonly Regex Marker = new Regex("^\\s*\\[WH:(?<id>[A-Za-z0-9_-]+):(?<role>IN|OUT|REQ|INFO|BOARD|STATS)\\](?<payload>[\\s\\S]*)$", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.CultureInvariant);

		internal static readonly Regex IgnoreMarker = new Regex("^\\s*\\[WH:IGNORE\\]\\s*$", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.CultureInvariant);

		internal static readonly Regex DeleteMarker = new Regex("^\\s*\\[WH:DELETE\\]\\s*$", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.CultureInvariant);

		internal static readonly Regex CleanMarker = new Regex("^\\s*\\[WH:CLEAN\\]\\s*$", RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.CultureInvariant);

		private const string RequestWarehouseKey = "ModerWarehouse.RequestWarehouse";

		private const string InfoWarehouseKey = "ModerWarehouse.InfoWarehouse";

		private const string BoardWarehouseKey = "ModerWarehouse.BoardWarehouse";

		private const string BoardGroupKey = "ModerWarehouse.BoardGroup";

		private const string BoardCellKey = "ModerWarehouse.BoardCell";

		private const string BoardRenderedTextKey = "ModerWarehouse.BoardRenderedText";

		private const string StatsWarehouseKey = "ModerWarehouse.StatsWarehouse";

		private const string StatsGroupKey = "ModerWarehouse.StatsGroup";

		private const string StatsCellKey = "ModerWarehouse.StatsCell";

		private const string StatsHistoryKey = "ModerWarehouse.StatsHistory";

		private const string StatsControlKey = "ModerWarehouse.StatsControl";

		private const string RequestRenderedTextKey = "ModerWarehouse.RequestRenderedText";

		private const string RequestTargetsKey = "ModerWarehouse.RequestTargets";

		private const string HelpGroupKey = "ModerWarehouse.HelpGroup";

		private const string HelpChildKey = "ModerWarehouse.HelpChild";

		private const string HelpWarehouseKey = "ModerWarehouse.HelpWarehouse";

		private const int BoardColumns = 4;

		private const int BoardRows = 4;

		private const int BoardCellCount = 16;

		private const int BoardPageSize = 12;

		private const int BoardAnchorCell = 12;

		private const int StatsColumns = 4;

		private const int StatsRows = 4;

		private const int StatsCellCount = 16;

		private const int StatsAnchorCell = 12;

		private const string AutoChestXKey = "ModerWarehouse.AutoChestX";

		private const string AutoChestYKey = "ModerWarehouse.AutoChestY";

		private const string AutoChestZKey = "ModerWarehouse.AutoChestZ";

		private const string AutoLabelItemKey = "ModerWarehouse.AutoLabelItem";

		private const string AutoLabelManagedKey = "ModerWarehouse.AutoLabelManaged";

		private const string InputRadiusKey = "ModerWarehouse.InputRadius";

		private const string InputWarehouseKey = "ModerWarehouse.InputWarehouse";

		private const string InputCanonicalKey = "ModerWarehouse.InputCanonical";

		private const float GroupRecoveryDistance = 50f;

		private readonly HashSet<ServersideQoLZDO> _signs = new HashSet<ServersideQoLZDO>();

		private static readonly Dictionary<string, string> GermanNames = new Dictionary<string, string>();

		private static readonly Dictionary<string, string> GermanDisplayNames = new Dictionary<string, string>();

		private static readonly Dictionary<string, string> EnglishNames = new Dictionary<string, string>();

		private static readonly Dictionary<string, string> EnglishDisplayNames = new Dictionary<string, string>();

		private static readonly Dictionary<string, string> PrefabNames = new Dictionary<string, string>();

		internal static readonly Dictionary<string, HashSet<string>> AliasOwners = new Dictionary<string, HashSet<string>>(StringComparer.Ordinal);

		private static bool _germanNamesLoaded;

		private SectorDictionary<HashSet<ServersideQoLZDO>> _containers;

		private readonly Dictionary<ServersideQoLZDO, PendingLabel> _pendingLabels = new Dictionary<ServersideQoLZDO, PendingLabel>();

		private readonly Dictionary<ServersideQoLZDO, string> _labelItemTokens = new Dictionary<ServersideQoLZDO, string>();

		private readonly Dictionary<ServersideQoLZDO, DateTime> _requestClearAt = new Dictionary<ServersideQoLZDO, DateTime>();

		private readonly Dictionary<string, DateTime> _orphanBoardSeenAt = new Dictionary<string, DateTime>(StringComparer.Ordinal);

		private readonly Dictionary<string, BoardControl> _boardControls = new Dictionary<string, BoardControl>(StringComparer.Ordinal);

		private readonly Dictionary<ServersideQoLZDO, DateTime> _helpPendingAt = new Dictionary<ServersideQoLZDO, DateTime>();

		private readonly Dictionary<string, DateTime> _boardEnsurePendingAt = new Dictionary<string, DateTime>(StringComparer.Ordinal);

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

		private readonly Dictionary<string, DateTime> _statsEnsurePendingAt = new Dictionary<string, DateTime>(StringComparer.Ordinal);

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

		private readonly Dictionary<string, StatsControl> _statsControls = new Dictionary<string, StatsControl>(StringComparer.Ordinal);

		private readonly Dictionary<string, DeliveryNotice> _deliveryNotices = new Dictionary<string, DeliveryNotice>(StringComparer.OrdinalIgnoreCase);

		private readonly Dictionary<string, BoardVisitState> _boardVisits = new Dictionary<string, BoardVisitState>(StringComparer.Ordinal);

		private NetworkOverview _networkOverview;

		private DateTime _networkOverviewAt;

		private bool _networkOverviewDirty = true;

		private readonly Dictionary<string, DateTime> _warehouseNextRun = new Dictionary<string, DateTime>(StringComparer.OrdinalIgnoreCase);

		private readonly HashSet<string> _warehousesRunning = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private readonly HashSet<string> _warehousesDirty = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private readonly Dictionary<string, WarehouseNode> _knownNodes = new Dictionary<string, WarehouseNode>(StringComparer.OrdinalIgnoreCase);

		private readonly Dictionary<string, bool> _lastAwake = new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase);

		private readonly Dictionary<string, DateTime> _lastCompleted = new Dictionary<string, DateTime>(StringComparer.OrdinalIgnoreCase);

		private readonly Dictionary<List<ServersideQoLZDO>, SpatialLookup<ServersideQoLZDO>> _spatialLookups = new Dictionary<List<ServersideQoLZDO>, SpatialLookup<ServersideQoLZDO>>();

		private List<ServersideQoLZDO> _storageSigns;

		private HashSet<ServersideQoLZDO> _localStorage;

		private readonly Dictionary<string, DateTime> _slowCycleLoggedAt = new Dictionary<string, DateTime>(StringComparer.OrdinalIgnoreCase);

		private readonly Dictionary<string, DateTime> _slowSampleAt = new Dictionary<string, DateTime>(StringComparer.OrdinalIgnoreCase);

		private readonly Dictionary<string, DateTime> _transferDiagnosticAt = new Dictionary<string, DateTime>(StringComparer.OrdinalIgnoreCase);

		private readonly Dictionary<string, PerfWindow> _performanceWindows = new Dictionary<string, PerfWindow>(StringComparer.OrdinalIgnoreCase);

		private readonly Dictionary<string, DateTime> _boardAwakeUntil = new Dictionary<string, DateTime>(StringComparer.OrdinalIgnoreCase);

		private readonly Dictionary<string, Dictionary<string, int>> _warehouseStockSnapshots = new Dictionary<string, Dictionary<string, int>>(StringComparer.OrdinalIgnoreCase);

		private readonly Dictionary<string, DateTime> _warehouseStockTimes = new Dictionary<string, DateTime>(StringComparer.OrdinalIgnoreCase);

		private readonly HashSet<string> _freshlyReadWarehouses = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

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

		private DateTime _globalInputValidationAt;

		private string _validatedTopology;

		private readonly Dictionary<string, RequestSearchNotice> _requestSearchNotices = new Dictionary<string, RequestSearchNotice>(StringComparer.OrdinalIgnoreCase);

		private Dictionary<ServersideQoLZDO, IInventory> _cycleInventories;

		private readonly Dictionary<ServersideQoLZDO, IInventory> _inventoryIndex = new Dictionary<ServersideQoLZDO, IInventory>();

		private string _performanceLogPath;

		private string _stockCachePath;

		private bool _performanceLogDisabled;

		private int _cycleContainers;

		private int _cycleSigns;

		private int _cycleStores;

		private int _cycleInventoryReads;

		private long _cycleTopologyMs;

		private long _cycleClassifyMs;

		private long _cycleDisplayMs;

		private long _cycleTransferMs;

		private int _cyclePeers;

		private int _cyclePingAverage;

		private int _cyclePingMaximum;

		private float _cycleLocalQualityMinimum;

		private float _cycleRemoteQualityMinimum;

		private float _cycleOutBytesPerSecond;

		private float _cycleInBytesPerSecond;

		private readonly Dictionary<ZDOID, DateTime> _inputInUseSince = new Dictionary<ZDOID, DateTime>();

		internal const int MaxWildcardItems = 10;

		private static readonly Dictionary<int, bool> SignPrefabs = new Dictionary<int, bool>();

		private static readonly HashSet<IInventory> PendingInventorySaves = new HashSet<IInventory>();

		private static readonly Dictionary<string, string> PieceEnglishNames = new Dictionary<string, string>();

		private static readonly Dictionary<string, string> PieceGermanNames = new Dictionary<string, string>();

		private const float HelpSignRange = 2.5f;

		private const string ChestRequestKey = "ModerWarehouse.ChestRequestWarehouse";

		private const string ChestInfoKey = "ModerWarehouse.ChestInfoWarehouse";

		private const string ChestTargetsKey = "ModerWarehouse.ChestRequestTargets";

		private const float ChestSignRange = 1.6f;

		private static readonly List<ZDO> WorldLookupBuffer = new List<ZDO>();

		private static readonly Dictionary<int, bool> ContainerPrefabs = new Dictionary<int, bool>();

		private readonly HashSet<ServersideQoLZDO> _chestLinkChecked = new HashSet<ServersideQoLZDO>();

		private const string ContainerSignsGuid = "ArgusMagnus.ServersideQoL.ContainerSigns";

		private IEnumerable<ServersideQoLZDO> StorageSigns
		{
			get
			{
				IEnumerable<ServersideQoLZDO> storageSigns = _storageSigns;
				return storageSigns ?? _signs;
			}
		}

		private static string DataDirectory => Path.Combine(Paths.ConfigPath, "ModerWarehousePlus");

		private static string OriginalStockCachePath => Path.Combine(Paths.PluginPath, "ModerWarehouse", "ModerWarehouse-stock-cache.tsv");

		private static bool ContainerSignsLoaded => Chainloader.PluginInfos.ContainsKey("ArgusMagnus.ServersideQoL.ContainerSigns");

		protected override void Initialize()
		{
			_signs.Clear();
			_pendingLabels.Clear();
			_requestClearAt.Clear();
			_orphanBoardSeenAt.Clear();
			_boardControls.Clear();
			_helpPendingAt.Clear();
			_boardEnsurePendingAt.Clear();
			_boardReadyGroups.Clear();
			_statsEnsurePendingAt.Clear();
			_statsReadyGroups.Clear();
			_statsControls.Clear();
			_deliveryNotices.Clear();
			_boardVisits.Clear();
			_networkOverview = null;
			_networkOverviewDirty = true;
			_warehouseNextRun.Clear();
			_warehousesRunning.Clear();
			_warehousesDirty.Clear();
			_knownNodes.Clear();
			_validatedTopology = null;
			_lastAwake.Clear();
			_lastCompleted.Clear();
			_spatialLookups.Clear();
			_storageSigns = null;
			_localStorage = null;
			_slowCycleLoggedAt.Clear();
			_slowSampleAt.Clear();
			_performanceWindows.Clear();
			_boardAwakeUntil.Clear();
			_warehouseStockSnapshots.Clear();
			_warehouseStockTimes.Clear();
			_freshlyReadWarehouses.Clear();
			_globalInputErrors.Clear();
			_globalInputValidationAt = DateTime.MinValue;
			_requestSearchNotices.Clear();
			_inventoryIndex.Clear();
			InitializePerformanceLog();
			InitializeStockCache();
			ContainerRegistryProcessor val = Processor.Instance<ContainerRegistryProcessor>();
			_containers = val.GetContainers(100f);
			val.ContainerChanged -= OnContainerChanged;
			val.ContainerChanged += OnContainerChanged;
			WarehouseLocalization.Initialize(delegate(string message)
			{
				((Processor)this).Logger.LogInfo((object)message);
			});
			LoadGermanNames();
			((Processor)this).Logger.LogInfo((object)("Moder Warehouse Plus 1.0.1: localization, configurable capacity alerts, player-safe input ownership, solo-friendly visit deltas, target stock shopping list, delivery confirmation, automatic IN help sign, interactive rotating 4x4 stock board, rolling 4x4 statistics board, persistent linked automatic signs, safe transfers and multi-item withdrawal initialized; " + $"{GermanNames.Count} EN/DE item names loaded."));
			((Processor)this).Logger.LogInfo((object)"Performance revision spatial-local-20260923: local labels, spatial links, coalesced scheduling, on-demand world reads.");
			((Processor)this).Logger.LogInfo((object)"Local patch 10: craft accepts * wildcards; patch 9: ModerWarehouse owns chest signs inside a warehouse (ContainerSigns contents lists are adopted as labels, 'Item 123' labels mean Item); patch 8: one save per chest per cycle; patch 7: faster world scans; patch 6: craft also covers hammer/hoe/cultivator pieces; patch 5: REQ/INFO links and help sign survive ContainerSigns sign recreation, craft REQ (craft <item> [lvl N] [xN]), wildcard REQ (*), sign-only IN/OUT marker scan, stale IN lock release (owner-based).");
		}

		private void OnContainerChanged(ServersideQoLZDO changed, ContainerState state)
		{
			_networkOverviewDirty = true;
			if (changed != null)
			{
				_inventoryIndex.Remove(changed);
				_cycleInventories?.Remove(changed);
			}
			HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			foreach (ServersideQoLZDO sign in _signs)
			{
				string text = WarehouseId(sign);
				if (!string.IsNullOrWhiteSpace(text) && hashSet.Add(text) && (changed == null || !_knownNodes.TryGetValue(text, out var value) || !(Distance2(changed, value.Input) > value.Radius * value.Radius)) && _warehousesDirty.Add(text))
				{
					((Processor)this).ScheduleReprocessing(sign, 0.5f);
				}
			}
		}

		private void InitializePerformanceLog()
		{
			_performanceLogDisabled = false;
			string dataDirectory = DataDirectory;
			_performanceLogPath = Path.Combine(dataDirectory, "ModerWarehouse-performance.log");
			try
			{
				Directory.CreateDirectory(dataDirectory);
				File.WriteAllText(_performanceLogPath, "Moder Warehouse Plus 1.0.1 performance log\r\n" + $"Started UTC: {WarehouseClock.UtcNow:O}\r\n" + "Revision: spatial-local-20260923; SUMMARY phase times are independent maxima; SLOW sampled at most once per 10s; skipped means throttled callbacks, not failed work.\r\nUTC | kind | warehouse | state | cycles | skipped | avg_ms | max_ms | topology_ms | classify_ms | display_ms | transfer_ms | containers | signs | stores | inventory_reads | peers | ping_avg_ms | ping_max_ms | local_quality_min | remote_quality_min | out_Bps | in_Bps\r\n", Encoding.UTF8);
				DeleteLegacyLog(Path.Combine(Paths.BepInExRootPath, "ModerWarehouse-performance.log"), "performance");
				((Processor)this).Logger.LogInfo((object)("Performance log reset: " + _performanceLogPath));
			}
			catch (Exception ex)
			{
				_performanceLogDisabled = true;
				((Processor)this).Logger.LogWarning((object)("Could not create warehouse performance log: " + ex.Message));
			}
		}

		private void InitializeStockCache()
		{
			string dataDirectory = DataDirectory;
			_stockCachePath = Path.Combine(dataDirectory, "ModerWarehouse-stock-cache.tsv");
			try
			{
				Directory.CreateDirectory(dataDirectory);
				if (WarehousePlugin.CopyIfMissing(OriginalStockCachePath, _stockCachePath))
				{
					((Processor)this).Logger.LogInfo((object)("Stock cache copied from " + OriginalStockCachePath + "."));
				}
				if (!File.Exists(_stockCachePath))
				{
					return;
				}
				string[] array = File.ReadAllLines(_stockCachePath, Encoding.UTF8);
				for (int i = 0; i < array.Length; i++)
				{
					string[] array2 = array[i].Split(new char[1] { '\t' });
					if (array2.Length >= 3 && int.TryParse(array2[2], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) && result > 0)
					{
						string key = Encoding.UTF8.GetString(Convert.FromBase64String(array2[0]));
						string key2 = Encoding.UTF8.GetString(Convert.FromBase64String(array2[1]));
						if (!_warehouseStockSnapshots.TryGetValue(key, out var value))
						{
							value = new Dictionary<string, int>(StringComparer.Ordinal);
							_warehouseStockSnapshots[key] = value;
						}
						value[key2] = result;
						if (array2.Length >= 4 && DateTime.TryParse(array2[3], CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal, out var result2))
						{
							_warehouseStockTimes[key] = result2.ToUniversalTime();
						}
					}
				}
				((Processor)this).Logger.LogInfo((object)$"Loaded last-known stock for {_warehouseStockSnapshots.Count} warehouse(s).");
			}
			catch (Exception ex)
			{
				((Processor)this).Logger.LogWarning((object)("Could not read warehouse stock cache: " + ex.Message));
			}
		}

		private void SaveStockCache()
		{
			if (string.IsNullOrWhiteSpace(_stockCachePath))
			{
				return;
			}
			try
			{
				string text = _stockCachePath + ".tmp";
				DateTime value;
				IEnumerable<string> contents = _warehouseStockSnapshots.OrderBy<KeyValuePair<string, Dictionary<string, int>>, string>((KeyValuePair<string, Dictionary<string, int>> pair) => pair.Key, StringComparer.OrdinalIgnoreCase).SelectMany((KeyValuePair<string, Dictionary<string, int>> warehouse) => warehouse.Value.Where((KeyValuePair<string, int> stock) => stock.Value > 0).OrderBy<KeyValuePair<string, int>, string>((KeyValuePair<string, int> stock) => stock.Key, StringComparer.Ordinal).Select(delegate(KeyValuePair<string, int> stock)
				{
					string[] obj = new string[7]
					{
						Convert.ToBase64String(Encoding.UTF8.GetBytes(warehouse.Key)),
						"\t",
						null,
						null,
						null,
						null,
						null
					};
					Encoding uTF = Encoding.UTF8;
					KeyValuePair<string, int> keyValuePair = stock;
					obj[2] = Convert.ToBase64String(uTF.GetBytes(keyValuePair.Key));
					obj[3] = "\t";
					keyValuePair = stock;
					obj[4] = keyValuePair.Value.ToString(CultureInfo.InvariantCulture);
					obj[5] = "\t";
					obj[6] = (_warehouseStockTimes.TryGetValue(warehouse.Key, out value) ? value.ToUniversalTime().ToString("O", CultureInfo.InvariantCulture) : string.Empty);
					return string.Concat(obj);
				}));
				File.WriteAllLines(text, contents, Encoding.UTF8);
				if (File.Exists(_stockCachePath))
				{
					File.Delete(_stockCachePath);
				}
				File.Move(text, _stockCachePath);
			}
			catch (Exception ex)
			{
				((Processor)this).Logger.LogWarning((object)("Could not save warehouse stock cache: " + ex.Message));
			}
		}

		private static bool StockEqual(Dictionary<string, int> left, Dictionary<string, int> right)
		{
			if (left == right)
			{
				return true;
			}
			if (left == null || right == null || left.Count != right.Count)
			{
				return false;
			}
			int value;
			return left.All((KeyValuePair<string, int> pair) => right.TryGetValue(pair.Key, out value) && value == pair.Value);
		}

		private static string WarehouseId(ServersideQoLZDO sign)
		{
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			string[] array = new string[4] { "ModerWarehouse.RequestWarehouse", "ModerWarehouse.InfoWarehouse", "ModerWarehouse.BoardWarehouse", "ModerWarehouse.StatsWarehouse" };
			foreach (string text in array)
			{
				string text2 = sign.ZDO.GetString(text, string.Empty);
				if (!string.IsNullOrWhiteSpace(text2))
				{
					return text2.Trim();
				}
			}
			Regex marker = Marker;
			ZDOVars vars = sign.Vars;
			Match match = marker.Match(PlainText(((ZDOVars)(ref vars)).GetText("")));
			if (!match.Success)
			{
				return string.Empty;
			}
			return match.Groups["id"].Value.Trim();
		}

		protected override bool ClaimExclusive(ServersideQoLZDO zdo)
		{
			return false;
		}

		protected override ProcessResult Process(ServersideQoLZDO zdo, IReadOnlyList<Peer> peers, ProcessorPrefabInfo<Sign> prefabInfo)
		{
			//IL_0055: 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_0b81: Unknown result type (might be due to invalid IL or missing references)
			//IL_082c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0d6c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0fb3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0979: Unknown result type (might be due to invalid IL or missing references)
			//IL_0f4d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0946: Unknown result type (might be due to invalid IL or missing references)
			//IL_0b4e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0b12: Unknown result type (might be due to invalid IL or missing references)
			//IL_0a64: Unknown result type (might be due to invalid IL or missing references)
			zdo.Fields<Piece>().Set((Func<Expression<Func<Piece, bool>>>)(() => (Piece x) => x.m_canBeRemoved), true, "/root/project/ModerWarehouse/WarehouseProcessor.cs", 855);
			ZDOVars vars = zdo.Vars;
			string text = PlainText(((ZDOVars)(ref vars)).GetText(""));
			if (CleanMarker.IsMatch(text))
			{
				List<ServersideQoLZDO> list = _signs.Where((ServersideQoLZDO other) => other != zdo && PlacedByWarehouse(other) && Distance2(other, zdo) < 0.0025f).ToList();
				foreach (ServersideQoLZDO item in list)
				{
					item.Destroy();
				}
				_signs.Remove(zdo);
				((Processor)this).Logger.LogInfo((object)$"Cleaned warehouse sign stack: removed {list.Count + 1} sign(s).");
				return (ProcessResult)4;
			}
			if (DeleteMarker.IsMatch(text))
			{
				string boardGroup = zdo.ZDO.GetString("ModerWarehouse.BoardGroup", string.Empty);
				if (!string.IsNullOrWhiteSpace(boardGroup))
				{
					foreach (ServersideQoLZDO item2 in _signs.Where((ServersideQoLZDO x) => x.ZDO.GetString("ModerWarehouse.BoardGroup", string.Empty) == boardGroup).ToList())
					{
						_signs.Remove(item2);
						item2.Destroy();
					}
					((Processor)this).Logger.LogInfo((object)("Deleted warehouse board group '" + boardGroup + "'."));
					return (ProcessResult)4;
				}
				string statsGroup = zdo.ZDO.GetString("ModerWarehouse.StatsGroup", string.Empty);
				if (!string.IsNullOrWhiteSpace(statsGroup))
				{
					foreach (ServersideQoLZDO item3 in _signs.Where((ServersideQoLZDO x) => x.ZDO.GetString("ModerWarehouse.StatsGroup", string.Empty) == statsGroup).ToList())
					{
						_signs.Remove(item3);
						item3.Destroy();
					}
					((Processor)this).Logger.LogInfo((object)("Deleted warehouse statistics group '" + statsGroup + "'."));
					return (ProcessResult)4;
				}
				_signs.Remove(zdo);
				((Processor)this).Logger.LogInfo((object)"Deleted warehouse sign marked [WH:DELETE].");
				return (ProcessResult)4;
			}
			bool num = _signs.Add(zdo);
			if (num)
			{
				zdo.Destroyed += delegate(ServersideQoLZDO destroyed)
				{
					_signs.Remove(destroyed);
				};
			}
			Match match = Marker.Match(text);
			if (num && match.Success)
			{
				_warehousesDirty.Add(match.Groups["id"].Value);
			}
			if (!match.Success)
			{
				RestoreChestSignLinks(zdo, text);
			}
			string text2 = zdo.ZDO.GetString("ModerWarehouse.RequestWarehouse", string.Empty);
			string text3 = zdo.ZDO.GetString("ModerWarehouse.InfoWarehouse", string.Empty);
			string text4 = zdo.ZDO.GetString("ModerWarehouse.BoardWarehouse", string.Empty);
			string text5 = zdo.ZDO.GetString("ModerWarehouse.StatsWarehouse", string.Empty);
			if (match.Success && match.Groups["role"].Value.Equals("REQ", StringComparison.OrdinalIgnoreCase))
			{
				text2 = match.Groups["id"].Value;
				zdo.ZDO.Set("ModerWarehouse.RequestWarehouse", text2);
				SaveChestSignLinks(zdo);
			}
			if (match.Success && match.Groups["role"].Value.Equals("INFO", StringComparison.OrdinalIgnoreCase))
			{
				text3 = match.Groups["id"].Value;
				zdo.ZDO.Set("ModerWarehouse.InfoWarehouse", text3);
				SaveChestSignLinks(zdo);
			}
			if (match.Success && match.Groups["role"].Value.Equals("BOARD", StringComparison.OrdinalIgnoreCase))
			{
				text4 = match.Groups["id"].Value;
				zdo.ZDO.Set("ModerWarehouse.BoardWarehouse", text4);
				if (string.IsNullOrWhiteSpace(zdo.ZDO.GetString("ModerWarehouse.BoardGroup", string.Empty)))
				{
					zdo.ZDO.Set("ModerWarehouse.BoardGroup", Guid.NewGuid().ToString("N"));
				}
				zdo.ZDO.Set("ModerWarehouse.BoardCell", 12);
				EnsureBoard(zdo, text4);
			}
			if (match.Success && match.Groups["role"].Value.Equals("STATS", StringComparison.OrdinalIgnoreCase))
			{
				text5 = match.Groups["id"].Value;
				zdo.ZDO.Set("ModerWarehouse.StatsWarehouse", text5);
				if (string.IsNullOrWhiteSpace(zdo.ZDO.GetString("ModerWarehouse.StatsGroup", string.Empty)))
				{
					zdo.ZDO.Set("ModerWarehouse.StatsGroup", Guid.NewGuid().ToString("N"));
				}
				zdo.ZDO.Set("ModerWarehouse.StatsCell", 12);
				EnsureStatsBoard(zdo, text5);
			}
			if (match.Success && match.Groups["role"].Value.Equals("IN", StringComparison.OrdinalIgnoreCase))
			{
				string text6 = match.Groups["id"].Value.Trim();
				float radius = ParseInputRadius(match.Groups["payload"].Value);
				WarehouseRenamePolicy.TryResolvePreviousId(zdo.ZDO.GetString("ModerWarehouse.InputWarehouse", string.Empty), text6, out var previousId);
				bool flag = HasOtherInputWithId(zdo, text6);
				if (WarehouseRenamePolicy.CanApplyRename(previousId, text6, flag))
				{
					MigrateWarehouseSigns(zdo, previousId, text6, radius);
					zdo.ZDO.Set("ModerWarehouse.InputWarehouse", text6);
				}
				else if (string.IsNullOrWhiteSpace(previousId) && !flag)
				{
					zdo.ZDO.Set("ModerWarehouse.InputWarehouse", text6);
				}
				else if (WarehouseRenamePolicy.ShouldLogRenameConflict(previousId, flag))
				{
					((Processor)this).Logger.LogWarning((object)("Warehouse rename to '" + text6 + "' was not applied: another IN marker already uses that name."));
				}
				zdo.ZDO.Set("ModerWarehouse.InputRadius", ParseInputRadius(match.Groups["payload"].Value));
				EnsureHelpSign(zdo, text6, ResolveLanguage(match.Groups["payload"].Value));
			}
			if (!string.IsNullOrWhiteSpace(text2))
			{
				string text7 = (match.Success ? match.Groups["payload"].Value.Trim() : text.Trim());
				string text8 = zdo.ZDO.GetString("ModerWarehouse.RequestRenderedText", string.Empty);
				if (string.IsNullOrWhiteSpace(text7))
				{
					_requestClearAt.Remove(zdo);
					WriteRequestPrompt(zdo, text2, LanguageForWarehouse(text2));
					return ((Processor)this).ScheduleReprocessing(Mathf.Max(1f, ConfigBase<WarehouseConfig>.Instance.ScanInterval.Value));
				}
				if (text7.StartsWith("SOLL ", StringComparison.OrdinalIgnoreCase) || text7.StartsWith("TARGET ", StringComparison.OrdinalIgnoreCase))
				{
					int startIndex = (text7.StartsWith("SOLL ", StringComparison.OrdinalIgnoreCase) ? 5 : 7);
					if (TryParseTargets(text7.Substring(startIndex).Trim(), out var targets))
					{
						zdo.ZDO.Set("ModerWarehouse.RequestTargets", SerializeTargets(targets));
						SaveChestSignLinks(zdo);
						((Processor)this).Logger.LogInfo((object)$"Warehouse '{text2}': saved {targets.Count} target stock value(s).");
						ProcessWarehouse(text2, peers, force: true);
					}
					else
					{
						string language = LanguageForWarehouse(text2);
						WriteRequestText(zdo, "<color=#ff6666>" + WarehouseLocalization.Text(language, "request.error_example") + "\n<color=white>" + WarehouseLocalization.Text(language, "request.target") + " Wood 1000");
					}
					return ((Processor)this).ScheduleReprocessing(Mathf.Max(1f, ConfigBase<WarehouseConfig>.Instance.ScanInterval.Value));
				}
				if (IsRequestPrompt(text7))
				{
					ProcessWarehouse(text2, peers);
					return ((Processor)this).ScheduleReprocessing(Mathf.Max(1f, ConfigBase<WarehouseConfig>.Instance.ScanInterval.Value));
				}
				if (!string.IsNullOrWhiteSpace(text8) && string.Equals(text7, text8, StringComparison.Ordinal))
				{
					if (IsStatus(text7) && !_requestClearAt.ContainsKey(zdo))
					{
						_requestClearAt[zdo] = WarehouseClock.UtcNow.AddSeconds(RequestDisplayPolicy.ClampResultSeconds(ConfigBase<WarehouseConfig>.Instance.RequestResultSeconds.Value));
					}
					if (_requestClearAt.TryGetValue(zdo, out var value) && WarehouseClock.UtcNow >= value)
					{
						_requestClearAt.Remove(zdo);
						WriteRequestPrompt(zdo, text2, LanguageForWarehouse(text2));
						ProcessWarehouse(text2, peers, force: true);
					}
					else
					{
						ProcessWarehouse(text2, peers);
					}
					return ((Processor)this).ScheduleReprocessing(Mathf.Max(1f, ConfigBase<WarehouseConfig>.Instance.ScanInterval.Value));
				}
				if (IsStatus(text7))
				{
					if (!_requestClearAt.TryGetValue(zdo, out var value2))
					{
						value2 = WarehouseClock.UtcNow.AddSeconds(RequestDisplayPolicy.ClampResultSeconds(ConfigBase<WarehouseConfig>.Instance.RequestResultSeconds.Value));
						_requestClearAt[zdo] = value2;
					}
					if (WarehouseClock.UtcNow >= value2)
					{
						WriteRequestPrompt(zdo, text2, LanguageForWarehouse(text2));
						_requestClearAt.Remove(zdo);
					}
					return ((Processor)this).ScheduleReprocessing(Mathf.Max(1f, ConfigBase<WarehouseConfig>.Instance.ScanInterval.Value));
				}
				_requestClearAt.Remove(zdo);
				ProcessWarehouse(text2, peers, force: true);
				return ((Processor)this).ScheduleReprocessing(Mathf.Max(1f, ConfigBase<WarehouseConfig>.Instance.ScanInterval.Value));
			}
			if (!string.IsNullOrWhiteSpace(text3))
			{
				ProcessWarehouse(text3, peers);
				return ((Processor)this).ScheduleReprocessing(Mathf.Max(1f, ConfigBase<WarehouseConfig>.Instance.ScanInterval.Value));
			}
			if (!string.IsNullOrWhiteSpace(text4))
			{
				string group = zdo.ZDO.GetString("ModerWarehouse.BoardGroup", string.Empty);
				int num2 = zdo.ZDO.GetInt("ModerWarehouse.BoardCell", -1);
				CaptureBoardControlInput(zdo, group, num2);
				if (!string.IsNullOrWhiteSpace(group))
				{
					DateTime value3;
					if (_signs.Any((ServersideQoLZDO sign) => sign.ZDO.GetString("ModerWarehouse.BoardGroup", string.Empty) == group && sign.ZDO.GetInt("ModerWarehouse.BoardCell", -1) == 12))
					{
						_orphanBoardSeenAt.Remove(group);
					}
					else if (!_orphanBoardSeenAt.TryGetValue(group, out value3))
					{
						_orphanBoardSeenAt[group] = WarehouseClock.UtcNow;
					}
					else if ((WarehouseClock.UtcNow - value3).TotalSeconds >= 10.0)
					{
						List<ServersideQoLZDO> list2 = _signs.Where((ServersideQoLZDO sign) => sign.ZDO.GetString("ModerWarehouse.BoardGroup", string.Empty) == group).ToList();
						foreach (ServersideQoLZDO item4 in list2)
						{
							_signs.Remove(item4);
							item4.Destroy();
						}
						_orphanBoardSeenAt.Remove(group);
						((Processor)this).Logger.LogInfo((object)$"Removed {list2.Count} orphaned stock-board sign(s) from group '{group}'.");
						return (ProcessResult)4;
					}
				}
				if (num2 == 12)
				{
					EnsureBoard(zdo, text4);
				}
				if (num2 == 12)
				{
					ProcessWarehouse(text4, peers);
				}
				return ((Processor)this).ScheduleReprocessing(Mathf.Min(1f, Mathf.Max(0.5f, ConfigBase<WarehouseConfig>.Instance.BoardPageSeconds.Value / 2f)));
			}
			if (!string.IsNullOrWhiteSpace(text5))
			{
				int num3 = zdo.ZDO.GetInt("ModerWarehouse.StatsCell", -1);
				string statsGroup2 = zdo.ZDO.GetString("ModerWarehouse.StatsGroup", string.Empty);
				if (num3 == 6 && !string.IsNullOrWhiteSpace(zdo.ZDO.GetString("ModerWarehouse.StatsHistory", string.Empty)))
				{
					foreach (ServersideQoLZDO item5 in _signs.Where((ServersideQoLZDO x) => x != zdo && x.ZDO.GetString("ModerWarehouse.StatsGroup", string.Empty) == statsGroup2).ToList())
					{
						_signs.Remove(item5);
						item5.Destroy();
					}
					zdo.ZDO.Set("ModerWarehouse.StatsCell", 12);
					_statsReadyGroups.Remove(statsGroup2);
					_statsEnsurePendingAt.Remove(statsGroup2);
					((Processor)this).Logger.LogInfo((object)("Warehouse '" + text5 + "': migrated 3x3 statistics board to 4x4."));
					num3 = 12;
				}
				if (num3 == 12)
				{
					EnsureStatsBoard(zdo, text5);
				}
				if (num3 == 12 || zdo.ZDO.GetBool("ModerWarehouse.StatsControl", false))
				{
					ProcessWarehouse(text5, peers);
				}
				return ((Processor)this).ScheduleReprocessing(Mathf.Min(1f, Mathf.Max(0.5f, ConfigBase<WarehouseConfig>.Instance.BoardPageSeconds.Value / 2f)));
			}
			if (match.Success || IgnoreMarker.IsMatch(text))
			{
				WriteWhite(zdo, text);
			}
			if (match.Success)
			{
				ProcessWarehouse(match.Groups["id"].Value, peers);
			}
			return ((Processor)this).ScheduleReprocessing(Mathf.Max(1f, ConfigBase<WarehouseConfig>.Instance.ScanInterval.Value));
		}

		private string LanguageForWarehouse(string id)
		{
			//IL_001f: 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)
			foreach (ServersideQoLZDO sign in _signs)
			{
				Regex marker = Marker;
				ZDOVars vars = sign.Vars;
				Match match = marker.Match(PlainText(((ZDOVars)(ref vars)).GetText("")));
				if (match.Success && match.Groups["id"].Value.Equals(id, StringComparison.OrdinalIgnoreCase) && match.Groups["role"].Value.Equals("IN", StringComparison.OrdinalIgnoreCase))
				{
					return ResolveLanguage(match.Groups["payload"].Value);
				}
			}
			return ResolveLanguage(string.Empty);
		}

		internal static string ResolveLanguage(string payload)
		{
			Match match = Regex.Match(payload ?? string.Empty, "(?:^|[\\r\\n])\\s*LANG(?:UAGE)?\\s*=\\s*(?<language>[A-Za-z][A-Za-z0-9_-]*)\\s*(?:$|[\\r\\n])", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
			return WarehouseLocalization.Resolve(match.Success ? match.Groups["language"].Value : ConfigBase<WarehouseConfig>.Instance.Language.Value);
		}

		private void ProcessWarehouse(string id, IReadOnlyList<Peer> peers, bool force = false)
		{
			if (string.IsNullOrWhiteSpace(id))
			{
				return;
			}
			string text = id.Trim();
			DateTime utcNow = WarehouseClock.UtcNow;
			if (_warehousesRunning.Contains(text))
			{
				PerformanceWindow(text).Skipped++;
				return;
			}
			bool flag = HasAwakeBoard(text, peers);
			bool value;
			bool woke = flag && (!_lastAwake.TryGetValue(text, out value) || !value);
			_lastAwake[text] = flag;
			_warehouseNextRun.TryGetValue(text, out var value2);
			_lastCompleted.TryGetValue(text, out var value3);
			if (!WarehouseSchedule.CanRun(utcNow, value2, value3, force, woke, _warehousesDirty.Contains(text)))
			{
				PerformanceWindow(text).Skipped++;
				return;
			}
			_warehousesDirty.Remove(text);
			double num = Math.Max(1.0, ConfigBase<WarehouseConfig>.Instance.ScanInterval.Value);
			double value4 = (flag ? 1.0 : Math.Max(30.0, num * 10.0));
			_warehouseNextRun[text] = utcNow.AddSeconds(value4);
			_warehousesRunning.Add(text);
			_cycleInventories = new Dictionary<ServersideQoLZDO, IInventory>();
			_cycleContainers = (_cycleSigns = (_cycleStores = (_cycleInventoryReads = 0)));
			_cycleTopologyMs = (_cycleClassifyMs = (_cycleDisplayMs = (_cycleTransferMs = 0L)));
			CapturePeerMetrics(peers);
			Stopwatch stopwatch = Stopwatch.StartNew();
			try
			{
				ProcessWarehouseCore(text, peers);
			}
			finally
			{
				FlushInventorySaves();
				stopwatch.Stop();
				_cycleInventories = null;
				_spatialLookups.Clear();
				_storageSigns = null;
				_localStorage = null;
				_warehousesRunning.Remove(text);
				_lastCompleted[text] = WarehouseClock.UtcNow;
				_warehouseNextRun[text] = WarehouseClock.UtcNow.AddSeconds(value4);
				RecordPerformance(text, flag, stopwatch.ElapsedMilliseconds);
				long num2 = (flag ? 150 : 250);
				if (stopwatch.ElapsedMilliseconds >= num2 && (!_slowCycleLoggedAt.TryGetValue(text, out var value5) || (WarehouseClock.UtcNow - value5).TotalSeconds >= 120.0))
				{
					_slowCycleLoggedAt[text] = WarehouseClock.UtcNow;
					((Processor)this).Logger.LogWarning((object)($"Warehouse '{text}': slow coordinated cycle {stopwatch.ElapsedMilliseconds} ms " + "(board " + (flag ? "awake" : "sleeping") + ")."));
				}
			}
		}

		private bool HasAwakeBoard(string id, IReadOnlyList<Peer> peers)
		{
			if (peers == null || peers.Count == 0)
			{
				return false;
			}
			float distance2 = Mathf.Pow(Mathf.Max(1f, ConfigBase<WarehouseConfig>.Instance.BoardWakeDistance.Value), 2f);
			return _signs.Any(delegate(ServersideQoLZDO sign)
			{
				string text = sign.ZDO.GetString("ModerWarehouse.BoardWarehouse", string.Empty);
				string text2 = sign.ZDO.GetString("ModerWarehouse.StatsWarehouse", string.Empty);
				return (text.Equals(id, StringComparison.OrdinalIgnoreCase) || text2.Equals(id, StringComparison.OrdinalIgnoreCase)) && peers.Any(delegate(Peer peer)
				{
					//IL_0004: Unknown result type (might be due to invalid IL or missing references)
					//IL_0014: 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_001e: Unknown result type (might be due to invalid IL or missing references)
					if (peer != null)
					{
						Vector3 val = peer.RefPos - sign.ZDO.GetPosition();
						return ((Vector3)(ref val)).sqrMagnitude <= distance2;
					}
					return false;
				});
			});
		}

		private void ProcessWarehouseCore(string id, IReadOnlyList<Peer> peers)
		{
			//IL_023a: Unknown result type (might be due to invalid IL or missing references)
			//IL_023f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0414: Unknown result type (might be due to invalid IL or missing references)
			//IL_0419: Unknown result type (might be due to invalid IL or missing references)
			//IL_095f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0964: Unknown result type (might be due to invalid IL or missing references)
			//IL_09a5: Unknown result type (might be due to invalid IL or missing references)
			Stopwatch stopwatch = Stopwatch.StartNew();
			string language = LanguageForWarehouse(id);
			List<ServersideQoLZDO> list = AllContainers();
			_cycleContainers = list.Count;
			_cycleSigns = _signs.Count;
			float num = ConfigBase<WarehouseConfig>.Instance.MarkerDistance.Value * ConfigBase<WarehouseConfig>.Instance.MarkerDistance.Value;
			List<WarehouseNode> list2 = DiscoverWarehouseNodes(list, num);
			ValidateWarehouseNodes(list2, num);
			ReconcileLegacyWarehouseSigns(list2);
			_knownNodes.Clear();
			foreach (WarehouseNode item2 in list2.Where((WarehouseNode x) => x.Valid))
			{
				_knownNodes[item2.Id] = item2;
			}
			UpdateInputHelpSigns(list2);
			UpdateUnpairedInputSigns(list2);
			_cycleTopologyMs = stopwatch.ElapsedMilliseconds;
			WarehouseNode localNode = list2.FirstOrDefault((WarehouseNode x) => x.Id.Equals(id, StringComparison.OrdinalIgnoreCase));
			if (localNode == null || !localNode.Valid)
			{
				return;
			}
			_localStorage = new HashSet<ServersideQoLZDO>(list.Where((ServersideQoLZDO x) => Distance2(x, localNode.Input) <= localNode.Radius * localNode.Radius));
			float signRadius = localNode.Radius + Math.Max(ConfigBase<WarehouseConfig>.Instance.LabelDistance.Value, ConfigBase<WarehouseConfig>.Instance.MarkerDistance.Value) + 2f;
			_storageSigns = _signs.Where((ServersideQoLZDO sign) => Distance2(sign, localNode.Input) <= signRadius * signRadius || LinkedInside(sign, localNode)).ToList();
			HashSet<ServersideQoLZDO> endpoints = new HashSet<ServersideQoLZDO>();
			ServersideQoLZDO input = localNode.Input;
			ServersideQoLZDO val = null;
			List<Request> list3 = new List<Request>();
			List<ServersideQoLZDO> list4 = new List<ServersideQoLZDO>();
			List<ServersideQoLZDO> list5 = new List<ServersideQoLZDO>();
			List<ServersideQoLZDO> list6 = new List<ServersideQoLZDO>();
			float num2 = ConfigBase<WarehouseConfig>.Instance.LabelDistance.Value * ConfigBase<WarehouseConfig>.Instance.LabelDistance.Value;
			ZDOVars vars;
			foreach (ServersideQoLZDO sign in _signs)
			{
				Regex marker = Marker;
				vars = sign.Vars;
				Match match = marker.Match(PlainText(((ZDOVars)(ref vars)).GetText("")));
				string text = sign.ZDO.GetString("ModerWarehouse.RequestWarehouse", string.Empty);
				string text2 = sign.ZDO.GetString("ModerWarehouse.InfoWarehouse", string.Empty);
				string text3 = sign.ZDO.GetString("ModerWarehouse.BoardWarehouse", string.Empty);
				string text4 = sign.ZDO.GetString("ModerWarehouse.StatsWarehouse", string.Empty);
				bool flag = !match.Success && text.Equals(id, StringComparison.OrdinalIgnoreCase);
				bool flag2 = !match.Success && text2.Equals(id, StringComparison.OrdinalIgnoreCase);
				bool flag3 = !match.Success && text3.Equals(id, StringComparison.OrdinalIgnoreCase);
				bool flag4 = !match.Success && text4.Equals(id, StringComparison.OrdinalIgnoreCase);
				if (!flag && !flag2 && !flag3 && !flag4 && (!match.Success || !match.Groups["id"].Value.Equals(id, StringComparison.OrdinalIgnoreCase)))
				{
					continue;
				}
				string text5 = (flag ? "REQ" : (flag2 ? "INFO" : (flag3 ? "BOARD" : (flag4 ? "STATS" : match.Groups["role"].Value.ToUpperInvariant()))));
				switch (text5)
				{
				case "REQ":
				{
					string text6;
					if (!flag)
					{
						text6 = match.Groups["payload"].Value.Trim();
					}
					else
					{
						vars = sign.Vars;
						text6 = PlainText(((ZDOVars)(ref vars)).GetText("")).Trim();
					}
					string payload = text6;
					list3.Add(new Request(sign, payload));
					continue;
				}
				case "INFO":
					list4.Add(sign);
					continue;
				case "BOARD":
					list5.Add(sign);
					continue;
				case "STATS":
					list6.Add(sign);
					continue;
				}
				ServersideQoLZDO val2 = NearestContainer(sign, list, num);
				if (val2 != null)
				{
					endpoints.Add(val2);
					if (text5 == "IN" && val2 == localNode.Input)
					{
						input = val2;
					}
					if (text5 == "OUT")
					{
						val = val2;
					}
				}
			}
			if (input == null)
			{
				return;
			}
			ContainerRegistryProcessor registry = Processor.Instance<ContainerRegistryProcessor>();
			ContainerState state = registry.GetState(input);
			if (state == null)
			{
				return;
			}
			IInventory val3 = InventoryFor(registry, input);
			if (val3 == null)
			{
				return;
			}
			float radius2 = localNode.Radius * localNode.Radius;
			HashSet<ServersideQoLZDO> ignored = DiscoverIgnored(list, num);
			RefreshAutomaticLabels(list, num2, registry, language);
			Dictionary<ServersideQoLZDO, string> labels = DiscoverLabels(list, num2);
			Dictionary<ServersideQoLZDO, ServersideQoLZDO> dictionary = DiscoverBlankSigns(list, num2);
			if (RemoveSupersededGeneratedLabels(dictionary, list, num2))
			{
				labels = DiscoverLabels(list, num2);
				dictionary = DiscoverBlankSigns(list, num2);
			}
			RepairGeneratedLabelPlacement(list, num2);
			HashSet<ServersideQoLZDO> optedIn = new HashSet<ServersideQoLZDO>(labels.Keys);
			optedIn.UnionWith(dictionary.Keys);
			List<ServersideQoLZDO> list7 = (from x in list
				where optedIn.Contains(x) && CanManageContainer(x, endpoints, ignored, optedIn.Contains(x))
				where Distance2(x, input) <= radius2
				where ContainsOnlyWarehouseItems(InventoryFor(registry, x))
				orderby Distance2(x, input)
				select x).ToList();
			int num3 = SanitizeForbiddenItems(list7, registry, list, num2);
			if (num3 > 0)
			{
				((Processor)this).Logger.LogWarning((object)($"Warehouse '{id}' janitor purged {num3} forbidden internal item(s) " + "from signed managed storage; IN was left untouched."));
			}
			_cycleStores = list7.Count;
			int moves = Math.Max(1, ConfigBase<WarehouseConfig>.Instance.MaxMoves.Value);
			int num4 = CorrectMisfiledItems(id, list7, labels, registry, ref moves);
			if (num4 > 0)
			{
				((Processor)this).Logger.LogInfo((object)$"Warehouse '{id}' janitor refiled {num4} valid misplaced item(s) in total.");
			}
			ServersideQoLZDO val4 = ((IEnumerable<ServersideQoLZDO>)list6).FirstOrDefault((Func<ServersideQoLZDO, bool>)((ServersideQoLZDO sign) => sign.ZDO.GetInt("ModerWarehouse.StatsCell", -1) == 12));
			WarehouseStatsHistory warehouseStatsHistory = ((val4 == null) ? null : WarehouseStatsHistory.Parse(val4.ZDO.GetString("ModerWarehouse.StatsHistory", string.Empty)));
			warehouseStatsHistory?.SetSnapshot(list7, delegate(ServersideQoLZDO store)
			{
				IInventory val6 = InventoryFor(registry, store);
				return (val6 == null) ? null : val6.Items;
			});
			Dictionary<string, int> dictionary2 = (from val6 in list7.SelectMany(delegate(ServersideQoLZDO store)
				{
					IInventory val6 = InventoryFor(registry, store);
					IEnumerable<ItemData> enumerable = ((val6 != null) ? val6.Items : null);
					return enumerable ?? Enumerable.Empty<ItemData>();
				})
				where val6 != null && val6.m_stack > 0 && val6.m_shared != null
				group val6 by val6.m_shared.m_name).ToDictionary<IGrouping<string, ItemData>, string, int>((IGrouping<string, ItemData> group) => group.Key, (IGrouping<string, ItemData> group) => group.Sum((ItemData val6) => val6.m_stack), StringComparer.Ordinal);
			if (!_warehouseStockSnapshots.TryGetValue(id, out var value) || !StockEqual(value, dictionary2))
			{
				_warehouseStockSnapshots[id] = dictionary2;
				_warehouseStockTimes[id] = WarehouseClock.UtcNow;
				SaveStockCache();
			}
			_cycleClassifyMs = stopwatch.ElapsedMilliseconds - _cycleTopologyMs;
			WhitenWarehouseLabels(list7.Concat(endpoints).Concat(ignored).Distinct()
				.ToList(), list, num2);
			LabelHomogeneousStores(list7, labels, dictionary, registry, language);
			AdoptContainerSigns(list7, list, num2);
			WriteWarehouseInfo(id, language, list4, list7, labels, registry);
			WriteShoppingLists(id, language, list3.Select((Request x) => x.Sign), list7, registry);
			WriteWarehouseBoard(id, language, list5, list7, registry, peers, list2, list, num);
			_cycleDisplayMs = stopwatch.ElapsedMilliseconds - _cycleTopologyMs - _cycleClassifyMs;
			int num5 = 0;
			HashSet<ServersideQoLZDO> hashSet = new HashSet<ServersideQoLZDO>();
			Dictionary<string, int> dictionary3 = new Dictionary<string, int>(StringComparer.CurrentCultureIgnoreCase);
			bool flag5 = val3.Items.Any((ItemData val6) => IsWarehouseItem(val6) && val6.m_stack > 0);
			vars = input.Vars;
			bool flag6 = ((ZDOVars)(ref vars)).GetInUse(false);
			if (flag6 && TryReleaseStaleInputLock(id, input, state, peers))
			{
				flag6 = false;
			}
			else if (!flag6)
			{
				_inputInUseSince.Remove(input.ZDO.m_uid);
			}
			bool flag7 = state.ZDO.IsOwnerOrUnassigned();
			bool flag8 = flag5 && !flag6 && Own(state);
			int num6 = 0;
			int num7 = 0;
			int num8 = 0;
			int num9 = 0;
			if (flag5 && !flag6 && flag8)
			{
				foreach (ItemData item in val3.Items.ToList())
				{
					if (moves <= 0 || item == null || item.m_stack <= 0)
					{
						break;
					}
					if (!IsWarehouseItem(item))
					{
						continue;
					}
					int num10 = item.m_stack;
					string value4;
					foreach (ServersideQoLZDO item3 in list7.Where((ServersideQoLZDO x) => (!labels.TryGetValue(x, out value4)) ? IsSingleItemType(InventoryFor(registry, x), item) : LabelMatches(x, value4, item, registry)).OrderByDescending(delegate(ServersideQoLZDO x)
					{
						if (HasItem(InventoryFor(registry, x), item))
						{
							return 2;
						}
						return (labels.TryGetValue(x, out value4) && LabelMatches(x, value4, item, registry)) ? 1 : 0;
					}))
					{
						if (moves <= 0 || num10 <= 0)
						{
							break;
						}
						num6++;
						ContainerState state2 = registry.GetState(item3);
						if (state2 == null)
						{
							num7++;
							continue;
						}
						if (!Own(state2))
						{
							num8++;
							continue;
						}
						IInventory val5 = InventoryFor(registry, item3);
						bool flag9 = !labels.ContainsKey(item3) && IsSingleItemType(val5, item);
						int num11 = Move(val3, val5, item, num10);
						if (num11 <= 0)
						{
							num9++;
						}
						if (num11 <= 0)
						{
							continue;
						}
						string key = LocalizedItemName(item, language);
						dictionary3[key] = (dictionary3.TryGetValue(key, out var value2) ? (value2 + num11) : num11);
						num5 += num11;
						warehouseStatsHistory?.AddInput(item.m_shared.m_name, num11);
						num10 -= num11;
						moves--;
						hashSet.Add(item3);
						if (flag9 && ConfigBase<WarehouseConfig>.Instance.AutoCreateLabels.Value)
						{
							string value3 = CreateAutomaticLabel(item3, item, dictionary, language);
							if (!string.IsNullOrEmpty(value3))
							{
								labels[item3] = value3;
							}
						}
					}
				}
			}
			if (val3.Items.Any((ItemData val6) => val6 != null && val6.m_stack > 0) && num5 == 0 && ShouldLogTransferDiagnostic(id))
			{
				string text7 = string.Join(", ", from val6 in val3.Items
					where val6 != null && val6.m_stack > 0
					select $"{ItemName(val6)} x{val6.m_stack}" + string.Format(" [shared='{0}', cheated={1}, ", val6.m_shared?.m_name ?? "<null>", val6.m_cheated) + "type='" + (((object)Unsafe.As<ItemType, ItemType>(ref val6.m_shared?.m_itemType)/*cast due to .constrained prefix*/).ToString() ?? "<null>") + "']");
				int num12 = val3.Items.Count((ItemData val6) => val6 != null && val6.m_stack > 0 && !IsWarehouseItem(val6));
				((Processor)this).Logger.LogWarning((object)("Warehouse '" + id + "': transfer diagnostic; input=[" + text7 + "], " + $"warehouseItems={flag5}, rejectedItemStacks={num12}, " + $"INInUse={flag6}, sourceOwnerBefore={flag7}, " + $"sourceOwnershipGranted={flag8}, stores={list7.Count}, " + $"matchingTargets={num6}, missingTargetStates={num7}, " + $"targetOwnershipBlocked={num8}, moveReturnedZero={num9}."));
			}
			if (num5 > 0)
			{
				_deliveryNotices[id] = new DeliveryNotice(dictionary3, WarehouseClock.UtcNow.AddSeconds(Math.Max(2f, ConfigBase<WarehouseConfig>.Instance.DeliveryNoticeSeconds.Value)));
				((Processor)this).Logger.LogInfo((object)($"Warehouse '{id}': stored {num5} item(s) into " + $"{hashSet.Count} chest(s); {list7.Count} candidate chest(s) scanned."));
			}
			if (val != null)
			{
				foreach (Request item4 in list3)
				{
					ProcessRequest(id, language, item4, val, list7, registry, warehouseStatsHistory, ref moves, list2, list, num);
				}
			}
			if (warehouseStatsHistory != null)
			{
				warehouseStatsHistory.SetSnapshot(list7, delegate(ServersideQoLZDO store)
				{
					IInventory val6 = InventoryFor(registry, store);
					return (val6 == null) ? null : val6.Items;
				});
				if (warehouseStatsHistory.Changed)
				{
					val4.ZDO.Set("ModerWarehouse.StatsHistory", warehouseStatsHistory.Serialize());
				}
				WriteStatisticsBoard(id, language, list6, warehouseStatsHistory, list7, registry, peers, list2, list, num);
			}
			_cycleTransferMs = stopwatch.ElapsedMilliseconds - _cycleTopologyMs - _cycleClassifyMs - _cycleDisplayMs;
		}

		private bool TryReleaseStaleInputLock(string id, ServersideQoLZDO input, ContainerState state, IReadOnlyList<Peer> peers)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: 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_008a: 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_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_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//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_0179: Unknown result type (might be due to invalid IL or missing references)
			float value = ConfigBase<WarehouseConfig>.Instance.StaleInputLockSeconds.Value;
			if (value <= 0f)
			{
				return false;
			}
			ZDOID uid = input.ZDO.m_uid;
			DateTime utcNow = WarehouseClock.UtcNow;
			if (!_inputInUseSince.TryGetValue(uid, out var value2))
			{
				_inputInUseSince[uid] = utcNow;
				((Processor)this).Logger.LogInfo((object)("Warehouse '" + id + "': IN chest is marked in use; watching for a stale lock."));
				return false;
			}
			double totalSeconds = (utcNow - value2).TotalSeconds;
			Vector3 position = input.ZDO.GetPosition();
			float num = Mathf.Max(0f, ConfigBase<WarehouseConfig>.Instance.StaleInputLockPlayerDistance.Value);
			long owner = input.ZDO.GetOwner();
			Peer val = peers?.FirstOrDefault((Func<Peer, bool>)((Peer peer) => ((peer != null) ? peer.ZNetPeer : null) != null && peer.ZNetPeer.m_uid == owner));
			int num2;
			if (val != null)
			{
				Vector3 val2 = val.RefPos - position;
				num2 = ((((Vector3)(ref val2)).sqrMagnitude <= num * num) ? 1 : 0);
			}
			else
			{
				num2 = 0;
			}
			bool flag = (byte)num2 != 0;
			float num3 = Mathf.Max(value, ConfigBase<WarehouseConfig>.Instance.StaleInputLockMaxSeconds.Value);
			if (totalSeconds < (double)value || (flag && totalSeconds < (double)num3))
			{
				return false;
			}
			if (!Own(state))
			{
				((Processor)this).Logger.LogWarning((object)$"Warehouse '{id}': stale IN chest lock ({totalSeconds:0} s) could not be released: ownership was not granted.");
				return false;
			}
			ZDOVars vars = input.Vars;
			((ZDOVars)(ref vars)).SetInUse(false, "/root/project/ModerWarehouse/WarehouseProcessor.cs", 1591);
			_inputInUseSince.Remove(uid);
			string arg = ((val == null) ? "its owner is not connected" : (flag ? $"hard limit {num3:0} s reached" : $"its owner is more than {num:0.#} m away"));
			((Processor)this).Logger.LogWarning((object)$"Warehouse '{id}': released stale IN chest lock after {totalSeconds:0} s; {arg}.");
			return true;
		}

		internal static float ParseInputRadius(string payload)
		{
			float num = Mathf.Max(10f, ConfigBase<WarehouseConfig>.Instance.Radius.Value);
			float num2 = Mathf.Clamp(ConfigBase<WarehouseConfig>.Instance.MinimumRadius.Value, 1f, num);
			Match match = Regex.Match(payload ?? string.Empty, "(?:^|\\s)R(?:ADIUS)?\\s*=\\s*(?<radius>\\d+(?:[\\.,]\\d+)?)", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
			if (!match.Success || !float.TryParse(match.Groups["radius"].Value.Replace(',', '.'), NumberStyles.Float, CultureInfo.InvariantCulture, out var result))
			{
				return num;
			}
			return Mathf.Clamp(result, num2, num);
		}

		private bool HasOtherInputWithId(ServersideQoLZDO inputSign, string id)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			foreach (ServersideQoLZDO sign in _signs)
			{
				if (sign != inputSign)
				{
					Regex marker = Marker;
					ZDOVars vars = sign.Vars;
					Match match = marker.Match(PlainText(((ZDOVars)(ref vars)).GetText("")));
					if (match.Success && match.Groups["role"].Value.Equals("IN", StringComparison.OrdinalIgnoreCase) && match.Groups["id"].Value.Equals(id, StringComparison.OrdinalIgnoreCase))
					{
						return true;
					}
				}
			}
			return false;
		}

		private void MigrateWarehouseSigns(ServersideQoLZDO inputSign, string oldId, string newId, float radius)
		{
			//IL_0315: Unknown result type (might be due to invalid IL or missing references)
			//IL_031a: Unknown result type (might be due to invalid IL or missing references)
			//IL_040d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0412: Unknown result type (might be due to invalid IL or missing references)
			int num = 0;
			string text = inputSign.ZDO.GetString("ModerWarehouse.HelpGroup", string.Empty);
			HashSet<string> hashSet = new HashSet<string>(from sign in _signs
				where sign.ZDO.GetString("ModerWarehouse.BoardWarehouse", string.Empty).Equals(oldId, StringComparison.OrdinalIgnoreCase) || sign.ZDO.GetString("ModerWarehouse.BoardWarehouse", string.Empty).Equals(newId, StringComparison.OrdinalIgnoreCase)
				select sign.ZDO.GetString("ModerWarehouse.BoardGroup", string.Empty) into @group
				where !string.IsNullOrWhiteSpace(@group)
				select @group, StringComparer.Ordinal);
			HashSet<string> hashSet2 = new HashSet<string>(from sign in _signs
				where sign.ZDO.GetString("ModerWarehouse.StatsWarehouse", string.Empty).Equals(oldId, StringComparison.OrdinalIgnoreCase) || sign.ZDO.GetString("ModerWarehouse.StatsWarehouse", string.Empty).Equals(newId, StringComparison.OrdinalIgnoreCase)
				select sign.ZDO.GetString("ModerWarehouse.StatsGroup", string.Empty) into @group
				where !string.IsNullOrWhiteSpace(@group)
				select @group, StringComparer.Ordinal);
			foreach (ServersideQoLZDO item in