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 AutoPinSigns v2.0.0
AutoPinSigns.dll
Decompiled 2 weeks ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using ConditionalConfigSync; using HarmonyLib; using Microsoft.CodeAnalysis; using Splatform; using TMPro; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("Auto Pin Signs")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("Auto Pin Signs")] [assembly: AssemblyCopyright("Copyright © 2023-2026")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("1f4794c6-9828-458d-bf06-a6acb8e038ac")] [assembly: AssemblyFileVersion("2.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("2.0.0.0")] [module: UnverifiableCode] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } } namespace AutoPinSigns { [Flags] internal enum AuthoritativePinTypes { None = 0, Fire = 1, Base = 2, Hammer = 4, Dot = 8, Portal = 0x10, All = 0x1F } [BepInPlugin("shudnal.AutoPinSigns", "Auto Pin Signs", "2.0.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] public sealed class AutoPinSigns : BaseUnityPlugin { [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static ConsoleEvent <>9__59_0; public static ConsoleOptionsFetcher <>9__59_1; internal void <InitCommands>b__59_0(ConsoleEventArgs args) { //IL_0106: Unknown result type (might be due to invalid IL or missing references) if (args.Args.Length < 2) { PrintCommandSyntax(args); return; } switch (args.Args[1].ToLowerInvariant()) { case "clear": { if (!IsEnabled) { args.Context.AddString("Auto Pin Signs is disabled."); break; } if (!Object.op_Implicit((Object)(object)Player.m_localPlayer)) { args.Context.AddString("The clear command requires a local player and map."); break; } float result = 5f; if (args.Args.Length > 2 && !TryParseFloat(args.Args[2], out result)) { args.Context.AddString("Invalid range."); break; } result = Mathf.Max(0f, result); int num = RemoveSavedUserPins(((Component)Player.m_localPlayer).transform.position, result); args.Context.AddString($"Removed {num} saved user pin(s) within {result:0.##} meters."); break; } case "status": args.Context.AddString(ServerPinSync.GetStatusText()); break; case "resync": args.Context.AddString(ServerPinSync.ForceResync()); break; default: PrintCommandSyntax(args); break; } } internal List<string> <InitCommands>b__59_1() { return new List<string> { "clear [range] - remove saved standard user pins near the player (default range: 5)", "status - show synchronization status", "resync - refresh local pins or request/rebuild the authoritative list" }; } } public const string pluginID = "shudnal.AutoPinSigns"; public const string pluginName = "Auto Pin Signs"; public const string pluginVersion = "2.0.0"; private static readonly Harmony harmony = new Harmony("shudnal.AutoPinSigns"); internal static readonly ConfigSync configSync = new ConfigSync("shudnal.AutoPinSigns") { DisplayName = "Auto Pin Signs", CurrentVersion = "2.0.0", MinimumRequiredVersion = "2.0.0" }; internal static ConfigEntry<bool> modEnabled; internal static ConfigEntry<bool> configLocked; internal static ConfigEntry<bool> loggingEnabled; internal static ConfigEntry<bool> allowSubstrings; internal static ConfigEntry<bool> removePinsWithoutSigns; internal static ConfigEntry<bool> stripHTMLTags; internal static ConfigEntry<bool> useStringsList; internal static ConfigEntry<string> configFireList; internal static ConfigEntry<string> configBaseList; internal static ConfigEntry<string> configHammerList; internal static ConfigEntry<string> configPinList; internal static ConfigEntry<string> configPortalList; internal static ConfigEntry<bool> useStringsPrefix; internal static ConfigEntry<string> configFirePrefix; internal static ConfigEntry<string> configBasePrefix; internal static ConfigEntry<string> configHammerPrefix; internal static ConfigEntry<string> configPinPrefix; internal static ConfigEntry<string> configPortalPrefix; internal static ConfigEntry<bool> useStringsSuffix; internal static ConfigEntry<string> configFireSuffix; internal static ConfigEntry<string> configBaseSuffix; internal static ConfigEntry<string> configHammerSuffix; internal static ConfigEntry<string> configPinSuffix; internal static ConfigEntry<string> configPortalSuffix; internal static ConfigEntry<bool> serverAuthoritativePins; internal static ConfigEntry<AuthoritativePinTypes> serverAuthoritativePinTypes; internal static ConfigEntry<bool> serverAuthoritativeAdminsOnly; internal static ConfigEntry<float> serverAuthoritativeMergeDistance; internal static ConfigEntry<bool> allowCheckedPinStatus; private static AutoPinSigns instance; internal static bool IsEnabled => modEnabled?.Value ?? false; internal static bool IsAuthoritativeMode => IsEnabled && (serverAuthoritativePins?.Value ?? false); private void Awake() { instance = this; CustomConfigs.Awake(); ConfigInit(); SignPinParser.RebuildRules(); ((ConditionalConfigSync)configSync).AddLockingConfigEntry<bool>(configLocked); harmony.PatchAll(); InitCommands(); Game.isModded = true; } private void FixedUpdate() { ServerPinSync.Tick(); LocalSignPins.Tick(); } private void OnDestroy() { ServerPinSync.ResetSession(); LocalSignPins.ClearStates(); harmony.UnpatchSelf(); ((BaseUnityPlugin)this).Config.Save(); instance = null; } internal static bool IsUserPinType(PinType pinType) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Invalid comparison between Unknown and I4 //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Invalid comparison between Unknown and I4 //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Invalid comparison between Unknown and I4 //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Invalid comparison between Unknown and I4 return (int)pinType == 0 || (int)pinType == 1 || (int)pinType == 2 || (int)pinType == 3 || (int)pinType == 6; } internal static AuthoritativePinTypes GetPinTypeFlag(PinType pinType) { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Expected I4, but got Unknown if (1 == 0) { } AuthoritativePinTypes result = (int)pinType switch { 0 => AuthoritativePinTypes.Fire, 1 => AuthoritativePinTypes.Base, 2 => AuthoritativePinTypes.Hammer, 3 => AuthoritativePinTypes.Dot, 6 => AuthoritativePinTypes.Portal, _ => AuthoritativePinTypes.None, }; if (1 == 0) { } return result; } internal static AuthoritativePinTypes GetConfiguredAuthoritativePinTypes() { return (serverAuthoritativePinTypes?.Value ?? AuthoritativePinTypes.All) & AuthoritativePinTypes.All; } internal static bool IsPinTypeEnabled(AuthoritativePinTypes types, PinType pinType) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) return (types & GetPinTypeFlag(pinType)) != 0; } internal static void LogInfo(object data) { ConfigEntry<bool> obj = loggingEnabled; if (obj != null && obj.Value && (Object)(object)instance != (Object)null) { ((BaseUnityPlugin)instance).Logger.LogInfo(data); } } internal static void LogWarning(object data) { if ((Object)(object)instance != (Object)null) { ((BaseUnityPlugin)instance).Logger.LogWarning(data); } } private ConfigDescription GetDescriptionSeparatedStrings(string description) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Expected O, but got Unknown return Chainloader.PluginInfos.ContainsKey("_shudnal.ConfigurationManager") ? new ConfigDescription(description, (AcceptableValueBase)null, Array.Empty<object>()) : new ConfigDescription(description, (AcceptableValueBase)null, new object[1] { new CustomConfigs.ConfigurationManagerAttributes { CustomDrawer = CustomConfigs.DrawSeparatedStrings(",") } }); } private void ConfigInit() { //IL_0197: Unknown result type (might be due to invalid IL or missing references) //IL_01a2: Expected O, but got Unknown modEnabled = ConfigEntry("General", "Enabled", defaultValue: true, "Enable the mod."); configLocked = ConfigEntry("General", "Lock Configuration", defaultValue: true, "Configuration is locked and can be changed by server administrators only."); loggingEnabled = ConfigEntry("General", "Logging enabled", defaultValue: false, "Enable diagnostic logging. [Not synchronized with server]", synchronizedSetting: false); removePinsWithoutSigns = ConfigEntry("General", "Remove nearby map pins without related signs", defaultValue: false, "Remove saved user pins in the currently loaded zone when no matching sign exists near the pin. Server-controlled pin types are never handled by this cleanup."); allowSubstrings = ConfigEntry("General", "Less strict string comparison", defaultValue: true, "After exact list and explicit prefix/suffix matching, allow the longest configured list value to occur anywhere in the sign text."); stripHTMLTags = ConfigEntry("General", "Strip HTML tags from text", defaultValue: true, "Strip rich-text tags before matching and before creating the pin name. The original sign text is still preserved for editing."); allowCheckedPinStatus = ConfigEntry("General", "Allow checked pin status", defaultValue: true, "Allow Shift + E on a recognized pinned sign to toggle its checked status. The state is stored in the sign ZDO and synchronized with authoritative pins."); useStringsList = ConfigEntry("Matching Mode", "Use string list matching", defaultValue: true, "First match exact full sign text against the configured lists; optionally use the longest partial list match after explicit prefixes and suffixes. The full processed sign text becomes the pin name."); useStringsPrefix = ConfigEntry("Matching Mode", "Use prefix matching", defaultValue: true, "Match explicit configured prefixes after exact list matching. Example: \"Pin: Boat\" creates a pin named \"Boat\". Use the reserved token anyPin only as the final prefix fallback."); useStringsSuffix = ConfigEntry("Matching Mode", "Use suffix matching", defaultValue: true, "Match explicit configured suffixes after prefixes. Example: \"Boat here\" with suffix \"here\" creates a pin named \"Boat\". Use the reserved token anyPin only as the final suffix fallback."); serverAuthoritativePins = ConfigEntry("Server Authoritative Pins", "Enabled", defaultValue: false, "Use the server's sign-derived pin list as the authoritative source for selected standard user pin types. Existing client pins using controlled types are temporarily hidden but remain unchanged in the player profile. They return when server authority or the corresponding controlled type is disabled. Server pins are not saved to the player profile. Pings, events, player pins, location pins and every other pin type are not changed."); serverAuthoritativePinTypes = ConfigEntry("Server Authoritative Pins", "Controlled pin types", AuthoritativePinTypes.All, "Standard user pin types displayed from the server snapshot while authority is active. Types not selected remain client-controlled."); serverAuthoritativeAdminsOnly = ConfigEntry("Server Authoritative Pins", "Only administrator signs", defaultValue: false, "Only publish signs whose stored author is present in the server administrator list. Host-authored signs are allowed."); serverAuthoritativeMergeDistance = ConfigEntry("Server Authoritative Pins", "Merge identical pins within distance", 0f, new ConfigDescription("Merge pins with the same parsed type and name when they are within this horizontal distance in meters. Candidates are ordered by ZDOID and the first one is retained. Set to 0 to disable merging.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1000f), Array.Empty<object>())); configFireList = ConfigEntry("Signs", "FireList", "fire", GetDescriptionSeparatedStrings("Case-insensitive words for Fire pins. Separate values with commas.")); configBaseList = ConfigEntry("Signs", "BaseList", "base,shelter,home,house", GetDescriptionSeparatedStrings("Case-insensitive words for Base pins. Separate values with commas.")); configHammerList = ConfigEntry("Signs", "HammerList", "hammer,crypt,mine,boss,cave", GetDescriptionSeparatedStrings("Case-insensitive words for Hammer pins. Separate values with commas.")); configPinList = ConfigEntry("Signs", "PinList", "pin,dot,ore,vein,point", GetDescriptionSeparatedStrings("Case-insensitive words for Dot pins. Separate values with commas.")); configPortalList = ConfigEntry("Signs", "PortalList", "portal", GetDescriptionSeparatedStrings("Case-insensitive words for Portal pins. Separate values with commas.")); configFirePrefix = ConfigEntry("Signs - Prefixes", "Fire", "Fire:", GetDescriptionSeparatedStrings(PrefixDescription("Fire"))); configBasePrefix = ConfigEntry("Signs - Prefixes", "Base", "Base:", GetDescriptionSeparatedStrings(PrefixDescription("Base"))); configHammerPrefix = ConfigEntry("Signs - Prefixes", "Hammer", "Hammer:", GetDescriptionSeparatedStrings(PrefixDescription("Hammer"))); configPinPrefix = ConfigEntry("Signs - Prefixes", "Pin", "Pin:", GetDescriptionSeparatedStrings(PrefixDescription("Dot"))); configPortalPrefix = ConfigEntry("Signs - Prefixes", "Portal", "Portal:", GetDescriptionSeparatedStrings(PrefixDescription("Portal"))); configFireSuffix = ConfigEntry("Signs - Suffixes", "Fire", "", GetDescriptionSeparatedStrings(SuffixDescription("Fire"))); configBaseSuffix = ConfigEntry("Signs - Suffixes", "Base", "", GetDescriptionSeparatedStrings(SuffixDescription("Base"))); configHammerSuffix = ConfigEntry("Signs - Suffixes", "Hammer", "", GetDescriptionSeparatedStrings(SuffixDescription("Hammer"))); configPinSuffix = ConfigEntry("Signs - Suffixes", "Pin", "", GetDescriptionSeparatedStrings(SuffixDescription("Dot"))); configPortalSuffix = ConfigEntry("Signs - Suffixes", "Portal", "", GetDescriptionSeparatedStrings(SuffixDescription("Portal"))); modEnabled.SettingChanged += OnModeSettingChanged; serverAuthoritativePins.SettingChanged += OnModeSettingChanged; serverAuthoritativePinTypes.SettingChanged += OnAuthoritativeSettingChanged; serverAuthoritativeAdminsOnly.SettingChanged += OnAuthoritativeSettingChanged; serverAuthoritativeMergeDistance.SettingChanged += OnAuthoritativeSettingChanged; allowCheckedPinStatus.SettingChanged += OnCheckedStatusSettingChanged; removePinsWithoutSigns.SettingChanged += OnCleanupSettingChanged; allowSubstrings.SettingChanged += OnMatchingSettingChanged; stripHTMLTags.SettingChanged += OnMatchingSettingChanged; useStringsList.SettingChanged += OnMatchingSettingChanged; useStringsPrefix.SettingChanged += OnMatchingSettingChanged; useStringsSuffix.SettingChanged += OnMatchingSettingChanged; configFireList.SettingChanged += OnMatchingSettingChanged; configBaseList.SettingChanged += OnMatchingSettingChanged; configHammerList.SettingChanged += OnMatchingSettingChanged; configPinList.SettingChanged += OnMatchingSettingChanged; configPortalList.SettingChanged += OnMatchingSettingChanged; configFirePrefix.SettingChanged += OnMatchingSettingChanged; configBasePrefix.SettingChanged += OnMatchingSettingChanged; configHammerPrefix.SettingChanged += OnMatchingSettingChanged; configPinPrefix.SettingChanged += OnMatchingSettingChanged; configPortalPrefix.SettingChanged += OnMatchingSettingChanged; configFireSuffix.SettingChanged += OnMatchingSettingChanged; configBaseSuffix.SettingChanged += OnMatchingSettingChanged; configHammerSuffix.SettingChanged += OnMatchingSettingChanged; configPinSuffix.SettingChanged += OnMatchingSettingChanged; configPortalSuffix.SettingChanged += OnMatchingSettingChanged; } private static string PrefixDescription(string pinName) { return "Case-insensitive prefixes for " + pinName + " pins. The matched prefix is omitted from the pin name and visible sign text. Separate values with commas. Use anyPin to match any non-empty sign text."; } private static string SuffixDescription(string pinName) { return "Case-insensitive suffixes for " + pinName + " pins. The matched suffix is omitted from the pin name and visible sign text. Separate values with commas. Use anyPin to match any non-empty sign text."; } private ConfigEntry<T> ConfigEntry<T>(string group, string name, T defaultValue, ConfigDescription description, bool synchronizedSetting = true) { ConfigEntry<T> val = ((BaseUnityPlugin)this).Config.Bind<T>(group, name, defaultValue, description); SyncedConfigEntry<T> val2 = ((ConditionalConfigSync)configSync).AddConfigEntry<T>(val); ((OwnConfigEntryBase)val2).SynchronizedConfig = synchronizedSetting; return val; } private ConfigEntry<T> ConfigEntry<T>(string group, string name, T defaultValue, string description, bool synchronizedSetting = true) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Expected O, but got Unknown return ConfigEntry(group, name, defaultValue, new ConfigDescription(description, (AcceptableValueBase)null, Array.Empty<object>()), synchronizedSetting); } private static void OnMatchingSettingChanged(object sender, EventArgs args) { SignPinParser.RebuildRules(); LocalSignPins.RefreshAll(); ServerPinSync.MarkDirty(); } private static void OnModeSettingChanged(object sender, EventArgs args) { ServerPinSync.OnModeChanged(); } private static void OnAuthoritativeSettingChanged(object sender, EventArgs args) { LocalSignPins.RefreshAll(); ServerPinSync.OnAuthoritativeSettingsChanged(); } private static void OnCheckedStatusSettingChanged(object sender, EventArgs args) { LocalSignPins.RefreshAll(); ServerPinSync.MarkDirty(immediate: true); } private static void OnCleanupSettingChanged(object sender, EventArgs args) { LocalSignPins.InvalidateCleanupZone(); } private static void InitCommands() { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Expected O, but got Unknown //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Expected O, but got Unknown string text = typeof(AutoPinSigns).Namespace.ToLowerInvariant(); object obj = <>c.<>9__59_0; if (obj == null) { ConsoleEvent val = delegate(ConsoleEventArgs args) { //IL_0106: Unknown result type (might be due to invalid IL or missing references) if (args.Args.Length < 2) { PrintCommandSyntax(args); } else { switch (args.Args[1].ToLowerInvariant()) { case "clear": if (!IsEnabled) { args.Context.AddString("Auto Pin Signs is disabled."); } else if (!Object.op_Implicit((Object)(object)Player.m_localPlayer)) { args.Context.AddString("The clear command requires a local player and map."); } else { float result = 5f; if (args.Args.Length > 2 && !TryParseFloat(args.Args[2], out result)) { args.Context.AddString("Invalid range."); } else { result = Mathf.Max(0f, result); int num = RemoveSavedUserPins(((Component)Player.m_localPlayer).transform.position, result); args.Context.AddString($"Removed {num} saved user pin(s) within {result:0.##} meters."); } } break; case "status": args.Context.AddString(ServerPinSync.GetStatusText()); break; case "resync": args.Context.AddString(ServerPinSync.ForceResync()); break; default: PrintCommandSyntax(args); break; } } }; <>c.<>9__59_0 = val; obj = (object)val; } object obj2 = <>c.<>9__59_1; if (obj2 == null) { ConsoleOptionsFetcher val2 = () => new List<string> { "clear [range] - remove saved standard user pins near the player (default range: 5)", "status - show synchronization status", "resync - refresh local pins or request/rebuild the authoritative list" }; <>c.<>9__59_1 = val2; obj2 = (object)val2; } new ConsoleCommand(text, "clear [range] | status | resync", (ConsoleEvent)obj, false, false, false, false, false, (ConsoleOptionsFetcher)obj2, true, false, false); } private static void PrintCommandSyntax(ConsoleEventArgs args) { args.Context.AddString("Syntax: autopinsigns clear [range] | status | resync"); } private static bool TryParseFloat(string value, out float result) { return float.TryParse(value, NumberStyles.Float, CultureInfo.InvariantCulture, out result) || float.TryParse(value, NumberStyles.Float, CultureInfo.CurrentCulture, out result); } private static int RemoveSavedUserPins(Vector3 position, float range) { //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)Minimap.instance)) { return 0; } int num = 0; List<PinData> pins = Minimap.instance.m_pins; for (int num2 = pins.Count - 1; num2 >= 0; num2--) { PinData val = pins[num2]; if (val.m_save && IsUserPinType(val.m_type) && !ServerPinSync.ControlsPinType(val.m_type) && !(Utils.DistanceXZ(position, val.m_pos) >= range)) { Minimap.instance.RemovePin(val); num++; } } return num; } } internal static class CustomConfigs { internal sealed class ConfigurationManagerAttributes { public Action<ConfigEntryBase>? CustomDrawer; } private static Type? configManagerStyles; private static FieldInfo? fontSizeField; internal static void Awake() { Assembly assembly = null; Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); for (int i = 0; i < assemblies.Length; i++) { if (assemblies[i].GetName().Name == "ConfigurationManager") { assembly = assemblies[i]; break; } } configManagerStyles = assembly?.GetType("ConfigurationManager.ConfigurationManagerStyles"); fontSizeField = ((configManagerStyles == null) ? null : AccessTools.Field(configManagerStyles, "fontSize")); } internal static Action<ConfigEntryBase> DrawSeparatedStrings(string separator) { return delegate(ConfigEntryBase configEntry) { bool flag = IsReadOnly(configEntry); string text = (configEntry.BoxedValue as string) ?? string.Empty; string[] array = text.Split(new string[1] { separator }, StringSplitOptions.None); string[] array2 = new string[array.Length + 1]; int count = 0; bool flag2 = false; GUIStyle style = GetStyle(GUI.skin.textArea); GUIStyle style2 = GetStyle(GUI.skin.button); GUILayout.BeginVertical(Array.Empty<GUILayoutOption>()); for (int i = 0; i < array.Length; i++) { GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); string text2 = array[i]; string text3 = GUILayout.TextField(text2, style, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.ExpandWidth(true) }); if (!flag && text3 != text2) { flag2 = true; } if (GUILayout.Button("x", style2, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(21f) }) && !flag) { flag2 = true; } else { array2[count++] = text3; } if (GUILayout.Button("+", style2, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(21f) }) && !flag) { flag2 = true; array2[count++] = string.Empty; } GUILayout.EndHorizontal(); } GUILayout.EndVertical(); if (flag2) { configEntry.BoxedValue = string.Join(separator, array2, 0, count); } }; } private static bool IsReadOnly(ConfigEntryBase configEntry) { object[] tags = configEntry.Description.Tags; foreach (object obj in tags) { Type type = obj.GetType(); if (!(type.Name != "ConfigurationManagerAttributes") && type.GetField("ReadOnly")?.GetValue(obj) is bool result) { return result; } } return false; } private static GUIStyle GetStyle(GUIStyle source) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Expected O, but got Unknown if (!(fontSizeField?.GetValue(null) is int num) || source.fontSize == num) { return source; } return new GUIStyle(source) { fontSize = num }; } } internal readonly struct SignPinMatch { internal readonly PinType Type; internal readonly string Name; internal SignPinMatch(PinType type, string name) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Unknown result type (might be due to invalid IL or missing references) Type = type; Name = name; } } internal static class SignPinParser { private sealed class RuleSet { internal readonly PinType Type; internal string[] ListTokens = Array.Empty<string>(); internal string[] PrefixTokens = Array.Empty<string>(); internal string[] SuffixTokens = Array.Empty<string>(); internal HashSet<string> ExactTokens = new HashSet<string>(StringComparer.OrdinalIgnoreCase); internal bool AnyPrefix; internal bool AnySuffix; internal RuleSet(PinType type) { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) Type = type; } } internal const string AnyPinToken = "anyPin"; private static readonly RuleSet[] rules = new RuleSet[5] { new RuleSet((PinType)0), new RuleSet((PinType)1), new RuleSet((PinType)2), new RuleSet((PinType)3), new RuleSet((PinType)6) }; internal static int RulesSignature { get; private set; } internal static void RebuildRules() { if (AutoPinSigns.configFireList != null) { SetRules(rules[0], AutoPinSigns.configFireList.Value, AutoPinSigns.configFirePrefix.Value, AutoPinSigns.configFireSuffix.Value); SetRules(rules[1], AutoPinSigns.configBaseList.Value, AutoPinSigns.configBasePrefix.Value, AutoPinSigns.configBaseSuffix.Value); SetRules(rules[2], AutoPinSigns.configHammerList.Value, AutoPinSigns.configHammerPrefix.Value, AutoPinSigns.configHammerSuffix.Value); SetRules(rules[3], AutoPinSigns.configPinList.Value, AutoPinSigns.configPinPrefix.Value, AutoPinSigns.configPinSuffix.Value); SetRules(rules[4], AutoPinSigns.configPortalList.Value, AutoPinSigns.configPortalPrefix.Value, AutoPinSigns.configPortalSuffix.Value); RulesSignature = CalculateRulesSignature(); } } internal static bool TryParse(string rawText, out SignPinMatch match) { //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Unknown result type (might be due to invalid IL or missing references) match = default(SignPinMatch); if (string.IsNullOrWhiteSpace(rawText)) { return false; } string text = (AutoPinSigns.stripHTMLTags.Value ? StripRichTextTags(rawText) : rawText); text = text.Trim(); if (text.Length == 0) { return false; } if (AutoPinSigns.useStringsList.Value && TryMatchExact(text, out match)) { return true; } if (AutoPinSigns.useStringsPrefix.Value) { if (TryMatchAffix(text, prefix: true, out match, out var matchedWithoutName)) { return true; } if (matchedWithoutName) { return false; } } if (AutoPinSigns.useStringsSuffix.Value) { if (TryMatchAffix(text, prefix: false, out match, out var matchedWithoutName2)) { return true; } if (matchedWithoutName2) { return false; } } if (AutoPinSigns.useStringsList.Value && AutoPinSigns.allowSubstrings.Value && TryMatchSubstring(text, out match)) { return true; } if (AutoPinSigns.useStringsPrefix.Value && TryGetAnyPinType(prefix: true, out var type)) { match = new SignPinMatch(type, text); return true; } if (AutoPinSigns.useStringsSuffix.Value && TryGetAnyPinType(prefix: false, out var type2)) { match = new SignPinMatch(type2, text); return true; } return false; } private static bool TryMatchExact(string text, out SignPinMatch match) { //IL_0025: Unknown result type (might be due to invalid IL or missing references) for (int i = 0; i < rules.Length; i++) { RuleSet ruleSet = rules[i]; if (ruleSet.ExactTokens.Contains(text)) { match = new SignPinMatch(ruleSet.Type, text); return true; } } match = default(SignPinMatch); return false; } private static bool TryMatchAffix(string text, bool prefix, out SignPinMatch match, out bool matchedWithoutName) { //IL_0116: Unknown result type (might be due to invalid IL or missing references) RuleSet ruleSet = null; string text2 = null; for (int i = 0; i < rules.Length; i++) { RuleSet ruleSet2 = rules[i]; string[] array = (prefix ? ruleSet2.PrefixTokens : ruleSet2.SuffixTokens); foreach (string text3 in array) { if ((prefix ? text.StartsWith(text3, StringComparison.OrdinalIgnoreCase) : text.EndsWith(text3, StringComparison.OrdinalIgnoreCase)) && (text2 == null || text3.Length > text2.Length)) { ruleSet = ruleSet2; text2 = text3; } } } if (text2 == null) { match = default(SignPinMatch); matchedWithoutName = false; return false; } string text4 = (prefix ? text.Substring(text2.Length).Trim() : text.Substring(0, text.Length - text2.Length).Trim()); if (text4.Length == 0) { match = default(SignPinMatch); matchedWithoutName = true; return false; } match = new SignPinMatch(ruleSet.Type, text4); matchedWithoutName = false; return true; } private static bool TryMatchSubstring(string text, out SignPinMatch match) { //IL_009e: Unknown result type (might be due to invalid IL or missing references) RuleSet ruleSet = null; string text2 = null; for (int i = 0; i < rules.Length; i++) { RuleSet ruleSet2 = rules[i]; string[] listTokens = ruleSet2.ListTokens; foreach (string text3 in listTokens) { if (text.IndexOf(text3, StringComparison.OrdinalIgnoreCase) >= 0 && (text2 == null || text3.Length > text2.Length)) { ruleSet = ruleSet2; text2 = text3; } } } if (text2 == null) { match = default(SignPinMatch); return false; } match = new SignPinMatch(ruleSet.Type, text); return true; } private static bool TryGetAnyPinType(bool prefix, out PinType type) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected I4, but got Unknown for (int i = 0; i < rules.Length; i++) { RuleSet ruleSet = rules[i]; if (prefix ? ruleSet.AnyPrefix : ruleSet.AnySuffix) { type = (PinType)(int)ruleSet.Type; return true; } } type = (PinType)8; return false; } private static int CalculateRulesSignature() { int hash = 17; AddHash(ref hash, AutoPinSigns.useStringsList.Value); AddHash(ref hash, AutoPinSigns.useStringsPrefix.Value); AddHash(ref hash, AutoPinSigns.useStringsSuffix.Value); AddHash(ref hash, AutoPinSigns.allowSubstrings.Value); AddHash(ref hash, AutoPinSigns.stripHTMLTags.Value); AddHash(ref hash, AutoPinSigns.configFireList.Value); AddHash(ref hash, AutoPinSigns.configBaseList.Value); AddHash(ref hash, AutoPinSigns.configHammerList.Value); AddHash(ref hash, AutoPinSigns.configPinList.Value); AddHash(ref hash, AutoPinSigns.configPortalList.Value); AddHash(ref hash, AutoPinSigns.configFirePrefix.Value); AddHash(ref hash, AutoPinSigns.configBasePrefix.Value); AddHash(ref hash, AutoPinSigns.configHammerPrefix.Value); AddHash(ref hash, AutoPinSigns.configPinPrefix.Value); AddHash(ref hash, AutoPinSigns.configPortalPrefix.Value); AddHash(ref hash, AutoPinSigns.configFireSuffix.Value); AddHash(ref hash, AutoPinSigns.configBaseSuffix.Value); AddHash(ref hash, AutoPinSigns.configHammerSuffix.Value); AddHash(ref hash, AutoPinSigns.configPinSuffix.Value); AddHash(ref hash, AutoPinSigns.configPortalSuffix.Value); return hash; } private static void AddHash(ref int hash, bool value) { hash = hash * 31 + (value ? 1 : 0); } private static void AddHash(ref int hash, string value) { hash = hash * 31 + StringExtensionMethods.GetStableHashCode(value ?? string.Empty); } private static void SetRules(RuleSet rule, string list, string prefixes, string suffixes) { rule.ListTokens = ParseTokens(list, allowAnyToken: false, out var _); rule.PrefixTokens = ParseTokens(prefixes, allowAnyToken: true, out rule.AnyPrefix); rule.SuffixTokens = ParseTokens(suffixes, allowAnyToken: true, out rule.AnySuffix); rule.ExactTokens = new HashSet<string>(rule.ListTokens, StringComparer.OrdinalIgnoreCase); } private static string[] ParseTokens(string value, bool allowAnyToken, out bool hasAnyToken) { hasAnyToken = false; if (string.IsNullOrWhiteSpace(value)) { return Array.Empty<string>(); } string[] array = value.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries); List<string> list = new List<string>(array.Length); HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase); for (int i = 0; i < array.Length; i++) { string text = array[i].Trim(); if (text.Length != 0) { if (allowAnyToken && text.Equals("anyPin", StringComparison.OrdinalIgnoreCase)) { hasAnyToken = true; } else if (hashSet.Add(text)) { list.Add(text); } } } list.Sort(delegate(string left, string right) { int num = right.Length.CompareTo(left.Length); return (num != 0) ? num : StringComparer.OrdinalIgnoreCase.Compare(left, right); }); return list.ToArray(); } private static string StripRichTextTags(string text) { int num = text.IndexOf('<'); if (num < 0) { return text; } StringBuilder stringBuilder = null; int num2 = 0; for (int i = num; i < text.Length; i++) { if (text[i] != '<') { continue; } int num3 = text.IndexOf('>', i + 1); if (num3 >= 0 && IsLikelyRichTextTag(text, i + 1, num3)) { if (stringBuilder == null) { stringBuilder = new StringBuilder(text.Length); } if (i > num2) { stringBuilder.Append(text, num2, i - num2); } i = num3; num2 = num3 + 1; } } if (stringBuilder == null) { return text; } if (num2 < text.Length) { stringBuilder.Append(text, num2, text.Length - num2); } return stringBuilder.ToString(); } private static bool IsLikelyRichTextTag(string text, int start, int end) { if (start >= end) { return false; } char c = text[start]; int result; switch (c) { case '/': return start + 1 < end && char.IsLetter(text[start + 1]); default: result = (char.IsLetter(c) ? 1 : 0); break; case '#': result = 1; break; } return (byte)result != 0; } } internal static class SignPinMetadata { private const int SchemaVersion = 1; private static readonly int SchemaKey = StringExtensionMethods.GetStableHashCode("shudnal.AutoPinSigns.Pin.Schema"); private static readonly int IsPinKey = StringExtensionMethods.GetStableHashCode("shudnal.AutoPinSigns.Pin.IsPin"); private static readonly int TypeKey = StringExtensionMethods.GetStableHashCode("shudnal.AutoPinSigns.Pin.Type"); private static readonly int NameKey = StringExtensionMethods.GetStableHashCode("shudnal.AutoPinSigns.Pin.Name"); private static readonly int TextHashKey = StringExtensionMethods.GetStableHashCode("shudnal.AutoPinSigns.Pin.TextHash"); private static readonly int RulesHashKey = StringExtensionMethods.GetStableHashCode("shudnal.AutoPinSigns.Pin.RulesHash"); private static readonly int CheckedKey = StringExtensionMethods.GetStableHashCode("shudnal.AutoPinSigns.Pin.Checked"); internal static bool IsWritingCacheMetadata { get; private set; } internal static bool TryGetPinState(ZDO zdo, string rawText, bool allowMetadataWrite, out SignPinMatch match, out bool isChecked) { //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) if (rawText == null) { rawText = string.Empty; } ConfigEntry<bool> allowCheckedPinStatus = AutoPinSigns.allowCheckedPinStatus; isChecked = allowCheckedPinStatus != null && allowCheckedPinStatus.Value && zdo != null && zdo.GetBool(CheckedKey, false); if (zdo == null) { return SignPinParser.TryParse(rawText, out match); } int stableHashCode = StringExtensionMethods.GetStableHashCode(rawText); if (zdo.GetInt(SchemaKey, 0) == 1 && zdo.GetInt(TextHashKey, 0) == stableHashCode && zdo.GetInt(RulesHashKey, 0) == SignPinParser.RulesSignature) { if (!zdo.GetBool(IsPinKey, false)) { match = default(SignPinMatch); return false; } PinType val = (PinType)zdo.GetInt(TypeKey, 8); string text = zdo.GetString(NameKey, string.Empty); if (AutoPinSigns.IsUserPinType(val) && !string.IsNullOrWhiteSpace(text)) { match = new SignPinMatch(val, text); return true; } } bool flag = SignPinParser.TryParse(rawText, out match); if (allowMetadataWrite) { WriteMetadata(zdo, stableHashCode, flag, match); } return flag; } internal static bool CanWriteMetadata(Sign sign) { return Object.op_Implicit((Object)(object)sign) && Object.op_Implicit((Object)(object)sign.m_nview) && sign.m_nview.IsValid() && ((Object.op_Implicit((Object)(object)ZNet.instance) && ZNet.instance.IsServer()) || sign.m_nview.IsOwner()); } internal static bool SetChecked(Sign sign, bool value) { if (!Object.op_Implicit((Object)(object)sign) || !Object.op_Implicit((Object)(object)sign.m_nview) || !sign.m_nview.IsValid()) { return false; } if (!sign.m_nview.IsOwner()) { sign.m_nview.ClaimOwnership(); } ZDO zDO = sign.m_nview.GetZDO(); if (zDO == null) { return false; } if (zDO.GetBool(CheckedKey, false) != value) { zDO.Set(CheckedKey, value); } return true; } private static void WriteMetadata(ZDO zdo, int textHash, bool hasMatch, SignPinMatch match) { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Expected I4, but got Unknown bool isWritingCacheMetadata = IsWritingCacheMetadata; IsWritingCacheMetadata = true; try { if (hasMatch) { SetIntIfChanged(zdo, TypeKey, (int)match.Type); SetStringIfChanged(zdo, NameKey, match.Name); } else { SetIntIfChanged(zdo, TypeKey, 8); SetStringIfChanged(zdo, NameKey, string.Empty); } SetBoolIfChanged(zdo, IsPinKey, hasMatch); SetIntIfChanged(zdo, RulesHashKey, SignPinParser.RulesSignature); SetIntIfChanged(zdo, TextHashKey, textHash); SetIntIfChanged(zdo, SchemaKey, 1); } finally { IsWritingCacheMetadata = isWritingCacheMetadata; } } private static void SetIntIfChanged(ZDO zdo, int key, int value) { if (zdo.GetInt(key, int.MinValue) != value) { zdo.Set(key, value, false); } } private static void SetBoolIfChanged(ZDO zdo, int key, bool value) { if (zdo.GetBool(key, !value) != value) { zdo.Set(key, value); } } private static void SetStringIfChanged(ZDO zdo, int key, string value) { if (value == null) { value = string.Empty; } if (zdo.GetString(key, string.Empty) != value) { zdo.Set(key, value); } } } internal static class LocalSignPins { private sealed class SignState { internal readonly Sign Sign; internal readonly Piece Piece; internal readonly WearNTear WearNTear; internal string RawText = string.Empty; internal bool HasMatch; internal SignPinMatch Match; internal bool IsChecked; internal string VisibleText = string.Empty; internal bool WasEnabled; internal PinData LocalPin; internal SignState(Sign sign) { Sign = sign; Piece = ((Component)sign).GetComponent<Piece>(); WearNTear = ((Component)sign).GetComponent<WearNTear>(); } internal void UpdateFromZdo() { //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)Sign) || !Object.op_Implicit((Object)(object)Sign.m_nview) || !Sign.m_nview.IsValid()) { return; } ZDO zDO = Sign.m_nview.GetZDO(); if (zDO != null) { string text = zDO.GetString(ZDOVars.s_text, ""); SignPinMatch match; bool isChecked; bool flag = SignPinMetadata.TryGetPinState(zDO, text, SignPinMetadata.CanWriteMetadata(Sign), out match, out isChecked); bool isEnabled = AutoPinSigns.IsEnabled; bool flag2 = RawText != text || HasMatch != flag || (flag && (Match.Type != match.Type || Match.Name != match.Name)) || IsChecked != isChecked || WasEnabled != isEnabled; RawText = text; HasMatch = flag; Match = match; IsChecked = isChecked; WasEnabled = isEnabled; if (flag2) { VisibleText = ((isEnabled && flag) ? ((isChecked ? "(x) " : string.Empty) + match.Name) : text); } UpdateVisibleText(); UpdateLocalPin(); } } internal void Refresh() { UpdateFromZdo(); } internal string GetVisibleText() { return VisibleText; } internal void UpdateVisibleText() { if (Object.op_Implicit((Object)(object)Sign) && !((Object)(object)Sign.m_textWidget == (Object)null)) { string visibleText = GetVisibleText(); if (((TMP_Text)Sign.m_textWidget).text != visibleText) { ((TMP_Text)Sign.m_textWidget).SetText(visibleText); } } } internal void UpdateLocalPin() { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) //IL_00f3: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_014d: Unknown result type (might be due to invalid IL or missing references) //IL_0177: Unknown result type (might be due to invalid IL or missing references) //IL_0187: Unknown result type (might be due to invalid IL or missing references) //IL_01c4: Unknown result type (might be due to invalid IL or missing references) //IL_0221: Unknown result type (might be due to invalid IL or missing references) //IL_022c: Unknown result type (might be due to invalid IL or missing references) //IL_0247: Unknown result type (might be due to invalid IL or missing references) //IL_024d: Unknown result type (might be due to invalid IL or missing references) bool flag = HasMatch && ServerPinSync.ControlsPinType(Match.Type); if (!AutoPinSigns.IsEnabled || flag || !HasMatch || !Object.op_Implicit((Object)(object)Minimap.instance) || !Object.op_Implicit((Object)(object)Sign)) { RemoveLocalPin(); return; } if (LocalPin != null && !Minimap.instance.m_pins.Contains(LocalPin)) { claimedLocalPins.Remove(LocalPin); LocalPin = null; } if (LocalPin != null && LocalPin.m_type == Match.Type && LocalPin.m_name == Match.Name && Utils.DistanceXZ(LocalPin.m_pos, ((Component)Sign).transform.position) < 1f && LocalPin.m_checked != IsChecked) { LocalPin.m_checked = IsChecked; } if (LocalPin != null && (LocalPin.m_type != Match.Type || LocalPin.m_name != Match.Name || Utils.DistanceXZ(LocalPin.m_pos, ((Component)Sign).transform.position) >= 1f)) { RemoveLocalPin(); } if (LocalPin == null) { LocalPin = FindExistingSavedPin(((Component)Sign).transform.position, Match); } if (LocalPin != null) { LocalPin.m_checked = IsChecked; claimedLocalPins.Add(LocalPin); return; } LocalPin = Minimap.instance.AddPin(((Component)Sign).transform.position, Match.Type, Match.Name, true, IsChecked, 0L, default(PlatformUserID)); if (LocalPin != null) { claimedLocalPins.Add(LocalPin); string name = Match.Name; Sprite icon = LocalPin.m_icon; AutoPinSigns.LogInfo($"Added local map pin from sign: \"{name}\" {((icon != null) ? ((Object)icon).name : null)}, checked: {IsChecked}."); Player localPlayer = Player.m_localPlayer; if (localPlayer != null) { ((Character)localPlayer).Message((MessageType)1, "$msg_pin_added: " + Match.Name, 0, LocalPin.m_icon); } } } internal void RemoveLocalPin() { //IL_007a: Unknown result type (might be due to invalid IL or missing references) if (LocalPin != null) { claimedLocalPins.Remove(LocalPin); if (Object.op_Implicit((Object)(object)Minimap.instance) && Minimap.instance.m_pins.Contains(LocalPin)) { string name = LocalPin.m_name; Sprite icon = LocalPin.m_icon; AutoPinSigns.LogInfo($"Removed local map pin from sign: \"{name}\" {((icon != null) ? ((Object)icon).name : null)} {LocalPin.m_pos}"); Minimap.instance.RemovePin(LocalPin); } LocalPin = null; } } } [HarmonyPatch(typeof(Sign), "Awake")] private static class Sign_Awake_Register { private static void Postfix(Sign __instance) { Register(__instance); } } [HarmonyPatch(typeof(Sign), "OnCheckPermissionCompleted")] private static class Sign_OnCheckPermissionCompleted_Update { private static void Postfix(Sign __instance) { Register(__instance); } } [HarmonyPatch(typeof(Sign), "GetText")] private static class Sign_GetText_ReturnOriginalText { [HarmonyPriority(800)] private static void Postfix(Sign __instance, ref string __result) { if (AutoPinSigns.IsEnabled && signStates.TryGetValue(__instance, out var value)) { __result = value.RawText; } } } [HarmonyPatch(typeof(Sign), "UpdateText")] private static class Sign_UpdateText_ApplyVisiblePinText { [HarmonyPriority(0)] private static void Postfix(Sign __instance) { if (AutoPinSigns.IsEnabled && signStates.TryGetValue(__instance, out var value)) { value.UpdateVisibleText(); } } } [HarmonyPatch(typeof(Sign), "GetHoverText")] private static class Sign_GetHoverText_AddCheckedCommand { private static void Postfix(Sign __instance, ref string __result) { if (TryGetPinnedState(__instance, out var state)) { __result = ReplaceHoverSignText(__result, state); ConfigEntry<bool> allowCheckedPinStatus = AutoPinSigns.allowCheckedPinStatus; if (allowCheckedPinStatus != null && allowCheckedPinStatus.Value) { string text = ((ZInput.IsNonClassicFunctionality() && ZInput.IsGamepadActive()) ? "$KEY_AltKeys" : "$KEY_AltPlace"); __result += Localization.instance.Localize("\n[<color=yellow><b>" + text + " + $KEY_Use</b></color>] $hud_crossoffpin"); } } } } [HarmonyPatch(typeof(Sign), "Interact", new Type[] { typeof(Humanoid), typeof(bool), typeof(bool) })] private static class Sign_Interact_ToggleCheckedStatus { private static bool Prefix(Sign __instance, Humanoid character, bool hold, bool alt, bool __runOriginal, ref bool __result) { if (!__runOriginal) { return true; } if (!(!alt || hold)) { ConfigEntry<bool> allowCheckedPinStatus = AutoPinSigns.allowCheckedPinStatus; if (allowCheckedPinStatus != null && allowCheckedPinStatus.Value && TryGetPinnedState(__instance, out var state)) { __result = TryToggleChecked(__instance, character, state); return false; } } return true; } } [HarmonyPatch(typeof(WearNTear), "Destroy")] private static class WearNTear_Destroy_RemovePin { private static void Prefix(WearNTear __instance) { if (wearNTearStates.TryGetValue(__instance, out var value)) { value.RemoveLocalPin(); } } } [HarmonyPatch(typeof(WearNTear), "OnDestroy")] private static class WearNTear_OnDestroy_Unregister { private static void Prefix(WearNTear __instance) { if (wearNTearStates.TryGetValue(__instance, out var value)) { claimedLocalPins.Remove(value.LocalPin); signStates.Remove(value.Sign); if (Object.op_Implicit((Object)(object)value.Piece)) { pieceStates.Remove(value.Piece); } wearNTearStates.Remove(__instance); } } } [HarmonyPatch(typeof(Minimap), "SetMapData", new Type[] { typeof(byte[]) })] private static class Minimap_SetMapData_RefreshLocalPins { private static void Postfix() { if (AutoPinSigns.IsEnabled) { RefreshAll(); } } } [HarmonyPatch(typeof(ZoneSystem), "OnDestroy")] private static class ZoneSystem_OnDestroy_ClearLocalStates { private static void Prefix() { ClearStates(); } } private const float LoadedSignAuditIntervalSeconds = 0.5f; private static readonly Dictionary<Sign, SignState> signStates = new Dictionary<Sign, SignState>(); private static readonly Dictionary<Piece, SignState> pieceStates = new Dictionary<Piece, SignState>(); private static readonly Dictionary<WearNTear, SignState> wearNTearStates = new Dictionary<WearNTear, SignState>(); private static readonly List<SignState> refreshBuffer = new List<SignState>(); private static readonly List<Piece> nearbyPieces = new List<Piece>(); private static readonly HashSet<PinData> claimedLocalPins = new HashSet<PinData>(); private static Vector2i currentZone = new Vector2i(int.MinValue, int.MinValue); private static Minimap observedMinimap; private static float nextLoadedSignAuditAt; internal static void Register(Sign sign) { if (!Object.op_Implicit((Object)(object)sign)) { return; } if (!signStates.TryGetValue(sign, out var value)) { value = new SignState(sign); signStates.Add(sign, value); if (Object.op_Implicit((Object)(object)value.Piece)) { pieceStates[value.Piece] = value; } if (Object.op_Implicit((Object)(object)value.WearNTear)) { wearNTearStates[value.WearNTear] = value; } } value.UpdateFromZdo(); ServerPinSync.RegisterLoadedSign(sign); } internal static void RefreshAll() { InvalidateCleanupZone(); refreshBuffer.Clear(); foreach (SignState value in signStates.Values) { refreshBuffer.Add(value); } for (int i = 0; i < refreshBuffer.Count; i++) { refreshBuffer[i].Refresh(); } refreshBuffer.Clear(); } internal static void InvalidateCleanupZone() { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) currentZone = new Vector2i(int.MinValue, int.MinValue); } internal static void ClearStates() { //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) signStates.Clear(); pieceStates.Clear(); wearNTearStates.Clear(); refreshBuffer.Clear(); nearbyPieces.Clear(); claimedLocalPins.Clear(); currentZone = new Vector2i(int.MinValue, int.MinValue); observedMinimap = null; nextLoadedSignAuditAt = 0f; } internal static void Tick() { if ((Object)(object)observedMinimap != (Object)(object)Minimap.instance) { observedMinimap = Minimap.instance; if (Object.op_Implicit((Object)(object)observedMinimap)) { RefreshAll(); } } if (Time.realtimeSinceStartup >= nextLoadedSignAuditAt) { nextLoadedSignAuditAt = Time.realtimeSinceStartup + 0.5f; RefreshLoadedSignStates(); } TickNearbyCleanup(); } private static void RefreshLoadedSignStates() { refreshBuffer.Clear(); foreach (SignState value in signStates.Values) { refreshBuffer.Add(value); } for (int i = 0; i < refreshBuffer.Count; i++) { refreshBuffer[i].UpdateFromZdo(); } refreshBuffer.Clear(); } private static void TickNearbyCleanup() { //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00d8: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) if (!AutoPinSigns.IsEnabled) { return; } ConfigEntry<bool> removePinsWithoutSigns = AutoPinSigns.removePinsWithoutSigns; if (removePinsWithoutSigns == null || !removePinsWithoutSigns.Value || !Object.op_Implicit((Object)(object)ZNet.instance) || !Object.op_Implicit((Object)(object)Minimap.instance) || !Object.op_Implicit((Object)(object)ZoneSystem.instance)) { return; } Vector2i zone = ZoneSystem.GetZone(ZNet.instance.GetReferencePosition()); if (zone == currentZone) { return; } currentZone = zone; if (!IsCurrentZoneActive()) { return; } List<PinData> pins = Minimap.instance.m_pins; for (int num = pins.Count - 1; num >= 0; num--) { PinData val = pins[num]; if (val.m_save && AutoPinSigns.IsUserPinType(val.m_type) && !ServerPinSync.ControlsPinType(val.m_type) && !(ZoneSystem.GetZone(val.m_pos) != currentZone) && !HasRelatedLoadedSign(val)) { string name = val.m_name; Sprite icon = val.m_icon; AutoPinSigns.LogInfo($"Removed saved user pin without a matching sign: \"{name}\" {((icon != null) ? ((Object)icon).name : null)} {val.m_pos}"); Minimap.instance.RemovePin(val); } } } private static bool HasRelatedLoadedSign(PinData pin) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) nearbyPieces.Clear(); Piece.GetAllPiecesInRadius(pin.m_pos, 1f, nearbyPieces); for (int i = 0; i < nearbyPieces.Count; i++) { if (pieceStates.TryGetValue(nearbyPieces[i], out var value) && value.HasMatch && value.Match.Type == pin.m_type && value.Match.Name == pin.m_name) { return true; } } return false; } private static bool IsCurrentZoneActive() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) ZoneData value; return ZoneSystem.instance.IsZoneLoaded(currentZone) && ZoneSystem.instance.m_zones.TryGetValue(currentZone, out value) && value.m_ttl <= 0.1f; } private static PinData FindExistingSavedPin(Vector3 position, SignPinMatch match) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) List<PinData> pins = Minimap.instance.m_pins; for (int i = 0; i < pins.Count; i++) { PinData val = pins[i]; if (val.m_save && !claimedLocalPins.Contains(val) && val.m_type == match.Type && val.m_name == match.Name && Utils.DistanceXZ(position, val.m_pos) < 1f) { return val; } } return null; } private static bool TryGetPinnedState(Sign sign, out SignState state) { state = null; if (!AutoPinSigns.IsEnabled || !Object.op_Implicit((Object)(object)sign)) { return false; } if (!signStates.TryGetValue(sign, out state)) { Register(sign); if (!signStates.TryGetValue(sign, out state)) { return false; } } else { state.UpdateFromZdo(); } return state.HasMatch; } private static string ReplaceHoverSignText(string hoverText, SignState state) { if (string.IsNullOrEmpty(hoverText) || state == null) { return hoverText; } string text = state.RawText ?? string.Empty; string visibleText = state.GetVisibleText(); if (text.Length == 0 || text == visibleText) { return hoverText; } int num = hoverText.IndexOf(text, StringComparison.Ordinal); return (num < 0) ? hoverText : hoverText.Remove(num, text.Length).Insert(num, visibleText); } private static bool TryToggleChecked(Sign sign, Humanoid user, SignState state) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) ConfigEntry<bool> allowCheckedPinStatus = AutoPinSigns.allowCheckedPinStatus; if (allowCheckedPinStatus == null || !allowCheckedPinStatus.Value || !state.HasMatch) { return false; } if (!PrivateArea.CheckAccess(((Component)sign).transform.position, 0f, true, false)) { if (user != null) { ((Character)user).Message((MessageType)2, "$piece_noaccess", 0, (Sprite)null); } return true; } if (!SignPinMetadata.SetChecked(sign, !state.IsChecked)) { return true; } state.UpdateFromZdo(); AutoPinSigns.LogInfo($"Set checked status for sign pin \"{state.Match.Name}\" to {state.IsChecked}."); return true; } } internal static class ServerPinSync { private readonly struct ServerPin : IEquatable<ServerPin> { internal readonly Vector3 Position; internal readonly PinType Type; internal readonly string Name; internal readonly bool Checked; internal readonly long Creator; internal readonly string Author; internal ServerPin(Vector3 position, PinType type, string name, bool isChecked, long creator, string author) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) Position = position; Type = type; Name = name ?? string.Empty; Checked = isChecked; Creator = creator; Author = author ?? string.Empty; } public bool Equals(ServerPin other) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: 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_001a: Unknown result type (might be due to invalid IL or missing references) return Position == other.Position && Type == other.Type && Name == other.Name && Checked == other.Checked && Creator == other.Creator && Author == other.Author; } } private readonly struct ServerPinCandidate { internal readonly string ZdoId; internal readonly ServerPin Pin; internal ServerPinCandidate(string zdoId, ServerPin pin) { ZdoId = zdoId ?? string.Empty; Pin = pin; } } private readonly struct PinIdentity : IEquatable<PinIdentity> { private readonly PinType type; private readonly string name; internal PinIdentity(PinType type, string name) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Unknown result type (might be due to invalid IL or missing references) this.type = type; this.name = name ?? string.Empty; } public bool Equals(PinIdentity other) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) return type == other.type && name == other.name; } public override bool Equals(object obj) { return obj is PinIdentity other && Equals(other); } public override int GetHashCode() { //IL_0003: 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_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected I4, but got Unknown return (type * 397) ^ name.GetHashCode(); } } private sealed class PinDataReferenceComparer : IEqualityComparer<PinData> { internal static readonly PinDataReferenceComparer Instance = new PinDataReferenceComparer(); public bool Equals(PinData left, PinData right) { return left == right; } public int GetHashCode(PinData value) { return RuntimeHelpers.GetHashCode(value); } } private sealed class MapDataPinSwapState { internal readonly List<PinData> RemovedAuthoritativePins = new List<PinData>(); internal readonly List<PinData> AddedClientPins = new List<PinData>(); } private sealed class ServerPinComparer : IComparer<ServerPin> { internal static readonly ServerPinComparer Instance = new ServerPinComparer(); public int Compare(ServerPin left, ServerPin right) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Expected I4, but got Unknown //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Expected I4, but got Unknown float x = left.Position.x; int num = x.CompareTo(right.Position.x); if (num != 0) { return num; } x = left.Position.z; num = x.CompareTo(right.Position.z); if (num != 0) { return num; } x = left.Position.y; num = x.CompareTo(right.Position.y); if (num != 0) { return num; } num = ((int)left.Type).CompareTo((int)right.Type); if (num != 0) { return num; } num = string.CompareOrdinal(left.Name, right.Name); if (num != 0) { return num; } bool flag = left.Checked; num = flag.CompareTo(right.Checked); if (num != 0) { return num; } long creator = left.Creator; num = creator.CompareTo(right.Creator); if (num != 0) { return num; } return string.CompareOrdinal(left.Author, right.Author); } } private sealed class ServerPinCandidateComparer : IComparer<ServerPinCandidate> { internal static readonly ServerPinCandidateComparer Instance = new ServerPinCandidateComparer(); public int Compare(ServerPinCandidate left, ServerPinCandidate right) { return string.CompareOrdinal(left.ZdoId, right.ZdoId); } } [HarmonyPatch(typeof(ZNetScene), "Awake")] private static class ZNetScene_Awake_DiscoverSigns { private static void Postfix() { DiscoverSignPrefabs(); if (Object.op_Implicit((Object)(object)ZNet.instance) && ZNet.instance.IsServer()) { RebuildSignIndex(); MarkDirty(immediate: true); } } } [HarmonyPatch(typeof(ZDOMan), "Load")] private static class ZDOMan_Load_IndexSigns { private static void Postfix() { if (Object.op_Implicit((Object)(object)ZNet.instance) && ZNet.instance.IsServer()) { RebuildSignIndex(); MarkDirty(immediate: true); } } } [HarmonyPatch(typeof(ZDOMan), "CreateNewZDO", new Type[] { typeof(ZDOID), typeof(Vector3), typeof(int) })] private static class ZDOMan_CreateNewZDO_TrackSign { private static void Postfix(ZDO __result) { if (Object.op_Implicit((Object)(object)ZNet.instance) && ZNet.instance.IsServer() && TryTrackSignZdo(__result)) { MarkDirty(); } } } [HarmonyPatch(typeof(ZDOMan), "HandleDestroyedZDO")] private static class ZDOMan_HandleDestroyedZDO_UntrackSign { private static void Prefix(ZDOMan __instance, ZDOID uid) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) if (Object.op_Implicit((Object)(object)ZNet.instance) && ZNet.instance.IsServer()) { ZDO zDO = __instance.GetZDO(uid); if (zDO != null && signZdos.Remove(zDO)) { MarkDirty(); } } } } [HarmonyPatch(typeof(ZDO), "Deserialize")] private static class ZDO_Deserialize_TrackSignChange { private static void Postfix(ZDO __instance) { if (Object.op_Implicit((Object)(object)ZNet.instance) && ZNet.instance.IsServer() && TryTrackSignZdo(__instance)) { MarkDirty(); } } } [HarmonyPatch(typeof(ZDO), "IncreaseDataRevision")] private static class ZDO_IncreaseDataRevision_TrackSignChange { private static void Postfix(ZDO __instance) { if (!SignPinMetadata.IsWritingCacheMetadata && Object.op_Implicit((Object)(object)ZNet.instance) && ZNet.instance.IsServer() && TryTrackSignZdo(__instance)) { MarkDirty(); } } } [HarmonyPatch(typeof(Minimap), "AddPin")] private static class Minimap_AddPin_MarkProjectionDirty { private static void Postfix(PinType type, PinData __result) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) if (!isReconcilingProjection && HasConfirmedAuthority && __result != null && ControlsPinType(type)) { MarkProjectionDirty(); } } } [HarmonyPatch(typeof(Minimap), "RemovePin", new Type[] { typeof(PinData) })] private static class Minimap_RemovePin_MarkProjectionDirty { private static void Postfix(PinData pin) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) if (!isReconcilingProjection && HasConfirmedAuthority && pin != null && ControlsPinType(pin.m_type)) { MarkProjectionDirty(); } } } [HarmonyPatch(typeof(Minimap), "ShowPinNameInput")] private static class Minimap_ShowPinNameInput_PreventClientPinCreation { private static bool Prefix(Minimap __instance) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) return !ControlsPinType(__instance.m_selectedType); } } [HarmonyPatch(typeof(Minimap), "GetMapData", new Type[] { })] private static class Minimap_GetMapData_PreserveClientPins { [HarmonyPriority(800)] private static void Prefix(Minimap __instance, out MapDataPinSwapState __state) { __state = SwapAuthoritativePinsForClientPins(__instance); } [HarmonyPriority(0)] private static Exception Finalizer(Minimap __instance, MapDataPinSwapState __state, Exception __exception) { RestoreAuthoritativePinsAfterMapData(__instance, __state); return __exception; } } [HarmonyPatch(typeof(Minimap), "UpdatePins")] private static class Minimap_UpdatePins_MaintainProjection { [HarmonyPriority(800)] private static void Prefix() { MaintainAuthoritativeProjection(); } } [HarmonyPatch(typeof(Minimap), "SetMapData", new Type[] { typeof(byte[]) })] private static class Minimap_SetMapData_ReconcileServerPins { private static Exception Finalizer(Minimap __instance, Exception __exception) { if (__exception == null && HasConfirmedAuthority) { HideControlledClientPins(__instance, replaceSnapshot: true); MarkProjectionDirty(); MaintainAuthoritativeProjection(forceAudit: true); } return __exception; } } [HarmonyPatch(typeof(Minimap), "AddSharedMapData")] private static class Minimap_AddSharedMapData_ReconcileServerPins { private static Exception Finalizer(Minimap __instance, Exception __exception) { if (__exception == null && HasConfirmedAuthority) { HideControlledClientPins(__instance); MarkProjectionDirty(); MaintainAuthoritativeProjection(forceAudit: true); } return __exception; } } [HarmonyPatch(typeof(ZNet), "Shutdown")] private static class ZNet_Shutdown_Reset { private static void Prefix() { ResetSession(); } } [HarmonyPatch(typeof(ZoneSystem), "OnDestroy")] private static class ZoneSystem_OnDestroy_Reset { private static void Postfix() { ResetSession(); } } private const int ProtocolVersion = 2; private const int MaximumReceivedPins = 100000; private const float RebuildDelaySeconds = 0.35f; private const float InitialRequestDelaySeconds = 0.5f; private const float RequestRetrySeconds = 5f; private const float MinimumRequestIntervalSeconds = 1f; private const int MaximumAutomaticRequestAttempts = 6; private const int MaximumBackgroundProbeAttempts = 2; private const float ProjectionAuditIntervalSeconds = 1f; private const float ProjectionLogIntervalSeconds = 5f; private const string RequestRpc = "shudnal.AutoPinSigns.ServerPins.Request"; private const string ResponseRpc = "shudnal.AutoPinSigns.ServerPins.Response"; private static readonly HashSet<int> signPrefabHashes = new HashSet<int> { StringExtensionMethods.GetStableHashCode("sign") }; private static readonly HashSet<ZDO> signZdos = new HashSet<ZDO>(); private static readonly List<ZDO> staleZdos = new List<ZDO>(); private static readonly List<ServerPin> authoritativePins = new List<ServerPin>(); private static readonly List<ServerPin> rebuiltPins = new List<ServerPin>(); private static readonly List<ServerPinCandidate> candidatePins = new List<ServerPinCandidate>(); private static readonly Dictionary<PinIdentity, List<ServerPin>> mergedPinGroups = new Dictionary<PinIdentity, List<ServerPin>>(); private static readonly List<ServerPin> receivedPins = new List<ServerPin>(); private static readonly List<PinData> appliedPins = new List<PinData>(); private static readonly HashSet<PinData> appliedPinSet = new HashSet<PinData>(PinDataReferenceComparer.Instance); private static readonly List<PinData> hiddenClientPins = new List<PinData>(); private static readonly HashSet<PinData> hiddenClientPinSet = new HashSet<PinData>(PinDataReferenceComparer.Instance); private static readonly Dictionary<PinData, int> hiddenClientPinIndices = new Dictionary<PinData, int>(PinDataReferenceComparer.Instance); private static readonly Dictionary<long, float> nextAllowedRequestByPeer = new Dictionary<long, float>(); private static readonly MethodInfo listContainsIdMethod = AccessTools.DeclaredMethod(typeof(ZNet), "ListContainsId", (Type[])null, (Type[])null); private static readonly object[] listContainsIdArguments = new object[2]; private static ZRoutedRpc registeredRpcInstance; private static bool serverListDirty; private static bool serverModeBroadcastPending; private static float rebuildAt; private static long serverRevision; private static bool clientHasSnapshot; private static bool clientAuthorityConfirmed; private static AuthoritativePinTypes clientAuthoritativeTypes = AuthoritativePinTypes.None; private static long clientRevision = -1L; private static float nextRequestAt; private static int clientRequestAttempts; private static bool clientRequestLimitLogged; private static bool modeStateInitialized; private static bool lastAuthoritativeMode; private static Minimap projectionMinimap; private static Minimap hiddenClientPinsMinimap; private static bool projectionDirty; private static bool isReconcilingProjection; private static bool isSwappingMapDataPins; private static int lastObservedPinCount = -1; private static float nextProjectionAuditAt; private static float nextProjectionLogAt; private static bool initialProjectionCompleted; private static bool HasConfirmedAuthority => Object.op_Implicit((Object)(object)ZNet.instance) && (ZNet.instance.IsServer() ? AutoPinSigns.IsAuthoritativeMode : (AutoPinSigns.IsEnabled && clientAuthorityConfirmed)); internal static bool HasActiveAuthority => HasConfirmedAuthority; private static AuthoritativePinTypes ControlledPinTypes => (Object.op_Implicit((Object)(object)ZNet.instance) && ZNet.instance.IsServer()) ? AutoPinSigns.GetConfiguredAuthoritativePinTypes() : clientAuthoritativeTypes; internal static bool ControlsPinType(PinType pinType) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) return HasConfirmedAuthority && AutoPinSigns.IsPinTypeEnabled(ControlledPinTypes, pinType); } internal static void Tick() { if (!Object.op_Implicit((Object)(object)ZNet.instance) || ZRoutedRpc.instance == null) { return; } RegisterRpcs(); bool isAuthoritativeMode = AutoPinSigns.IsAuthoritativeMode; if (!modeStateInitialized || lastAuthoritativeMode != isAuthoritativeMode) { modeStateInitialized = true; lastAuthoritativeMode = isAuthoritativeMode; ApplyModeState(); } if (ZNet.instance.IsServer()) { if (AutoPinSigns.IsAuthoritativeMode && serverListDirty && Time.realtimeSinceStartup >= rebuildAt) { RebuildServerList(broadcastIfChanged: true); } else if (!AutoPinSigns.IsAuthoritativeMode && serverModeBroadcastPending && BroadcastSnapshot()) { serverModeBroadcastPending = false; } MaintainAuthoritativeProjection(); return; } MaintainAuthoritativeProjection(); if (!AutoPinSigns.IsEnabled) { return; } int num = (AutoPinSigns.IsAuthoritativeMode ? 6 : 2); if (!clientHasSnapshot && clientRequestAttempts < num && Time.realtimeSinceStartup >= nextRequestAt) { RequestServerPins(); } else if (!clientHasSnapshot && clientRequestAttempts >= num && !clientRequestLimitLogged) { clientRequestLimitLogged = true; if (AutoPinSigns.IsAuthoritativeMode) { AutoPinSigns.LogWarning("Authoritative pin synchronization received no server response and stopped automatic retries. Use 'autopinsigns resync' to retry."); } } } internal static void OnModeChanged() { modeStateInitialized = true; lastAuthoritativeMode = AutoPinSigns.IsAuthoritativeMode; ApplyModeState(); } internal static void OnAuthoritativeSettingsChanged() { if (Object.op_Implicit((Object)(object)ZNet.instance)) { if (ZNet.instance.IsServer()) { RemoveAppliedPins(); HideControlledClientPins(Minimap.instance); LocalSignPins.RefreshAll(); serverModeBroadcastPending = true; MarkProjectionDirty(); MarkDirty(immediate: true); } else if (clientAuthorityConfirmed) { clientHasSnapshot = false; clientRequestAttempts = 0; clientRequestLimitLogged = false; nextRequestAt = Time.realtimeSinceStartup; } } } private static void ApplyModeState() { if (Object.op_Implicit((Object)(object)ZNet.instance) && ZNet.instance.IsServer()) { if (AutoPinSigns.IsAuthoritativeMode) { HideControlledClientPins(Minimap.instance); LocalSignPins.RefreshAll(); serverModeBroadcastPending = true; initialProjectionCompleted = false; MarkProjectionDirty(); DiscoverSignPrefabs(); RebuildSignIndex(); MarkDirty(immediate: true); } else { RemoveAppliedPins(); ResetProjectionTracking(); initialProjectionCompleted = false; serverModeBroadcastPending = true; if (BroadcastSnapshot()) { serverModeBroadcastPending = false; } LocalSignPins.RefreshAll(); } } else if (!AutoPinSigns.IsEnabled) { RemoveAppliedPins(); receivedPins.Clear(); clientHasSnapshot = false; clientAuthorityConfirmed = false; clientAuthoritativeTypes = AuthoritativePinTypes.None; clientRevision = -1L; clientRequestAttempts = 0; clientRequestLimitLogged = false; initialProjectionCompleted = false; ResetProjectionTracking(); LocalSignPins.RefreshAll(); } else { if (!clientHasSnapshot || (AutoPinSigns.IsAuthoritativeMode && !clientAuthorityConfirmed)) { clientHasSnapshot = false; clientRequestAttempts = 0; clientRequestLimitLogged = false; nextRequestAt = Time.realtimeSinceStartup + 0.5f; } if (clientAuthorityConfirmed) { MarkProjectionDirty(); MaintainAuthoritativeProjection(forceAudit: true); } LocalSignPins.RefreshAll(); } } internal static void MarkDirty(bool immediate = false) { if (Object.op_Implicit((Object)(object)ZNet.instance) && ZNet.instance.IsServer()) { serverListDirty = true; float num = Time.realtimeSinceStartup + (immediate ? 0f : 0.35f); if (rebuildAt <= Time.realtimeSinceStartup || num < rebuildAt) { rebuildAt = num; } } } internal static void RegisterLoadedSign(Sign sign) { if (!Object.op_Implicit((Object)(object)sign) || !Object.op_Implicit((Object)(object)sign.m_nview) || !sign.m_nview.IsValid()) { return; } ZDO zDO = sign.m_nview.GetZDO(); if (zDO == null) { return; } bool flag = signPrefabHashes.Add(zDO.GetPrefab()); if (Object.op_Implicit((Object)(object)ZNet.instance) && ZNet.instance.IsServer()) { signZdos.Add(zDO); if (flag) { RebuildSignIndex(); } MarkDirty(); } } internal static string ForceResync() { if (!AutoPinSigns.IsEnabled) { return "Auto Pin Signs is disabled."; } if (Object.op_Implicit((Object)(object)ZNet.instance) && ZNet.instance.IsServer() && !AutoPinSigns.IsAuthoritativeMode) { LocalSignPins.RefreshAll(); return "Auto Pin Signs local sign pins refreshed."; } if (Object.op_Implicit((Object)(object)ZNet.instance) && ZNet.instance.IsServer()) { DiscoverSignPrefabs(); RebuildSignIndex(); MarkDirty(immediate: true); return "Auto Pin Signs authoritative rebuild queued."; } clientHasSnapshot = false; clientRequestAttempts = 0; clientRequestLimitLogged = false; nextRequestAt = Time.realtimeSinceStartup; return "Auto Pin Signs authoritative snapshot requested."; } internal static string GetStatusText() { if (!AutoPinSigns.IsEnabled) { return "Auto Pin Signs: disabled."; } if (!Object.op_Implicit((Object)(object)ZNet.instance)) { return AutoPinSigns.IsAuthoritativeMode ? "Auto Pin Signs: authoritative mode configured, no active network session." : "Auto Pin Signs: local sign discovery mode, no active network session."; } if (ZNet.instance.IsServer()) { if (!AutoPinSigns.IsAuthoritativeMode) { return "Auto Pin Signs: local sign discovery mode."; } return $"Auto Pin Signs: server authoritative mode, revision {serverRevision}, controlled types {ControlledPinTypes}, {authoritativePins.Count} pin(s), {appliedPins.Count} projected pin(s), {hiddenClientPins.Count} hidden client pin(s), {signZdos.Count} indexed sign ZDO(s)."; } string text = (clientAuthorityConfirmed ? "ready" : (clientHasSnapshot ? "server mode inactive" : "pending")); return $"Auto Pin Signs: server authoritative probe {text}, revision {clientRevision}, controlled types {clientAuthoritativeTypes}, {receivedPins.Count} received pin(s), {appliedPins.Count} projected pin(s), {hiddenClientPins.Count} hidden client pin(s), request attempts {clientRequestAttempts}/{6}."; } internal static void ResetSession() { RemoveAppliedPins(); signZdos.Clear(); staleZdos.Clear(); authoritativePins.Clear(); rebuiltPins.Clear(); candidatePins.Clear(); mergedPinGroups.Clear(); receivedPins.Clear(); nextAllowedRequestByPeer.Clear(); serverListDirty = false; serverModeBroadcastPending = false; rebuildAt = 0f; serverRevision = 0L; clientHasSnapshot = false; clientAuthorityConfirmed = false; clientAuthoritativeTypes = AuthoritativePinTypes.None; clientRevision = -1L; nextRequestAt = 0f; clientRequestAttempts = 0; clientRequestLimitLogged = false; modeStateInitialized = false; lastAuthoritativeMode = false; initialProjectionCompleted = false; ResetProjectionTracking(); hiddenClientPins.Clear(); hiddenClientPinSet.Clear(); hiddenClientPinIndices.Clear(); hiddenClientPinsMinimap = null; isSwappingMapDataPins = false; } private static void RegisterRpcs() { ZRoutedRpc instance = ZRoutedRpc.instance; if (instance != null && registeredRpcInstance != instance) { instance.Register<ZPackage>("shudnal.AutoPinSigns.ServerPins.Request", (Action<long, ZPackage>)RPC_RequestServerPins); instance.Register<ZPackage>("shudnal.AutoPinSigns.ServerPins.Response", (Action<long, ZPackage>)RPC_ReceiveServerPins); registeredRpcInstance = instance; } } private static void RequestServerPins() { //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Expected O, but got Unknown clientRequestAttempts++; nextRequestAt = Time.realtimeSinceStartup + 5f; if (ZRoutedRpc.instance != null && Object.op_Implicit((Object)(object)ZNet.instance) && !ZNet.instance.IsServer()) { ZPackage val = new ZPackage(); val.Write(2); val.Write(clientRevision); val.Write((int)clientAuthoritativeTypes); long serverPeerID = ZRoutedRpc.instance.GetServerPeerID(); ZRoutedRpc.instance.InvokeRoutedRPC(serverPeerID, "shudnal.AutoPinSigns.ServerPins.Request", new object[1] { val }); AutoPinSigns.LogInfo($"Requested authoritative server pins; local revision {clientRevision}."); } } private static void RPC_RequestServerPins(long sender, ZPackage package) { if (!Object.op_Implicit((Object)(object)ZNet.instance) || !ZNet.instance.IsServer() || package == null) { return; } float realtimeSinceStartup = Time.realtimeSinceStartup; if (nextAllowedRequestByPeer.TryGetValue(sender, out var value) && realtimeSinceStartup < value) { return; } nextAllowedRequestByPeer[sender] = realtimeSinceStartup + 1f; try { int num = package.ReadInt(); long requestedRevision = package.ReadLong(); AuthoritativePinTypes requestedTypes = (AuthoritativePinTypes)(package.ReadInt() & 0x1F); if (num != 2) { AutoPinSigns.LogWarning($"Ignored Auto Pin Signs request using unsupported protocol {num} from peer {sender}."); } else if (!AutoPinSigns.IsAuthoritativeMode || !serverListDirty || !RebuildServerList(broadcastIfChanged: true)) { SendSnapshot(sender, requestedRevision, requestedTypes); } } catch (Exception ex) { AutoPinSigns.LogWarning($"Failed to read Auto Pin Signs request from peer {sender}: {ex.Message}"); } } private static void SendSnapshot(long target, long requestedRevision, AuthoritativePinTypes requestedTypes) { ZPackage val = CreateSnapshotPackage(requestedRevision, requestedTypes); ZRoutedRpc.instance.InvokeRoutedRPC(target, "shudnal.AutoPinSigns.ServerPins.Response", new object[1] { val }); } private static bool BroadcastSnapshot() { if (ZRoutedRpc.instance == null || !Object.op_Implicit((Object)(object)ZNet.instance) || !ZNet.instance.IsServer()) { return false; } ZPackage val = CreateSnapshotPackage(-1L, AuthoritativePinTypes.None); ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.Everybody, "shudnal.AutoPinSigns.ServerPins.Response", new object[1] { val }); return true; } private static ZPackage CreateSnapshotPackage(long requestedRevision, AuthoritativePinTypes requestedTypes) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Expected O, but got Unknown //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Expected I4, but got Unknown ZPackage val = new ZPackage(); AuthoritativePinTypes authoritativePinTypes = (AutoPinSigns.IsAuthoritativeMode ? AutoPinSigns.GetConfiguredAuthoritativePinTypes() : AuthoritativePinTypes.None); val.Write(2); val.Write(AutoPinSigns.IsAuthoritativeMode); val.Write((int)authoritativePinTypes); val.Write(serverRevision); bool flag = AutoPinSigns.IsAuthoritativeMode && (requestedRevision != serverRevision || requestedTypes != authoritativePinTypes); val.Write(flag); if (!flag) { return val; } val.Write(authoritativePins.Count); for (int i = 0; i < authoritativePins.Count; i++) { ServerPin serverPin = authoritativePins[i]; val.Write(serverPin.Position); val.Write((int)serverPin.Type); val.Write(serverPin.Name); val.Write(serverPin.Checked); val.Write(serverPin.Creator); val.Write(serverPin.Author); } return val; } private static void RPC_ReceiveServerPins(long sender, ZPackage package) { //IL_018c: Unknown result type (might be due to invalid IL or missing references) //IL_0191: Unknown result type (might be due to invalid IL or missing references) //IL_0199: Unknown result type (might be due to invalid IL or missing references) //IL_01bc: Unknown result type (might be due to invalid IL or missing references) //IL_01ce: Unknown result type (might be due to invalid IL or missing references) //IL_01d0: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)ZNet.instance) || ZNet.instance.IsServer() || package == null || ZRoutedRpc.instance == null || sender != ZRoutedRpc.instance.GetServerPeerID()) { return; } try { int num = package.ReadInt(); if (num != 2) { AutoPinSigns.LogWarning($"Ignored authoritative pin snapshot using unsupported protocol {num}."); return; } bool flag = package.ReadBool(); AuthoritativePinTypes authoritativePinTypes = (AuthoritativePinTypes)(package.ReadInt() & 0x1F); long num2 = package.ReadLong(); bool flag2 = package.ReadBool(); if (!flag) { RemoveAppliedPins(); receivedPins.Clear(); clientHasSnapshot = true; clientAuthorityConfirmed = false; clientAuthoritativeTypes = AuthoritativePinTypes.None; clientRevision = num2; initialProjectionCompleted = false; ResetProjectionTracking(); clientRequestAttempts = 0; clientRequestLimitLogged = false; LocalSignPins.RefreshAll(); return; } if (clientAuthorityConfirmed && clientAuthoritativeTypes != (authoritativePinTypes & AuthoritativePinTypes.All)) { RemoveAppliedPins(); initialProjectionCompleted = false; } if (flag2) { int num3 = package.ReadInt(); if (num3 < 0 || num3 > 100000) { AutoPinSigns.LogWarning($"Rejected authoritative pin snapshot with invalid count {num3}."); clientHasSnapshot = false; nextRequestAt = Time.realtimeSinceStartup + 5f; return; } receivedPins.Clear(); for (int i = 0; i < num3; i++) { Vector3 position = package.ReadVector3(); PinType val = (PinType)package.ReadInt(); string name = package.ReadString(); bool isChecked = package.ReadBool(); long creator = package.ReadLong(); string author = package.ReadString(); if (AutoPinSigns.IsPinTypeEnabled(authoritativePinTypes, val)) { receivedPins.Add(new ServerPin(position, val, name, isChecked, creator, author)); } } } bool flag3 = !clientAuthorityConfirmed; clientRevision = num2; clientAuthoritativeTypes = authoritativePinTypes & AuthoritativePinTypes.All; clientHasSnapshot = true; clientAuthorityConfirmed = true; if (flag3) { initialProjectionCompleted = false; } clientRequestAttempts = 0; clientRequestLimitLogged = false; MarkProjectionDirty(); MaintainAuthoritativeProjection(forceAudit: true); LocalSignPins.RefreshAll(); AutoPinSigns.LogInfo($"Received authoritative server pins revision {clientRevision}; {receivedPins.Count} pin(s), controlled types: {clientAuthoritativeTypes}."); } catch (Exception ex) { clientHasSnapshot = false; nextRequestAt = Time.realtimeSinceStartup + 5f; AutoPinSigns.LogWarning("Failed to read authoritative pin snapshot: " + ex.Message); } } private static List<ServerPin> GetProjectionSource() { return (Object.op_Implicit((Object)(object)ZNet.instance) && ZNet.instance.IsServer()) ? authoritativePins : receivedPins; } private static void MarkProjectionDirty() { projectionDirty = true; nextProjectionAuditAt = 0f; } private static void ResetProjectionTracking() { projectionMinimap = null; projectionDirty = false; isReconcilingProjection = false; lastObservedPinCount = -1; nextProjectionAuditAt = 0f; nextProjectionLogAt = 0f; appliedPins.Clear(); appliedPinSet.Clear(); } private static void MaintainAuthoritativeProjection(bool forceAudit = false) { if (!HasConfirmedAuthority || !Object.op_Implicit((Object)(object)Minimap.instance) || isReconcilingProjection || isSwappingMapDataPins) { return; } Minimap instance = Minimap.instance; if (projectionMinimap != instance) { projectionMinimap = instance; appliedPins.Clear(); appliedPinSet.Clear(); if (Object.op_Implicit((Object)(object)hiddenClientPinsMinimap) && hiddenClientPinsMinimap != instance) { hiddenClientPins.Clear(); hiddenClientPinSet.Clear(); hiddenClientPinIndices.Clear(); hiddenClientPinsMinimap = null; } lastObservedPinCount = -1; MarkProjectionDirty(); } float realtimeSinceStartup = Time.realtimeSinceStartup; int count = instance.m_pins.Count; bool flag = count != lastObservedPinCount; bool flag2 = realtimeSinceStartup >= nextProjectionAuditAt; if ((!projectionDirty || forceAudit || !(realtimeSinceStartup < nextProjectionAuditAt)) && (projectionDirty || flag || forceAudit || flag2)) { List<ServerPin> projectionSource = GetProjectionSource(); if (!ProjectionMatches(instance, projectionSource)) { RebuildProjection(instance, projectionSource); return; } projectionDirty = false; lastObservedPinCount = count; nextProjectionAuditAt = realtimeSinceStartup + 1f; } } private static bool ProjectionMatches(Minimap minimap, List<ServerPin> source) { //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_00f7: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) if (appliedPins.Count != source.Count || appliedPinSet.Count != source.Count) { return false; } int num = 0; List<PinData> pins = minimap.m_pins; for (int i = 0; i < pins.Count; i++) { PinData val = pins[i]; if (val != null && ControlsPinType(val.m_type)) { if (!appliedPinSet.Contains(val)) { return false; } num++; } } if (num != appliedPins.Count) { return false; } for (int j = 0; j < source.Count; j++) { PinData val2 = appliedPins[j]; ServerPin serverPin = source[j]; if (val2 == null || val2.m_save || val2.m_type != serverPin.Type || val2.m_name != serverPin.Name || val2.m_checked != serverPin.Checked || val2.m_pos != serverPin.Position) { return false; } } return true; } private static int HideControlledClientPins(Minimap minimap, bool replaceSnapshot = false) { //IL_007d: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)minimap)) { return 0; } if (replaceSnapshot || hiddenClientPinsMinimap != minimap) { hiddenClientPins.Clear(); hiddenClientPinSet.Clear(); hiddenClientPinIndices.Clear(); hiddenClientPinsMinimap = minimap; } int num = 0; List<PinData> pins = minimap.m_pins; for (int i = 0; i < pins.Count; i++) { PinData val = pins[i]; if (val != null && val.m_save && ControlsPinType(val.m_type) && !appliedPinSet.Contains(val) && hiddenClientPinSet.Add(val)) { hiddenClientPins.Add(val); hiddenClientPinIndices[val] = i; num++; } } for (int num2 = pins.Count - 1; num2 >= 0; num2--) { PinData val2 = pins[num2]; if (val2 != null && hiddenClientPinSet.Contains(val2)) { minimap.RemovePin(val2); } } return num; } private static void RestoreHiddenClientPins(Minimap minimap) { if (!Object.op_Implicit((Object)(object)minimap) || hiddenClientPinsMinimap != minimap) { return; } List<PinData> pins = minimap.m_pins; for (int i = 0; i < hiddenClientPins.Count; i++) { PinData val = hiddenClientPins[i]; if (val != null && !pins.Contains(val)) { int value; int index = (hiddenClientPinIndices.TryGetValue(val, out value) ? Mathf.Clamp(value, 0, pins.Count) : pins.Count); pins.Insert(index, val); } } hiddenClientPins.Clear(); hiddenClientPinSet.Clear(); hiddenClientPinIndices.Clear(); hiddenClientPinsMinimap = null; } private static MapDataPinSwapState SwapAuthoritativePinsForClientPins(Minimap minimap) { if (!Object.op_Implicit((Object)(object)minimap) || isSwappingMapDataPins) { return null; } if (Object.op_Implicit((Object)(object)hiddenClientPinsMinimap) && hiddenClientPinsMinimap != minimap) { hiddenClientPins.Clear(); hiddenClientPinSet.Clear(); hiddenClientPinIndices.Clear(); hiddenClientPinsMinimap = null; } MapDataPinSwapState mapDataPinSwapState = null; try { if (HasConfirmedAuthority) { isReconcilingProjection = true; try { HideControlledClientPins(minimap); } finally { isReconcilingProjection = false; } } if (appliedPins.Count == 0 && hiddenClientPins.Count == 0) { return null; } mapDataPinSwapState = new MapDataPinSwapState(); isSwappingMapDataPins = true; List<PinData> pins = minimap.m_pins; for (int num = pins.Count - 1; num >= 0; num--) { PinData val = pins[num]; if (val != null && appliedPinSet.Contains(val)) { mapDataPinSwapState.RemovedAuthoritativePins.Insert(0, val); pins.RemoveAt(num); } } for (int i = 0; i < hiddenClientPins.Count; i++) { PinData val2 = hiddenClientPins[i]; if (val2 != null && !pins.Contains(val2)) { int value; int index = (hiddenClientPinIndices.TryGetValue(val2, out value) ? Mathf.Clamp(value, 0, pins.Count) : pins.Count); pins.Insert(index, val2); mapDataPinSwapState.AddedClientPins.Add(val2); } } return mapDataPinSwapState; } catch (Exception ex) { RestoreAuthoritativePinsAfterMapData(minimap, mapDataPinSwapState); RestoreHiddenClientPins(minimap); MarkProjectionDirty(); AutoPinSigns.LogWarning("Failed to prepare client pins for player profile serialization: " + ex.Message); return null; } } private static void RestoreAuthoritativePinsAfterMapData(Minimap minimap, MapDataPinSwapState state) { if (state == null) { return; } try { if (!Object.op_Implicit((Object)(object)minimap)) { return; } List<PinData> pins = minimap.m_pins; for (int num = state.AddedClientPins.Count - 1; num >= 0; num--) { pins.Remove(state.AddedClientPins[num]); } for (int i = 0; i < state.RemovedAuthoritativePins.Count; i++) { PinData val = state.RemovedAuthoritativePins[i]; if (val != null && !pins.Contains(val)) { pins.Add(val); } } lastObservedPinCount = pins.Count; } catch (Exception ex) { MarkProjectionDirty(); AutoPinSigns.LogWarning("Failed to restore authoritative pins after player profile serialization: " + ex.Message); } finally { isSwappingMapDataPins = false; } } private static void RebuildProjection(Minimap minimap, List<ServerPin> source) { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) isReconcilingProjection = true; bool firstProjection = !initialProjectionCompleted; bool flag = false; int hiddenCount = 0; try { hiddenCount = HideControlledClientPins(minimap); List<PinData> pins = minimap.m_pins; for (int num = pins.Count - 1; num >= 0; num--) { PinData val = pins[num]; if (val != null && ControlsPinType(val.m_type)) { minimap.RemovePin(val); } } appliedPins.Clear(); appliedPinSet.Clear(); for (int i = 0; i < source.Count; i++) { ServerPin serverPin = source[i]; PinData val2 = minimap.AddPin(serverPin.Position, serverPin.Type, serverPin.Name, false, serverPin.Checked, serverPin.Creator, ResolveAuthor(serverPin.Author)); if (val2 != null) { appliedPins.Add(val2); appliedPinSet.Add(val2); } } flag = appliedPins.Count == source.Count; if (!flag) { AutoPinSigns.LogWarning($"Authoritative pin projection created {appliedPins.Count} of {source.Count} expected pin(s); retrying later."); } } catch (Exception ex) { AutoPinSigns.LogWarning("Failed to reconcile authoritative pin projection: " + ex.