Decompiled source of UsefulRunestones v1.0.1

UsefulRunestones.dll

Decompiled a week ago
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.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Cryptography;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using JetBrains.Annotations;
using Microsoft.CodeAnalysis;
using ServerSync;
using TMPro;
using UnityEngine;
using YamlDotNet.Core;
using YamlDotNet.Core.Events;
using YamlDotNet.Core.ObjectPool;
using YamlDotNet.Core.Tokens;
using YamlDotNet.Helpers;
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("UsefulRunestones")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("sighsorry")]
[assembly: AssemblyProduct("UsefulRunestones")]
[assembly: AssemblyCopyright("Copyright (c) 2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("B89A0DCE-224B-4815-9A25-BB6FBE6B15BF")]
[assembly: AssemblyFileVersion("1.0.1")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.1.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.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;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace UsefulRunestones
{
	[BepInPlugin("sighsorry.UsefulRunestones", "UsefulRunestones", "1.0.1")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class UsefulRunestonesPlugin : BaseUnityPlugin
	{
		public enum Toggle
		{
			On = 1,
			Off = 0
		}

		private sealed class ConfigurationManagerAttributes
		{
			public int? Order;
		}

		internal const string ModName = "UsefulRunestones";

		internal const string ModVersion = "1.0.1";

		internal const string Author = "sighsorry";

		internal const string ModGUID = "sighsorry.UsefulRunestones";

		internal const string YamlFileName = "UsefulRunestones.yml";

		private const float FileReloadDebounceSeconds = 0.25f;

		internal static readonly ManualLogSource UsefulRunestonesLogger = Logger.CreateLogSource("UsefulRunestones");

		private static readonly ConfigSync Sync = new ConfigSync("sighsorry.UsefulRunestones")
		{
			DisplayName = "UsefulRunestones",
			CurrentVersion = "1.0.1",
			MinimumRequiredVersion = "1.0.1"
		};

		private readonly Harmony _harmony = new Harmony("sighsorry.UsefulRunestones");

		private CustomSyncedValue<string> _syncedYaml;

		private FileSystemWatcher? _watcher;

		private float _reloadDueAt = -1f;

		private int _yamlReloadQueued;

		private ConfigEntry<Toggle> _lockConfiguration;

		private ConfigEntry<Toggle> _enableRunestoneGlobalPins;

		private ConfigEntry<Toggle> _enableVegvisirGlobalEffects;

		internal static UsefulRunestonesPlugin? Instance { get; private set; }

		private static string ConfigDirectoryPath => Paths.ConfigPath;

		private static string YamlFilePath => Path.Combine(ConfigDirectoryPath, "UsefulRunestones.yml");

		private static bool IsSourceOfTruth => Sync.IsSourceOfTruth;

		internal static bool IsRunestoneGlobalPinsEnabled()
		{
			UsefulRunestonesPlugin? instance = Instance;
			if (instance == null)
			{
				return false;
			}
			return instance._enableRunestoneGlobalPins?.Value == Toggle.On;
		}

		internal static bool IsVegvisirGlobalEffectsEnabled()
		{
			UsefulRunestonesPlugin? instance = Instance;
			if (instance == null)
			{
				return false;
			}
			return instance._enableVegvisirGlobalEffects?.Value == Toggle.On;
		}

		private void Awake()
		{
			Directory.CreateDirectory(ConfigDirectoryPath);
			EnsureDefaultYamlExists();
			BindConfiguration();
			Instance = this;
			_syncedYaml = new CustomSyncedValue<string>(Sync, "configuration-yaml", "", 50);
			_syncedYaml.ValueChanged += HandleSyncedYamlChanged;
			Sync.SourceOfTruthChanged += HandleSourceOfTruthChanged;
			LoadLocalYamlAndPublish("startup");
			_harmony.PatchAll(typeof(UsefulRunestonesPlugin).Assembly);
			InitializeWatcher();
			((BaseUnityPlugin)this).Config.Save();
		}

		private void Update()
		{
			UsefulRunestonesRuntime.EnsureRunestoneGlobalPinRpcRegistered();
			ProcessQueuedYamlReload();
		}

		private void OnDestroy()
		{
			if ((Object)(object)Instance == (Object)(object)this)
			{
				Instance = null;
			}
			if (_syncedYaml != null)
			{
				_syncedYaml.ValueChanged -= HandleSyncedYamlChanged;
			}
			Sync.SourceOfTruthChanged -= HandleSourceOfTruthChanged;
			_watcher?.Dispose();
			_watcher = null;
			_harmony.UnpatchSelf();
			((BaseUnityPlugin)this).Config.Save();
		}

		private void BindConfiguration()
		{
			bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet;
			((BaseUnityPlugin)this).Config.SaveOnConfigSet = false;
			try
			{
				_lockConfiguration = BindOrderedSyncedConfigEntry("1 - General", "Lock Configuration", Toggle.On, "If on, synced configuration can be changed by server admins only.", 300);
				_enableRunestoneGlobalPins = BindOrderedSyncedConfigEntry("1 - General", "Enable Runestone Global Pins", Toggle.On, "If on, pinless RuneStones can reveal one saved map pin from UsefulRunestones.yml runestoneGlobalPins rows. Local selections are stable per loaded RuneStone; server-resolved selections are stable per RuneStone position for the server session.", 200);
				_enableVegvisirGlobalEffects = BindOrderedSyncedConfigEntry("1 - General", "Enable Vegvisir Global Effects", Toggle.On, "If on, Vegvisirs can grant weighted status effects from UsefulRunestones.yml vegvisirGlobalEffects rows when a Vegvisir interaction succeeds. The selected effect, optional shared visual effect, and per-player cooldowns live on the loaded Vegvisir instance and reset when it unloads.", 100);
				Sync.AddLockingConfigEntry<Toggle>(_lockConfiguration);
			}
			finally
			{
				((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet;
			}
		}

		private ConfigEntry<T> BindOrderedSyncedConfigEntry<T>(string group, string name, T value, string description, int configManagerOrder)
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Expected O, but got Unknown
			ConfigDescription val = new ConfigDescription(description + " [Synced with Server]", (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes
				{
					Order = configManagerOrder
				}
			});
			ConfigEntry<T> val2 = ((BaseUnityPlugin)this).Config.Bind<T>(group, name, value, val);
			Sync.AddConfigEntry<T>(val2).SynchronizedConfig = true;
			return val2;
		}

		private void EnsureDefaultYamlExists()
		{
			if (!File.Exists(YamlFilePath))
			{
				File.WriteAllText(YamlFilePath, UsefulRunestonesConfiguration.BuildDefaultYaml());
				UsefulRunestonesLogger.LogInfo((object)("Created " + YamlFilePath + "."));
			}
		}

		private void InitializeWatcher()
		{
			_watcher = new FileSystemWatcher(ConfigDirectoryPath, "UsefulRunestones.yml")
			{
				IncludeSubdirectories = false,
				NotifyFilter = (NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.LastWrite | NotifyFilters.CreationTime)
			};
			_watcher.Changed += QueueYamlReload;
			_watcher.Created += QueueYamlReload;
			_watcher.Renamed += QueueYamlReload;
			_watcher.EnableRaisingEvents = true;
		}

		private void QueueYamlReload(object sender, FileSystemEventArgs args)
		{
			Interlocked.Exchange(ref _yamlReloadQueued, 1);
		}

		private void ProcessQueuedYamlReload()
		{
			if (Interlocked.Exchange(ref _yamlReloadQueued, 0) == 1)
			{
				if (!IsSourceOfTruth)
				{
					_reloadDueAt = -1f;
					return;
				}
				_reloadDueAt = Time.realtimeSinceStartup + 0.25f;
			}
			if (!(_reloadDueAt < 0f))
			{
				if (!IsSourceOfTruth)
				{
					_reloadDueAt = -1f;
				}
				else if (!(Time.realtimeSinceStartup < _reloadDueAt))
				{
					_reloadDueAt = -1f;
					LoadLocalYamlAndPublish("file change");
				}
			}
		}

		private void HandleSourceOfTruthChanged(bool sourceOfTruth)
		{
			if (sourceOfTruth)
			{
				LoadLocalYamlAndPublish("authority change");
			}
			else
			{
				ApplyYaml(_syncedYaml.Value ?? "", "server sync");
			}
		}

		private void HandleSyncedYamlChanged()
		{
			if (!IsSourceOfTruth)
			{
				ApplyYaml(_syncedYaml.Value ?? "", "server sync");
			}
		}

		private void LoadLocalYamlAndPublish(string source)
		{
			EnsureDefaultYamlExists();
			string text;
			try
			{
				text = File.ReadAllText(YamlFilePath);
			}
			catch (Exception ex)
			{
				UsefulRunestonesLogger.LogError((object)("Failed to read " + YamlFilePath + ". " + ex.GetType().Name + ": " + ex.Message));
				return;
			}
			if (ApplyYaml(text, source) && !string.Equals(_syncedYaml.Value ?? "", text, StringComparison.Ordinal))
			{
				_syncedYaml.AssignLocalValue(text);
			}
		}

		private static bool ApplyYaml(string yaml, string source)
		{
			UsefulRunestonesConfiguration configuration;
			try
			{
				configuration = UsefulRunestonesConfiguration.Parse(yaml);
			}
			catch (Exception ex)
			{
				UsefulRunestonesLogger.LogError((object)("Rejected UsefulRunestones YAML from " + source + ". Keeping the previous configuration. " + ex.GetType().Name + ": " + ex.Message));
				return false;
			}
			UsefulRunestonesRuntime.SetConfiguration(configuration);
			UsefulRunestonesLogger.LogInfo((object)("Loaded UsefulRunestones YAML from " + source + "."));
			return true;
		}
	}
	internal sealed class UsefulRunestonesConfiguration
	{
		private static readonly IDeserializer Deserializer = new DeserializerBuilder().WithNamingConvention(CamelCaseNamingConvention.Instance).Build();

		[YamlMember(Order = 1)]
		public RunestoneGlobalPinsDefinition? RunestoneGlobalPins { get; set; }

		[YamlMember(Order = 2)]
		public VegvisirGlobalEffectsDefinition? VegvisirGlobalEffects { get; set; }

		[YamlMember(Order = 3)]
		public VegvisirGlobalEffectsLocalizationDefinition? Localization { get; set; }

		internal static UsefulRunestonesConfiguration Parse(string yaml)
		{
			UsefulRunestonesConfiguration obj = (string.IsNullOrWhiteSpace(yaml) ? new UsefulRunestonesConfiguration() : (Deserializer.Deserialize<UsefulRunestonesConfiguration>(yaml) ?? new UsefulRunestonesConfiguration()));
			Normalize(obj);
			return obj;
		}

		private static void Normalize(UsefulRunestonesConfiguration configuration)
		{
			NormalizeRunestoneGlobalPins(configuration.RunestoneGlobalPins);
			NormalizeVegvisirGlobalEffects(configuration.VegvisirGlobalEffects);
			NormalizeLocalization(configuration.Localization);
		}

		private static void NormalizeRunestoneGlobalPins(RunestoneGlobalPinsDefinition? definition)
		{
			if (definition?.TargetLocations == null)
			{
				return;
			}
			foreach (RunestoneGlobalPinTargetDefinition targetLocation in definition.TargetLocations)
			{
				targetLocation.LocationName = (targetLocation.LocationName ?? "").Trim();
				targetLocation.PinName = ShorthandScalar.NormalizeOptionalString(targetLocation.PinName, acceptNullLiteral: false);
				targetLocation.PinType = ShorthandScalar.NormalizeOptionalString(targetLocation.PinType, acceptNullLiteral: false);
				targetLocation.SourceBiomes = (from value in targetLocation.SourceBiomes?.Select((string value) => (value ?? "").Trim())
					where value.Length > 0
					select value).Distinct<string>(StringComparer.OrdinalIgnoreCase).ToList();
				targetLocation.Chance = Mathf.Clamp01(targetLocation.Chance ?? 1f);
			}
		}

		private static void NormalizeVegvisirGlobalEffects(VegvisirGlobalEffectsDefinition? definition)
		{
			if (definition?.Biomes == null)
			{
				return;
			}
			foreach (VegvisirGlobalEffectsBiomeDefinition biome in definition.Biomes)
			{
				string text = (biome.Biome ?? "").Trim();
				biome.Biome = (string.Equals(text, "All", StringComparison.OrdinalIgnoreCase) ? null : ((text.Length > 0) ? text : null));
				if (biome.StatusEffects == null)
				{
					continue;
				}
				foreach (VegvisirGlobalEffectDefinition statusEffect in biome.StatusEffects)
				{
					NormalizeVegvisirGlobalEffect(statusEffect);
				}
			}
		}

		private static void NormalizeVegvisirGlobalEffect(VegvisirGlobalEffectDefinition effect)
		{
			effect.StatusEffect = (effect.StatusEffect ?? "").Trim();
			if (effect.Weight.HasValue)
			{
				effect.Weight = Mathf.Max(0f, effect.Weight.Value);
			}
			if (effect.DurationSeconds.HasValue && effect.DurationSeconds.Value <= 0f)
			{
				effect.DurationSeconds = null;
			}
			effect.EffectPrefab = ShorthandScalar.NormalizeOptionalString(effect.EffectPrefab, acceptNullLiteral: false);
		}

		private static void NormalizeLocalization(VegvisirGlobalEffectsLocalizationDefinition? localization)
		{
			if (localization != null)
			{
				localization.MessageVegvisirEffectReceived = ShorthandScalar.NormalizeOptionalString(localization.MessageVegvisirEffectReceived, acceptNullLiteral: false);
				localization.MessageVegvisirClearStatus = ShorthandScalar.NormalizeOptionalString(localization.MessageVegvisirClearStatus, acceptNullLiteral: false);
				localization.MessageVegvisirBuffCooldown = ShorthandScalar.NormalizeOptionalString(localization.MessageVegvisirBuffCooldown, acceptNullLiteral: false);
				localization.MessageVegvisirAlreadyActive = ShorthandScalar.NormalizeOptionalString(localization.MessageVegvisirAlreadyActive, acceptNullLiteral: false);
			}
		}

		internal static string BuildDefaultYaml()
		{
			return "# UsefulRunestones\n#\n# runestoneGlobalPins: pinless RuneStones can add map pins for configured locations.\n# Row: locationName, chance, pinName, pinType, sourceBiomes\n# ex) Vendor_BlackForest, 0.5, Some pin name, Icon3, [Meadows]\n# chance is the final selection chance. Remaining chance means no pin; totals over 1 are normalized.\n# normalized ex) three targets at 0.5 total 1.5, so each selected target has 0.5 / 1.5 = 33.3%.\n# sourceBiomes adds extra RuneStone source biomes; [] means only the target location's own biome.\n# pinName defaults to target Location.m_discoverLabel, then child Teleport.m_enterText, then locationName.\n# pinType defaults to Icon3. Options: Icon0, Icon1, Icon2, Icon3, Death, Bed, Icon4, Shout, None, Boss, Player, RandomEvent, Ping, EventArea, Hildir1, Hildir2, Hildir3.\nrunestoneGlobalPins:\n  - Vendor_BlackForest, 0.5, Haldor, Icon3\n  - CombatRuin01, 0.5\n  - Hildir_camp, 0.5\n  - BogWitch_Camp, 0.5\n  - SunkenCrypt4, 0.5\n  - MountainCave02, 0.5\n  - StoneHenge1, 0.5\n  - StoneHenge3, 0.5\n  - StoneHenge4, 0.5\n  - StoneHenge5, 0.5\n  - Mistlands_DvergrTownEntrance1, 0.5\n  - Mistlands_DvergrTownEntrance2, 0.5\n  - Mistlands_Excavation1, 0.5\n  - Mistlands_Excavation2, 0.5\n  - Mistlands_Excavation3, 0.5\n  - PlaceofMystery1, 0.5\n  - PlaceofMystery2, 0.5\n  - PlaceofMystery3, 0.5\n\n# vegvisirGlobalEffects: weighted rewards when a normal Vegvisir interaction succeeds.\n# Row: StatusEffect, durationSeconds, cooldownSeconds, weight, effectPrefab\n# All is pooled with the current biome row; every other key must be one biome name.\n# Each loaded Vegvisir locks one weighted pick until unload; unload/load resets its pick and cooldown state.\n# Omitted duration/cooldown use StatusEffect prefab values; omitted weight defaults to 1.\n# cooldown 0 = none; cooldown < 0 = once per loaded Vegvisir/player.\n# VRS_ClearStatus ignores duration, clears current status effects, and shows \"You got bamboozled\".\n# If the selected StatusEffect is already active, it is not reapplied and no cooldown/fx starts.\n# effectPrefab is optional and must start with vfx_, sfx_, or fx_ case-insensitively.\nvegvisirGlobalEffects:\n  - All:\n    - CorpseRun, 60, 120, 0.1, vfx_StaminaUpgrade\n    - SoftDeath, 120, 240, 0.5, vfx_HealthUpgrade\n    - VRS_ClearStatus, 0, 5, 0.1, sfx_goblin_idle\n  - Meadows:\n    - Rested, 240, 480, 1, vfx_HealthUpgrade\n    - BeltStrength, 120, 240, 1, vfx_HealthUpgrade\n    - AdrenalineRush, 120, 240, 1\n    # - Lightning, 5, 10, 1000, fx_eikthyr_stomp\n  - BlackForest:\n    - GP_Eikthyr, 120, 240, 1\n    - Potion_health_minor\n    - Potion_stamina_minor\n    - Potion_tasty, 10, 20, 1, vfx_StaminaUpgrade\n    - SetEffect_BerserkerArmor, 120, 240, 1, vfx_HealthUpgrade\n    - SetEffect_TrollArmor, 120, 240, 1, vfx_StaminaUpgrade\n    - TrinketBronzeHealth, 60, 120, 1, fx_Adrenaline1\n    - TrinketBronzeStamina, 60, 120, 1, fx_Adrenaline1\n    - AdrenalineRush, 120, 240, 1\n    - Wet, 60, -1, 0.5, sfx_gdking_scream\n  - Swamp:\n    - GP_TheElder, 120, 240, 1\n    - Potion_hasty, 120, 240, 1\n    - Potion_strength, 120, 240, 1\n    - Potion_swimmer, 240, 360, 1\n    - Potion_TrollPheromones, 240, 360, 1\n    - Potion_poisonresist, 120, 240, 1\n    - Potion_health_medium\n    - Potion_stamina_minor\n    - SetEffect_RootArmor, 120, 240, 1\n    - TrinketIronHealth, 60, 120, 0.5, fx_Adrenaline1\n    - TrinketIronStamina, 60, 120, 0.5, fx_Adrenaline1\n    - AdrenalineRush2, 120, 240, 1, fx_Adrenaline1\n    - Puke, 5, 10, 0.5, fx_Bonemass_aoe_start\n  - Mountain:\n    - GP_Bonemass, 120, 240, 1\n    - Potion_frostresist, 120, 240, 1\n    - Potion_tamer, 240, 480, 1\n    - SetEffect_FenringArmor, 120, 240, 1, vfx_StaminaUpgrade\n    - SetEffect_WolfArmor, 120, 240, 1, vfx_HealthUpgrade\n    - SetEffect_FishingHat, 240, 480, 1, vfx_StaminaUpgrade\n    - SetEffect_HarvesterArmor, 240, 480, 1, vfx_StaminaUpgrade\n    - TrinketChitinSwim, 120, 240, 0.5, fx_Adrenaline1\n    - TrinketSilverDamage, 60, 120, 0.5, fx_Adrenaline1\n    - TrinketSilverResist, 60, 120, 0.5, fx_Adrenaline1\n    - AdrenalineRush2, 120, 240, 1, fx_Adrenaline1\n    - Frost, 5, 10, 0.5, sfx_dragon_scream\n  - Plains:\n    - GP_Moder, 120, 240, 1\n    - Potion_BugRepellent, 120, 240, 1\n    - Potion_bzerker, 30, 60, 1\n    - Potion_barleywine, 120, 240, 1\n    - Potion_stamina_medium\n    - SetEffect_BerserkerUndeadArmor, 120, 240, 1, vfx_StaminaUpgrade\n    - TrinketBlackDamageHealth, 60, 120, 0.5, fx_Adrenaline1\n    - TrinketBlackStamina, 60, 120, 0.5, fx_Adrenaline1\n    - AdrenalineRush3, 120, 240, 1, fx_Adrenaline1\n    - Tared, 5, 10, 0.5, sfx_goblinking_taunt\n  - Mistlands:\n    - GP_Yagluth, 120, 240, 1\n    - Potion_eitr_minor\n    - Potion_health_major\n    - Potion_LightFoot, 120, 240, 1\n    - Potion_stamina_lingering, 120, 240, 1\n    - SetEffect_MageArmor, 120, 240, 1, vfx_StaminaUpgrade\n    - SE_Dvergr_buff, 60, 120, 1, vfx_HealthUpgrade\n    - SlowFall, 60, 120, 1, vfx_StaminaUpgrade\n    - Staff_shield, 120, 240, 1, vfx_HealthUpgrade\n    - TrinketCarapaceEitr, 60, 120, 0.5, fx_Adrenaline1\n    - TrinketScaleStaminaDamage, 60, 120, 0.5, fx_Adrenaline1\n    - Demister, 120, 240, 1\n    - AdrenalineRush3, 120, 240, 1, fx_Adrenaline1\n    - Slimed, 5, 10, 0.5, sfx_HiveQueen_callout\n  - AshLands:\n    - GP_Queen, 120, 240, 1\n    - Potion_eitr_lingering, 120, 240, 1\n    - Potion_health_lingering, 120, 240, 1\n    - SetEffect_AshlandsMediumArmor, 120, 240, 1, vfx_StaminaUpgrade\n    - WindRun, 120, 240, 1, vfx_StaminaUpgrade\n    - TrinketFlametalEitr, 60, 120, 0.5, fx_Adrenaline1\n    - TrinketFlametalStaminaHealth, 60, 120, 0.5, fx_Adrenaline1\n    - Warm, 60, 120, 0.5, vfx_HealthUpgrade\n    - AdrenalineRush4, 120, 240, 1, fx_Adrenaline1\n    - Immobilized, 5, 10, 0.5, sfx_fader_taunt\n  - DeepNorth:\n    - GP_Fader, 120, 240, 1\n    - AdrenalineRush4, 120, 240, 1, fx_Adrenaline1\n\nlocalization:\n  messageVegvisirEffectReceived: \"You have received {name}\"\n  messageVegvisirClearStatus: \"You got bamboozled\"\n  messageVegvisirBuffCooldown: \"Buff Cooldown {seconds}s\"\n  messageVegvisirAlreadyActive: \"Already active {name}\"";
		}
	}
	internal sealed class RunestoneGlobalPinsDefinition : IYamlConvertible
	{
		[YamlMember(Order = 1)]
		public List<RunestoneGlobalPinTargetDefinition>? TargetLocations { get; set; }

		void IYamlConvertible.Read(IParser parser, Type expectedType, ObjectDeserializer nestedObjectDeserializer)
		{
			List<RunestoneGlobalPinTargetDefinition> list = new List<RunestoneGlobalPinTargetDefinition>();
			parser.Consume<SequenceStart>();
			SequenceEnd @event;
			while (!parser.Accept<SequenceEnd>(out @event))
			{
				RunestoneGlobalPinTargetDefinition runestoneGlobalPinTargetDefinition = (RunestoneGlobalPinTargetDefinition)nestedObjectDeserializer(typeof(RunestoneGlobalPinTargetDefinition));
				if (runestoneGlobalPinTargetDefinition != null)
				{
					list.Add(runestoneGlobalPinTargetDefinition);
				}
			}
			parser.Consume<SequenceEnd>();
			TargetLocations = list;
		}

		void IYamlConvertible.Write(IEmitter emitter, ObjectSerializer nestedObjectSerializer)
		{
			emitter.Emit(new SequenceStart(null, null, isImplicit: false, SequenceStyle.Block));
			IEnumerable<RunestoneGlobalPinTargetDefinition> targetLocations = TargetLocations;
			foreach (RunestoneGlobalPinTargetDefinition item in targetLocations ?? Enumerable.Empty<RunestoneGlobalPinTargetDefinition>())
			{
				nestedObjectSerializer(item);
			}
			emitter.Emit(new SequenceEnd());
		}
	}
	internal sealed class RunestoneGlobalPinTargetDefinition : IYamlConvertible
	{
		[YamlMember(Order = 1)]
		public string LocationName { get; set; } = "";

		[YamlMember(Order = 2)]
		public float? Chance { get; set; }

		[YamlMember(Order = 3)]
		public string? PinName { get; set; }

		[YamlMember(Order = 4)]
		public string? PinType { get; set; }

		[YamlMember(Order = 5)]
		public List<string>? SourceBiomes { get; set; }

		void IYamlConvertible.Read(IParser parser, Type expectedType, ObjectDeserializer nestedObjectDeserializer)
		{
			if (!parser.TryConsume<YamlDotNet.Core.Events.Scalar>(out var @event))
			{
				throw new YamlException("runestoneGlobalPins rows must use scalar shorthand: locationName, chance, pinName, pinType, sourceBiomes.");
			}
			ReadShorthand(@event.Value);
		}

		void IYamlConvertible.Write(IEmitter emitter, ObjectSerializer nestedObjectSerializer)
		{
			emitter.Emit(new YamlDotNet.Core.Events.Scalar(FormatShorthand()));
		}

		private void ReadShorthand(string? rawValue)
		{
			List<string> list = SplitShorthand(rawValue);
			if (list.Count > 5)
			{
				throw new YamlException("runestoneGlobalPins rows support at most five comma-separated values: locationName, chance, pinName, pinType, sourceBiomes.");
			}
			LocationName = ((list.Count > 0) ? list[0].Trim() : "");
			Chance = ((list.Count > 1) ? ShorthandScalar.ParseOptionalFloat(list[1], "chance", "runestoneGlobalPins", acceptNullLiteral: true) : ((float?)null));
			PinName = ((list.Count > 2) ? ShorthandScalar.NormalizeOptionalString(list[2], acceptNullLiteral: true) : null);
			PinType = ((list.Count > 3) ? ShorthandScalar.NormalizeOptionalString(list[3], acceptNullLiteral: true) : null);
			SourceBiomes = ((list.Count > 4) ? ParseSourceBiomes(list[4]) : null);
		}

		private string FormatShorthand()
		{
			List<string> list = new List<string> { LocationName ?? "" };
			List<string> sourceBiomes = SourceBiomes;
			int num = ((sourceBiomes != null && sourceBiomes.Count > 0) ? 4 : ((!string.IsNullOrWhiteSpace(PinType)) ? 3 : ((!string.IsNullOrWhiteSpace(PinName)) ? 2 : (Chance.HasValue ? 1 : 0))));
			if (num >= 1)
			{
				list.Add(ShorthandScalar.FormatOptionalFloat(Chance));
			}
			if (num >= 2)
			{
				list.Add(PinName ?? "");
			}
			if (num >= 3)
			{
				list.Add(PinType ?? "");
			}
			if (num >= 4)
			{
				list.Add(FormatSourceBiomes(SourceBiomes));
			}
			return string.Join(", ", list);
		}

		private static List<string> SplitShorthand(string? rawValue)
		{
			List<string> list = new List<string>();
			StringBuilder stringBuilder = new StringBuilder();
			int num = 0;
			string text = rawValue ?? "";
			foreach (char c in text)
			{
				switch (c)
				{
				case '[':
					num++;
					stringBuilder.Append(c);
					continue;
				case ']':
					num--;
					if (num < 0)
					{
						throw new YamlException("runestoneGlobalPins sourceBiomes has an unmatched ']'.");
					}
					stringBuilder.Append(c);
					continue;
				case ',':
					if (num == 0)
					{
						list.Add(stringBuilder.ToString().Trim());
						stringBuilder.Clear();
						continue;
					}
					break;
				}
				stringBuilder.Append(c);
			}
			if (num != 0)
			{
				throw new YamlException("runestoneGlobalPins sourceBiomes has an unmatched '['.");
			}
			list.Add(stringBuilder.ToString().Trim());
			return list;
		}

		private static List<string>? ParseSourceBiomes(string? rawValue)
		{
			string text = (rawValue ?? "").Trim();
			if (text.Length == 0 || string.Equals(text, "null", StringComparison.OrdinalIgnoreCase))
			{
				return null;
			}
			if (text.StartsWith("[", StringComparison.Ordinal) || text.EndsWith("]", StringComparison.Ordinal))
			{
				if (!text.StartsWith("[", StringComparison.Ordinal) || !text.EndsWith("]", StringComparison.Ordinal))
				{
					throw new YamlException("runestoneGlobalPins sourceBiomes must use [Biome] or [Biome, Biome] when brackets are used.");
				}
				string text2 = text.Substring(1, text.Length - 2).Trim();
				if (text2.Length == 0)
				{
					return new List<string>();
				}
				return (from value in text2.Split(new char[1] { ',' })
					select value.Trim() into value
					where value.Length > 0
					select value).ToList();
			}
			return new List<string> { text };
		}

		private static string FormatSourceBiomes(List<string>? sourceBiomes)
		{
			if (sourceBiomes == null || sourceBiomes.Count == 0)
			{
				return "[]";
			}
			return "[" + string.Join(", ", sourceBiomes) + "]";
		}
	}
	internal sealed class VegvisirGlobalEffectsDefinition : IYamlConvertible
	{
		[YamlMember(Order = 1)]
		public List<VegvisirGlobalEffectsBiomeDefinition>? Biomes { get; set; }

		void IYamlConvertible.Read(IParser parser, Type expectedType, ObjectDeserializer nestedObjectDeserializer)
		{
			List<VegvisirGlobalEffectsBiomeDefinition> list = new List<VegvisirGlobalEffectsBiomeDefinition>();
			parser.Consume<SequenceStart>();
			SequenceEnd @event;
			while (!parser.Accept<SequenceEnd>(out @event))
			{
				parser.Consume<MappingStart>();
				MappingEnd event2;
				while (!parser.Accept<MappingEnd>(out event2))
				{
					string rawKey = (parser.Consume<YamlDotNet.Core.Events.Scalar>().Value ?? "").Trim();
					List<VegvisirGlobalEffectDefinition> statusEffects = (List<VegvisirGlobalEffectDefinition>)nestedObjectDeserializer(typeof(List<VegvisirGlobalEffectDefinition>));
					list.Add(new VegvisirGlobalEffectsBiomeDefinition
					{
						Biome = ParseVegvisirGlobalEffectsBiomeKey(rawKey),
						StatusEffects = statusEffects
					});
				}
				parser.Consume<MappingEnd>();
			}
			parser.Consume<SequenceEnd>();
			Biomes = list;
		}

		void IYamlConvertible.Write(IEmitter emitter, ObjectSerializer nestedObjectSerializer)
		{
			emitter.Emit(new SequenceStart(null, null, isImplicit: false, SequenceStyle.Block));
			IEnumerable<VegvisirGlobalEffectsBiomeDefinition> biomes = Biomes;
			foreach (VegvisirGlobalEffectsBiomeDefinition item in biomes ?? Enumerable.Empty<VegvisirGlobalEffectsBiomeDefinition>())
			{
				emitter.Emit(new MappingStart(null, null, isImplicit: false, MappingStyle.Block));
				emitter.Emit(new YamlDotNet.Core.Events.Scalar(FormatVegvisirGlobalEffectsBiomeKey(item.Biome)));
				nestedObjectSerializer(item.StatusEffects ?? new List<VegvisirGlobalEffectDefinition>());
				emitter.Emit(new MappingEnd());
			}
			emitter.Emit(new SequenceEnd());
		}

		private static string? ParseVegvisirGlobalEffectsBiomeKey(string rawKey)
		{
			string text = rawKey.Trim();
			if (text.Length == 0)
			{
				throw new YamlException("vegvisirGlobalEffects biome key cannot be empty. Use All to match every biome.");
			}
			if (text.StartsWith("[", StringComparison.Ordinal) || text.EndsWith("]", StringComparison.Ordinal))
			{
				throw new YamlException("vegvisirGlobalEffects biome key '" + text + "' is invalid. Use one biome name per row, or All to match every biome.");
			}
			if (Enumerable.Contains(text, ','))
			{
				throw new YamlException("vegvisirGlobalEffects biome key '" + text + "' is invalid. Use one biome name per row instead of comma-separated biome names.");
			}
			if (text == "*")
			{
				throw new YamlException("vegvisirGlobalEffects uses '*' as a biome wildcard. Use All instead.");
			}
			if (!string.Equals(text, "all", StringComparison.OrdinalIgnoreCase))
			{
				return text;
			}
			return null;
		}

		private static string FormatVegvisirGlobalEffectsBiomeKey(string? biome)
		{
			string text = (biome ?? "").Trim();
			if (text.Length != 0)
			{
				return text;
			}
			return "All";
		}
	}
	internal sealed class VegvisirGlobalEffectsLocalizationDefinition
	{
		[YamlMember(Order = 1)]
		public string? MessageVegvisirEffectReceived { get; set; }

		[YamlMember(Order = 2)]
		public string? MessageVegvisirClearStatus { get; set; }

		[YamlMember(Order = 3)]
		public string? MessageVegvisirBuffCooldown { get; set; }

		[YamlMember(Order = 4)]
		public string? MessageVegvisirAlreadyActive { get; set; }
	}
	internal sealed class VegvisirGlobalEffectsBiomeDefinition
	{
		[YamlMember(Order = 1)]
		public string? Biome { get; set; }

		[YamlMember(Order = 2)]
		public List<VegvisirGlobalEffectDefinition>? StatusEffects { get; set; }
	}
	internal sealed class VegvisirGlobalEffectDefinition : IYamlConvertible
	{
		[YamlIgnore]
		public string StatusEffect { get; set; } = "";

		[YamlIgnore]
		public float? DurationSeconds { get; set; }

		[YamlIgnore]
		public float? CooldownSeconds { get; set; }

		[YamlIgnore]
		public float? Weight { get; set; }

		[YamlIgnore]
		public string? EffectPrefab { get; set; }

		void IYamlConvertible.Read(IParser parser, Type expectedType, ObjectDeserializer nestedObjectDeserializer)
		{
			if (!parser.TryConsume<YamlDotNet.Core.Events.Scalar>(out var @event))
			{
				throw new YamlException("vegvisirGlobalEffects status effect rows must use scalar shorthand: StatusEffect, durationSeconds, cooldownSeconds, weight, effectPrefab.");
			}
			ReadShorthand(@event.Value);
		}

		void IYamlConvertible.Write(IEmitter emitter, ObjectSerializer nestedObjectSerializer)
		{
			emitter.Emit(new YamlDotNet.Core.Events.Scalar(FormatShorthand()));
		}

		private void ReadShorthand(string? rawValue)
		{
			string[] array = (rawValue ?? "").Split(new char[1] { ',' });
			if (array.Length > 5)
			{
				throw new YamlException("vegvisirGlobalEffects status effect rows support at most five comma-separated values: StatusEffect, durationSeconds, cooldownSeconds, weight, effectPrefab.");
			}
			StatusEffect = ((array.Length != 0) ? array[0].Trim() : "");
			DurationSeconds = ((array.Length > 1) ? ShorthandScalar.ParseOptionalFloat(array[1], "durationSeconds", "vegvisirGlobalEffects", acceptNullLiteral: false) : ((float?)null));
			CooldownSeconds = ((array.Length > 2) ? ShorthandScalar.ParseOptionalFloat(array[2], "cooldownSeconds", "vegvisirGlobalEffects", acceptNullLiteral: false) : ((float?)null));
			Weight = ((array.Length > 3) ? ShorthandScalar.ParseOptionalFloat(array[3], "weight", "vegvisirGlobalEffects", acceptNullLiteral: false) : ((float?)null));
			EffectPrefab = ((array.Length > 4) ? ShorthandScalar.NormalizeOptionalString(array[4], acceptNullLiteral: false) : null);
		}

		private string FormatShorthand()
		{
			List<string> list = new List<string> { StatusEffect ?? "" };
			int num = ((!string.IsNullOrWhiteSpace(EffectPrefab)) ? 4 : (Weight.HasValue ? 3 : (CooldownSeconds.HasValue ? 2 : (DurationSeconds.HasValue ? 1 : 0))));
			if (num >= 1)
			{
				list.Add(ShorthandScalar.FormatOptionalFloat(DurationSeconds));
			}
			if (num >= 2)
			{
				list.Add(ShorthandScalar.FormatOptionalFloat(CooldownSeconds));
			}
			if (num >= 3)
			{
				list.Add(ShorthandScalar.FormatOptionalFloat(Weight));
			}
			if (num >= 4)
			{
				list.Add(EffectPrefab ?? "");
			}
			return string.Join(", ", list);
		}
	}
	internal static class ShorthandScalar
	{
		internal static float? ParseOptionalFloat(string? rawValue, string fieldName, string context, bool acceptNullLiteral)
		{
			string text = (rawValue ?? "").Trim();
			if (text.Length == 0 || (acceptNullLiteral && string.Equals(text, "null", StringComparison.OrdinalIgnoreCase)))
			{
				return null;
			}
			if (!float.TryParse(text, NumberStyles.Float, CultureInfo.InvariantCulture, out var result) || float.IsNaN(result) || float.IsInfinity(result))
			{
				throw new YamlException(context + " value '" + text + "' is not a valid " + fieldName + " number.");
			}
			return result;
		}

		internal static string FormatOptionalFloat(float? value)
		{
			if (!value.HasValue)
			{
				return "";
			}
			return value.Value.ToString("R", CultureInfo.InvariantCulture);
		}

		internal static string? NormalizeOptionalString(string? rawValue, bool acceptNullLiteral)
		{
			string text = (rawValue ?? "").Trim();
			if (text.Length != 0 && (!acceptNullLiteral || !string.Equals(text, "null", StringComparison.OrdinalIgnoreCase)))
			{
				return text;
			}
			return null;
		}
	}
	internal static class UsefulRunestonesRuntime
	{
		private sealed class RunestoneGlobalPinsRollState
		{
			public string DefinitionKey { get; set; } = "";

			public ResolvedRunestoneGlobalPin? Pin { get; set; }
		}

		private sealed class RunestoneGlobalPinRequestState
		{
			public long LastAcceptedTimestamp { get; set; }
		}

		private sealed class RunestoneGlobalPinCandidate
		{
			public float Chance { get; set; }

			public ResolvedRunestoneGlobalPin Pin { get; set; } = new ResolvedRunestoneGlobalPin();
		}

		private sealed class ResolvedRunestoneGlobalPin
		{
			public string LocationName { get; set; } = "";

			public string PinName { get; set; } = "";

			public PinType PinType { get; set; } = (PinType)3;

			public Vector3 Position { get; set; }
		}

		private sealed class RunestoneGlobalPinLocationIndex
		{
			public int ZoneSystemId { get; set; }

			public int LocationInstanceCount { get; set; }

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

		private sealed class VegvisirGlobalEffectCandidate
		{
			public VegvisirGlobalEffectDefinition Definition { get; set; } = new VegvisirGlobalEffectDefinition();

			public StatusEffect? StatusEffect { get; set; }

			public float Weight { get; set; }

			public bool ClearsStatusEffects { get; set; }

			public string EffectKey { get; set; } = "";
		}

		private sealed class VegvisirGlobalEffectSelectionState
		{
			public string CandidateSignature { get; set; } = "";

			public int SelectedIndex { get; set; } = -1;

			public Dictionary<long, DateTime> LastGrantedByPlayer { get; } = new Dictionary<long, DateTime>();
		}

		private static readonly object RuntimeWarningLock = new object();

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

		private static UsefulRunestonesConfiguration _configuration = new UsefulRunestonesConfiguration();

		private const int RunestoneGlobalPinRpcVersion = 1;

		private const string RunestoneGlobalPinRequestRpc = "UsefulRunestones Runestone GlobalPin Request";

		private const string RunestoneGlobalPinResponseRpc = "UsefulRunestones Runestone GlobalPin Response";

		private const float RunestoneGlobalPinRpcInteractionDistance = 10f;

		private const double RunestoneGlobalPinRpcRequestIntervalSeconds = 1.0;

		private const float RunestoneGlobalPinServerRollCellSize = 16f;

		private static readonly ConditionalWeakTable<RuneStone, RunestoneGlobalPinsRollState> RunestoneGlobalPinsRolls = new ConditionalWeakTable<RuneStone, RunestoneGlobalPinsRollState>();

		private static readonly ConditionalWeakTable<ZNetPeer, RunestoneGlobalPinRequestState> RunestoneGlobalPinRequestStates = new ConditionalWeakTable<ZNetPeer, RunestoneGlobalPinRequestState>();

		private static readonly object RunestoneGlobalPinsLock = new object();

		private static readonly Random RunestoneGlobalPinsRandom = new Random();

		private static readonly byte[] RunestoneGlobalPinServerRollSecret = CreateRunestoneGlobalPinServerRollSecret();

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

		private static RunestoneGlobalPinLocationIndex? RunestoneGlobalPinLocationIndexCache;

		private static ZRoutedRpc? RunestoneGlobalPinRegisteredRpcInstance;

		private const string VegvisirGlobalEffectClearStatusKey = "VRS_ClearStatus";

		private static readonly object VegvisirGlobalEffectsLock = new object();

		private static readonly Random VegvisirGlobalEffectsRandom = new Random();

		private static readonly ConditionalWeakTable<Vegvisir, VegvisirGlobalEffectSelectionState> VegvisirGlobalEffectSelections = new ConditionalWeakTable<Vegvisir, VegvisirGlobalEffectSelectionState>();

		internal static void SetConfiguration(UsefulRunestonesConfiguration configuration)
		{
			_configuration = configuration;
			ClearRuntimeWarnings();
			ResetRunestoneGlobalPinsRuntimeState();
		}

		private static bool IsGameDataReady()
		{
			if ((Object)(object)ZoneSystem.instance != (Object)null && (Object)(object)ZNetScene.instance != (Object)null)
			{
				return (Object)(object)ObjectDB.instance != (Object)null;
			}
			return false;
		}

		private static string GetZoneLocationPrefabName(ZoneLocation? location)
		{
			return (location?.m_prefabName ?? location?.m_prefab.Name ?? "").Trim();
		}

		private static StatusEffect? ResolveStatusEffect(string? statusEffectName, string? warnContext)
		{
			string trimmedName = (statusEffectName ?? "").Trim();
			if (trimmedName.Length == 0)
			{
				return null;
			}
			ObjectDB instance = ObjectDB.instance;
			StatusEffect val = ((instance != null) ? instance.GetStatusEffect(StringExtensionMethods.GetStableHashCode(trimmedName)) : null);
			if ((Object)(object)val != (Object)null)
			{
				return val;
			}
			val = ((IEnumerable<StatusEffect>)ObjectDB.instance?.m_StatusEffects).FirstOrDefault((Func<StatusEffect, bool>)((StatusEffect effect) => string.Equals(((Object)effect).name, trimmedName, StringComparison.OrdinalIgnoreCase) || string.Equals(effect.m_name, trimmedName, StringComparison.OrdinalIgnoreCase)));
			if ((Object)(object)val != (Object)null)
			{
				return val;
			}
			if (!string.IsNullOrWhiteSpace(warnContext))
			{
				WarnInvalidEntry("Entry '" + warnContext + "' references unknown status effect '" + trimmedName + "'.");
			}
			return null;
		}

		private static void WarnInvalidEntry(string message)
		{
			WarnOnce(message, message);
		}

		private static void WarnOnce(string warningKey, string message)
		{
			lock (RuntimeWarningLock)
			{
				if (!RuntimeWarningLogs.Add(warningKey))
				{
					return;
				}
			}
			UsefulRunestonesPlugin.UsefulRunestonesLogger.LogWarning((object)message);
		}

		private static void ClearRuntimeWarnings()
		{
			lock (RuntimeWarningLock)
			{
				RuntimeWarningLogs.Clear();
			}
		}

		internal static void TryApplyRunestoneGlobalPins(RuneStone? runestone, bool hold, string? originalLocationName)
		{
			BossStone val = default(BossStone);
			if (hold || (Object)(object)runestone == (Object)null || ((Component)runestone).TryGetComponent<BossStone>(ref val) || !UsefulRunestonesPlugin.IsRunestoneGlobalPinsEnabled() || !string.IsNullOrWhiteSpace(originalLocationName ?? runestone.m_locationName) || TryRequestServerResolvedRunestoneGlobalPin(runestone, originalLocationName))
			{
				return;
			}
			RunestoneGlobalPinsDefinition runestoneGlobalPins = _configuration.RunestoneGlobalPins;
			if (runestoneGlobalPins?.TargetLocations != null && runestoneGlobalPins.TargetLocations.Count != 0)
			{
				ResolvedRunestoneGlobalPin orRollRunestoneGlobalPin = GetOrRollRunestoneGlobalPin(runestone, runestoneGlobalPins);
				if (orRollRunestoneGlobalPin != null)
				{
					AddOrShowRunestoneGlobalPin(orRollRunestoneGlobalPin);
				}
			}
		}

		internal static void EnsureRunestoneGlobalPinRpcRegistered()
		{
			ZRoutedRpc instance = ZRoutedRpc.instance;
			if (instance != null && instance != RunestoneGlobalPinRegisteredRpcInstance)
			{
				if (RunestoneGlobalPinRegisteredRpcInstance != null)
				{
					ResetRunestoneGlobalPinsRuntimeState();
				}
				instance.Register<ZPackage>("UsefulRunestones Runestone GlobalPin Request", (Action<long, ZPackage>)OnRunestoneGlobalPinRequestRpc);
				instance.Register<ZPackage>("UsefulRunestones Runestone GlobalPin Response", (Action<long, ZPackage>)OnRunestoneGlobalPinResponseRpc);
				RunestoneGlobalPinRegisteredRpcInstance = instance;
			}
		}

		private static void ResetRunestoneGlobalPinsRuntimeState()
		{
			lock (RunestoneGlobalPinsLock)
			{
				RunestoneGlobalPinDefaultPinNames.Clear();
				RunestoneGlobalPinLocationIndexCache = null;
			}
		}

		private static ResolvedRunestoneGlobalPin? GetOrRollRunestoneGlobalPin(RuneStone runestone, RunestoneGlobalPinsDefinition definition)
		{
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			if (ShouldWaitForRunestoneGlobalPinSourceBiomeResolution(definition))
			{
				return null;
			}
			string text = CreateRunestoneGlobalPinsDefinitionKey(definition);
			lock (RunestoneGlobalPinsLock)
			{
				if (!RunestoneGlobalPinsRolls.TryGetValue(runestone, out RunestoneGlobalPinsRollState value))
				{
					value = new RunestoneGlobalPinsRollState();
					RunestoneGlobalPinsRolls.Add(runestone, value);
				}
				if (value.DefinitionKey == text)
				{
					return value.Pin;
				}
				value.DefinitionKey = text;
				value.Pin = RollRunestoneGlobalPin(((Component)runestone).transform.position, definition);
				return value.Pin;
			}
		}

		private static ResolvedRunestoneGlobalPin? RollServerRunestoneGlobalPin(Vector3 runestonePosition, RunestoneGlobalPinsDefinition definition)
		{
			//IL_000a: 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 (ShouldWaitForRunestoneGlobalPinSourceBiomeResolution(definition))
			{
				return null;
			}
			double value = CreateRunestoneGlobalPinServerSelectionRoll(runestonePosition, definition);
			lock (RunestoneGlobalPinsLock)
			{
				return RollRunestoneGlobalPin(runestonePosition, definition, value);
			}
		}

		private static ResolvedRunestoneGlobalPin? RollRunestoneGlobalPin(Vector3 runestonePosition, RunestoneGlobalPinsDefinition definition, double? selectionRoll = null)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: 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_0070: 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_007d: Unknown result type (might be due to invalid IL or missing references)
			if (definition.TargetLocations == null || (Object)(object)ZoneSystem.instance == (Object)null)
			{
				return null;
			}
			List<RunestoneGlobalPinCandidate> list = new List<RunestoneGlobalPinCandidate>();
			Biome runestoneGlobalPinBiome = GetRunestoneGlobalPinBiome(runestonePosition);
			foreach (RunestoneGlobalPinTargetDefinition targetLocation in definition.TargetLocations)
			{
				float valueOrDefault = targetLocation.Chance.GetValueOrDefault();
				if (targetLocation.LocationName.Length != 0 && !(valueOrDefault <= 0f))
				{
					Biome allowedSourceBiomeMask = ResolveRunestoneGlobalPinSourceBiomeMask(targetLocation);
					if (TryFindClosestRunestoneGlobalPinLocation(targetLocation.LocationName, runestonePosition, runestoneGlobalPinBiome, allowedSourceBiomeMask, out var closest))
					{
						ResolvedRunestoneGlobalPin pin = CreateResolvedRunestoneGlobalPin(targetLocation, closest);
						list.Add(new RunestoneGlobalPinCandidate
						{
							Chance = valueOrDefault,
							Pin = pin
						});
					}
				}
			}
			return SelectRunestoneGlobalPinCandidate(list, selectionRoll);
		}

		private static ResolvedRunestoneGlobalPin? SelectRunestoneGlobalPinCandidate(List<RunestoneGlobalPinCandidate> candidates, double? selectionRoll)
		{
			float num = 0f;
			foreach (RunestoneGlobalPinCandidate candidate in candidates)
			{
				num += candidate.Chance;
			}
			if (num <= 0f)
			{
				return null;
			}
			if (num > 1f)
			{
				WarnOnce("globalpin|chance-total-over-1|" + num.ToString("R", CultureInfo.InvariantCulture), "Runestone global pin chance values add up to " + num.ToString("0.###", CultureInfo.InvariantCulture) + " after filtering. Normalizing them, so exactly one candidate can be selected.");
			}
			float num2 = Math.Max(1f, num);
			double num3 = (selectionRoll ?? RunestoneGlobalPinsRandom.NextDouble()) * (double)num2;
			float num4 = 0f;
			foreach (RunestoneGlobalPinCandidate candidate2 in candidates)
			{
				num4 += candidate2.Chance;
				if (num3 < (double)num4)
				{
					return candidate2.Pin;
				}
			}
			return null;
		}

		private static ResolvedRunestoneGlobalPin CreateResolvedRunestoneGlobalPin(RunestoneGlobalPinTargetDefinition target, LocationInstance locationInstance)
		{
			//IL_0046: 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_0065: 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_006d: Unknown result type (might be due to invalid IL or missing references)
			string pinName;
			if (!string.IsNullOrWhiteSpace(target.PinName))
			{
				pinName = target.PinName.Trim();
			}
			else
			{
				string runestoneGlobalPinConfiguredDefaultName = GetRunestoneGlobalPinConfiguredDefaultName(target.LocationName);
				pinName = ((runestoneGlobalPinConfiguredDefaultName.Length > 0) ? runestoneGlobalPinConfiguredDefaultName : target.LocationName);
			}
			PinType pinType = ParseRunestoneGlobalPinType(target.PinType, target.LocationName);
			return new ResolvedRunestoneGlobalPin
			{
				LocationName = target.LocationName,
				PinName = pinName,
				PinType = pinType,
				Position = locationInstance.m_position
			};
		}

		private static void AddOrShowRunestoneGlobalPin(ResolvedRunestoneGlobalPin pin)
		{
			//IL_000b: 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)
			Minimap instance = Minimap.instance;
			if (instance != null)
			{
				instance.DiscoverLocation(pin.Position, pin.PinType, pin.PinName, true);
			}
		}

		private static bool TryFindClosestRunestoneGlobalPinLocation(string locationName, Vector3 runestonePosition, Biome runestoneBiome, Biome allowedSourceBiomeMask, out LocationInstance closest)
		{
			//IL_0002: 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_002c: 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_0030: 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_003a: 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_004c: 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_0042: 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_0067: 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)
			closest = default(LocationInstance);
			if (!TryGetRunestoneGlobalPinLocationInstances(locationName, out List<LocationInstance> instances))
			{
				return false;
			}
			float num = float.MaxValue;
			bool flag = false;
			foreach (LocationInstance item in instances)
			{
				if (RunestoneGlobalPinBiomesMatch(GetRunestoneGlobalPinBiome(item.m_position), runestoneBiome) || RunestoneGlobalPinAllowsSourceBiome(runestoneBiome, allowedSourceBiomeMask))
				{
					float num2 = Utils.DistanceXZ(runestonePosition, item.m_position);
					if (!flag || num2 < num)
					{
						num = num2;
						closest = item;
						flag = true;
					}
				}
			}
			return flag;
		}

		private static bool TryGetRunestoneGlobalPinLocationInstances(string locationName, out List<LocationInstance> instances)
		{
			instances = null;
			string text = (locationName ?? "").Trim();
			if (text.Length == 0 || !TryGetRunestoneGlobalPinLocationIndex(out RunestoneGlobalPinLocationIndex index))
			{
				return false;
			}
			if (index.InstancesByName.TryGetValue(text, out instances))
			{
				return instances.Count > 0;
			}
			return false;
		}

		private static bool TryGetRunestoneGlobalPinLocationIndex(out RunestoneGlobalPinLocationIndex index)
		{
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: 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_00c8: Unknown result type (might be due to invalid IL or missing references)
			index = null;
			ZoneSystem instance = ZoneSystem.instance;
			if ((Object)(object)instance == (Object)null)
			{
				RunestoneGlobalPinLocationIndexCache = null;
				return false;
			}
			int instanceID = ((Object)instance).GetInstanceID();
			int count = instance.m_locationInstances.Count;
			if (RunestoneGlobalPinLocationIndexCache != null && RunestoneGlobalPinLocationIndexCache.ZoneSystemId == instanceID && RunestoneGlobalPinLocationIndexCache.LocationInstanceCount == count)
			{
				index = RunestoneGlobalPinLocationIndexCache;
				return true;
			}
			RunestoneGlobalPinLocationIndex runestoneGlobalPinLocationIndex = new RunestoneGlobalPinLocationIndex
			{
				ZoneSystemId = instanceID,
				LocationInstanceCount = count
			};
			foreach (LocationInstance value2 in instance.m_locationInstances.Values)
			{
				string zoneLocationPrefabName = GetZoneLocationPrefabName(value2.m_location);
				if (zoneLocationPrefabName.Length != 0)
				{
					if (!runestoneGlobalPinLocationIndex.InstancesByName.TryGetValue(zoneLocationPrefabName, out List<LocationInstance> value))
					{
						value = new List<LocationInstance>();
						runestoneGlobalPinLocationIndex.InstancesByName[zoneLocationPrefabName] = value;
					}
					value.Add(value2);
				}
			}
			RunestoneGlobalPinLocationIndexCache = runestoneGlobalPinLocationIndex;
			index = runestoneGlobalPinLocationIndex;
			return true;
		}

		private static Biome ResolveRunestoneGlobalPinSourceBiomeMask(RunestoneGlobalPinTargetDefinition target)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			if (target.SourceBiomes == null || target.SourceBiomes.Count == 0)
			{
				return (Biome)0;
			}
			if (BiomeResolutionSupport.TryResolveBiomeMask(target.SourceBiomes, out var biomeMask))
			{
				return biomeMask;
			}
			WarnOnce("globalpin|invalid-source-biome|" + target.LocationName + "|" + string.Join(",", target.SourceBiomes), "Runestone global pin target '" + target.LocationName + "' has invalid sourceBiomes value. Matching-biome RuneStones can still target it.");
			return (Biome)0;
		}

		private static bool ShouldWaitForRunestoneGlobalPinSourceBiomeResolution(RunestoneGlobalPinsDefinition definition)
		{
			if (definition.TargetLocations == null)
			{
				return false;
			}
			foreach (RunestoneGlobalPinTargetDefinition targetLocation in definition.TargetLocations)
			{
				if (BiomeResolutionSupport.ShouldWaitForExpandWorldDataBiomeResolution(targetLocation.SourceBiomes))
				{
					return true;
				}
			}
			return false;
		}

		private static bool RunestoneGlobalPinBiomesMatch(Biome targetBiome, Biome runestoneBiome)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0004: 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_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)
			//IL_000e: Invalid comparison between Unknown and I4
			if (targetBiome != runestoneBiome)
			{
				if ((int)targetBiome != 0 && (int)runestoneBiome != 0)
				{
					return (targetBiome & runestoneBiome) > 0;
				}
				return false;
			}
			return true;
		}

		private static bool RunestoneGlobalPinAllowsSourceBiome(Biome runestoneBiome, Biome allowedSourceBiomeMask)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Invalid comparison between Unknown and I4
			//IL_0008: 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_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Invalid comparison between Unknown and I4
			if ((int)allowedSourceBiomeMask != 895)
			{
				if ((int)allowedSourceBiomeMask != 0 && (int)runestoneBiome != 0)
				{
					return (allowedSourceBiomeMask & runestoneBiome) > 0;
				}
				return false;
			}
			return true;
		}

		private static Biome GetRunestoneGlobalPinBiome(Vector3 position)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_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)
			if (WorldGenerator.instance == null)
			{
				return Heightmap.FindBiome(position);
			}
			return WorldGenerator.instance.GetBiome(position);
		}

		private static string GetRunestoneGlobalPinConfiguredDefaultName(string locationName)
		{
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			if (RunestoneGlobalPinDefaultPinNames.TryGetValue(locationName, out string value))
			{
				return value;
			}
			string text = "";
			if ((Object)(object)ZoneSystem.instance != (Object)null)
			{
				foreach (ZoneLocation location in ZoneSystem.instance.m_locations)
				{
					if (string.Equals(GetZoneLocationPrefabName(location), locationName, StringComparison.OrdinalIgnoreCase) && location.m_prefab.IsValid)
					{
						location.m_prefab.Load();
						try
						{
							text = GetRunestoneGlobalPinConfiguredDefaultName(location.m_prefab.Asset);
						}
						finally
						{
							location.m_prefab.Release();
						}
						break;
					}
				}
			}
			RunestoneGlobalPinDefaultPinNames[locationName] = text;
			return text;
		}

		private static string GetRunestoneGlobalPinConfiguredDefaultName(GameObject? locationPrefab)
		{
			if ((Object)(object)locationPrefab == (Object)null)
			{
				return "";
			}
			string text = (locationPrefab.GetComponent<Location>()?.m_discoverLabel ?? "").Trim();
			if (text.Length > 0)
			{
				return text;
			}
			Teleport[] componentsInChildren = locationPrefab.GetComponentsInChildren<Teleport>(true);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				string text2 = (componentsInChildren[i]?.m_enterText ?? "").Trim();
				if (text2.Length > 0)
				{
					return text2;
				}
			}
			return "";
		}

		private static PinType ParseRunestoneGlobalPinType(string? rawPinType, string locationName)
		{
			//IL_0011: 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_0042: Unknown result type (might be due to invalid IL or missing references)
			string text = (string.IsNullOrWhiteSpace(rawPinType) ? ((object)(PinType)3/*cast due to .constrained prefix*/).ToString() : rawPinType.Trim());
			if (Enum.TryParse<PinType>(text, ignoreCase: true, out PinType result) && Enum.IsDefined(typeof(PinType), result))
			{
				return result;
			}
			WarnOnce("globalpin|invalid-pintype|" + locationName + "|" + text, "Runestone global pin target '" + locationName + "' has invalid pinType '" + text + "'. Using Icon3.");
			return (PinType)3;
		}

		private static bool TryRequestServerResolvedRunestoneGlobalPin(RuneStone runestone, string? runestoneLocationName)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Expected O, but got Unknown
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			EnsureRunestoneGlobalPinRpcRegistered();
			if (ZRoutedRpc.instance == null || (Object)(object)ZNet.instance == (Object)null || ZNet.instance.IsServer())
			{
				return false;
			}
			ZPackage val = new ZPackage();
			val.Write(1);
			val.Write(((Component)runestone).transform.position);
			val.Write(runestoneLocationName ?? runestone.m_locationName ?? "");
			long serverPeerID = ZRoutedRpc.instance.GetServerPeerID();
			if (serverPeerID != 0L)
			{
				ZRoutedRpc.instance.InvokeRoutedRPC(serverPeerID, "UsefulRunestones Runestone GlobalPin Request", new object[1] { val });
			}
			return true;
		}

		private static void OnRunestoneGlobalPinRequestRpc(long sender, ZPackage package)
		{
			//IL_002e: 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_003c: Expected O, but got Unknown
			if (ZRoutedRpc.instance != null && !((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer() && TryReadRunestoneGlobalPinRequest(package, out Vector3 runestonePosition, out string runestoneLocationName))
			{
				ResolvedRunestoneGlobalPin pin = ResolveServerRunestoneGlobalPin(sender, runestonePosition, runestoneLocationName);
				ZPackage val = new ZPackage();
				WriteRunestoneGlobalPinResponse(val, pin);
				ZRoutedRpc.instance.InvokeRoutedRPC(sender, "UsefulRunestones Runestone GlobalPin Response", new object[1] { val });
			}
		}

		private static ResolvedRunestoneGlobalPin? ResolveServerRunestoneGlobalPin(long sender, Vector3 runestonePosition, string? runestoneLocationName)
		{
			//IL_0008: 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)
			if (!UsefulRunestonesPlugin.IsRunestoneGlobalPinsEnabled() || !IsValidServerRunestoneGlobalPinRequest(sender, runestonePosition, runestoneLocationName))
			{
				return null;
			}
			RunestoneGlobalPinsDefinition runestoneGlobalPins = _configuration.RunestoneGlobalPins;
			if (runestoneGlobalPins?.TargetLocations == null || runestoneGlobalPins.TargetLocations.Count == 0)
			{
				return null;
			}
			return RollServerRunestoneGlobalPin(runestonePosition, runestoneGlobalPins);
		}

		private static bool IsValidServerRunestoneGlobalPinRequest(long sender, Vector3 runestonePosition, string? runestoneLocationName)
		{
			//IL_001f: 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_0031: Unknown result type (might be due to invalid IL or missing references)
			ZRoutedRpc instance = ZRoutedRpc.instance;
			ZNetPeer val = ((instance != null) ? instance.GetPeer(sender) : null);
			if (val == null || !val.IsReady() || !IsFiniteRunestoneGlobalPinVector3(val.GetRefPos()) || Vector3.Distance(val.GetRefPos(), runestonePosition) > 10f || !string.IsNullOrWhiteSpace(runestoneLocationName))
			{
				return false;
			}
			long timestamp = Stopwatch.GetTimestamp();
			lock (RunestoneGlobalPinsLock)
			{
				RunestoneGlobalPinRequestState orCreateValue = RunestoneGlobalPinRequestStates.GetOrCreateValue(val);
				if (orCreateValue.LastAcceptedTimestamp != 0L && (double)(timestamp - orCreateValue.LastAcceptedTimestamp) / (double)Stopwatch.Frequency < 1.0)
				{
					return false;
				}
				orCreateValue.LastAcceptedTimestamp = timestamp;
				return true;
			}
		}

		private static bool TryReadRunestoneGlobalPinRequest(ZPackage package, out Vector3 runestonePosition, out string runestoneLocationName)
		{
			//IL_0001: 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_0030: Unknown result type (might be due to invalid IL or missing references)
			runestonePosition = default(Vector3);
			runestoneLocationName = "";
			try
			{
				if (package.ReadInt() != 1)
				{
					return false;
				}
				runestonePosition = package.ReadVector3();
				runestoneLocationName = package.ReadString();
				return IsFiniteRunestoneGlobalPinVector3(runestonePosition);
			}
			catch (Exception ex)
			{
				WarnOnce("globalpin|rpc-request-invalid", "Received invalid runestone global pin request RPC. Ignoring it. " + ex.GetType().Name + ": " + ex.Message);
				return false;
			}
		}

		private static void WriteRunestoneGlobalPinResponse(ZPackage package, ResolvedRunestoneGlobalPin? pin)
		{
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Expected I4, but got Unknown
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			package.Write(1);
			package.Write(pin != null);
			if (pin != null)
			{
				package.Write(pin.LocationName ?? "");
				package.Write(pin.PinName ?? "");
				package.Write((int)pin.PinType);
				package.Write(pin.Position);
			}
		}

		private static void OnRunestoneGlobalPinResponseRpc(long sender, ZPackage package)
		{
			if (!((Object)(object)ZNet.instance == (Object)null) && !ZNet.instance.IsServer() && !((Object)(object)Minimap.instance == (Object)null) && !((Object)(object)Player.m_localPlayer == (Object)null) && UsefulRunestonesPlugin.IsRunestoneGlobalPinsEnabled())
			{
				List<RunestoneGlobalPinTargetDefinition> list = _configuration.RunestoneGlobalPins?.TargetLocations;
				if (list != null && list.Count > 0 && IsRunestoneGlobalPinServerRoutedSender(sender) && TryReadRunestoneGlobalPinResponse(package, out ResolvedRunestoneGlobalPin pin) && pin != null)
				{
					AddOrShowRunestoneGlobalPin(pin);
				}
			}
		}

		private static bool IsRunestoneGlobalPinServerRoutedSender(long sender)
		{
			if (ZRoutedRpc.instance != null)
			{
				return sender == ZRoutedRpc.instance.GetServerPeerID();
			}
			return false;
		}

		private static bool TryReadRunestoneGlobalPinResponse(ZPackage package, out ResolvedRunestoneGlobalPin? pin)
		{
			//IL_0030: 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_0042: 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_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			pin = null;
			try
			{
				if (package.ReadInt() != 1 || !package.ReadBool())
				{
					return true;
				}
				string locationName = package.ReadString();
				string pinName = package.ReadString();
				PinType val = (PinType)package.ReadInt();
				Vector3 val2 = package.ReadVector3();
				if (!Enum.IsDefined(typeof(PinType), val) || !IsFiniteRunestoneGlobalPinVector3(val2))
				{
					return false;
				}
				pin = new ResolvedRunestoneGlobalPin
				{
					LocationName = locationName,
					PinName = pinName,
					PinType = val,
					Position = val2
				};
				return true;
			}
			catch (Exception ex)
			{
				WarnOnce("globalpin|rpc-response-invalid", "Received invalid runestone global pin response RPC. Ignoring it. " + ex.GetType().Name + ": " + ex.Message);
				return false;
			}
		}

		private static bool IsFiniteRunestoneGlobalPinVector3(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 string CreateRunestoneGlobalPinsDefinitionKey(RunestoneGlobalPinsDefinition definition)
		{
			StringBuilder stringBuilder = new StringBuilder();
			if (definition.TargetLocations != null)
			{
				foreach (RunestoneGlobalPinTargetDefinition targetLocation in definition.TargetLocations)
				{
					stringBuilder.Append(targetLocation.LocationName).Append('|').Append(targetLocation.Chance?.ToString("R", CultureInfo.InvariantCulture) ?? "")
						.Append('|')
						.Append((targetLocation.SourceBiomes == null) ? "" : string.Join(",", targetLocation.SourceBiomes))
						.Append('|')
						.Append(targetLocation.PinName ?? "")
						.Append('|')
						.Append(targetLocation.PinType ?? "")
						.Append('\n');
				}
			}
			return stringBuilder.ToString();
		}

		private static double CreateRunestoneGlobalPinServerSelectionRoll(Vector3 runestonePosition, RunestoneGlobalPinsDefinition definition)
		{
			//IL_0008: 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)
			string s = Mathf.FloorToInt(runestonePosition.x / 16f).ToString(CultureInfo.InvariantCulture) + "|" + Mathf.FloorToInt(runestonePosition.z / 16f).ToString(CultureInfo.InvariantCulture) + "\n" + CreateRunestoneGlobalPinsDefinitionKey(definition);
			byte[] bytes = Encoding.UTF8.GetBytes(s);
			byte[] value;
			using (HMACSHA256 hMACSHA = new HMACSHA256(RunestoneGlobalPinServerRollSecret))
			{
				value = hMACSHA.ComputeHash(bytes);
			}
			return (double)(BitConverter.ToUInt64(value, 0) >> 11) / 9007199254740992.0;
		}

		private static byte[] CreateRunestoneGlobalPinServerRollSecret()
		{
			byte[] array = new byte[32];
			using RandomNumberGenerator randomNumberGenerator = RandomNumberGenerator.Create();
			randomNumberGenerator.GetBytes(array);
			return array;
		}

		internal static void TryApplyVegvisirGlobalEffects(Vegvisir? vegvisir, Humanoid? character, bool hold, bool interactionSucceeded)
		{
			VegvisirGlobalEffectsDefinition vegvisirGlobalEffects = _configuration.VegvisirGlobalEffects;
			if (!(!interactionSucceeded || hold) && !((Object)(object)vegvisir == (Object)null))
			{
				Player val = (Player)(object)((character is Player) ? character : null);
				if (val != null && !((Object)(object)val != (Object)(object)Player.m_localPlayer) && UsefulRunestonesPlugin.IsVegvisirGlobalEffectsEnabled() && IsGameDataReady() && VegvisirCanOpenMap(vegvisir) && vegvisirGlobalEffects?.Biomes != null && vegvisirGlobalEffects.Biomes.Count != 0)
				{
					TryApplyVegvisirGlobalEffects(val, vegvisir, vegvisirGlobalEffects);
				}
			}
		}

		private static void TryApplyVegvisirGlobalEffects(Player player, Vegvisir vegvisir, VegvisirGlobalEffectsDefinition definition)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			if (ShouldWaitForVegvisirGlobalEffectsBiomeResolution(definition))
			{
				return;
			}
			List<VegvisirGlobalEffectCandidate> candidates = BuildVegvisirGlobalEffectCandidates(GetVegvisirGlobalEffectBiome(((Component)vegvisir).transform.position), definition);
			VegvisirGlobalEffectSelectionState orCreateValue = VegvisirGlobalEffectSelections.GetOrCreateValue(vegvisir);
			VegvisirGlobalEffectCandidate vegvisirGlobalEffectCandidate = SelectVegvisirGlobalEffectCandidate(orCreateValue, candidates);
			if (vegvisirGlobalEffectCandidate == null)
			{
				return;
			}
			long playerID = player.GetPlayerID();
			bool alreadyActive;
			if (IsVegvisirGlobalEffectOnCooldown(orCreateValue, playerID, vegvisirGlobalEffectCandidate, out var remainingCooldownSeconds, out var alreadyReceived))
			{
				if (alreadyReceived)
				{
					QueueVegvisirGlobalEffectMessage(player, vegvisirGlobalEffectCandidate);
				}
				else
				{
					QueueVegvisirGlobalEffectCooldownMessage(player, remainingCooldownSeconds);
				}
			}
			else if (!TryGrantVegvisirGlobalEffect(player, vegvisirGlobalEffectCandidate, out alreadyActive))
			{
				if (alreadyActive && (Object)(object)vegvisirGlobalEffectCandidate.StatusEffect != (Object)null)
				{
					QueueVegvisirGlobalEffectAlreadyActiveMessage(player, vegvisirGlobalEffectCandidate.StatusEffect);
				}
			}
			else
			{
				SetVegvisirGlobalEffectLastGranted(orCreateValue, playerID, vegvisirGlobalEffectCandidate);
				SpawnVegvisirGlobalEffectPrefab(vegvisir, vegvisirGlobalEffectCandidate.Definition);
				QueueVegvisirGlobalEffectMessage(player, vegvisirGlobalEffectCandidate);
			}
		}

		private static List<VegvisirGlobalEffectCandidate> BuildVegvisirGlobalEffectCandidates(Biome sourceBiome, VegvisirGlobalEffectsDefinition definition)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			List<VegvisirGlobalEffectCandidate> list = new List<VegvisirGlobalEffectCandidate>();
			IEnumerable<VegvisirGlobalEffectsBiomeDefinition> biomes = definition.Biomes;
			foreach (VegvisirGlobalEffectsBiomeDefinition item in biomes ?? Enumerable.Empty<VegvisirGlobalEffectsBiomeDefinition>())
			{
				if (!MatchesVegvisirGlobalEffectBiome(sourceBiome, item))
				{
					continue;
				}
				IEnumerable<VegvisirGlobalEffectDefinition> statusEffects = item.StatusEffects;
				foreach (VegvisirGlobalEffectDefinition item2 in statusEffects ?? Enumerable.Empty<VegvisirGlobalEffectDefinition>())
				{
					float num = item2.Weight ?? 1f;
					if (num <= 0f)
					{
						continue;
					}
					string statusEffectName = (item2.StatusEffect ?? "").Trim();
					if (IsVegvisirGlobalClearStatusEffect(statusEffectName))
					{
						list.Add(new VegvisirGlobalEffectCandidate
						{
							Definition = item2,
							ClearsStatusEffects = true,
							EffectKey = "VRS_ClearStatus",
							Weight = num
						});
						continue;
					}
					StatusEffect val = ResolveStatusEffect(statusEffectName, "vegvisirGlobalEffects/statusEffects/statusEffect");
					if (!((Object)(object)val == (Object)null))
					{
						list.Add(new VegvisirGlobalEffectCandidate
						{
							Definition = item2,
							StatusEffect = val,
							EffectKey = GetVegvisirGlobalEffectNameKey(val),
							Weight = num
						});
					}
				}
			}
			return list;
		}

		private static VegvisirGlobalEffectCandidate? SelectVegvisirGlobalEffectCandidate(VegvisirGlobalEffectSelectionState state, List<VegvisirGlobalEffectCandidate> candidates)
		{
			if (candidates.Count == 0)
			{
				return null;
			}
			string text = BuildVegvisirGlobalEffectCandidateSignature(candidates);
			lock (VegvisirGlobalEffectsLock)
			{
				if (string.Equals(state.CandidateSignature, text, StringComparison.Ordinal) && state.SelectedIndex >= 0 && state.SelectedIndex < candidates.Count)
				{
					return candidates[state.SelectedIndex];
				}
				state.CandidateSignature = text;
				state.SelectedIndex = SelectVegvisirGlobalEffectCandidateIndex(candidates);
				state.LastGrantedByPlayer.Clear();
				return (state.SelectedIndex >= 0) ? candidates[state.SelectedIndex] : null;
			}
		}

		private static int SelectVegvisirGlobalEffectCandidateIndex(List<VegvisirGlobalEffectCandidate> candidates)
		{
			float num = 0f;
			foreach (VegvisirGlobalEffectCandidate candidate in candidates)
			{
				num += candidate.Weight;
			}
			if (num <= 0f)
			{
				return -1;
			}
			double num2 = VegvisirGlobalEffectsRandom.NextDouble() * (double)num;
			float num3 = 0f;
			for (int i = 0; i < candidates.Count; i++)
			{
				num3 += candidates[i].Weight;
				if (num2 < (double)num3)
				{
					return i;
				}
			}
			return candidates.Count - 1;
		}

		private static string BuildVegvisirGlobalEffectCandidateSignature(List<VegvisirGlobalEffectCandidate> candidates)
		{
			StringBuilder stringBuilder = new StringBuilder();
			foreach (VegvisirGlobalEffectCandidate candidate in candidates)
			{
				stringBuilder.Append('|').Append(candidate.EffectKey).Append(':')
					.Append(candidate.Weight.ToString("R", CultureInfo.InvariantCulture))
					.Append(':')
					.Append(GetEffectiveVegvisirGlobalEffectCooldownSeconds(candidate).ToString("R", CultureInfo.InvariantCulture))
					.Append(':')
					.Append(candidate.Definition.DurationSeconds?.ToString("R", CultureInfo.InvariantCulture) ?? "")
					.Append(':')
					.Append(candidate.Definition.EffectPrefab ?? "");
			}
			return stringBuilder.ToString();
		}

		private static bool MatchesVegvisirGlobalEffectBiome(Biome sourceBiome, VegvisirGlobalEffectsBiomeDefinition definition)
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Invalid comparison between Unknown and I4
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Invalid comparison between Unknown and I4
			string text = (definition.Biome ?? "").Trim();
			if (text.Length == 0)
			{
				return true;
			}
			if (!BiomeResolutionSupport.TryResolveBiomeToken(text, out var biome))
			{
				WarnInvalidEntry("vegvisirGlobalEffects uses unknown biome '" + text + "'. That biome block is ignored.");
				return false;
			}
			if ((int)biome != 895)
			{
				return (sourceBiome & biome) > 0;
			}
			return true;
		}

		private static bool ShouldWaitForVegvisirGlobalEffectsBiomeResolution(VegvisirGlobalEffectsDefinition definition)
		{
			IEnumerable<VegvisirGlobalEffectsBiomeDefinition> biomes = definition.Biomes;
			foreach (VegvisirGlobalEffectsBiomeDefinition item in biomes ?? Enumerable.Empty<VegvisirGlobalEffectsBiomeDefinition>())
			{
				string text = (item.Biome ?? "").Trim();
				if (text.Length != 0 && BiomeResolutionSupport.ShouldWaitForExpandWorldDataBiomeResolution(new string[1] { text }))
				{
					return true;
				}
			}
			return false;
		}

		private static bool TryGrantVegvisirGlobalEffect(Player player, VegvisirGlobalEffectCandidate candidate, out bool alreadyActive)
		{
			alreadyActive = false;
			SEMan sEMan = ((Character)player).GetSEMan();
			if (candidate.ClearsStatusEffects)
			{
				ClearVegvisirGlobalStatusEffects(player, sEMan);
				return true;
			}
			StatusEffect statusEffect = candidate.StatusEffect;
			if ((Object)(object)statusEffect == (Object)null)
			{
				return false;
			}
			if ((Object)(object)sEMan.GetStatusEffect(statusEffect.NameHash()) != (Object)null)
			{
				alreadyActive = true;
				return false;
			}
			List<StatusEffect> list = new List<StatusEffect>();
			string category = statusEffect.m_category;
			if (!string.IsNullOrWhiteSpace(category))
			{
				int num = statusEffect.NameHash();
				foreach (StatusEffect statusEffect2 in sEMan.GetStatusEffects())
				{
					if ((Object)(object)statusEffect2 != (Object)null && statusEffect2.NameHash() != num && string.Equals(statusEffect2.m_category, category, StringComparison.Ordinal))
					{
						list.Add(statusEffect2);
					}
				}
			}
			StatusEffect val = sEMan.AddStatusEffect(statusEffect, true, 0, 0f) ?? sEMan.GetStatusEffect(statusEffect.NameHash());
			if ((Object)(object)val == (Object)null)
			{
				return false;
			}
			foreach (StatusEffect item in list)
			{
				if (sEMan.GetStatusEffect(item.NameHash()) == item)
				{
					sEMan.RemoveStatusEffect(item, true);
				}
			}
			ApplyVegvisirGlobalEffectDuration(val, candidate.Definition);
			return true;
		}

		private static void ClearVegvisirGlobalStatusEffects(Player player, SEMan seMan)
		{
			player.ClearHardDeath();
			seMan.RemoveAllStatusEffects(true);
		}

		private static void ApplyVegvisirGlobalEffectDuration(StatusEffect activeStatusEffect, VegvisirGlobalEffectDefinition definition)
		{
			float num = ((definition.DurationSeconds.HasValue && definition.DurationSeconds.Value > 0f) ? definition.DurationSeconds.Value : ((activeStatusEffect.m_ttl > 0f) ? activeStatusEffect.m_ttl : 0f));
			if (!(num <= 0f))
			{
				activeStatusEffect.m_ttl = num;
				activeStatusEffect.m_time = 0f;
			}
		}

		private static void SpawnVegvisirGlobalEffectPrefab(Vegvisir vegvisir, VegvisirGlobalEffectDefinition definition)
		{
			//IL_0095: 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)
			string text = (definition.EffectPrefab ?? "").Trim();
			if (text.Length == 0 || (Object)(object)ZNetScene.instance == (Object)null)
			{
				return;
			}
			if (!IsAllowedVegvisirGlobalEffectPrefabName(text))
			{
				WarnOnce("vegvisir|invalid-effect-prefab|" + text, "vegvisirGlobalEffects effectPrefab '" + text + "' is ignored. effectPrefab must start with vfx_, sfx_, or fx_ (case-insensitive).");
				return;
			}
			GameObject prefab = ZNetScene.instance.GetPrefab(text);
			if ((Object)(object)prefab == (Object)null)
			{
				WarnOnce("vegvisir|missing-effect-prefab|" + text, "vegvisirGlobalEffects references unknown effect prefab '" + text + "'.");
			}
			else
			{
				ZNetScene.instance.SpawnObject(((Component)vegvisir).transform.position, ((Component)vegvisir).transform.rotation, prefab);
			}
		}

		private static bool IsAllowedVegvisirGlobalEffectPrefabName(string effectPrefabName)
		{
			if (!effectPrefabName.StartsWith("vfx_", StringComparison.OrdinalIgnoreCase) && !effectPrefabName.StartsWith("sfx_", StringComparison.OrdinalIgnoreCase))
			{
				return effectPrefabName.StartsWith("fx_", StringComparison.OrdinalIgnoreCase);
			}
			return true;
		}

		private static bool IsVegvisirGlobalEffectOnCooldown(VegvisirGlobalEffectSelectionState state, long playerId, VegvisirGlobalEffectCandidate candidate, out int remainingCooldownSeconds, out bool alreadyReceived)
		{
			remainingCooldownSeconds = 0;
			alreadyReceived = false;
			float effectiveVegvisirGlobalEffectCooldownSeconds = GetEffectiveVegvisirGlobalEffectCooldownSeconds(candidate);
			if (effectiveVegvisirGlobalEffectCooldownSeconds == 0f)
			{
				return false;
			}
			DateTime value;
			lock (VegvisirGlobalEffectsLock)
			{
				if (!state.LastGrantedByPlayer.TryGetValue(playerId, out value))
				{
					return false;
				}
			}
			if (effectiveVegvisirGlobalEffectCooldownSeconds < 0f)
			{
				alreadyReceived = true;
				return true;
			}
			double totalSeconds = (GetVegvisirGlobalEffectNow() - value).TotalSeconds;
			if (totalSeconds < 0.0)
			{
				remainingCooldownSeconds = Mathf.CeilToInt(effectiveVegvisirGlobalEffectCooldownSeconds);
				return true;
			}
			double num = (double)effectiveVegvisirGlobalEffectCooldownSeconds - totalSeconds;
			if (num <= 0.0)
			{
				return false;
			}
			remainingCooldownSeconds = Mathf.Max(1, Mathf.CeilToInt((float)num));
			return true;
		}

		private static void SetVegvisirGlobalEffectLastGranted(VegvisirGlobalEffectSelectionState state, long playerId, VegvisirGlobalEffectCandidate candidate)
		{
			if (GetEffectiveVegvisirGlobalEffectCooldownSeconds(candidate) == 0f)
			{
				lock (VegvisirGlobalEffectsLock)
				{
					state.LastGrantedByPlayer.Remove(playerId);
					return;
				}
			}
			lock (VegvisirGlobalEffectsLock)
			{
				state.LastGrantedByPlayer[playerId] = GetVegvisirGlobalEffectNow();
			}
		}

		private static float GetEffectiveVegvisirGlobalEffectCooldownSeconds(VegvisirGlobalEffectCandidate candidate)
		{
			if (candidate.Definition.CooldownSeconds.HasValue)
			{
				return candidate.Definition.CooldownSeconds.Value;
			}
			float num = candidate.StatusEffect?.m_cooldown ?? 0f;
			if (!(num > 0f))
			{
				return 0f;
			}
			return num;
		}

		private static string GetVegvisirGlobalEffectNameKey(StatusEffect statusEffect)
		{
			if (!string.IsNullOrWhiteSpace(((Object)statusEffect).name))
			{
				return ((Object)statusEffect).name;
			}
			return statusEffect.m_name;
		}

		private static bool IsVegvisirGlobalClearStatusEffect(string? statusEffectName)
		{
			return string.Equals((statusEffectName ?? "").Trim(), "VRS_ClearStatus", StringComparison.OrdinalIgnoreCase);
		}

		private static bool VegvisirCanOpenMap(Vegvisir vegvisir)
		{
			if (vegvisir.m_locations != null)
			{
				return vegvisir.m_locations.Any((VegvisrLocation location) => location?.m_showMap ?? false);
			}
			return false;
		}

		private static DateTime GetVegvisirGlobalEffectNow()
		{
			if (!((Object)(object)ZNet.instance != (Object)null))
			{
				return DateTime.UtcNow;
			}
			return ZNet.instance.GetTime();
		}

		private static Biome GetVegvisirGlobalEffectBiome(Vector3 position)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_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)
			if (WorldGenerator.instance == null)
			{
				return Heightmap.FindBiome(position);
			}
			return WorldGenerator.instance.GetBiome(position);
		}

		private static void QueueVegvisirGlobalEffectMessage(Player player, VegvisirGlobalEffectCandidate candidate)
		{
			VegvisirGlobalEffectsLocalizationDefinition localization = _configuration.Localization;
			if (candidate.ClearsStatusEffects)
			{
				QueueVegvisirGlobalEffectCenterMessage(player, GetVegvisirGlobalEffectLocalizedText(localization?.MessageVegvisirClearStatus, "You got bamboozled"));
			}
			else if (!((Object)(object)candidate.StatusEffect == (Object)null))
			{
				QueueVegvisirGlobalEffectCenterMessage(player, FormatVegvisirGlobalEffectNamedMessage(GetVegvisirGlobalEffectLocalizedText(localization?.MessageVegvisirEffectReceived, "You have received {name}"), GetVegvisirGlobalEffectDisplayName(candidate.StatusEffect)));
			}
		}

		private static void QueueVegvisirGlobalEffectCooldownMessage(Player player, int remainingCooldownSeconds)
		{
			QueueVegvisirGlobalEffectCenterMessage(player, FormatVegvisirGlobalEffectCooldownMessage(GetVegvisirGlobalEffectLocalizedText(_configuration.Localization?.MessageVegvisirBuffCooldown, "Buff Cooldown {seconds}s"), Mathf.Max(1, remainingCooldownSeconds)));
		}

		private static void QueueVegvisirGlobalEffectAlreadyActiveMessage(Player player, StatusEffect statusEffect)
		{
			QueueVegvisirGlobalEffectCenterMessage(player, FormatVegvisirGlobalEffectNamedMessage(GetVegvisirGlobalEffectLocalizedText(_configuration.Localization?.MessageVegvisirAlreadyActive, "Already active {name}"), GetVegvisirGlobalEffectDisplayName(statusEffect)));
		}

		private static string GetVegvisirGlobalEffectLocalizedText(string? configuredText, string fallbackText)
		{
			string text = (configuredText ?? "").Trim();
			if (text.Length != 0)
			{
				return text;
			}
			return fallbackText;
		}

		private static string FormatVegvisirGlobalEffectNamedMessage(string message, string displayName)
		{
			if (message.IndexOf("{name}", StringComparison.Ordinal) < 0)
			{
				return message + " \"" + displayName + "\"";
			}
			return message.Replace("{name}", displayName);
		}

		private static string FormatVegvisirGlobalEffectCooldownMessage(string message, int remainingCooldownSeconds)
		{
			string text = remainingCooldownSeconds.ToString(CultureInfo.InvariantCulture);
			if (message.IndexOf("{seconds}", StringComparison.Ordinal) >= 0)
			{
				return message.Replace("{seconds}", text);
			}
			if (message.IndexOf('N') >= 0)
			{
				return message.Replace("N", text);
			}
			return message + " " + text + "s";
		}

		private static void QueueVegvisirGlobalEffectCenterMessage(Player player, string message)
		{
			UsefulRunestonesPlugin? instance = UsefulRunestonesPlugin.Instance;
			if (instance != null)
			{
				((MonoBehaviour)instance).StartCoroutine(ShowVegvisirGlobalEffectCenterMessageAfterDelay(player.GetPlayerID(), message));
			}
		}

		private static IEnumerator ShowVegvisirGlobalEffectCenterMessageAfterDelay(long playerId, string message)
		{
			yield return (object)new WaitForSeconds(1f);
			Player localPlayer = Player.m_localPlayer;
			if (!((Object)(object)localPlayer == (Object)null) && localPlayer.GetPlayerID() == playerId)
			{
				((Character)localPlayer).Message((MessageType)2, message, 0, (Sprite)null);
			}
		}

		private static string GetVegvisirGlobalEffectDisplayName(StatusEffect statusEffect)
		{
			string text = (string.IsNullOrWhiteSpace(statusEffect.m_name) ? GetVegvisirGlobalEffectNameKey(statusEffect) : statusEffect.m_name);
			string text2 = ((Localization.instance != null) ? Localization.instance.Localize(text) : text);
			if (!string.IsNullOrWhiteSpace(text2))
			{
				return text2;
			}
			return GetVegvisirGlobalEffectNameKey(statusEffect);
		}
	}
	[HarmonyPatch(typeof(RuneStone), "Interact")]
	internal static class RuneStoneInteractGlobalPinsPatch
	{
		private static void Prefix(RuneStone __instance, ref string? __state)
		{
			__state = __instance.m_locationName;
		}

		private static void Postfix(RuneStone __instance, bool hold, string? __state)
		{
			UsefulRunestonesRuntime.TryApplyRunestoneGlobalPins(__instance, hold, __state);
		}
	}
	[HarmonyPatch(typeof(Vegvisir), "Interact")]
	internal static class VegvisirInteractGlobalEffectsPatch
	{
		private static void Postfix(Vegvisir __instance, Humanoid character, bool hold, bool __result)
		{
			UsefulRunestonesRuntime.TryApplyVegvisirGlobalEffects(__instance, character, hold, __result);
		}
	}
	internal static class BiomeResolutionSupport
	{
		private static readonly Dictionary<string, Biome> VanillaBiomeLookup = BuildVanillaBiomeLookup();

		private static readonly Type? ExpandWorldDataDataManagerType = Type.GetType("ExpandWorldData.DataManager, ExpandWorldData");

		private static readonly PropertyInfo? ExpandWorldDataIsReadyProperty = ExpandWorldDataDataManagerType?.GetProperty("IsReady", BindingFlags.Static | BindingFlags.Public);

		private static readonly MethodInfo? ExpandWorldDataTryGetBiomeMethod = Type.GetType("ExpandWorldData.BiomeManager, ExpandWorldData")?.GetMethod("TryGetBiome", BindingFlags.Static | BindingFlags.Public, null, new Type[2]
		{
			typeof(string),
			typeof(Biome).MakeByRefType()
		}, null);

		internal static bool TryResolveBiomeToken(string? configuredBiome, out Biome biome)
		{
			string text = (configuredBiome ?? "").Trim();
			if (text.Length == 0)
			{
				biome = (Biome)0;
				return false;
			}
			if (string.Equals(text, "All", StringComparison.OrdinalIgnoreCase))
			{
				biome = (Biome)895;
				return true;
			}
			if (int.TryParse(text, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result) && result != 0)
			{
				biome = (Biome)result;
				return true;
			}
			if (Enum.TryParse<Biome>(text, ignoreCase: true, out biome))
			{
				return true;
			}
			if (VanillaBiomeLookup.TryGetValue(NormalizeBiomeToken(text), out biome))
			{
				return true;
			}
			if (TryResolveExpandWorldDataBiome(text, out biome))
			{
				return true;
			}
			biome = (Biome)0;
			return false;
		}

		internal static bool TryResolveBiomeMask(IEnumerable<string>? configuredBiomes, out Biome biomeMask)
		{
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Invalid comparison between Unknown and I4
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Expected I4, but got Unknown
			biomeMask = (Biome)0;
			bool result = false;
			foreach (string item in configuredBiomes ?? Array.Empty<string>())
			{
				string text = (item ?? "").Trim();
				if (text.Length != 0)
				{
					result = true;
					if (!TryResolveBiomeToken(text, out var biome))
					{
						biomeMask = (Biome)0;
						return false;
					}
					if ((int)biome == 895)
					{
						biomeMask = (Biome)895;
						return true;
					}
					biomeMask |= biome;
				}
			}
			return result;
		}

		private static bool IsExpandWorldDataPresent()
		{
			if (!(ExpandWorldDataTryGetBiomeMethod != null))
			{
				return ExpandWorldDataIsReadyProperty != null;
			}
			return true;
		}

		private static bool IsExpandWorldDataReadyOrUnavailable()
		{
			if (ExpandWorldDataIsReadyProperty == null)
			{
				return true;
			}
			try
			{
				return (ExpandWorldDataIsReadyProperty.GetValue(null) as bool?) ?? true;
			}
			catch
			{
				return true;
			}
		}

		internal static bool ShouldWaitForExpandWorldDataBiomeResolution(IEnumerable<string>? configuredBiomes)
		{
			if (!HasConfiguredBiomeTokens(configuredBiomes))
			{
				return false;
			}
			if (TryResolveBiomeMask(configuredBiomes, out var _) || !IsExpandWorldDataPresent() || IsExpandWorldDataReadyOrUnavailable())
			{
				return false;
			}
			return true;
		}

		private static bool HasConfiguredBiomeTokens(IEnumerable<string>? configuredBiomes)
		{
			foreach (string item in configuredBiomes ?? Array.Empty<string>())
			{
				if (!string.IsNullOrWhiteSpace(item))
				{
					return true;
				}
			}
			return false;
		}

		private static string NormalizeBiomeToken(string? value)
		{
			StringBuilder stringBuilder = new StringBuilder();
			string text = (value ?? "").Trim();
			foreach (char c in text)
			{
				if (char.IsLetterOrDigit(c))
				{
					stringBuilder.Append(char.ToLowerInvariant(c));
				}
			}
			return stringBuilder.ToString();
		}

		private static Dictionary<string, Biome> BuildVanillaBiomeLookup()
		{
			//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_0041: Unknown result type (might be due to invalid IL or missing references)
			Dictionary<string, Biome> dictionary = new Dictionary<string, Biome>(StringComparer.OrdinalIgnoreCase);
			foreach (Biome value in Enum.GetValues(typeof(Biome)))
			{
				dictionary[NormalizeBiomeToken(((object)value/*cast due to .constrained prefix*/).ToString())] = value;
			}
			dictionary["ashlands"] = (Biome)32;
			return dictionary;
		}

		private static bool TryResolveExpandWorldDataBiome(string configuredBiome, out Biome biome)
		{
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Expected I4, but got Unknown
			if (ExpandWorldDataTryGetBiomeMethod == null)
			{
				biome = (Biome)0;
				return false;
			}
			object[] array = new object[2]
			{
				configuredBiome,
				(object)(Biome)0
			};
			object obj = ExpandWorldDataTryGetBiomeMethod.Invoke(null, array);
			bool flag = default(bool);
			int num;
			if (obj is bool)
			{
				flag = (bool)obj;
				num = 1;
			}
			else
			{
				num = 0;
			}
			if (((uint)num & (flag ? 1u : 0u)) != 0 && array[1] is Biome val)
			{
				biome = (Biome)(int)val;
				return true;
			}
			biome = (Biome)0;
			return false;
		}
	}
}
namespace ServerSync
{
	[PublicAPI]
	internal abstract class OwnConfigEntryBase
	{
		public object? LocalBaseValue;

