Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of ModerWarehousePlus v1.0.1
plugins/ModerWarehousePlus.dll
Decompiled 4 days ago
The result has been truncated due to the large size, download it to view full contents!
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