using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.IO.Compression; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Runtime.Versioning; using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; using System.Threading; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using JetBrains.Annotations; using Jotunn.Configs; using Jotunn.Managers; using Microsoft.CodeAnalysis; using ServerSync; using Splatform; using TMPro; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.Events; using UnityEngine.UI; using YamlDotNet.Core; using YamlDotNet.Core.Events; using YamlDotNet.Core.ObjectPool; using YamlDotNet.Core.Tokens; using YamlDotNet.Helpers; using YamlDotNet.RepresentationModel; using YamlDotNet.Serialization; using YamlDotNet.Serialization.BufferedDeserialization; using YamlDotNet.Serialization.BufferedDeserialization.TypeDiscriminators; using YamlDotNet.Serialization.Callbacks; using YamlDotNet.Serialization.Converters; using YamlDotNet.Serialization.EventEmitters; using YamlDotNet.Serialization.NamingConventions; using YamlDotNet.Serialization.NodeDeserializers; using YamlDotNet.Serialization.NodeTypeResolvers; using YamlDotNet.Serialization.ObjectFactories; using YamlDotNet.Serialization.ObjectGraphTraversalStrategies; using YamlDotNet.Serialization.ObjectGraphVisitors; using YamlDotNet.Serialization.Schemas; using YamlDotNet.Serialization.TypeInspectors; using YamlDotNet.Serialization.TypeResolvers; using YamlDotNet.Serialization.Utilities; using YamlDotNet.Serialization.ValueDeserializers; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("PortalRules")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("sighsorry")] [assembly: AssemblyProduct("PortalRules")] [assembly: AssemblyCopyright("Copyright © 2026 sighsorry")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("4358610B-F3F4-4843-B7AF-98B7BC60DCDE")] [assembly: AssemblyFileVersion("1.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyVersion("1.0.0.0")] 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 PortalRules { internal static class AdminPortalBiomeDefaults { private sealed class BiomeDefaultsYaml { [YamlMember(Alias = "format_version")] public int FormatVersion { get; set; } [YamlMember(Alias = "biomes")] public Dictionary? Biomes { get; set; } } [HarmonyPatch(typeof(ZoneSystem), "Start")] [HarmonyAfter(new string[] { "expand_world_data" })] [HarmonyPriority(0)] private static class BiomeRegistryReadyPatch { private static void Postfix() { if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer()) { CompleteBiomeRegistryInitialization(); } } } private const int FormatVersion = 1; private const int MaximumBiomeCount = 4096; private const int MaximumBiomeNameLength = 128; private const int MaximumReloadAttempts = 5; private const long MaximumFileBytes = 524288L; private const string DirectoryName = "PortalRules"; private const string FileName = "admin-portal-biome-global-keys.yml"; private const float RegistryReadyFallbackSeconds = 5f; private static readonly KeyValuePair[] VanillaTemplate = new KeyValuePair[9] { new KeyValuePair("Meadows", ""), new KeyValuePair("BlackForest", "defeated_eikthyr"), new KeyValuePair("Swamp", "defeated_gdking"), new KeyValuePair("Ocean", "defeated_bonemass"), new KeyValuePair("Mountain", "defeated_bonemass"), new KeyValuePair("Plains", "defeated_dragon"), new KeyValuePair("Mistlands", "defeated_goblinking"), new KeyValuePair("AshLands", "defeated_queen"), new KeyValuePair("DeepNorth", "defeated_fader") }; private static readonly HashSet AmbiguousPlainYamlScalars = new HashSet(StringComparer.OrdinalIgnoreCase) { "null", "true", "false", "y", "n", "yes", "no", "on", "off" }; private static readonly TimeSpan ReloadDebounce = TimeSpan.FromMilliseconds(500.0); private static readonly TimeSpan ReloadRetryDelay = TimeSpan.FromSeconds(1.0); private static readonly TimeSpan PollingInterval = TimeSpan.FromSeconds(30.0); private static readonly TimeSpan WatcherRestartRetryDelay = TimeSpan.FromSeconds(10.0); private static readonly IDeserializer Deserializer = new DeserializerBuilder().WithDuplicateKeyChecking().Build(); private static Dictionary _defaults = new Dictionary(StringComparer.OrdinalIgnoreCase); private static FileSystemWatcher? _watcher; private static string _configurationDirectory = ""; private static string _filePath = ""; private static bool _active; private static bool _hasValidSnapshot; private static bool _templatePending; private static bool _biomeRegistryReady; private static bool _registryFallbackLogged; private static float _registryFallbackNotBefore; private static DateTime _templateRetryNotBeforeUtc; private static DateTime _nextPollUtc; private static int _reloadRequested; private static int _reloadAttempts; private static int _watcherRestartRequested; private static long _reloadNotBeforeUtcTicks; private static long _watcherRestartNotBeforeUtcTicks; internal static bool IsActive => _active; internal static bool IsBiomeRegistryReady => _biomeRegistryReady; internal static bool HasValidSnapshot { get { if (_active) { return _hasValidSnapshot; } return false; } } internal static void BeginServerSession() { if (_active || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer()) { return; } bool biomeRegistryReady = _biomeRegistryReady; EndServerSession(); _active = true; _defaults = new Dictionary(StringComparer.OrdinalIgnoreCase); _hasValidSnapshot = false; _templatePending = false; _biomeRegistryReady = biomeRegistryReady; _registryFallbackLogged = false; _registryFallbackNotBefore = Time.realtimeSinceStartup + 5f; _templateRetryNotBeforeUtc = DateTime.MinValue; _nextPollUtc = DateTime.UtcNow + PollingInterval; Interlocked.Exchange(ref _reloadRequested, 0); Interlocked.Exchange(ref _reloadAttempts, 0); Interlocked.Exchange(ref _watcherRestartRequested, 0); Interlocked.Exchange(ref _reloadNotBeforeUtcTicks, 0L); Interlocked.Exchange(ref _watcherRestartNotBeforeUtcTicks, 0L); _configurationDirectory = Path.Combine(Paths.ConfigPath, "PortalRules"); _filePath = Path.Combine(_configurationDirectory, "admin-portal-biome-global-keys.yml"); try { Directory.CreateDirectory(_configurationDirectory); _templatePending = !File.Exists(_filePath); try { StartWatcher(); } catch (Exception ex) { StopWatcher(); Interlocked.Exchange(ref _watcherRestartRequested, 1); ScheduleWatcherRestart(WatcherRestartRetryDelay); PortalRulesPlugin.PortalRulesLogger.LogError((object)("Failed to watch admin-portal-biome-global-keys.yml; periodic reload remains active: " + ex.Message)); } bool changed; if (_templatePending) { PortalRulesPlugin.PortalRulesLogger.LogInfo((object)"admin-portal-biome-global-keys.yml will be generated after the biome registry is ready."); } else if (!TryReload(logOnlyWhenChanged: false, out changed)) { ScheduleReload(ReloadRetryDelay); } } catch (Exception ex2) { StopWatcher(); _active = false; _templatePending = false; PortalRulesPlugin.PortalRulesLogger.LogError((object)("Failed to initialize admin-portal-biome-global-keys.yml: " + ex2.Message)); } } internal static bool EnsureTemplateReady() { if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer()) { return false; } BeginServerSession(); if (!_active) { return false; } if (!_templatePending) { if (_hasValidSnapshot || !File.Exists(_filePath)) { return _hasValidSnapshot; } return TryReloadImmediately(); } if (!File.Exists(_filePath) && DateTime.UtcNow < _templateRetryNotBeforeUtc) { return false; } string filePath = _filePath; try { bool flag = false; if (!File.Exists(filePath)) { string content = BuildRegistryTemplate(); flag = TryCreateFileWithoutOverwrite(filePath, content); } if (!_active || !string.Equals(_filePath, filePath, StringComparison.Ordinal) || !File.Exists(filePath)) { return false; } _templatePending = false; _templateRetryNotBeforeUtc = DateTime.MinValue; if (flag) { PortalRulesPlugin.PortalRulesLogger.LogInfo((object)"Generated admin-portal-biome-global-keys.yml from the registered biome names."); } return TryReloadImmediately(); } catch (Exception ex) { _templateRetryNotBeforeUtc = DateTime.UtcNow + ReloadRetryDelay; PortalRulesPlugin.PortalRulesLogger.LogError((object)("Failed to generate admin-portal-biome-global-keys.yml; it will remain pending until the next retry: " + ex.Message)); return false; } } internal static void EndServerSession() { StopWatcher(); _active = false; _defaults = new Dictionary(StringComparer.OrdinalIgnoreCase); _hasValidSnapshot = false; _templatePending = false; _biomeRegistryReady = false; _registryFallbackLogged = false; _registryFallbackNotBefore = 0f; _templateRetryNotBeforeUtc = DateTime.MinValue; _nextPollUtc = DateTime.MinValue; _configurationDirectory = ""; _filePath = ""; Interlocked.Exchange(ref _reloadRequested, 0); Interlocked.Exchange(ref _reloadAttempts, 0); Interlocked.Exchange(ref _watcherRestartRequested, 0); Interlocked.Exchange(ref _reloadNotBeforeUtcTicks, 0L); Interlocked.Exchange(ref _watcherRestartNotBeforeUtcTicks, 0L); } internal static bool Tick() { TryCompleteBiomeRegistryFallback(); if (!_active) { return false; } DateTime utcNow = DateTime.UtcNow; if (Volatile.Read(in _watcherRestartRequested) != 0 && utcNow.Ticks >= Interlocked.Read(in _watcherRestartNotBeforeUtcTicks)) { Interlocked.Exchange(ref _watcherRestartRequested, 0); try { StartWatcher(); RequestReload(); } catch (Exception ex) { PortalRulesPlugin.PortalRulesLogger.LogError((object)("Failed to restart the admin-portal-biome-global-keys.yml watcher: " + ex.Message)); Interlocked.Exchange(ref _watcherRestartRequested, 1); ScheduleWatcherRestart(WatcherRestartRetryDelay); } } if (_templatePending) { if (!File.Exists(_filePath)) { if (_biomeRegistryReady && utcNow >= _templateRetryNotBeforeUtc) { bool hasValidSnapshot = _hasValidSnapshot; if (EnsureTemplateReady()) { return !hasValidSnapshot; } return false; } return false; } _templatePending = false; ScheduleReload(TimeSpan.Zero); } if (utcNow >= _nextPollUtc) { _nextPollUtc = utcNow + PollingInterval; if (Volatile.Read(in _reloadRequested) == 0) { ScheduleReload(TimeSpan.Zero); } } if (Volatile.Read(in _reloadRequested) == 0 || utcNow.Ticks < Interlocked.Read(in _reloadNotBeforeUtcTicks)) { return false; } Interlocked.Exchange(ref _reloadRequested, 0); if (TryReload(logOnlyWhenChanged: true, out var changed)) { Interlocked.Exchange(ref _reloadAttempts, 0); return changed; } if (Interlocked.Increment(ref _reloadAttempts) <= 5) { ScheduleReload(ReloadRetryDelay); } return false; } private static void TryCompleteBiomeRegistryFallback() { if (!_biomeRegistryReady && !((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer() && !((Object)(object)ZoneSystem.instance == (Object)null) && !(Time.realtimeSinceStartup < _registryFallbackNotBefore)) { if (!_registryFallbackLogged) { _registryFallbackLogged = true; PortalRulesPlugin.PortalRulesLogger.LogWarning((object)"The ZoneSystem biome-registry completion hook was not observed; using the currently registered biome names as a fallback."); } CompleteBiomeRegistryInitialization(); } } private static void CompleteBiomeRegistryInitialization() { BeginServerSession(); if (!_biomeRegistryReady) { _biomeRegistryReady = true; if (_active) { EnsureTemplateReady(); } PublicPortalCatalog.RefreshAndBroadcast(); } } internal static bool TryResolve(Vector3 position, out string requiredGlobalKey) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) requiredGlobalKey = ""; if (!_active || !_hasValidSnapshot || WorldGenerator.instance == null) { return false; } Biome biome = WorldGenerator.instance.GetBiome(position); string name = Enum.GetName(typeof(Biome), biome); if (!string.IsNullOrWhiteSpace(name)) { return _defaults.TryGetValue(name.Trim(), out requiredGlobalKey); } return false; } private static string BuildRegistryTemplate() { return BuildRegistryTemplate(Enum.GetNames(typeof(Biome))); } private static string BuildRegistryTemplate(string[] registeredNames) { if (registeredNames.Length > 4096) { throw new InvalidDataException($"biome registry exceeds {4096} entries"); } Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); for (int i = 0; i < registeredNames.Length; i++) { string text = (registeredNames[i] ?? "").Trim(); if (!string.Equals(text, "None", StringComparison.OrdinalIgnoreCase) && !string.Equals(text, "All", StringComparison.OrdinalIgnoreCase)) { try { ValidateBiomeName(text); } catch (InvalidDataException) { string value = ((text.Length <= 64) ? text : (text.Substring(0, 64) + "...")); PortalRulesPlugin.PortalRulesLogger.LogWarning((object)("Skipped invalid registered biome name " + FormatYamlKey(value) + " while generating admin-portal-biome-global-keys.yml.")); continue; } if (!dictionary.TryGetValue(text, out var value2) || string.CompareOrdinal(text, value2) < 0) { dictionary[text] = text; } } } StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("# PortalRules default GlobalKey requirements for newly initialized Admin portals."); stringBuilder.AppendLine("# This file is generated once from the server's registered biome names and is"); stringBuilder.AppendLine("# never automatically edited. Delete it before the next server session to rebuild it."); stringBuilder.AppendLine("# Biome names are case-insensitive. Expand World Data custom biomes use the stable"); stringBuilder.AppendLine("# `biome:` name from its biome YAML, not a translated name or numeric biome value."); stringBuilder.AppendLine("# The biome is resolved from each portal's actual world position."); stringBuilder.AppendLine("# A RequiredGlobalKey already stored in the portal ZDO wins over this default."); stringBuilder.AppendLine("# This includes InfinityHammer blueprint data and EWD objectData/locationObjectData."); stringBuilder.AppendLine("# An explicitly stored empty RequiredGlobalKey suppresses the biome default."); stringBuilder.AppendLine("# With YouAreNotWorthy, the selected key is checked per character; otherwise it"); stringBuilder.AppendLine("# is checked against the world's shared GlobalKeys."); stringBuilder.AppendLine("# An empty value means that biome has no default required GlobalKey."); stringBuilder.AppendLine("# Reloads affect only Admin portals whose initial authority is resolved afterwards."); stringBuilder.Append("format_version: ").AppendLine(1.ToString(CultureInfo.InvariantCulture)); stringBuilder.AppendLine("biomes:"); KeyValuePair[] vanillaTemplate = VanillaTemplate; for (int i = 0; i < vanillaTemplate.Length; i++) { KeyValuePair keyValuePair = vanillaTemplate[i]; dictionary.Remove(keyValuePair.Key); AppendBiome(stringBuilder, keyValuePair.Key, keyValuePair.Value); } List list = new List(dictionary.Values); list.Sort(CompareBiomeNames); if (VanillaTemplate.Length + list.Count > 4096) { throw new InvalidDataException($"generated biome list exceeds {4096} entries"); } foreach (string item in list) { AppendBiome(stringBuilder, item, ""); } string text2 = stringBuilder.ToString(); if ((long)Encoding.UTF8.GetByteCount(text2) > 524288L) { throw new InvalidDataException($"generated template exceeds {524288L} bytes"); } return text2; } private static int CompareBiomeNames(string left, string right) { int num = StringComparer.OrdinalIgnoreCase.Compare(left, right); if (num == 0) { return StringComparer.Ordinal.Compare(left, right); } return num; } private static void AppendBiome(StringBuilder yaml, string biomeName, string requiredGlobalKey) { yaml.Append(" ").Append(FormatYamlKey(biomeName)).Append(':'); if (requiredGlobalKey.Length != 0) { yaml.Append(' ').Append(requiredGlobalKey); } yaml.AppendLine(); } private static string FormatYamlKey(string value) { if (IsSafePlainYamlKey(value)) { return value; } StringBuilder stringBuilder = new StringBuilder(value.Length + 2); stringBuilder.Append('"'); foreach (char c in value) { switch (c) { case '\\': stringBuilder.Append("\\\\"); continue; case '"': stringBuilder.Append("\\\""); continue; case '\u2028': case '\u2029': { StringBuilder stringBuilder2 = stringBuilder.Append("\\u"); int num = c; stringBuilder2.Append(num.ToString("X4", CultureInfo.InvariantCulture)); continue; } } if (char.IsControl(c)) { StringBuilder stringBuilder3 = stringBuilder.Append("\\u"); int num = c; stringBuilder3.Append(num.ToString("X4", CultureInfo.InvariantCulture)); } else { stringBuilder.Append(c); } } return stringBuilder.Append('"').ToString(); } private static bool IsSafePlainYamlKey(string value) { if (value.Length == 0 || AmbiguousPlainYamlScalars.Contains(value) || (!char.IsLetter(value[0]) && value[0] != '_')) { return false; } foreach (char c in value) { if (!char.IsLetterOrDigit(c) && c != '_' && c != '-') { return false; } } return true; } private static bool TryCreateFileWithoutOverwrite(string path, string content) { string text = Path.Combine(Path.GetDirectoryName(path) ?? throw new InvalidOperationException("Configuration directory is unavailable."), "." + Path.GetFileName(path) + "." + Guid.NewGuid().ToString("N") + ".pending"); try { PortalAccountStore.WriteAtomicFile(text, content, null); try { File.Move(text, path); return true; } catch (IOException) when (File.Exists(path)) { return false; } } finally { if (File.Exists(text)) { File.Delete(text); } } } private static bool TryReloadImmediately() { Interlocked.Exchange(ref _reloadRequested, 0); Interlocked.Exchange(ref _reloadAttempts, 0); if (TryReload(logOnlyWhenChanged: false, out var _)) { _nextPollUtc = DateTime.UtcNow + PollingInterval; return true; } ScheduleReload(ReloadRetryDelay); return false; } private static bool TryReload(bool logOnlyWhenChanged, out bool changed) { changed = false; if (!File.Exists(_filePath)) { PortalRulesPlugin.PortalRulesLogger.LogWarning((object)"admin-portal-biome-global-keys.yml is missing; retaining the last valid biome defaults. Use 'biomes: {}' to clear them."); return false; } try { if (new FileInfo(_filePath).Length > 524288) { throw new InvalidDataException($"file exceeds {524288L} bytes"); } BiomeDefaultsYaml biomeDefaultsYaml = Deserializer.Deserialize(File.ReadAllText(_filePath)); if (biomeDefaultsYaml == null || biomeDefaultsYaml.FormatVersion != 1 || biomeDefaultsYaml.Biomes == null) { throw new InvalidDataException($"format_version must be {1} and biomes must be present"); } if (biomeDefaultsYaml.Biomes.Count > 4096) { throw new InvalidDataException($"biomes exceeds {4096} entries"); } Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (KeyValuePair biome in biomeDefaultsYaml.Biomes) { string text = (biome.Key ?? "").Trim(); ValidateBiomeName(text); if (!PublicPortalData.TryNormalizeRequiredGlobalKey(biome.Value, out string normalized, out RequiredGlobalKeyValidationFailure failure)) { throw new InvalidDataException($"GlobalKey for biome '{text}' is invalid ({failure})"); } if (dictionary.ContainsKey(text)) { throw new InvalidDataException("biome '" + text + "' is duplicated after case and whitespace normalization"); } dictionary.Add(text, normalized); } changed = !_hasValidSnapshot || !DictionariesEqual(_defaults, dictionary); _defaults = dictionary; _hasValidSnapshot = true; if (!logOnlyWhenChanged | changed) { PortalRulesPlugin.PortalRulesLogger.LogInfo((object)$"Loaded {_defaults.Count} Admin portal biome GlobalKey default(s)."); } return true; } catch (Exception ex) { PortalRulesPlugin.PortalRulesLogger.LogError((object)("Failed to reload admin-portal-biome-global-keys.yml; retaining the last valid biome defaults: " + ex.Message)); return false; } } private static void ValidateBiomeName(string biomeName) { if (biomeName.Length == 0) { throw new InvalidDataException("biome names must not be empty"); } if (biomeName.Length > 128) { throw new InvalidDataException($"biome name '{biomeName}' exceeds {128} characters"); } for (int i = 0; i < biomeName.Length; i++) { if (char.IsControl(biomeName[i])) { throw new InvalidDataException("biome name '" + biomeName + "' contains control characters"); } } if (long.TryParse(biomeName, NumberStyles.Integer, CultureInfo.InvariantCulture, out var _)) { throw new InvalidDataException("biome '" + biomeName + "' is numeric; use its stable biome name instead"); } } private static bool DictionariesEqual(Dictionary left, Dictionary right) { if (left.Count != right.Count) { return false; } foreach (KeyValuePair item in left) { if (!right.TryGetValue(item.Key, out string value) || !string.Equals(value, item.Value, StringComparison.Ordinal)) { return false; } } return true; } private static void StartWatcher() { StopWatcher(); _watcher = new FileSystemWatcher(_configurationDirectory, "admin-portal-biome-global-keys.yml") { IncludeSubdirectories = false, NotifyFilter = (NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.LastWrite) }; _watcher.Changed += OnFileChanged; _watcher.Created += OnFileChanged; _watcher.Deleted += OnFileChanged; _watcher.Renamed += OnFileRenamed; _watcher.Error += OnWatcherError; _watcher.EnableRaisingEvents = true; } private static void StopWatcher() { if (_watcher != null) { _watcher.EnableRaisingEvents = false; _watcher.Changed -= OnFileChanged; _watcher.Created -= OnFileChanged; _watcher.Deleted -= OnFileChanged; _watcher.Renamed -= OnFileRenamed; _watcher.Error -= OnWatcherError; _watcher.Dispose(); _watcher = null; } } private static void OnFileChanged(object sender, FileSystemEventArgs args) { RequestReload(); } private static void OnFileRenamed(object sender, RenamedEventArgs args) { RequestReload(); } private static void OnWatcherError(object sender, ErrorEventArgs args) { PortalRulesPlugin.PortalRulesLogger.LogError((object)("The admin-portal-biome-global-keys.yml watcher failed and will be restarted: " + args.GetException().Message)); Interlocked.Exchange(ref _watcherRestartRequested, 1); ScheduleWatcherRestart(WatcherRestartRetryDelay); RequestReload(); } private static void RequestReload() { Interlocked.Exchange(ref _reloadAttempts, 0); ScheduleReload(ReloadDebounce); } private static void ScheduleReload(TimeSpan delay) { Interlocked.Exchange(ref _reloadNotBeforeUtcTicks, (DateTime.UtcNow + delay).Ticks); Interlocked.Exchange(ref _reloadRequested, 1); } private static void ScheduleWatcherRestart(TimeSpan delay) { Interlocked.Exchange(ref _watcherRestartNotBeforeUtcTicks, (DateTime.UtcNow + delay).Ticks); } } [HarmonyPatch] internal static class AdminPortalPrefabManager { [HarmonyPatch(typeof(Game), "Awake")] private static class GameAwakePatch { private static void Postfix(Game __instance) { RegisterPortalHashes(__instance); } } [HarmonyPatch(typeof(TeleportWorld), "UpdatePortal")] private static class ShowAdminPortalWhirlingEffectNearbyPatch { [HarmonyPriority(0)] private static void Postfix(TeleportWorld __instance) { //IL_0064: Unknown result type (might be due to invalid IL or missing references) ZDO portalZdo = PublicPortalKinds.GetPortalZdo(__instance); if (portalZdo != null && PublicPortalKinds.IsAdminPortalPrefab(portalZdo.GetPrefab())) { EffectFade target_found = __instance.m_target_found; if ((Object)(object)target_found != (Object)null) { Player localPlayer = Player.m_localPlayer; Transform val = (((Object)(object)__instance.m_proximityRoot != (Object)null) ? __instance.m_proximityRoot : ((Component)__instance).transform); float num = Mathf.Max(0f, __instance.m_activationRange); bool active = (Object)(object)localPlayer != (Object)null && (Object)(object)Player.GetClosestPlayer(val.position, num) != (Object)null; target_found.SetActive(active); } } } } [HarmonyPatch(typeof(Piece), "Awake")] private static class ClearAdminPortalPieceCreatorPatch { private static void Postfix(Piece __instance, ref long ___m_creator) { if (IsAdminPortalPiece(__instance)) { ___m_creator = 0L; if (!ZNetView.m_forceDisableInit) { DisablePlacedPortalSolidColliders(((Component)__instance).gameObject); HidePlacedPortalMeshes(((Component)__instance).gameObject); } } } } [HarmonyPatch(typeof(Piece), "SetCreator")] private static class BlockAdminPortalPieceCreatorPatch { private static bool Prefix(Piece __instance) { return !IsAdminPortalPiece(__instance); } } [HarmonyPatch(typeof(PieceTable), "UpdateAvailable")] private static class AdminPortalBuildMenuVisibilityPatch { [HarmonyPriority(0)] private static void Postfix(PieceTable __instance, Player player, List> ___m_availablePieces) { foreach (List ___m_availablePiece in ___m_availablePieces) { ___m_availablePiece.RemoveAll(IsAdminPortalPiece); } if ((Object)(object)player != (Object)(object)Player.m_localPlayer || !PortalRulesPlugin.HasAdminDebugAccess) { return; } int num = 0; if (num < 0 || num >= ___m_availablePieces.Count) { return; } List list = ___m_availablePieces[num]; foreach (GameObject piece in __instance.m_pieces) { Piece val = (((Object)(object)piece != (Object)null) ? piece.GetComponent() : null); if ((Object)(object)val != (Object)null && IsAdminPortalPiece(val) && !list.Contains(val)) { list.Add(val); } } } } [HarmonyPatch(typeof(Player), "TryPlacePiece")] private static class AdminPortalPlacementAccessPatch { private static bool Prefix(Player __instance, Piece piece, ref bool __result) { if (!IsAdminPortalPiece(piece)) { return true; } if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer && PortalRulesPlugin.HasAdminDebugAccess) { return true; } if (__instance != null) { ((Character)__instance).Message((MessageType)2, new PortalRulesMessage("$sighsorry_portalrules_admin_portal_placement_requires_admin_debug").Localize(), 0, (Sprite)null); } __result = false; return false; } } private readonly struct PortalCloneSpec { internal readonly string SourcePrefabName; internal readonly string TargetPrefabName; internal readonly string DisplayName; internal PortalCloneSpec(string sourcePrefabName, string targetPrefabName, string displayName) { SourcePrefabName = sourcePrefabName; TargetPrefabName = targetPrefabName; DisplayName = displayName; } } private static readonly PortalCloneSpec[] PortalCloneSpecs = new PortalCloneSpec[2] { new PortalCloneSpec("portal_wood", "admin_portal_wood", "$sighsorry_portalrules_admin_wood_portal_name"), new PortalCloneSpec("portal_stone", "admin_portal_stone", "$sighsorry_portalrules_admin_stone_portal_name") }; private static bool _registered; private static Player? _lastBuildMenuPlayer; private static bool _lastBuildMenuAccess; private static bool _buildMenuAccessInitialized; private static readonly MethodInfo? UpdateAvailablePiecesMethod = AccessTools.DeclaredMethod(typeof(Player), "UpdateAvailablePiecesList", (Type[])null, (Type[])null); internal static void Initialize() { PrefabManager.OnVanillaPrefabsAvailable += RegisterAdminPortals; PieceManager.OnPiecesRegistered += RegisterAdminPortalPieces; } internal static void Shutdown() { PrefabManager.OnVanillaPrefabsAvailable -= RegisterAdminPortals; PieceManager.OnPiecesRegistered -= RegisterAdminPortalPieces; _lastBuildMenuPlayer = null; _lastBuildMenuAccess = false; _buildMenuAccessInitialized = false; } internal static void Tick() { Player localPlayer = Player.m_localPlayer; if (!_registered || (Object)(object)localPlayer == (Object)null) { _lastBuildMenuPlayer = null; _buildMenuAccessInitialized = false; return; } bool hasAdminDebugAccess = PortalRulesPlugin.HasAdminDebugAccess; if (_buildMenuAccessInitialized && _lastBuildMenuPlayer == localPlayer && _lastBuildMenuAccess == hasAdminDebugAccess) { return; } _lastBuildMenuPlayer = localPlayer; _lastBuildMenuAccess = hasAdminDebugAccess; _buildMenuAccessInitialized = true; if (!((Character)localPlayer).InPlaceMode()) { return; } if (UpdateAvailablePiecesMethod == null) { PortalRulesPlugin.PortalRulesLogger.LogWarning((object)"Could not refresh the Hammer piece list after admin build access changed."); return; } try { UpdateAvailablePiecesMethod.Invoke(localPlayer, Array.Empty()); } catch (Exception ex) { PortalRulesPlugin.PortalRulesLogger.LogWarning((object)("Could not refresh the Hammer piece list after admin build access changed: " + ex.GetBaseException().Message)); } } private static void RegisterAdminPortals() { if (_registered) { PrefabManager.OnVanillaPrefabsAvailable -= RegisterAdminPortals; return; } int num = 0; PortalCloneSpec[] portalCloneSpecs = PortalCloneSpecs; for (int i = 0; i < portalCloneSpecs.Length; i++) { PortalCloneSpec portalCloneSpec = portalCloneSpecs[i]; GameObject prefab = PrefabManager.Instance.GetPrefab(portalCloneSpec.TargetPrefabName); if ((Object)(object)prefab != (Object)null) { num++; continue; } GameObject prefab2 = PrefabManager.Instance.GetPrefab(portalCloneSpec.SourcePrefabName); Piece sourcePiece = ((prefab2 != null) ? prefab2.GetComponent() : null); prefab = PrefabManager.Instance.CreateClonedPrefab(portalCloneSpec.TargetPrefabName, portalCloneSpec.SourcePrefabName); if ((Object)(object)prefab == (Object)null) { PortalRulesPlugin.PortalRulesLogger.LogWarning((object)("Could not clone '" + portalCloneSpec.SourcePrefabName + "' into '" + portalCloneSpec.TargetPrefabName + "'.")); } else { PrepareAdminPortal(prefab, sourcePiece, portalCloneSpec.DisplayName); PrefabManager.Instance.AddPrefab(prefab); num++; } } if (num == PortalCloneSpecs.Length) { _registered = true; PrefabManager.OnVanillaPrefabsAvailable -= RegisterAdminPortals; PortalRulesPlugin.PortalRulesLogger.LogInfo((object)"Registered admin portal prefabs: admin_portal_wood, admin_portal_stone."); } } private static void RegisterAdminPortalPieces() { int num = 0; PortalCloneSpec[] portalCloneSpecs = PortalCloneSpecs; for (int i = 0; i < portalCloneSpecs.Length; i++) { PortalCloneSpec portalCloneSpec = portalCloneSpecs[i]; GameObject prefab = PrefabManager.Instance.GetPrefab(portalCloneSpec.TargetPrefabName); if ((Object)(object)prefab == (Object)null) { PortalRulesPlugin.PortalRulesLogger.LogWarning((object)("Could not find admin portal '" + portalCloneSpec.TargetPrefabName + "' for Hammer registration.")); continue; } try { PieceManager.Instance.RegisterPieceInPieceTable(prefab, PieceTables.Hammer, PieceCategories.Misc); num++; } catch (Exception ex) { PortalRulesPlugin.PortalRulesLogger.LogWarning((object)("Could not add admin portal '" + portalCloneSpec.TargetPrefabName + "' to the Hammer piece table: " + ex.Message)); } } if (num == PortalCloneSpecs.Length) { PortalRulesPlugin.PortalRulesLogger.LogInfo((object)"Registered admin portal pieces in the Hammer Misc category."); } } private static void PrepareAdminPortal(GameObject prefab, Piece? sourcePiece, string displayName) { //IL_007c: Unknown result type (might be due to invalid IL or missing references) PrepareAdminPiece(prefab, sourcePiece, displayName); RemoveRootComponent(prefab); TeleportWorld component = prefab.GetComponent(); if ((Object)(object)component != (Object)null) { ((Behaviour)component).enabled = true; component.m_allowAllItems = true; component.m_proximityRoot = (((Object)(object)component.m_proximityRoot != (Object)null) ? component.m_proximityRoot : prefab.transform); EnsureTargetFoundEffect(prefab, component); EnsureModelRenderer(prefab, component); } ZNetView component2 = prefab.GetComponent(); if ((Object)(object)component2 != (Object)null) { component2.m_persistent = true; component2.m_distant = false; component2.m_type = (ObjectType)2; } EnsureInteractionTrigger(prefab); } private static void PrepareAdminPiece(GameObject prefab, Piece? sourcePiece, string displayName) { //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) Piece component = prefab.GetComponent(); if ((Object)(object)component == (Object)null) { PortalRulesPlugin.PortalRulesLogger.LogWarning((object)("Admin portal '" + ((Object)prefab).name + "' has no Piece metadata; its icon cannot be displayed.")); return; } if ((Object)(object)sourcePiece?.m_icon != (Object)null) { component.m_icon = sourcePiece.m_icon; } else { PortalRulesPlugin.PortalRulesLogger.LogWarning((object)("Admin portal '" + ((Object)prefab).name + "' could not reuse its source portal icon.")); } component.m_name = displayName; component.m_description = "$sighsorry_portalrules_admin_portal_description"; component.m_category = (PieceCategory)0; component.m_enabled = false; component.m_canBeRemoved = false; component.m_craftingStation = null; component.m_resources = Array.Empty(); component.m_destroyedLootPrefab = null; component.m_comfort = 0; component.m_comfortGroup = (ComfortGroup)0; component.m_comfortObject = null; component.m_harvest = false; ((StaticTarget)component).m_primaryTarget = false; ((StaticTarget)component).m_randomTarget = false; component.m_targetNonPlayerBuilt = false; } private static bool IsAdminPortalPiece(Piece piece) { if ((Object)(object)piece == (Object)null) { return false; } string prefabName = Utils.GetPrefabName(((Component)piece).gameObject); if (!(prefabName == "admin_portal_wood")) { return prefabName == "admin_portal_stone"; } return true; } private static void EnsureInteractionTrigger(GameObject prefab) { //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: 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_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: 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_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)prefab.transform.Find("ADMIN_INTERACT") != (Object)null)) { Collider val = FindTeleportCollider(prefab); Transform val2 = (((Object)(object)val != (Object)null) ? ((Component)val).transform : prefab.transform); GameObject val3 = new GameObject("ADMIN_INTERACT") { layer = LayerOrFallback("piece_nonsolid", prefab.layer) }; val3.transform.SetParent(prefab.transform, false); val3.transform.localPosition = val2.localPosition; val3.transform.localRotation = val2.localRotation; val3.transform.localScale = val2.localScale; BoxCollider val4 = val3.AddComponent(); ((Collider)val4).isTrigger = true; BoxCollider val5 = (BoxCollider)(object)((val is BoxCollider) ? val : null); if (val5 != null) { val4.center = val5.center; val4.size = val5.size; } else { val4.center = new Vector3(0f, 1.5f, 0f); val4.size = new Vector3(2f, 3f, 0.5f); } } } private static Collider FindTeleportCollider(GameObject prefab) { Collider[] componentsInChildren = prefab.GetComponentsInChildren(true); foreach (Collider val in componentsInChildren) { if ((Object)(object)((Component)val).GetComponent() != (Object)null) { return val; } } return null; } private static void DisablePlacedPortalSolidColliders(GameObject portal) { Collider[] componentsInChildren = portal.GetComponentsInChildren(true); foreach (Collider val in componentsInChildren) { if (!val.isTrigger) { val.enabled = false; } } } private static void HidePlacedPortalMeshes(GameObject portal) { TeleportWorld component = portal.GetComponent(); object obj; if (component == null) { obj = null; } else { EffectFade target_found = component.m_target_found; obj = ((target_found != null) ? ((Component)target_found).transform : null); } Transform val = (Transform)obj; MeshRenderer[] componentsInChildren = portal.GetComponentsInChildren(true); foreach (MeshRenderer val2 in componentsInChildren) { if ((Object)(object)val == (Object)null || !((Component)val2).transform.IsChildOf(val)) { ((Renderer)val2).enabled = false; } } SkinnedMeshRenderer[] componentsInChildren2 = portal.GetComponentsInChildren(true); foreach (SkinnedMeshRenderer val3 in componentsInChildren2) { if ((Object)(object)val == (Object)null || !((Component)val3).transform.IsChildOf(val)) { ((Renderer)val3).enabled = false; } } } private static void EnsureTargetFoundEffect(GameObject prefab, TeleportWorld teleportWorld) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Expected O, but got Unknown if (!((Object)(object)teleportWorld.m_target_found != (Object)null)) { GameObject val = new GameObject("ADMIN_TARGET_FOUND_DUMMY"); val.transform.SetParent(prefab.transform, false); teleportWorld.m_target_found = val.AddComponent(); } } private static void EnsureModelRenderer(GameObject prefab, TeleportWorld teleportWorld) { //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)teleportWorld.m_model != (Object)null)) { MeshRenderer val = prefab.GetComponentInChildren(true); if ((Object)(object)val == (Object)null) { GameObject obj = GameObject.CreatePrimitive((PrimitiveType)3); ((Object)obj).name = "ADMIN_MODEL_DUMMY"; obj.transform.SetParent(prefab.transform, false); obj.transform.localScale = Vector3.one * 0.1f; RemoveRootComponent(obj); val = obj.GetComponent(); } teleportWorld.m_model = val; } } private static int LayerOrFallback(string layerName, int fallback) { int num = LayerMask.NameToLayer(layerName); if (num < 0) { return fallback; } return num; } private static void RemoveRootComponent(GameObject gameObject) where T : Component { T component = gameObject.GetComponent(); if ((Object)(object)component != (Object)null) { Object.DestroyImmediate((Object)(object)component); } } internal static void RegisterPortalHashes(Game game) { PortalCloneSpec[] portalCloneSpecs = PortalCloneSpecs; for (int i = 0; i < portalCloneSpecs.Length; i++) { int stableHashCode = StringExtensionMethods.GetStableHashCode(portalCloneSpecs[i].TargetPrefabName); if (!game.PortalPrefabHash.Contains(stableHashCode)) { game.PortalPrefabHash.Add(stableHashCode); } } } } [HarmonyPatch] internal static class AdminPortalOperations { private sealed class RequiredGlobalKeyTextReceiver : TextReceiver { private readonly ZDOID _portalId; private readonly string _currentValue; internal RequiredGlobalKeyTextReceiver(ZDOID portalId, string currentValue) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) _portalId = portalId; _currentValue = currentValue ?? ""; } public string GetText() { return _currentValue; } public void SetText(string text) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) RequestRequiredGlobalKeyChange(_portalId, text); } } [HarmonyPatch(typeof(TeleportWorld), "SetText")] private static class AdminPortalSetTextPatch { private static bool Prefix(TeleportWorld __instance, string text) { ZDO portalZdo = PublicPortalKinds.GetPortalZdo(__instance); if (portalZdo == null || !PublicPortalInteraction.IsAdminPortal(portalZdo)) { return true; } RequestTagChange(__instance, text); return false; } } [HarmonyPatch(typeof(TeleportWorld), "RPC_SetTag", new Type[] { typeof(long), typeof(string), typeof(string) })] private static class BlockVanillaAdminPortalTagRpcPatch { private static bool Prefix(TeleportWorld __instance) { ZDO portalZdo = PublicPortalKinds.GetPortalZdo(__instance); if (portalZdo != null) { return !PublicPortalInteraction.IsAdminPortal(portalZdo); } return true; } } [HarmonyPatch(typeof(Player), "RemovePiece")] private static class AdminPortalRemovalPatch { private static bool Prefix(Player __instance, ref bool __result) { if ((Object)(object)__instance == (Object)null || (Object)(object)__instance != (Object)(object)Player.m_localPlayer || !TryGetAimedAdminPortal(__instance, out ZDO zdo)) { return true; } if (!PortalRulesPlugin.HasAdminDebugAccess) { ShowOperationMessage(new PortalRulesMessage("$sighsorry_portalrules_admin_portal_dismantling_requires_admin_debug")); __result = false; return false; } RequestRemoval(zdo); __result = false; return false; } private static bool TryGetAimedAdminPortal(Player player, out ZDO zdo) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) zdo = null; RaycastHit val = default(RaycastHit); if ((Object)(object)GameCamera.instance == (Object)null || !Physics.Raycast(((Component)GameCamera.instance).transform.position, ((Component)GameCamera.instance).transform.forward, ref val, 50f, AdminRemoveRayMask, (QueryTriggerInteraction)2) || Vector3.Distance(((RaycastHit)(ref val)).point, ((Character)player).GetEyePoint()) >= player.m_maxPlaceDistance) { return false; } TeleportWorld componentInParent = ((Component)((RaycastHit)(ref val)).collider).GetComponentInParent(); ZDO val2 = (((Object)(object)componentInParent != (Object)null) ? PublicPortalKinds.GetPortalZdo(componentInParent) : null); if (val2 == null || !PublicPortalInteraction.IsAdminPortal(val2)) { return false; } zdo = val2; return true; } } private const float AdminOperationCooldownSeconds = 0.25f; private const int MaximumPortalTagLength = 10; private static readonly Dictionary LastAdminOperationAt = new Dictionary(); private static readonly int AdminRemoveRayMask = LayerMask.GetMask(new string[7] { "Default", "static_solid", "Default_small", "piece", "piece_nonsolid", "terrain", "vehicle" }); internal static void Shutdown() { LastAdminOperationAt.Clear(); } internal static void RegisterPeer(ZNet znet, ZNetPeer peer) { if (!((Object)(object)znet == (Object)null) && peer?.m_rpc != null && znet.IsServer()) { peer.m_rpc.Register("sighsorry.PortalRules.ChangeAdminPortalTag.v1", (Action)OnRemoteTagChange); peer.m_rpc.Register("sighsorry.PortalRules.ChangeAdminPortalRequiredGlobalKey.v1", (Action)OnRemoteRequiredGlobalKeyChange); peer.m_rpc.Register("sighsorry.PortalRules.RemoveAdminPortal.v1", (Action)OnRemoteRemoval); } } internal static void ForgetPeer(ZRpc rpc) { if (rpc != null) { LastAdminOperationAt.Remove(rpc); } } private static void RequestTagChange(TeleportWorld portal, string tag) { //IL_004e: 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) ZDO portalZdo = PublicPortalKinds.GetPortalZdo(portal); if (portalZdo == null) { ShowOperationMessage(new PortalRulesMessage("$sighsorry_portalrules_admin_portal_unavailable")); return; } if ((Object)(object)ZNet.instance == (Object)null) { ShowOperationMessage(new PortalRulesMessage("$sighsorry_portalrules_server_unavailable")); return; } if (ZNet.instance.IsServer()) { TryApplyTagChange(portalZdo.m_uid, tag, null, PortalRulesPlugin.IsAdmin, Player.m_debugMode, out var message); ShowOperationMessage(message); return; } ZRpc serverRPC = ZNet.instance.GetServerRPC(); if (serverRPC == null) { ShowOperationMessage(new PortalRulesMessage("$sighsorry_portalrules_server_connection_unavailable")); return; } serverRPC.Invoke("sighsorry.PortalRules.ChangeAdminPortalTag.v1", new object[3] { portalZdo.m_uid, tag ?? "", Player.m_debugMode }); } private static void RequestRemoval(ZDO zdo) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ZNet.instance == (Object)null) { ShowOperationMessage(new PortalRulesMessage("$sighsorry_portalrules_server_unavailable")); return; } if (ZNet.instance.IsServer()) { TryApplyRemoval(zdo.m_uid, null, PortalRulesPlugin.IsAdmin, Player.m_debugMode, out var message); ShowOperationMessage(message); return; } ZRpc serverRPC = ZNet.instance.GetServerRPC(); if (serverRPC == null) { ShowOperationMessage(new PortalRulesMessage("$sighsorry_portalrules_server_connection_unavailable")); return; } serverRPC.Invoke("sighsorry.PortalRules.RemoveAdminPortal.v1", new object[2] { zdo.m_uid, Player.m_debugMode }); } internal static void OpenRequiredGlobalKeyInput(TeleportWorld portal) { //IL_0045: Unknown result type (might be due to invalid IL or missing references) ZDO portalZdo = PublicPortalKinds.GetPortalZdo(portal); if (portalZdo == null) { ShowOperationMessage(new PortalRulesMessage("$sighsorry_portalrules_admin_portal_unavailable")); return; } TextInput instance = TextInput.instance; if ((Object)(object)instance == (Object)null) { ShowOperationMessage(new PortalRulesMessage("$sighsorry_portalrules_text_input_unavailable")); } else { instance.RequestText((TextReceiver)(object)new RequiredGlobalKeyTextReceiver(portalZdo.m_uid, PublicPortalCatalog.GetEffectiveRequiredGlobalKey(portalZdo)), new PortalRulesMessage("$sighsorry_portalrules_required_global_key_input_title").Localize(), 128); } } private static void RequestRequiredGlobalKeyChange(ZDOID portalId, string requiredGlobalKey) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ZNet.instance == (Object)null || ZDOMan.instance == null) { ShowOperationMessage(new PortalRulesMessage("$sighsorry_portalrules_server_unavailable")); return; } if (ZNet.instance.IsServer()) { TryApplyRequiredGlobalKeyChange(portalId, requiredGlobalKey, null, PortalRulesPlugin.IsAdmin, Player.m_debugMode, out var message); ShowOperationMessage(message); return; } ZRpc serverRPC = ZNet.instance.GetServerRPC(); if (serverRPC == null) { ShowOperationMessage(new PortalRulesMessage("$sighsorry_portalrules_server_connection_unavailable")); return; } serverRPC.Invoke("sighsorry.PortalRules.ChangeAdminPortalRequiredGlobalKey.v1", new object[3] { portalId, requiredGlobalKey ?? "", Player.m_debugMode }); } private static void OnRemoteTagChange(ZRpc rpc, ZDOID portalId, string tag, bool debugEnabled) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) if (!TryResolveRemoteAdminOperation(rpc, out ZNetPeer peer, out bool requesterIsAdmin, out PortalRulesMessage rejection)) { if (peer != null) { PublicPortalServerPolicy.SendPolicyMessage(peer, rejection); } } else { LogRejectedOperation(TryApplyTagChange(portalId, tag, peer, requesterIsAdmin, debugEnabled, out var message), peer, portalId, "tag change", message); PublicPortalServerPolicy.SendPolicyMessage(peer, message); } } private static void OnRemoteRemoval(ZRpc rpc, ZDOID portalId, bool debugEnabled) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) if (!TryResolveRemoteAdminOperation(rpc, out ZNetPeer peer, out bool requesterIsAdmin, out PortalRulesMessage rejection)) { if (peer != null) { PublicPortalServerPolicy.SendPolicyMessage(peer, rejection); } } else { LogRejectedOperation(TryApplyRemoval(portalId, peer, requesterIsAdmin, debugEnabled, out var message), peer, portalId, "removal", message); PublicPortalServerPolicy.SendPolicyMessage(peer, message); } } private static void OnRemoteRequiredGlobalKeyChange(ZRpc rpc, ZDOID portalId, string requiredGlobalKey, bool debugEnabled) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) if (!TryResolveRemoteAdminOperation(rpc, out ZNetPeer peer, out bool requesterIsAdmin, out PortalRulesMessage rejection)) { if (peer != null) { PublicPortalServerPolicy.SendPolicyMessage(peer, rejection); } } else { LogRejectedOperation(TryApplyRequiredGlobalKeyChange(portalId, requiredGlobalKey, peer, requesterIsAdmin, debugEnabled, out var message), peer, portalId, "Required GlobalKey change", message); PublicPortalServerPolicy.SendPolicyMessage(peer, message); } } private static bool TryResolveRemoteAdminOperation(ZRpc rpc, out ZNetPeer? peer, out bool requesterIsAdmin, out PortalRulesMessage rejection) { peer = null; requesterIsAdmin = false; rejection = new PortalRulesMessage("$sighsorry_portalrules_admin_request_unauthenticated"); ZNet instance = ZNet.instance; if ((Object)(object)instance == (Object)null || !instance.IsServer()) { return false; } peer = PublicPortalData.FindPeer(instance, rpc); if (peer == null || !peer.IsReady()) { return false; } float realtimeSinceStartup = Time.realtimeSinceStartup; if (LastAdminOperationAt.TryGetValue(rpc, out var value) && realtimeSinceStartup - value < 0.25f) { rejection = new PortalRulesMessage("$sighsorry_portalrules_admin_change_rate_limited"); return false; } LastAdminOperationAt[rpc] = realtimeSinceStartup; requesterIsAdmin = PublicPortalData.IsPeerAdmin(instance, peer); return true; } private static bool TryApplyTagChange(ZDOID portalId, string tag, ZNetPeer? requesterPeer, bool requesterIsAdmin, bool debugEnabled, out PortalRulesMessage message) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) if (!TryGetAuthorizedAdminPortal(portalId, requesterPeer, requesterIsAdmin, debugEnabled, out ZDO zdo, out message)) { return false; } if (tag == null) { tag = ""; } if (tag.Length > 10) { message = new PortalRulesMessage("$sighsorry_portalrules_admin_tag_length_limit", 10.ToString(CultureInfo.InvariantCulture)); return false; } if (!PublicPortalCatalog.SetAuthoritativeAdminPortalTag(zdo, tag)) { message = new PortalRulesMessage("$sighsorry_portalrules_admin_tag_update_failed"); return false; } message = new PortalRulesMessage("$sighsorry_portalrules_admin_tag_updated"); return true; } private static bool TryApplyRemoval(ZDOID portalId, ZNetPeer? requesterPeer, bool requesterIsAdmin, bool debugEnabled, out PortalRulesMessage message) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) if (!TryGetAuthorizedAdminPortal(portalId, requesterPeer, requesterIsAdmin, debugEnabled, out ZDO zdo, out message)) { return false; } if (!PublicPortalServerPolicy.QueueAdminPortalRemoval(zdo)) { message = new PortalRulesMessage("$sighsorry_portalrules_admin_portal_removal_unavailable"); return false; } message = new PortalRulesMessage("$sighsorry_portalrules_admin_portal_dismantling_queued"); return true; } private static bool TryApplyRequiredGlobalKeyChange(ZDOID portalId, string requiredGlobalKey, ZNetPeer? requesterPeer, bool requesterIsAdmin, bool debugEnabled, out PortalRulesMessage message) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) if (!TryGetAuthorizedAdminPortal(portalId, requesterPeer, requesterIsAdmin, debugEnabled, out ZDO zdo, out message)) { return false; } if (!TryNormalizeRequiredGlobalKeyMessage(requiredGlobalKey, out string normalizedRequiredGlobalKey, out message)) { return false; } if (normalizedRequiredGlobalKey.Length != 0 && RequiredGlobalKeyAccess.YouAreNotWorthyInstalled) { switch ((requesterPeer != null) ? RequiredGlobalKeyAccess.QueryPeer(requesterPeer, normalizedRequiredGlobalKey) : RequiredGlobalKeyAccess.QueryLocal(normalizedRequiredGlobalKey)) { case RequiredGlobalKeyQueryResult.Invalid: message = new PortalRulesMessage("$sighsorry_portalrules_ynw_required_global_key_rejected"); return false; case RequiredGlobalKeyQueryResult.Unavailable: message = new PortalRulesMessage("$sighsorry_portalrules_ynw_key_api_unavailable"); return false; } } if (!PublicPortalCatalog.SetAuthoritativeAdminPortalRequiredGlobalKey(zdo, normalizedRequiredGlobalKey)) { message = new PortalRulesMessage("$sighsorry_portalrules_admin_required_global_key_update_failed"); return false; } message = ((normalizedRequiredGlobalKey.Length == 0) ? new PortalRulesMessage("$sighsorry_portalrules_admin_required_global_key_cleared") : new PortalRulesMessage("$sighsorry_portalrules_admin_required_global_key_updated")); return true; } private static bool TryNormalizeRequiredGlobalKeyMessage(string? requiredGlobalKey, out string normalizedRequiredGlobalKey, out PortalRulesMessage message) { if (PublicPortalData.TryNormalizeRequiredGlobalKey(requiredGlobalKey, out normalizedRequiredGlobalKey, out var failure)) { message = PortalRulesMessage.Empty; return true; } switch (failure) { case RequiredGlobalKeyValidationFailure.TooLong: message = new PortalRulesMessage("$sighsorry_portalrules_required_global_key_length_limit", 128.ToString(CultureInfo.InvariantCulture)); break; case RequiredGlobalKeyValidationFailure.ControlCharacters: message = new PortalRulesMessage("$sighsorry_portalrules_required_global_key_control_characters"); break; case RequiredGlobalKeyValidationFailure.NumericKey: message = new PortalRulesMessage("$sighsorry_portalrules_required_global_key_numeric_invalid"); break; case RequiredGlobalKeyValidationFailure.ReservedKey: message = new PortalRulesMessage("$sighsorry_portalrules_required_global_key_not_allowed"); break; default: message = new PortalRulesMessage("$sighsorry_portalrules_admin_required_global_key_update_failed"); break; } return false; } private static bool TryGetAuthorizedAdminPortal(ZDOID portalId, ZNetPeer? requesterPeer, bool requesterIsAdmin, bool debugEnabled, out ZDO zdo, out PortalRulesMessage message) { //IL_005f: Unknown result type (might be due to invalid IL or missing references) zdo = null; if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || ZDOMan.instance == null) { message = new PortalRulesMessage("$sighsorry_portalrules_server_not_ready"); return false; } if (!requesterIsAdmin || !debugEnabled) { message = new PortalRulesMessage("$sighsorry_portalrules_admin_portal_controls_require_admin_debug"); return false; } ZDO zDO = ZDOMan.instance.GetZDO(portalId); if (zDO == null || !zDO.IsValid() || !PublicPortalInteraction.IsAdminPortal(zDO)) { message = new PortalRulesMessage("$sighsorry_portalrules_requested_admin_portal_unavailable"); return false; } if (!PublicPortalInteraction.TryValidatePortalInteraction(zDO, requesterPeer, out message)) { return false; } if (!((requesterPeer != null) ? PublicPortalData.TryGetPeerOwner(requesterPeer, out var _) : PublicPortalData.LocalOwner().IsValid)) { message = new PortalRulesMessage("$sighsorry_portalrules_platform_identity_unverified"); return false; } zdo = zDO; return true; } private static void LogRejectedOperation(bool success, ZNetPeer peer, ZDOID portalId, string operation, PortalRulesMessage message) { //IL_007a: Unknown result type (might be due to invalid IL or missing references) if (!success && PublicPortalData.TryGetPeerOwner(peer, out var owner)) { string text = (message.IsEmpty ? "" : ((message.Arguments.Length == 0) ? message.Token : (message.Token + " [" + string.Join(", ", message.Arguments) + "]"))); PortalRulesPlugin.PortalRulesLogger.LogWarning((object)$"Rejected admin portal {operation} from {owner.Id} for {portalId}: {text}"); } } private static void ShowOperationMessage(PortalRulesMessage message) { string text = message.Localize(); if (!string.IsNullOrWhiteSpace(text)) { Player localPlayer = Player.m_localPlayer; if (localPlayer != null) { ((Character)localPlayer).Message((MessageType)2, text, 0, (Sprite)null); } } } } internal readonly struct PortalClanMembership { private readonly string? _primaryClanId; private readonly string? _primaryClanName; private readonly string? _guestClanId; public string PrimaryClanId => _primaryClanId ?? string.Empty; public string PrimaryClanName => _primaryClanName ?? string.Empty; public string GuestClanId => _guestClanId ?? string.Empty; public bool HasPrimaryClan => PrimaryClanId.Length != 0; public PortalClanMembership(string primaryClanId, string primaryClanName, string guestClanId) { _primaryClanId = Normalize(primaryClanId); _primaryClanName = ((_primaryClanId.Length == 0) ? string.Empty : Normalize(primaryClanName)); _guestClanId = Normalize(guestClanId); } public bool ContainsClan(string clanId) { string text = Normalize(clanId); if (text.Length != 0) { if (!string.Equals(text, PrimaryClanId, StringComparison.Ordinal)) { return string.Equals(text, GuestClanId, StringComparison.Ordinal); } return true; } return false; } private static string Normalize(string? value) { return (value ?? string.Empty).Trim(); } } internal static class ClanPortalAccess { private const string ClanApiTypeName = "Clan.ClanApi"; private const int MinimumApiVersion = 4; private const string ResolvedResultName = "Resolved"; private static readonly object Sync = new object(); private static MemberInfo? _apiVersionMember; private static MethodInfo? _resolveMembershipsMethod; private static System.Reflection.EventInfo? _registryChangedEventInfo; private static Delegate? _registryChangedHandler; private static bool _initialized; private static bool _apiAvailable; private static bool _registryChangedEventBound; private static bool _bindingFailureLogged; private static bool _invocationFailureLogged; internal static bool IsServerRegistryAvailable { get { lock (Sync) { return IsApiAvailableLocked(); } } } internal static event Action? RegistryChanged; internal static void Initialize() { lock (Sync) { if (_initialized) { return; } _initialized = true; Assembly installedClanAssembly = GetInstalledClanAssembly(); if (!(installedClanAssembly == null)) { BindAssemblyLocked(installedClanAssembly); if (_resolveMembershipsMethod == null || _registryChangedEventInfo == null || !TryReadApiVersion(out var apiVersion) || apiVersion < 4) { LogBindingFailureOnce($"Clan integration requires API version {4} " + "with ResolveMemberships and RegistryChanged; integration is disabled."); ResetBindingLocked(); } else if (!TryBindRegistryChangedEventLocked()) { LogBindingFailureOnce("Could not bind Clan.RegistryChanged; Clan integration is disabled."); ResetBindingLocked(); } else { _apiAvailable = true; } } } } internal static void Shutdown() { lock (Sync) { _initialized = false; ResetBindingLocked(); } } internal static bool TryResolvePeerMembership(ZNetPeer? peer, out PortalClanMembership membership) { membership = default(PortalClanMembership); if (TryGetPeerIdentity(peer, out string platformId, out long playerId)) { return TryResolveMembership(platformId, playerId, out membership); } return false; } internal static bool TryResolveLocalMembership(out PortalClanMembership membership) { membership = default(PortalClanMembership); if (!TryGetLocalIdentity(out string platformId, out long playerId)) { return false; } return TryResolveMembership(platformId, playerId, out membership); } internal static bool TryResolveBuilderMembership(PortalBuilder builder, out PortalClanMembership membership) { membership = default(PortalClanMembership); if (builder.IsValid && builder.CharacterPlayerId != 0L) { return TryResolveMembership(builder.PlatformId, builder.CharacterPlayerId, out membership); } return false; } internal static bool IsRequesterInBuilderPrimaryClan(PortalBuilder builder, ZNetPeer? requesterPeer) { if (!TryResolveBuilderMembership(builder, out var membership) || !membership.HasPrimaryClan) { return false; } if ((requesterPeer != null) ? TryResolvePeerMembership(requesterPeer, out var membership2) : TryResolveLocalMembership(out membership2)) { return membership2.ContainsClan(membership.PrimaryClanId); } return false; } private static bool TryResolveMembership(string platformId, long playerId, out PortalClanMembership membership) { membership = default(PortalClanMembership); if (string.IsNullOrWhiteSpace(platformId) || playerId == 0L) { return false; } MethodInfo resolveMembershipsMethod; lock (Sync) { if (!IsApiAvailableLocked()) { return false; } resolveMembershipsMethod = _resolveMembershipsMethod; } if (resolveMembershipsMethod == null) { return false; } object[] array = new object[6] { platformId.Trim(), playerId, null, null, null, null }; try { if (!string.Equals(resolveMembershipsMethod.Invoke(null, array)?.ToString(), "Resolved", StringComparison.Ordinal)) { return false; } membership = new PortalClanMembership((array[2] as string) ?? string.Empty, (array[3] as string) ?? string.Empty, (array[4] as string) ?? string.Empty); return true; } catch (Exception exception) { LogInvocationFailureOnce(exception); membership = default(PortalClanMembership); return false; } } private static bool TryGetPeerIdentity(ZNetPeer? peer, out string platformId, out long playerId) { platformId = string.Empty; playerId = 0L; if (PublicPortalData.TryGetPeerSteamId64(peer, out platformId)) { return PublicPortalData.TryGetAuthenticatedPeerPlayerId(peer, out playerId); } return false; } private static bool TryGetLocalIdentity(out string platformId, out long playerId) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) platformId = string.Empty; playerId = 0L; try { IDistributionPlatform distributionPlatform = PlatformManager.DistributionPlatform; Player localPlayer = Player.m_localPlayer; if (((distributionPlatform != null) ? distributionPlatform.LocalUser : null) == null || (Object)(object)localPlayer == (Object)null) { return false; } platformId = ((object)((IUser)distributionPlatform.LocalUser).PlatformUserID/*cast due to .constrained prefix*/).ToString().Trim(); playerId = localPlayer.GetPlayerID(); return platformId.Length != 0 && playerId != 0; } catch (Exception) { platformId = string.Empty; playerId = 0L; return false; } } private static bool IsApiAvailableLocked() { if (_initialized && _apiAvailable && _resolveMembershipsMethod != null) { return _registryChangedEventBound; } return false; } private static void BindAssemblyLocked(Assembly assembly) { Type type = assembly.GetType("Clan.ClanApi", throwOnError: false); if (!(type == null)) { _apiVersionMember = (MemberInfo?)(((object)type.GetProperty("ApiVersion", BindingFlags.Static | BindingFlags.Public)) ?? ((object)type.GetField("ApiVersion", BindingFlags.Static | BindingFlags.Public))); _resolveMembershipsMethod = type.GetMethod("ResolveMemberships", BindingFlags.Static | BindingFlags.Public, null, new Type[6] { typeof(string), typeof(long), typeof(string).MakeByRefType(), typeof(string).MakeByRefType(), typeof(string).MakeByRefType(), typeof(string).MakeByRefType() }, null); _registryChangedEventInfo = type.GetEvent("RegistryChanged", BindingFlags.Static | BindingFlags.Public); } } private static Assembly? GetInstalledClanAssembly() { try { PluginInfo value; return (!Chainloader.PluginInfos.TryGetValue("sighsorry.Clan", out value)) ? null : ((object)value.Instance)?.GetType().Assembly; } catch (Exception ex) { LogBindingFailureOnce("Could not inspect the installed Clan plugin; integration is disabled: " + ex.Message); return null; } } private static bool TryReadApiVersion(out int apiVersion) { apiVersion = 0; try { MemberInfo apiVersionMember = _apiVersionMember; object obj = ((apiVersionMember is PropertyInfo propertyInfo) ? propertyInfo.GetValue(null, null) : ((!(apiVersionMember is FieldInfo fieldInfo)) ? null : fieldInfo.GetValue(null))); object obj2 = obj; if (obj2 == null) { return false; } apiVersion = Convert.ToInt32(obj2); return true; } catch (Exception) { return false; } } private static bool TryBindRegistryChangedEventLocked() { if (_registryChangedEventInfo?.EventHandlerType != typeof(Action)) { return false; } try { _registryChangedHandler = new Action(HandleRegistryChanged); _registryChangedEventInfo.AddEventHandler(null, _registryChangedHandler); _registryChangedEventBound = true; return true; } catch (Exception) { _registryChangedHandler = null; _registryChangedEventBound = false; return false; } } private static void HandleRegistryChanged() { Delegate[] invocationList; lock (Sync) { if (!_initialized || ClanPortalAccess.RegistryChanged == null) { return; } invocationList = ClanPortalAccess.RegistryChanged.GetInvocationList(); } Delegate[] array = invocationList; foreach (Delegate obj in array) { try { ((Action)obj)(); } catch (Exception) { } } } private static void ResetBindingLocked() { if (_registryChangedEventBound && _registryChangedEventInfo != null && (object)_registryChangedHandler != null) { try { _registryChangedEventInfo.RemoveEventHandler(null, _registryChangedHandler); } catch (Exception) { } } _apiAvailable = false; _registryChangedEventBound = false; _registryChangedHandler = null; _registryChangedEventInfo = null; _resolveMembershipsMethod = null; _apiVersionMember = null; } private static void LogBindingFailureOnce(string message) { if (!_bindingFailureLogged) { _bindingFailureLogged = true; PortalRulesPlugin.PortalRulesLogger.LogWarning((object)message); } } private static void LogInvocationFailureOnce(Exception exception) { lock (Sync) { if (_invocationFailureLogged) { return; } _invocationFailureLogged = true; } PortalRulesPlugin.PortalRulesLogger.LogWarning((object)("Clan.ResolveMemberships failed; the request was denied: " + exception.Message)); } } internal enum InviteTravelCooldownFailure : byte { None, DataUnavailable, StorageUnavailable, Active, AccountCapacityReached, PortalCapacityReached, FileCapacityReached, SaveFailed, ReservationConflict } internal readonly struct InviteTravelCooldownReservation { internal readonly string ReservationId; internal readonly string AccountId; internal readonly string SourceFavoriteId; internal readonly string DestinationFavoriteId; internal readonly bool RecordsDeparture; internal readonly bool RecordsArrival; internal bool IsActive { get { if (!RecordsDeparture) { return RecordsArrival; } return true; } } internal InviteTravelCooldownReservation(string reservationId, string accountId, string sourceFavoriteId, string destinationFavoriteId, bool recordsDeparture, bool recordsArrival) { ReservationId = reservationId; AccountId = accountId; SourceFavoriteId = sourceFavoriteId; DestinationFavoriteId = destinationFavoriteId; RecordsDeparture = recordsDeparture; RecordsArrival = recordsArrival; } } internal static class InviteTravelCooldownStore { private sealed class PendingReservation { internal readonly InviteTravelCooldownReservation Value; internal readonly long ExpiresAtUtc; internal readonly long SerializedByteEstimate; internal PendingReservation(InviteTravelCooldownReservation value, long expiresAtUtc, long serializedByteEstimate) { Value = value; ExpiresAtUtc = expiresAtUtc; SerializedByteEstimate = serializedByteEstimate; } } private sealed class CooldownFile { [YamlMember(Alias = "format_version")] public int FormatVersion { get; set; } [YamlMember(Alias = "worlds")] public Dictionary>>? Worlds { get; set; } } private sealed class CooldownEntryYaml { [YamlMember(Alias = "departure_last_utc")] public long DepartureLastUtc { get; set; } [YamlMember(Alias = "arrival_last_utc")] public long ArrivalLastUtc { get; set; } } private readonly struct CooldownEntry { public readonly long DepartureLastUtc; public readonly long ArrivalLastUtc; public CooldownEntry(long departureLastUtc, long arrivalLastUtc) { DepartureLastUtc = Math.Max(0L, departureLastUtc); ArrivalLastUtc = Math.Max(0L, arrivalLastUtc); } public CooldownEntry WithDeparture(long value) { return new CooldownEntry(value, ArrivalLastUtc); } public CooldownEntry WithArrival(long value) { return new CooldownEntry(DepartureLastUtc, value); } } private const int FormatVersion = 1; private const int MaximumWorldCount = 1024; private const int MaximumAccountsPerWorld = 32768; private const int MaximumPortalsPerAccount = 100000; private const long MaximumFileBytes = 16777216L; private const int MaximumSerializedPortalMutationBytes = 256; private const int MaximumSerializedAccountHeaderBytes = 64; private const int MaximumPendingReservations = 4096; private const int MaximumReservationIdLength = 128; private const long PendingReservationLifetimeSeconds = 180L; private const long MaximumUtcSeconds = 253402300799L; private const string DirectoryName = "PortalRules"; private const string FileName = "invite-travel-cooldowns.yml"; private static readonly TimeSpan SaveDebounce = TimeSpan.FromSeconds(1.0); private static readonly TimeSpan SaveRetryDelay = TimeSpan.FromSeconds(10.0); private static readonly IDeserializer Deserializer = new DeserializerBuilder().WithDuplicateKeyChecking().Build(); private static readonly Dictionary>> Worlds = new Dictionary>>(StringComparer.Ordinal); private static readonly Dictionary PendingReservations = new Dictionary(StringComparer.Ordinal); private static readonly Dictionary PendingDirectionOwners = new Dictionary(StringComparer.Ordinal); private static string _filePath = ""; private static string _worldId = ""; private static bool _serverActive; private static bool _saveDirty; private static bool _saveFaulted; private static DateTime _nextSaveAttemptUtc; private static long _serializedByteUpperBound; internal static void BeginServerSession() { ClearPendingReservations(); if (!EndServerSession()) { PortalRulesPlugin.PortalRulesLogger.LogError((object)"Invite cooldown storage retained unsaved data from the previous session; the new session will remain unavailable until that data can be saved."); } else { if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || ZNet.World == null) { return; } long uid = ZNet.World.m_uid; if (uid != 0L) { _worldId = uid.ToString(CultureInfo.InvariantCulture); string text = Path.Combine(Paths.ConfigPath, "PortalRules"); _filePath = Path.Combine(text, "invite-travel-cooldowns.yml"); try { Directory.CreateDirectory(text); Load(); bool flag = Prune(UtcNowSeconds()); if (!Worlds.ContainsKey(_worldId)) { if (Worlds.Count >= 1024) { throw new InvalidDataException(string.Format("{0} cannot track more than {1} worlds", "invite-travel-cooldowns.yml", 1024)); } Worlds.Add(_worldId, new Dictionary>(StringComparer.Ordinal)); flag = true; } if (flag) { Save(); } _saveDirty = false; _saveFaulted = false; _nextSaveAttemptUtc = DateTime.UtcNow + SaveDebounce; _serverActive = true; return; } catch (Exception ex) { Worlds.Clear(); _worldId = ""; _filePath = ""; _serverActive = false; _saveDirty = false; _saveFaulted = false; _nextSaveAttemptUtc = DateTime.MinValue; _serializedByteUpperBound = 0L; PortalRulesPlugin.PortalRulesLogger.LogError((object)("Failed to initialize Invite cooldown storage: " + ex.Message)); return; } } PortalRulesPlugin.PortalRulesLogger.LogError((object)"Invite cooldown storage is unavailable because the world UID is invalid."); } } internal static bool EndServerSession() { ClearPendingReservations(); bool num = Flush(); _serverActive = false; if (!num && _saveDirty) { PortalRulesPlugin.PortalRulesLogger.LogError((object)"Invite cooldown storage could not complete its final save; unsaved data is being retained in memory."); return false; } _saveDirty = false; _saveFaulted = false; _nextSaveAttemptUtc = DateTime.MinValue; _serializedByteUpperBound = 0L; _worldId = ""; _filePath = ""; Worlds.Clear(); return true; } internal static void Tick() { PrunePendingReservations(UtcNowSeconds()); if (_saveDirty && !(DateTime.UtcNow < _nextSaveAttemptUtc)) { bool flag = !_serverActive && !string.IsNullOrEmpty(_filePath); if (TryFlushPending(out string _) && flag && !((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer() && ZNet.World != null) { BeginServerSession(); } } } internal static bool Flush() { if (!_saveDirty) { return true; } if (string.IsNullOrEmpty(_filePath)) { return false; } string error; return TryFlushPending(out error); } internal static void GetServerDeadlines(string steamId, string favoriteId, out long departureUntilUtc, out long arrivalUntilUtc) { departureUntilUtc = 0L; arrivalUntilUtc = 0L; if (_serverActive && TryNormalizeKeys(steamId, favoriteId, out string accountId, out string portalId) && TryGetEntry(accountId, portalId, out var entry)) { departureUntilUtc = CalculateDeadline(entry.DepartureLastUtc, PublicPortalConfig.InviteDepartureCooldownHours.Value); arrivalUntilUtc = CalculateDeadline(entry.ArrivalLastUtc, PublicPortalConfig.InviteArrivalCooldownHours.Value); } } internal static void RemovePortal(string favoriteId) { if (!_serverActive || !PublicPortalData.TryNormalizeFavoriteId(favoriteId, out string portalId) || !TryGetCurrentWorldAccounts(out Dictionary> accounts)) { return; } PendingReservation[] array = PendingReservations.Values.Where((PendingReservation candidate) => (candidate.Value.RecordsDeparture && string.Equals(candidate.Value.SourceFavoriteId, portalId, StringComparison.Ordinal)) || (candidate.Value.RecordsArrival && string.Equals(candidate.Value.DestinationFavoriteId, portalId, StringComparison.Ordinal))).ToArray(); for (int num = 0; num < array.Length; num++) { RemovePendingReservation(array[num]); } bool flag = false; foreach (Dictionary value in accounts.Values) { flag |= value.Remove(portalId); } if (flag) { string[] array2 = (from pair in accounts where pair.Value.Count == 0 select pair.Key).ToArray(); foreach (string key in array2) { accounts.Remove(key); } _saveDirty = true; } } internal static bool TryReserve(string reservationId, string steamId, bool usesInviteAsSource, string sourceFavoriteId, bool usesInviteAsDestination, string destinationFavoriteId, out InviteTravelCooldownReservation reservation, out InviteTravelCooldownFailure failure, out long departureRemaining, out long arrivalRemaining) { reservation = default(InviteTravelCooldownReservation); failure = InviteTravelCooldownFailure.None; departureRemaining = 0L; arrivalRemaining = 0L; double num = PublicPortalConfig.InviteDepartureCooldownHours.Value; double num2 = PublicPortalConfig.InviteArrivalCooldownHours.Value; bool flag = usesInviteAsSource && num > 0.0; bool flag2 = usesInviteAsDestination && num2 > 0.0; if (!flag && !flag2) { return true; } string normalizedFavoriteId = ""; string normalizedFavoriteId2 = ""; if (string.IsNullOrWhiteSpace(reservationId) || reservationId.Length > 128 || !_serverActive || !PublicPortalData.TryNormalizeSteamId64(steamId, out string steamId2) || (flag && !PublicPortalData.TryNormalizeFavoriteId(sourceFavoriteId, out normalizedFavoriteId)) || (flag2 && !PublicPortalData.TryNormalizeFavoriteId(destinationFavoriteId, out normalizedFavoriteId2))) { failure = InviteTravelCooldownFailure.DataUnavailable; return false; } if (_saveFaulted && (DateTime.UtcNow < _nextSaveAttemptUtc || !TryFlushPending(out string error))) { failure = InviteTravelCooldownFailure.StorageUnavailable; return false; } long num3 = UtcNowSeconds(); PrunePendingReservations(num3); if (Prune(num3)) { _saveDirty = true; if (!TryFlushPending(out error)) { failure = InviteTravelCooldownFailure.StorageUnavailable; return false; } } CooldownEntry entry = default(CooldownEntry); CooldownEntry entry2 = default(CooldownEntry); if (flag) { TryGetEntry(steamId2, normalizedFavoriteId, out entry); } if (flag2) { TryGetEntry(steamId2, normalizedFavoriteId2, out entry2); } departureRemaining = (flag ? GetRemainingSeconds(CalculateDeadline(entry.DepartureLastUtc, num), num3) : 0); arrivalRemaining = (flag2 ? GetRemainingSeconds(CalculateDeadline(entry2.ArrivalLastUtc, num2), num3) : 0); if (departureRemaining > 0 || arrivalRemaining > 0) { failure = InviteTravelCooldownFailure.Active; return false; } if (PendingReservations.Count >= 4096) { failure = InviteTravelCooldownFailure.StorageUnavailable; return false; } string text = (flag ? CreateDirectionKey(steamId2, normalizedFavoriteId, departure: true) : ""); string text2 = (flag2 ? CreateDirectionKey(steamId2, normalizedFavoriteId2, departure: false) : ""); if ((text.Length > 0 && PendingDirectionOwners.ContainsKey(text)) || (text2.Length > 0 && PendingDirectionOwners.ContainsKey(text2))) { failure = InviteTravelCooldownFailure.ReservationConflict; return false; } if (!TryGetCurrentWorldAccounts(out Dictionary> accounts)) { failure = InviteTravelCooldownFailure.DataUnavailable; return false; } Dictionary portalEntries; bool flag3 = accounts.TryGetValue(steamId2, out portalEntries); if (!flag3) { HashSet hashSet = new HashSet(StringComparer.Ordinal); foreach (PendingReservation value2 in PendingReservations.Values) { if (!accounts.ContainsKey(value2.Value.AccountId)) { hashSet.Add(value2.Value.AccountId); } } if (!hashSet.Contains(steamId2) && accounts.Count > 32768 - hashSet.Count - 1) { failure = InviteTravelCooldownFailure.AccountCapacityReached; return false; } portalEntries = new Dictionary(StringComparer.Ordinal); } HashSet hashSet2 = new HashSet(StringComparer.Ordinal); foreach (PendingReservation value3 in PendingReservations.Values) { InviteTravelCooldownReservation value = value3.Value; if (string.Equals(value.AccountId, steamId2, StringComparison.Ordinal)) { if (value.RecordsDeparture) { hashSet2.Add(value.SourceFavoriteId); } if (value.RecordsArrival) { hashSet2.Add(value.DestinationFavoriteId); } } } HashSet hashSet3 = new HashSet(StringComparer.Ordinal); HashSet hashSet4 = new HashSet(StringComparer.Ordinal); if (flag && !portalEntries.ContainsKey(normalizedFavoriteId) && !hashSet2.Contains(normalizedFavoriteId)) { hashSet3.Add(normalizedFavoriteId); } if (flag) { hashSet4.Add(normalizedFavoriteId); } if (flag2 && !portalEntries.ContainsKey(normalizedFavoriteId2) && !hashSet2.Contains(normalizedFavoriteId2)) { hashSet3.Add(normalizedFavoriteId2); } if (flag2) { hashSet4.Add(normalizedFavoriteId2); } int num4 = hashSet2.Count((string favoriteId) => !portalEntries.ContainsKey(favoriteId)); if (portalEntries.Count > 100000 - num4 - hashSet3.Count) { failure = InviteTravelCooldownFailure.PortalCapacityReached; return false; } long num5 = (long)hashSet4.Count * 256L + (flag3 ? 0 : 64); long num6 = PendingReservations.Values.Sum((PendingReservation pending) => pending.SerializedByteEstimate); if (_serializedByteUpperBound > 16777216 - num6 - num5) { if (!_saveDirty || !TryFlushPending(out error)) { failure = InviteTravelCooldownFailure.FileCapacityReached; return false; } if (_serializedByteUpperBound > 16777216 - num6 - num5) { failure = InviteTravelCooldownFailure.FileCapacityReached; return false; } } reservation = new InviteTravelCooldownReservation(reservationId, steamId2, normalizedFavoriteId, normalizedFavoriteId2, flag, flag2); PendingReservations.Add(reservationId, new PendingReservation(reservation, Math.Min(253402300799L, num3 + 180), num5)); if (text.Length > 0) { PendingDirectionOwners.Add(text, reservationId); } if (text2.Length > 0) { PendingDirectionOwners.Add(text2, reservationId); } return true; } internal static bool TryCommitReservation(InviteTravelCooldownReservation reservation, out InviteTravelCooldownFailure failure) { failure = InviteTravelCooldownFailure.None; if (!reservation.IsActive) { return true; } long num = UtcNowSeconds(); if (!PendingReservations.TryGetValue(reservation.ReservationId, out PendingReservation value) || value.ExpiresAtUtc < num || !ReservationMatches(reservation, value.Value)) { CancelReservation(reservation.ReservationId); failure = InviteTravelCooldownFailure.DataUnavailable; return false; } if (!_serverActive) { failure = InviteTravelCooldownFailure.DataUnavailable; return false; } if (_saveFaulted && (DateTime.UtcNow < _nextSaveAttemptUtc || !TryFlushPending(out string error))) { failure = InviteTravelCooldownFailure.StorageUnavailable; return false; } if (!TryGetCurrentWorldAccounts(out Dictionary> accounts)) { failure = InviteTravelCooldownFailure.DataUnavailable; return false; } Dictionary value2; bool flag = accounts.TryGetValue(reservation.AccountId, out value2); if (!flag) { if (accounts.Count >= 32768) { failure = InviteTravelCooldownFailure.AccountCapacityReached; return false; } value2 = new Dictionary(StringComparer.Ordinal); } CooldownEntry value3 = default(CooldownEntry); CooldownEntry value4 = default(CooldownEntry); bool flag2 = reservation.RecordsDeparture && value2.TryGetValue(reservation.SourceFavoriteId, out value3); bool flag3 = reservation.RecordsArrival && value2.TryGetValue(reservation.DestinationFavoriteId, out value4); int num2 = 0; if (reservation.RecordsDeparture && !flag2) { num2++; } if (reservation.RecordsArrival && !flag3 && (!reservation.RecordsDeparture || !string.Equals(reservation.SourceFavoriteId, reservation.DestinationFavoriteId, StringComparison.Ordinal))) { num2++; } if (value2.Count > 100000 - num2) { failure = InviteTravelCooldownFailure.PortalCapacityReached; return false; } if (_serializedByteUpperBound > 16777216 - value.SerializedByteEstimate) { failure = InviteTravelCooldownFailure.FileCapacityReached; return false; } if (!flag) { accounts.Add(reservation.AccountId, value2); } bool saveDirty = _saveDirty; long serializedByteUpperBound = _serializedByteUpperBound; if (reservation.RecordsDeparture) { value2[reservation.SourceFavoriteId] = value3.WithDeparture(num); } if (reservation.RecordsArrival) { CooldownEntry value5; CooldownEntry cooldownEntry = ((reservation.RecordsDeparture && string.Equals(reservation.SourceFavoriteId, reservation.DestinationFavoriteId, StringComparison.Ordinal) && value2.TryGetValue(reservation.DestinationFavoriteId, out value5)) ? value5 : value4); value2[reservation.DestinationFavoriteId] = cooldownEntry.WithArrival(num); } _serializedByteUpperBound += value.SerializedByteEstimate; _saveDirty = true; if (TryFlushPending(out error)) { RemovePendingReservation(value); return true; } RestoreEntry(value2, reservation.SourceFavoriteId, flag2, value3); RestoreEntry(value2, reservation.DestinationFavoriteId, flag3, value4); if (value2.Count == 0) { accounts.Remove(reservation.AccountId); } _saveDirty = saveDirty || _saveFaulted; _serializedByteUpperBound = serializedByteUpperBound; failure = InviteTravelCooldownFailure.SaveFailed; return false; } internal static void CancelReservation(string reservationId) { if (!string.IsNullOrEmpty(reservationId) && PendingReservations.TryGetValue(reservationId, out PendingReservation value)) { RemovePendingReservation(value); } } internal static string FormatRemaining(long seconds) { seconds = Math.Max(1L, seconds); long num = Math.Max(1L, (seconds + 59) / 60); if (num < 60) { return PortalRulesLocalization.Translate("$sighsorry_portalrules_duration_minutes", num.ToString(CultureInfo.InvariantCulture)); } long num2 = num / 60; long num3 = num % 60; if (num3 != 0L) { return PortalRulesLocalization.Translate("$sighsorry_portalrules_duration_hours_minutes", num2.ToString(CultureInfo.InvariantCulture), num3.ToString(CultureInfo.InvariantCulture)); } return PortalRulesLocalization.Translate("$sighsorry_portalrules_duration_hours", num2.ToString(CultureInfo.InvariantCulture)); } internal static long GetRemainingSeconds(long deadlineUtc) { return GetRemainingSeconds(deadlineUtc, PublicPortalCatalog.GetEstimatedServerUtcNowSeconds()); } internal static bool TryGetInviteArrivalCooldownRemaining(PublicPortalCatalogEntry portal, out long remainingSeconds) { remainingSeconds = 0L; if (portal.AccessMode != PublicPortalAccessMode.Invite || portal.InviteArrivalCooldownUntilUtc <= 0) { return false; } remainingSeconds = GetRemainingSeconds(portal.InviteArrivalCooldownUntilUtc); return remainingSeconds > 0; } private static bool TryNormalizeKeys(string steamId, string favoriteId, out string accountId, out string portalId) { accountId = ""; portalId = ""; if (PublicPortalData.TryNormalizeSteamId64(steamId, out accountId)) { return PublicPortalData.TryNormalizeFavoriteId(favoriteId, out portalId); } return false; } private static string CreateDirectionKey(string accountId, string favoriteId, bool departure) { return accountId + (departure ? ":D:" : ":A:") + favoriteId; } private static bool ReservationMatches(InviteTravelCooldownReservation first, InviteTravelCooldownReservation second) { if (string.Equals(first.ReservationId, second.ReservationId, StringComparison.Ordinal) && string.Equals(first.AccountId, second.AccountId, StringComparison.Ordinal) && string.Equals(first.SourceFavoriteId, second.SourceFavoriteId, StringComparison.Ordinal) && string.Equals(first.DestinationFavoriteId, second.DestinationFavoriteId, StringComparison.Ordinal) && first.RecordsDeparture == second.RecordsDeparture) { return first.RecordsArrival == second.RecordsArrival; } return false; } private static void PrunePendingReservations(long nowUtc) { PendingReservation[] array = PendingReservations.Values.Where((PendingReservation candidate) => candidate.ExpiresAtUtc < nowUtc).ToArray(); for (int num = 0; num < array.Length; num++) { RemovePendingReservation(array[num]); } } private static void RemovePendingReservation(PendingReservation pending) { InviteTravelCooldownReservation value = pending.Value; PendingReservations.Remove(value.ReservationId); if (value.RecordsDeparture) { PendingDirectionOwners.Remove(CreateDirectionKey(value.AccountId, value.SourceFavoriteId, departure: true)); } if (value.RecordsArrival) { PendingDirectionOwners.Remove(CreateDirectionKey(value.AccountId, value.DestinationFavoriteId, departure: false)); } } private static void ClearPendingReservations() { PendingReservations.Clear(); PendingDirectionOwners.Clear(); } private static bool TryGetEntry(string accountId, string portalId, out CooldownEntry entry) { entry = default(CooldownEntry); if (TryGetCurrentWorldAccounts(out Dictionary> accounts) && accounts.TryGetValue(accountId, out var value)) { return value.TryGetValue(portalId, out entry); } return false; } private static bool TryGetCurrentWorldAccounts(out Dictionary> accounts) { accounts = null; if (!string.IsNullOrEmpty(_worldId)) { return Worlds.TryGetValue(_worldId, out accounts); } return false; } private static void RestoreEntry(Dictionary entries, string portalId, bool existed, CooldownEntry previous) { if (!string.IsNullOrEmpty(portalId)) { if (existed) { entries[portalId] = previous; } else { entries.Remove(portalId); } } } private static long CalculateDeadline(long lastUtc, double hours) { if (lastUtc <= 0 || hours <= 0.0 || double.IsNaN(hours) || double.IsInfinity(hours)) { return 0L; } long num = (long)Math.Ceiling(Math.Min(hours, 8760.0) * 3600.0); if (lastUtc <= 253402300799L - num) { return lastUtc + num; } return 253402300799L; } private static long GetRemainingSeconds(long deadlineUtc, long nowUtc) { if (deadlineUtc <= nowUtc) { return 0L; } return deadlineUtc - nowUtc; } private static long UtcNowSeconds() { return DateTimeOffset.UtcNow.ToUnixTimeSeconds(); } private static long NormalizeUtcSeconds(long value) { if (value < 0 || value > 253402300799L) { return 0L; } return value; } private static void Load() { Worlds.Clear(); string path = _filePath + ".bak"; if (!File.Exists(_filePath)) { if (File.Exists(path)) { LoadFromPath(path); Save(); PortalRulesPlugin.PortalRulesLogger.LogWarning((object)"Recovered invite-travel-cooldowns.yml from its backup because the primary file was missing."); } return; } try { LoadFromPath(_filePath); } catch (Exception ex) { Worlds.Clear(); if (!File.Exists(path)) { throw; } try { LoadFromPath(path); string text = _filePath + ".invalid-" + DateTime.UtcNow.ToString("yyyyMMddHHmmssfff", CultureInfo.InvariantCulture) + "-" + Guid.NewGuid().ToString("N"); File.Move(_filePath, text); Save(); PortalRulesPlugin.PortalRulesLogger.LogWarning((object)("Recovered invite-travel-cooldowns.yml from its backup. The invalid primary file was preserved as '" + Path.GetFileName(text) + "'.")); } catch (Exception ex2) { Worlds.Clear(); throw new InvalidDataException("Failed to load invite-travel-cooldowns.yml and its backup. Primary error: " + ex.Message + ". Recovery error: " + ex2.Message, ex2); } } } private static void LoadFromPath(string path) { Worlds.Clear(); _serializedByteUpperBound = 0L; if (new FileInfo(path).Length > 16777216) { throw new InvalidDataException($"'{Path.GetFileName(path)}' exceeds {16777216L} bytes"); } string text = File.ReadAllText(path); CooldownFile cooldownFile = Deserializer.Deserialize(text); if (cooldownFile == null || cooldownFile.FormatVersion != 1 || cooldownFile.Worlds == null) { throw new InvalidDataException(string.Format("{0} must contain format_version {1} and worlds", "invite-travel-cooldowns.yml", 1)); } if (cooldownFile.Worlds.Count > 1024) { throw new InvalidDataException(string.Format("{0} contains more than {1} worlds", "invite-travel-cooldowns.yml", 1024)); } foreach (KeyValuePair>> world in cooldownFile.Worlds) { if (!long.TryParse(world.Key, NumberStyles.AllowLeadingSign, CultureInfo.InvariantCulture, out var result) || result == 0L || !string.Equals(world.Key, result.ToString(CultureInfo.InvariantCulture), StringComparison.Ordinal) || world.Value == null || world.Value.Count > 32768) { throw new InvalidDataException("invite-travel-cooldowns.yml contains an invalid world entry '" + world.Key + "'"); } Dictionary> dictionary = new Dictionary>(StringComparer.Ordinal); foreach (KeyValuePair> item in world.Value) { if (!PublicPortalData.TryNormalizeSteamId64(item.Key, out string steamId) || !string.Equals(item.Key, steamId, StringComparison.Ordinal) || item.Value == null || item.Value.Count > 100000) { throw new InvalidDataException("invite-travel-cooldowns.yml contains an invalid account entry '" + item.Key + "'"); } Dictionary dictionary2 = new Dictionary(StringComparer.Ordinal); foreach (KeyValuePair item2 in item.Value) { if (!PublicPortalData.TryNormalizeFavoriteId(item2.Key, out string normalizedFavoriteId) || !string.Equals(item2.Key, normalizedFavoriteId, StringComparison.Ordinal) || item2.Value == null || NormalizeUtcSeconds(item2.Value.DepartureLastUtc) != item2.Value.DepartureLastUtc || NormalizeUtcSeconds(item2.Value.ArrivalLastUtc) != item2.Value.ArrivalLastUtc) { throw new InvalidDataException("invite-travel-cooldowns.yml contains an invalid portal entry '" + item2.Key + "'"); } dictionary2.Add(normalizedFavoriteId, new CooldownEntry(item2.Value.DepartureLastUtc, item2.Value.ArrivalLastUtc)); } dictionary.Add(steamId, dictionary2); } Worlds.Add(world.Key, dictionary); } _serializedByteUpperBound = Encoding.UTF8.GetByteCount(text); } private static bool Prune(long nowUtc) { bool result = false; long oldestUsefulUtc = Math.Max(0L, nowUtc - 31622400); string[] array; foreach (Dictionary> value in Worlds.Values) { foreach (Dictionary value2 in value.Values) { array = (from pair in value2 where pair.Value.DepartureLastUtc < oldestUsefulUtc && pair.Value.ArrivalLastUtc < oldestUsefulUtc select pair.Key).ToArray(); foreach (string key in array) { value2.Remove(key); result = true; } } array = (from pair in value where pair.Value.Count == 0 select pair.Key).ToArray(); foreach (string key2 in array) { value.Remove(key2); result = true; } } array = (from pair in Worlds where pair.Value.Count == 0 && !string.Equals(pair.Key, _worldId, StringComparison.Ordinal) select pair.Key).ToArray(); foreach (string key3 in array) { Worlds.Remove(key3); result = true; } return result; } private static void Save() { if (string.IsNullOrEmpty(_filePath)) { throw new InvalidOperationException("Invite cooldown path is not initialized"); } if (Worlds.Count > 1024) { throw new InvalidDataException(string.Format("{0} cannot store more than {1} worlds", "invite-travel-cooldowns.yml", 1024)); } foreach (KeyValuePair>> world in Worlds) { if (world.Value.Count > 32768) { throw new InvalidDataException(string.Format("{0} cannot store more than {1} accounts per world", "invite-travel-cooldowns.yml", 32768)); } if (world.Value.Values.Any((Dictionary portals) => portals.Count > 100000)) { throw new InvalidDataException(string.Format("{0} cannot store more than {1} portals per account", "invite-travel-cooldowns.yml", 100000)); } } StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("# AUTO-GENERATED by PortalRules. Do not edit while the server is running."); stringBuilder.AppendLine("# Cooldowns are scoped by world, SteamID64, portal FavoriteId, and direction."); stringBuilder.AppendLine("format_version: 1"); if (Worlds.Count == 0) { stringBuilder.AppendLine("worlds: {}"); } else { stringBuilder.AppendLine("worlds:"); foreach (KeyValuePair>> item in Worlds.OrderBy>>, string>((KeyValuePair>> pair) => pair.Key, StringComparer.Ordinal)) { stringBuilder.Append(" \"").Append(item.Key).AppendLine("\":"); if (item.Value.Count == 0) { stringBuilder.AppendLine(" {}"); continue; } foreach (KeyValuePair> item2 in item.Value.OrderBy>, string>((KeyValuePair> pair) => pair.Key, StringComparer.Ordinal)) { stringBuilder.Append(" \"").Append(item2.Key).AppendLine("\":"); if (item2.Value.Count == 0) { stringBuilder.AppendLine(" {}"); continue; } foreach (KeyValuePair item3 in item2.Value.OrderBy, string>((KeyValuePair pair) => pair.Key, StringComparer.Ordinal)) { stringBuilder.Append(" \"").Append(item3.Key).AppendLine("\":"); StringBuilder stringBuilder2 = stringBuilder.Append(" departure_last_utc: "); long departureLastUtc = item3.Value.DepartureLastUtc; stringBuilder2.AppendLine(departureLastUtc.ToString(CultureInfo.InvariantCulture)); StringBuilder stringBuilder3 = stringBuilder.Append(" arrival_last_utc: "); departureLastUtc = item3.Value.ArrivalLastUtc; stringBuilder3.AppendLine(departureLastUtc.ToString(CultureInfo.InvariantCulture)); } } } } string text = stringBuilder.ToString(); int byteCount = Encoding.UTF8.GetByteCount(text); if ((long)byteCount > 16777216L) { throw new InvalidDataException(string.Format("{0} would exceed {1} bytes", "invite-travel-cooldowns.yml", 16777216L)); } PortalAccountStore.WriteAtomicFile(_filePath, text, _filePath + ".bak"); _serializedByteUpperBound = byteCount; } private static bool TryFlushPending(out string error) { error = ""; if (!_saveDirty) { _saveFaulted = false; return true; } try { Save(); _saveDirty = false; _saveFaulted = false; _nextSaveAttemptUtc = DateTime.UtcNow + SaveDebounce; return true; } catch (Exception ex) { _saveDirty = true; _saveFaulted = true; _nextSaveAttemptUtc = DateTime.UtcNow + SaveRetryDelay; error = ex.Message; PortalRulesPlugin.PortalRulesLogger.LogError((object)("Failed to save Invite cooldown data: " + ex.Message)); return false; } } } [BepInPlugin("sighsorry.PortalRules", "PortalRules", "1.0.0")] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInIncompatibility("org.bepinex.plugins.targetportal")] public class PortalRulesPlugin : BaseUnityPlugin { public enum Toggle { On = 1, Off = 0 } private sealed class ConfigurationManagerAttributes { public int? Order { get; set; } public int? CategoryOrder { get; set; } public bool? Browsable { get; set; } } internal const string ModName = "PortalRules"; internal const string ModVersion = "1.0.0"; internal const string Author = "sighsorry"; internal const string ModGUID = "sighsorry.PortalRules"; internal const string ClanSoftDependencyGuid = "sighsorry.Clan"; internal const string CurrencyPocketSoftDependencyGuid = "Azumatt.CurrencyPocket"; internal const string YouAreNotWorthySoftDependencyGuid = "sighsorry.YouAreNotWorthy"; internal const string TargetPortalIncompatibilityGuid = "org.bepinex.plugins.targetportal"; private static readonly string ConfigFileName = "sighsorry.PortalRules.cfg"; private static readonly string ConfigFileFullPath; private readonly Harmony _harmony = new Harmony("sighsorry.PortalRules"); public static readonly ManualLogSource PortalRulesLogger; private static readonly ConfigSync ConfigSync; private FileSystemWatcher? _watcher; private readonly object _reloadLock = new object(); private DateTime _lastConfigReloadTime; private int _clanRegistryDirty; private const long RELOAD_DELAY = 10000000L; private static ConfigEntry _serverConfigLocked; internal static bool IsAdmin { get { if (!((Object)(object)ZNet.instance == (Object)null) && !ZNet.instance.IsServer()) { return PublicPortalData.ServerAssignedIsAdmin; } return true; } } internal static bool HasAdminDebugAccess { get { if (IsAdmin) { return Player.m_debugMode; } return false; } } public void Awake() { bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet; ((BaseUnityPlugin)this).Config.SaveOnConfigSet = false; try { PortalRulesLocalization.Initialize(); _serverConfigLocked = ConfigEntry("1 - General", "Lock Configuration", Toggle.On, "If on, the configuration is locked and can be changed by server admins only.", synchronizedSetting: true, 200, 500); ConfigSync.AddLockingConfigEntry(_serverConfigLocked); PublicPortalConfig.Init(this); RequiredGlobalKeyAccess.Initialize(); Assembly executingAssembly = Assembly.GetExecutingAssembly(); ClanPortalAccess.RegistryChanged += OnClanRegistryChanged; ClanPortalAccess.Initialize(); AdminPortalPrefabManager.Initialize(); _harmony.PatchAll(executingAssembly); ((BaseUnityPlugin)this).Config.Save(); } finally { ((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet; } SetupWatcher(); } private void Update() { if (AdminPortalBiomeDefaults.Tick()) { PublicPortalCatalog.RefreshAndBroadcast(); } if (PortalAccountStore.Tick()) { PublicPortalServerPolicy.NotifyQuotaConfigurationChanged(); } InviteTravelCooldownStore.Tick(); PublicPortalTeleportService.Tick(); PublicPortalCatalog.TickIdentityRegistry(); AdminPortalPrefabManager.Tick(); PublicPortalMapController.Instance.Tick(); if (Interlocked.Exchange(ref _clanRegistryDirty, 0) != 0) { PublicPortalCatalog.RefreshClanViews(); } } private void OnDestroy() { try { TryCleanup("configuration watcher", delegate { if (_watcher != null) { _watcher.EnableRaisingEvents = false; _watcher.Dispose(); _watcher = null; } }); TryCleanup("portal map", PublicPortalMapController.Instance.End); TryCleanup("connected portal HUD", PublicPortalConnectedHud.Shutdown); TryCleanup("portal catalog", PublicPortalCatalog.Shutdown); TryCleanup("server policy", PublicPortalServerPolicy.Shutdown); TryCleanup("teleport service", PublicPortalTeleportService.Shutdown); TryCleanup("portal interaction", PublicPortalInteraction.Shutdown); TryCleanup("admin portal operations", AdminPortalOperations.Shutdown); TryCleanup("admin portal prefabs", AdminPortalPrefabManager.Shutdown); TryCleanup("GlobalKey integration", RequiredGlobalKeyAccess.Shutdown); TryCleanup("Clan event subscription", delegate { ClanPortalAccess.RegistryChanged -= OnClanRegistryChanged; }); TryCleanup("Clan integration", ClanPortalAccess.Shutdown); TryCleanup("configuration save", delegate { SaveWithRespectToConfigSet(); }); } finally { TryCleanup("Harmony patches", (Action)_harmony.UnpatchSelf); } } private static void TryCleanup(string component, Action cleanup) { try { cleanup(); } catch (Exception ex) { PortalRulesLogger.LogError((object)("Failed to clean up " + component + ": " + ex.Message)); } } private void OnClanRegistryChanged() { Interlocked.Exchange(ref _clanRegistryDirty, 1); } private void SetupWatcher() { _watcher = new FileSystemWatcher(Paths.ConfigPath, ConfigFileName); _watcher.Changed += ReadConfigValues; _watcher.Created += ReadConfigValues; _watcher.Renamed += ReadConfigValues; _watcher.SynchronizingObject = ThreadingHelper.SynchronizingObject; _watcher.EnableRaisingEvents = true; } private void ReadConfigValues(object sender, FileSystemEventArgs e) { DateTime now = DateTime.Now; if (now.Ticks - _lastConfigReloadTime.Ticks < 10000000) { return; } lock (_reloadLock) { if (!File.Exists(ConfigFileFullPath)) { PortalRulesLogger.LogWarning((object)"Config file does not exist. Skipping reload."); return; } try { PortalRulesLogger.LogDebug((object)"Reloading configuration..."); SaveWithRespectToConfigSet(reload: true); PortalRulesLogger.LogInfo((object)"Configuration reload complete."); } catch (Exception ex) { PortalRulesLogger.LogError((object)("Error reloading configuration: " + ex.Message)); } } _lastConfigReloadTime = now; } private void SaveWithRespectToConfigSet(bool reload = false) { bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet; ((BaseUnityPlugin)this).Config.SaveOnConfigSet = false; try { if (reload) { ((BaseUnityPlugin)this).Config.Reload(); } ((BaseUnityPlugin)this).Config.Save(); } finally { ((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet; } } internal ConfigEntry ConfigEntry(string group, string name, T value, ConfigDescription description, bool synchronizedSetting = true, int? order = null, int? categoryOrder = null, bool? browsable = null) { //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Expected O, but got Unknown object[] array = description.Tags ?? Array.Empty(); if (order.HasValue || categoryOrder.HasValue || browsable.HasValue) { array = array.Concat(new object[1] { new ConfigurationManagerAttributes { Order = order, CategoryOrder = categoryOrder, Browsable = browsable } }).ToArray(); } ConfigDescription val = new ConfigDescription(description.Description + (synchronizedSetting ? " [Synced with Server]" : " [Not Synced with Server]"), description.AcceptableValues, array); ConfigEntry val2 = ((BaseUnityPlugin)this).Config.Bind(group, name, value, val); ConfigSync.AddConfigEntry(val2).SynchronizedConfig = synchronizedSetting; return val2; } internal ConfigEntry ConfigEntry(string group, string name, T value, string description, bool synchronizedSetting = true, int? order = null, int? categoryOrder = null, bool? browsable = null) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected O, but got Unknown return ConfigEntry(group, name, value, new ConfigDescription(description, (AcceptableValueBase)null, Array.Empty()), synchronizedSetting, order, categoryOrder, browsable); } static PortalRulesPlugin() { string configPath = Paths.ConfigPath; char directorySeparatorChar = Path.DirectorySeparatorChar; ConfigFileFullPath = configPath + directorySeparatorChar + ConfigFileName; PortalRulesLogger = Logger.CreateLogSource("PortalRules"); ConfigSync = new ConfigSync("sighsorry.PortalRules") { DisplayName = "PortalRules", CurrentVersion = "1.0.0", MinimumRequiredVersion = "1.0.0" }; _serverConfigLocked = null; } } public static class KeyboardExtensions { public static bool IsKeyDown(this KeyboardShortcut shortcut) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) if ((int)((KeyboardShortcut)(ref shortcut)).MainKey != 0 && Input.GetKeyDown(((KeyboardShortcut)(ref shortcut)).MainKey)) { return ((KeyboardShortcut)(ref shortcut)).Modifiers.All((Func)Input.GetKey); } return false; } public static bool IsKeyHeld(this KeyboardShortcut shortcut) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) if ((int)((KeyboardShortcut)(ref shortcut)).MainKey != 0 && Input.GetKey(((KeyboardShortcut)(ref shortcut)).MainKey)) { return ((KeyboardShortcut)(ref shortcut)).Modifiers.All((Func)Input.GetKey); } return false; } } public static class ToggleExtentions { public static bool IsOn(this PortalRulesPlugin.Toggle value) { return value == PortalRulesPlugin.Toggle.On; } public static bool IsOff(this PortalRulesPlugin.Toggle value) { return value == PortalRulesPlugin.Toggle.Off; } } internal static class PortalAccountStore { private sealed class PlayerIdentitiesYaml { [YamlMember(Alias = "format_version")] public int FormatVersion { get; set; } [YamlMember(Alias = "identities")] public Dictionary? Identities { get; set; } } private sealed class PortalLimitOverridesYaml { [YamlMember(Alias = "format_version")] public int FormatVersion { get; set; } [YamlMember(Alias = "overrides")] public Dictionary? Overrides { get; set; } } private readonly struct PortalLimitOverride { public readonly int PortalLimit; public readonly int? InviteLimit; public PortalLimitOverride(int portalLimit, int? inviteLimit) { PortalLimit = portalLimit; InviteLimit = inviteLimit; } public bool Matches(PortalLimitOverride other) { if (PortalLimit == other.PortalLimit) { return InviteLimit == other.InviteLimit; } return false; } } private const int IdentityFormatVersion = 1; private const int OverrideFormatVersion = 2; private const int MaximumIdentityCount = 16384; private const int MaximumOverrideCount = 4096; private const int MaximumOverrideReloadAttempts = 5; private const int MaximumPortalLimit = 10000; private const long MaximumIdentityFileBytes = 2097152L; private const long MaximumOverrideFileBytes = 524288L; private const string DirectoryName = "PortalRules"; private const string IdentityFileName = "player-identities.yml"; private const string OverrideFileName = "portal-limit-overrides.yml"; private static readonly TimeSpan IdentitySaveDebounce = TimeSpan.FromSeconds(2.0); private static readonly TimeSpan IdentityMaximumSaveDelay = TimeSpan.FromSeconds(30.0); private static readonly TimeSpan IdentitySaveRetryDelay = TimeSpan.FromSeconds(10.0); private static readonly TimeSpan OverrideReloadDebounce = TimeSpan.FromMilliseconds(500.0); private static readonly TimeSpan OverrideReloadRetryDelay = TimeSpan.FromSeconds(1.0); private static readonly TimeSpan OverridePollingInterval = TimeSpan.FromSeconds(30.0); private static readonly TimeSpan WatcherRestartRetryDelay = TimeSpan.FromSeconds(10.0); private static readonly UTF8Encoding Utf8WithoutBom = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false); private static readonly IDeserializer Deserializer = new DeserializerBuilder().WithDuplicateKeyChecking().Build(); private static readonly Dictionary PlayerIdentities = new Dictionary(); private static readonly HashSet ConflictedPlayerIds = new HashSet(); private static Dictionary _portalLimitOverrides = new Dictionary(StringComparer.Ordinal); private static FileSystemWatcher? _overrideWatcher; private static string _configurationDirectory = ""; private static string _identityFilePath = ""; private static string _overrideFilePath = ""; private static bool _active; private static bool _hasValidOverrideSnapshot; private static bool _identityDirty; private static DateTime _identityFirstDirtyUtc; private static DateTime _identitySaveNotBeforeUtc; private static DateTime _nextOverridePollUtc; private static int _overrideReloadRequested; private static int _overrideReloadAttempts; private static int _overrideWatcherRestartRequested; private static long _overrideReloadNotBeforeUtcTicks; private static long _overrideWatcherRestartNotBeforeUtcTicks; internal static bool IsActive => _active; internal static bool HasValidOverrideSnapshot { get { if (_active) { return _hasValidOverrideSnapshot; } return false; } } internal static void BeginServerSession() { //IL_01a8: Unknown result type (might be due to invalid IL or missing references) if (_active || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer()) { return; } if (!EndServerSession()) { PortalRulesPlugin.PortalRulesLogger.LogError((object)"PortalRules identity storage retained unsaved data from the previous session; the new session will remain unavailable until that data can be saved."); return; } _active = true; PlayerIdentities.Clear(); ConflictedPlayerIds.Clear(); _portalLimitOverrides = new Dictionary(StringComparer.Ordinal); _hasValidOverrideSnapshot = false; _identityDirty = false; _identityFirstDirtyUtc = DateTime.MinValue; _identitySaveNotBeforeUtc = DateTime.MinValue; _nextOverridePollUtc = DateTime.UtcNow + OverridePollingInterval; Interlocked.Exchange(ref _overrideReloadRequested, 0); Interlocked.Exchange(ref _overrideReloadAttempts, 0); Interlocked.Exchange(ref _overrideWatcherRestartRequested, 0); Interlocked.Exchange(ref _overrideReloadNotBeforeUtcTicks, 0L); Interlocked.Exchange(ref _overrideWatcherRestartNotBeforeUtcTicks, 0L); _configurationDirectory = Path.Combine(Paths.ConfigPath, "PortalRules"); _identityFilePath = Path.Combine(_configurationDirectory, "player-identities.yml"); _overrideFilePath = Path.Combine(_configurationDirectory, "portal-limit-overrides.yml"); try { Directory.CreateDirectory(_configurationDirectory); LoadPlayerIdentities(); EnsureOverrideTemplate(); try { StartOverrideWatcher(); } catch (Exception ex) { StopOverrideWatcher(); Interlocked.Exchange(ref _overrideWatcherRestartRequested, 1); ScheduleOverrideWatcherRestart(WatcherRestartRetryDelay); PortalRulesPlugin.PortalRulesLogger.LogError((object)("Failed to watch portal-limit-overrides.yml; periodic reload remains active: " + ex.Message)); } if (!TryReloadOverrides(logOnlyWhenChanged: false, out var _)) { ScheduleOverrideReload(OverrideReloadRetryDelay); } } catch (Exception ex2) { StopOverrideWatcher(); _active = false; PortalRulesPlugin.PortalRulesLogger.LogError((object)("Failed to initialize PortalRules account files: " + ex2.Message)); } if (_active && (int)ZNet.m_onlineBackend != 0) { PortalRulesPlugin.PortalRulesLogger.LogWarning((object)"PortalRules account attribution is Steam-only. Counted portal placement will fail closed on a non-Steam backend."); } } internal static bool EndServerSession() { bool num = FlushPlayerIdentities(); StopOverrideWatcher(); _active = false; if (!num && _identityDirty) { PortalRulesPlugin.PortalRulesLogger.LogError((object)"PortalRules identity storage could not complete its final save; unsaved identities are being retained in memory for retry."); return false; } PlayerIdentities.Clear(); ConflictedPlayerIds.Clear(); _portalLimitOverrides = new Dictionary(StringComparer.Ordinal); _hasValidOverrideSnapshot = false; _identityDirty = false; _identityFirstDirtyUtc = DateTime.MinValue; _identitySaveNotBeforeUtc = DateTime.MinValue; _nextOverridePollUtc = DateTime.MinValue; _configurationDirectory = ""; _identityFilePath = ""; _overrideFilePath = ""; Interlocked.Exchange(ref _overrideReloadRequested, 0); Interlocked.Exchange(ref _overrideReloadAttempts, 0); Interlocked.Exchange(ref _overrideWatcherRestartRequested, 0); Interlocked.Exchange(ref _overrideReloadNotBeforeUtcTicks, 0L); Interlocked.Exchange(ref _overrideWatcherRestartNotBeforeUtcTicks, 0L); return true; } internal static bool Tick() { DateTime utcNow = DateTime.UtcNow; bool flag = !_active && _identityDirty && !string.IsNullOrEmpty(_identityFilePath); if (_identityDirty && utcNow >= _identitySaveNotBeforeUtc && SavePlayerIdentities(utcNow) && flag) { EndServerSession(); if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer()) { BeginServerSession(); } } if (!_active) { return false; } if (Volatile.Read(in _overrideWatcherRestartRequested) != 0 && utcNow.Ticks >= Interlocked.Read(in _overrideWatcherRestartNotBeforeUtcTicks)) { Interlocked.Exchange(ref _overrideWatcherRestartRequested, 0); try { StartOverrideWatcher(); RequestOverrideReload(); } catch (Exception ex) { PortalRulesPlugin.PortalRulesLogger.LogError((object)("Failed to restart the portal-limit-overrides.yml watcher: " + ex.Message)); Interlocked.Exchange(ref _overrideWatcherRestartRequested, 1); ScheduleOverrideWatcherRestart(WatcherRestartRetryDelay); } } if (utcNow >= _nextOverridePollUtc) { _nextOverridePollUtc = utcNow + OverridePollingInterval; if (Volatile.Read(in _overrideReloadRequested) == 0) { ScheduleOverrideReload(TimeSpan.Zero); } } if (Volatile.Read(in _overrideReloadRequested) != 0 && utcNow.Ticks >= Interlocked.Read(in _overrideReloadNotBeforeUtcTicks)) { Interlocked.Exchange(ref _overrideReloadRequested, 0); if (TryReloadOverrides(logOnlyWhenChanged: true, out var changed)) { Interlocked.Exchange(ref _overrideReloadAttempts, 0); return changed; } if (Interlocked.Increment(ref _overrideReloadAttempts) <= 5) { ScheduleOverrideReload(OverrideReloadRetryDelay); } } return false; } internal static bool FlushPlayerIdentities() { if (!_identityDirty) { return true; } if (string.IsNullOrEmpty(_identityFilePath)) { return false; } return SavePlayerIdentities(DateTime.UtcNow); } internal static bool TryRememberIdentity(long playerId, string steamId, out bool added) { added = false; if (!_active || playerId == 0L || !PublicPortalData.TryNormalizeSteamId64(steamId, out string steamId2)) { return false; } if (ConflictedPlayerIds.Contains(playerId)) { return false; } if (PlayerIdentities.TryGetValue(playerId, out string value)) { if (string.Equals(value, steamId2, StringComparison.Ordinal)) { return true; } ConflictedPlayerIds.Add(playerId); PortalRulesPlugin.PortalRulesLogger.LogError((object)($"Refused identity collision for playerID {playerId}: " + "stored SteamID64 " + value + ", authenticated SteamID64 " + steamId2 + ". The stored mapping was not overwritten and this playerID is blocked for this session.")); return false; } if (PlayerIdentities.Count >= 16384) { PortalRulesPlugin.PortalRulesLogger.LogError((object)(string.Format("Refused playerID {0}: {1} reached ", playerId, "player-identities.yml") + $"the {16384} identity limit.")); return false; } PlayerIdentities.Add(playerId, steamId2); added = true; MarkPlayerIdentitiesDirty(DateTime.UtcNow); return true; } internal static bool TryResolveSteamId(long playerId, out string steamId) { steamId = ""; if (_active && playerId != 0L && !ConflictedPlayerIds.Contains(playerId)) { return PlayerIdentities.TryGetValue(playerId, out steamId); } return false; } internal static int GetEffectivePortalLimit(string steamId, int defaultLimit) { if (!PublicPortalData.TryNormalizeSteamId64(steamId, out string steamId2)) { return 0; } if (!_active || !_portalLimitOverrides.TryGetValue(steamId2, out var value)) { return Math.Max(-1, Math.Min(10000, defaultLimit)); } return value.PortalLimit; } internal static int GetEffectiveInvitePortalLimit(string steamId, int defaultLimit) { if (!PublicPortalData.TryNormalizeSteamId64(steamId, out string steamId2)) { return 0; } int result = Math.Max(-1, Math.Min(10000, defaultLimit)); if (!_active || !_portalLimitOverrides.TryGetValue(steamId2, out var value) || !value.InviteLimit.HasValue) { return result; } return value.InviteLimit.Value; } private static void LoadPlayerIdentities() { if (!File.Exists(_identityFilePath)) { string path = _identityFilePath + ".bak"; if (File.Exists(path) && TryReadPlayerIdentities(path, out Dictionary identities)) { ReplacePlayerIdentities(identities); PortalRulesPlugin.PortalRulesLogger.LogWarning((object)"Recovered missing player-identities.yml from its last valid backup."); } _identityDirty = true; SavePlayerIdentities(DateTime.UtcNow); return; } if (TryReadPlayerIdentities(_identityFilePath, out Dictionary identities2)) { ReplacePlayerIdentities(identities2); return; } string path2 = _identityFilePath + ".bak"; QuarantineInvalidIdentityFile(); if (File.Exists(path2) && TryReadPlayerIdentities(path2, out Dictionary identities3)) { ReplacePlayerIdentities(identities3); PortalRulesPlugin.PortalRulesLogger.LogWarning((object)"Recovered player-identities.yml from its last valid backup."); } else { PlayerIdentities.Clear(); PortalRulesPlugin.PortalRulesLogger.LogError((object)"Could not load player-identities.yml or its backup; existing unstamped portals will remain unattributed until identities are learned again."); } _identityDirty = true; SavePlayerIdentities(DateTime.UtcNow); } private static bool TryReadPlayerIdentities(string path, out Dictionary identities) { identities = new Dictionary(); try { if (new FileInfo(path).Length > 2097152) { throw new InvalidDataException($"file exceeds {2097152L} bytes"); } PlayerIdentitiesYaml playerIdentitiesYaml = Deserializer.Deserialize(File.ReadAllText(path)); if (playerIdentitiesYaml == null || playerIdentitiesYaml.FormatVersion != 1 || playerIdentitiesYaml.Identities == null) { throw new InvalidDataException($"format_version must be {1} and identities must be present"); } if (playerIdentitiesYaml.Identities.Count > 16384) { throw new InvalidDataException($"identities exceeds {16384} entries"); } foreach (KeyValuePair identity in playerIdentitiesYaml.Identities) { if (!long.TryParse(identity.Key, NumberStyles.AllowLeadingSign, CultureInfo.InvariantCulture, out var result) || result == 0L || !string.Equals(identity.Key, result.ToString(CultureInfo.InvariantCulture), StringComparison.Ordinal)) { throw new InvalidDataException("identity key '" + identity.Key + "' is not a canonical nonzero playerID"); } if (!PublicPortalData.TryNormalizeSteamId64(identity.Value, out string steamId) || !string.Equals(identity.Value, steamId, StringComparison.Ordinal)) { throw new InvalidDataException("identity value for playerID " + identity.Key + " is not a bare SteamID64"); } identities.Add(result, steamId); } return true; } catch (Exception ex) { PortalRulesPlugin.PortalRulesLogger.LogError((object)("Failed to read " + Path.GetFileName(path) + ": " + ex.Message)); identities.Clear(); return false; } } private static void ReplacePlayerIdentities(Dictionary identities) { PlayerIdentities.Clear(); foreach (KeyValuePair identity in identities) { PlayerIdentities.Add(identity.Key, identity.Value); } } private static bool SavePlayerIdentities(DateTime nowUtc) { try { WriteAtomicFile(_identityFilePath, SerializePlayerIdentities(), _identityFilePath + ".bak"); _identityDirty = false; _identityFirstDirtyUtc = DateTime.MinValue; _identitySaveNotBeforeUtc = DateTime.MinValue; return true; } catch (Exception ex) { _identityDirty = true; _identityFirstDirtyUtc = nowUtc; _identitySaveNotBeforeUtc = nowUtc + IdentitySaveRetryDelay; PortalRulesPlugin.PortalRulesLogger.LogError((object)("Failed to save player-identities.yml; retrying later: " + ex.Message)); return false; } } private static string SerializePlayerIdentities() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("# AUTO-GENERATED by PortalRules. Do not edit while the server is running."); stringBuilder.Append("format_version: ").AppendLine(1.ToString(CultureInfo.InvariantCulture)); if (PlayerIdentities.Count == 0) { stringBuilder.AppendLine("identities: {}"); return stringBuilder.ToString(); } stringBuilder.AppendLine("identities:"); foreach (KeyValuePair item in PlayerIdentities.OrderBy, long>((KeyValuePair pair) => pair.Key)) { stringBuilder.Append(" \"").Append(item.Key.ToString(CultureInfo.InvariantCulture)).Append("\": \"") .Append(item.Value) .AppendLine("\""); } return stringBuilder.ToString(); } private static void MarkPlayerIdentitiesDirty(DateTime nowUtc) { if (!_identityDirty) { _identityFirstDirtyUtc = nowUtc; } _identityDirty = true; DateTime dateTime = nowUtc + IdentitySaveDebounce; DateTime dateTime2 = _identityFirstDirtyUtc + IdentityMaximumSaveDelay; _identitySaveNotBeforeUtc = ((dateTime < dateTime2) ? dateTime : dateTime2); } private static void QuarantineInvalidIdentityFile() { if (File.Exists(_identityFilePath)) { string text = DateTime.UtcNow.ToString("yyyyMMdd-HHmmss", CultureInfo.InvariantCulture); string text2 = _identityFilePath + ".invalid-" + text; if (File.Exists(text2)) { text2 = text2 + "-" + Guid.NewGuid().ToString("N"); } File.Move(_identityFilePath, text2); PortalRulesPlugin.PortalRulesLogger.LogWarning((object)("Moved invalid player-identities.yml to " + Path.GetFileName(text2) + ".")); } } private static void EnsureOverrideTemplate() { if (!File.Exists(_overrideFilePath)) { WriteAtomicFile(_overrideFilePath, "# PortalRules per-Steam-account portal limit overrides.\n# Keys must be bare 17-digit SteamID64 values.\n# Each value is: portal_limit OR portal_limit, invite_limit\n# portal_limit: -1 = unlimited, 0 = block new counted portals, 1..10000 = custom limit.\n# invite_limit: -1 = unlimited, 0 = disable Invite and return existing eligible Invite portals to the Builder's Personal access, 1..10000 = custom limit.\n# If invite_limit is omitted, the current server config value is used dynamically.\n# A trailing comma is invalid; omit the comma and second value together.\n# Schema examples:\n# format_version: 2\n# overrides:\n# \"76561198000000000\": 10 ## portal_limit; invite_limit uses server config\n# \"76561198000000001\": 10, 1 ## portal_limit, invite_limit\n# format_version 1 remains unsupported; no legacy migration is performed.\nformat_version: 2\noverrides: {}\n", null); } } private static bool TryReloadOverrides(bool logOnlyWhenChanged, out bool changed) { changed = false; if (!File.Exists(_overrideFilePath)) { PortalRulesPlugin.PortalRulesLogger.LogWarning((object)"portal-limit-overrides.yml is missing; retaining the last valid overrides. Use 'overrides: {}' to clear them."); return false; } try { if (new FileInfo(_overrideFilePath).Length > 524288) { throw new InvalidDataException($"file exceeds {524288L} bytes"); } PortalLimitOverridesYaml portalLimitOverridesYaml = Deserializer.Deserialize(File.ReadAllText(_overrideFilePath)); if (portalLimitOverridesYaml == null || portalLimitOverridesYaml.FormatVersion != 2 || portalLimitOverridesYaml.Overrides == null) { throw new InvalidDataException($"format_version must be {2} and overrides must be present"); } if (portalLimitOverridesYaml.Overrides.Count > 4096) { throw new InvalidDataException($"overrides exceeds {4096} entries"); } Dictionary dictionary = new Dictionary(StringComparer.Ordinal); foreach (KeyValuePair @override in portalLimitOverridesYaml.Overrides) { if (!PublicPortalData.TryNormalizeSteamId64(@override.Key, out string steamId) || !string.Equals(@override.Key, steamId, StringComparison.Ordinal)) { throw new InvalidDataException("override key '" + @override.Key + "' is not a bare SteamID64"); } string[] array = (@override.Value ?? "").Split(new char[1] { ',' }); int num = array.Length; bool flag = ((num < 1 || num > 2) ? true : false); if (flag || !int.TryParse(array[0].Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out var result)) { throw new InvalidDataException("override for " + @override.Key + " must be 'portal_limit' or 'portal_limit, invite_limit'"); } ValidateOverrideLimit(@override.Key, "portal_limit", result); int? inviteLimit = null; if (array.Length == 2) { if (!int.TryParse(array[1].Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out var result2)) { throw new InvalidDataException("override for " + @override.Key + " must be 'portal_limit' or 'portal_limit, invite_limit'"); } ValidateOverrideLimit(@override.Key, "invite_limit", result2); inviteLimit = result2; } dictionary.Add(steamId, new PortalLimitOverride(result, inviteLimit)); } changed = !_hasValidOverrideSnapshot || !DictionariesEqual(_portalLimitOverrides, dictionary); _portalLimitOverrides = dictionary; _hasValidOverrideSnapshot = true; if (!logOnlyWhenChanged | changed) { PortalRulesPlugin.PortalRulesLogger.LogInfo((object)$"Loaded {_portalLimitOverrides.Count} account limit override(s)."); } return true; } catch (Exception ex) { PortalRulesPlugin.PortalRulesLogger.LogError((object)("Failed to reload portal-limit-overrides.yml; retaining the last valid overrides: " + ex.Message)); return false; } } private static void ValidateOverrideLimit(string steamId, string fieldName, int value) { if (value < -1 || value > 10000) { throw new InvalidDataException(fieldName + " override for " + steamId + " must be between -1 and " + 10000); } } private static bool DictionariesEqual(Dictionary left, Dictionary right) { if (left.Count != right.Count) { return false; } foreach (KeyValuePair item in left) { if (!right.TryGetValue(item.Key, out var value) || !value.Matches(item.Value)) { return false; } } return true; } private static void StartOverrideWatcher() { StopOverrideWatcher(); _overrideWatcher = new FileSystemWatcher(_configurationDirectory, "portal-limit-overrides.yml") { IncludeSubdirectories = false, NotifyFilter = (NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.LastWrite) }; _overrideWatcher.Changed += OnOverrideFileChanged; _overrideWatcher.Created += OnOverrideFileChanged; _overrideWatcher.Deleted += OnOverrideFileChanged; _overrideWatcher.Renamed += OnOverrideFileRenamed; _overrideWatcher.Error += OnOverrideWatcherError; _overrideWatcher.EnableRaisingEvents = true; } private static void StopOverrideWatcher() { if (_overrideWatcher != null) { _overrideWatcher.EnableRaisingEvents = false; _overrideWatcher.Changed -= OnOverrideFileChanged; _overrideWatcher.Created -= OnOverrideFileChanged; _overrideWatcher.Deleted -= OnOverrideFileChanged; _overrideWatcher.Renamed -= OnOverrideFileRenamed; _overrideWatcher.Error -= OnOverrideWatcherError; _overrideWatcher.Dispose(); _overrideWatcher = null; } } private static void OnOverrideFileChanged(object sender, FileSystemEventArgs args) { RequestOverrideReload(); } private static void OnOverrideFileRenamed(object sender, RenamedEventArgs args) { RequestOverrideReload(); } private static void OnOverrideWatcherError(object sender, ErrorEventArgs args) { PortalRulesPlugin.PortalRulesLogger.LogError((object)("The portal-limit-overrides.yml watcher failed and will be restarted: " + args.GetException().Message)); Interlocked.Exchange(ref _overrideWatcherRestartRequested, 1); ScheduleOverrideWatcherRestart(WatcherRestartRetryDelay); RequestOverrideReload(); } private static void RequestOverrideReload() { Interlocked.Exchange(ref _overrideReloadAttempts, 0); ScheduleOverrideReload(OverrideReloadDebounce); } private static void ScheduleOverrideReload(TimeSpan delay) { Interlocked.Exchange(ref _overrideReloadNotBeforeUtcTicks, (DateTime.UtcNow + delay).Ticks); Interlocked.Exchange(ref _overrideReloadRequested, 1); } private static void ScheduleOverrideWatcherRestart(TimeSpan delay) { Interlocked.Exchange(ref _overrideWatcherRestartNotBeforeUtcTicks, (DateTime.UtcNow + delay).Ticks); } internal static void WriteAtomicFile(string path, string content, string? backupPath) { string? obj = Path.GetDirectoryName(path) ?? throw new InvalidOperationException("Target directory is unavailable."); Directory.CreateDirectory(obj); string text = Path.Combine(obj, "." + Path.GetFileName(path) + "." + Guid.NewGuid().ToString("N") + ".tmp"); try { using (FileStream fileStream = new FileStream(text, FileMode.CreateNew, FileAccess.Write, FileShare.None)) { using StreamWriter streamWriter = new StreamWriter(fileStream, Utf8WithoutBom); streamWriter.Write(content); streamWriter.Flush(); fileStream.Flush(flushToDisk: true); } if (File.Exists(path)) { try { File.Replace(text, path, backupPath, ignoreMetadataErrors: true); return; } catch (PlatformNotSupportedException) { ReplaceUsingMoves(text, path, backupPath); return; } catch (NotSupportedException) { ReplaceUsingMoves(text, path, backupPath); return; } catch (IOException) when (Path.DirectorySeparatorChar == '/') { ReplaceUsingMoves(text, path, backupPath); return; } } File.Move(text, path); } finally { if (File.Exists(text)) { File.Delete(text); } } } private static void ReplaceUsingMoves(string temporaryPath, string path, string? backupPath) { string text = backupPath ?? (path + ".replace-backup"); if (File.Exists(text)) { File.Delete(text); } File.Move(path, text); try { File.Move(temporaryPath, path); if (backupPath == null && File.Exists(text)) { File.Delete(text); } } catch { if (!File.Exists(path) && File.Exists(text)) { File.Move(text, path); } throw; } } } internal static class PortalRulesLocalization { internal const string PortalDeniedToken = "sighsorry_portalrules_portal_denied"; internal const string MissingGlobalKeyToken = "sighsorry_portalrules_missing_global_key"; internal const string OwnedTokenPrefix = "sighsorry_portalrules_"; private const string EnglishLanguage = "English"; private const string KoreanLanguage = "Korean"; private const string ExternalFilePrefix = "PortalRules."; private const string EmbeddedResourcePrefix = "PortalRules.translations."; private const long MaxExternalTranslationFileBytes = 1048576L; private static readonly object Sync = new object(); private static readonly StringComparer FileSystemPathComparer = ((Path.DirectorySeparatorChar == '\\') ? StringComparer.OrdinalIgnoreCase : StringComparer.Ordinal); private static readonly IComparer DeterministicPathComparer = Comparer.Create(delegate(string left, string right) { int num = FileSystemPathComparer.Compare(left, right); return (num == 0) ? StringComparer.Ordinal.Compare(left, right) : num; }); private static readonly Dictionary BuiltInEnglish = new Dictionary(StringComparer.Ordinal) { ["sighsorry_portalrules_portal_denied"] = "You cannot use this portal.", ["sighsorry_portalrules_missing_global_key"] = "You need \"{0}\"." }; private static readonly Dictionary> ExternalTranslations = new Dictionary>(StringComparer.OrdinalIgnoreCase); private static readonly HashSet LoggedFormatFailures = new HashSet(StringComparer.Ordinal); private static Dictionary _english = new Dictionary(BuiltInEnglish, StringComparer.Ordinal); private static Dictionary _safeEnglish = new Dictionary(BuiltInEnglish, StringComparer.Ordinal); private static Dictionary _embeddedKorean = new Dictionary(StringComparer.Ordinal); private static Dictionary _active = new Dictionary(BuiltInEnglish, StringComparer.Ordinal); private static string _activeLanguage = "English"; private static bool _initialized; private static bool _addWordResolved; private static bool _addWordFailureLogged; private static MethodInfo? _addWordMethod; internal static void Initialize() { lock (Sync) { if (_initialized) { return; } try { Dictionary dictionary = new Dictionary(BuiltInEnglish, StringComparer.Ordinal); if (TryReadEmbedded("English", out Dictionary translations)) { Merge(dictionary, translations); } Dictionary dictionary2 = new Dictionary(dictionary, StringComparer.Ordinal); Dictionary dictionary3 = new Dictionary(StringComparer.Ordinal); if (TryReadEmbedded("Korean", out Dictionary translations2)) { Merge(dictionary3, translations2); } Dictionary> dictionary4 = LoadExternalTranslations(); if (dictionary4.TryGetValue("English", out Dictionary value)) { Merge(dictionary2, value); } _english = dictionary2; _safeEnglish = dictionary; _embeddedKorean = dictionary3; ExternalTranslations.Clear(); foreach (KeyValuePair> item in dictionary4) { ExternalTranslations[item.Key] = item.Value; } } catch (Exception arg) { PortalRulesPlugin.PortalRulesLogger.LogError((object)$"PortalRules localization initialization failed; using built-in English: {arg}"); ResetToBuiltInEnglish(); } finally { _initialized = true; } } ApplyCurrentLanguage(); } internal static string Translate(string token, params string[] args) { EnsureInitialized(); string originalToken = (token ?? string.Empty).Trim(); string text = NormalizeToken(token); if (text.Length == 0) { return string.Empty; } string activeLanguage; string value; string text2; bool flag2; lock (Sync) { activeLanguage = _activeLanguage; bool flag = _safeEnglish.TryGetValue(text, out value); if (!flag) { value = (BuiltInEnglish.TryGetValue(text, out string value2) ? value2 : text); } string value3; bool num = _active.TryGetValue(text, out value3); text2 = (num ? value3 : value); flag2 = num || flag; } string[] array = args ?? Array.Empty(); if (!text.StartsWith("sighsorry_portalrules_", StringComparison.Ordinal) || !flag2) { return TranslateVanilla(originalToken, text, array); } try { CultureInfo currentCulture = CultureInfo.CurrentCulture; object[] args2 = array; return string.Format(currentCulture, text2, args2); } catch (FormatException exception) { LogFormatFailureOnce(activeLanguage, text, exception); if (!string.Equals(text2, value, StringComparison.Ordinal)) { try { CultureInfo currentCulture2 = CultureInfo.CurrentCulture; string format = value; object[] args2 = array; return string.Format(currentCulture2, format, args2); } catch (FormatException exception2) { LogFormatFailureOnce("English", text, exception2); } } return value; } catch (Exception ex) { PortalRulesPlugin.PortalRulesLogger.LogWarning((object)("Could not translate PortalRules token '" + text + "'; using English text: " + ex.Message)); return value; } } internal static void ApplyCurrentLanguage() { try { Localization instance = Localization.instance; string selectedLanguage = GetSelectedLanguage(instance); ApplyLanguage(instance, selectedLanguage); } catch (Exception ex) { PortalRulesPlugin.PortalRulesLogger.LogWarning((object)("Could not apply the current PortalRules language; keeping English: " + ex.Message)); SetActiveLanguage("English"); } } internal static void ApplyLanguage(Localization? localization, string? language) { EnsureInitialized(); string text = language?.Trim() ?? string.Empty; string text2 = ((text.Length == 0) ? "English" : text); Dictionary dictionary = BuildActiveLanguage(text2); lock (Sync) { _activeLanguage = text2; _active = dictionary; } RegisterActiveTranslations(localization, dictionary); } private static void EnsureInitialized() { bool initialized; lock (Sync) { initialized = _initialized; } if (!initialized) { Initialize(); } } private static Dictionary BuildActiveLanguage(string language) { lock (Sync) { Dictionary dictionary = new Dictionary(_english, StringComparer.Ordinal); if (string.Equals(language, "Korean", StringComparison.OrdinalIgnoreCase)) { Merge(dictionary, _embeddedKorean); } if (!string.Equals(language, "English", StringComparison.OrdinalIgnoreCase) && ExternalTranslations.TryGetValue(language, out Dictionary value)) { Merge(dictionary, value); } return dictionary; } } private static void SetActiveLanguage(string language) { Dictionary active = BuildActiveLanguage(language); lock (Sync) { _activeLanguage = language; _active = active; } } private static string GetSelectedLanguage(Localization? localization) { if (localization == null) { return "English"; } try { string selectedLanguage = localization.GetSelectedLanguage(); return string.IsNullOrWhiteSpace(selectedLanguage) ? "English" : selectedLanguage.Trim(); } catch { return "English"; } } private static Dictionary> LoadExternalTranslations() { Dictionary> source = DiscoverExternalFiles(); Dictionary> dictionary = new Dictionary>(StringComparer.OrdinalIgnoreCase); foreach (KeyValuePair> item in source.OrderBy>, string>((KeyValuePair> pair) => pair.Key, StringComparer.OrdinalIgnoreCase).ThenBy>, string>((KeyValuePair> pair) => pair.Key, StringComparer.Ordinal)) { Dictionary dictionary2 = new Dictionary(StringComparer.Ordinal); Dictionary dictionary3 = new Dictionary(StringComparer.Ordinal); foreach (string item2 in item.Value.OrderBy((string result) => result, StringComparer.OrdinalIgnoreCase).ThenBy((string result) => result, StringComparer.Ordinal)) { if (!TryReadFile(item2, out Dictionary translations)) { continue; } foreach (KeyValuePair item3 in translations) { if (dictionary3.TryGetValue(item3.Key, out var value)) { PortalRulesPlugin.PortalRulesLogger.LogWarning((object)("Duplicate PortalRules translation token '" + item3.Key + "' for language '" + item.Key + "' in '" + value + "' and '" + item2 + "'; the later ordinal path wins.")); } dictionary2[item3.Key] = item3.Value; dictionary3[item3.Key] = item2; } } if (dictionary2.Count > 0) { dictionary[item.Key] = dictionary2; } } return dictionary; } private static Dictionary> DiscoverExternalFiles() { Dictionary> dictionary = new Dictionary>(StringComparer.OrdinalIgnoreCase); string bepInExRootPath = Paths.BepInExRootPath; if (string.IsNullOrWhiteSpace(bepInExRootPath) || !Directory.Exists(bepInExRootPath)) { return dictionary; } Stack stack = new Stack(); HashSet hashSet = new HashSet(FileSystemPathComparer); stack.Push(Path.GetFullPath(bepInExRootPath)); while (stack.Count > 0) { string text = stack.Pop(); if (!hashSet.Add(text)) { continue; } string[] array; try { array = Directory.GetFiles(text); } catch (Exception ex) { PortalRulesPlugin.PortalRulesLogger.LogWarning((object)("Could not inspect PortalRules translation directory '" + text + "': " + ex.Message)); array = Array.Empty(); } string[] array2 = array; foreach (string path in array2) { if (TryGetExternalLanguage(path, out string language)) { if (!dictionary.TryGetValue(language, out var value)) { value = (dictionary[language] = new List()); } value.Add(Path.GetFullPath(path)); } } string[] directories; try { directories = Directory.GetDirectories(text); } catch (Exception ex2) { PortalRulesPlugin.PortalRulesLogger.LogWarning((object)("Could not inspect child directories below '" + text + "': " + ex2.Message)); continue; } Array.Sort(directories, DeterministicPathComparer); for (int num = directories.Length - 1; num >= 0; num--) { string text2 = directories[num]; try { if ((File.GetAttributes(text2) & FileAttributes.ReparsePoint) == 0) { stack.Push(Path.GetFullPath(text2)); } } catch (Exception ex3) { PortalRulesPlugin.PortalRulesLogger.LogWarning((object)("Could not inspect PortalRules translation path '" + text2 + "': " + ex3.Message)); } } } return dictionary; } private static bool TryGetExternalLanguage(string path, out string language) { language = string.Empty; string extension = Path.GetExtension(path); if (!string.Equals(extension, ".yml", StringComparison.OrdinalIgnoreCase) && !string.Equals(extension, ".yaml", StringComparison.OrdinalIgnoreCase) && !string.Equals(extension, ".json", StringComparison.OrdinalIgnoreCase)) { return false; } string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(path); if (!fileNameWithoutExtension.StartsWith("PortalRules.", StringComparison.OrdinalIgnoreCase)) { return false; } language = fileNameWithoutExtension.Substring("PortalRules.".Length); if (string.IsNullOrWhiteSpace(language) || language.Length != language.Trim().Length || language.IndexOf('.') >= 0) { language = string.Empty; return false; } return true; } private static bool TryReadEmbedded(string language, out Dictionary translations) { translations = new Dictionary(StringComparer.Ordinal); string text = "PortalRules.translations." + language + ".yml"; try { using Stream stream = typeof(PortalRulesLocalization).Assembly.GetManifestResourceStream(text); if (stream == null) { PortalRulesPlugin.PortalRulesLogger.LogWarning((object)("Embedded PortalRules translation '" + text + "' was not found.")); return false; } using StreamReader streamReader = new StreamReader(stream, Encoding.UTF8, detectEncodingFromByteOrderMarks: true); return TryParseTranslations(streamReader.ReadToEnd(), text, out translations); } catch (Exception ex) { PortalRulesPlugin.PortalRulesLogger.LogWarning((object)("Could not read embedded PortalRules translation '" + text + "': " + ex.Message)); return false; } } private static bool TryReadFile(string path, out Dictionary translations) { translations = new Dictionary(StringComparer.Ordinal); try { long length = new FileInfo(path).Length; if (length > 1048576) { PortalRulesPlugin.PortalRulesLogger.LogWarning((object)($"PortalRules translation '{path}' is {length} bytes and exceeds the " + $"{1048576L}-byte limit; the file was ignored.")); return false; } return TryParseTranslations(File.ReadAllText(path), path, out translations); } catch (Exception ex) { PortalRulesPlugin.PortalRulesLogger.LogWarning((object)("Could not read PortalRules translation '" + path + "': " + ex.Message)); return false; } } private static bool TryParseTranslations(string content, string source, out Dictionary translations) { translations = new Dictionary(StringComparer.Ordinal); if (string.IsNullOrWhiteSpace(content)) { PortalRulesPlugin.PortalRulesLogger.LogWarning((object)("PortalRules translation '" + source + "' is empty and was ignored.")); return false; } try { YamlStream yamlStream = new YamlStream(); using StringReader input = new StringReader(content); yamlStream.Load(input); if (yamlStream.Documents.Count != 1 || !(yamlStream.Documents[0].RootNode is YamlMappingNode yamlMappingNode)) { PortalRulesPlugin.PortalRulesLogger.LogWarning((object)("PortalRules translation '" + source + "' must contain exactly one mapping document.")); return false; } foreach (KeyValuePair child in yamlMappingNode.Children) { if (!(child.Key is YamlScalarNode yamlScalarNode) || string.IsNullOrWhiteSpace(yamlScalarNode.Value)) { PortalRulesPlugin.PortalRulesLogger.LogWarning((object)("PortalRules translation '" + source + "' contains an empty or non-scalar token and was ignored.")); continue; } string text = NormalizeToken(yamlScalarNode.Value); if (!text.StartsWith("sighsorry_portalrules_", StringComparison.Ordinal)) { PortalRulesPlugin.PortalRulesLogger.LogWarning((object)("Translation token '" + yamlScalarNode.Value + "' in '" + source + "' is not owned by PortalRules and was ignored.")); continue; } if (!(child.Value is YamlScalarNode yamlScalarNode2) || string.IsNullOrWhiteSpace(yamlScalarNode2.Value)) { PortalRulesPlugin.PortalRulesLogger.LogWarning((object)("PortalRules translation token '" + text + "' in '" + source + "' has no scalar text and was ignored.")); continue; } if (translations.ContainsKey(text)) { PortalRulesPlugin.PortalRulesLogger.LogWarning((object)("Duplicate PortalRules translation token '" + text + "' inside '" + source + "'; the later value wins.")); } translations[text] = yamlScalarNode2.Value; } return translations.Count > 0; } catch (Exception ex) { PortalRulesPlugin.PortalRulesLogger.LogWarning((object)("Could not parse PortalRules translation '" + source + "': " + ex.Message)); translations.Clear(); return false; } } private static string NormalizeToken(string? token) { string text = (token ?? string.Empty).Trim(); if (!text.StartsWith("$", StringComparison.Ordinal)) { return text; } return text.Substring(1); } private static string TranslateVanilla(string originalToken, string normalizedToken, string[] args) { Localization instance = Localization.instance; if (instance == null) { return originalToken; } string text = (originalToken.StartsWith("$", StringComparison.Ordinal) ? originalToken : ("$" + normalizedToken)); try { return instance.Localize(text, args); } catch (Exception ex) { PortalRulesPlugin.PortalRulesLogger.LogWarning((object)("Could not delegate localization token '" + text + "' to Valheim: " + ex.Message)); return originalToken; } } private static void RegisterActiveTranslations(Localization? localization, IReadOnlyDictionary translations) { if (localization == null) { return; } MethodInfo methodInfo = ResolveAddWord(); if (methodInfo == null) { LogAddWordFailureOnce("Localization.AddWord(string, string) could not be resolved; PortalRules.Translate remains available."); return; } foreach (KeyValuePair translation in translations) { try { methodInfo.Invoke(localization, new object[2] { translation.Key, translation.Value }); } catch (Exception ex) { PortalRulesPlugin.PortalRulesLogger.LogWarning((object)("Could not register PortalRules localization token '" + translation.Key + "' through Localization.AddWord: " + ex.GetBaseException().Message)); } } } private static MethodInfo? ResolveAddWord() { lock (Sync) { if (_addWordResolved) { return _addWordMethod; } _addWordResolved = true; try { _addWordMethod = AccessTools.DeclaredMethod(typeof(Localization), "AddWord", new Type[2] { typeof(string), typeof(string) }, (Type[])null); } catch (Exception ex) { LogAddWordFailureOnce("Localization.AddWord binding failed: " + ex.Message); } return _addWordMethod; } } private static void LogAddWordFailureOnce(string message) { lock (Sync) { if (_addWordFailureLogged) { return; } _addWordFailureLogged = true; } PortalRulesPlugin.PortalRulesLogger.LogWarning((object)message); } private static void Merge(IDictionary destination, IReadOnlyDictionary source) { foreach (KeyValuePair item in source) { destination[item.Key] = item.Value; } } private static void ResetToBuiltInEnglish() { lock (Sync) { _english = new Dictionary(BuiltInEnglish, StringComparer.Ordinal); _safeEnglish = new Dictionary(BuiltInEnglish, StringComparer.Ordinal); _embeddedKorean = new Dictionary(StringComparer.Ordinal); _active = new Dictionary(BuiltInEnglish, StringComparer.Ordinal); _activeLanguage = "English"; ExternalTranslations.Clear(); } } private static void LogFormatFailureOnce(string language, string token, FormatException exception) { string item = language + "\n" + token; lock (Sync) { if (!LoggedFormatFailures.Add(item)) { return; } } PortalRulesPlugin.PortalRulesLogger.LogWarning((object)("Invalid format string for PortalRules token '" + token + "' in language '" + language + "'; using the English fallback: " + exception.Message)); } } [HarmonyPatch(typeof(Localization), "SetupLanguage")] internal static class PortalRulesLocalizationSetupLanguagePatch { private static void Postfix(Localization __instance, string language) { try { PortalRulesLocalization.ApplyLanguage(__instance, language); } catch (Exception ex) { PortalRulesPlugin.PortalRulesLogger.LogWarning((object)("Could not apply PortalRules translations after a language change: " + ex.Message)); } } } [HarmonyPatch(typeof(FejdStartup), "SetupGui")] internal static class PortalRulesLocalizationSetupGuiPatch { private static void Postfix() { try { PortalRulesLocalization.ApplyCurrentLanguage(); } catch (Exception ex) { PortalRulesPlugin.PortalRulesLogger.LogWarning((object)("Could not apply PortalRules translations while setting up the GUI: " + ex.Message)); } } } internal readonly struct PortalRulesMessage { private const string TokenPrefix = "$sighsorry_portalrules_"; private const int MaximumTokenLength = 128; private const int MaximumArgumentCount = 8; private const int MaximumArgumentLength = 512; internal static readonly PortalRulesMessage Empty = new PortalRulesMessage(""); internal readonly string Token; internal readonly string[] Arguments; internal bool IsEmpty => Token.Length == 0; internal PortalRulesMessage(string token, params string[] arguments) { Token = NormalizeToken(token); Arguments = SanitizeArguments(arguments); } internal string Localize() { if (IsEmpty) { return string.Empty; } string[] array = new string[Arguments.Length]; for (int i = 0; i < Arguments.Length; i++) { string text = Arguments[i]; array[i] = (IsNestedAccessModeToken(text) ? PortalRulesLocalization.Translate(text) : StringExtensionMethods.RemoveRichTextTags(text)); } return PortalRulesLocalization.Translate(Token, array); } internal void Write(ZPackage package) { package.Write(Token); package.Write(Arguments.Length); string[] arguments = Arguments; foreach (string text in arguments) { package.Write(text); } } internal static bool TryRead(ZPackage package, out PortalRulesMessage message) { message = Empty; string text = package.ReadString(); int num = package.ReadInt(); if (text.Length == 0 && num == 0) { message = Empty; return true; } if (!IsValidToken(text) || num < 0 || num > 8) { return false; } string[] array = new string[num]; for (int i = 0; i < num; i++) { string text2 = package.ReadString(); if (!IsValidArgument(text2)) { return false; } array[i] = text2; } message = new PortalRulesMessage(text, array); return true; } private static string NormalizeToken(string? token) { string text = (token ?? string.Empty).Trim(); if (text.Length == 0) { return string.Empty; } if (!text.StartsWith("$", StringComparison.Ordinal)) { text = "$" + text; } if (!IsValidToken(text)) { return string.Empty; } return text; } private static string[] SanitizeArguments(string[]? arguments) { if (arguments == null || arguments.Length == 0) { return Array.Empty(); } int num = Math.Min(arguments.Length, 8); string[] array = new string[num]; for (int i = 0; i < num; i++) { string text = arguments[i] ?? string.Empty; if (text.Length > 512) { text = text.Substring(0, 512); } array[i] = RemoveControlCharacters(text); } return array; } private static bool IsValidToken(string token) { if (token.Length <= "$sighsorry_portalrules_".Length || token.Length > 128 || !token.StartsWith("$sighsorry_portalrules_", StringComparison.Ordinal)) { return false; } for (int i = "$sighsorry_portalrules_".Length; i < token.Length; i++) { char c = token[i]; switch (c) { case '_': case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x': case 'y': case 'z': continue; } if (c < '0' || c > '9') { return false; } } return true; } private static bool IsValidArgument(string argument) { if (argument.Length <= 512) { return argument.IndexOfAny(new char[3] { '\r', '\n', '\0' }) < 0; } return false; } private static bool IsNestedAccessModeToken(string token) { switch (token) { case "$sighsorry_portalrules_access_mode_personal": case "$sighsorry_portalrules_access_mode_admin": case "$sighsorry_portalrules_access_mode_public": case "$sighsorry_portalrules_access_mode_invite": case "$sighsorry_portalrules_access_mode_clan": case "$sighsorry_portalrules_access_mode_tagged": return true; default: return false; } } private static string RemoveControlCharacters(string value) { char[] array = null; int num = 0; foreach (char c in value) { if (char.IsControl(c)) { if (array == null) { array = value.ToCharArray(); } continue; } if (array != null) { array[num] = c; } num++; } if (array != null) { return new string(array, 0, num); } return value; } } internal static class PublicPortalConfig { public static ConfigEntry LimitToVanillaPortals; public static ConfigEntry EnablePortalMap; public static ConfigEntry ToggleAccessiblePortalsKey; public static ConfigEntry AutoCloseGraceSeconds; public static ConfigEntry PortalMapWheelZoomMultiplier; public static ConfigEntry FavoritePortalListCollapsed; public static ConfigEntry ToggleAccessKey; public static ConfigEntry PublicAccessDurationSeconds; public static ConfigEntry MaxInvitePortalsPerAccount; public static ConfigEntry InviteDepartureCooldownHours; public static ConfigEntry InviteArrivalCooldownHours; public static ConfigEntry MaxClanPortalsPerClan; public static ConfigEntry EnableAccountPortalLimit; public static ConfigEntry MaxPortalsPerAccount; public static ConfigEntry CountedPortalPrefabs; public static ConfigEntry TravelCostScope; public static ConfigEntry BaseCoinCost; public static ConfigEntry BaseFareIncludedDistanceMeters; public static ConfigEntry CoinsPerKilometer; public static void Init(PortalRulesPlugin plugin) { //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Expected O, but got Unknown //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Expected O, but got Unknown //IL_01c2: Unknown result type (might be due to invalid IL or missing references) //IL_0219: Unknown result type (might be due to invalid IL or missing references) //IL_0241: Expected O, but got Unknown //IL_0290: Unknown result type (might be due to invalid IL or missing references) //IL_02b8: Expected O, but got Unknown //IL_02e6: Unknown result type (might be due to invalid IL or missing references) //IL_030e: Expected O, but got Unknown //IL_033c: Unknown result type (might be due to invalid IL or missing references) //IL_0364: Expected O, but got Unknown //IL_03c0: Unknown result type (might be due to invalid IL or missing references) //IL_03e5: Expected O, but got Unknown //IL_0445: Unknown result type (might be due to invalid IL or missing references) //IL_046d: Expected O, but got Unknown //IL_055b: Unknown result type (might be due to invalid IL or missing references) //IL_0580: Expected O, but got Unknown //IL_05ae: Unknown result type (might be due to invalid IL or missing references) //IL_05d3: Expected O, but got Unknown //IL_0601: Unknown result type (might be due to invalid IL or missing references) //IL_0623: Expected O, but got Unknown LimitToVanillaPortals = plugin.ConfigEntry("1 - General", "Limit To Vanilla Portals", PortalRulesPlugin.Toggle.Off, "If on, normal portal handling is limited to vanilla wood and stone portal prefabs. PortalRules Admin Portal prefabs remain handled.", synchronizedSetting: true, 100, 500); EnablePortalMap = plugin.ConfigEntry("2 - Portal Map", "Enable Portal Map", PortalRulesPlugin.Toggle.On, "If on, entering a portal opens a portal target map.", synchronizedSetting: true, 400, 400); ToggleAccessiblePortalsKey = plugin.ConfigEntry("2 - Portal Map", "Toggle Accessible Portal Pins Key", new KeyboardShortcut((KeyCode)112, Array.Empty()), "Keyboard shortcut used while the large map is open to show or hide accessible portal pins.", synchronizedSetting: false, 300, 400); AutoCloseGraceSeconds = plugin.ConfigEntry("2 - Portal Map", "Auto Close Grace Seconds", 0.5f, new ConfigDescription("Seconds to wait after leaving the source portal area before closing the portal target map. Set to 0 to disable automatic closing.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 2f), Array.Empty()), synchronizedSetting: false, 200, 400); PortalMapWheelZoomMultiplier = plugin.ConfigEntry("2 - Portal Map", "Portal Map Wheel Zoom Multiplier", 3, new ConfigDescription("Multiplier applied to mouse-wheel zoom only while the portal destination map is active. 1 uses Valheim's normal zoom rate.", (AcceptableValueBase)(object)new AcceptableValueRange(1, 10), Array.Empty()), synchronizedSetting: false, 100, 400); FavoritePortalListCollapsed = plugin.ConfigEntry("2 - Portal Map", "Favorite Portal List Collapsed", PortalRulesPlugin.Toggle.Off, "Stores whether the Favorite Portals list is collapsed.", synchronizedSetting: false, 50, 400, false); FavoritePortalListCollapsed.SettingChanged += delegate { PublicPortalMapController.Instance.RefreshFavoritePanelFromPreference(); }; ToggleAccessKey = plugin.ConfigEntry("3 - Access Modes", "Portal Access Modifier Key", new KeyboardShortcut((KeyCode)304, Array.Empty()), "Modifier key held while interacting with a portal to cycle its access mode.", synchronizedSetting: false, 600, 300); PublicAccessDurationSeconds = plugin.ConfigEntry("3 - Access Modes", "Public Access Duration Seconds", 900, new ConfigDescription("Seconds before a player-built Public portal returns to its authenticated Builder's Personal access. 0 disables automatic reversion.", (AcceptableValueBase)(object)new AcceptableValueRange(0, 604800), Array.Empty()), synchronizedSetting: true, 500, 300); PublicAccessDurationSeconds.SettingChanged += delegate { PublicPortalCatalog.RefreshTemporaryPublicConfiguration(); }; MaxInvitePortalsPerAccount = plugin.ConfigEntry("3 - Access Modes", "Max Invite Portals Per Account", 1, new ConfigDescription("Maximum player-built portals one authenticated Steam account may keep in Invite mode. -1 is unlimited. An effective value of 0 disables Invite mode and returns existing eligible Invite portals to their immutable Builder's Personal access.", (AcceptableValueBase)(object)new AcceptableValueRange(-1, 10000), Array.Empty()), synchronizedSetting: true, 400, 300); InviteDepartureCooldownHours = plugin.ConfigEntry("3 - Access Modes", "Invite Departure Cooldown Hours", 1f, new ConfigDescription("Per-Steam-account, per-Invite-portal cooldown after using that portal as the source. 0 disables the departure cooldown.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 8760f), Array.Empty()), synchronizedSetting: true, 300, 300); InviteArrivalCooldownHours = plugin.ConfigEntry("3 - Access Modes", "Invite Arrival Cooldown Hours", 1f, new ConfigDescription("Per-Steam-account, per-Invite-portal cooldown after using that portal as the destination. 0 disables the arrival cooldown.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 8760f), Array.Empty()), synchronizedSetting: true, 200, 300); EventHandler eventHandler = delegate { PublicPortalCatalog.RefreshInviteCooldownConfiguration(); }; InviteDepartureCooldownHours.SettingChanged += eventHandler; InviteArrivalCooldownHours.SettingChanged += eventHandler; MaxClanPortalsPerClan = plugin.ConfigEntry("3 - Access Modes", "Max Clan Portals Per Clan", 5, new ConfigDescription("Maximum player-built portals that may be assigned to one Clan. -1 is unlimited and 0 disables entering Clan mode.", (AcceptableValueBase)(object)new AcceptableValueRange(-1, 10000), Array.Empty()), synchronizedSetting: true, 100, 300); EnableAccountPortalLimit = plugin.ConfigEntry("4 - Account Portal Limit", "Enable Account Portal Limit", PortalRulesPlugin.Toggle.On, "If on, the server limits player-built portals by authenticated SteamID64, across all characters on that Steam account. If off, the account limit and every portal_limit override are bypassed; Invite and Clan limits remain active.", synchronizedSetting: true, 300, 200); MaxPortalsPerAccount = plugin.ConfigEntry("4 - Account Portal Limit", "Max Portals Per Account", 10, new ConfigDescription("Maximum counted portals an authenticated account may have in this world. -1 makes the global default unlimited while portal_limit overrides remain active; 0 blocks new counted portals.", (AcceptableValueBase)(object)new AcceptableValueRange(-1, 10000), Array.Empty()), synchronizedSetting: true, 200, 200); CountedPortalPrefabs = plugin.ConfigEntry("4 - Account Portal Limit", "Counted Portal Prefabs", "portal_wood,portal,portal_stone", "Comma-separated prefab names counted by the account limit. Admin portal prefabs are always excluded.", synchronizedSetting: true, 100, 200); EventHandler eventHandler2 = delegate { PublicPortalServerPolicy.NotifyQuotaConfigurationChanged(); }; EnableAccountPortalLimit.SettingChanged += eventHandler2; MaxPortalsPerAccount.SettingChanged += eventHandler2; MaxInvitePortalsPerAccount.SettingChanged += eventHandler2; MaxClanPortalsPerClan.SettingChanged += eventHandler2; CountedPortalPrefabs.SettingChanged += eventHandler2; TravelCostScope = plugin.ConfigEntry("5 - Portal Travel Costs", "Travel Cost Scope", PublicPortalTravelCostScope.Off, "Off disables Coins costs. All charges every handled portal trip. AdminPortalTrips charges trips where either endpoint is an Admin Portal prefab. PersonalAndClanRoutesFree makes a trip free only when both endpoints are Personal or Clan. AllItemsSourceTrips charges only when the source portal allows every item.", synchronizedSetting: true, 400, 100); BaseCoinCost = plugin.ConfigEntry("5 - Portal Travel Costs", "Base Coin Cost", 10, new ConfigDescription("Coins charged for a paid portal trip before any distance surcharge.", (AcceptableValueBase)(object)new AcceptableValueRange(0, 1000000), Array.Empty()), synchronizedSetting: true, 300, 100); BaseFareIncludedDistanceMeters = plugin.ConfigEntry("5 - Portal Travel Costs", "Base Fare Included Distance Meters", 1000f, new ConfigDescription("XZ distance covered by the base fare before the per-kilometer surcharge begins.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1000000f), Array.Empty()), synchronizedSetting: true, 200, 100); CoinsPerKilometer = plugin.ConfigEntry("5 - Portal Travel Costs", "Coins Per Kilometer", 5f, new ConfigDescription("Additional Coins per kilometer beyond the distance included in the base fare. The surcharge is rounded up.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1000000f), Array.Empty()), synchronizedSetting: true, 100, 100); } } [HarmonyPatch] internal static class PublicPortalConnectedHud { [HarmonyPatch(typeof(Hud), "UpdateCrosshair")] private static class ConnectedTravelCostHoverPatch { private static void Postfix(Hud __instance, Player player) { if (!TryGetConnectedTravelInfo(player, out var travelCost, out var allowsAllItems) || !EnsureConnectedTravelBadge(__instance)) { HideConnectedTravelBadge(); return; } UpdateConnectedTravelBadge(travelCost, allowsAllItems); PositionConnectedTravelBadge(__instance); } } private static readonly Color UnaffordableTravelColor = new Color(1f, 0.42f, 0.32f); private static readonly Color AllItemsTravelColor = new Color(0.72f, 0.92f, 0.72f); private static Hud? ConnectedTravelHud; private static GameObject? ConnectedTravelBadge; private static Image? ConnectedTravelCoinIcon; private static TextMeshProUGUI? ConnectedTravelCostText; private static TextMeshProUGUI? ConnectedTravelAllItemsText; private static string ConnectedTravelLayoutText = ""; private static Vector2 ConnectedTravelLayoutSize = new Vector2(float.NaN, float.NaN); private static float ConnectedTravelLayoutFontSize = float.NaN; internal static void Shutdown() { //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ConnectedTravelBadge != (Object)null) { Object.Destroy((Object)(object)ConnectedTravelBadge); } ConnectedTravelHud = null; ConnectedTravelBadge = null; ConnectedTravelCoinIcon = null; ConnectedTravelCostText = null; ConnectedTravelAllItemsText = null; ConnectedTravelLayoutText = ""; ConnectedTravelLayoutSize = new Vector2(float.NaN, float.NaN); ConnectedTravelLayoutFontSize = float.NaN; } private static bool TryGetConnectedTravelInfo(Player player, out int travelCost, out bool allowsAllItems) { //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) travelCost = 0; allowsAllItems = false; if ((Object)(object)player == (Object)null) { return false; } GameObject hoverObject = ((Humanoid)player).GetHoverObject(); TeleportWorld val = (((Object)(object)hoverObject != (Object)null) ? hoverObject.GetComponentInParent() : null); if ((Object)(object)val == (Object)null || !PublicPortalKinds.IsHandledPortal(val)) { return false; } ZDO portalZdo = PublicPortalKinds.GetPortalZdo(val); if (portalZdo == null || (PublicPortalConfig.EnablePortalMap.Value.IsOn() && PublicPortalCatalog.GetEffectiveAccessMode(portalZdo) != PublicPortalAccessMode.Tagged) || !PublicPortalCatalog.TryGetClientEntry(portalZdo.m_uid, out var entry)) { return false; } ZDOID connectionZDOID = portalZdo.GetConnectionZDOID((ConnectionType)1); if (((ZDOID)(ref connectionZDOID)).IsNone() || connectionZDOID == portalZdo.m_uid || !PublicPortalCatalog.TryGetClientEntry(connectionZDOID, out var entry2) || (PublicPortalConfig.EnablePortalMap.Value.IsOn() && entry2.AccessMode != PublicPortalAccessMode.Tagged) || !PublicPortalAccess.CanUsePortal(entry) || !PublicPortalAccess.CanUsePortal(entry2)) { return false; } travelCost = PublicPortalTravelCost.CalculateCost(entry, entry2); allowsAllItems = val.m_allowAllItems; return (travelCost > 0) | allowsAllItems; } private static bool EnsureConnectedTravelBadge(Hud hud) { //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Expected O, but got Unknown //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0127: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_013c: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) //IL_017f: Unknown result type (might be due to invalid IL or missing references) //IL_0184: Unknown result type (might be due to invalid IL or missing references) //IL_0190: Unknown result type (might be due to invalid IL or missing references) //IL_01a2: Unknown result type (might be due to invalid IL or missing references) //IL_01a8: Unknown result type (might be due to invalid IL or missing references) //IL_01ad: Unknown result type (might be due to invalid IL or missing references) //IL_01b8: Unknown result type (might be due to invalid IL or missing references) //IL_01c2: Unknown result type (might be due to invalid IL or missing references) //IL_01cd: Unknown result type (might be due to invalid IL or missing references) //IL_01d7: Unknown result type (might be due to invalid IL or missing references) //IL_01e2: Unknown result type (might be due to invalid IL or missing references) //IL_01ec: Unknown result type (might be due to invalid IL or missing references) //IL_01ed: Unknown result type (might be due to invalid IL or missing references) //IL_0201: Unknown result type (might be due to invalid IL or missing references) //IL_024b: Unknown result type (might be due to invalid IL or missing references) //IL_0250: Unknown result type (might be due to invalid IL or missing references) //IL_025c: Unknown result type (might be due to invalid IL or missing references) //IL_026e: Unknown result type (might be due to invalid IL or missing references) //IL_0274: Unknown result type (might be due to invalid IL or missing references) //IL_0279: Unknown result type (might be due to invalid IL or missing references) //IL_027a: Unknown result type (might be due to invalid IL or missing references) //IL_0284: Unknown result type (might be due to invalid IL or missing references) //IL_0285: Unknown result type (might be due to invalid IL or missing references) //IL_028f: Unknown result type (might be due to invalid IL or missing references) //IL_029a: Unknown result type (might be due to invalid IL or missing references) //IL_02a4: Unknown result type (might be due to invalid IL or missing references) //IL_0343: Unknown result type (might be due to invalid IL or missing references) //IL_0348: Unknown result type (might be due to invalid IL or missing references) //IL_0354: Unknown result type (might be due to invalid IL or missing references) //IL_03cf: Unknown result type (might be due to invalid IL or missing references) //IL_042d: Unknown result type (might be due to invalid IL or missing references) //IL_0432: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)hud == (Object)null || (Object)(object)hud.m_hoverName == (Object)null) { return false; } if ((Object)(object)ConnectedTravelHud == (Object)(object)hud && (Object)(object)ConnectedTravelBadge != (Object)null && (Object)(object)ConnectedTravelCoinIcon != (Object)null && (Object)(object)ConnectedTravelCostText != (Object)null && (Object)(object)ConnectedTravelAllItemsText != (Object)null) { if ((Object)(object)ConnectedTravelCoinIcon.sprite == (Object)null) { ConnectedTravelCoinIcon.sprite = PublicPortalTravelCost.GetCoinIcon(); } return true; } if ((Object)(object)ConnectedTravelBadge != (Object)null) { Object.Destroy((Object)(object)ConnectedTravelBadge); } Sprite coinIcon = PublicPortalTravelCost.GetCoinIcon(); GameObject val = new GameObject("PortalRulesConnectedTravelInfo", new Type[1] { typeof(RectTransform) }); val.layer = ((Component)hud.m_hoverName).gameObject.layer; val.transform.SetParent(((TMP_Text)hud.m_hoverName).transform, false); RectTransform val2 = (RectTransform)val.transform; val2.anchorMin = ((TMP_Text)hud.m_hoverName).rectTransform.pivot; val2.anchorMax = ((TMP_Text)hud.m_hoverName).rectTransform.pivot; val2.pivot = new Vector2(0f, 0.5f); val2.anchoredPosition = new Vector2(6f, 0f); val2.sizeDelta = new Vector2(0f, 22f); GameObject val3 = new GameObject("CoinsIcon", new Type[2] { typeof(RectTransform), typeof(Image) }) { layer = val.layer }; val3.transform.SetParent(val.transform, false); RectTransform val4 = (RectTransform)val3.transform; val4.anchorMin = new Vector2(0f, 0.5f); val4.anchorMax = new Vector2(0f, 0.5f); val4.pivot = new Vector2(0f, 0.5f); val4.anchoredPosition = Vector2.zero; val4.sizeDelta = new Vector2(20f, 20f); Image component = val3.GetComponent(); component.sprite = coinIcon; component.preserveAspect = true; ((Graphic)component).raycastTarget = false; GameObject val5 = new GameObject("Count", new Type[2] { typeof(RectTransform), typeof(TextMeshProUGUI) }) { layer = val.layer }; val5.transform.SetParent(val.transform, false); RectTransform val6 = (RectTransform)val5.transform; val6.anchorMin = Vector2.zero; val6.anchorMax = Vector2.one; val6.offsetMin = new Vector2(24f, 0f); val6.offsetMax = Vector2.zero; TextMeshProUGUI component2 = val5.GetComponent(); ((TMP_Text)component2).font = ((TMP_Text)hud.m_hoverName).font; ((TMP_Text)component2).fontSharedMaterial = ((TMP_Text)hud.m_hoverName).fontSharedMaterial; ((TMP_Text)component2).fontSize = Mathf.Max(14f, ((TMP_Text)hud.m_hoverName).fontSize * 0.75f); ((TMP_Text)component2).fontStyle = (FontStyles)1; ((TMP_Text)component2).alignment = (TextAlignmentOptions)513; ((TMP_Text)component2).textWrappingMode = (TextWrappingModes)0; ((TMP_Text)component2).overflowMode = (TextOverflowModes)0; ((Graphic)component2).raycastTarget = false; GameObject val7 = new GameObject("AllItems", new Type[2] { typeof(RectTransform), typeof(TextMeshProUGUI) }) { layer = val.layer }; val7.transform.SetParent(val.transform, false); TextMeshProUGUI component3 = val7.GetComponent(); ((TMP_Text)component3).font = ((TMP_Text)hud.m_hoverName).font; ((TMP_Text)component3).fontSharedMaterial = ((TMP_Text)hud.m_hoverName).fontSharedMaterial; ((TMP_Text)component3).fontSize = Mathf.Max(14f, ((TMP_Text)hud.m_hoverName).fontSize * 0.75f); ((TMP_Text)component3).fontStyle = (FontStyles)1; ((TMP_Text)component3).alignment = (TextAlignmentOptions)513; ((TMP_Text)component3).textWrappingMode = (TextWrappingModes)0; ((TMP_Text)component3).overflowMode = (TextOverflowModes)0; ((Graphic)component3).color = AllItemsTravelColor; ((TMP_Text)component3).text = PortalRulesLocalization.Translate("$sighsorry_portalrules_all_items"); ((Graphic)component3).raycastTarget = false; val.SetActive(false); ConnectedTravelHud = hud; ConnectedTravelBadge = val; ConnectedTravelCoinIcon = component; ConnectedTravelCostText = component2; ConnectedTravelAllItemsText = component3; ConnectedTravelLayoutText = ""; ConnectedTravelLayoutSize = new Vector2(float.NaN, float.NaN); ConnectedTravelLayoutFontSize = float.NaN; return true; } private static void UpdateConnectedTravelBadge(int travelCost, bool allowsAllItems) { //IL_01ca: Unknown result type (might be due to invalid IL or missing references) //IL_01d5: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ConnectedTravelBadge == (Object)null || (Object)(object)ConnectedTravelCoinIcon == (Object)null || (Object)(object)ConnectedTravelCostText == (Object)null || (Object)(object)ConnectedTravelAllItemsText == (Object)null) { return; } float num = 0f; bool flag = travelCost > 0; Sprite val = (flag ? (ConnectedTravelCoinIcon.sprite ?? PublicPortalTravelCost.GetCoinIcon()) : null); bool flag2 = (Object)(object)val != (Object)null; ((Component)ConnectedTravelCoinIcon).gameObject.SetActive(flag2); if (flag2) { ConnectedTravelCoinIcon.sprite = val; SetConnectedTravelChildRect(((Graphic)ConnectedTravelCoinIcon).rectTransform, num, 20f); num += 24f; } ((Component)ConnectedTravelCostText).gameObject.SetActive(flag); if (flag) { ((TMP_Text)ConnectedTravelCostText).text = (flag2 ? PortalRulesLocalization.Translate("$sighsorry_portalrules_quantity", travelCost.ToString()) : PortalRulesLocalization.Translate("$sighsorry_portalrules_coins_quantity", travelCost.ToString())); ((Graphic)ConnectedTravelCostText).color = ((PublicPortalTravelCost.GetLocalCoinCount() >= travelCost) ? Color.white : UnaffordableTravelColor); float num2 = Mathf.Ceil(((TMP_Text)ConnectedTravelCostText).preferredWidth) + 2f; SetConnectedTravelChildRect(((TMP_Text)ConnectedTravelCostText).rectTransform, num, num2); num += num2; } ((Component)ConnectedTravelAllItemsText).gameObject.SetActive(allowsAllItems); if (allowsAllItems) { if (num > 0f) { num += 8f; } ((TMP_Text)ConnectedTravelAllItemsText).text = (flag ? PortalRulesLocalization.Translate("$sighsorry_portalrules_all_items_bulleted") : PortalRulesLocalization.Translate("$sighsorry_portalrules_all_items")); float num3 = Mathf.Ceil(((TMP_Text)ConnectedTravelAllItemsText).preferredWidth) + 2f; SetConnectedTravelChildRect(((TMP_Text)ConnectedTravelAllItemsText).rectTransform, num, num3); num += num3; } ((RectTransform)ConnectedTravelBadge.transform).sizeDelta = new Vector2(num, 22f); if (!ConnectedTravelBadge.activeSelf) { ConnectedTravelBadge.SetActive(true); } } private static void SetConnectedTravelChildRect(RectTransform rect, float x, float width) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: 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_0046: 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) rect.anchorMin = new Vector2(0f, 0.5f); rect.anchorMax = new Vector2(0f, 0.5f); rect.pivot = new Vector2(0f, 0.5f); rect.anchoredPosition = new Vector2(x, 0f); rect.sizeDelta = new Vector2(width, 20f); } private static void PositionConnectedTravelBadge(Hud hud) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Expected O, but got Unknown //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_0180: Unknown result type (might be due to invalid IL or missing references) //IL_0190: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ConnectedTravelBadge == (Object)null || (Object)(object)hud.m_hoverName == (Object)null) { return; } RectTransform rectTransform = ((TMP_Text)hud.m_hoverName).rectTransform; Rect rect = rectTransform.rect; Vector2 size = ((Rect)(ref rect)).size; string text = ((TMP_Text)hud.m_hoverName).text ?? ""; float fontSize = ((TMP_Text)hud.m_hoverName).fontSize; if (string.Equals(ConnectedTravelLayoutText, text, StringComparison.Ordinal) && ConnectedTravelLayoutSize == size && ConnectedTravelLayoutFontSize.Equals(fontSize)) { return; } ConnectedTravelLayoutText = text; ConnectedTravelLayoutSize = size; ConnectedTravelLayoutFontSize = fontSize; RectTransform val = (RectTransform)ConnectedTravelBadge.transform; try { ((TMP_Text)hud.m_hoverName).ForceMeshUpdate(false, false); TMP_TextInfo textInfo = ((TMP_Text)hud.m_hoverName).textInfo; if (textInfo == null || textInfo.lineCount <= 0 || textInfo.lineInfo[0].characterCount <= 0) { SetConnectedTravelFallbackPosition(val); return; } TMP_LineInfo val2 = textInfo.lineInfo[0]; float num = val2.lineExtents.max.x + 6f; float num2 = (val2.ascender + val2.descender) * 0.5f; if (float.IsNaN(num) || float.IsInfinity(num) || float.IsNaN(num2) || float.IsInfinity(num2)) { SetConnectedTravelFallbackPosition(val); return; } val.anchorMin = rectTransform.pivot; val.anchorMax = rectTransform.pivot; val.pivot = new Vector2(0f, 0.5f); val.anchoredPosition = new Vector2(num, num2); } catch (Exception ex) { SetConnectedTravelFallbackPosition(val); PortalRulesPlugin.PortalRulesLogger.LogDebug((object)("Could not align the connected portal fare with the first hover line: " + ex.Message)); } } private static void SetConnectedTravelFallbackPosition(RectTransform badgeRect) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: 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_004a: Unknown result type (might be due to invalid IL or missing references) badgeRect.anchorMin = new Vector2(0.5f, 0f); badgeRect.anchorMax = new Vector2(0.5f, 0f); badgeRect.pivot = new Vector2(0.5f, 1f); badgeRect.anchoredPosition = new Vector2(0f, -4f); } private static void HideConnectedTravelBadge() { if ((Object)(object)ConnectedTravelBadge != (Object)null && ConnectedTravelBadge.activeSelf) { ConnectedTravelBadge.SetActive(false); } } } internal enum PublicPortalAccessMode { Personal, Admin, Public, Clan, Tagged, Invite } internal enum PublicPortalTravelCostScope { Off, All, AdminPortalTrips, PersonalAndClanRoutesFree, AllItemsSourceTrips } internal enum RequiredGlobalKeyValidationFailure { None, TooLong, ControlCharacters, NumericKey, ReservedKey } internal readonly struct PortalOwner { public readonly string Id; public readonly string Name; public bool IsValid => !string.IsNullOrWhiteSpace(Id); public PortalOwner(string id, string name) { Id = PublicPortalData.NormalizeId(id); Name = name ?? ""; } } internal readonly struct PortalBuilder { public readonly string AccountId; public readonly string Name; public readonly string PlatformId; public readonly long CharacterPlayerId; public readonly long BuildSequence; public bool IsValid => !string.IsNullOrWhiteSpace(AccountId); public PortalBuilder(string accountId, string name) : this(accountId, name, accountId, 0L, 0L) { } public PortalBuilder(string accountId, string name, string platformId, long characterPlayerId, long buildSequence) { AccountId = PublicPortalData.NormalizeId(accountId); Name = name ?? ""; PlatformId = PublicPortalData.NormalizeId(platformId); CharacterPlayerId = characterPlayerId; BuildSequence = Math.Max(0L, buildSequence); } public PortalBuilder WithBuildSequence(long buildSequence) { return new PortalBuilder(AccountId, Name, PlatformId, CharacterPlayerId, buildSequence); } } internal static class PublicPortalData { private static readonly FieldRef SteamPlatform = AccessTools.FieldRefAccess("m_steamPlatform"); private static string _serverAssignedOwnerId = ""; private static bool _serverAssignedIsAdmin; internal const int ClanAccessAuthorityVersion = 3; internal const int RequiredGlobalKeyAuthorityVersion = 4; internal const int TemporaryPublicAuthorityVersion = 5; internal const int InviteAccessAuthorityVersion = 6; internal const int CurrentAccessAuthorityVersion = 6; internal const int CurrentBuilderAuthorityVersion = 2; internal const int MaximumRequiredGlobalKeyLength = 128; internal const long MaximumPublicExpiresAtUtcSeconds = 253402300799L; internal const float MaximumTeleportSourceDistance = 15f; public const string AccessModeKey = "PortalRules AccessMode"; public const string OwnerIdKey = "PortalRules OwnerId"; public const string OwnerNameKey = "PortalRules OwnerName"; public const string AuthorizedClanIdKey = "PortalRules AuthorizedClanId"; public const string AuthorityVersionKey = "PortalRules AuthorityVersion"; public const string BuilderAccountIdKey = "PortalRules BuilderAccountId"; public const string BuilderNameKey = "PortalRules BuilderName"; public const string BuilderPlatformIdKey = "PortalRules BuilderPlatformId"; public const string BuilderCharacterPlayerIdKey = "PortalRules BuilderPlayerId"; public const string PortalBuildSequenceKey = "PortalRules BuildSequence"; public const string BuilderAuthorityVersionKey = "PortalRules BuilderAuthorityVersion"; public const string AuthorizedPrefabHashKey = "PortalRules AuthorizedPrefabHash"; public const string PublicExpiresAtUtcSecondsKey = "PortalRules PublicExpiresAtUtc"; public const string FavoriteIdKey = "PortalRules FavoriteId"; public const string RequiredGlobalKeyKey = "PortalRules RequiredGlobalKey"; internal const int MaximumFavoriteCount = 10; public const string ChangeAccessModeRpc = "sighsorry.PortalRules.ChangeAccessMode.v4"; public const string ChangeAccessModeResultRpc = "sighsorry.PortalRules.ChangeAccessModeResult.v5"; public const string ChangeAdminPortalTagRpc = "sighsorry.PortalRules.ChangeAdminPortalTag.v1"; public const string ChangeAdminPortalRequiredGlobalKeyRpc = "sighsorry.PortalRules.ChangeAdminPortalRequiredGlobalKey.v1"; public const string RemoveAdminPortalRpc = "sighsorry.PortalRules.RemoveAdminPortal.v1"; public const string FavoritesKey = "PortalRules Favorites v3"; public static string ServerAssignedOwnerId => _serverAssignedOwnerId; public static bool ServerAssignedIsAdmin => _serverAssignedIsAdmin; public static PublicPortalAccessMode GetAccessMode(ZDO zdo) { int num = zdo.GetInt("PortalRules AccessMode", 0); if (!Enum.IsDefined(typeof(PublicPortalAccessMode), num) || (num == 5 && zdo.GetInt("PortalRules AuthorityVersion", 0) < 6)) { return PublicPortalAccessMode.Personal; } return (PublicPortalAccessMode)num; } public static PortalOwner GetOwner(ZDO zdo) { return new PortalOwner(zdo.GetString("PortalRules OwnerId", ""), zdo.GetString("PortalRules OwnerName", "")); } public static PortalBuilder GetBuilder(ZDO zdo) { return new PortalBuilder(zdo.GetString("PortalRules BuilderAccountId", ""), zdo.GetString("PortalRules BuilderName", ""), zdo.GetString("PortalRules BuilderPlatformId", ""), zdo.GetLong("PortalRules BuilderPlayerId", 0L), zdo.GetLong("PortalRules BuildSequence", 0L)); } public static string GetAuthorizedClanId(ZDO zdo) { return (zdo.GetString("PortalRules AuthorizedClanId", "") ?? "").Trim(); } public static string GetRequiredGlobalKey(ZDO zdo) { return (zdo.GetString("PortalRules RequiredGlobalKey", "") ?? "").Trim(); } public static long GetPublicExpiresAtUtcSeconds(ZDO zdo) { return NormalizePublicExpiresAtUtcSeconds(zdo.GetLong("PortalRules PublicExpiresAtUtc", 0L)); } public static void SetAccessMode(ZDO zdo, PublicPortalAccessMode mode, PortalOwner owner) { zdo.Set("PortalRules AccessMode", (int)mode); zdo.Set("PortalRules OwnerId", owner.Id); zdo.Set("PortalRules OwnerName", owner.Name); } public static void SetAuthorizedClanId(ZDO zdo, string authorizedClanId) { zdo.Set("PortalRules AuthorizedClanId", (authorizedClanId ?? "").Trim()); } public static void SetRequiredGlobalKey(ZDO zdo, string requiredGlobalKey) { zdo.Set("PortalRules RequiredGlobalKey", (requiredGlobalKey ?? "").Trim()); } public static void SetPublicExpiresAtUtcSeconds(ZDO zdo, long publicExpiresAtUtcSeconds) { zdo.Set("PortalRules PublicExpiresAtUtc", NormalizePublicExpiresAtUtcSeconds(publicExpiresAtUtcSeconds)); } internal static long NormalizePublicExpiresAtUtcSeconds(long value) { if (value <= 0 || value > 253402300799L) { return 0L; } return value; } internal static bool TryNormalizeRequiredGlobalKey(string? value, out string normalized, out RequiredGlobalKeyValidationFailure failure) { //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Invalid comparison between Unknown and I4 //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Invalid comparison between Unknown and I4 normalized = (value ?? "").Trim(); failure = RequiredGlobalKeyValidationFailure.None; if (normalized.Length == 0) { return true; } if (normalized.Length > 128) { failure = RequiredGlobalKeyValidationFailure.TooLong; return false; } string text = normalized; for (int i = 0; i < text.Length; i++) { if (char.IsControl(text[i])) { failure = RequiredGlobalKeyValidationFailure.ControlCharacters; return false; } } GlobalKeys val = default(GlobalKeys); string keyValue = ZoneSystem.GetKeyValue(normalized, ref text, ref val); if (long.TryParse(keyValue, NumberStyles.Integer, CultureInfo.InvariantCulture, out var _)) { failure = RequiredGlobalKeyValidationFailure.NumericKey; return false; } GlobalKeys result2; bool flag = Enum.TryParse(keyValue, ignoreCase: true, out result2) && Enum.IsDefined(typeof(GlobalKeys), result2); if (flag) { bool flag2 = (((int)result2 == 32 || (int)result2 == 43) ? true : false); flag = flag2; } if (flag) { failure = RequiredGlobalKeyValidationFailure.ReservedKey; return false; } return true; } public static void SetBuilder(ZDO zdo, PortalBuilder builder, int prefabHash) { zdo.Set("PortalRules BuilderAccountId", builder.AccountId); zdo.Set("PortalRules BuilderName", builder.Name); zdo.Set("PortalRules BuilderPlatformId", builder.PlatformId); zdo.Set("PortalRules BuilderPlayerId", builder.CharacterPlayerId); zdo.Set("PortalRules BuildSequence", builder.BuildSequence); zdo.Set("PortalRules AuthorizedPrefabHash", prefabHash); } public static PortalOwner LocalOwner() { string text = _serverAssignedOwnerId; if (string.IsNullOrWhiteSpace(text) && TryGetLocalSteamId64(out string steamId)) { text = steamId; } string name = (((Object)(object)Player.m_localPlayer != (Object)null) ? ((Character)Player.m_localPlayer).GetHoverName() : ""); return new PortalOwner(text, name); } public static void SetServerAssignedOwnerId(string ownerId) { _serverAssignedOwnerId = NormalizeId(ownerId); } public static void SetServerAssignedIsAdmin(bool isAdmin) { _serverAssignedIsAdmin = isAdmin; } public static bool TryGetPeerOwner(ZNetPeer? peer, out PortalOwner owner) { owner = new PortalOwner("", ""); if (!TryGetPeerSteamId64(peer, out string steamId)) { return false; } owner = new PortalOwner(steamId, peer?.m_playerName ?? ""); return owner.IsValid; } internal unsafe static bool TryGetPeerSteamId64(ZNetPeer? peer, out string steamId) { //IL_0007: 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) steamId = ""; if ((int)ZNet.m_onlineBackend != 0 || (Object)(object)ZNet.instance == (Object)null || peer?.m_socket == null) { return false; } string hostName = peer.m_socket.GetHostName(); if (string.IsNullOrWhiteSpace(hostName)) { return false; } PlatformUserID val = default(PlatformUserID); ((PlatformUserID)(ref val))..ctor(SteamPlatform.Invoke(ZNet.instance), hostName); return TryNormalizeSteamId64(((object)(*(PlatformUserID*)(&val))/*cast due to .constrained prefix*/).ToString(), out steamId); } internal static bool TryGetLocalSteamId64(out string steamId) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) steamId = ""; if ((int)ZNet.m_onlineBackend != 0) { return false; } try { if (TryNormalizeSteamId64(((object)Unsafe.As(ref UserInfo.GetLocalUser().UserId)/*cast due to .constrained prefix*/).ToString(), out steamId)) { return true; } } catch (Exception) { } IDistributionPlatform distributionPlatform = PlatformManager.DistributionPlatform; if (((distributionPlatform != null) ? distributionPlatform.LocalUser : null) != null) { return TryNormalizeSteamId64(((object)((IUser)distributionPlatform.LocalUser).PlatformUserID/*cast due to .constrained prefix*/).ToString(), out steamId); } return false; } internal static bool TryGetAuthenticatedPeerPlayerId(ZNetPeer? peer, out long playerId) { ZDO character; return TryGetAuthenticatedPeerCharacter(peer, out character, out playerId); } public static bool IsPeerAdmin(ZNet znet, ZNetPeer peer) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) if ((int)ZNet.m_onlineBackend == 0 && (Object)(object)znet != (Object)null && peer?.m_socket != null) { return znet.IsAdmin(peer.m_socket.GetHostName()); } return false; } internal static ZNetPeer? FindPeer(ZNet? znet, ZRpc? rpc) { if ((Object)(object)znet == (Object)null || rpc == null) { return null; } foreach (ZNetPeer peer in znet.GetPeers()) { if (peer != null && peer.m_rpc == rpc) { return peer; } } return null; } internal static bool TryGetAuthenticatedPeerCharacterPosition(ZNetPeer? peer, out Vector3 position) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) position = Vector3.zero; if ((Object)(object)ZNetScene.instance == (Object)null || !TryGetAuthenticatedPeerCharacter(peer, out ZDO character, out long playerId)) { return false; } GameObject val = ZNetScene.instance.FindInstance(peer.m_characterID); Player val2 = (((Object)(object)val != (Object)null) ? val.GetComponent() : null); ZNetView val3 = (((Object)(object)val != (Object)null) ? val.GetComponent() : null); if ((Object)(object)val2 == (Object)null || (Object)(object)val3 == (Object)null || !val3.IsValid() || val3.GetZDO() != character || ((Character)val2).GetZDOID() != peer.m_characterID || val2.GetPlayerID() != playerId) { return false; } position = character.GetPosition(); return IsFinite(position); } private static bool TryGetAuthenticatedPeerCharacter(ZNetPeer? peer, out ZDO character, out long playerId) { //IL_005a: Unknown result type (might be due to invalid IL or missing references) character = null; playerId = 0L; if (peer == null || !peer.IsReady() || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || ZDOMan.instance == null || ((ZDOID)(ref peer.m_characterID)).IsNone() || ((ZDOID)(ref peer.m_characterID)).UserID != peer.m_uid) { return false; } ZDO zDO = ZDOMan.instance.GetZDO(peer.m_characterID); if (zDO == null || !zDO.IsValid() || zDO.GetOwner() != peer.m_uid) { return false; } if ((Object)(object)ZNetScene.instance != (Object)null) { GameObject prefab = ZNetScene.instance.GetPrefab(zDO.GetPrefab()); if ((Object)(object)prefab == (Object)null || (Object)(object)prefab.GetComponent() == (Object)null) { return false; } } long num = zDO.GetLong(ZDOVars.s_playerID, 0L); if (num == 0L) { return false; } character = zDO; playerId = num; return true; } public static bool IsLocalOwner(PortalOwner owner) { if (owner.IsValid) { return string.Equals(owner.Id, LocalOwner().Id, StringComparison.Ordinal); } return false; } public static string NormalizeId(string id) { id = (id ?? "").Trim(); if (!id.StartsWith("Steam_", StringComparison.Ordinal)) { return id; } return id.Substring("Steam_".Length); } internal static bool TryNormalizeSteamId64(string? value, out string steamId) { steamId = ""; string text = (value ?? "").Trim(); if (text.StartsWith("Steam_", StringComparison.OrdinalIgnoreCase)) { text = text.Substring("Steam_".Length); } if (text.Length != 17 || !ulong.TryParse(text, NumberStyles.None, CultureInfo.InvariantCulture, out var result) || result == 0L) { return false; } steamId = result.ToString(CultureInfo.InvariantCulture); return string.Equals(text, steamId, StringComparison.Ordinal); } public static bool TryNormalizeFavoriteId(string? favoriteId, out string normalizedFavoriteId) { normalizedFavoriteId = ""; if (!Guid.TryParseExact((favoriteId ?? "").Trim(), "N", out var result) || result == Guid.Empty) { return false; } normalizedFavoriteId = result.ToString("N"); return true; } public static void SetFavoriteId(ZDO zdo, string favoriteId) { if (zdo != null && TryNormalizeFavoriteId(favoriteId, out string normalizedFavoriteId)) { zdo.Set("PortalRules FavoriteId", normalizedFavoriteId); } } private static bool IsFinite(Vector3 value) { //IL_0000: 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_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) if (!float.IsNaN(value.x) && !float.IsInfinity(value.x) && !float.IsNaN(value.y) && !float.IsInfinity(value.y) && !float.IsNaN(value.z)) { return !float.IsInfinity(value.z); } return false; } public static List ReadFavorites() { if ((Object)(object)Player.m_localPlayer == (Object)null || !Player.m_localPlayer.m_customData.TryGetValue("PortalRules Favorites v3", out var value) || string.IsNullOrWhiteSpace(value)) { return new List(); } List list = new List(10); HashSet hashSet = new HashSet(StringComparer.Ordinal); string[] array = value.Split(new char[1] { ',' }, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < array.Length; i++) { if (TryNormalizeFavoriteId(array[i], out string normalizedFavoriteId) && hashSet.Add(normalizedFavoriteId)) { list.Add(normalizedFavoriteId); if (list.Count >= 10) { break; } } } return list; } public static void WriteFavorites(IEnumerable favorites) { if ((Object)(object)Player.m_localPlayer == (Object)null) { return; } List list = new List(10); HashSet hashSet = new HashSet(StringComparer.Ordinal); foreach (string favorite in favorites) { if (TryNormalizeFavoriteId(favorite, out string normalizedFavoriteId) && hashSet.Add(normalizedFavoriteId)) { list.Add(normalizedFavoriteId); if (list.Count >= 10) { break; } } } string value = string.Join(",", list); if (string.IsNullOrWhiteSpace(value)) { Player.m_localPlayer.m_customData.Remove("PortalRules Favorites v3"); } else { Player.m_localPlayer.m_customData["PortalRules Favorites v3"] = value; } } } internal readonly struct PublicPortalCatalogEntry { public readonly ZDOID Id; public readonly string FavoriteId; public readonly int PrefabHash; public readonly bool AllowsAllItems; public readonly Vector3 Position; public readonly Quaternion Rotation; public readonly string Tag; public readonly PublicPortalAccessMode AccessMode; public readonly PortalOwner Owner; public readonly int MyPortalOrdinal; public readonly int MyPortalLimit; public readonly int ModeOrdinal; public readonly int ModeCurrent; public readonly int ModeLimit; public readonly long InviteDepartureCooldownUntilUtc; public readonly long InviteArrivalCooldownUntilUtc; public PublicPortalCatalogEntry(ZDOID id, string favoriteId, int prefabHash, bool allowsAllItems, Vector3 position, Quaternion rotation, string tag, PublicPortalAccessMode accessMode, PortalOwner owner, int myPortalOrdinal = 0, int myPortalLimit = 0, int modeOrdinal = 0, int modeCurrent = 0, int modeLimit = 0, long inviteDepartureCooldownUntilUtc = 0L, long inviteArrivalCooldownUntilUtc = 0L) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_002f: 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_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) Id = id; FavoriteId = (PublicPortalData.TryNormalizeFavoriteId(favoriteId, out string normalizedFavoriteId) ? normalizedFavoriteId : ""); PrefabHash = prefabHash; AllowsAllItems = allowsAllItems; Position = position; Rotation = rotation; Tag = tag ?? ""; AccessMode = accessMode; Owner = owner; MyPortalOrdinal = Math.Max(0, myPortalOrdinal); MyPortalLimit = Math.Max(-1, myPortalLimit); ModeOrdinal = Math.Max(0, modeOrdinal); ModeCurrent = Math.Max(0, modeCurrent); ModeLimit = Math.Max(-1, modeLimit); InviteDepartureCooldownUntilUtc = Math.Max(0L, inviteDepartureCooldownUntilUtc); InviteArrivalCooldownUntilUtc = Math.Max(0L, inviteArrivalCooldownUntilUtc); } public bool HasSameContent(PublicPortalCatalogEntry 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_0050: 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_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) if (Id == other.Id && string.Equals(FavoriteId, other.FavoriteId, StringComparison.Ordinal) && PrefabHash == other.PrefabHash && AllowsAllItems == other.AllowsAllItems && Position == other.Position && Rotation == other.Rotation && string.Equals(Tag, other.Tag, StringComparison.Ordinal) && AccessMode == other.AccessMode && string.Equals(Owner.Id, other.Owner.Id, StringComparison.Ordinal) && string.Equals(Owner.Name, other.Owner.Name, StringComparison.Ordinal) && MyPortalOrdinal == other.MyPortalOrdinal && MyPortalLimit == other.MyPortalLimit && ModeOrdinal == other.ModeOrdinal && ModeCurrent == other.ModeCurrent && ModeLimit == other.ModeLimit && InviteDepartureCooldownUntilUtc == other.InviteDepartureCooldownUntilUtc) { return InviteArrivalCooldownUntilUtc == other.InviteArrivalCooldownUntilUtc; } return false; } public PublicPortalCatalogEntry WithDisplayMetadata(int myPortalOrdinal, int myPortalLimit, int modeOrdinal, int modeCurrent, int modeLimit, long inviteDepartureCooldownUntilUtc = 0L, long inviteArrivalCooldownUntilUtc = 0L) { //IL_0001: 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_001f: Unknown result type (might be due to invalid IL or missing references) return new PublicPortalCatalogEntry(Id, FavoriteId, PrefabHash, AllowsAllItems, Position, Rotation, Tag, AccessMode, Owner, myPortalOrdinal, myPortalLimit, modeOrdinal, modeCurrent, modeLimit, inviteDepartureCooldownUntilUtc, inviteArrivalCooldownUntilUtc); } } internal readonly struct LocalPortalPlacementState { public readonly PortalBuilder PreviousBuilder; public readonly HashSet? PreviousPendingPortalIds; public LocalPortalPlacementState(PortalBuilder previousBuilder, HashSet? previousPendingPortalIds) { PreviousBuilder = previousBuilder; PreviousPendingPortalIds = previousPendingPortalIds; } } internal static class PublicPortalCatalog { private readonly struct ServerPortalAuthority { public readonly PublicPortalAccessMode AccessMode; public readonly PortalOwner Owner; public readonly string AuthorizedClanId; public readonly PortalBuilder Builder; public readonly int PrefabHash; public readonly string FavoriteId; public readonly string RequiredGlobalKey; public readonly long PublicExpiresAtUtcSeconds; public readonly bool RequiredGlobalKeyIsValid; public ServerPortalAuthority(PublicPortalAccessMode accessMode, PortalOwner owner, string authorizedClanId, PortalBuilder builder, int prefabHash, string favoriteId, string requiredGlobalKey, long publicExpiresAtUtcSeconds) { AccessMode = accessMode; Owner = owner; AuthorizedClanId = authorizedClanId ?? ""; Builder = builder; PrefabHash = prefabHash; FavoriteId = favoriteId ?? ""; RequiredGlobalKeyIsValid = PublicPortalData.TryNormalizeRequiredGlobalKey(requiredGlobalKey, out string normalized, out RequiredGlobalKeyValidationFailure _); RequiredGlobalKey = (RequiredGlobalKeyIsValid ? normalized : (requiredGlobalKey ?? "").Trim()); PublicExpiresAtUtcSeconds = ((accessMode == PublicPortalAccessMode.Public && builder.IsValid && !PublicPortalKinds.IsAdminPortalPrefab(prefabHash)) ? PublicPortalData.NormalizePublicExpiresAtUtcSeconds(publicExpiresAtUtcSeconds) : 0); } } private readonly struct AdminPortalTagAuthority { public readonly string Tag; public AdminPortalTagAuthority(string tag) { Tag = tag ?? ""; } } private sealed class RecipientContext { public readonly string OwnerId; public readonly bool IsAdmin; public readonly PortalClanMembership ClanMembership; public readonly ZNetPeer? Peer; public readonly Dictionary RequiredGlobalKeyResults = new Dictionary(StringComparer.Ordinal); public RecipientContext(string ownerId, bool isAdmin, PortalClanMembership clanMembership, ZNetPeer? peer) { OwnerId = ownerId ?? ""; IsAdmin = isAdmin; ClanMembership = clanMembership; Peer = peer; } } private readonly struct SentViewState { public readonly int Revision; public readonly string RecipientOwnerId; public readonly bool IsAdmin; public readonly ulong RecipientViewFingerprint; public readonly long ViewToken; public readonly bool Acknowledged; public readonly float LastSentAt; public SentViewState(int revision, string recipientOwnerId, bool isAdmin, ulong recipientViewFingerprint, long viewToken, bool acknowledged, float lastSentAt) { Revision = revision; RecipientOwnerId = recipientOwnerId; IsAdmin = isAdmin; RecipientViewFingerprint = recipientViewFingerprint; ViewToken = viewToken; Acknowledged = acknowledged; LastSentAt = lastSentAt; } public bool MatchesView(int revision, string recipientOwnerId, bool isAdmin, ulong recipientViewFingerprint) { if (MatchesRecipient(revision, recipientOwnerId, isAdmin)) { return RecipientViewFingerprint == recipientViewFingerprint; } return false; } public bool MatchesRecipient(int revision, string recipientOwnerId, bool isAdmin) { if (Revision == revision && IsAdmin == isAdmin) { return string.Equals(RecipientOwnerId, recipientOwnerId, StringComparison.Ordinal); } return false; } public SentViewState WithAcknowledgement() { return new SentViewState(Revision, RecipientOwnerId, IsAdmin, RecipientViewFingerprint, ViewToken, acknowledged: true, LastSentAt); } } internal sealed class PortalSyncContext { public readonly ZNetPeer? Peer; public readonly HashSet CreatedIds = new HashSet(); public PortalSyncContext(ZNetPeer? peer) { Peer = peer; } } private sealed class PendingSnapshot { public readonly int Revision; public readonly long ViewToken; public readonly int ChunkCount; public readonly int TotalCount; public readonly string RecipientOwnerId; public readonly bool RecipientIsAdmin; public readonly long ServerUtcAtSend; public readonly long FirstChunkReceivedTimestamp; public readonly List Entries; public readonly HashSet FavoriteIds = new HashSet(StringComparer.Ordinal); public int NextChunk; public PendingSnapshot(int revision, long viewToken, int chunkCount, int totalCount, string recipientOwnerId, bool recipientIsAdmin, long serverUtcAtSend) { Revision = revision; ViewToken = viewToken; ChunkCount = chunkCount; TotalCount = totalCount; RecipientOwnerId = recipientOwnerId; RecipientIsAdmin = recipientIsAdmin; ServerUtcAtSend = serverUtcAtSend; FirstChunkReceivedTimestamp = Stopwatch.GetTimestamp(); Entries = new List(totalCount); } } private const byte FormatVersion = 7; private const int MaximumCatalogEntries = 100000; private const int MaximumEntriesPerChunk = 96; private const int MaximumChunkCount = 1042; private const int MaximumTagLength = 256; private const int MaximumOwnerIdLength = 128; private const int MaximumOwnerNameLength = 128; private const int MaximumClanIdLength = 128; private const int MaximumFavoriteIdLength = 32; private const float MaximumAdminPortalPlacementDistance = 15f; private const float MaximumPortalCoordinateMagnitude = 1000000f; private const double MinimumQuaternionSqrMagnitude = 0.25; private const double MaximumQuaternionSqrMagnitude = 4.0; private const float ServerRefreshSeconds = 5f; private const float ClientRequestCooldownSeconds = 0.5f; private const float ServerRequestCooldownSeconds = 30f; private const float SnapshotRetrySeconds = 30f; private const string RequestCatalogRpc = "sighsorry.PortalRules.CatalogRequest.v7"; private const string ReceiveCatalogRpc = "sighsorry.PortalRules.CatalogSnapshot.v7"; private static readonly List ServerEntries = new List(); private static readonly List ClientEntries = new List(); private static readonly Dictionary ClientIndex = new Dictionary(); private static readonly Dictionary ServerAuthority = new Dictionary(); private static readonly Dictionary ServerPortalsByFavoriteId = new Dictionary(StringComparer.Ordinal); private static readonly Dictionary AdminPortalTags = new Dictionary(); private static readonly Dictionary> ServerPortalsByBuilder = new Dictionary>(StringComparer.Ordinal); private static readonly HashSet InitialWorldPortalIds = new HashSet(); private static readonly HashSet PendingRemovalIds = new HashSet(); private static readonly Dictionary LastServerRequestAt = new Dictionary(); private static readonly Dictionary LastSentViews = new Dictionary(); private static Game? _registeredGame; private static Game? _serverLoopGame; private static ZNet? _sessionZNet; private static int _serverRevision; private static int _clientRevision = -1; private static long _clientViewToken; private static long _nextViewToken; private static float _lastClientRequestAt = -100f; private static float _nextIdentityReconcileAt = -100f; private static bool _authorityBackfilled; private static bool _hasSnapshot; private static bool _worldLoaded; private static bool _serverCatalogDirty; private static bool _serverDirtyFlushScheduled; private static bool _localRecipientViewInitialized; private static string _localRecipientOwnerId = ""; private static bool _localRecipientIsAdmin; private static ulong _localRecipientViewFingerprint; private static long _sessionGeneration; private static long _nextBuildSequence; private static long _clientServerUtcAtSync; private static long _clientServerClockTimestamp; [ThreadStatic] private static PortalSyncContext? _activePortalSync; [ThreadStatic] private static PortalBuilder _activeLocalPlacementBuilder; [ThreadStatic] private static HashSet? _pendingLocalPlacementPortalIds; private static PendingSnapshot? _pendingSnapshot; public static IReadOnlyList Entries => ClientEntries; public static bool HasSnapshot => _hasSnapshot; internal static bool IsServerReady { get { if (_worldLoaded && _authorityBackfilled && _hasSnapshot && (Object)(object)ZNet.instance != (Object)null) { return ZNet.instance.IsServer(); } return false; } } public static event Action? Updated; private static void WriteCatalogEntry(ZPackage package, PublicPortalCatalogEntry entry) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0032: 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) package.Write(entry.Id); package.Write(entry.FavoriteId); package.Write(entry.PrefabHash); package.Write(entry.AllowsAllItems); package.Write(entry.Position); package.Write(entry.Rotation); package.Write(entry.Tag); package.Write((int)entry.AccessMode); package.Write(entry.Owner.Id); package.Write(entry.Owner.Name); package.Write(entry.MyPortalOrdinal); package.Write(entry.MyPortalLimit); package.Write(entry.ModeOrdinal); package.Write(entry.ModeCurrent); package.Write(entry.ModeLimit); package.Write(entry.InviteDepartureCooldownUntilUtc); package.Write(entry.InviteArrivalCooldownUntilUtc); } private static bool TryReadCatalogEntry(ZPackage package, HashSet favoriteIds, out PublicPortalCatalogEntry entry) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_017a: Unknown result type (might be due to invalid IL or missing references) //IL_0183: Unknown result type (might be due to invalid IL or missing references) //IL_01e4: Unknown result type (might be due to invalid IL or missing references) //IL_01e9: Unknown result type (might be due to invalid IL or missing references) //IL_01eb: Unknown result type (might be due to invalid IL or missing references) ZDOID id = package.ReadZDOID(); string text = package.ReadString(); int prefabHash = package.ReadInt(); bool allowsAllItems = package.ReadBool(); Vector3 val = package.ReadVector3(); Quaternion val2 = package.ReadQuaternion(); string text2 = package.ReadString(); int num = package.ReadInt(); string text3 = package.ReadString(); string text4 = package.ReadString(); int num2 = package.ReadInt(); int num3 = package.ReadInt(); int num4 = package.ReadInt(); int num5 = package.ReadInt(); int num6 = package.ReadInt(); long num7 = package.ReadLong(); long num8 = package.ReadLong(); string normalizedFavoriteId = ""; bool flag = text.Length <= 32 && PublicPortalData.TryNormalizeFavoriteId(text, out normalizedFavoriteId); bool num9 = num == 5 || num == 3; bool flag2 = num2 >= 0 && num2 <= 100000 && num3 >= -1 && num3 <= 10000 && (num2 != 0 || num3 == 0); bool flag3 = ((!num9) ? (num4 == 0 && num5 == 0 && num6 == 0) : (num4 > 0 && num4 <= 100000 && num5 >= num4 && num5 <= 100000 && num6 >= -1 && num6 <= 10000)); bool flag4 = num7 >= 0 && num7 <= 253402300799L && num8 >= 0 && num8 <= 253402300799L && (num == 5 || (num7 == 0L && num8 == 0)); if (((ZDOID)(ref id)).IsNone() || !flag || !favoriteIds.Add(normalizedFavoriteId) || !IsFinite(val) || !IsFinite(val2) || text2.Length > 256 || text3.Length > 128 || text4.Length > 128 || !Enum.IsDefined(typeof(PublicPortalAccessMode), num) || !flag2 || !flag3 || !flag4) { entry = default(PublicPortalCatalogEntry); return false; } entry = new PublicPortalCatalogEntry(id, normalizedFavoriteId, prefabHash, allowsAllItems, val, val2, text2, (PublicPortalAccessMode)num, new PortalOwner(text3, text4), num2, num3, num4, num5, num6, num7, num8); return true; } public static void Register(Game game) { if ((Object)(object)game == (Object)null) { return; } if (_sessionZNet != ZNet.instance) { BeginNetworkSession(ZNet.instance); } if (_registeredGame != game) { _registeredGame = game; if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer()) { RefreshAndBroadcast(); _serverLoopGame = game; ((MonoBehaviour)game).StartCoroutine(ServerRefreshLoop(game)); } else { RequestRefresh(force: true); } } } public static void RegisterPeer(ZNet znet, ZNetPeer peer) { if (!((Object)(object)znet == (Object)null) && peer?.m_rpc != null) { if (znet.IsServer()) { peer.m_rpc.Register("sighsorry.PortalRules.CatalogRequest.v7", (Action)OnCatalogRequest); } else { peer.m_rpc.Register("sighsorry.PortalRules.CatalogSnapshot.v7", (Action)OnCatalogReceived); } } } public static void BeginNetworkSession(ZNet? znet) { if (!((Object)(object)znet == (Object)null) && _sessionZNet != znet) { InviteTravelCooldownStore.EndServerSession(); AdminPortalBiomeDefaults.EndServerSession(); PortalAccountStore.EndServerSession(); ResetSessionState(); _registeredGame = null; _sessionZNet = znet; if (znet.IsServer()) { PortalAccountStore.BeginServerSession(); AdminPortalBiomeDefaults.BeginServerSession(); } } } public static void NotifyServerWorldLoaded(ZNet znet) { if (_sessionZNet != znet) { BeginNetworkSession(znet); } _worldLoaded = true; if (znet.IsServer()) { InviteTravelCooldownStore.BeginServerSession(); } if ((Object)(object)_registeredGame != (Object)null && znet.IsServer()) { RefreshAndBroadcast(); } } public static void Shutdown() { InviteTravelCooldownStore.EndServerSession(); AdminPortalBiomeDefaults.EndServerSession(); PortalAccountStore.EndServerSession(); ResetSessionState(); _registeredGame = null; _sessionZNet = null; } public static void ForgetPeer(ZRpc? rpc) { if (rpc != null) { LastServerRequestAt.Remove(rpc); LastSentViews.Remove(rpc); } } public static void RequestRefresh(bool force = false) { if ((Object)(object)ZNet.instance == (Object)null) { return; } if (ZNet.instance.IsServer()) { RefreshAndBroadcast(); return; } float realtimeSinceStartup = Time.realtimeSinceStartup; if (force || !(realtimeSinceStartup - _lastClientRequestAt < 0.5f)) { ZRpc serverRPC = ZNet.instance.GetServerRPC(); if (serverRPC != null) { _lastClientRequestAt = realtimeSinceStartup; serverRPC.Invoke("sighsorry.PortalRules.CatalogRequest.v7", new object[2] { _clientRevision, _clientViewToken }); } } } public static void ObservePortal(ZDO zdo) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_0158: 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_0117: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) if (zdo == null || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || ServerAuthority.ContainsKey(zdo.m_uid) || PendingRemovalIds.Contains(zdo.m_uid) || !zdo.IsValid() || (!PublicPortalKinds.IsHandledPortal(zdo) && !PublicPortalServerPolicy.IsCountedPortalPrefab(zdo.GetPrefab()))) { return; } bool flag = _activePortalSync != null; if (!_authorityBackfilled) { if (flag) { PublicPortalServerPolicy.RejectUnverifiedPortal(zdo, _activePortalSync?.Peer); } return; } if (PublicPortalKinds.IsAdminPortalPrefab(zdo.GetPrefab())) { ZNetPeer val = _activePortalSync?.Peer; if (flag && !IsAuthenticatedRemoteAdminPortalCreation(zdo, val)) { PublicPortalServerPolicy.RejectUnverifiedPortal(zdo, val); PortalRulesPlugin.PortalRulesLogger.LogWarning((object)$"Rejected admin portal creation {zdo.m_uid} from a non-admin or unverified peer."); } else if (!CanInitializeAdminPortalAuthority(zdo)) { if (flag) { PublicPortalServerPolicy.RejectUnverifiedPortal(zdo, val); PortalRulesPlugin.PortalRulesLogger.LogWarning((object)$"Rejected admin portal creation {zdo.m_uid} while biome GlobalKey defaults were unavailable."); } else { InitialWorldPortalIds.Add(zdo.m_uid); } } else { SetAndPersistServerAuthority(zdo, CreateAdminPortalAuthority(zdo, PublicPortalAccessMode.Public, CreateServerFavoriteId(), ResolveInitialAdminRequiredGlobalKey(zdo)), forceSend: true); MarkServerCatalogDirty(zdo); } return; } if (!flag && _pendingLocalPlacementPortalIds != null) { _pendingLocalPlacementPortalIds.Add(zdo.m_uid); return; } ServerPortalAuthority authority; if (InitialWorldPortalIds.Contains(zdo.m_uid)) { authority = ReadInitialAuthority(zdo); } else { PortalBuilder builder; bool flag2 = TryResolveActiveBuilder(zdo, out builder); long num = zdo.GetLong(ZDOVars.s_creator, 0L); if (!flag2 && !flag && num != 0L) { flag2 = TryCreateRegistryBuilder(zdo, out builder); } if (!flag2) { if (flag || num != 0L) { ZNetPeer sourcePeer = _activePortalSync?.Peer; PublicPortalServerPolicy.RejectUnverifiedPortal(zdo, sourcePeer); return; } authority = CreateAuthorityForNewPortal(zdo); } else { PortalOwner owner = new PortalOwner(builder.AccountId, builder.Name); PortalBuilder builder2 = SanitizeBuilder(builder); if (!PublicPortalServerPolicy.TryAcceptNewPortal(zdo, builder2, _activePortalSync?.Peer)) { return; } authority = new ServerPortalAuthority(PublicPortalAccessMode.Personal, SanitizeOwner(owner), "", builder2, zdo.GetPrefab(), CreateServerFavoriteId(), "", CreateTemporaryPublicExpirationUtcSeconds(PublicPortalAccessMode.Personal, builder2, zdo.GetPrefab())); } } if (TryNormalizeTemporaryPublicAuthority(authority, GetUtcNowSeconds(), out var updatedAuthority, out var _)) { authority = updatedAuthority; } SetAndPersistServerAuthority(zdo, authority, forceSend: true); MarkServerCatalogDirty(zdo); } public static PortalSyncContext? BeginPortalSync(ZRpc rpc) { if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer() && _worldLoaded && !_authorityBackfilled) { RefreshAndBroadcast(); } PortalSyncContext? activePortalSync = _activePortalSync; _activePortalSync = new PortalSyncContext(((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer()) ? PublicPortalData.FindPeer(ZNet.instance, rpc) : null); return activePortalSync; } public static void RestorePortalSync(PortalSyncContext? previousContext) { _activePortalSync = previousContext; } public static void ObserveRemoteZdoCreated(ZDO zdo) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) if (zdo != null && _activePortalSync != null) { _activePortalSync.CreatedIds.Add(zdo.m_uid); } } public static LocalPortalPlacementState BeginLocalPlacement(Player player) { if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer() && _worldLoaded && !_authorityBackfilled) { RefreshAndBroadcast(); } LocalPortalPlacementState result = new LocalPortalPlacementState(_activeLocalPlacementBuilder, _pendingLocalPlacementPortalIds); _activeLocalPlacementBuilder = (((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer() && (Object)(object)player != (Object)null && (Object)(object)player == (Object)(object)Player.m_localPlayer && TryEnsureLocalIdentity(out string steamId)) ? SanitizeBuilder(new PortalBuilder(steamId, ((Character)player).GetHoverName(), steamId, player.GetPlayerID(), 0L)) : new PortalBuilder("", "")); _pendingLocalPlacementPortalIds = new HashSet(); return result; } public static void CompleteLocalPlacement(LocalPortalPlacementState state) { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) HashSet pendingLocalPlacementPortalIds = _pendingLocalPlacementPortalIds; _pendingLocalPlacementPortalIds = null; try { if (!((Object)(object)ZNet.instance != (Object)null) || !ZNet.instance.IsServer() || ZDOMan.instance == null || pendingLocalPlacementPortalIds == null) { return; } ZDOID[] array = pendingLocalPlacementPortalIds.ToArray(); foreach (ZDOID val in array) { ZDO zDO = ZDOMan.instance.GetZDO(val); if (zDO != null && zDO.IsValid()) { ObservePortal(zDO); } } } finally { _activeLocalPlacementBuilder = state.PreviousBuilder; _pendingLocalPlacementPortalIds = state.PreviousPendingPortalIds; } } internal static void TickIdentityRegistry() { if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer() && PortalAccountStore.IsActive && !(Time.realtimeSinceStartup < _nextIdentityReconcileAt)) { _nextIdentityReconcileAt = Time.realtimeSinceStartup + 1f; TryEnsureLocalIdentity(out string _); PublicPortalServerPolicy.BroadcastQuotaStates(); } } internal static bool TryEnsurePeerIdentity(ZNetPeer? peer, out string steamId) { steamId = ""; if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || !PublicPortalData.TryGetPeerSteamId64(peer, out steamId) || !PublicPortalData.TryGetAuthenticatedPeerPlayerId(peer, out var playerId) || !PortalAccountStore.TryRememberIdentity(playerId, steamId, out var added)) { steamId = ""; return false; } if (added) { BackfillBuilderForCreator(playerId, steamId, peer?.m_playerName ?? ""); } return true; } internal static bool TryEnsureLocalIdentity(out string steamId) { steamId = ""; Player localPlayer = Player.m_localPlayer; if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || (Object)(object)localPlayer == (Object)null || !PublicPortalData.TryGetLocalSteamId64(out steamId)) { steamId = ""; return false; } long playerID = localPlayer.GetPlayerID(); if (!PortalAccountStore.TryRememberIdentity(playerID, steamId, out var added)) { steamId = ""; return false; } if (added) { BackfillBuilderForCreator(playerID, steamId, ((Character)localPlayer).GetHoverName()); } return true; } public static bool TryGetAuthoritativeAccess(ZDOID portalId, out PublicPortalAccessMode accessMode, out PortalOwner owner) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) if (!PendingRemovalIds.Contains(portalId) && ServerAuthority.TryGetValue(portalId, out var value)) { bool flag = IsTemporaryPublicAccessExpired(value, GetUtcNowSeconds()); accessMode = ((!flag) ? value.AccessMode : PublicPortalAccessMode.Personal); owner = (flag ? new PortalOwner(value.Builder.AccountId, value.Builder.Name) : value.Owner); return true; } accessMode = PublicPortalAccessMode.Personal; owner = new PortalOwner("", ""); return false; } internal static bool IsAuthoritativeAdminPortal(ZDOID portalId) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) if (!PendingRemovalIds.Contains(portalId) && ServerAuthority.TryGetValue(portalId, out var value)) { return PublicPortalKinds.IsAdminPortalPrefab(value.PrefabHash); } return false; } internal static bool TryGetTemporaryPublicRemainingSeconds(ZDOID portalId, out int remainingSeconds) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) remainingSeconds = 0; if (!ServerAuthority.TryGetValue(portalId, out var value) || value.AccessMode != PublicPortalAccessMode.Public || value.PublicExpiresAtUtcSeconds == 0L) { return false; } long num = value.PublicExpiresAtUtcSeconds - GetUtcNowSeconds(); if (num <= 0) { return false; } remainingSeconds = (int)Math.Min(2147483647L, num); return true; } internal static bool TryAuthorizeTeleport(ZNetPeer? peer, ZDOID sourcePortalId, ZDOID targetPortalId, string reservationId, out PortalTravelAuthorization authorization, out PortalTravelDenial denial) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) authorization = default(PortalTravelAuthorization); denial = new PortalTravelDenial(PortalTravelDenialCode.Denied, "", 0L, 0L); if (!TryCreateRemoteAuthorizationContext(peer, out RecipientContext recipient, out Vector3 playerPosition)) { return false; } return TryAuthorizeTeleport(recipient, playerPosition, sourcePortalId, targetPortalId, requireConnectedPortal: false, reservationId, out authorization, out denial); } internal static bool TryAuthorizeLocalTeleport(ZDOID sourcePortalId, ZDOID targetPortalId, string reservationId, out PortalTravelAuthorization authorization, out PortalTravelDenial denial) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) CreateLocalAuthorizationContext(out RecipientContext recipient, out Vector3 playerPosition); return TryAuthorizeTeleport(recipient, playerPosition, sourcePortalId, targetPortalId, requireConnectedPortal: false, reservationId, out authorization, out denial); } internal static bool TryAuthorizeConnectedTeleport(ZNetPeer? peer, ZDOID sourcePortalId, ZDOID targetPortalId, string reservationId, out PortalTravelAuthorization authorization, out PortalTravelDenial denial) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) authorization = default(PortalTravelAuthorization); denial = new PortalTravelDenial(PortalTravelDenialCode.Denied, "", 0L, 0L); if (!TryCreateRemoteAuthorizationContext(peer, out RecipientContext recipient, out Vector3 playerPosition)) { return false; } return TryAuthorizeTeleport(recipient, playerPosition, sourcePortalId, targetPortalId, requireConnectedPortal: true, reservationId, out authorization, out denial); } internal static bool TryAuthorizeLocalConnectedTeleport(ZDOID sourcePortalId, ZDOID targetPortalId, string reservationId, out PortalTravelAuthorization authorization, out PortalTravelDenial denial) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) CreateLocalAuthorizationContext(out RecipientContext recipient, out Vector3 playerPosition); return TryAuthorizeTeleport(recipient, playerPosition, sourcePortalId, targetPortalId, requireConnectedPortal: true, reservationId, out authorization, out denial); } internal static bool TryAuthorizeMapOpen(ZNetPeer? peer, ZDOID sourcePortalId, out PortalTravelDenial denial) { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) denial = new PortalTravelDenial(PortalTravelDenialCode.Denied, "", 0L, 0L); if (PublicPortalConfig.EnablePortalMap.Value.IsOff() || !TryCreateRemoteAuthorizationContext(peer, out RecipientContext recipient, out Vector3 playerPosition)) { return false; } ZDO sourcePortal; ServerPortalAuthority sourceAuthority; return TryAuthorizePortalSource(recipient, playerPosition, sourcePortalId, requireConnectedPortal: false, out sourcePortal, out sourceAuthority, out denial); } internal static bool TryAuthorizeLocalMapOpen(ZDOID sourcePortalId, out PortalTravelDenial denial) { //IL_0032: 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) denial = new PortalTravelDenial(PortalTravelDenialCode.Denied, "", 0L, 0L); if (PublicPortalConfig.EnablePortalMap.Value.IsOff()) { return false; } CreateLocalAuthorizationContext(out RecipientContext recipient, out Vector3 playerPosition); ZDO sourcePortal; ServerPortalAuthority sourceAuthority; return TryAuthorizePortalSource(recipient, playerPosition, sourcePortalId, requireConnectedPortal: false, out sourcePortal, out sourceAuthority, out denial); } public static bool SetAuthoritativeAccess(ZDO zdo, PublicPortalAccessMode accessMode, string authorizedClanId) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) if (zdo == null || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || PendingRemovalIds.Contains(zdo.m_uid) || !ServerAuthority.TryGetValue(zdo.m_uid, out var value)) { return false; } if (TryNormalizeTemporaryPublicAuthority(value, GetUtcNowSeconds(), out var updatedAuthority, out var _)) { value = updatedAuthority; } string text = SanitizeClanId(authorizedClanId); bool flag = PublicPortalKinds.IsAdminPortalPrefab(value.PrefabHash); if (flag && !IsAllowedAdminPortalMode(accessMode)) { return false; } PortalBuilder builder = (flag ? new PortalBuilder("", "") : value.Builder); if (!flag && !builder.IsValid) { return false; } PortalOwner portalOwner = (flag ? new PortalOwner("", "") : new PortalOwner(builder.AccountId, builder.Name)); if (flag) { text = ""; } if (!flag && accessMode == PublicPortalAccessMode.Invite && value.AccessMode != PublicPortalAccessMode.Invite) { int effectiveInvitePortalLimit = PortalAccountStore.GetEffectiveInvitePortalLimit(builder.AccountId, PublicPortalConfig.MaxInvitePortalsPerAccount.Value); if (effectiveInvitePortalLimit >= 0 && GetBuilderInvitePortalCount(builder.AccountId) >= effectiveInvitePortalLimit) { return false; } } bool flag2 = !flag; if (flag2) { bool flag3 = (uint)(accessMode - 3) <= 1u; flag2 = flag3; } if (flag2) { PortalClanMembership membership; bool flag4 = ClanPortalAccess.TryResolveBuilderMembership(builder, out membership) && membership.HasPrimaryClan; if (accessMode == PublicPortalAccessMode.Clan && !flag4) { return false; } text = (flag4 ? SanitizeClanId(membership.PrimaryClanId) : ""); if (accessMode == PublicPortalAccessMode.Clan && string.IsNullOrWhiteSpace(text)) { return false; } if (accessMode == PublicPortalAccessMode.Clan && (value.AccessMode != PublicPortalAccessMode.Clan || !string.Equals(value.AuthorizedClanId, text, StringComparison.Ordinal))) { int num = Math.Max(-1, Math.Min(10000, PublicPortalConfig.MaxClanPortalsPerClan.Value)); if (num >= 0 && GetClanPortalCount(text) >= num) { return false; } } } long publicExpiresAtUtcSeconds = ((accessMode == PublicPortalAccessMode.Public && value.AccessMode == PublicPortalAccessMode.Public) ? value.PublicExpiresAtUtcSeconds : CreateTemporaryPublicExpirationUtcSeconds(accessMode, builder, value.PrefabHash)); PortalOwner owner = portalOwner; flag2 = (uint)(accessMode - 3) <= 1u; ServerPortalAuthority authority = new ServerPortalAuthority(accessMode, owner, flag2 ? text : "", builder, value.PrefabHash, value.FavoriteId, value.RequiredGlobalKey, publicExpiresAtUtcSeconds); SetAndPersistServerAuthority(zdo, authority, forceSend: true); RefreshAndBroadcast(); return true; } internal static bool SetAuthoritativeAdminPortalTag(ZDO zdo, string tag) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) if (zdo == null || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || PendingRemovalIds.Contains(zdo.m_uid) || !ServerAuthority.TryGetValue(zdo.m_uid, out var value) || !PublicPortalKinds.IsAdminPortalPrefab(value.PrefabHash)) { return false; } AdminPortalTags[zdo.m_uid] = new AdminPortalTagAuthority(Truncate(tag, 10)); zdo.UpdateConnection((ConnectionType)1, ZDOID.None); WriteAuthorityToZdo(zdo, value, forceSend: true); RefreshAndBroadcast(); PublicPortalTaggedConnections.RefreshConnections(); return true; } internal static bool SetAuthoritativeAdminPortalRequiredGlobalKey(ZDO zdo, string requiredGlobalKey) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) if (zdo == null || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || PendingRemovalIds.Contains(zdo.m_uid) || !ServerAuthority.TryGetValue(zdo.m_uid, out var value) || !PublicPortalKinds.IsAdminPortalPrefab(value.PrefabHash) || !PublicPortalData.TryNormalizeRequiredGlobalKey(requiredGlobalKey, out string normalized, out RequiredGlobalKeyValidationFailure _)) { return false; } ServerPortalAuthority authority = new ServerPortalAuthority(value.AccessMode, value.Owner, value.AuthorizedClanId, value.Builder, value.PrefabHash, value.FavoriteId, normalized, value.PublicExpiresAtUtcSeconds); SetAndPersistServerAuthority(zdo, authority, forceSend: true); RefreshAndBroadcast(); return true; } internal static int GetBuilderPortalCount(string builderAccountId) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) if (!PublicPortalData.TryNormalizeSteamId64(builderAccountId, out string steamId) || !ServerPortalsByBuilder.TryGetValue(steamId, out HashSet value)) { return 0; } int num = 0; foreach (ZDOID item in value) { if (!PendingRemovalIds.Contains(item) && ServerAuthority.TryGetValue(item, out var value2) && PublicPortalServerPolicy.IsCountedPortalPrefab(value2.PrefabHash)) { num++; } } return num; } internal static int GetBuilderInvitePortalCount(string builderAccountId) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) if (!PublicPortalData.TryNormalizeSteamId64(builderAccountId, out string steamId) || !ServerPortalsByBuilder.TryGetValue(steamId, out HashSet value)) { return 0; } int num = 0; foreach (ZDOID item in value) { if (!PendingRemovalIds.Contains(item) && ServerAuthority.TryGetValue(item, out var value2) && value2.Builder.IsValid && value2.AccessMode == PublicPortalAccessMode.Invite && !PublicPortalKinds.IsAdminPortalPrefab(value2.PrefabHash)) { num++; } } return num; } internal static int GetClanPortalCount(string clanId) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) string text = SanitizeClanId(clanId); if (string.IsNullOrEmpty(text)) { return 0; } int num = 0; foreach (KeyValuePair item in ServerAuthority) { ServerPortalAuthority value = item.Value; if (!PendingRemovalIds.Contains(item.Key) && value.Builder.IsValid && value.AccessMode == PublicPortalAccessMode.Clan && !PublicPortalKinds.IsAdminPortalPrefab(value.PrefabHash) && string.Equals(value.AuthorizedClanId, text, StringComparison.Ordinal)) { num++; } } return num; } internal static bool TryGetAuthoritativeBuilder(ZDOID portalId, out PortalBuilder builder) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) if (!PendingRemovalIds.Contains(portalId) && ServerAuthority.TryGetValue(portalId, out var value)) { builder = value.Builder; return true; } builder = new PortalBuilder("", ""); return false; } internal static bool MarkPendingRemoval(ZDOID portalId) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) if (((ZDOID)(ref portalId)).IsNone() || !PendingRemovalIds.Add(portalId)) { return false; } MarkServerCatalogDirty(); return true; } internal static void NotifyPortalDestroyed(ZDOID portalId) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) RemoveServerAuthority(portalId); PendingRemovalIds.Remove(portalId); InitialWorldPortalIds.Remove(portalId); MarkServerCatalogDirty(); } internal static bool TryGetClientEntry(ZDOID portalId, out PublicPortalCatalogEntry entry) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) return ClientIndex.TryGetValue(portalId, out entry); } internal static bool TryCanLocalServerUserUsePortal(ZDOID portalId, out bool canUse) { //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) canUse = false; if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || PendingRemovalIds.Contains(portalId) || !ServerAuthority.TryGetValue(portalId, out var value)) { return false; } RecipientContext recipient = CreateRecipientContext(PublicPortalData.LocalOwner(), PortalRulesPlugin.IsAdmin, ResolveLocalMembership()); canUse = CanRecipientUseAuthority(value, recipient); return true; } public static PublicPortalAccessMode GetEffectiveAccessMode(ZDO zdo) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) if (zdo != null) { if (PendingRemovalIds.Contains(zdo.m_uid)) { return PublicPortalAccessMode.Personal; } if (ServerAuthority.TryGetValue(zdo.m_uid, out var value)) { if (!IsTemporaryPublicAccessExpired(value, GetUtcNowSeconds())) { return value.AccessMode; } return PublicPortalAccessMode.Personal; } if (ClientIndex.TryGetValue(zdo.m_uid, out var value2)) { return value2.AccessMode; } } if (zdo == null) { return PublicPortalAccessMode.Personal; } return PublicPortalData.GetAccessMode(zdo); } public static PortalOwner GetEffectiveOwner(ZDO zdo) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) if (zdo != null) { if (PendingRemovalIds.Contains(zdo.m_uid)) { return new PortalOwner("", ""); } if (ServerAuthority.TryGetValue(zdo.m_uid, out var value)) { if (!IsTemporaryPublicAccessExpired(value, GetUtcNowSeconds())) { return value.Owner; } return new PortalOwner(value.Builder.AccountId, value.Builder.Name); } if (ClientIndex.TryGetValue(zdo.m_uid, out var value2)) { return value2.Owner; } } if (zdo == null) { return new PortalOwner("", ""); } return PublicPortalData.GetOwner(zdo); } internal static string GetEffectiveAuthorizedClanId(ZDO zdo) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) if (zdo == null || PendingRemovalIds.Contains(zdo.m_uid)) { return ""; } if (!ServerAuthority.TryGetValue(zdo.m_uid, out var value)) { return SanitizeClanId(PublicPortalData.GetAuthorizedClanId(zdo)); } return value.AuthorizedClanId; } internal static string GetEffectiveRequiredGlobalKey(ZDO zdo) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) if (zdo == null || PendingRemovalIds.Contains(zdo.m_uid)) { return ""; } if (!ServerAuthority.TryGetValue(zdo.m_uid, out var value)) { return PublicPortalData.GetRequiredGlobalKey(zdo); } return value.RequiredGlobalKey; } public static void RefreshAndBroadcast() { if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer()) { bool num = RefreshServerSnapshot(); RefreshLocalRecipientView(num); if (num) { BroadcastSnapshot(); } else { BroadcastChangedViews(); } PublicPortalServerPolicy.BroadcastQuotaStates(); } } internal static void RefreshCountedPortalConfiguration() { //IL_004c: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer()) { return; } if (ZDOMan.instance != null) { foreach (ZDO portal in ZDOMan.instance.GetPortals()) { if (portal != null && portal.IsValid()) { InitialWorldPortalIds.Add(portal.m_uid); } } } RefreshAndBroadcast(); } internal static void RefreshTemporaryPublicConfiguration() { //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || ZDOMan.instance == null || !_authorityBackfilled) { return; } long utcNowSeconds = GetUtcNowSeconds(); int num = 0; KeyValuePair[] array = ServerAuthority.ToArray(); for (int i = 0; i < array.Length; i++) { KeyValuePair keyValuePair = array[i]; if (PendingRemovalIds.Contains(keyValuePair.Key)) { continue; } ZDO zDO = ZDOMan.instance.GetZDO(keyValuePair.Key); if (zDO != null && zDO.IsValid()) { long num2 = CreateTemporaryPublicExpirationUtcSeconds(keyValuePair.Value.AccessMode, keyValuePair.Value.Builder, keyValuePair.Value.PrefabHash, utcNowSeconds); if (num2 != keyValuePair.Value.PublicExpiresAtUtcSeconds) { ServerPortalAuthority authority = new ServerPortalAuthority(keyValuePair.Value.AccessMode, keyValuePair.Value.Owner, keyValuePair.Value.AuthorizedClanId, keyValuePair.Value.Builder, keyValuePair.Value.PrefabHash, keyValuePair.Value.FavoriteId, keyValuePair.Value.RequiredGlobalKey, num2); SetAndPersistServerAuthority(zDO, authority, forceSend: true); num++; } } } if (num > 0) { PortalRulesPlugin.PortalRulesLogger.LogInfo((object)$"Reset temporary Public access for {num} portal(s) after a duration configuration change."); } RefreshAndBroadcast(); } internal static void RefreshInviteCooldownConfiguration() { if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer() && _hasSnapshot) { RefreshLocalClientEntries(forceUpdate: true); BroadcastSnapshot(); } } private static void MarkServerCatalogDirty(ZDO portal) { if (PublicPortalKinds.IsHandledPortal(portal) || PublicPortalServerPolicy.IsCountedPortalPrefab(portal.GetPrefab())) { MarkServerCatalogDirty(); } } private static void MarkServerCatalogDirty() { _serverCatalogDirty = true; Game registeredGame = _registeredGame; ZNet sessionZNet = _sessionZNet; if (!_serverDirtyFlushScheduled && _worldLoaded && !((Object)(object)registeredGame == (Object)null) && !((Object)(object)sessionZNet == (Object)null) && ZNet.instance == sessionZNet && sessionZNet.IsServer()) { _serverDirtyFlushScheduled = true; ((MonoBehaviour)registeredGame).StartCoroutine(FlushDirtyServerCatalogNextFrame(registeredGame, sessionZNet, _sessionGeneration)); } } private static IEnumerator FlushDirtyServerCatalogNextFrame(Game game, ZNet sessionZNet, long generation) { yield return null; if (generation == _sessionGeneration && _registeredGame == game && _sessionZNet == sessionZNet && ZNet.instance == sessionZNet && _worldLoaded && sessionZNet.IsServer()) { _serverDirtyFlushScheduled = false; if (_serverCatalogDirty) { _serverCatalogDirty = false; RefreshAndBroadcast(); } } } internal static void RefreshClanViews() { if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer() && _hasSnapshot) { RefreshLocalRecipientView(catalogChanged: false); BroadcastChangedViews(); } } public static void ReconcileAuthorityToZdos() { //IL_003f: 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) if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || ZDOMan.instance == null) { return; } KeyValuePair[] array = ServerAuthority.ToArray(); for (int i = 0; i < array.Length; i++) { KeyValuePair keyValuePair = array[i]; if (!PendingRemovalIds.Contains(keyValuePair.Key)) { ZDO zDO = ZDOMan.instance.GetZDO(keyValuePair.Key); if (zDO != null && zDO.IsValid() && !ZdoMatchesAuthority(zDO, keyValuePair.Value)) { WriteAuthorityToZdo(zDO, keyValuePair.Value, forceSend: false); } } } } private static IEnumerator ServerRefreshLoop(Game game) { yield return (object)new WaitForSeconds(1f); while (_serverLoopGame == game && _registeredGame == game && (Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer()) { RefreshAndBroadcast(); yield return (object)new WaitForSeconds(5f); } } private static void OnCatalogRequest(ZRpc rpc, int knownRevision, long knownViewToken) { ZNet instance = ZNet.instance; if ((Object)(object)instance == (Object)null || !instance.IsServer()) { return; } ZNetPeer val = PublicPortalData.FindPeer(instance, rpc); if (val == null || !val.IsReady()) { PortalRulesPlugin.PortalRulesLogger.LogDebug((object)"Rejected a portal catalog request from an unauthenticated connection."); return; } float realtimeSinceStartup = Time.realtimeSinceStartup; float value2; if (_hasSnapshot && knownRevision == _serverRevision && LastSentViews.TryGetValue(rpc, out var value) && knownViewToken != 0L && knownViewToken == value.ViewToken && PublicPortalData.TryGetPeerOwner(val, out var owner) && value.MatchesRecipient(_serverRevision, SanitizeOwner(owner).Id, PublicPortalData.IsPeerAdmin(instance, val))) { LastSentViews[rpc] = value.WithAcknowledgement(); } else if (!LastServerRequestAt.TryGetValue(rpc, out value2) || !(realtimeSinceStartup - value2 < 30f)) { LastServerRequestAt[rpc] = realtimeSinceStartup; if (!_hasSnapshot) { RefreshServerSnapshot(); } RecipientContext recipient = CreateRecipientContext(val); ulong fingerprint; List visibleEntries = BuildRecipientEntries(recipient, out fingerprint); SendSnapshot(rpc, val, recipient, visibleEntries, fingerprint); } } private static void OnCatalogReceived(ZRpc rpc, ZPackage package) { if ((Object)(object)ZNet.instance == (Object)null || ZNet.instance.IsServer() || rpc != ZNet.instance.GetServerRPC()) { PortalRulesPlugin.PortalRulesLogger.LogWarning((object)"Ignored portal catalog data from a non-server connection."); return; } try { byte b = package.ReadByte(); if (b != 7) { PortalRulesPlugin.PortalRulesLogger.LogWarning((object)$"Ignored portal catalog format {b}; expected {(byte)7}."); return; } int num = package.ReadInt(); long num2 = package.ReadLong(); int num3 = package.ReadInt(); int num4 = package.ReadInt(); int num5 = package.ReadInt(); string text = package.ReadString(); bool flag = package.ReadBool(); long num6 = package.ReadLong(); int num7 = package.ReadInt(); int num8 = Math.Max(1, (num5 + 96 - 1) / 96); if (num < 0 || num2 == 0L || num < _clientRevision || num3 < 0 || num4 < 1 || num4 > 1042 || num3 >= num4 || num5 < 0 || num5 > 100000 || num4 != num8 || text.Length > 128 || num7 < 0 || num7 > 96 || num7 > num5 || num6 <= 0 || num6 > 253402300799L) { PortalRulesPlugin.PortalRulesLogger.LogWarning((object)($"Ignored malformed portal catalog chunk {num3}/{num4} " + $"for revision {num}.")); ResetPendingSnapshot(); return; } PendingSnapshot pendingSnapshot = _pendingSnapshot; if (num3 == 0) { pendingSnapshot = (_pendingSnapshot = new PendingSnapshot(num, num2, num4, num5, text, flag, num6)); } else if (pendingSnapshot == null || num != pendingSnapshot.Revision || num2 != pendingSnapshot.ViewToken || num4 != pendingSnapshot.ChunkCount || num3 != pendingSnapshot.NextChunk || num5 != pendingSnapshot.TotalCount || !string.Equals(text, pendingSnapshot.RecipientOwnerId, StringComparison.Ordinal) || flag != pendingSnapshot.RecipientIsAdmin || num6 != pendingSnapshot.ServerUtcAtSend) { PortalRulesPlugin.PortalRulesLogger.LogWarning((object)$"Ignored out-of-sequence portal catalog chunk {num3}/{num4}."); ResetPendingSnapshot(); return; } PendingSnapshot pendingSnapshot2 = pendingSnapshot; List list = new List(num7); for (int i = 0; i < num7; i++) { if (!TryReadCatalogEntry(package, pendingSnapshot2.FavoriteIds, out var entry)) { PortalRulesPlugin.PortalRulesLogger.LogWarning((object)$"Ignored malformed portal catalog entry at index {i}."); ResetPendingSnapshot(); return; } list.Add(entry); } pendingSnapshot2.Entries.AddRange(list); pendingSnapshot2.NextChunk++; if (pendingSnapshot2.NextChunk < pendingSnapshot2.ChunkCount) { return; } if (pendingSnapshot2.Entries.Count != pendingSnapshot2.TotalCount) { PortalRulesPlugin.PortalRulesLogger.LogWarning((object)($"Ignored incomplete portal catalog revision {pendingSnapshot2.Revision}: " + $"expected {pendingSnapshot2.TotalCount}, " + $"received {pendingSnapshot2.Entries.Count}.")); ResetPendingSnapshot(); return; } List entries = pendingSnapshot2.Entries; int revision = pendingSnapshot2.Revision; long viewToken = pendingSnapshot2.ViewToken; string recipientOwnerId = pendingSnapshot2.RecipientOwnerId; bool recipientIsAdmin = pendingSnapshot2.RecipientIsAdmin; long serverUtcAtSend = pendingSnapshot2.ServerUtcAtSend; long firstChunkReceivedTimestamp = pendingSnapshot2.FirstChunkReceivedTimestamp; ResetPendingSnapshot(); SynchronizeClientServerClock(serverUtcAtSend, firstChunkReceivedTimestamp); bool flag2 = !string.Equals(PublicPortalData.ServerAssignedOwnerId, recipientOwnerId, StringComparison.Ordinal) || PublicPortalData.ServerAssignedIsAdmin != recipientIsAdmin; if (_hasSnapshot && revision == _clientRevision && SnapshotsMatch(ClientEntries, entries)) { PublicPortalData.SetServerAssignedOwnerId(recipientOwnerId); PublicPortalData.SetServerAssignedIsAdmin(recipientIsAdmin); _clientViewToken = viewToken; if (flag2) { PublicPortalCatalog.Updated?.Invoke(); } AcknowledgeSnapshot(revision, viewToken); } else { _clientRevision = revision; _clientViewToken = viewToken; _hasSnapshot = true; PublicPortalData.SetServerAssignedOwnerId(recipientOwnerId); PublicPortalData.SetServerAssignedIsAdmin(recipientIsAdmin); ClientEntries.Clear(); ClientEntries.AddRange(entries); RebuildClientIndex(); PublicPortalCatalog.Updated?.Invoke(); AcknowledgeSnapshot(revision, viewToken); } } catch (Exception ex) { ResetPendingSnapshot(); PortalRulesPlugin.PortalRulesLogger.LogError((object)("Failed to read portal catalog: " + ex.Message)); } } private static bool RefreshServerSnapshot() { //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_01a2: Unknown result type (might be due to invalid IL or missing references) //IL_0180: Unknown result type (might be due to invalid IL or missing references) //IL_03d0: Unknown result type (might be due to invalid IL or missing references) //IL_01ef: Unknown result type (might be due to invalid IL or missing references) //IL_0423: Unknown result type (might be due to invalid IL or missing references) //IL_0428: Unknown result type (might be due to invalid IL or missing references) //IL_042f: Unknown result type (might be due to invalid IL or missing references) //IL_026c: Unknown result type (might be due to invalid IL or missing references) //IL_0480: Unknown result type (might be due to invalid IL or missing references) //IL_0485: Unknown result type (might be due to invalid IL or missing references) //IL_048c: Unknown result type (might be due to invalid IL or missing references) //IL_0298: Unknown result type (might be due to invalid IL or missing references) //IL_029d: Unknown result type (might be due to invalid IL or missing references) //IL_02a1: Unknown result type (might be due to invalid IL or missing references) //IL_02a6: Unknown result type (might be due to invalid IL or missing references) //IL_02a8: Unknown result type (might be due to invalid IL or missing references) //IL_02c8: Unknown result type (might be due to invalid IL or missing references) //IL_02b1: Unknown result type (might be due to invalid IL or missing references) //IL_02e2: Unknown result type (might be due to invalid IL or missing references) //IL_0301: Unknown result type (might be due to invalid IL or missing references) //IL_0303: Unknown result type (might be due to invalid IL or missing references) if (!_worldLoaded || ZDOMan.instance == null) { return false; } PortalAccountStore.BeginServerSession(); AdminPortalBiomeDefaults.BeginServerSession(); if (!AdminPortalBiomeDefaults.IsBiomeRegistryReady) { return false; } AdminPortalBiomeDefaults.EnsureTemplateReady(); TryEnsureLocalIdentity(out string steamId); if ((Object)(object)ZNet.instance != (Object)null) { ZNetPeer[] array = ZNet.instance.GetPeers().ToArray(); foreach (ZNetPeer val in array) { if (val != null && val.IsReady()) { TryEnsurePeerIdentity(val, out steamId); } } } bool flag = !_authorityBackfilled; long utcNowSeconds = GetUtcNowSeconds(); int num = 0; int num2 = 0; HashSet livePortalIds = new HashSet(); List list = new List(); ZDO[] array2 = (from portal in ZDOMan.instance.GetPortals() where portal != null orderby portal.m_uid select portal).ToArray(); SeedNextBuildSequence(array2); ZDO[] array3 = array2; foreach (ZDO val2 in array3) { if (val2 == null || !val2.IsValid()) { continue; } livePortalIds.Add(val2.m_uid); if (PendingRemovalIds.Contains(val2.m_uid)) { continue; } if (flag) { InitialWorldPortalIds.Add(val2.m_uid); } if (PublicPortalKinds.IsAdminPortalPrefab(val2.GetPrefab()) && !ServerAuthority.ContainsKey(val2.m_uid) && !CanInitializeAdminPortalAuthority(val2)) { continue; } bool flag2 = false; if (ServerAuthority.TryGetValue(val2.m_uid, out var value)) { if (TryBackfillAuthorityBuilder(val2, value, out var updatedAuthority)) { value = updatedAuthority; flag2 = true; } } else { if (!PublicPortalKinds.IsHandledPortal(val2) && !PublicPortalServerPolicy.IsCountedPortalPrefab(val2.GetPrefab())) { continue; } value = ((flag || InitialWorldPortalIds.Contains(val2.m_uid)) ? ReadInitialAuthority(val2) : CreateAuthorityForNewPortal(val2)); flag2 = true; } if (TryNormalizeTemporaryPublicAuthority(value, utcNowSeconds, out var updatedAuthority2, out var revertedToPersonal)) { value = updatedAuthority2; flag2 = true; if (revertedToPersonal) { num++; } } if (TryNormalizeDisabledInviteAuthority(value, out var updatedAuthority3)) { value = updatedAuthority3; flag2 = true; num2++; } if (flag2) { SetAndPersistServerAuthority(val2, value, forceSend: true); } else if (!ZdoMatchesAuthority(val2, value)) { PortalRulesPlugin.PortalRulesLogger.LogDebug((object)$"Restoring server-authoritative access data for portal {val2.m_uid}."); WriteAuthorityToZdo(val2, value, forceSend: true); } if (PublicPortalKinds.IsHandledPortal(val2)) { Vector3 position = val2.GetPosition(); Quaternion rotation = val2.GetRotation(); if (!IsSafePortalPosition(position) || !TryNormalizePortalRotation(rotation, out var normalized)) { PortalRulesPlugin.PortalRulesLogger.LogWarning((object)$"Skipped portal {val2.m_uid} with invalid transform data."); } else { list.Add(new PublicPortalCatalogEntry(val2.m_uid, value.FavoriteId, value.PrefabHash, PublicPortalKinds.PrefabAllowsAllItems(value.PrefabHash), position, normalized, Truncate(val2.GetString(ZDOVars.s_tag, ""), 256), value.AccessMode, value.Owner, 0, 0, 0, 0, 0, 0L, 0L)); } } } if (num > 0) { PortalRulesPlugin.PortalRulesLogger.LogInfo((object)$"Returned {num} expired Public portal(s) to their Builder's Personal access."); } if (num2 > 0) { PortalRulesPlugin.PortalRulesLogger.LogInfo((object)$"Returned {num2} Invite portal(s) with an effective zero Invite limit to their Builder's Personal access."); } ZDOID[] array4 = ServerAuthority.Keys.Where((ZDOID id) => !livePortalIds.Contains(id)).ToArray(); for (int i = 0; i < array4.Length; i++) { RemoveServerAuthority(array4[i]); } array4 = InitialWorldPortalIds.Where((ZDOID id) => !livePortalIds.Contains(id)).ToArray(); foreach (ZDOID item in array4) { InitialWorldPortalIds.Remove(item); } array4 = PendingRemovalIds.Where((ZDOID id) => !livePortalIds.Contains(id)).ToArray(); foreach (ZDOID item2 in array4) { PendingRemovalIds.Remove(item2); } ApplyModeDisplayMetadata(list); list.Sort(delegate(PublicPortalCatalogEntry left, PublicPortalCatalogEntry right) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) ZDOID id = left.Id; return ((ZDOID)(ref id)).CompareTo(right.Id); }); if (list.Count > 100000) { PortalRulesPlugin.PortalRulesLogger.LogWarning((object)$"Portal catalog exceeded {100000} entries; truncating the snapshot."); list.RemoveRange(100000, list.Count - 100000); } _authorityBackfilled = true; bool num3 = !_hasSnapshot; _hasSnapshot = true; if (!num3 && SnapshotsMatch(ServerEntries, list)) { return false; } ServerEntries.Clear(); ServerEntries.AddRange(list); _serverRevision++; return true; } private static ServerPortalAuthority ReadInitialAuthority(ZDO portal) { //IL_013f: Unknown result type (might be due to invalid IL or missing references) //IL_01d9: Unknown result type (might be due to invalid IL or missing references) string text = ReadInitialFavoriteId(portal); int num = portal.GetInt("PortalRules AuthorityVersion", 0); if (PublicPortalKinds.IsAdminPortalPrefab(portal.GetPrefab())) { string favoriteId = text; string requiredGlobalKey = ResolveInitialAdminRequiredGlobalKey(portal); return CreateAdminPortalAuthority(portal, null, favoriteId, requiredGlobalKey); } int num2 = portal.GetInt("PortalRules AccessMode", -1); bool flag = Enum.IsDefined(typeof(PublicPortalAccessMode), num2) && (num2 != 5 || num >= 6); PortalOwner owner = SanitizeOwner(PublicPortalData.GetOwner(portal)); bool flag2 = num >= 1; bool flag3 = num >= 3 && owner.IsValid; if (flag3) { bool flag4 = (uint)(num2 - 3) <= 1u; flag3 = flag4; } string authorizedClanId = (flag3 ? SanitizeClanId(PublicPortalData.GetAuthorizedClanId(portal)) : ""); bool num3 = portal.GetInt("PortalRules BuilderAuthorityVersion", 0) >= 2; PortalBuilder builder = (num3 ? SanitizeBuilder(PublicPortalData.GetBuilder(portal)) : new PortalBuilder("", "")); int num4 = (num3 ? portal.GetInt("PortalRules AuthorizedPrefabHash", portal.GetPrefab()) : portal.GetPrefab()); if (num4 == 0) { num4 = portal.GetPrefab(); } if (!flag2 && owner.IsValid) { PortalRulesPlugin.PortalRulesLogger.LogWarning((object)($"Portal {portal.m_uid} has legacy ownership that was not server-authorized; " + "a server admin must reclaim it.")); owner = new PortalOwner("", ""); } if (!builder.IsValid && TryCreateRegistryBuilder(portal, out var builder2)) { builder = builder2; } int num5; switch (num2) { case 2: num5 = ((owner.IsValid || portal.GetLong(ZDOVars.s_creator, 0L) != 0) ? 1 : 0); break; default: num5 = 0; break; case 3: case 4: case 5: num5 = 1; break; } bool flag5 = (byte)num5 != 0; if (flag && flag5 && !builder.IsValid) { PortalRulesPlugin.PortalRulesLogger.LogWarning((object)($"Portal {portal.m_uid} had {(PublicPortalAccessMode)num2} access " + "without valid current Builder authority; it was changed to Personal.")); num2 = 0; authorizedClanId = ""; } if (flag && (flag2 || owner.IsValid)) { return new ServerPortalAuthority((PublicPortalAccessMode)num2, owner, authorizedClanId, builder, num4, text, "", (num >= 5) ? PublicPortalData.GetPublicExpiresAtUtcSeconds(portal) : 0); } if (flag) { return new ServerPortalAuthority((PublicPortalAccessMode)num2, new PortalOwner("", ""), "", builder, num4, text, "", (num >= 5) ? PublicPortalData.GetPublicExpiresAtUtcSeconds(portal) : 0); } return new ServerPortalAuthority(PublicPortalAccessMode.Personal, new PortalOwner("", ""), "", builder, num4, text, "", 0L); } private static bool TryCreateRegistryBuilder(ZDO portal, out PortalBuilder builder, string fallbackName = "") { builder = new PortalBuilder("", ""); if (portal == null || PublicPortalKinds.IsAdminPortalPrefab(portal.GetPrefab())) { return false; } long num = portal.GetLong(ZDOVars.s_creator, 0L); if (num == 0L || !PortalAccountStore.TryResolveSteamId(num, out string steamId)) { return false; } string text = portal.GetString(ZDOVars.s_creatorName, ""); builder = SanitizeBuilder(new PortalBuilder(steamId, string.IsNullOrWhiteSpace(text) ? fallbackName : text, steamId, num, 0L)); return builder.IsValid; } private static bool TryBackfillAuthorityBuilder(ZDO portal, ServerPortalAuthority authority, out ServerPortalAuthority updatedAuthority, string fallbackName = "") { updatedAuthority = authority; if (authority.Builder.IsValid || PublicPortalKinds.IsAdminPortalPrefab(authority.PrefabHash) || !TryCreateRegistryBuilder(portal, out var builder, fallbackName)) { return false; } updatedAuthority = new ServerPortalAuthority(authority.AccessMode, authority.Owner, authority.AuthorizedClanId, builder, authority.PrefabHash, authority.FavoriteId, authority.RequiredGlobalKey, authority.PublicExpiresAtUtcSeconds); return true; } private static void BackfillBuilderForCreator(long playerId, string steamId, string fallbackName) { //IL_0069: Unknown result type (might be due to invalid IL or missing references) if (playerId == 0L || ZDOMan.instance == null || !PublicPortalData.TryNormalizeSteamId64(steamId, out string steamId2)) { return; } int num = 0; KeyValuePair[] array = ServerAuthority.ToArray(); for (int i = 0; i < array.Length; i++) { KeyValuePair keyValuePair = array[i]; if (keyValuePair.Value.Builder.IsValid || PublicPortalKinds.IsAdminPortalPrefab(keyValuePair.Value.PrefabHash)) { continue; } ZDO zDO = ZDOMan.instance.GetZDO(keyValuePair.Key); if (zDO == null || !zDO.IsValid() || zDO.GetLong(ZDOVars.s_creator, 0L) != playerId) { continue; } string text = zDO.GetString(ZDOVars.s_creatorName, ""); PortalBuilder builder = SanitizeBuilder(new PortalBuilder(steamId2, string.IsNullOrWhiteSpace(text) ? fallbackName : text, steamId2, playerId, 0L)); if (builder.IsValid) { ServerPortalAuthority authority = new ServerPortalAuthority(keyValuePair.Value.AccessMode, keyValuePair.Value.Owner, keyValuePair.Value.AuthorizedClanId, builder, keyValuePair.Value.PrefabHash, keyValuePair.Value.FavoriteId, keyValuePair.Value.RequiredGlobalKey, keyValuePair.Value.PublicExpiresAtUtcSeconds); if (TryNormalizeTemporaryPublicAuthority(authority, GetUtcNowSeconds(), out var updatedAuthority, out var _)) { authority = updatedAuthority; } SetAndPersistServerAuthority(zDO, authority, forceSend: true); num++; } } if (num > 0) { PortalRulesPlugin.PortalRulesLogger.LogInfo((object)($"Attributed {num} existing portal(s) from playerID {playerId} " + "to SteamID64 " + steamId2 + ".")); MarkServerCatalogDirty(); } } private static ServerPortalAuthority CreateAuthorityForNewPortal(ZDO portal) { if (PublicPortalKinds.IsAdminPortalPrefab(portal.GetPrefab())) { return CreateAdminPortalAuthority(portal, PublicPortalAccessMode.Public, CreateServerFavoriteId(), ResolveInitialAdminRequiredGlobalKey(portal)); } if (TryCreateServerLocalCreatorlessBlueprintAuthority(portal, out var authority)) { return authority; } return new ServerPortalAuthority(PublicPortalAccessMode.Personal, new PortalOwner("", ""), "", new PortalBuilder("", ""), portal.GetPrefab(), CreateServerFavoriteId(), "", 0L); } private static bool TryCreateServerLocalCreatorlessBlueprintAuthority(ZDO portal, out ServerPortalAuthority authority) { //IL_00e0: Unknown result type (might be due to invalid IL or missing references) authority = default(ServerPortalAuthority); int prefab = portal.GetPrefab(); if (_activePortalSync != null || ZDOMan.instance == null || PublicPortalKinds.IsAdminPortalPrefab(prefab) || portal.GetLong(ZDOVars.s_creator, 0L) != 0L || ((ZDOID)(ref portal.m_uid)).UserID != ZDOMan.GetSessionID() || portal.GetInt("PortalRules AuthorityVersion", 0) != 6 || portal.GetInt("PortalRules BuilderAuthorityVersion", 0) != 2 || portal.GetInt("PortalRules AuthorizedPrefabHash", 0) != prefab) { return false; } int num = portal.GetInt("PortalRules AccessMode", -1); if (num != 2) { return false; } PublicPortalAccessMode publicPortalAccessMode = (PublicPortalAccessMode)num; authority = new ServerPortalAuthority(publicPortalAccessMode, new PortalOwner("", ""), "", new PortalBuilder("", ""), prefab, CreateServerFavoriteId(), "", 0L); PortalRulesPlugin.PortalRulesLogger.LogDebug((object)($"Restored {publicPortalAccessMode} access for server-created creatorless blueprint " + $"portal {portal.m_uid}; copied identity and favorite data were discarded.")); return true; } private static string ReadInitialFavoriteId(ZDO portal) { //IL_003d: Unknown result type (might be due to invalid IL or missing references) string text = portal.GetString("PortalRules FavoriteId", ""); if (PublicPortalData.TryNormalizeFavoriteId(text, out string normalizedFavoriteId) && !ServerPortalsByFavoriteId.ContainsKey(normalizedFavoriteId)) { return normalizedFavoriteId; } if (!string.IsNullOrWhiteSpace(text)) { PortalRulesPlugin.PortalRulesLogger.LogWarning((object)($"Portal {portal.m_uid} had an invalid or duplicate favorite ID; " + "the server assigned a new one.")); } return CreateServerFavoriteId(); } private static string CreateServerFavoriteId() { string text; do { text = Guid.NewGuid().ToString("N"); } while (ServerPortalsByFavoriteId.ContainsKey(text)); return text; } private static bool ZdoMatchesAuthority(ZDO portal, ServerPortalAuthority authority) { //IL_0092: Unknown result type (might be due to invalid IL or missing references) PortalOwner owner = PublicPortalData.GetOwner(portal); PortalBuilder builder = PublicPortalData.GetBuilder(portal); bool num = !PublicPortalKinds.IsAdminPortalPrefab(authority.PrefabHash) || (portal.GetLong(ZDOVars.s_creator, 0L) == 0L && string.IsNullOrEmpty(portal.GetString(ZDOVars.s_creatorName, ""))); bool flag = !PublicPortalKinds.IsAdminPortalPrefab(authority.PrefabHash) || ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer() && portal.GetOwner() == ZDOMan.GetSessionID()); AdminPortalTagAuthority value; bool flag2 = !PublicPortalKinds.IsAdminPortalPrefab(authority.PrefabHash) || (AdminPortalTags.TryGetValue(portal.m_uid, out value) && string.Equals(portal.GetString(ZDOVars.s_tag, ""), value.Tag, StringComparison.Ordinal) && string.IsNullOrEmpty(portal.GetString(ZDOVars.s_tagauthor, ""))); bool flag3 = string.Equals(portal.GetString("PortalRules RequiredGlobalKey", ""), authority.RequiredGlobalKey, StringComparison.Ordinal); bool flag4 = PublicPortalData.GetPublicExpiresAtUtcSeconds(portal) == authority.PublicExpiresAtUtcSeconds; if (num && flag && flag2 && flag3 && flag4 && string.Equals(portal.GetString("PortalRules FavoriteId", ""), authority.FavoriteId, StringComparison.Ordinal) && portal.GetInt("PortalRules AuthorityVersion", 0) >= 6 && portal.GetInt("PortalRules BuilderAuthorityVersion", 0) >= 2 && portal.GetInt("PortalRules AccessMode", -1) == (int)authority.AccessMode && string.Equals(owner.Id, authority.Owner.Id, StringComparison.Ordinal) && string.Equals(owner.Name, authority.Owner.Name, StringComparison.Ordinal) && string.Equals(SanitizeClanId(PublicPortalData.GetAuthorizedClanId(portal)), authority.AuthorizedClanId, StringComparison.Ordinal) && string.Equals(builder.AccountId, authority.Builder.AccountId, StringComparison.Ordinal) && string.Equals(builder.Name, authority.Builder.Name, StringComparison.Ordinal) && string.Equals(builder.PlatformId, authority.Builder.PlatformId, StringComparison.Ordinal) && builder.CharacterPlayerId == authority.Builder.CharacterPlayerId && builder.BuildSequence == authority.Builder.BuildSequence && portal.GetInt("PortalRules AuthorizedPrefabHash", 0) == authority.PrefabHash) { return portal.GetPrefab() == authority.PrefabHash; } return false; } private static void WriteAuthorityToZdo(ZDO portal, ServerPortalAuthority authority, bool forceSend) { //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_010e: Unknown result type (might be due to invalid IL or missing references) portal.SetPrefab(authority.PrefabHash); PublicPortalData.SetAccessMode(portal, authority.AccessMode, authority.Owner); PublicPortalData.SetAuthorizedClanId(portal, authority.AuthorizedClanId); PublicPortalData.SetBuilder(portal, authority.Builder, authority.PrefabHash); PublicPortalData.SetFavoriteId(portal, authority.FavoriteId); PublicPortalData.SetRequiredGlobalKey(portal, authority.RequiredGlobalKey); PublicPortalData.SetPublicExpiresAtUtcSeconds(portal, authority.PublicExpiresAtUtcSeconds); if (PublicPortalKinds.IsAdminPortalPrefab(authority.PrefabHash)) { EnsureAdminPortalTagAuthority(portal); AdminPortalTagAuthority adminPortalTagAuthority = AdminPortalTags[portal.m_uid]; portal.Set(ZDOVars.s_creator, 0L); portal.Set(ZDOVars.s_creatorName, ""); portal.Set(ZDOVars.s_tag, adminPortalTagAuthority.Tag); portal.Set(ZDOVars.s_tagauthor, ""); if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer()) { portal.SetOwner(ZDOMan.GetSessionID()); } } portal.Set("PortalRules AuthorityVersion", 6); portal.Set("PortalRules BuilderAuthorityVersion", 2); if (forceSend && ZDOMan.instance != null) { ZDOMan.instance.ForceSendZDO(portal.m_uid); } } private static void SetAndPersistServerAuthority(ZDO portal, ServerPortalAuthority authority, bool forceSend) { //IL_0033: Unknown result type (might be due to invalid IL or missing references) if (PublicPortalKinds.IsAdminPortalPrefab(portal.GetPrefab())) { EnsureAdminPortalTagAuthority(portal); authority = CreateAdminPortalAuthority(portal, authority.AccessMode, authority.FavoriteId, authority.RequiredGlobalKey); } authority = SetServerAuthority(portal.m_uid, authority); WriteAuthorityToZdo(portal, authority, forceSend); } private static ServerPortalAuthority CreateAdminPortalAuthority(ZDO portal, PublicPortalAccessMode? requestedMode = null, string? favoriteId = null, string? requiredGlobalKey = null) { PublicPortalAccessMode publicPortalAccessMode = (PublicPortalAccessMode)(((int?)requestedMode) ?? portal.GetInt("PortalRules AccessMode", 2)); if (!IsAllowedAdminPortalMode(publicPortalAccessMode)) { publicPortalAccessMode = PublicPortalAccessMode.Public; } string normalizedFavoriteId; return new ServerPortalAuthority(publicPortalAccessMode, new PortalOwner("", ""), "", new PortalBuilder("", ""), portal.GetPrefab(), PublicPortalData.TryNormalizeFavoriteId(favoriteId, out normalizedFavoriteId) ? normalizedFavoriteId : CreateServerFavoriteId(), requiredGlobalKey ?? "", 0L); } private static string ResolveInitialAdminRequiredGlobalKey(ZDO portal) { //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) string text = default(string); if (portal.GetString("PortalRules RequiredGlobalKey", ref text)) { if (PublicPortalData.TryNormalizeRequiredGlobalKey(text, out string normalized, out RequiredGlobalKeyValidationFailure failure)) { return normalized; } PortalRulesPlugin.PortalRulesLogger.LogError((object)($"Admin portal {portal.m_uid} has an invalid explicit Required GlobalKey " + $"({failure}); preserving it so access fails closed.")); return (text ?? "").Trim(); } if (!AdminPortalBiomeDefaults.TryResolve(portal.GetPosition(), out string requiredGlobalKey)) { return ""; } return requiredGlobalKey; } private static bool CanInitializeAdminPortalAuthority(ZDO portal) { string text = default(string); if (!portal.GetString("PortalRules RequiredGlobalKey", ref text)) { return AdminPortalBiomeDefaults.HasValidSnapshot; } return true; } private static bool IsAllowedAdminPortalMode(PublicPortalAccessMode mode) { if ((uint)(mode - 1) <= 1u || mode == PublicPortalAccessMode.Tagged) { return true; } return false; } private static long GetUtcNowSeconds() { return DateTimeOffset.UtcNow.ToUnixTimeSeconds(); } private static long CreateTemporaryPublicExpirationUtcSeconds(PublicPortalAccessMode accessMode, PortalBuilder builder, int prefabHash) { return CreateTemporaryPublicExpirationUtcSeconds(accessMode, builder, prefabHash, GetUtcNowSeconds()); } private static long CreateTemporaryPublicExpirationUtcSeconds(PublicPortalAccessMode accessMode, PortalBuilder builder, int prefabHash, long nowUtcSeconds) { int value = PublicPortalConfig.PublicAccessDurationSeconds.Value; if (accessMode != PublicPortalAccessMode.Public || !builder.IsValid || PublicPortalKinds.IsAdminPortalPrefab(prefabHash) || value <= 0) { return 0L; } return Math.Min(253402300799L, nowUtcSeconds + value); } private static bool IsTemporaryPublicAccessExpired(ServerPortalAuthority authority, long nowUtcSeconds) { if (PublicPortalConfig.PublicAccessDurationSeconds.Value > 0 && authority.AccessMode == PublicPortalAccessMode.Public && authority.Builder.IsValid && !PublicPortalKinds.IsAdminPortalPrefab(authority.PrefabHash)) { if (authority.PublicExpiresAtUtcSeconds != 0L) { return authority.PublicExpiresAtUtcSeconds <= nowUtcSeconds; } return true; } return false; } private static bool TryNormalizeTemporaryPublicAuthority(ServerPortalAuthority authority, long nowUtcSeconds, out ServerPortalAuthority updatedAuthority, out bool revertedToPersonal) { updatedAuthority = authority; revertedToPersonal = false; if (authority.AccessMode != PublicPortalAccessMode.Public || !authority.Builder.IsValid || PublicPortalKinds.IsAdminPortalPrefab(authority.PrefabHash) || PublicPortalConfig.PublicAccessDurationSeconds.Value <= 0) { if (authority.PublicExpiresAtUtcSeconds == 0L) { return false; } updatedAuthority = new ServerPortalAuthority(authority.AccessMode, authority.Owner, authority.AuthorizedClanId, authority.Builder, authority.PrefabHash, authority.FavoriteId, authority.RequiredGlobalKey, 0L); return true; } if (!IsTemporaryPublicAccessExpired(authority, nowUtcSeconds)) { return false; } updatedAuthority = new ServerPortalAuthority(PublicPortalAccessMode.Personal, new PortalOwner(authority.Builder.AccountId, authority.Builder.Name), "", authority.Builder, authority.PrefabHash, authority.FavoriteId, authority.RequiredGlobalKey, 0L); revertedToPersonal = true; return true; } private static bool TryNormalizeDisabledInviteAuthority(ServerPortalAuthority authority, out ServerPortalAuthority updatedAuthority) { updatedAuthority = authority; if (!PortalAccountStore.HasValidOverrideSnapshot || authority.AccessMode != PublicPortalAccessMode.Invite || !authority.Builder.IsValid || PublicPortalKinds.IsAdminPortalPrefab(authority.PrefabHash) || PortalAccountStore.GetEffectiveInvitePortalLimit(authority.Builder.AccountId, PublicPortalConfig.MaxInvitePortalsPerAccount.Value) != 0) { return false; } updatedAuthority = new ServerPortalAuthority(PublicPortalAccessMode.Personal, new PortalOwner(authority.Builder.AccountId, authority.Builder.Name), "", authority.Builder, authority.PrefabHash, authority.FavoriteId, authority.RequiredGlobalKey, 0L); return true; } private static void EnsureAdminPortalTagAuthority(ZDO portal) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) if (!AdminPortalTags.ContainsKey(portal.m_uid)) { AdminPortalTags.Add(portal.m_uid, new AdminPortalTagAuthority(Truncate(portal.GetString(ZDOVars.s_tag, ""), 10))); } } private static void BroadcastSnapshot() { if ((Object)(object)ZNet.instance == (Object)null) { return; } ZNetPeer[] array = ZNet.instance.GetPeers().ToArray(); foreach (ZNetPeer val in array) { if (val.IsReady() && val.m_rpc != null) { SendSnapshot(val.m_rpc, val); } } } private static void BroadcastChangedViews() { if ((Object)(object)ZNet.instance == (Object)null || !_hasSnapshot) { return; } ZNetPeer[] array = ZNet.instance.GetPeers().ToArray(); foreach (ZNetPeer val in array) { if (val.IsReady() && val.m_rpc != null) { RecipientContext recipientContext = CreateRecipientContext(val); ulong fingerprint; List visibleEntries = BuildRecipientEntries(recipientContext, out fingerprint); if (!LastSentViews.TryGetValue(val.m_rpc, out var value) || !value.MatchesView(_serverRevision, recipientContext.OwnerId, recipientContext.IsAdmin, fingerprint) || (!value.Acknowledged && Time.realtimeSinceStartup - value.LastSentAt >= 30f)) { SendSnapshot(val.m_rpc, val, recipientContext, visibleEntries, fingerprint); } } } } private static void SendSnapshot(ZRpc rpc, ZNetPeer peer) { if (!rpc.IsConnected()) { LastServerRequestAt.Remove(rpc); LastSentViews.Remove(rpc); } else { RecipientContext recipient = CreateRecipientContext(peer); ulong fingerprint; List visibleEntries = BuildRecipientEntries(recipient, out fingerprint); SendSnapshot(rpc, peer, recipient, visibleEntries, fingerprint); } } private static void SendSnapshot(ZRpc rpc, ZNetPeer peer, RecipientContext recipient, List visibleEntries, ulong recipientViewFingerprint) { //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Expected O, but got Unknown if (!rpc.IsConnected()) { LastServerRequestAt.Remove(rpc); LastSentViews.Remove(rpc); return; } SentViewState value; long num = ((LastSentViews.TryGetValue(rpc, out value) && value.MatchesView(_serverRevision, recipient.OwnerId, recipient.IsAdmin, recipientViewFingerprint)) ? value.ViewToken : NextViewToken()); int num2 = Math.Max(1, (visibleEntries.Count + 96 - 1) / 96); long utcNowSeconds = GetUtcNowSeconds(); for (int i = 0; i < num2; i++) { int num3 = i * 96; int num4 = Math.Min(96, visibleEntries.Count - num3); ZPackage val = new ZPackage(); val.Write((byte)7); val.Write(_serverRevision); val.Write(num); val.Write(i); val.Write(num2); val.Write(visibleEntries.Count); val.Write(recipient.OwnerId); val.Write(recipient.IsAdmin); val.Write(utcNowSeconds); val.Write(num4); for (int j = 0; j < num4; j++) { WriteCatalogEntry(val, visibleEntries[num3 + j]); } rpc.Invoke("sighsorry.PortalRules.CatalogSnapshot.v7", new object[1] { val }); } LastSentViews[rpc] = new SentViewState(_serverRevision, recipient.OwnerId, recipient.IsAdmin, recipientViewFingerprint, num, acknowledged: false, Time.realtimeSinceStartup); PublicPortalServerPolicy.SendQuotaState(peer, force: true); } private static void AcknowledgeSnapshot(int revision, long viewToken) { if (!((Object)(object)ZNet.instance == (Object)null) && !ZNet.instance.IsServer()) { ZRpc serverRPC = ZNet.instance.GetServerRPC(); if (serverRPC != null && serverRPC.IsConnected()) { serverRPC.Invoke("sighsorry.PortalRules.CatalogRequest.v7", new object[2] { revision, viewToken }); } } } private static long NextViewToken() { _nextViewToken++; if (_nextViewToken == 0L) { _nextViewToken++; } return _nextViewToken; } private static RecipientContext CreateRecipientContext(ZNetPeer peer) { PortalOwner owner2; PortalOwner owner = (PublicPortalData.TryGetPeerOwner(peer, out owner2) ? owner2 : new PortalOwner("", "")); bool isAdmin = (Object)(object)ZNet.instance != (Object)null && PublicPortalData.IsPeerAdmin(ZNet.instance, peer); return CreateRecipientContext(owner, isAdmin, ResolvePeerMembership(peer), peer); } private static RecipientContext CreateRecipientContext(PortalOwner owner, bool isAdmin, PortalClanMembership clanMembership, ZNetPeer? peer = null) { return new RecipientContext(SanitizeOwner(owner).Id, isAdmin, clanMembership, peer); } private static bool TryCreateRemoteAuthorizationContext(ZNetPeer? peer, out RecipientContext recipient, out Vector3 playerPosition) { //IL_0004: 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) recipient = null; playerPosition = Vector3.positiveInfinity; if (!PublicPortalData.TryGetPeerOwner(peer, out var owner) || !PublicPortalData.TryGetAuthenticatedPeerCharacterPosition(peer, out playerPosition)) { return false; } recipient = CreateRecipientContext(owner, PublicPortalData.IsPeerAdmin(ZNet.instance, peer), ResolvePeerMembership(peer), peer); return true; } private static void CreateLocalAuthorizationContext(out RecipientContext recipient, out Vector3 playerPosition) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) playerPosition = (((Object)(object)Player.m_localPlayer != (Object)null) ? ((Component)Player.m_localPlayer).transform.position : Vector3.positiveInfinity); recipient = CreateRecipientContext(PublicPortalData.LocalOwner(), PortalRulesPlugin.IsAdmin, ResolveLocalMembership()); } private static void RefreshLocalRecipientView(bool catalogChanged) { if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || (Object)(object)Player.m_localPlayer == (Object)null) { ResetLocalRecipientViewState(); return; } RecipientContext recipientContext = CreateRecipientContext(PublicPortalData.LocalOwner(), PortalRulesPlugin.IsAdmin, ResolveLocalMembership()); ulong fingerprint; List received = BuildRecipientEntries(recipientContext, out fingerprint); bool flag = !_localRecipientViewInitialized || !string.Equals(_localRecipientOwnerId, recipientContext.OwnerId, StringComparison.Ordinal) || _localRecipientIsAdmin != recipientContext.IsAdmin || _localRecipientViewFingerprint != fingerprint; _localRecipientViewInitialized = true; _localRecipientOwnerId = recipientContext.OwnerId; _localRecipientIsAdmin = recipientContext.IsAdmin; _localRecipientViewFingerprint = fingerprint; if (catalogChanged || flag) { ApplyLocalClientEntries(received, forceUpdate: true); } } private static void ResetLocalRecipientViewState() { _localRecipientViewInitialized = false; _localRecipientOwnerId = ""; _localRecipientIsAdmin = false; _localRecipientViewFingerprint = 0uL; } private static PortalClanMembership ResolvePeerMembership(ZNetPeer? peer) { if (!ClanPortalAccess.TryResolvePeerMembership(peer, out var membership)) { return default(PortalClanMembership); } return membership; } private static PortalClanMembership ResolveLocalMembership() { if (!ClanPortalAccess.TryResolveLocalMembership(out var membership)) { return default(PortalClanMembership); } return membership; } private static bool ShouldSendEntry(PublicPortalCatalogEntry entry, RecipientContext recipient) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) if (ServerAuthority.TryGetValue(entry.Id, out var value)) { return CanRecipientUseAuthority(value, recipient); } return false; } private static void ApplyModeDisplayMetadata(List entries) { //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Unknown result type (might be due to invalid IL or missing references) //IL_01bb: Unknown result type (might be due to invalid IL or missing references) Dictionary dictionary = new Dictionary(); foreach (IGrouping> item2 in ServerAuthority.Where((KeyValuePair pair) => !PendingRemovalIds.Contains(pair.Key) && pair.Value.AccessMode == PublicPortalAccessMode.Invite && pair.Value.Builder.IsValid && !PublicPortalKinds.IsAdminPortalPrefab(pair.Value.PrefabHash)).GroupBy, string>((KeyValuePair pair) => pair.Value.Builder.AccountId, StringComparer.Ordinal)) { List> list = OrderAuthorityByBuildSequence(item2); int effectiveInvitePortalLimit = PortalAccountStore.GetEffectiveInvitePortalLimit(item2.Key, PublicPortalConfig.MaxInvitePortalsPerAccount.Value); for (int num = 0; num < list.Count; num++) { dictionary[list[num].Key] = (num + 1, list.Count, effectiveInvitePortalLimit); } } int item = Math.Max(-1, Math.Min(10000, PublicPortalConfig.MaxClanPortalsPerClan.Value)); foreach (IGrouping> item3 in ServerAuthority.Where((KeyValuePair pair) => !PendingRemovalIds.Contains(pair.Key) && pair.Value.AccessMode == PublicPortalAccessMode.Clan && pair.Value.Builder.IsValid && !string.IsNullOrWhiteSpace(pair.Value.AuthorizedClanId) && !PublicPortalKinds.IsAdminPortalPrefab(pair.Value.PrefabHash)).GroupBy, string>((KeyValuePair pair) => pair.Value.AuthorizedClanId, StringComparer.Ordinal)) { List> list2 = OrderAuthorityByBuildSequence(item3); for (int num2 = 0; num2 < list2.Count; num2++) { dictionary[list2[num2].Key] = (num2 + 1, list2.Count, item); } } for (int num3 = 0; num3 < entries.Count; num3++) { PublicPortalCatalogEntry publicPortalCatalogEntry = entries[num3]; if (dictionary.TryGetValue(publicPortalCatalogEntry.Id, out var value)) { entries[num3] = publicPortalCatalogEntry.WithDisplayMetadata(0, 0, value.Item1, value.Item2, value.Item3, 0L, 0L); } } } private static List> OrderAuthorityByBuildSequence(IEnumerable> authorities) { return authorities.OrderBy((KeyValuePair pair) => (pair.Value.Builder.BuildSequence <= 0) ? long.MaxValue : pair.Value.Builder.BuildSequence).ThenBy, string>((KeyValuePair pair) => pair.Value.FavoriteId, StringComparer.Ordinal).ThenBy((KeyValuePair pair) => pair.Key) .ToList(); } private static List BuildRecipientEntries(RecipientContext recipient) { //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) Dictionary dictionary = new Dictionary(); int num = 0; if (PublicPortalData.TryNormalizeSteamId64(recipient.OwnerId, out string recipientAccountId)) { List> list = OrderAuthorityByBuildSequence(ServerAuthority.Where((KeyValuePair pair) => !PendingRemovalIds.Contains(pair.Key) && pair.Value.Builder.IsValid && string.Equals(pair.Value.Builder.AccountId, recipientAccountId, StringComparison.Ordinal) && PublicPortalServerPolicy.IsCountedPortalPrefab(pair.Value.PrefabHash))); for (int num2 = 0; num2 < list.Count; num2++) { dictionary[list[num2].Key] = num2 + 1; } num = (PublicPortalConfig.EnableAccountPortalLimit.Value.IsOff() ? (-1) : PortalAccountStore.GetEffectivePortalLimit(recipientAccountId, PublicPortalConfig.MaxPortalsPerAccount.Value)); } List list2 = new List(); foreach (PublicPortalCatalogEntry serverEntry in ServerEntries) { if (ShouldSendEntry(serverEntry, recipient)) { int value; int num3 = (dictionary.TryGetValue(serverEntry.Id, out value) ? value : 0); long departureUntilUtc = 0L; long arrivalUntilUtc = 0L; if (serverEntry.AccessMode == PublicPortalAccessMode.Invite && !string.IsNullOrWhiteSpace(recipient.OwnerId)) { InviteTravelCooldownStore.GetServerDeadlines(recipient.OwnerId, serverEntry.FavoriteId, out departureUntilUtc, out arrivalUntilUtc); } list2.Add(serverEntry.WithDisplayMetadata(num3, (num3 > 0) ? num : 0, serverEntry.ModeOrdinal, serverEntry.ModeCurrent, serverEntry.ModeLimit, departureUntilUtc, arrivalUntilUtc)); } } return list2; } private static List BuildRecipientEntries(RecipientContext recipient, out ulong fingerprint) { List list = BuildRecipientEntries(recipient); fingerprint = ComputeRecipientViewFingerprint(list); return list; } private static void RefreshLocalClientEntries(bool forceUpdate) { if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || (Object)(object)Player.m_localPlayer == (Object)null) { if (ClientEntries.Count > 0) { ClientEntries.Clear(); ClientIndex.Clear(); if (forceUpdate) { PublicPortalCatalog.Updated?.Invoke(); } } } else { RecipientContext recipientContext = CreateRecipientContext(PublicPortalData.LocalOwner(), PortalRulesPlugin.IsAdmin, ResolveLocalMembership()); ulong fingerprint; List received = BuildRecipientEntries(recipientContext, out fingerprint); _localRecipientViewInitialized = true; _localRecipientOwnerId = recipientContext.OwnerId; _localRecipientIsAdmin = recipientContext.IsAdmin; _localRecipientViewFingerprint = fingerprint; ApplyLocalClientEntries(received, forceUpdate); } } private static void ApplyLocalClientEntries(List received, bool forceUpdate) { bool num = !SnapshotsMatch(ClientEntries, received); _clientRevision = _serverRevision; if (num || forceUpdate) { ClientEntries.Clear(); ClientEntries.AddRange(received); RebuildClientIndex(); PublicPortalCatalog.Updated?.Invoke(); } } private static ulong ComputeRecipientViewFingerprint(IReadOnlyList entries) { ulong fingerprint = 14695981039346656037uL; HashFingerprintInt(ref fingerprint, entries.Count, 1099511628211uL); foreach (PublicPortalCatalogEntry entry in entries) { HashFingerprintInt(ref fingerprint, entry.FavoriteId.Length, 1099511628211uL); string favoriteId = entry.FavoriteId; foreach (char c in favoriteId) { fingerprint ^= c; fingerprint *= 1099511628211L; } HashFingerprintInt(ref fingerprint, (int)entry.AccessMode, 1099511628211uL); HashFingerprintInt(ref fingerprint, entry.AllowsAllItems ? 1 : 0, 1099511628211uL); HashFingerprintInt(ref fingerprint, entry.MyPortalOrdinal, 1099511628211uL); HashFingerprintInt(ref fingerprint, entry.MyPortalLimit, 1099511628211uL); HashFingerprintInt(ref fingerprint, entry.ModeOrdinal, 1099511628211uL); HashFingerprintInt(ref fingerprint, entry.ModeCurrent, 1099511628211uL); HashFingerprintInt(ref fingerprint, entry.ModeLimit, 1099511628211uL); HashFingerprintLong(ref fingerprint, entry.InviteDepartureCooldownUntilUtc, 1099511628211uL); HashFingerprintLong(ref fingerprint, entry.InviteArrivalCooldownUntilUtc, 1099511628211uL); } return fingerprint; } private static void HashFingerprintInt(ref ulong fingerprint, int value, ulong prime) { HashFingerprintLong(ref fingerprint, value, prime); } private static void HashFingerprintLong(ref ulong fingerprint, long value, ulong prime) { ulong num = (ulong)value; for (int i = 0; i < 8; i++) { fingerprint ^= (byte)num; fingerprint *= prime; num >>= 8; } } private static void ResetSessionState() { _sessionGeneration++; _serverLoopGame = null; _serverRevision = 0; _clientRevision = -1; _clientViewToken = 0L; _nextViewToken = 0L; _nextBuildSequence = 0L; _lastClientRequestAt = -100f; _nextIdentityReconcileAt = -100f; _authorityBackfilled = false; _hasSnapshot = false; _worldLoaded = false; _serverCatalogDirty = false; _serverDirtyFlushScheduled = false; ResetLocalRecipientViewState(); _activePortalSync = null; _activeLocalPlacementBuilder = new PortalBuilder("", ""); _pendingLocalPlacementPortalIds = null; ServerEntries.Clear(); ClientEntries.Clear(); ClientIndex.Clear(); ServerAuthority.Clear(); ServerPortalsByFavoriteId.Clear(); AdminPortalTags.Clear(); ServerPortalsByBuilder.Clear(); InitialWorldPortalIds.Clear(); PendingRemovalIds.Clear(); LastServerRequestAt.Clear(); LastSentViews.Clear(); _clientServerUtcAtSync = 0L; _clientServerClockTimestamp = 0L; PublicPortalData.SetServerAssignedOwnerId(""); PublicPortalData.SetServerAssignedIsAdmin(isAdmin: false); ResetPendingSnapshot(); } private static void ResetPendingSnapshot() { _pendingSnapshot = null; } private static void RebuildClientIndex() { //IL_0025: Unknown result type (might be due to invalid IL or missing references) ClientIndex.Clear(); foreach (PublicPortalCatalogEntry clientEntry in ClientEntries) { ClientIndex[clientEntry.Id] = clientEntry; } } private static bool TryAuthorizeTeleport(RecipientContext recipient, Vector3 playerPosition, ZDOID sourcePortalId, ZDOID targetPortalId, bool requireConnectedPortal, string reservationId, out PortalTravelAuthorization authorization, out PortalTravelDenial denial) { //IL_003c: 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_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0118: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_0120: 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) //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_0136: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_013d: 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_012b: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) //IL_015f: Unknown result type (might be due to invalid IL or missing references) //IL_0178: 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_01f6: Unknown result type (might be due to invalid IL or missing references) //IL_01f8: Unknown result type (might be due to invalid IL or missing references) //IL_01cf: Unknown result type (might be due to invalid IL or missing references) //IL_01d4: Unknown result type (might be due to invalid IL or missing references) //IL_01d6: Unknown result type (might be due to invalid IL or missing references) //IL_01db: Unknown result type (might be due to invalid IL or missing references) authorization = default(PortalTravelAuthorization); denial = new PortalTravelDenial(PortalTravelDenialCode.Denied, "", 0L, 0L); if ((!requireConnectedPortal && PublicPortalConfig.EnablePortalMap.Value.IsOff()) || ((ZDOID)(ref targetPortalId)).IsNone() || sourcePortalId == targetPortalId) { return false; } if (!TryAuthorizePortalSource(recipient, playerPosition, sourcePortalId, requireConnectedPortal, out ZDO sourcePortal, out ServerPortalAuthority sourceAuthority, out denial)) { return false; } Vector3 position = sourcePortal.GetPosition(); if (!TryGetUsablePortal(targetPortalId, recipient, source: false, out ZDO portal, out denial)) { return false; } if (!ServerAuthority.TryGetValue(targetPortalId, out var value)) { denial = new PortalTravelDenial(PortalTravelDenialCode.DestinationUnavailable, "", 0L, 0L); return false; } bool flag = value.AccessMode == PublicPortalAccessMode.Tagged; if (requireConnectedPortal && PublicPortalConfig.EnablePortalMap.Value.IsOn() && !flag) { denial = new PortalTravelDenial(PortalTravelDenialCode.DestinationNotTagged, "", 0L, 0L); return false; } if (requireConnectedPortal && (sourcePortal.GetConnectionZDOID((ConnectionType)1) != targetPortalId || portal.GetConnectionZDOID((ConnectionType)1) != sourcePortalId)) { denial = new PortalTravelDenial(PortalTravelDenialCode.ConnectionChanged, "", 0L, 0L); return false; } Vector3 position2 = portal.GetPosition(); Quaternion rotation = portal.GetRotation(); if (!IsSafePortalPosition(position2) || !TryNormalizePortalRotation(rotation, out var normalized)) { position2 = Vector3.zero; rotation = Quaternion.identity; denial = new PortalTravelDenial(PortalTravelDenialCode.DestinationPositionInvalid, "", 0L, 0L); return false; } rotation = normalized; int coinCost = PublicPortalTravelCost.CalculateCost(sourceAuthority.PrefabHash, sourceAuthority.AccessMode, PublicPortalKinds.PrefabAllowsAllItems(sourceAuthority.PrefabHash), position, value.PrefabHash, value.AccessMode, position2); bool usesInviteAsSource = sourceAuthority.AccessMode == PublicPortalAccessMode.Invite; bool usesInviteAsDestination = value.AccessMode == PublicPortalAccessMode.Invite; if (!InviteTravelCooldownStore.TryReserve(reservationId, recipient.OwnerId, usesInviteAsSource, sourceAuthority.FavoriteId, usesInviteAsDestination, value.FavoriteId, out var reservation, out var failure, out var departureRemaining, out var arrivalRemaining)) { position2 = Vector3.zero; rotation = Quaternion.identity; coinCost = 0; denial = CreateInviteCooldownDenial(failure, departureRemaining, arrivalRemaining); return false; } authorization = new PortalTravelAuthorization(position2, rotation, coinCost, recipient.OwnerId, sourceAuthority.FavoriteId, value.FavoriteId, usesInviteAsSource, usesInviteAsDestination, reservation); denial = PortalTravelDenial.None; return true; } private static bool TryAuthorizePortalSource(RecipientContext recipient, Vector3 playerPosition, ZDOID sourcePortalId, bool requireConnectedPortal, out ZDO sourcePortal, out ServerPortalAuthority sourceAuthority, out PortalTravelDenial denial) { //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_010b: 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_018c: Unknown result type (might be due to invalid IL or missing references) //IL_018d: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Unknown result type (might be due to invalid IL or missing references) //IL_019d: Unknown result type (might be due to invalid IL or missing references) //IL_019e: Unknown result type (might be due to invalid IL or missing references) sourcePortal = null; sourceAuthority = default(ServerPortalAuthority); denial = new PortalTravelDenial(PortalTravelDenialCode.Denied, "", 0L, 0L); if (string.IsNullOrWhiteSpace(recipient.OwnerId) || ((ZDOID)(ref sourcePortalId)).IsNone() || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || ZDOMan.instance == null) { return false; } if ((Object)(object)ZoneSystem.instance == (Object)null) { denial = new PortalTravelDenial(PortalTravelDenialCode.NotReady, "", 0L, 0L); return false; } if (ZoneSystem.instance.GetGlobalKey((GlobalKeys)27)) { denial = new PortalTravelDenial(PortalTravelDenialCode.PortalsBlocked, "", 0L, 0L); return false; } if (ZoneSystem.instance.GetGlobalKey((GlobalKeys)28)) { RandEventSystem instance = RandEventSystem.instance; float num = default(float); if (((instance != null) ? instance.GetBossEvent() : null) != null || (ZoneSystem.instance.GetGlobalKey((GlobalKeys)38, ref num) && num > 0f)) { denial = new PortalTravelDenial(PortalTravelDenialCode.BossBlocked, "", 0L, 0L); return false; } } if (!TryGetUsablePortal(sourcePortalId, recipient, source: true, out sourcePortal, out denial)) { return false; } if (!ServerAuthority.TryGetValue(sourcePortalId, out sourceAuthority)) { denial = new PortalTravelDenial(PortalTravelDenialCode.SourceUnavailable, "", 0L, 0L); return false; } bool flag = sourceAuthority.AccessMode == PublicPortalAccessMode.Tagged; if (requireConnectedPortal) { if (PublicPortalConfig.EnablePortalMap.Value.IsOn() && !flag) { denial = new PortalTravelDenial(PortalTravelDenialCode.SourceNotTagged, "", 0L, 0L); return false; } } else if (flag) { denial = new PortalTravelDenial(PortalTravelDenialCode.TaggedRequiresConnectedDestination, "", 0L, 0L); return false; } Vector3 position = sourcePortal.GetPosition(); if (!IsFinite(playerPosition) || !IsSafePortalPosition(position) || Vector3.Distance(playerPosition, position) > 15f) { denial = new PortalTravelDenial(PortalTravelDenialCode.SourceTooFar, "", 0L, 0L); return false; } denial = PortalTravelDenial.None; return true; } private static PortalTravelDenial CreateInviteCooldownDenial(InviteTravelCooldownFailure failure, long departureRemaining, long arrivalRemaining) { return failure switch { InviteTravelCooldownFailure.DataUnavailable => new PortalTravelDenial(PortalTravelDenialCode.InviteCooldownDataUnavailable, "", 0L, 0L), InviteTravelCooldownFailure.StorageUnavailable => new PortalTravelDenial(PortalTravelDenialCode.InviteCooldownStorageUnavailable, "", 0L, 0L), InviteTravelCooldownFailure.Active => new PortalTravelDenial(PortalTravelDenialCode.InviteCooldownActive, "", Math.Max(0L, departureRemaining), Math.Max(0L, arrivalRemaining)), InviteTravelCooldownFailure.AccountCapacityReached => new PortalTravelDenial(PortalTravelDenialCode.InviteCooldownAccountCapacityReached, "", 0L, 0L), InviteTravelCooldownFailure.PortalCapacityReached => new PortalTravelDenial(PortalTravelDenialCode.InviteCooldownPortalCapacityReached, "", 0L, 0L), InviteTravelCooldownFailure.FileCapacityReached => new PortalTravelDenial(PortalTravelDenialCode.InviteCooldownFileCapacityReached, "", 0L, 0L), InviteTravelCooldownFailure.SaveFailed => new PortalTravelDenial(PortalTravelDenialCode.InviteCooldownSaveFailed, "", 0L, 0L), InviteTravelCooldownFailure.ReservationConflict => new PortalTravelDenial(PortalTravelDenialCode.RequestRateLimited, "", 0L, 0L), _ => new PortalTravelDenial(PortalTravelDenialCode.InviteCooldownDataUnavailable, "", 0L, 0L), }; } internal static void RefreshCommittedInviteCooldownView(ZNetPeer? peer) { if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer()) { RecipientContext recipient; if (peer != null) { recipient = CreateRecipientContext(peer); } else { CreateLocalAuthorizationContext(out recipient, out Vector3 _); } RefreshRecipientCooldownView(recipient); } } private static void RefreshRecipientCooldownView(RecipientContext recipient) { if (recipient.Peer != null) { if (recipient.Peer.m_rpc != null && recipient.Peer.m_rpc.IsConnected()) { SendSnapshot(recipient.Peer.m_rpc, recipient.Peer); } } else { RefreshLocalClientEntries(forceUpdate: true); } } internal static long GetEstimatedServerUtcNowSeconds() { if ((Object)(object)ZNet.instance == (Object)null || ZNet.instance.IsServer() || _clientServerUtcAtSync <= 0 || _clientServerClockTimestamp <= 0) { return GetUtcNowSeconds(); } long num = Stopwatch.GetTimestamp() - _clientServerClockTimestamp; if (num <= 0) { return _clientServerUtcAtSync; } long num2 = (long)((double)num / (double)Stopwatch.Frequency); if (_clientServerUtcAtSync <= 253402300799L - num2) { return _clientServerUtcAtSync + num2; } return 253402300799L; } private static void SynchronizeClientServerClock(long serverUtcAtSend, long firstChunkReceivedTimestamp) { _clientServerUtcAtSync = serverUtcAtSend; _clientServerClockTimestamp = ((firstChunkReceivedTimestamp > 0) ? firstChunkReceivedTimestamp : Stopwatch.GetTimestamp()); } private static bool TryGetUsablePortal(ZDOID portalId, RecipientContext recipient, bool source, out ZDO portal, out PortalTravelDenial denial) { //IL_0024: 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_0049: Unknown result type (might be due to invalid IL or missing references) portal = null; denial = new PortalTravelDenial(source ? PortalTravelDenialCode.SourceUnavailable : PortalTravelDenialCode.DestinationUnavailable, "", 0L, 0L); if (PendingRemovalIds.Contains(portalId) || !ServerAuthority.TryGetValue(portalId, out var value) || ZDOMan.instance == null) { return false; } ZDO zDO = ZDOMan.instance.GetZDO(portalId); if (zDO == null || !zDO.IsValid()) { return false; } if (!ZdoMatchesAuthority(zDO, value)) { WriteAuthorityToZdo(zDO, value, forceSend: true); } if (!PublicPortalKinds.IsHandledPortal(zDO)) { return false; } if (!CanRecipientUseBaseAuthority(value, recipient)) { denial = new PortalTravelDenial(source ? PortalTravelDenialCode.SourceAccessDenied : PortalTravelDenialCode.DestinationAccessDenied, "", 0L, 0L); return false; } if (!TryCheckRecipientRequiredGlobalKey(value, recipient, out denial)) { return false; } portal = zDO; denial = PortalTravelDenial.None; return true; } private static bool CanRecipientUseAuthority(ServerPortalAuthority authority, RecipientContext recipient) { PortalTravelDenial denial; if (CanRecipientUseBaseAuthority(authority, recipient)) { return TryCheckRecipientRequiredGlobalKey(authority, recipient, out denial); } return false; } private static bool CanRecipientUseBaseAuthority(ServerPortalAuthority authority, RecipientContext recipient) { if (IsTemporaryPublicAccessExpired(authority, GetUtcNowSeconds())) { if (authority.Builder.IsValid) { return string.Equals(authority.Builder.AccountId, recipient.OwnerId, StringComparison.Ordinal); } return false; } if (authority.Owner.IsValid && string.Equals(authority.Owner.Id, recipient.OwnerId, StringComparison.Ordinal)) { return true; } return authority.AccessMode switch { PublicPortalAccessMode.Public => true, PublicPortalAccessMode.Invite => true, PublicPortalAccessMode.Tagged => PublicPortalKinds.IsAdminPortalPrefab(authority.PrefabHash) || (!string.IsNullOrWhiteSpace(authority.AuthorizedClanId) && recipient.ClanMembership.ContainsClan(authority.AuthorizedClanId)), PublicPortalAccessMode.Admin => recipient.IsAdmin, PublicPortalAccessMode.Clan => !string.IsNullOrWhiteSpace(authority.AuthorizedClanId) && recipient.ClanMembership.ContainsClan(authority.AuthorizedClanId), _ => false, }; } private static bool TryCheckRecipientRequiredGlobalKey(ServerPortalAuthority authority, RecipientContext recipient, out PortalTravelDenial denial) { denial = PortalTravelDenial.None; if (string.IsNullOrEmpty(authority.RequiredGlobalKey)) { return true; } if (!authority.RequiredGlobalKeyIsValid) { denial = new PortalTravelDenial(PortalTravelDenialCode.RequiredGlobalKeyInvalid, "", 0L, 0L); return false; } if (!recipient.RequiredGlobalKeyResults.TryGetValue(authority.RequiredGlobalKey, out var value)) { value = ((recipient.Peer != null) ? RequiredGlobalKeyAccess.QueryPeer(recipient.Peer, authority.RequiredGlobalKey) : RequiredGlobalKeyAccess.QueryLocal(authority.RequiredGlobalKey)); recipient.RequiredGlobalKeyResults[authority.RequiredGlobalKey] = value; } if (RequiredGlobalKeyAccess.IsPresent(value)) { return true; } PortalTravelDenial portalTravelDenial; switch (value) { case RequiredGlobalKeyQueryResult.PersonalMissing: case RequiredGlobalKeyQueryResult.SharedMissing: portalTravelDenial = new PortalTravelDenial(PortalTravelDenialCode.RequiredGlobalKeyMissing, authority.RequiredGlobalKey, 0L, 0L); break; case RequiredGlobalKeyQueryResult.Invalid: portalTravelDenial = new PortalTravelDenial(PortalTravelDenialCode.RequiredGlobalKeyInvalid, "", 0L, 0L); break; default: portalTravelDenial = new PortalTravelDenial(PortalTravelDenialCode.RequiredGlobalKeyUnavailable, "", 0L, 0L); break; } denial = portalTravelDenial; return false; } private static bool TryResolveActiveBuilder(ZDO zdo, out PortalBuilder builder) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) builder = new PortalBuilder("", ""); if (_activePortalSync != null) { if (!_activePortalSync.CreatedIds.Contains(zdo.m_uid)) { return false; } ZNetPeer val = _activePortalSync?.Peer; if (val == null || !val.IsReady() || ((ZDOID)(ref val.m_characterID)).IsNone() || ((ZDOID)(ref zdo.m_uid)).UserID != val.m_uid || zdo.GetOwner() != val.m_uid || ZDOMan.instance == null) { return false; } if (!PublicPortalData.TryGetAuthenticatedPeerPlayerId(val, out var playerId) || !PublicPortalData.TryGetPeerSteamId64(val, out string steamId)) { return false; } long num = zdo.GetLong(ZDOVars.s_creator, 0L); if (num == 0L || playerId != num || !PortalAccountStore.TryRememberIdentity(playerId, steamId, out var added)) { return false; } if (added) { BackfillBuilderForCreator(playerId, steamId, val.m_playerName); } builder = SanitizeBuilder(new PortalBuilder(steamId, val.m_playerName, steamId, playerId, 0L)); return builder.IsValid; } if (_activeLocalPlacementBuilder.IsValid && (Object)(object)Player.m_localPlayer != (Object)null) { long playerID = Player.m_localPlayer.GetPlayerID(); long num2 = zdo.GetLong(ZDOVars.s_creator, 0L); if (playerID == 0L || num2 == 0L || num2 != playerID || !PortalAccountStore.TryResolveSteamId(playerID, out string steamId2) || !string.Equals(steamId2, _activeLocalPlacementBuilder.AccountId, StringComparison.Ordinal)) { return false; } builder = SanitizeBuilder(_activeLocalPlacementBuilder); return builder.IsValid; } return false; } private static bool IsAuthenticatedRemoteAdminPortalCreation(ZDO zdo, ZNetPeer? peer) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_009f: 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) Vector3 position = zdo.GetPosition(); if (_activePortalSync != null && _activePortalSync.CreatedIds.Contains(zdo.m_uid) && peer != null && peer.IsReady() && ((ZDOID)(ref zdo.m_uid)).UserID == peer.m_uid && zdo.GetOwner() == peer.m_uid && (Object)(object)ZNet.instance != (Object)null && PublicPortalData.IsPeerAdmin(ZNet.instance, peer) && PublicPortalData.TryGetPeerOwner(peer, out var _) && PublicPortalData.TryGetAuthenticatedPeerCharacterPosition(peer, out var position2) && IsSafePortalPosition(position) && TryNormalizePortalRotation(zdo.GetRotation(), out var _)) { return Vector3.Distance(position2, position) <= 15f; } return false; } private static ServerPortalAuthority SetServerAuthority(ZDOID portalId, ServerPortalAuthority authority) { //IL_0159: Unknown result type (might be due to invalid IL or missing references) //IL_015a: Unknown result type (might be due to invalid IL or missing references) //IL_01b7: Unknown result type (might be due to invalid IL or missing references) //IL_0244: Unknown result type (might be due to invalid IL or missing references) //IL_0251: Unknown result type (might be due to invalid IL or missing references) //IL_01e5: Unknown result type (might be due to invalid IL or missing references) //IL_01e7: Unknown result type (might be due to invalid IL or missing references) //IL_02a0: Unknown result type (might be due to invalid IL or missing references) //IL_0239: Unknown result type (might be due to invalid IL or missing references) if (authority.Builder.IsValid) { PortalBuilder builder = authority.Builder; if (builder.BuildSequence <= 0) { builder = builder.WithBuildSequence(NextBuildSequence()); } else { _nextBuildSequence = Math.Max(_nextBuildSequence, builder.BuildSequence); } if (builder.BuildSequence != authority.Builder.BuildSequence) { authority = new ServerPortalAuthority(authority.AccessMode, authority.Owner, authority.AuthorizedClanId, builder, authority.PrefabHash, authority.FavoriteId, authority.RequiredGlobalKey, authority.PublicExpiresAtUtcSeconds); } } if (authority.Builder.IsValid && !PublicPortalKinds.IsAdminPortalPrefab(authority.PrefabHash) && (!string.Equals(authority.Owner.Id, authority.Builder.AccountId, StringComparison.Ordinal) || !string.Equals(authority.Owner.Name, authority.Builder.Name, StringComparison.Ordinal))) { authority = new ServerPortalAuthority(authority.AccessMode, new PortalOwner(authority.Builder.AccountId, authority.Builder.Name), authority.AuthorizedClanId, authority.Builder, authority.PrefabHash, authority.FavoriteId, authority.RequiredGlobalKey, authority.PublicExpiresAtUtcSeconds); } string normalizedFavoriteId; bool num = PublicPortalData.TryNormalizeFavoriteId(authority.FavoriteId, out normalizedFavoriteId); ZDOID value; bool flag = num && ServerPortalsByFavoriteId.TryGetValue(normalizedFavoriteId, out value) && value != portalId; if (!num || flag) { normalizedFavoriteId = CreateServerFavoriteId(); } if (!string.Equals(authority.FavoriteId, normalizedFavoriteId, StringComparison.Ordinal)) { authority = new ServerPortalAuthority(authority.AccessMode, authority.Owner, authority.AuthorizedClanId, authority.Builder, authority.PrefabHash, normalizedFavoriteId, authority.RequiredGlobalKey, authority.PublicExpiresAtUtcSeconds); } ServerPortalAuthority value2; bool num2 = ServerAuthority.TryGetValue(portalId, out value2); if (num2 && !string.Equals(value2.FavoriteId, normalizedFavoriteId, StringComparison.Ordinal) && ServerPortalsByFavoriteId.TryGetValue(value2.FavoriteId, out var value3) && value3 == portalId) { ServerPortalsByFavoriteId.Remove(value2.FavoriteId); } if (num2 && value2.Builder.IsValid && !string.Equals(value2.Builder.AccountId, authority.Builder.AccountId, StringComparison.Ordinal)) { RemoveBuilderPortal(value2.Builder.AccountId, portalId); } ServerAuthority[portalId] = authority; ServerPortalsByFavoriteId[normalizedFavoriteId] = portalId; if (!authority.Builder.IsValid) { return authority; } if (!ServerPortalsByBuilder.TryGetValue(authority.Builder.AccountId, out HashSet value4)) { value4 = new HashSet(); ServerPortalsByBuilder.Add(authority.Builder.AccountId, value4); } value4.Add(portalId); return authority; } private static long NextBuildSequence() { if (_nextBuildSequence < long.MaxValue) { _nextBuildSequence++; return _nextBuildSequence; } long num = 1L; HashSet hashSet = (from authority in ServerAuthority.Values where authority.Builder.BuildSequence > 0 select authority.Builder.BuildSequence).ToHashSet(); if (ZDOMan.instance != null) { foreach (ZDO portal in ZDOMan.instance.GetPortals()) { if (portal != null && portal.GetInt("PortalRules BuilderAuthorityVersion", 0) >= 2) { long num2 = portal.GetLong("PortalRules BuildSequence", 0L); if (num2 > 0) { hashSet.Add(num2); } } } } for (; num < long.MaxValue && hashSet.Contains(num); num++) { } return num; } private static void SeedNextBuildSequence(IEnumerable portals) { foreach (ServerPortalAuthority value in ServerAuthority.Values) { _nextBuildSequence = Math.Max(_nextBuildSequence, value.Builder.BuildSequence); } foreach (ZDO portal in portals) { if (portal != null && portal.GetInt("PortalRules BuilderAuthorityVersion", 0) >= 2) { _nextBuildSequence = Math.Max(_nextBuildSequence, portal.GetLong("PortalRules BuildSequence", 0L)); } } } private static void RemoveServerAuthority(ZDOID portalId) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) AdminPortalTags.Remove(portalId); if (ServerAuthority.TryGetValue(portalId, out var value)) { InviteTravelCooldownStore.RemovePortal(value.FavoriteId); ServerAuthority.Remove(portalId); if (ServerPortalsByFavoriteId.TryGetValue(value.FavoriteId, out var value2) && value2 == portalId) { ServerPortalsByFavoriteId.Remove(value.FavoriteId); } if (value.Builder.IsValid) { RemoveBuilderPortal(value.Builder.AccountId, portalId); } } } private static void RemoveBuilderPortal(string builderAccountId, ZDOID portalId) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) if (ServerPortalsByBuilder.TryGetValue(builderAccountId, out HashSet value)) { value.Remove(portalId); if (value.Count == 0) { ServerPortalsByBuilder.Remove(builderAccountId); } } } private static PortalBuilder SanitizeBuilder(PortalBuilder builder) { string steamId; string text = (PublicPortalData.TryNormalizeSteamId64(builder.AccountId, out steamId) ? steamId : ""); return new PortalBuilder(text, Truncate(builder.Name, 128), string.IsNullOrWhiteSpace(builder.PlatformId) ? text : Truncate(builder.PlatformId, 128), builder.CharacterPlayerId, builder.BuildSequence); } private static PortalOwner SanitizeOwner(PortalOwner owner) { string text = owner.Id ?? ""; if (text.Length > 128) { text = ""; } return new PortalOwner(text, Truncate(owner.Name, 128)); } private static string SanitizeClanId(string clanId) { clanId = (clanId ?? "").Trim(); if (clanId.Length > 128) { return ""; } return clanId; } private static string Truncate(string value, int maximumLength) { if (value == null) { value = ""; } if (value.Length > maximumLength) { return value.Substring(0, maximumLength); } return value; } private static bool IsFinite(Vector3 value) { //IL_0000: 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_001a: Unknown result type (might be due to invalid IL or missing references) if (IsFinite(value.x) && IsFinite(value.y)) { return IsFinite(value.z); } return false; } private static bool IsSafePortalPosition(Vector3 value) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0008: 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) //IL_002c: Unknown result type (might be due to invalid IL or missing references) if (IsFinite(value) && Math.Abs(value.x) <= 1000000f && Math.Abs(value.y) <= 1000000f) { return Math.Abs(value.z) <= 1000000f; } return false; } private static bool IsFinite(Quaternion value) { //IL_0000: 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_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) if (IsFinite(value.x) && IsFinite(value.y) && IsFinite(value.z)) { return IsFinite(value.w); } return false; } private static bool TryNormalizePortalRotation(Quaternion value, out Quaternion normalized) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0034: 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) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: 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_00ad: Unknown result type (might be due to invalid IL or missing references) normalized = Quaternion.identity; if (!IsFinite(value)) { return false; } double num = (double)value.x * (double)value.x + (double)value.y * (double)value.y + (double)value.z * (double)value.z + (double)value.w * (double)value.w; if (num < 0.25 || num > 4.0) { return false; } float num2 = (float)(1.0 / Math.Sqrt(num)); normalized = new Quaternion(value.x * num2, value.y * num2, value.z * num2, value.w * num2); return IsFinite(normalized); } private static bool IsFinite(float value) { if (!float.IsNaN(value)) { return !float.IsInfinity(value); } return false; } private static bool SnapshotsMatch(IReadOnlyList left, IReadOnlyList right) { if (left.Count != right.Count) { return false; } for (int i = 0; i < left.Count; i++) { if (!left[i].HasSameContent(right[i])) { return false; } } return true; } } internal static class PublicPortalServerPolicy { private sealed class PendingRemoval { public readonly ZDOID PortalId; public int Attempts; public float NextAttemptAt; public PendingRemoval(ZDOID portalId) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) PortalId = portalId; } } private readonly struct QuotaState { public readonly int EffectiveLimit; public readonly int CurrentCount; public readonly int EffectiveInviteLimit; public readonly int CurrentInviteCount; public QuotaState(int effectiveLimit, int currentCount, int effectiveInviteLimit, int currentInviteCount) { EffectiveLimit = effectiveLimit; CurrentCount = currentCount; EffectiveInviteLimit = effectiveInviteLimit; CurrentInviteCount = currentInviteCount; } public bool Matches(QuotaState other) { if (EffectiveLimit == other.EffectiveLimit && CurrentCount == other.CurrentCount && EffectiveInviteLimit == other.EffectiveInviteLimit) { return CurrentInviteCount == other.CurrentInviteCount; } return false; } } private const string PolicyMessageRpc = "sighsorry.PortalRules.PolicyMessage.v2"; private const string QuotaStateRpc = "sighsorry.PortalRules.QuotaState.v2"; private const string InfinityHammerPluginGuid = "infinity_hammer"; private const byte QuotaStateFormatVersion = 2; private const int MaximumPortalLimit = 10000; private const int MaximumPortalCount = 100000; private const int MaximumRemovalAttemptsBeforeWarning = 5; private const int MaximumQueuedRemovalsPerFrame = 10; private static readonly Queue PendingRemovals = new Queue(); private static readonly Dictionary DeferredRemovals = new Dictionary(); private static readonly HashSet CountedPortalHashes = new HashSet(); private static readonly Dictionary LastSentQuotaStates = new Dictionary(); private static Game? _registeredGame; private static ZNet? _sessionZNet; private static string _cachedCountedPrefabSetting = ""; private static long _sessionGeneration; private static bool _hasClientQuotaState; private static int _clientEffectiveLimit = -1; private static int _clientCurrentCount; private static int _clientEffectiveInviteLimit = -1; private static int _clientCurrentInviteCount; private static MethodInfo? _infinityHammerNoCreatorGetter; private static MethodInfo? _infinityHammerSelectionGetter; private static Type? _infinityHammerObjectSelectionType; internal static void BeginNetworkSession(ZNet? znet) { if (!((Object)(object)znet == (Object)null) && _sessionZNet != znet) { ResetSessionState(); _sessionZNet = znet; } } internal static void Register(Game game) { if (!((Object)(object)game == (Object)null) && !((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer() && _registeredGame != game) { _registeredGame = game; _sessionZNet = ZNet.instance; long sessionGeneration = _sessionGeneration; ((MonoBehaviour)game).StartCoroutine(ServerRemovalLoop(game, _sessionZNet, sessionGeneration)); } } internal static void Shutdown() { ResetSessionState(); _sessionZNet = null; } internal static void RegisterPeer(ZNet znet, ZNetPeer peer) { if (!((Object)(object)znet == (Object)null) && peer?.m_rpc != null && !znet.IsServer()) { ResetClientQuotaState(); peer.m_rpc.Register("sighsorry.PortalRules.PolicyMessage.v2", (Action)OnPolicyMessage); peer.m_rpc.Register("sighsorry.PortalRules.QuotaState.v2", (Action)OnQuotaState); } } internal static void ForgetPeer(ZRpc? rpc) { if (rpc != null) { LastSentQuotaStates.Remove(rpc); if ((Object)(object)ZNet.instance != (Object)null && !ZNet.instance.IsServer() && rpc == ZNet.instance.GetServerRPC()) { ResetClientQuotaState(); } } } internal static void SendQuotaState(ZNetPeer? peer, bool force = false) { //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Expected O, but got Unknown if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer() && PublicPortalCatalog.IsServerReady && peer?.m_rpc != null && peer.IsReady() && peer.m_rpc.IsConnected() && PublicPortalCatalog.TryEnsurePeerIdentity(peer, out string steamId)) { QuotaState quotaState = BuildQuotaState(steamId); if (force || !LastSentQuotaStates.TryGetValue(peer.m_rpc, out var value) || !value.Matches(quotaState)) { ZPackage val = new ZPackage(); val.Write((byte)2); val.Write(quotaState.EffectiveLimit); val.Write(quotaState.CurrentCount); val.Write(quotaState.EffectiveInviteLimit); val.Write(quotaState.CurrentInviteCount); peer.m_rpc.Invoke("sighsorry.PortalRules.QuotaState.v2", new object[1] { val }); LastSentQuotaStates[peer.m_rpc] = quotaState; } } } internal static void BroadcastQuotaStates() { if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer()) { return; } foreach (ZNetPeer peer in ZNet.instance.GetPeers()) { SendQuotaState(peer); } RefreshLocalServerQuotaState(); } internal static void NotifyQuotaConfigurationChanged() { RefreshCountedPortalHashes(); if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer()) { PublicPortalCatalog.RefreshCountedPortalConfiguration(); } else { BroadcastQuotaStates(); } } private static QuotaState BuildQuotaState(string steamId) { int effectiveLimit = (PublicPortalConfig.EnableAccountPortalLimit.Value.IsOff() ? (-1) : PortalAccountStore.GetEffectivePortalLimit(steamId, PublicPortalConfig.MaxPortalsPerAccount.Value)); int currentCount = Math.Min(100000, PublicPortalCatalog.GetBuilderPortalCount(steamId)); int effectiveInvitePortalLimit = PortalAccountStore.GetEffectiveInvitePortalLimit(steamId, PublicPortalConfig.MaxInvitePortalsPerAccount.Value); int currentInviteCount = Math.Min(100000, PublicPortalCatalog.GetBuilderInvitePortalCount(steamId)); return new QuotaState(effectiveLimit, currentCount, effectiveInvitePortalLimit, currentInviteCount); } private static void RefreshLocalServerQuotaState() { if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer() && PublicPortalCatalog.IsServerReady && PublicPortalCatalog.TryEnsureLocalIdentity(out string steamId)) { QuotaState quotaState = BuildQuotaState(steamId); _clientEffectiveLimit = quotaState.EffectiveLimit; _clientCurrentCount = quotaState.CurrentCount; _clientEffectiveInviteLimit = quotaState.EffectiveInviteLimit; _clientCurrentInviteCount = quotaState.CurrentInviteCount; _hasClientQuotaState = true; } } internal static bool TryGetInviteQuotaState(string steamId, out int currentCount, out int effectiveLimit) { currentCount = 0; effectiveLimit = 0; if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || !PublicPortalCatalog.IsServerReady || !PublicPortalData.TryNormalizeSteamId64(steamId, out string steamId2)) { return false; } QuotaState quotaState = BuildQuotaState(steamId2); currentCount = quotaState.CurrentInviteCount; effectiveLimit = quotaState.EffectiveInviteLimit; return true; } internal static bool TryGetClanQuotaState(string clanId, out int currentCount, out int effectiveLimit) { currentCount = 0; effectiveLimit = Math.Max(-1, Math.Min(10000, PublicPortalConfig.MaxClanPortalsPerClan.Value)); string text = (clanId ?? "").Trim(); if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || !PublicPortalCatalog.IsServerReady || text.Length == 0) { return false; } currentCount = Math.Min(100000, PublicPortalCatalog.GetClanPortalCount(text)); return true; } internal static bool IsCountedPortalPrefab(int prefabHash) { RefreshCountedPortalHashes(); if (prefabHash != 0 && !PublicPortalKinds.IsAdminPortalPrefab(prefabHash)) { return CountedPortalHashes.Contains(prefabHash); } return false; } internal static bool TryAcceptNewPortal(ZDO zdo, PortalBuilder builder, ZNetPeer? sourcePeer) { //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_006d: Unknown result type (might be due to invalid IL or missing references) if (zdo == null || !IsCountedPortalPrefab(zdo.GetPrefab()) || PublicPortalConfig.EnableAccountPortalLimit.Value.IsOff()) { return true; } if (!PublicPortalData.TryNormalizeSteamId64(builder.AccountId, out string steamId)) { RejectUnverifiedPortal(zdo, sourcePeer); return false; } int effectivePortalLimit = PortalAccountStore.GetEffectivePortalLimit(steamId, PublicPortalConfig.MaxPortalsPerAccount.Value); if (effectivePortalLimit < 0) { return true; } int builderPortalCount = PublicPortalCatalog.GetBuilderPortalCount(steamId); if (builderPortalCount < effectivePortalLimit) { return true; } if (PublicPortalCatalog.MarkPendingRemoval(zdo.m_uid)) { QueueRemoval(zdo.m_uid); } SendPolicyMessage(sourcePeer, new PortalRulesMessage("$sighsorry_portalrules_portal_limit_reached", builderPortalCount.ToString(CultureInfo.InvariantCulture), effectivePortalLimit.ToString(CultureInfo.InvariantCulture))); SendQuotaState(sourcePeer, force: true); PortalRulesPlugin.PortalRulesLogger.LogWarning((object)($"Rejected portal {zdo.m_uid} from {steamId}: " + $"account already has {builderPortalCount}/{effectivePortalLimit} counted portals.")); return false; } internal static void RejectUnverifiedPortal(ZDO zdo, ZNetPeer? sourcePeer) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) if (zdo != null) { if (PublicPortalCatalog.MarkPendingRemoval(zdo.m_uid)) { QueueRemoval(zdo.m_uid); } SendPolicyMessage(sourcePeer, new PortalRulesMessage("$sighsorry_portalrules_portal_placement_account_unverified")); PortalRulesPlugin.PortalRulesLogger.LogWarning((object)$"Rejected unverified portal creation {zdo.m_uid}."); } } internal static bool QueueAdminPortalRemoval(ZDO zdo) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) if (zdo == null || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || !zdo.IsValid() || !PublicPortalInteraction.IsAdminPortal(zdo) || !PublicPortalCatalog.MarkPendingRemoval(zdo.m_uid)) { return false; } QueueRemoval(zdo.m_uid); PublicPortalCatalog.RefreshAndBroadcast(); PublicPortalTaggedConnections.RefreshConnections(); return true; } internal static bool ShouldBlockLocalPlacement(Piece piece, out string message) { message = ""; if ((Object)(object)piece == (Object)null || PublicPortalConfig.EnableAccountPortalLimit.Value.IsOff() || !IsCountedPortalPrefab(StringExtensionMethods.GetStableHashCode(Utils.GetPrefabName(((Component)piece).gameObject)))) { return false; } if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer() && IsInfinityHammerNoCreatorActive()) { return false; } string text = PortalRulesLocalization.Translate("$sighsorry_portalrules_portal_limit_syncing"); int num; int num2; if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer()) { if (!PublicPortalCatalog.IsServerReady || !PublicPortalCatalog.TryEnsureLocalIdentity(out string steamId)) { message = text; return true; } num = PortalAccountStore.GetEffectivePortalLimit(steamId, PublicPortalConfig.MaxPortalsPerAccount.Value); num2 = PublicPortalCatalog.GetBuilderPortalCount(steamId); } else { if (!_hasClientQuotaState) { message = text; return true; } num = _clientEffectiveLimit; num2 = _clientCurrentCount; } if (num < 0) { return false; } if (num2 < num) { return false; } message = PortalRulesLocalization.Translate("$sighsorry_portalrules_portal_limit_placement_blocked", num.ToString(CultureInfo.InvariantCulture)); return true; } private static bool IsInfinityHammerNoCreatorActive() { try { if (!TryBindInfinityHammerCompat()) { return false; } object obj = _infinityHammerNoCreatorGetter?.Invoke(null, null); if (!(obj is bool) || !(bool)obj) { return false; } object obj2 = _infinityHammerSelectionGetter?.Invoke(null, null); return obj2 != null && _infinityHammerObjectSelectionType.IsInstanceOfType(obj2); } catch (Exception) { return false; } } private static bool TryBindInfinityHammerCompat() { if (_infinityHammerNoCreatorGetter != null && _infinityHammerSelectionGetter != null && _infinityHammerObjectSelectionType != null) { return true; } if (!Chainloader.PluginInfos.TryGetValue("infinity_hammer", out var value)) { return false; } Assembly obj = ((object)value.Instance)?.GetType().Assembly; Type type = obj?.GetType("InfinityHammer.Configuration", throwOnError: false, ignoreCase: false); Type type2 = obj?.GetType("InfinityHammer.Selection", throwOnError: false, ignoreCase: false); Type type3 = obj?.GetType("InfinityHammer.ObjectSelection", throwOnError: false, ignoreCase: false); MethodInfo methodInfo = type?.GetProperty("NoCreator", BindingFlags.Static | BindingFlags.Public)?.GetGetMethod(nonPublic: false); MethodInfo methodInfo2 = type2?.GetMethod("Get", BindingFlags.Static | BindingFlags.Public, null, Type.EmptyTypes, null); if (methodInfo?.ReturnType != typeof(bool) || methodInfo2 == null || type3 == null) { return false; } _infinityHammerNoCreatorGetter = methodInfo; _infinityHammerSelectionGetter = methodInfo2; _infinityHammerObjectSelectionType = type3; return true; } private static IEnumerator ServerRemovalLoop(Game game, ZNet sessionZNet, long generation) { while (IsActiveSession(game, sessionZNet, generation)) { PromoteDeferredRemovals(); if (PendingRemovals.Count > 0) { ProcessPendingRemovals(10); yield return null; } else { yield return (object)new WaitForSeconds(1f); } } } private static void QueueRemoval(ZDOID portalId) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) if (!((ZDOID)(ref portalId)).IsNone()) { PendingRemovals.Enqueue(new PendingRemoval(portalId)); } } private static void ProcessPendingRemovals(int maximumCount) { //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00ca: Unknown result type (might be due to invalid IL or missing references) int num = Math.Min(Math.Max(0, maximumCount), PendingRemovals.Count); for (int i = 0; i < num; i++) { PendingRemoval pendingRemoval = PendingRemovals.Dequeue(); try { DestroyPortal(pendingRemoval); } catch (Exception ex) { pendingRemoval.Attempts++; pendingRemoval.NextAttemptAt = Time.realtimeSinceStartup + Math.Min(60f, (float)Math.Pow(2.0, Math.Min(pendingRemoval.Attempts, 6))); DeferredRemovals[pendingRemoval.PortalId] = pendingRemoval; if (pendingRemoval.Attempts == 1) { PortalRulesPlugin.PortalRulesLogger.LogError((object)$"Failed to remove portal {pendingRemoval.PortalId}: {ex.Message}"); } else if (pendingRemoval.Attempts == 5) { PortalRulesPlugin.PortalRulesLogger.LogWarning((object)($"Portal {pendingRemoval.PortalId} removal has failed repeatedly; " + "the server will retry with backoff. Last error: " + ex.Message)); } } } } private static void PromoteDeferredRemovals() { //IL_0076: Unknown result type (might be due to invalid IL or missing references) if (DeferredRemovals.Count == 0) { return; } float realtimeSinceStartup = Time.realtimeSinceStartup; List list = new List(); foreach (PendingRemoval value in DeferredRemovals.Values) { if (value.NextAttemptAt <= realtimeSinceStartup) { list.Add(value); } } foreach (PendingRemoval item in list) { DeferredRemovals.Remove(item.PortalId); PendingRemovals.Enqueue(item); } } private static void DestroyPortal(PendingRemoval removal) { //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_002d: 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) ZDOMan instance = ZDOMan.instance; if (instance == null) { throw new InvalidOperationException("ZDO manager is unavailable."); } ZDO zDO = instance.GetZDO(removal.PortalId); if (zDO == null || !zDO.IsValid()) { PublicPortalCatalog.NotifyPortalDestroyed(removal.PortalId); return; } zDO.SetOwner(ZDOMan.GetSessionID()); ZNetScene instance2 = ZNetScene.instance; GameObject val = ((instance2 != null) ? instance2.FindInstance(removal.PortalId) : null); if ((Object)(object)val != (Object)null) { try { WearNTear component = val.GetComponent(); ZNetView component2 = val.GetComponent(); bool flag = (Object)(object)component2 != (Object)null && component2.IsValid(); if ((Object)(object)component != (Object)null && flag) { component.Remove(true); } else { instance2.Destroy(val); if (!flag) { instance.DestroyZDO(zDO); } } return; } catch (Exception ex) { PortalRulesPlugin.PortalRulesLogger.LogError((object)($"Portal instance removal failed for {removal.PortalId}; " + "falling back to ZDO removal: " + ex.Message)); instance.DestroyZDO(zDO); return; } } instance.DestroyZDO(zDO); } internal static void SendPolicyMessage(ZNetPeer? peer, PortalRulesMessage message) { //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Expected O, but got Unknown if (message.IsEmpty) { return; } if (peer != null) { if (peer.m_rpc != null && peer.m_rpc.IsConnected()) { ZPackage val = new ZPackage(); message.Write(val); peer.m_rpc.Invoke("sighsorry.PortalRules.PolicyMessage.v2", new object[1] { val }); } } else { Player localPlayer = Player.m_localPlayer; if (localPlayer != null) { ((Character)localPlayer).Message((MessageType)2, message.Localize(), 0, (Sprite)null); } } } private static void OnPolicyMessage(ZRpc rpc, ZPackage package) { if ((Object)(object)ZNet.instance == (Object)null || ZNet.instance.IsServer() || rpc != ZNet.instance.GetServerRPC()) { return; } try { if (!PortalRulesMessage.TryRead(package, out var message)) { PortalRulesPlugin.PortalRulesLogger.LogWarning((object)"Ignored a malformed PortalRules policy message from the server."); return; } Player localPlayer = Player.m_localPlayer; if (localPlayer != null) { ((Character)localPlayer).Message((MessageType)2, message.Localize(), 0, (Sprite)null); } } catch (Exception ex) { PortalRulesPlugin.PortalRulesLogger.LogWarning((object)("Failed to read a PortalRules policy message: " + ex.Message)); } } private static void OnQuotaState(ZRpc rpc, ZPackage package) { if ((Object)(object)ZNet.instance == (Object)null || ZNet.instance.IsServer() || rpc != ZNet.instance.GetServerRPC()) { PortalRulesPlugin.PortalRulesLogger.LogWarning((object)"Ignored portal quota state from a non-server connection."); return; } try { byte num = package.ReadByte(); int num2 = package.ReadInt(); int num3 = package.ReadInt(); int num4 = package.ReadInt(); int num5 = package.ReadInt(); if (num != 2 || num2 < -1 || num2 > 10000 || num3 < 0 || num3 > 100000 || num4 < -1 || num4 > 10000 || num5 < 0 || num5 > 100000) { PortalRulesPlugin.PortalRulesLogger.LogWarning((object)"Ignored malformed portal quota state from the server."); return; } _clientEffectiveLimit = num2; _clientCurrentCount = num3; _clientEffectiveInviteLimit = num4; _clientCurrentInviteCount = num5; _hasClientQuotaState = true; } catch (Exception ex) { PortalRulesPlugin.PortalRulesLogger.LogError((object)("Failed to read portal quota state: " + ex.Message)); } } private static void RefreshCountedPortalHashes() { string text = PublicPortalConfig.CountedPortalPrefabs.Value ?? ""; if (string.Equals(text, _cachedCountedPrefabSetting, StringComparison.Ordinal)) { return; } _cachedCountedPrefabSetting = text; CountedPortalHashes.Clear(); string[] array = text.Split(new char[1] { ',' }); for (int i = 0; i < array.Length; i++) { string text2 = array[i].Trim(); if (!string.IsNullOrWhiteSpace(text2)) { CountedPortalHashes.Add(StringExtensionMethods.GetStableHashCode(text2)); } } } private static bool IsActiveSession(Game game, ZNet sessionZNet, long generation) { if (generation == _sessionGeneration && _registeredGame == game && _sessionZNet == sessionZNet && ZNet.instance == sessionZNet) { return sessionZNet.IsServer(); } return false; } private static void ResetSessionState() { _sessionGeneration++; _registeredGame = null; PendingRemovals.Clear(); DeferredRemovals.Clear(); LastSentQuotaStates.Clear(); ResetClientQuotaState(); } private static void ResetClientQuotaState() { _hasClientQuotaState = false; _clientEffectiveLimit = -1; _clientCurrentCount = 0; _clientEffectiveInviteLimit = -1; _clientCurrentInviteCount = 0; } } internal static class PublicPortalAccess { public static bool CanUsePortal(ZDO zdo) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) if (zdo == null) { return false; } if (PublicPortalCatalog.TryCanLocalServerUserUsePortal(zdo.m_uid, out var canUse)) { return canUse; } if ((Object)(object)ZNet.instance != (Object)null && !ZNet.instance.IsServer()) { if (PublicPortalCatalog.HasSnapshot && PublicPortalCatalog.TryGetClientEntry(zdo.m_uid, out var entry)) { return CanUsePortal(entry); } return false; } if (!CanUsePortal(PublicPortalCatalog.GetEffectiveAccessMode(zdo), PublicPortalCatalog.GetEffectiveOwner(zdo))) { return false; } return RequiredGlobalKeyAccess.IsPresent(RequiredGlobalKeyAccess.QueryLocal(PublicPortalCatalog.GetEffectiveRequiredGlobalKey(zdo))); } public static bool CanUsePortal(PublicPortalCatalogEntry portal) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) if (PublicPortalCatalog.TryCanLocalServerUserUsePortal(portal.Id, out var canUse)) { return canUse; } if (portal.AccessMode == PublicPortalAccessMode.Clan && (Object)(object)ZNet.instance != (Object)null && !ZNet.instance.IsServer()) { return true; } return CanUsePortal(portal.AccessMode, portal.Owner); } private static bool CanUsePortal(PublicPortalAccessMode mode, PortalOwner owner) { if ((mode == PublicPortalAccessMode.Public || (uint)(mode - 4) <= 1u) ? true : false) { return true; } if (PublicPortalData.IsLocalOwner(owner)) { return true; } return mode switch { PublicPortalAccessMode.Admin => PortalRulesPlugin.IsAdmin, PublicPortalAccessMode.Clan => false, _ => false, }; } public static bool CanEditPortal(ZDO zdo) { if (zdo == null) { return false; } if (PublicPortalInteraction.IsAdminPortal(zdo)) { return PortalRulesPlugin.HasAdminDebugAccess; } PortalBuilder builder = PublicPortalData.GetBuilder(zdo); if (!builder.IsValid) { return false; } if (PublicPortalData.TryGetLocalSteamId64(out string steamId) && string.Equals(steamId, builder.AccountId, StringComparison.Ordinal)) { return true; } if (PublicPortalCatalog.GetEffectiveAccessMode(zdo) == PublicPortalAccessMode.Invite) { return false; } if (!PortalRulesPlugin.HasAdminDebugAccess) { return ClanPortalAccess.IsRequesterInBuilderPrimaryClan(builder, null); } return true; } } [HarmonyPatch] internal static class PublicPortalInteraction { [HarmonyPatch(typeof(Game), "Start")] private static class RegisterRpcPatch { private static void Postfix(Game __instance) { LastAccessModeRequestAt.Clear(); PublicPortalMapController.Instance.End(); PublicPortalCatalog.Register(__instance); PublicPortalServerPolicy.Register(__instance); } } [HarmonyPatch(typeof(ZNet), "OnNewConnection")] private static class RegisterPeerRpcPatch { private static void Postfix(ZNet __instance, ZNetPeer peer) { PublicPortalCatalog.RegisterPeer(__instance, peer); PublicPortalServerPolicy.RegisterPeer(__instance, peer); PublicPortalTeleportService.RegisterPeer(__instance, peer); PublicPortalModeChangeEffects.RegisterPeer(__instance, peer); AdminPortalOperations.RegisterPeer(__instance, peer); if (__instance.IsServer()) { peer.m_rpc.Register("sighsorry.PortalRules.ChangeAccessMode.v4", (Action)OnRemoteAccessModeChange); } else { peer.m_rpc.Register("sighsorry.PortalRules.ChangeAccessModeResult.v5", (Action)OnAccessModeChangeResult); } } } [HarmonyPatch(typeof(ZNet), "Awake")] private static class BeginNetworkSessionPatch { private static void Postfix(ZNet __instance) { PublicPortalCatalog.BeginNetworkSession(__instance); PublicPortalServerPolicy.BeginNetworkSession(__instance); PublicPortalTeleportService.BeginNetworkSession(__instance); } } [HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")] private static class RegisterPeerIdentityPatch { private static void Postfix(ZNet __instance, ZRpc rpc) { RefreshPeerIdentity(__instance, rpc); } } [HarmonyPatch(typeof(ZNet), "RPC_CharacterID")] private static class RegisterPeerCharacterIdentityPatch { private static void Postfix(ZNet __instance, ZRpc rpc) { RefreshPeerIdentity(__instance, rpc); } } [HarmonyPatch(typeof(ZNet), "Disconnect")] private static class CleanupPeerStatePatch { private static void Prefix(ZNetPeer peer) { if (peer?.m_rpc != null) { LastAccessModeRequestAt.Remove(peer.m_rpc); PublicPortalCatalog.ForgetPeer(peer.m_rpc); PublicPortalServerPolicy.ForgetPeer(peer.m_rpc); PublicPortalTeleportService.ForgetPeer(peer.m_rpc); AdminPortalOperations.ForgetPeer(peer.m_rpc); } } } [HarmonyPatch(typeof(ZNet), "LoadWorld")] private static class ServerWorldLoadedPatch { private static void Postfix(ZNet __instance) { PublicPortalCatalog.NotifyServerWorldLoaded(__instance); } } [HarmonyPatch(typeof(TeleportWorld), "Awake")] private static class ObservePortalPatch { private static void Postfix(TeleportWorld __instance) { if (PublicPortalKinds.IsHandledPortal(__instance) && !((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer()) { ZDO portalZdo = PublicPortalKinds.GetPortalZdo(__instance); if (portalZdo != null) { PublicPortalCatalog.ObservePortal(portalZdo); } } } } [HarmonyPatch(typeof(ZDOMan), "AddPortal")] private static class ObserveAddedPortalPatch { private static void Postfix(ZDO zdo) { if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer()) { PublicPortalCatalog.ObservePortal(zdo); } } } [HarmonyPatch(typeof(ZDOMan), "RPC_ZDOData")] private static class BindPortalCreationToTransportPatch { private static void Prefix(ZRpc rpc, out PublicPortalCatalog.PortalSyncContext? __state) { __state = PublicPortalCatalog.BeginPortalSync(rpc); } private static Exception? Finalizer(Exception? __exception, PublicPortalCatalog.PortalSyncContext? __state) { PublicPortalCatalog.RestorePortalSync(__state); return __exception; } } [HarmonyPatch(typeof(ZDOMan), "CreateNewZDO", new Type[] { typeof(ZDOID), typeof(Vector3), typeof(int) })] private static class ObserveRemoteCreatedZdoPatch { private static void Postfix(ZDO __result) { PublicPortalCatalog.ObserveRemoteZdoCreated(__result); } } [HarmonyPatch(typeof(Player), "TryPlacePiece")] private static class PreviewPortalLimitPatch { private static bool Prefix(Player __instance, Piece piece, ref bool __result) { if ((Object)(object)__instance == (Object)null || (Object)(object)__instance != (Object)(object)Player.m_localPlayer || !PublicPortalServerPolicy.ShouldBlockLocalPlacement(piece, out string message)) { return true; } ((Character)__instance).Message((MessageType)2, message, 0, (Sprite)null); __result = false; return false; } } [HarmonyPatch(typeof(Player), "PlacePiece")] private static class BindLocalPortalPlacementPatch { private static void Prefix(Player __instance, out LocalPortalPlacementState __state) { __state = PublicPortalCatalog.BeginLocalPlacement(__instance); } private static Exception? Finalizer(Exception? __exception, LocalPortalPlacementState __state) { try { PublicPortalCatalog.CompleteLocalPlacement(__state); } catch (Exception ex) { PortalRulesPlugin.PortalRulesLogger.LogError((object)("Failed to finalize local portal placement authority: " + ex.Message)); } return __exception; } } [HarmonyPatch(typeof(ZDOMan), "HandleDestroyedZDO")] private static class ObserveDestroyedPortalPatch { private static void Prefix(ZDOID uid) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer()) { PublicPortalCatalog.NotifyPortalDestroyed(uid); } } } [HarmonyPatch(typeof(ZDOMan), "PrepareSave")] private static class ReconcileBeforeWorldSavePatch { private static void Prefix() { PublicPortalCatalog.ReconcileAuthorityToZdos(); PortalAccountStore.FlushPlayerIdentities(); InviteTravelCooldownStore.Flush(); } } [HarmonyPatch(typeof(TeleportWorld), "GetHoverText")] private static class HoverTextPatch { private static void Postfix(TeleportWorld __instance, ref string __result) { //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Unknown result type (might be due to invalid IL or missing references) if (!PublicPortalKinds.IsHandledPortal(__instance)) { return; } ZDO portalZdo = PublicPortalKinds.GetPortalZdo(__instance); if (portalZdo == null) { return; } PublicPortalAccessMode effectiveAccessMode = PublicPortalCatalog.GetEffectiveAccessMode(portalZdo); if (PublicPortalAccess.CanEditPortal(portalZdo)) { string text = ((Localization.instance != null) ? Localization.instance.Localize("$KEY_Use") : "$KEY_Use"); __result = __result + "\n" + PortalRulesLocalization.Translate("$sighsorry_portalrules_hover_change_access_mode", ((object)PublicPortalConfig.ToggleAccessKey.Value/*cast due to .constrained prefix*/).ToString(), text, AccessModeLabel(effectiveAccessMode)); } if (!IsAdminPortal(portalZdo) && effectiveAccessMode == PublicPortalAccessMode.Public && TryGetTemporaryPublicRemainingSeconds(portalZdo, out var remainingSeconds)) { __result = __result + "\n" + PortalRulesLocalization.Translate("$sighsorry_portalrules_hover_temporary_public_remaining", remainingSeconds.ToString()); } switch (effectiveAccessMode) { case PublicPortalAccessMode.Invite: { string text2 = PortalRulesLocalization.Translate("$sighsorry_portalrules_hover_invite_description"); string text3 = ""; if (PublicPortalCatalog.TryGetClientEntry(portalZdo.m_uid, out var entry2)) { string[] array2 = new string[2]; int modeCurrent = entry2.ModeCurrent; array2[0] = modeCurrent.ToString(); array2[1] = FormatPortalLimit(entry2.ModeLimit); text2 = PortalRulesLocalization.Translate("$sighsorry_portalrules_hover_invite_description_with_quota", array2); long remainingSeconds2 = InviteTravelCooldownStore.GetRemainingSeconds(entry2.InviteDepartureCooldownUntilUtc); if (remainingSeconds2 > 0) { text3 = PortalRulesLocalization.Translate("$sighsorry_portalrules_invite_departure_cooldown", InviteTravelCooldownStore.FormatRemaining(remainingSeconds2)); } } if (!string.IsNullOrEmpty(text3)) { __result = __result + "\n" + text3; } __result = __result + "\n" + text2; break; } case PublicPortalAccessMode.Clan: { if (PublicPortalCatalog.TryGetClientEntry(portalZdo.m_uid, out var entry)) { string obj = __result; string[] array = new string[2]; int modeCurrent = entry.ModeCurrent; array[0] = modeCurrent.ToString(); array[1] = FormatPortalLimit(entry.ModeLimit); __result = obj + "\n" + PortalRulesLocalization.Translate("$sighsorry_portalrules_hover_clan_quota", array); } break; } } if (IsAdminPortal(portalZdo) && PortalRulesPlugin.HasAdminDebugAccess) { string text4 = ((Localization.instance != null) ? Localization.instance.Localize("$KEY_Use") : "$KEY_Use"); string effectiveRequiredGlobalKey = PublicPortalCatalog.GetEffectiveRequiredGlobalKey(portalZdo); string text5 = (string.IsNullOrEmpty(effectiveRequiredGlobalKey) ? PortalRulesLocalization.Translate("$sighsorry_portalrules_none") : StringExtensionMethods.RemoveRichTextTags(effectiveRequiredGlobalKey)); __result = __result + "\n" + PortalRulesLocalization.Translate("$sighsorry_portalrules_hover_required_global_key", PortalRulesLocalization.Translate("$sighsorry_portalrules_key_alt"), text4, text5); } } } [HarmonyPatch(typeof(TeleportWorld), "Interact")] private static class ToggleModePatch { private static bool Prefix(TeleportWorld __instance, Humanoid human, bool hold, ref bool __result) { //IL_002b: Unknown result type (might be due to invalid IL or missing references) if (hold || (Object)(object)human != (Object)(object)Player.m_localPlayer || !PublicPortalKinds.IsHandledPortal(__instance)) { return true; } ZDO portalZdo = PublicPortalKinds.GetPortalZdo(__instance); if (portalZdo == null) { return true; } bool flag = PublicPortalConfig.ToggleAccessKey.Value.IsKeyHeld(); bool flag2 = IsAdminPortal(portalZdo); if (flag2 && !PortalRulesPlugin.HasAdminDebugAccess) { Player localPlayer = Player.m_localPlayer; if (localPlayer != null) { ((Character)localPlayer).Message((MessageType)2, PortalRulesLocalization.Translate("$sighsorry_portalrules_admin_portal_controls_require_admin_debug"), 0, (Sprite)null); } __result = true; return false; } bool flag3 = Input.GetKey((KeyCode)308) || Input.GetKey((KeyCode)307); if (flag2 && flag3) { AdminPortalOperations.OpenRequiredGlobalKeyInput(__instance); __result = true; return false; } if (flag2 && !flag) { TextInput instance = TextInput.instance; if (instance != null) { instance.RequestText((TextReceiver)(object)__instance, "$piece_portal_tag", 10); } __result = true; return false; } if (!flag) { return true; } if (!PublicPortalAccess.CanEditPortal(portalZdo)) { Player localPlayer2 = Player.m_localPlayer; if (localPlayer2 != null) { ((Character)localPlayer2).Message((MessageType)2, PortalRulesLocalization.Translate("$sighsorry_portalrules_access_cannot_be_changed"), 0, (Sprite)null); } __result = true; return false; } RequestAccessModeChange(portalZdo); __result = true; return false; } } private const int CycleAccessModeRequest = -1; private const float AccessModeRequestCooldownSeconds = 1f; private const float MaximumPortalInteractionDistance = 8f; private static readonly Dictionary LastAccessModeRequestAt = new Dictionary(); internal static void Shutdown() { LastAccessModeRequestAt.Clear(); } internal static bool IsAdminPortal(ZDO zdo) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) if (zdo == null) { return false; } if (PublicPortalKinds.IsAdminPortalPrefab(zdo.GetPrefab()) || PublicPortalCatalog.IsAuthoritativeAdminPortal(zdo.m_uid)) { return true; } if (PublicPortalCatalog.TryGetClientEntry(zdo.m_uid, out var entry)) { return PublicPortalKinds.IsAdminPortalPrefab(entry.PrefabHash); } return false; } internal static string AccessModeLabel(PublicPortalAccessMode mode) { return PortalRulesLocalization.Translate(AccessModeToken(mode)); } private static string AccessModeToken(PublicPortalAccessMode mode) { return mode switch { PublicPortalAccessMode.Personal => "$sighsorry_portalrules_access_mode_personal", PublicPortalAccessMode.Admin => "$sighsorry_portalrules_access_mode_admin", PublicPortalAccessMode.Public => "$sighsorry_portalrules_access_mode_public", PublicPortalAccessMode.Invite => "$sighsorry_portalrules_access_mode_invite", PublicPortalAccessMode.Clan => "$sighsorry_portalrules_access_mode_clan", PublicPortalAccessMode.Tagged => "$sighsorry_portalrules_access_mode_tagged", _ => "$sighsorry_portalrules_access_mode_personal", }; } private static PublicPortalAccessMode NextAccessModeCandidate(PublicPortalAccessMode mode, bool isAdminPortal) { if (isAdminPortal) { return mode switch { PublicPortalAccessMode.Admin => PublicPortalAccessMode.Public, PublicPortalAccessMode.Public => PublicPortalAccessMode.Tagged, PublicPortalAccessMode.Tagged => PublicPortalAccessMode.Admin, _ => PublicPortalAccessMode.Public, }; } return mode switch { PublicPortalAccessMode.Personal => PublicPortalAccessMode.Admin, PublicPortalAccessMode.Admin => PublicPortalAccessMode.Public, PublicPortalAccessMode.Public => PublicPortalAccessMode.Invite, PublicPortalAccessMode.Invite => PublicPortalAccessMode.Clan, PublicPortalAccessMode.Clan => PublicPortalAccessMode.Tagged, PublicPortalAccessMode.Tagged => PublicPortalAccessMode.Personal, _ => PublicPortalAccessMode.Personal, }; } private static void RequestAccessModeChange(ZDO zdo) { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ZNet.instance == (Object)null) { Player localPlayer = Player.m_localPlayer; if (localPlayer != null) { ((Character)localPlayer).Message((MessageType)2, PortalRulesLocalization.Translate("$sighsorry_portalrules_access_change_server_unavailable"), 0, (Sprite)null); } return; } if (ZNet.instance.IsServer()) { ShowAccessModeResult(TryApplyAccessModeChange(zdo.m_uid, -1, null, PortalRulesPlugin.IsAdmin, Player.m_debugMode, out var appliedMode, out var message), appliedMode, message); return; } ZRpc serverRPC = ZNet.instance.GetServerRPC(); if (serverRPC == null) { Player localPlayer2 = Player.m_localPlayer; if (localPlayer2 != null) { ((Character)localPlayer2).Message((MessageType)2, PortalRulesLocalization.Translate("$sighsorry_portalrules_access_change_connection_unavailable"), 0, (Sprite)null); } } else { serverRPC.Invoke("sighsorry.PortalRules.ChangeAccessMode.v4", new object[3] { zdo.m_uid, -1, Player.m_debugMode }); } } private static void OnRemoteAccessModeChange(ZRpc rpc, ZDOID portalId, int accessMode, bool debugEnabled) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) ZNet instance = ZNet.instance; if ((Object)(object)instance == (Object)null || !instance.IsServer()) { return; } ZNetPeer val = PublicPortalData.FindPeer(instance, rpc); if (val == null || !val.IsReady() || ((ZDOID)(ref val.m_characterID)).IsNone()) { SendAccessModeResult(rpc, portalId, success: false, PublicPortalAccessMode.Personal, new PortalRulesMessage("$sighsorry_portalrules_access_change_unauthenticated")); return; } float realtimeSinceStartup = Time.realtimeSinceStartup; if (LastAccessModeRequestAt.TryGetValue(rpc, out var value) && realtimeSinceStartup - value < 1f) { SendAccessModeResult(rpc, portalId, success: false, PublicPortalAccessMode.Personal, new PortalRulesMessage("$sighsorry_portalrules_access_change_rate_limited")); return; } LastAccessModeRequestAt[rpc] = realtimeSinceStartup; if (!PublicPortalData.TryGetPeerOwner(val, out var owner)) { SendAccessModeResult(rpc, portalId, success: false, PublicPortalAccessMode.Personal, new PortalRulesMessage("$sighsorry_portalrules_platform_identity_unverified")); return; } bool requesterIsAdmin = PublicPortalData.IsPeerAdmin(instance, val); PublicPortalAccessMode appliedMode; PortalRulesMessage message; bool flag = TryApplyAccessModeChange(portalId, accessMode, val, requesterIsAdmin, debugEnabled, out appliedMode, out message); if (!flag) { PortalRulesPlugin.PortalRulesLogger.LogWarning((object)$"Rejected portal access request from {owner.Id} for {portalId}: {message.Token}"); } SendAccessModeResult(rpc, portalId, flag, appliedMode, message); } private static void OnAccessModeChangeResult(ZRpc rpc, ZPackage package) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ZNet.instance == (Object)null || ZNet.instance.IsServer() || rpc != ZNet.instance.GetServerRPC()) { return; } ZDOID val; bool flag; int num; PortalRulesMessage message; try { val = package.ReadZDOID(); flag = package.ReadBool(); num = package.ReadInt(); if (!PortalRulesMessage.TryRead(package, out message)) { PortalRulesPlugin.PortalRulesLogger.LogWarning((object)"Rejected malformed portal access result from the server."); return; } } catch (Exception ex) { PortalRulesPlugin.PortalRulesLogger.LogWarning((object)("Rejected malformed portal access result from the server: " + ex.Message)); return; } if (((ZDOID)(ref val)).IsNone()) { PortalRulesPlugin.PortalRulesLogger.LogWarning((object)"Rejected portal access result without a portal ID."); return; } if (!Enum.IsDefined(typeof(PublicPortalAccessMode), num)) { PortalRulesPlugin.PortalRulesLogger.LogWarning((object)$"Rejected portal access result with unknown mode {num}."); return; } PublicPortalAccessMode accessMode = (PublicPortalAccessMode)num; if (flag) { PublicPortalCatalog.RequestRefresh(force: true); } ShowAccessModeResult(flag, accessMode, message); } private static bool TryApplyAccessModeChange(ZDOID portalId, int rawAccessMode, ZNetPeer? requesterPeer, bool requesterIsAdmin, bool requesterDebugEnabled, out PublicPortalAccessMode appliedMode, out PortalRulesMessage message) { //IL_005d: 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_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_022d: Unknown result type (might be due to invalid IL or missing references) //IL_027f: Unknown result type (might be due to invalid IL or missing references) appliedMode = PublicPortalAccessMode.Personal; if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || ZDOMan.instance == null) { message = new PortalRulesMessage("$sighsorry_portalrules_server_not_ready"); return false; } if (rawAccessMode != -1) { message = new PortalRulesMessage("$sighsorry_portalrules_access_change_invalid_operation"); return false; } ZDO zDO = ZDOMan.instance.GetZDO(portalId); if (zDO == null || !zDO.IsValid() || !ZDOMan.instance.GetPortals().Contains(zDO) || !PublicPortalKinds.IsHandledPortal(zDO)) { message = new PortalRulesMessage("$sighsorry_portalrules_destination_not_managed_portal"); return false; } if (!TryValidatePortalInteraction(zDO, requesterPeer, out message)) { return false; } if (!PublicPortalCatalog.TryGetAuthoritativeAccess(portalId, out var accessMode, out var owner)) { PublicPortalCatalog.ObservePortal(zDO); if (!PublicPortalCatalog.TryGetAuthoritativeAccess(portalId, out accessMode, out owner)) { message = new PortalRulesMessage("$sighsorry_portalrules_portal_missing_from_catalog"); return false; } } bool flag = IsAdminPortal(zDO); if (flag && (!requesterIsAdmin || !requesterDebugEnabled)) { message = new PortalRulesMessage("$sighsorry_portalrules_admin_portal_change_requires_admin_debug"); return false; } PortalBuilder builder = new PortalBuilder("", ""); if (!flag && (!PublicPortalCatalog.TryGetAuthoritativeBuilder(portalId, out builder) || !builder.IsValid)) { message = new PortalRulesMessage("$sighsorry_portalrules_portal_builder_unverified"); return false; } appliedMode = accessMode; string steamId; bool flag2 = ((requesterPeer != null) ? PublicPortalData.TryGetPeerSteamId64(requesterPeer, out steamId) : PublicPortalData.TryGetLocalSteamId64(out steamId)) && builder.IsValid && string.Equals(steamId, builder.AccountId, StringComparison.Ordinal); if (!flag && accessMode == PublicPortalAccessMode.Invite && !flag2) { message = new PortalRulesMessage("$sighsorry_portalrules_invite_access_change_builder_only"); return false; } if (!flag && !flag2 && !(requesterIsAdmin && requesterDebugEnabled) && !ClanPortalAccess.IsRequesterInBuilderPrimaryClan(builder, requesterPeer)) { message = new PortalRulesMessage("$sighsorry_portalrules_access_cannot_be_changed"); return false; } if (!TryResolveNextAccessMode(accessMode, flag, requesterIsAdmin, flag2, builder, out PublicPortalAccessMode nextMode, out string authorizedClanId, out string authorizedClanName)) { message = new PortalRulesMessage("$sighsorry_portalrules_no_eligible_access_mode"); return false; } if (!PublicPortalCatalog.SetAuthoritativeAccess(zDO, nextMode, authorizedClanId)) { message = new PortalRulesMessage("$sighsorry_portalrules_catalog_update_failed"); return false; } appliedMode = nextMode; if (nextMode != accessMode) { PublicPortalModeChangeEffects.Broadcast(zDO.m_uid); PublicPortalTaggedConnections.RefreshConnections(); } string text = AccessModeToken(nextMode); int remainingSeconds; if (nextMode == PublicPortalAccessMode.Clan && !string.IsNullOrWhiteSpace(authorizedClanName)) { message = new PortalRulesMessage("$sighsorry_portalrules_access_changed_clan", text, authorizedClanName); } else if (!flag && nextMode == PublicPortalAccessMode.Public && PublicPortalCatalog.TryGetTemporaryPublicRemainingSeconds(zDO.m_uid, out remainingSeconds)) { message = new PortalRulesMessage("$sighsorry_portalrules_access_changed_temporary", text, remainingSeconds.ToString(CultureInfo.InvariantCulture)); } else { message = new PortalRulesMessage("$sighsorry_portalrules_access_changed", text); } return true; } private static bool TryResolveNextAccessMode(PublicPortalAccessMode currentMode, bool isAdminPortal, bool requesterIsAdmin, bool requesterIsBuilder, PortalBuilder builder, out PublicPortalAccessMode nextMode, out string authorizedClanId, out string authorizedClanName) { nextMode = currentMode; authorizedClanId = ""; authorizedClanName = ""; int num = (isAdminPortal ? 3 : 6); for (int i = 0; i < num; i++) { authorizedClanId = ""; authorizedClanName = ""; PublicPortalAccessMode publicPortalAccessMode = (nextMode = NextAccessModeCandidate(nextMode, isAdminPortal)); if ((publicPortalAccessMode == PublicPortalAccessMode.Admin && !requesterIsAdmin) || (publicPortalAccessMode == PublicPortalAccessMode.Invite && (!requesterIsBuilder || !PublicPortalServerPolicy.TryGetInviteQuotaState(builder.AccountId, out var currentCount, out var effectiveLimit) || (effectiveLimit >= 0 && currentCount >= effectiveLimit)))) { continue; } bool flag = !isAdminPortal; if (flag) { bool flag2 = (uint)(publicPortalAccessMode - 3) <= 1u; flag = flag2; } if (flag) { PortalClanMembership membership = default(PortalClanMembership); bool flag3 = ClanPortalAccess.IsServerRegistryAvailable && ClanPortalAccess.TryResolveBuilderMembership(builder, out membership) && membership.HasPrimaryClan; if (publicPortalAccessMode == PublicPortalAccessMode.Clan && (!flag3 || !PublicPortalServerPolicy.TryGetClanQuotaState(membership.PrimaryClanId, out var currentCount2, out var effectiveLimit2) || (effectiveLimit2 >= 0 && currentCount2 >= effectiveLimit2))) { continue; } if (flag3) { authorizedClanId = membership.PrimaryClanId; authorizedClanName = membership.PrimaryClanName; } } return true; } nextMode = currentMode; authorizedClanId = ""; authorizedClanName = ""; return false; } internal static bool TryValidatePortalInteraction(ZDO zdo, ZNetPeer? requesterPeer, out PortalRulesMessage message) { //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) message = new PortalRulesMessage("$sighsorry_portalrules_move_closer_to_portal"); if (zdo == null || !zdo.IsValid() || (Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || ZDOMan.instance == null || !ZDOMan.instance.GetPortals().Contains(zdo) || !PublicPortalKinds.IsHandledPortal(zdo)) { message = new PortalRulesMessage("$sighsorry_portalrules_requested_portal_unavailable"); return false; } Vector3 position; if (requesterPeer != null) { if (!PublicPortalData.TryGetAuthenticatedPeerCharacterPosition(requesterPeer, out position)) { message = new PortalRulesMessage("$sighsorry_portalrules_active_character_unverified"); return false; } } else { Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer == (Object)null) { message = new PortalRulesMessage("$sighsorry_portalrules_local_player_unverified"); return false; } position = ((Component)localPlayer).transform.position; } Vector3 position2 = zdo.GetPosition(); if (!IsFinite(position) || !IsFinite(position2) || Vector3.Distance(position, position2) > 8f) { return false; } message = PortalRulesMessage.Empty; return true; } private static bool IsFinite(Vector3 value) { //IL_0000: 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_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) if (!float.IsNaN(value.x) && !float.IsInfinity(value.x) && !float.IsNaN(value.y) && !float.IsInfinity(value.y) && !float.IsNaN(value.z)) { return !float.IsInfinity(value.z); } return false; } private static void SendAccessModeResult(ZRpc rpc, ZDOID portalId, bool success, PublicPortalAccessMode accessMode, PortalRulesMessage message) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown //IL_0007: Unknown result type (might be due to invalid IL or missing references) ZPackage val = new ZPackage(); val.Write(portalId); val.Write(success); val.Write((int)accessMode); message.Write(val); rpc.Invoke("sighsorry.PortalRules.ChangeAccessModeResult.v5", new object[1] { val }); } private static void ShowAccessModeResult(bool success, PublicPortalAccessMode accessMode, PortalRulesMessage message) { string text = ((!message.IsEmpty) ? message.Localize() : (success ? PortalRulesLocalization.Translate("$sighsorry_portalrules_access_changed", AccessModeLabel(accessMode)) : PortalRulesLocalization.Translate("$sighsorry_portalrules_access_change_failed"))); Player localPlayer = Player.m_localPlayer; if (localPlayer != null) { ((Character)localPlayer).Message((MessageType)2, text, 0, (Sprite)null); } } private static void RefreshPeerIdentity(ZNet znet, ZRpc rpc) { if (!((Object)(object)znet == (Object)null) && znet.IsServer()) { PublicPortalServerPolicy.SendQuotaState(PublicPortalData.FindPeer(znet, rpc), force: true); } } private static bool TryGetTemporaryPublicRemainingSeconds(ZDO zdo, out int remainingSeconds) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) if (PublicPortalCatalog.TryGetTemporaryPublicRemainingSeconds(zdo.m_uid, out remainingSeconds)) { return true; } long num = PublicPortalData.GetPublicExpiresAtUtcSeconds(zdo) - PublicPortalCatalog.GetEstimatedServerUtcNowSeconds(); if (num <= 0) { remainingSeconds = 0; return false; } remainingSeconds = (int)Math.Min(2147483647L, num); return true; } private static string FormatPortalLimit(int limit) { if (limit >= 0) { return limit.ToString(); } return "∞"; } } internal static class PublicPortalKinds { internal const string WoodPortalPrefabName = "portal_wood"; internal const string StonePortalPrefabName = "portal_stone"; internal const string AdminWoodPortalPrefabName = "admin_portal_wood"; internal const string AdminStonePortalPrefabName = "admin_portal_stone"; private static readonly int WoodPortalHash = StringExtensionMethods.GetStableHashCode("portal_wood"); private static readonly int StonePortalHash = StringExtensionMethods.GetStableHashCode("portal_stone"); private static readonly int AdminWoodPortalHash = StringExtensionMethods.GetStableHashCode("admin_portal_wood"); private static readonly int AdminStonePortalHash = StringExtensionMethods.GetStableHashCode("admin_portal_stone"); internal static bool IsAdminPortalPrefab(int prefabHash) { if (prefabHash != AdminWoodPortalHash) { return prefabHash == AdminStonePortalHash; } return true; } internal static bool PrefabAllowsAllItems(int prefabHash) { if (IsAdminPortalPrefab(prefabHash)) { return true; } GameObject val = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab(prefabHash) : null); TeleportWorld val2 = (((Object)(object)val != (Object)null) ? val.GetComponent() : null); if ((Object)(object)val2 != (Object)null) { return val2.m_allowAllItems; } return false; } internal static bool IsHandledPortal(TeleportWorld portal) { if ((Object)(object)portal == (Object)null) { return false; } if (PublicPortalConfig.LimitToVanillaPortals.Value.IsOff()) { return true; } string prefabName = Utils.GetPrefabName(((Component)portal).gameObject); switch (prefabName) { default: return prefabName == "admin_portal_stone"; case "portal_wood": case "portal_stone": case "admin_portal_wood": return true; } } internal static bool IsHandledPortal(ZDO zdo) { if (zdo == null) { return false; } if (PublicPortalConfig.LimitToVanillaPortals.Value.IsOff()) { return true; } int prefab = zdo.GetPrefab(); if (prefab != WoodPortalHash && prefab != StonePortalHash) { return IsAdminPortalPrefab(prefab); } return true; } internal static ZDO? GetPortalZdo(TeleportWorld portal) { if ((Object)(object)portal == (Object)null) { return null; } ZNetView component = ((Component)portal).GetComponent(); if (!((Object)(object)component != (Object)null) || !component.IsValid()) { return null; } return component.GetZDO(); } } [HarmonyPatch] internal static class PublicPortalMap { [HarmonyPatch(typeof(Minimap), "UpdateMap")] private static class PortalMapWheelZoomPatch { private const int MouseWheelClampSearchWindow = 12; private static IEnumerable Transpiler(IEnumerable instructions) { MethodInfo getMouseScrollWheel = AccessTools.DeclaredMethod(typeof(ZInput), "GetMouseScrollWheel", (Type[])null, (Type[])null); MethodInfo clampFloat = AccessTools.DeclaredMethod(typeof(Mathf), "Clamp", new Type[3] { typeof(float), typeof(float), typeof(float) }, (Type[])null); MethodInfo clampPortalMapMouseWheel = AccessTools.DeclaredMethod(typeof(PublicPortalMap), "ClampPortalMapMouseWheel", (Type[])null, (Type[])null); int clampSearchRemaining = 0; bool foundMinimumClamp = false; bool foundMaximumClamp = false; bool replaced = false; foreach (CodeInstruction instruction in instructions) { if (!replaced && CodeInstructionExtensions.Calls(instruction, getMouseScrollWheel)) { clampSearchRemaining = 12; foundMinimumClamp = false; foundMaximumClamp = false; } else if (!replaced && clampSearchRemaining > 0) { if (instruction.opcode == OpCodes.Ldc_R4 && instruction.operand is float num) { foundMinimumClamp = foundMinimumClamp || num == -0.05f; foundMaximumClamp = foundMaximumClamp || num == 0.05f; } else if (foundMinimumClamp && foundMaximumClamp && CodeInstructionExtensions.Calls(instruction, clampFloat)) { instruction.operand = clampPortalMapMouseWheel; replaced = true; } clampSearchRemaining--; } yield return instruction; } if (!replaced) { PortalRulesPlugin.PortalRulesLogger.LogWarning((object)"Could not patch the portal-map mouse-wheel zoom rate; using Valheim's default rate."); } } } [HarmonyPatch(typeof(TeleportWorldTrigger), "OnTriggerEnter")] private static class PortalTriggerPatch { private static bool Prefix(TeleportWorldTrigger __instance, Collider colliderIn) { TeleportWorld componentInParent = ((Component)__instance).GetComponentInParent(); if (!IsLocalPlayer(colliderIn)) { return true; } if (!PublicPortalKinds.IsHandledPortal(componentInParent)) { return true; } if (IsMapSelectionPortal(componentInParent)) { PublicPortalMapController.Instance.Begin(componentInParent, ((Component)__instance).GetComponent(), colliderIn); return false; } PublicPortalTeleportService.TryBeginConnectedTeleport(componentInParent); return false; } } [HarmonyPatch(typeof(TeleportWorld), "HaveTarget")] private static class HaveTargetPatch { private static bool Prefix(TeleportWorld __instance, ref bool __result) { if (!IsMapSelectionPortal(__instance)) { return true; } __result = true; return false; } } [HarmonyPatch(typeof(TeleportWorld), "TargetFound")] private static class TargetFoundPatch { private static bool Prefix(TeleportWorld __instance, ref bool __result) { if (!IsMapSelectionPortal(__instance)) { return true; } __result = true; return false; } } [HarmonyPatch(typeof(Minimap), "SetMapMode")] private static class CloseSelectionOnMapClosePatch { private static void Postfix(MapMode mode) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) PublicPortalMapController.Instance.OnMapModeChanged(mode); } } [HarmonyPatch(typeof(Minimap), "OnMapLeftClick")] private static class PortalMapLeftClickPatch { private static bool Prefix(Minimap __instance) { if (!PublicPortalMapController.Instance.IsSelecting) { return true; } PublicPortalMapController.Instance.HandleLeftClick(__instance); return false; } } [HarmonyPatch(typeof(Minimap), "OnMapRightClick")] private static class PortalMapRightClickPatch { private static bool Prefix(Minimap __instance) { if (!PublicPortalMapController.Instance.IsSelecting) { return true; } PublicPortalMapController.Instance.HandleRightClick(__instance); return false; } } private static bool IsLocalPlayer(Collider collider) { Player component = ((Component)collider).GetComponent(); if ((Object)(object)component != (Object)null) { return (Object)(object)Player.m_localPlayer == (Object)(object)component; } return false; } private static bool IsMapSelectionPortal(TeleportWorld portal) { if ((Object)(object)portal == (Object)null || PublicPortalConfig.EnablePortalMap.Value.IsOff() || !PublicPortalKinds.IsHandledPortal(portal)) { return false; } ZDO portalZdo = PublicPortalKinds.GetPortalZdo(portal); if (portalZdo != null) { return PublicPortalCatalog.GetEffectiveAccessMode(portalZdo) != PublicPortalAccessMode.Tagged; } return false; } private static float ClampPortalMapMouseWheel(float value, float minimum, float maximum) { float num = Mathf.Clamp(value, minimum, maximum); if (!PublicPortalMapController.Instance.IsSelecting) { return num; } int num2 = Mathf.Clamp(PublicPortalConfig.PortalMapWheelZoomMultiplier.Value, 1, 10); return num * (float)num2; } } internal sealed class PublicPortalMapController { private delegate Vector3 ScreenToWorldPointDelegate(Minimap minimap, Vector3 screenPoint); private delegate bool TakeInputDelegate(PlayerController controller, bool look); private readonly struct FavoriteFareState : IEquatable { private readonly PublicPortalTravelCostScope _scope; private readonly int _baseCoinCost; private readonly float _includedDistanceMeters; private readonly float _coinsPerKilometer; private readonly int _localCoinCount; private readonly bool _itemsBlocked; private readonly bool _noTeleportVisualAvailable; private readonly string _inviteCooldownState; internal FavoriteFareState(PublicPortalTravelCostScope scope, int baseCoinCost, float includedDistanceMeters, float coinsPerKilometer, int localCoinCount, bool itemsBlocked, bool noTeleportVisualAvailable, string inviteCooldownState) { _scope = scope; _baseCoinCost = baseCoinCost; _includedDistanceMeters = includedDistanceMeters; _coinsPerKilometer = coinsPerKilometer; _localCoinCount = localCoinCount; _itemsBlocked = itemsBlocked; _noTeleportVisualAvailable = noTeleportVisualAvailable; _inviteCooldownState = inviteCooldownState; } public bool Equals(FavoriteFareState other) { if (_scope == other._scope && _baseCoinCost == other._baseCoinCost) { float includedDistanceMeters = _includedDistanceMeters; if (includedDistanceMeters.Equals(other._includedDistanceMeters)) { includedDistanceMeters = _coinsPerKilometer; if (includedDistanceMeters.Equals(other._coinsPerKilometer) && _localCoinCount == other._localCoinCount && _itemsBlocked == other._itemsBlocked && _noTeleportVisualAvailable == other._noTeleportVisualAvailable) { return string.Equals(_inviteCooldownState, other._inviteCooldownState, StringComparison.Ordinal); } } } return false; } } private const string AvailablePortalsHintName = "PortalRulesAvailablePortals"; private const string AddPinHintName = "AddPin"; private const string HintMainKeyColor = "#FFA500"; private const float FavoriteFareStateCheckIntervalSeconds = 0.2f; private static ScreenToWorldPointDelegate? ScreenToWorldPoint = CreateScreenToWorldPointDelegate(); private static readonly TakeInputDelegate? TakeInput = CreateTakeInputDelegate(); public static readonly PublicPortalMapController Instance = new PublicPortalMapController(); private readonly PublicPortalPinController _pins = new PublicPortalPinController(); private readonly PublicPortalFavoritePanel _favorites = new PublicPortalFavoritePanel(); private bool _sourceAllowsAllItems; private ZDOID _sourcePortalId = ZDOID.None; private PublicPortalCatalogEntry? _sourcePortal; private Collider? _sourceTriggerCollider; private Collider? _playerCollider; private float _lostSourceAreaAt = -1f; private bool _showingAccessiblePins; private bool _showEmptyMessageWhenCatalogArrives; private bool _lastAdminDebugMapAccess; private Minimap? _availablePortalsHintOwner; private GameObject? _availablePortalsHint; private TMP_Text? _availablePortalsHintLabel; private float _nextFavoriteFareStateCheckAt = -1f; private FavoriteFareState? _favoriteFareState; public bool IsSelecting { get; private set; } private PublicPortalMapController() { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) PublicPortalCatalog.Updated += OnCatalogUpdated; } public void Begin(TeleportWorld sourcePortal, Collider? sourceTriggerCollider, Collider playerCollider) { //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Minimap.instance == (Object)null || ZDOMan.instance == null || (Object)(object)Player.m_localPlayer == (Object)null || (Object)(object)sourcePortal == (Object)null) { return; } ZDO portalZdo = PublicPortalKinds.GetPortalZdo(sourcePortal); if (portalZdo != null) { PublicPortalTeleportService.CancelPending(); ZDOID sourcePortalId = portalZdo.m_uid; PublicPortalTeleportService.AuthorizeMapOpen(sourcePortalId, delegate { //IL_0007: Unknown result type (might be due to invalid IL or missing references) BeginAuthorized(sourcePortalId, sourcePortal, sourceTriggerCollider, playerCollider); }); } } private void BeginAuthorized(ZDOID authorizedSourcePortalId, TeleportWorld sourcePortal, Collider? sourceTriggerCollider, Collider playerCollider) { //IL_0053: 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_0075: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)Minimap.instance == (Object)null || ZDOMan.instance == null || (Object)(object)Player.m_localPlayer == (Object)null || (Object)(object)sourcePortal == (Object)null || (Object)(object)playerCollider == (Object)null || (Object)(object)((Component)playerCollider).GetComponent() != (Object)(object)Player.m_localPlayer) { return; } ZDO portalZdo = PublicPortalKinds.GetPortalZdo(sourcePortal); if (portalZdo != null && !(portalZdo.m_uid != authorizedSourcePortalId)) { IsSelecting = true; _sourceAllowsAllItems = sourcePortal.m_allowAllItems; _sourcePortalId = authorizedSourcePortalId; _sourcePortal = ResolveSourcePortalEntry(sourcePortal, portalZdo); _sourceTriggerCollider = sourceTriggerCollider; _playerCollider = playerCollider; _lostSourceAreaAt = -1f; OpenMapAt(((Component)sourcePortal).transform.position); InventoryGui instance = InventoryGui.instance; if (instance != null) { instance.Hide(); } SetAccessiblePortalPinsVisible(visible: true, showMessage: false); } } public void Tick() { //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Invalid comparison between Unknown and I4 //IL_00a8: Unknown result type (might be due to invalid IL or missing references) Minimap instance = Minimap.instance; if ((Object)(object)instance == (Object)null) { DestroyAvailablePortalsHint(); if (IsSelecting || _showingAccessiblePins || _pins.Count > 0 || _favorites.IsVisible) { End(); } return; } if ((int)instance.m_mode != 2) { DestroyAvailablePortalsHint(); if (IsSelecting || _showingAccessiblePins) { End(); } return; } bool hasAdminDebugAccess = PortalRulesPlugin.HasAdminDebugAccess; if (_showingAccessiblePins && _lastAdminDebugMapAccess != hasAdminDebugAccess) { _lastAdminDebugMapAccess = hasAdminDebugAccess; RefreshVisiblePins(requestServerRefresh: false); } if (PublicPortalConfig.EnablePortalMap.Value.IsOn() && PublicPortalConfig.ToggleAccessiblePortalsKey.Value.IsKeyDown() && CanProcessMapShortcutInput()) { ToggleAccessiblePortalPins(); } UpdateAvailablePortalsHint(instance); _pins.UpdateTravelBadges(IsSelecting, _sourcePortal, AllowsAllItemsForCurrentMapTrip()); RefreshFavoriteFareStateIfChanged(); if (IsSelecting && !(PublicPortalConfig.AutoCloseGraceSeconds.Value <= 0f) && !((Object)(object)Player.m_localPlayer == (Object)null)) { if (IsInsideSourceTrigger()) { _lostSourceAreaAt = -1f; } else if (_lostSourceAreaAt < 0f) { _lostSourceAreaAt = Time.time; } else if (Time.time - _lostSourceAreaAt >= PublicPortalConfig.AutoCloseGraceSeconds.Value) { Minimap.instance.SetMapMode((MapMode)1); End(); } } } private static TakeInputDelegate? CreateTakeInputDelegate() { try { MethodInfo methodInfo = AccessTools.DeclaredMethod(typeof(PlayerController), "TakeInput", new Type[1] { typeof(bool) }, (Type[])null); return (methodInfo == null) ? null : AccessTools.MethodDelegate(methodInfo, (object)null, false); } catch { return null; } } private static ScreenToWorldPointDelegate? CreateScreenToWorldPointDelegate() { try { MethodInfo methodInfo = AccessTools.DeclaredMethod(typeof(Minimap), "ScreenToWorldPoint", new Type[1] { typeof(Vector3) }, (Type[])null); if (methodInfo == null) { PortalRulesPlugin.PortalRulesLogger.LogWarning((object)"Minimap.ScreenToWorldPoint was not found; PortalRules map pin clicking is disabled."); return null; } return AccessTools.MethodDelegate(methodInfo, (object)null, false); } catch (Exception ex) { PortalRulesPlugin.PortalRulesLogger.LogWarning((object)("Could not bind Minimap.ScreenToWorldPoint; PortalRules map pin clicking is disabled: " + ex.Message)); return null; } } private static bool CanProcessMapShortcutInput() { if (TakeInput == null || (Object)(object)Player.m_localPlayer == (Object)null) { return false; } PlayerController component = ((Component)Player.m_localPlayer).GetComponent(); if ((Object)(object)component != (Object)null) { return TakeInput(component, look: false); } return false; } public void OnMapModeChanged(MapMode mode) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Invalid comparison between Unknown and I4 if ((int)mode == 2) { if (!IsSelecting && PublicPortalConfig.EnablePortalMap.Value.IsOn()) { SetAccessiblePortalPinsVisible(visible: true, showMessage: false); } } else { End(); } } public void End() { //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Unknown result type (might be due to invalid IL or missing references) DestroyAvailablePortalsHint(); if (IsSelecting || _showingAccessiblePins || _pins.Count != 0 || _favorites.IsVisible) { IsSelecting = false; _sourcePortalId = ZDOID.None; _sourcePortal = null; PublicPortalTeleportService.CancelPending(); _sourceTriggerCollider = null; _playerCollider = null; _lostSourceAreaAt = -1f; _favoriteFareState = null; _nextFavoriteFareStateCheckAt = -1f; SetAccessiblePortalPinsVisible(visible: false, showMessage: false); } } public void HandleLeftClick(Minimap minimap) { PublicPortalCatalogEntry? publicPortalCatalogEntry = FindClickedPortal(minimap); if (publicPortalCatalogEntry.HasValue) { TryTeleportTo(publicPortalCatalogEntry.Value); } } public void HandleRightClick(Minimap minimap) { PublicPortalCatalogEntry? publicPortalCatalogEntry = FindClickedPortal(minimap); if (publicPortalCatalogEntry.HasValue) { ToggleFavorite(publicPortalCatalogEntry.Value); RefreshFavorites(); } } private void RefreshFavorites() { _favorites.Refresh(_sourcePortal, ItemsBlockedForCurrentMapTrip(), _pins.Portals, TryTeleportTo, RemoveFavorite, FocusFavorite); _favoriteFareState = (_favorites.IsCollapsed ? ((FavoriteFareState?)null) : new FavoriteFareState?(CaptureFavoriteFareState())); } internal void RefreshFavoritePanelFromPreference() { if (IsSelecting && _favorites.IsVisible) { RefreshFavorites(); } } private void RefreshFavoriteFareStateIfChanged() { if (!IsSelecting || !_favorites.IsVisible || _favorites.IsCollapsed) { _favoriteFareState = null; _nextFavoriteFareStateCheckAt = -1f; return; } float unscaledTime = Time.unscaledTime; if (!(_nextFavoriteFareStateCheckAt >= 0f) || !(unscaledTime < _nextFavoriteFareStateCheckAt)) { _nextFavoriteFareStateCheckAt = unscaledTime + 0.2f; FavoriteFareState favoriteFareState = CaptureFavoriteFareState(); if (!_favoriteFareState.HasValue) { _favoriteFareState = favoriteFareState; } else if (!_favoriteFareState.Value.Equals(favoriteFareState)) { RefreshFavorites(); } } } private FavoriteFareState CaptureFavoriteFareState() { bool flag = ItemsBlockedForCurrentMapTrip(); Sprite sprite; Color color; Material material; bool noTeleportVisualAvailable = !flag || PublicPortalTravelCost.TryGetNoTeleportVisual(out sprite, out color, out material); return new FavoriteFareState(PublicPortalConfig.TravelCostScope.Value, PublicPortalConfig.BaseCoinCost.Value, PublicPortalConfig.BaseFareIncludedDistanceMeters.Value, PublicPortalConfig.CoinsPerKilometer.Value, PublicPortalTravelCost.IsEnabled ? PublicPortalTravelCost.GetLocalCoinCount() : 0, flag, noTeleportVisualAvailable, GetFavoriteInviteCooldownDisplayState()); } private string GetFavoriteInviteCooldownDisplayState() { List favoriteIds = PublicPortalData.ReadFavorites(); long remainingSeconds; return string.Join("|", from portal in _pins.Portals.Where((PublicPortalCatalogEntry portal) => favoriteIds.Contains(portal.FavoriteId) && portal.AccessMode == PublicPortalAccessMode.Invite).OrderBy((PublicPortalCatalogEntry portal) => portal.FavoriteId, StringComparer.Ordinal) select (!InviteTravelCooldownStore.TryGetInviteArrivalCooldownRemaining(portal, out remainingSeconds)) ? (portal.FavoriteId + ":-") : (portal.FavoriteId + ":" + InviteTravelCooldownStore.FormatRemaining(remainingSeconds))); } private void TryTeleportTo(PublicPortalCatalogEntry portal) { //IL_0038: Unknown result type (might be due to invalid IL or missing references) if (InviteTravelCooldownStore.TryGetInviteArrivalCooldownRemaining(portal, out var remainingSeconds)) { Player localPlayer = Player.m_localPlayer; if (localPlayer != null) { ((Character)localPlayer).Message((MessageType)2, PortalRulesLocalization.Translate("$sighsorry_portalrules_invite_arrival_cooldown_remaining", InviteTravelCooldownStore.FormatRemaining(remainingSeconds)), 0, (Sprite)null); } } else { PublicPortalTeleportService.TeleportTo(portal, _sourcePortalId, _sourceAllowsAllItems, End); } } private bool AllowsAllItemsForCurrentMapTrip() { if (IsSelecting) { return _sourceAllowsAllItems; } return false; } private bool ItemsBlockedForCurrentMapTrip() { if (IsSelecting && !AllowsAllItemsForCurrentMapTrip() && (Object)(object)Player.m_localPlayer != (Object)null) { return !((Humanoid)Player.m_localPlayer).IsTeleportable(); } return false; } private void ToggleAccessiblePortalPins() { SetAccessiblePortalPinsVisible(!_showingAccessiblePins, showMessage: true); } private void SetAccessiblePortalPinsVisible(bool visible, bool showMessage) { if (!visible) { _showingAccessiblePins = false; _showEmptyMessageWhenCatalogArrives = false; _pins.Clear(); _favorites.Destroy(); return; } _showingAccessiblePins = true; _lastAdminDebugMapAccess = PortalRulesPlugin.HasAdminDebugAccess; _showEmptyMessageWhenCatalogArrives = showMessage && !PublicPortalCatalog.HasSnapshot; RefreshVisiblePins(requestServerRefresh: true); if (_pins.Count == 0 && PublicPortalCatalog.HasSnapshot && showMessage) { Player localPlayer = Player.m_localPlayer; if (localPlayer != null) { ((Character)localPlayer).Message((MessageType)2, PortalRulesLocalization.Translate("$sighsorry_portalrules_no_accessible_portals"), 0, (Sprite)null); } } } private PublicPortalCatalogEntry? FindClickedPortal(Minimap minimap) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) if (ScreenToWorldPoint == null) { return null; } try { Vector3 worldPoint = ScreenToWorldPoint(minimap, ZInput.mousePosition); return _pins.FindClosest(worldPoint); } catch (Exception ex) { ScreenToWorldPoint = null; PortalRulesPlugin.PortalRulesLogger.LogWarning((object)("Could not convert the map cursor to world coordinates; PortalRules map pin clicking is disabled: " + ex.Message)); return null; } } private static void OpenMapAt(Vector3 position) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)Minimap.instance == (Object)null)) { bool noMap = Game.m_noMap; Game.m_noMap = false; Minimap.instance.ShowPointOnMap(position); Game.m_noMap = noMap; } } private static PublicPortalCatalogEntry ResolveSourcePortalEntry(TeleportWorld sourcePortal, ZDO sourceZdo) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) if (PublicPortalCatalog.TryGetClientEntry(sourceZdo.m_uid, out var entry)) { return entry; } return new PublicPortalCatalogEntry(sourceZdo.m_uid, "", sourceZdo.GetPrefab(), sourcePortal.m_allowAllItems, ((Component)sourcePortal).transform.position, ((Component)sourcePortal).transform.rotation, sourceZdo.GetString(ZDOVars.s_tag, ""), PublicPortalCatalog.GetEffectiveAccessMode(sourceZdo), PublicPortalCatalog.GetEffectiveOwner(sourceZdo), 0, 0, 0, 0, 0, 0L, 0L); } private void FocusFavorite(PublicPortalCatalogEntry portal) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Invalid comparison between Unknown and I4 //IL_0024: Unknown result type (might be due to invalid IL or missing references) if (IsSelecting && !((Object)(object)Minimap.instance == (Object)null) && (int)Minimap.instance.m_mode == 2) { OpenMapAt(portal.Position); } } private bool IsInsideSourceTrigger() { //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_sourceTriggerCollider == (Object)null || (Object)(object)_playerCollider == (Object)null) { return false; } if (!_sourceTriggerCollider.enabled || !_playerCollider.enabled) { return false; } Bounds bounds = _sourceTriggerCollider.bounds; if (((Bounds)(ref bounds)).Intersects(_playerCollider.bounds)) { return true; } if ((Object)(object)Player.m_localPlayer == (Object)null) { return false; } Vector3 position = ((Component)Player.m_localPlayer).transform.position; return Vector3.Distance(_sourceTriggerCollider.ClosestPoint(position), position) <= 0.05f; } private void ToggleFavorite(PublicPortalCatalogEntry target) { if (!PublicPortalData.TryNormalizeFavoriteId(target.FavoriteId, out string normalizedFavoriteId)) { return; } List list = PublicPortalData.ReadFavorites(); string text; if (list.Remove(normalizedFavoriteId)) { text = PortalRulesLocalization.Translate("$sighsorry_portalrules_favorite_removed"); } else { bool flag = false; if (list.Count >= 10) { HashSet availableFavoriteIds = new HashSet(StringComparer.Ordinal); foreach (PublicPortalCatalogEntry portal in _pins.Portals) { if (!string.IsNullOrWhiteSpace(portal.FavoriteId)) { availableFavoriteIds.Add(portal.FavoriteId); } } int num = list.FindIndex((string candidate) => !availableFavoriteIds.Contains(candidate)); if (num >= 0) { list.RemoveAt(num); flag = true; } } if (list.Count >= 10) { Player localPlayer = Player.m_localPlayer; if (localPlayer != null) { ((Character)localPlayer).Message((MessageType)1, PortalRulesLocalization.Translate("$sighsorry_portalrules_favorite_limit", 10.ToString()), 0, (Sprite)null); } return; } list.Add(normalizedFavoriteId); text = (flag ? PortalRulesLocalization.Translate("$sighsorry_portalrules_favorite_added_replaced_unavailable") : PortalRulesLocalization.Translate("$sighsorry_portalrules_favorite_added")); } PublicPortalData.WriteFavorites(list); Player localPlayer2 = Player.m_localPlayer; if (localPlayer2 != null) { ((Character)localPlayer2).Message((MessageType)1, text, 0, (Sprite)null); } } private void RemoveFavorite(PublicPortalCatalogEntry target) { if (!PublicPortalData.TryNormalizeFavoriteId(target.FavoriteId, out string normalizedFavoriteId)) { return; } List list = PublicPortalData.ReadFavorites(); if (list.Remove(normalizedFavoriteId)) { PublicPortalData.WriteFavorites(list); Player localPlayer = Player.m_localPlayer; if (localPlayer != null) { ((Character)localPlayer).Message((MessageType)1, PortalRulesLocalization.Translate("$sighsorry_portalrules_favorite_removed"), 0, (Sprite)null); } RefreshFavorites(); } } private void UpdateAvailablePortalsHint(Minimap minimap) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) KeyboardShortcut value = PublicPortalConfig.ToggleAccessiblePortalsKey.Value; if (!PublicPortalConfig.EnablePortalMap.Value.IsOn() || (int)((KeyboardShortcut)(ref value)).MainKey == 0 || PlatformPrefs.GetInt("KeyHints", 1) != 1 || !((Object)(object)minimap.m_largeRoot != (Object)null) || !minimap.m_largeRoot.activeInHierarchy) { HideAvailablePortalsHint(); return; } if ((Object)(object)_availablePortalsHintOwner != (Object)(object)minimap || (Object)(object)_availablePortalsHint == (Object)null) { BuildAvailablePortalsHint(minimap); } if (!((Object)(object)_availablePortalsHint == (Object)null) && !((Object)(object)_availablePortalsHintLabel == (Object)null)) { if (!_availablePortalsHint.activeSelf) { _availablePortalsHint.SetActive(true); MarkAvailablePortalsHintLayoutForRebuild(); } string text = PortalRulesLocalization.Translate(_showingAccessiblePins ? "$sighsorry_portalrules_hint_hide_available_portals" : "$sighsorry_portalrules_hint_show_available_portals", FormatHintShortcut(value)); if (!string.Equals(_availablePortalsHintLabel.text, text, StringComparison.Ordinal)) { _availablePortalsHintLabel.text = text; MarkAvailablePortalsHintLayoutForRebuild(); } } } private void BuildAvailablePortalsHint(Minimap minimap) { DestroyAvailablePortalsHint(); GameObject largeRoot = minimap.m_largeRoot; Transform val = ((largeRoot != null) ? largeRoot.transform.Find("KeyHints/keyboard_hints") : null); Transform val2 = ((val != null) ? val.Find("AddPin") : null); if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null) { return; } Transform val3 = val.Find("PortalRulesAvailablePortals"); GameObject val4 = (((Object)(object)val3 != (Object)null) ? ((Component)val3).gameObject : Object.Instantiate(((Component)val2).gameObject, val, false)); ((Object)val4).name = "PortalRulesAvailablePortals"; int num = val2.GetSiblingIndex(); if (val4.transform.GetSiblingIndex() < num) { num--; } val4.transform.SetSiblingIndex(Mathf.Max(0, num)); Transform val5 = val4.transform.Find("keyboard_hint"); if ((Object)(object)val5 != (Object)null) { ((Component)val5).gameObject.SetActive(false); } Transform obj = val4.transform.Find("Label"); TMP_Text val6 = ((obj != null) ? ((Component)obj).GetComponent() : null) ?? val4.GetComponentInChildren(true); if ((Object)(object)val6 == (Object)null) { if ((Object)(object)val3 == (Object)null) { Object.Destroy((Object)(object)val4); } return; } _availablePortalsHintOwner = minimap; _availablePortalsHint = val4; _availablePortalsHintLabel = val6; val6.richText = true; val4.SetActive(true); MarkAvailablePortalsHintLayoutForRebuild(); } private void HideAvailablePortalsHint() { if ((Object)(object)_availablePortalsHint != (Object)null && _availablePortalsHint.activeSelf) { _availablePortalsHint.SetActive(false); MarkAvailablePortalsHintLayoutForRebuild(); } } private void DestroyAvailablePortalsHint() { if ((Object)(object)_availablePortalsHint != (Object)null) { Object.Destroy((Object)(object)_availablePortalsHint); } _availablePortalsHintOwner = null; _availablePortalsHint = null; _availablePortalsHintLabel = null; } private void MarkAvailablePortalsHintLayoutForRebuild() { GameObject? availablePortalsHint = _availablePortalsHint; Transform obj = ((availablePortalsHint != null) ? availablePortalsHint.transform.parent : null); RectTransform val = (RectTransform)(object)((obj is RectTransform) ? obj : null); if (val != null) { LayoutRebuilder.MarkLayoutForRebuild(val); } } private static string FormatHintShortcut(KeyboardShortcut shortcut) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) List list = ((KeyboardShortcut)(ref shortcut)).Modifiers.Select(FormatHintKey).ToList(); list.Add("" + FormatHintKey(((KeyboardShortcut)(ref shortcut)).MainKey) + ""); return string.Join(" + ", list); } private unsafe static string FormatHintKey(KeyCode key) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Expected I4, but got Unknown string text; switch (key - 303) { case 0: case 1: text = PortalRulesLocalization.Translate("$sighsorry_portalrules_key_shift"); break; case 2: case 3: text = PortalRulesLocalization.Translate("$sighsorry_portalrules_key_control"); break; case 4: case 5: text = PortalRulesLocalization.Translate("$sighsorry_portalrules_key_alt"); break; default: text = ((object)(*(KeyCode*)(&key))/*cast due to .constrained prefix*/).ToString(); break; } return StringExtensionMethods.RemoveRichTextTags(text); } private void OnCatalogUpdated() { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Invalid comparison between Unknown and I4 //IL_002c: Unknown result type (might be due to invalid IL or missing references) if (!_showingAccessiblePins || (Object)(object)Minimap.instance == (Object)null || (int)Minimap.instance.m_mode != 2) { return; } if (IsSelecting) { if (!PublicPortalCatalog.TryGetClientEntry(_sourcePortalId, out var entry) || !PublicPortalAccess.CanUsePortal(entry)) { Player localPlayer = Player.m_localPlayer; if (localPlayer != null) { ((Character)localPlayer).Message((MessageType)2, PortalRulesLocalization.Translate("$sighsorry_portalrules_source_portal_no_longer_accessible"), 0, (Sprite)null); } Minimap.instance.SetMapMode((MapMode)1); End(); return; } _sourcePortal = entry; } RefreshVisiblePins(requestServerRefresh: false); if (_showEmptyMessageWhenCatalogArrives && _pins.Count == 0) { Player localPlayer2 = Player.m_localPlayer; if (localPlayer2 != null) { ((Character)localPlayer2).Message((MessageType)2, PortalRulesLocalization.Translate("$sighsorry_portalrules_no_accessible_portals"), 0, (Sprite)null); } } _showEmptyMessageWhenCatalogArrives = false; } private void RefreshVisiblePins(bool requestServerRefresh) { _pins.Refresh(requestServerRefresh); if (IsSelecting) { RefreshFavorites(); } } } internal enum RequiredGlobalKeyQueryResult : byte { Invalid, Unavailable, PersonalMissing, PersonalPresent, SharedMissing, SharedPresent } internal static class RequiredGlobalKeyAccess { private const int SupportedApiVersion = 1; private const string ApiTypeName = "YouAreNotWorthy.YouAreNotWorthyApi"; private static bool _initialized; private static bool _youAreNotWorthyInstalled; private static bool _apiAvailable; private static bool _runtimeFailureLogged; private static MethodInfo? _queryLocal; private static MethodInfo? _queryPeer; private static MethodInfo? _showLocalMissingRequirement; internal static bool YouAreNotWorthyInstalled { get { EnsureInitialized(); return _youAreNotWorthyInstalled; } } internal static void Initialize() { if (_initialized) { return; } _initialized = true; _youAreNotWorthyInstalled = Chainloader.PluginInfos.TryGetValue("sighsorry.YouAreNotWorthy", out var value); if (!_youAreNotWorthyInstalled) { PortalRulesPlugin.PortalRulesLogger.LogInfo((object)"YouAreNotWorthy is not installed; Required GlobalKeys use shared world progression."); return; } try { Type type = (((object)value.Instance)?.GetType().Assembly)?.GetType("YouAreNotWorthy.YouAreNotWorthyApi", throwOnError: false, ignoreCase: false); FieldInfo fieldInfo = type?.GetField("ApiVersion", BindingFlags.Static | BindingFlags.Public); object obj = (((object)fieldInfo != null && fieldInfo.IsLiteral) ? fieldInfo.GetRawConstantValue() : fieldInfo?.GetValue(null)); int num = ((obj != null) ? Convert.ToInt32(obj, CultureInfo.InvariantCulture) : 0); _queryLocal = type?.GetMethod("QueryLocal", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(string) }, null); _queryPeer = type?.GetMethod("QueryPeer", BindingFlags.Static | BindingFlags.Public, null, new Type[2] { typeof(ZNetPeer), typeof(string) }, null); _showLocalMissingRequirement = type?.GetMethod("TryShowLocalMissingRequirement", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(string) }, null); _apiAvailable = num == 1 && _queryLocal != null && _queryPeer != null; if (_apiAvailable) { PortalRulesPlugin.PortalRulesLogger.LogInfo((object)$"Using YouAreNotWorthy Required GlobalKey API v{num}."); if (_showLocalMissingRequirement == null) { PortalRulesPlugin.PortalRulesLogger.LogWarning((object)"YouAreNotWorthy does not expose its localized missing-requirement message API. Key-gated travel remains fail-closed, but PortalRules will show a generic compatibility message."); } } else { PortalRulesPlugin.PortalRulesLogger.LogError((object)("YouAreNotWorthy is installed but its Required GlobalKey API is unavailable " + $"or incompatible (found v{num}, expected v{1}). " + "Key-gated portals will fail closed.")); } } catch (Exception ex) { _apiAvailable = false; PortalRulesPlugin.PortalRulesLogger.LogError((object)("Could not bind the YouAreNotWorthy Required GlobalKey API; key-gated portals will fail closed: " + ex.GetBaseException().Message)); } } internal static void Shutdown() { _initialized = false; _youAreNotWorthyInstalled = false; _apiAvailable = false; _runtimeFailureLogged = false; _queryLocal = null; _queryPeer = null; _showLocalMissingRequirement = null; } internal static RequiredGlobalKeyQueryResult QueryLocal(string requiredGlobalKey) { if (!TryPrepare(requiredGlobalKey, out string normalized)) { return RequiredGlobalKeyQueryResult.Invalid; } if (normalized.Length == 0) { return RequiredGlobalKeyQueryResult.SharedPresent; } EnsureInitialized(); if (!_youAreNotWorthyInstalled) { return QuerySharedWorldKey(normalized); } return Invoke(_queryLocal, new object[1] { normalized }); } internal static RequiredGlobalKeyQueryResult QueryPeer(ZNetPeer? peer, string requiredGlobalKey) { if (!TryPrepare(requiredGlobalKey, out string normalized)) { return RequiredGlobalKeyQueryResult.Invalid; } if (normalized.Length == 0) { return RequiredGlobalKeyQueryResult.SharedPresent; } EnsureInitialized(); if (!_youAreNotWorthyInstalled) { return QuerySharedWorldKey(normalized); } return Invoke(_queryPeer, new object[2] { peer, normalized }); } internal static bool IsPresent(RequiredGlobalKeyQueryResult result) { if (result == RequiredGlobalKeyQueryResult.PersonalPresent || result == RequiredGlobalKeyQueryResult.SharedPresent) { return true; } return false; } internal static bool TryShowLocalMissingRequirement(string requiredGlobalKey) { if (!TryPrepare(requiredGlobalKey, out string normalized) || normalized.Length == 0) { return false; } EnsureInitialized(); if (!_youAreNotWorthyInstalled || !_apiAvailable || _showLocalMissingRequirement == null) { return false; } try { _showLocalMissingRequirement.Invoke(null, new object[1] { normalized }); return true; } catch (Exception ex) { LogRuntimeFailureOnce("YouAreNotWorthy failed to show its missing-requirement message; PortalRules will show a generic compatibility message: " + ex.GetBaseException().Message); return false; } } private static bool TryPrepare(string value, out string normalized) { RequiredGlobalKeyValidationFailure failure; return PublicPortalData.TryNormalizeRequiredGlobalKey(value, out normalized, out failure); } private static RequiredGlobalKeyQueryResult QuerySharedWorldKey(string key) { ZoneSystem instance = ZoneSystem.instance; if ((Object)(object)instance == (Object)null) { return RequiredGlobalKeyQueryResult.Unavailable; } string text = default(string); GlobalKeys val = default(GlobalKeys); string keyValue = ZoneSystem.GetKeyValue(key.ToLowerInvariant(), ref text, ref val); string a = default(string); if (!instance.GetGlobalKey(keyValue, ref a)) { return RequiredGlobalKeyQueryResult.SharedMissing; } if (!string.IsNullOrEmpty(text) && !string.Equals(a, text, StringComparison.Ordinal)) { return RequiredGlobalKeyQueryResult.SharedMissing; } return RequiredGlobalKeyQueryResult.SharedPresent; } private static RequiredGlobalKeyQueryResult Invoke(MethodInfo? method, object?[] arguments) { if (!_apiAvailable || method == null) { return RequiredGlobalKeyQueryResult.Unavailable; } try { object obj = method.Invoke(null, arguments); int num = ((obj != null) ? Convert.ToInt32(obj, CultureInfo.InvariantCulture) : (-1)); if (num >= 0 && num <= 255 && Enum.IsDefined(typeof(RequiredGlobalKeyQueryResult), (byte)num)) { byte num2 = (byte)num; if (num2 == 1) { LogRuntimeFailureOnce("YouAreNotWorthy could not resolve a character key snapshot; the affected portal view remains fail-closed until it is available."); } return (RequiredGlobalKeyQueryResult)num2; } LogRuntimeFailureOnce($"YouAreNotWorthy returned unknown Required GlobalKey result {num}; " + "the affected portal view remains fail-closed."); } catch (Exception ex) { LogRuntimeFailureOnce("YouAreNotWorthy Required GlobalKey query failed; the affected portal view remains fail-closed: " + ex.GetBaseException().Message); } return RequiredGlobalKeyQueryResult.Unavailable; } private static void LogRuntimeFailureOnce(string message) { if (!_runtimeFailureLogged) { _runtimeFailureLogged = true; PortalRulesPlugin.PortalRulesLogger.LogWarning((object)message); } } private static void EnsureInitialized() { if (!_initialized) { Initialize(); } } } internal static class PublicPortalModeChangeEffects { private const string PlayModeChangeEffectRpc = "sighsorry.PortalRules.PlayModeChangeEffect.v1"; public static void RegisterPeer(ZNet znet, ZNetPeer peer) { if (!((Object)(object)znet == (Object)null) && peer?.m_rpc != null && !znet.IsServer()) { peer.m_rpc.Register("sighsorry.PortalRules.PlayModeChangeEffect.v1", (Action)OnPlayModeChangeEffect); } } public static void Broadcast(ZDOID portalId) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0064: Unknown result type (might be due to invalid IL or missing references) ZNet instance = ZNet.instance; if ((Object)(object)instance == (Object)null || !instance.IsServer()) { return; } Play(portalId); foreach (ZNetPeer peer in instance.GetPeers()) { if (peer.IsReady() && peer.m_rpc != null && peer.m_rpc.IsConnected()) { peer.m_rpc.Invoke("sighsorry.PortalRules.PlayModeChangeEffect.v1", new object[1] { portalId }); } } } private static void OnPlayModeChangeEffect(ZRpc rpc, ZDOID portalId) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)ZNet.instance == (Object)null) && !ZNet.instance.IsServer() && rpc == ZNet.instance.GetServerRPC()) { Play(portalId); } } private static void Play(ZDOID portalId) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) GameObject val = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.FindInstance(portalId) : null); TeleportWorld val2 = (((Object)(object)val != (Object)null) ? val.GetComponent() : null); if (!((Object)(object)val2 == (Object)null) && val2.m_connected != null) { val2.m_connected.Create(((Component)val2).transform.position, ((Component)val2).transform.rotation, (Transform)null, 1f, -1); } } } internal sealed class PublicPortalPinController { private sealed class PinDecoration { public readonly GameObject Root; public readonly Text MyPortalText; public readonly Text FavoriteStar; public readonly Text CooldownCross; public Image? Icon; public Color OriginalIconColor; public PinDecoration(GameObject root, Text myPortalText, Text favoriteStar, Text cooldownCross, Image? icon) { //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) Root = root; MyPortalText = myPortalText; FavoriteStar = favoriteStar; CooldownCross = cooldownCross; Icon = icon; OriginalIconColor = (((Object)(object)icon != (Object)null) ? ((Graphic)icon).color : Color.white); } } private sealed class TravelBadge { public readonly GameObject Root; public readonly RectTransform RootRect; public readonly Image CoinIcon; public readonly RectTransform CoinIconRect; public readonly Text CostText; public readonly RectTransform CostTextRect; public readonly Image NoTeleportIcon; public TravelBadge(GameObject root, Image coinIcon, Text costText, Image noTeleportIcon) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected O, but got Unknown //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Expected O, but got Unknown //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Expected O, but got Unknown Root = root; RootRect = (RectTransform)root.transform; CoinIcon = coinIcon; CoinIconRect = (RectTransform)((Component)coinIcon).transform; CostText = costText; CostTextRect = (RectTransform)((Component)costText).transform; NoTeleportIcon = noTeleportIcon; } } private const float PortalClickRadius = 96f; private const float TravelBadgeRefreshIntervalSeconds = 0.2f; private const float PinDecorationRefreshIntervalSeconds = 0.2f; private const float NoTeleportIconSize = 21f; private static readonly Color UnaffordableColor = new Color(1f, 0.42f, 0.32f); private static readonly Color CooldownIconColor = new Color(0.42f, 0.42f, 0.42f, 0.82f); private static readonly Color CooldownCrossColor = new Color(0.92f, 0.18f, 0.12f); private static readonly Color FavoriteStarColor = new Color(1f, 0.76f, 0.2f); private const string CooldownPinColorOpen = ""; private const string CooldownPinLineMarker = "\n"; private static FieldRef? LargeZoom = CreateLargeZoomAccessor(); private readonly Dictionary _activePins = new Dictionary(); private readonly Dictionary _travelBadges = new Dictionary(); private readonly Dictionary _pinDecorations = new Dictionary(); private float _nextTravelBadgeRefreshAt = -1f; private float _nextPinDecorationRefreshAt = -1f; public int Count => _activePins.Count; public IEnumerable Portals => _activePins.Values; public void Refresh(bool requestServerRefresh = true) { //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_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) Clear(); Minimap instance = Minimap.instance; if ((Object)(object)instance == (Object)null) { if (requestServerRefresh) { PublicPortalCatalog.RequestRefresh(); } return; } PublicPortalPinTypeRegistrar.EnsureRegistered(instance); foreach (PublicPortalCatalogEntry entry in PublicPortalCatalog.Entries) { if (PublicPortalAccess.CanUsePortal(entry) && !ShouldHideAdminTaggedPortal(entry)) { string text = BuildPinName(entry); PinData val = instance.AddPin(entry.Position, PublicPortalPinTypeRegistrar.PortalPinType, text, false, false, 0L, default(PlatformUserID)); val.m_doubleSize = false; _activePins[val] = entry; } } if (requestServerRefresh) { PublicPortalCatalog.RequestRefresh(); } } private static bool ShouldHideAdminTaggedPortal(PublicPortalCatalogEntry portal) { if (portal.AccessMode == PublicPortalAccessMode.Tagged && PublicPortalKinds.IsAdminPortalPrefab(portal.PrefabHash)) { return !PortalRulesPlugin.HasAdminDebugAccess; } return false; } public void Clear() { foreach (PinDecoration value in _pinDecorations.Values) { RestorePinIcon(value); if ((Object)(object)value.Root != (Object)null) { Object.Destroy((Object)(object)value.Root); } } _pinDecorations.Clear(); _nextPinDecorationRefreshAt = -1f; foreach (TravelBadge value2 in _travelBadges.Values) { if ((Object)(object)value2.Root != (Object)null) { Object.Destroy((Object)(object)value2.Root); } } _travelBadges.Clear(); _nextTravelBadgeRefreshAt = -1f; if ((Object)(object)Minimap.instance != (Object)null) { PinData[] array = _activePins.Keys.ToArray(); foreach (PinData val in array) { Minimap.instance.RemovePin(val); } } _activePins.Clear(); } public void UpdateTravelBadges(bool isSelecting, PublicPortalCatalogEntry? sourcePortal, bool allowsAllItems) { //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) UpdatePinDecorations(); bool flag = !allowsAllItems && (Object)(object)Player.m_localPlayer != (Object)null && !((Humanoid)Player.m_localPlayer).IsTeleportable(); if (!isSelecting || !sourcePortal.HasValue || (!PublicPortalTravelCost.IsEnabled && !flag)) { HideTravelBadges(); _nextTravelBadgeRefreshAt = -1f; return; } float unscaledTime = Time.unscaledTime; if (_nextTravelBadgeRefreshAt >= 0f && unscaledTime < _nextTravelBadgeRefreshAt) { return; } _nextTravelBadgeRefreshAt = unscaledTime + 0.2f; int localCoinCount = (PublicPortalTravelCost.IsEnabled ? PublicPortalTravelCost.GetLocalCoinCount() : 0); Sprite sharedCoinIcon = (PublicPortalTravelCost.IsEnabled ? PublicPortalTravelCost.GetCoinIcon() : null); foreach (KeyValuePair activePin in _activePins) { if (activePin.Value.Id == sourcePortal.Value.Id || InviteTravelCooldownStore.TryGetInviteArrivalCooldownRemaining(activePin.Value, out var _) || (Object)(object)activePin.Key.m_uiElement == (Object)null) { SetTravelBadgeVisible(activePin.Key, visible: false); continue; } int num = PublicPortalTravelCost.CalculateCost(sourcePortal.Value, activePin.Value); if (num <= 0 && !flag) { SetTravelBadgeVisible(activePin.Key, visible: false); continue; } TravelBadge travelBadge = EnsureTravelBadge(activePin.Key); if (travelBadge != null) { UpdateTravelBadge(travelBadge, num, flag, localCoinCount, sharedCoinIcon); if (!travelBadge.Root.activeSelf) { travelBadge.Root.SetActive(true); } } } } private void UpdatePinDecorations() { float unscaledTime = Time.unscaledTime; if (_nextPinDecorationRefreshAt >= 0f && unscaledTime < _nextPinDecorationRefreshAt) { return; } _nextPinDecorationRefreshAt = unscaledTime + 0.2f; HashSet hashSet = new HashSet(PublicPortalData.ReadFavorites(), StringComparer.Ordinal); foreach (KeyValuePair activePin in _activePins) { PinData key = activePin.Key; PublicPortalCatalogEntry value = activePin.Value; if (!((Object)(object)key.m_uiElement == (Object)null)) { PinDecoration pinDecoration = EnsurePinDecoration(key); bool flag = value.MyPortalOrdinal > 0; ((Component)pinDecoration.MyPortalText).gameObject.SetActive(flag); if (flag) { Text myPortalText = pinDecoration.MyPortalText; string[] array = new string[2]; int myPortalOrdinal = value.MyPortalOrdinal; array[0] = myPortalOrdinal.ToString(); array[1] = FormatLimit(value.MyPortalLimit); myPortalText.text = PortalRulesLocalization.Translate("$sighsorry_portalrules_my_portal_ordinal", array); } ((Component)pinDecoration.FavoriteStar).gameObject.SetActive(hashSet.Contains(value.FavoriteId)); long remainingSeconds; bool flag2 = InviteTravelCooldownStore.TryGetInviteArrivalCooldownRemaining(value, out remainingSeconds); UpdatePinIcon(pinDecoration, key.m_iconElement, flag2); ((Component)pinDecoration.CooldownCross).gameObject.SetActive(flag2); UpdateInviteCooldownLine(key, flag2 ? InviteTravelCooldownStore.FormatRemaining(remainingSeconds) : ""); } } } private PinDecoration EnsurePinDecoration(PinData pin) { //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) //IL_0088: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_014b: Unknown result type (might be due to invalid IL or missing references) //IL_0159: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_016d: Unknown result type (might be due to invalid IL or missing references) //IL_0191: Expected O, but got Unknown if (_pinDecorations.TryGetValue(pin, out PinDecoration value) && (Object)(object)value.Root != (Object)null) { if ((Object)(object)value.Root.transform.parent == (Object)(object)pin.m_uiElement) { return value; } RestorePinIcon(value); Object.Destroy((Object)(object)value.Root); } GameObject val = new GameObject("PortalRulesPinDecoration", new Type[1] { typeof(RectTransform) }); val.transform.SetParent((Transform)(object)pin.m_uiElement, false); RectTransform val2 = (RectTransform)val.transform; val2.anchorMin = new Vector2(0.5f, 0.5f); val2.anchorMax = new Vector2(0.5f, 0.5f); val2.pivot = new Vector2(0.5f, 0.5f); val2.anchoredPosition = Vector2.zero; val2.sizeDelta = Vector2.zero; Text myPortalText = CreateDecorationText(val.transform, "MyPortal", 12, (FontStyle)1, Color.white, new Vector2(0f, 25f), new Vector2(190f, 18f)); Text val3 = CreateDecorationText(val.transform, "FavoriteStar", 14, (FontStyle)1, FavoriteStarColor, Vector2.zero, new Vector2(20f, 20f)); val3.text = "★"; Text val4 = CreateDecorationText(val.transform, "InviteCooldownCross", 27, (FontStyle)1, CooldownCrossColor, Vector2.zero, new Vector2(34f, 34f)); val4.text = "X"; PinDecoration pinDecoration = new PinDecoration(val, myPortalText, val3, val4, pin.m_iconElement); _pinDecorations[pin] = pinDecoration; return pinDecoration; } private static void UpdateInviteCooldownLine(PinData pin, string cooldownText) { string text = pin.m_name ?? ""; int num = text.LastIndexOf("\n", StringComparison.Ordinal); string text2 = ((num >= 0) ? text.Substring(0, num) : text); string text3 = (string.IsNullOrEmpty(cooldownText) ? text2 : (string.IsNullOrEmpty(text2) ? ("" + cooldownText + "") : (text2 + "\n" + cooldownText + ""))); if (!string.Equals(text, text3, StringComparison.Ordinal)) { pin.m_name = text3; PinNameData namePinData = pin.m_NamePinData; if ((Object)(object)((namePinData != null) ? namePinData.PinNameText : null) != (Object)null) { pin.m_NamePinData.PinNameText.text = text3; } } } private static Text CreateDecorationText(Transform parent, string name, int fontSize, FontStyle fontStyle, Color color, Vector2 anchoredPosition, Vector2 size) { //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Expected O, but got Unknown //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(name, new Type[3] { typeof(RectTransform), typeof(Text), typeof(Outline) }); val.transform.SetParent(parent, false); RectTransform val2 = (RectTransform)val.transform; val2.anchorMin = new Vector2(0.5f, 0.5f); val2.anchorMax = new Vector2(0.5f, 0.5f); val2.pivot = new Vector2(0.5f, 0.5f); val2.anchoredPosition = anchoredPosition; val2.sizeDelta = size; Text component = val.GetComponent(); component.font = GUIManager.Instance.AveriaSerif; component.fontSize = fontSize; component.fontStyle = fontStyle; component.alignment = (TextAnchor)4; component.horizontalOverflow = (HorizontalWrapMode)1; component.verticalOverflow = (VerticalWrapMode)0; ((Graphic)component).color = color; ((Graphic)component).raycastTarget = false; Outline component2 = val.GetComponent(); ((Shadow)component2).effectColor = new Color(0f, 0f, 0f, 0.9f); ((Shadow)component2).effectDistance = new Vector2(1f, -1f); ((Shadow)component2).useGraphicAlpha = true; return component; } private static void UpdatePinIcon(PinDecoration decoration, Image? currentIcon, bool cooldownBlocked) { //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0056: 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_0032: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)decoration.Icon != (Object)(object)currentIcon) { RestorePinIcon(decoration); decoration.Icon = currentIcon; decoration.OriginalIconColor = (((Object)(object)currentIcon != (Object)null) ? ((Graphic)currentIcon).color : Color.white); } if ((Object)(object)decoration.Icon != (Object)null) { ((Graphic)decoration.Icon).color = (cooldownBlocked ? CooldownIconColor : decoration.OriginalIconColor); } } private static void RestorePinIcon(PinDecoration decoration) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)decoration.Icon != (Object)null) { ((Graphic)decoration.Icon).color = decoration.OriginalIconColor; } } private static string BuildPinName(PublicPortalCatalogEntry portal) { string text = SanitizePinLine(portal.Tag); string text2 = portal.AccessMode switch { PublicPortalAccessMode.Invite => BuildModeLine(PublicPortalInteraction.AccessModeLabel(PublicPortalAccessMode.Invite), portal.ModeOrdinal, portal.ModeLimit), PublicPortalAccessMode.Clan => BuildModeLine(PublicPortalInteraction.AccessModeLabel(PublicPortalAccessMode.Clan), portal.ModeOrdinal, portal.ModeLimit), _ => "", }; if (string.IsNullOrEmpty(text)) { return text2; } if (!string.IsNullOrEmpty(text2)) { return text + "\n" + text2; } return text; } private static string BuildModeLine(string label, int ordinal, int limit) { if (ordinal <= 0) { return label; } return PortalRulesLocalization.Translate("$sighsorry_portalrules_portal_mode_ordinal", label, ordinal.ToString(), FormatLimit(limit)); } private static string FormatLimit(int limit) { if (limit >= 0) { return limit.ToString(); } return "∞"; } private static string SanitizePinLine(string? value) { return StringExtensionMethods.RemoveRichTextTags(value ?? "").Replace('\r', ' ').Replace('\n', ' ') .Replace('\t', ' ') .Trim(); } private void HideTravelBadges() { foreach (TravelBadge value in _travelBadges.Values) { if ((Object)(object)value.Root != (Object)null && value.Root.activeSelf) { value.Root.SetActive(false); } } } private void SetTravelBadgeVisible(PinData pin, bool visible) { if (_travelBadges.TryGetValue(pin, out TravelBadge value) && (Object)(object)value.Root != (Object)null && value.Root.activeSelf != visible) { value.Root.SetActive(visible); } } private TravelBadge? EnsureTravelBadge(PinData pin) { //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Expected O, but got Unknown //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: 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_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_013d: 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_0157: 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) //IL_016c: 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_0181: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Unknown result type (might be due to invalid IL or missing references) //IL_0196: Unknown result type (might be due to invalid IL or missing references) //IL_01d9: Unknown result type (might be due to invalid IL or missing references) //IL_01de: Unknown result type (might be due to invalid IL or missing references) //IL_01f0: Unknown result type (might be due to invalid IL or missing references) //IL_01f6: Unknown result type (might be due to invalid IL or missing references) //IL_01fb: Unknown result type (might be due to invalid IL or missing references) //IL_01fc: Unknown result type (might be due to invalid IL or missing references) //IL_0206: Unknown result type (might be due to invalid IL or missing references) //IL_0207: Unknown result type (might be due to invalid IL or missing references) //IL_0211: Unknown result type (might be due to invalid IL or missing references) //IL_021c: Unknown result type (might be due to invalid IL or missing references) //IL_0226: Unknown result type (might be due to invalid IL or missing references) //IL_0296: Unknown result type (might be due to invalid IL or missing references) //IL_029b: Unknown result type (might be due to invalid IL or missing references) //IL_02ad: Unknown result type (might be due to invalid IL or missing references) //IL_02b3: Unknown result type (might be due to invalid IL or missing references) //IL_02b8: Unknown result type (might be due to invalid IL or missing references) //IL_02c3: Unknown result type (might be due to invalid IL or missing references) //IL_02cd: Unknown result type (might be due to invalid IL or missing references) //IL_02d8: Unknown result type (might be due to invalid IL or missing references) //IL_02e2: Unknown result type (might be due to invalid IL or missing references) //IL_02ed: Unknown result type (might be due to invalid IL or missing references) //IL_02f7: Unknown result type (might be due to invalid IL or missing references) //IL_0302: Unknown result type (might be due to invalid IL or missing references) //IL_0316: Unknown result type (might be due to invalid IL or missing references) //IL_0320: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)pin.m_uiElement == (Object)null) { return null; } if (_travelBadges.TryGetValue(pin, out TravelBadge value) && (Object)(object)value.Root != (Object)null) { if ((Object)(object)value.Root.transform.parent == (Object)(object)pin.m_uiElement) { return value; } Object.Destroy((Object)(object)value.Root); } GameObject val = new GameObject("PortalTravelInfo", new Type[1] { typeof(RectTransform) }); val.transform.SetParent((Transform)(object)pin.m_uiElement, false); RectTransform val2 = (RectTransform)val.transform; val2.anchorMin = new Vector2(1f, 0.5f); val2.anchorMax = new Vector2(1f, 0.5f); val2.pivot = new Vector2(0f, 0.5f); val2.anchoredPosition = new Vector2(4f, 0f); val2.sizeDelta = new Vector2(0f, 20f); GameObject val3 = new GameObject("CoinsIcon", new Type[2] { typeof(RectTransform), typeof(Image) }); val3.transform.SetParent(val.transform, false); RectTransform val4 = (RectTransform)val3.transform; val4.anchorMin = new Vector2(0f, 0.5f); val4.anchorMax = new Vector2(0f, 0.5f); val4.pivot = new Vector2(0f, 0.5f); val4.anchoredPosition = Vector2.zero; val4.sizeDelta = new Vector2(18f, 18f); Image component = val3.GetComponent(); component.preserveAspect = true; ((Graphic)component).raycastTarget = false; GameObject val5 = new GameObject("Count", new Type[2] { typeof(RectTransform), typeof(Text) }); val5.transform.SetParent(val.transform, false); RectTransform val6 = (RectTransform)val5.transform; val6.anchorMin = Vector2.zero; val6.anchorMax = Vector2.one; val6.offsetMin = new Vector2(22f, 0f); val6.offsetMax = Vector2.zero; Text component2 = val5.GetComponent(); component2.font = GUIManager.Instance.AveriaSerif; component2.fontSize = 14; component2.fontStyle = (FontStyle)1; component2.alignment = (TextAnchor)3; component2.horizontalOverflow = (HorizontalWrapMode)1; component2.verticalOverflow = (VerticalWrapMode)0; ((Graphic)component2).raycastTarget = false; GameObject val7 = new GameObject("NoTeleport", new Type[2] { typeof(RectTransform), typeof(Image) }); val7.transform.SetParent(val.transform, false); RectTransform val8 = (RectTransform)val7.transform; val8.anchorMin = new Vector2(0f, 0.5f); val8.anchorMax = new Vector2(0f, 0.5f); val8.pivot = new Vector2(1f, 0f); val8.anchoredPosition = new Vector2(-4f, 0f); val8.sizeDelta = new Vector2(21f, 21f); Image component3 = val7.GetComponent(); component3.preserveAspect = true; ((Graphic)component3).raycastTarget = false; val7.SetActive(false); TravelBadge travelBadge = new TravelBadge(val, component, component2, component3); _travelBadges[pin] = travelBadge; return travelBadge; } private static void UpdateTravelBadge(TravelBadge badge, int travelCost, bool showNoTeleport, int localCoinCount, Sprite? sharedCoinIcon) { //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00c9: Unknown result type (might be due to invalid IL or missing references) float num = 0f; bool flag = travelCost > 0; Sprite val = (flag ? (badge.CoinIcon.sprite ?? sharedCoinIcon) : null); bool flag2 = (Object)(object)val != (Object)null; ((Component)badge.CoinIcon).gameObject.SetActive(flag2); if (flag2) { badge.CoinIcon.sprite = val; SetLeftAlignedRect(badge.CoinIconRect, num, 18f); num += 22f; } ((Component)badge.CostText).gameObject.SetActive(flag); if (flag) { badge.CostText.text = (flag2 ? PortalRulesLocalization.Translate("$sighsorry_portalrules_quantity", travelCost.ToString()) : PortalRulesLocalization.Translate("$sighsorry_portalrules_coins_quantity", travelCost.ToString())); ((Graphic)badge.CostText).color = ((localCoinCount >= travelCost) ? Color.white : UnaffordableColor); float num2 = Mathf.Ceil(badge.CostText.preferredWidth) + 2f; SetLeftAlignedRect(badge.CostTextRect, num, num2); num += num2; } Sprite sprite = null; Color color = Color.white; Material material = null; bool flag3 = showNoTeleport && PublicPortalTravelCost.TryGetNoTeleportVisual(out sprite, out color, out material); ((Component)badge.NoTeleportIcon).gameObject.SetActive(flag3); if (flag3) { badge.NoTeleportIcon.sprite = sprite; ((Graphic)badge.NoTeleportIcon).color = color; ((Graphic)badge.NoTeleportIcon).material = material; } badge.RootRect.sizeDelta = new Vector2(num, 20f); } private static void SetLeftAlignedRect(RectTransform rect, float x, float width) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0020: 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_0046: 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) rect.anchorMin = new Vector2(0f, 0.5f); rect.anchorMax = new Vector2(0f, 0.5f); rect.pivot = new Vector2(0f, 0.5f); rect.anchoredPosition = new Vector2(x, 0f); rect.sizeDelta = new Vector2(width, 18f); } public PublicPortalCatalogEntry? FindClosest(Vector3 worldPoint) { //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) PublicPortalCatalogEntry? result = null; float num = float.MaxValue; Minimap instance = Minimap.instance; float num2 = 1f; if ((Object)(object)instance != (Object)null && LargeZoom != null) { try { num2 = LargeZoom.Invoke(instance) * 2f; } catch (Exception ex) { LargeZoom = null; PortalRulesPlugin.PortalRulesLogger.LogWarning((object)("Could not read Minimap.m_largeZoom; portal pin click scaling is disabled: " + ex.Message)); } } float num3 = 96f * num2; foreach (PublicPortalCatalogEntry value in _activePins.Values) { float num4 = Utils.DistanceXZ(worldPoint, value.Position); if (num4 < num3 && num4 < num) { result = value; num = num4; } } return result; } private static FieldRef? CreateLargeZoomAccessor() { try { return AccessTools.FieldRefAccess("m_largeZoom"); } catch (Exception ex) { PortalRulesPlugin.PortalRulesLogger.LogWarning((object)("Could not bind Minimap.m_largeZoom; portal pin click scaling is disabled: " + ex.Message)); return null; } } } [HarmonyPatch] internal static class PublicPortalPinTypeRegistrar { [HarmonyPatch(typeof(Minimap), "Start")] private static class MinimapStartPatch { private static void Postfix(Minimap __instance) { EnsureRegistered(__instance); } } private static readonly FieldRef VisibleIconTypes = AccessTools.FieldRefAccess("m_visibleIconTypes"); private static PinType _portalPinType = (PinType)3; private static Minimap? _registeredMinimap; private static Sprite? _portalSprite; public static PinType PortalPinType => _portalPinType; public static void EnsureRegistered(Minimap minimap) { //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) //IL_006e: 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_0080: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)minimap == (Object)null) && _registeredMinimap != minimap) { ref bool[] reference = ref VisibleIconTypes.Invoke(minimap); Sprite portalSprite = GetPortalSprite(); if (!((Object)(object)portalSprite == (Object)null) && reference != null && minimap.m_icons != null) { int num = reference.Length; bool[] array = new bool[num + 1]; Array.Copy(reference, array, num); array[num] = true; _portalPinType = (PinType)num; reference = array; minimap.m_icons.Add(new SpriteData { m_name = _portalPinType, m_icon = portalSprite }); _registeredMinimap = minimap; } } } private static Sprite? GetPortalSprite() { //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_portalSprite != (Object)null) { return _portalSprite; } _portalSprite = FindSprite("teleport_6") ?? FindSprite("teleport_7") ?? FindSprite("mapicon_portal"); if ((Object)(object)_portalSprite != (Object)null) { return _portalSprite; } Texture2D val = FindTexture("teleport_6") ?? FindTexture("teleport_7"); if ((Object)(object)val == (Object)null) { return null; } _portalSprite = Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f), 100f); ((Object)_portalSprite).name = "PortalRules Portal Icon"; return _portalSprite; } private static Sprite? FindSprite(string name) { Sprite[] array = Resources.FindObjectsOfTypeAll(); foreach (Sprite val in array) { if ((Object)(object)val != (Object)null && ((Object)val).name == name) { return val; } } return null; } private static Texture2D? FindTexture(string name) { Texture2D[] array = Resources.FindObjectsOfTypeAll(); foreach (Texture2D val in array) { if ((Object)(object)val != (Object)null && ((Object)val).name == name) { return val; } } return null; } } [HarmonyPatch] internal static class PublicPortalTaggedConnections { private delegate void SetConnectionDelegate(Game game, ZDO portal, ZDOID connection, bool forceImmediateConnection); [HarmonyPatch(typeof(Game), "ClearCurrentlyConnectingPortals")] private static class ClearCrossModeConnectionsPatch { private static void Postfix(Game __instance) { //IL_002b: 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_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) if (!ShouldPartitionConnections() || ZDOMan.instance == null) { return; } foreach (ZDO portal in ZDOMan.instance.GetPortals()) { ZDOID connectionZDOID = portal.GetConnectionZDOID((ConnectionType)1); if (!(connectionZDOID == ZDOID.None)) { ZDO zDO = ZDOMan.instance.GetZDO(connectionZDOID); if (zDO == null || !AreConnectionScopesCompatible(portal, zDO)) { SetConnection(__instance, portal, ZDOID.None, forceImmediateConnection: false); } } } } } [HarmonyPatch(typeof(Game), "FindRandomUnconnectedPortal")] private static class FindCompatiblePortalPatch { private static void Prefix(ref List portals, ZDO skip) { if (!ShouldPartitionConnections()) { return; } List list = new List(); foreach (ZDO portal in portals) { if (AreConnectionScopesCompatible(skip, portal)) { list.Add(portal); } } portals = list; } } private static readonly SetConnectionDelegate SetConnection = AccessTools.MethodDelegate(AccessTools.DeclaredMethod(typeof(Game), "SetConnection", new Type[3] { typeof(ZDO), typeof(ZDOID), typeof(bool) }, (Type[])null), (object)null, false); internal static void RefreshConnections() { if (ShouldPartitionConnections() && (Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer() && (Object)(object)Game.instance != (Object)null) { Game.instance.ConnectPortals(); } } private static bool ShouldPartitionConnections() { return PublicPortalConfig.EnablePortalMap.Value.IsOn(); } private static bool IsTaggedConnectable(ZDO zdo) { if (zdo != null && PublicPortalKinds.IsHandledPortal(zdo)) { return PublicPortalCatalog.GetEffectiveAccessMode(zdo) == PublicPortalAccessMode.Tagged; } return false; } private static bool AreConnectionScopesCompatible(ZDO source, ZDO target) { bool flag = IsTaggedConnectable(source); bool flag2 = IsTaggedConnectable(target); if (!flag || !flag2) { return flag == flag2; } bool flag3 = PublicPortalKinds.IsAdminPortalPrefab(source.GetPrefab()); bool flag4 = PublicPortalKinds.IsAdminPortalPrefab(target.GetPrefab()); if (flag3 || flag4) { return flag3 && flag4; } string effectiveAuthorizedClanId = PublicPortalCatalog.GetEffectiveAuthorizedClanId(source); string effectiveAuthorizedClanId2 = PublicPortalCatalog.GetEffectiveAuthorizedClanId(target); if (effectiveAuthorizedClanId.Length != 0 || effectiveAuthorizedClanId2.Length != 0) { if (effectiveAuthorizedClanId.Length != 0) { return string.Equals(effectiveAuthorizedClanId, effectiveAuthorizedClanId2, StringComparison.Ordinal); } return false; } PortalOwner effectiveOwner = PublicPortalCatalog.GetEffectiveOwner(source); PortalOwner effectiveOwner2 = PublicPortalCatalog.GetEffectiveOwner(target); if (effectiveOwner.IsValid && effectiveOwner2.IsValid) { return string.Equals(effectiveOwner.Id, effectiveOwner2.Id, StringComparison.Ordinal); } return false; } } internal sealed class PublicPortalFavoritePanel { private sealed class FavoriteRowPointerHandler : MonoBehaviour, IPointerClickHandler, IEventSystemHandler, IPointerEnterHandler { private Action? _onRightClick; private Action? _onPointerEnter; internal void Initialize(Action onRightClick, Action onPointerEnter) { _onRightClick = onRightClick; _onPointerEnter = onPointerEnter; } public void OnPointerClick(PointerEventData eventData) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 if ((int)eventData.button == 1) { ((AbstractEventData)eventData).Use(); _onRightClick?.Invoke(); } } public void OnPointerEnter(PointerEventData eventData) { _onPointerEnter?.Invoke(); } } private const float PanelWidth = 320f; private const float ExpandedPanelHeight = 420f; private const float CollapsedPanelHeight = 46f; private const float HeaderHeight = 30f; private const float HeaderToggleSize = 26f; private const float HeaderToggleGap = 7f; private const int ToggleIconPixels = 32; private static readonly Color UnaffordableColor = new Color(1f, 0.42f, 0.32f); private static readonly Color CooldownRowColor = new Color(0.28f, 0.28f, 0.28f, 0.65f); private static readonly Color CooldownLabelColor = new Color(0.68f, 0.68f, 0.68f); private static readonly Color CooldownTimeColor = new Color(1f, 0.68f, 0.35f); private static readonly Color HeaderToggleColor = new Color(1f, 0.52f, 0.16f, 0.72f); private static readonly Color HeaderToggleHoverColor = new Color(1f, 0.68f, 0.28f, 0.96f); private static readonly Color HeaderTogglePressedColor = new Color(0.78f, 0.32f, 0.08f, 0.92f); private GameObject? _root; private Sprite? _collapseIcon; private Sprite? _expandIcon; public bool IsVisible => (Object)(object)_root != (Object)null; public bool IsCollapsed => PublicPortalConfig.FavoritePortalListCollapsed.Value.IsOn(); public void Refresh(PublicPortalCatalogEntry? sourcePortal, bool itemsBlocked, IEnumerable activePortals, Action onTeleport, Action onRemoveFavorite, Action onHoverPortal) { //IL_01b5: 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) EnsureRoot(); if ((Object)(object)_root == (Object)null) { return; } Transform[] array = ((IEnumerable)_root.transform).Cast().ToArray(); for (int i = 0; i < array.Length; i++) { Object.Destroy((Object)(object)((Component)array[i]).gameObject); } bool isCollapsed = IsCollapsed; CreateHeader(_root.transform, isCollapsed, ToggleCollapsed); SetPanelHeight(isCollapsed); if (isCollapsed) { return; } List source = PublicPortalData.ReadFavorites(); Dictionary portalsByFavoriteId = new Dictionary(StringComparer.Ordinal); foreach (PublicPortalCatalogEntry activePortal in activePortals) { if (!string.IsNullOrEmpty(activePortal.FavoriteId) && !portalsByFavoriteId.ContainsKey(activePortal.FavoriteId)) { portalsByFavoriteId.Add(activePortal.FavoriteId, activePortal); } } List list = (from favoriteId in source.Where(portalsByFavoriteId.ContainsKey) select portalsByFavoriteId[favoriteId]).ToList(); if (list.Count == 0) { CreateLabel(_root.transform, PortalRulesLocalization.Translate("$sighsorry_portalrules_favorite_portals_empty_hint"), 12, (FontStyle)0); return; } foreach (PublicPortalCatalogEntry item in list) { PublicPortalCatalogEntry captured = item; bool flag = false; int travelCost = 0; if (sourcePortal.HasValue) { PublicPortalCatalogEntry value = sourcePortal.Value; flag = value.Id != item.Id; if (flag) { travelCost = PublicPortalTravelCost.CalculateCost(value, item); } } long remainingSeconds; bool flag2 = InviteTravelCooldownStore.TryGetInviteArrivalCooldownRemaining(item, out remainingSeconds); string cooldownText = (flag2 ? InviteTravelCooldownStore.FormatRemaining(remainingSeconds) : ""); CreateButton(_root.transform, GetPortalDisplayName(item), travelCost, flag && itemsBlocked, flag2, cooldownText, delegate { onTeleport(captured); }, delegate { onRemoveFavorite(captured); }, delegate { onHoverPortal(captured); }); } } public void Destroy() { if ((Object)(object)_root != (Object)null) { Object.Destroy((Object)(object)_root); _root = null; } DestroyIcon(ref _collapseIcon); DestroyIcon(ref _expandIcon); } private void EnsureRoot() { //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Expected O, but got Unknown //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Expected O, but got Unknown if (!((Object)(object)_root != (Object)null) && !((Object)(object)Minimap.instance == (Object)null)) { _root = new GameObject("PublicPortalFavorites", new Type[3] { typeof(RectTransform), typeof(Image), typeof(VerticalLayoutGroup) }); _root.transform.SetParent(Minimap.instance.m_largeRoot.transform, false); RectTransform val = (RectTransform)_root.transform; val.anchorMin = new Vector2(0f, 1f); val.anchorMax = new Vector2(0f, 1f); val.pivot = new Vector2(0f, 1f); val.anchoredPosition = new Vector2(24f, -52f); val.sizeDelta = new Vector2(320f, 420f); ((Graphic)_root.GetComponent()).color = new Color(0f, 0f, 0f, 0.45f); VerticalLayoutGroup component = _root.GetComponent(); ((LayoutGroup)component).padding = new RectOffset(8, 8, 8, 8); ((HorizontalOrVerticalLayoutGroup)component).spacing = 6f; ((HorizontalOrVerticalLayoutGroup)component).childForceExpandWidth = true; ((HorizontalOrVerticalLayoutGroup)component).childForceExpandHeight = false; ((HorizontalOrVerticalLayoutGroup)component).childControlWidth = true; ((HorizontalOrVerticalLayoutGroup)component).childControlHeight = true; } } private void CreateHeader(Transform parent, bool collapsed, Action onToggle) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Expected O, but got Unknown //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Expected O, but got Unknown //IL_0103: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_0113: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_0132: Unknown result type (might be due to invalid IL or missing references) //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_0148: Unknown result type (might be due to invalid IL or missing references) //IL_015c: Unknown result type (might be due to invalid IL or missing references) //IL_016c: Unknown result type (might be due to invalid IL or missing references) //IL_0180: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Unknown result type (might be due to invalid IL or missing references) //IL_0194: Unknown result type (might be due to invalid IL or missing references) //IL_01a0: Unknown result type (might be due to invalid IL or missing references) //IL_01ac: Unknown result type (might be due to invalid IL or missing references) //IL_01cc: Unknown result type (might be due to invalid IL or missing references) //IL_01ee: Unknown result type (might be due to invalid IL or missing references) //IL_0202: Unknown result type (might be due to invalid IL or missing references) //IL_020c: Expected O, but got Unknown //IL_0231: Unknown result type (might be due to invalid IL or missing references) //IL_0236: Unknown result type (might be due to invalid IL or missing references) //IL_0248: Unknown result type (might be due to invalid IL or missing references) //IL_024e: Unknown result type (might be due to invalid IL or missing references) //IL_0253: Unknown result type (might be due to invalid IL or missing references) //IL_025e: Unknown result type (might be due to invalid IL or missing references) //IL_0268: Unknown result type (might be due to invalid IL or missing references) //IL_0273: Unknown result type (might be due to invalid IL or missing references) //IL_027d: Unknown result type (might be due to invalid IL or missing references) //IL_0288: Unknown result type (might be due to invalid IL or missing references) //IL_0292: Unknown result type (might be due to invalid IL or missing references) //IL_0293: Unknown result type (might be due to invalid IL or missing references) //IL_02a7: Unknown result type (might be due to invalid IL or missing references) //IL_02db: Unknown result type (might be due to invalid IL or missing references) //IL_02ef: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("FavoriteHeader", new Type[2] { typeof(RectTransform), typeof(LayoutElement) }); val.transform.SetParent(parent, false); val.GetComponent().preferredHeight = 30f; Text obj = CreateLabel(val.transform, PortalRulesLocalization.Translate("$sighsorry_portalrules_favorite_portals_title"), 14, (FontStyle)1); ((Graphic)obj).raycastTarget = false; RectTransform rectTransform = ((Graphic)obj).rectTransform; rectTransform.anchorMin = Vector2.zero; rectTransform.anchorMax = Vector2.one; rectTransform.offsetMin = Vector2.zero; rectTransform.offsetMax = new Vector2(-33f, 0f); GameObject val2 = new GameObject("FavoriteListToggle", new Type[3] { typeof(RectTransform), typeof(Image), typeof(Button) }); val2.transform.SetParent(val.transform, false); RectTransform val3 = (RectTransform)val2.transform; val3.anchorMin = new Vector2(1f, 0.5f); val3.anchorMax = new Vector2(1f, 0.5f); val3.pivot = new Vector2(1f, 0.5f); val3.anchoredPosition = Vector2.zero; val3.sizeDelta = new Vector2(26f, 26f); ((Graphic)val2.GetComponent()).color = Color.clear; Button component = val2.GetComponent