		public bool SynchronizedConfig = true;

		public abstract ConfigEntryBase BaseConfig { get; }
	}
	[PublicAPI]
	internal class SyncedConfigEntry<T>(ConfigEntry<T> sourceConfig) : OwnConfigEntryBase()
	{
		public readonly ConfigEntry<T> SourceConfig = sourceConfig;

		public override ConfigEntryBase BaseConfig => (ConfigEntryBase)(object)SourceConfig;

		public T Value
		{
			get
			{
				return SourceConfig.Value;
			}
			set
			{
				SourceConfig.Value = value;
			}
		}

		public void AssignLocalValue(T value)
		{
			if (LocalBaseValue == null)
			{
				Value = value;
			}
			else
			{
				LocalBaseValue = value;
			}
		}
	}
	internal abstract class CustomSyncedValueBase
	{
		public object? LocalBaseValue;

		public readonly string Identifier;

		public readonly Type Type;

		private object? boxedValue;

		protected bool localIsOwner;

		public readonly int Priority;

		public object? BoxedValue
		{
			get
			{
				return boxedValue;
			}
			set
			{
				boxedValue = value;
				this.ValueChanged?.Invoke();
			}
		}

		public event Action? ValueChanged;

		protected CustomSyncedValueBase(ConfigSync configSync, string identifier, Type type, int priority)
		{
			Priority = priority;
			Identifier = identifier;
			Type = type;
			configSync.AddCustomValue(this);
			localIsOwner = configSync.IsSourceOfTruth;
			configSync.SourceOfTruthChanged += delegate(bool truth)
			{
				localIsOwner = truth;
			};
		}
	}
	[PublicAPI]
	internal sealed class CustomSyncedValue<T> : CustomSyncedValueBase
	{
		public T Value
		{
			get
			{
				return (T)base.BoxedValue;
			}
			set
			{
				base.BoxedValue = value;
			}
		}

		public CustomSyncedValue(ConfigSync configSync, string identifier, T value = default(T), int priority = 0)
			: base(configSync, identifier, typeof(T), priority)
		{
			Value = value;
		}

		public void AssignLocalValue(T value)
		{
			if (localIsOwner)
			{
				Value = value;
			}
			else
			{
				LocalBaseValue = value;
			}
		}
	}
	internal class ConfigurationManagerAttributes
	{
		[UsedImplicitly]
		public bool? ReadOnly = false;
	}
	[PublicAPI]
	internal class ConfigSync
	{
		[HarmonyPatch(typeof(ZRpc), "HandlePackage")]
		private static class SnatchCurrentlyHandlingRPC
		{
			public static ZRpc? currentRpc;

			[HarmonyPrefix]
			private static void Prefix(ZRpc __instance)
			{
				currentRpc = __instance;
			}
		}

		[HarmonyPatch(typeof(ZNet), "Awake")]
		internal static class RegisterRPCPatch
		{
			[HarmonyPostfix]
			private static void Postfix(ZNet __instance)
			{
				isServer = __instance.IsServer();
				foreach (ConfigSync configSync2 in configSyncs)
				{
					ZRoutedRpc.instance.Register<ZPackage>(configSync2.Name + " ConfigSync", (Action<long, ZPackage>)configSync2.RPC_FromOtherClientConfigSync);
					if (isServer)
					{
						configSync2.InitialSyncDone = true;
						Debug.Log((object)("Registered '" + configSync2.Name + " ConfigSync' RPC - waiting for incoming connections"));
					}
				}
				if (isServer)
				{
					((MonoBehaviour)__instance).StartCoroutine(WatchAdminListChanges());
				}
				static void SendAdmin(List<ZNetPeer> peers, bool isAdmin)
				{
					ZPackage package = ConfigsToPackage(null, null, new PackageEntry[1]
					{
						new PackageEntry
						{
							section = "Internal",
							key = "lockexempt",
							type = typeof(bool),
							value = isAdmin
						}
					});
					ConfigSync configSync = configSyncs.First();
					if (configSync != null)
					{
						((MonoBehaviour)ZNet.instance).StartCoroutine(configSync.sendZPackage(peers, package));
					}
				}
				static IEnumerator WatchAdminListChanges()
				{
					MethodInfo listContainsId = AccessTools.DeclaredMethod(typeof(ZNet), "ListContainsId", (Type[])null, (Type[])null);
					SyncedList adminList = (SyncedList)AccessTools.DeclaredField(typeof(ZNet), "m_adminList").GetValue(ZNet.instance);
					List<string> CurrentList = new List<string>(adminList.GetList());
					while (true)
					{
						yield return (object)new WaitForSeconds(30f);
						if (!adminList.GetList().SequenceEqual(CurrentList))
						{
							CurrentList = new List<string>(adminList.GetList());
							List<ZNetPeer> adminPeer = ZNet.instance.GetPeers().Where(delegate(ZNetPeer p)
							{
								string hostName = p.m_rpc.GetSocket().GetHostName();
								return ((object)listContainsId == null) ? adminList.Contains(hostName) : ((bool)listContainsId.Invoke(ZNet.instance, new object[2] { adminList, hostName }));
							}).ToList();
							List<ZNetPeer> nonAdminPeer = ZNet.instance.GetPeers().Except(adminPeer).ToList();
							SendAdmin(nonAdminPeer, isAdmin: false);
							SendAdmin(adminPeer, isAdmin: true);
						}
					}
				}
			}
		}

		[HarmonyPatch(typeof(ZNet), "OnNewConnection")]
		private static class RegisterClientRPCPatch
		{
			[HarmonyPostfix]
			private static void Postfix(ZNet __instance, ZNetPeer peer)
			{
				if (__instance.IsServer())
				{
					return;
				}
				foreach (ConfigSync configSync in configSyncs)
				{
					peer.m_rpc.Register<ZPackage>(configSync.Name + " ConfigSync", (Action<ZRpc, ZPackage>)configSync.RPC_FromServerConfigSync);
				}
			}
		}

		private class ParsedConfigs
		{
			public readonly Dictionary<OwnConfigEntryBase, object?> configValues = new Dictionary<OwnConfigEntryBase, object>();

			public readonly Dictionary<CustomSyncedValueBase, object?> customValues = new Dictionary<CustomSyncedValueBase, object>();
		}

		[HarmonyPatch(typeof(ZNet), "Shutdown")]
		private class ResetConfigsOnShutdown
		{
			[HarmonyPostfix]
			private static void Postfix()
			{
				ProcessingServerUpdate = true;
				foreach (ConfigSync configSync in configSyncs)
				{
					configSync.resetConfigsFromServer();
					configSync.IsSourceOfTruth = true;
					configSync.InitialSyncDone = false;
				}
				ProcessingServerUpdate = false;
			}
		}

		[HarmonyPatch(typeof(ZNet), "RPC_PeerInfo")]
		private class SendConfigsAfterLogin
		{
			private class BufferingSocket : ZPlayFabSocket, ISocket
			{
				public volatile bool finished = false;

				public volatile int versionMatchQueued = -1;

				public readonly List<ZPackage> Package = new List<ZPackage>();

				public readonly ISocket Original;

				public BufferingSocket(ISocket original)
				{
					Original = original;
					((ZPlayFabSocket)this)..ctor();
				}

				public bool IsConnected()
				{
					return Original.IsConnected();
				}

				public ZPackage Recv()
				{
					return Original.Recv();
				}

				public int GetSendQueueSize()
				{
					return Original.GetSendQueueSize();
				}

				public int GetCurrentSendRate()
				{
					return Original.GetCurrentSendR