Decompiled source of DroppedAndLost v0.2.2

plugins/DroppedAndLost/DroppedAndLost.Core.dll

Decompiled 2 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Text.RegularExpressions;
using Microsoft.CodeAnalysis;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")]
[assembly: AssemblyCompany("Laoch")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright (c) Laoch")]
[assembly: AssemblyFileVersion("0.1.0.0")]
[assembly: AssemblyInformationalVersion("0.1.0+b6db23ff7b7805b7253c14474649d02fb69c35ec")]
[assembly: AssemblyProduct("DroppedAndLost")]
[assembly: AssemblyTitle("DroppedAndLost.Core")]
[assembly: AssemblyVersion("0.1.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace DroppedAndLost.Core
{
	public static class AccountId
	{
		private static readonly Regex Digits = new Regex("\\d{6,}", RegexOptions.Compiled);

		public static string Normalize(string raw)
		{
			if (string.IsNullOrEmpty(raw))
			{
				return "";
			}
			Match match = Digits.Match(raw);
			if (!match.Success)
			{
				return raw.Trim().ToLowerInvariant();
			}
			return match.Value;
		}
	}
	public struct PlayerSample
	{
		public long Id;

		public float X;

		public float Y;

		public float Z;

		public bool Alive;

		public bool Connected;
	}
	public struct BossDecision
	{
		public bool Allowed;

		public int Present;

		public int Needed;

		public string Message;
	}
	public static class BossGate
	{
		public static int CountPresent(DalSettings s, float altarX, float altarY, float altarZ, IList<PlayerSample> players, List<long> presentIds = null)
		{
			float num = s.BossPresenceRadius * s.BossPresenceRadius;
			int num2 = 0;
			for (int i = 0; i < players.Count; i++)
			{
				PlayerSample playerSample = players[i];
				if (playerSample.Connected && playerSample.Alive)
				{
					float num3 = playerSample.X - altarX;
					float num4 = playerSample.Y - altarY;
					float num5 = playerSample.Z - altarZ;
					if (!(num3 * num3 + num4 * num4 + num5 * num5 > num))
					{
						num2++;
						presentIds?.Add(playerSample.Id);
					}
				}
			}
			return num2;
		}

		public static BossDecision Evaluate(DalSettings s, float altarX, float altarY, float altarZ, long summonerId, IList<PlayerSample> players)
		{
			int minPlayersForBoss = s.MinPlayersForBoss;
			if (minPlayersForBoss <= 1)
			{
				return new BossDecision
				{
					Allowed = true,
					Present = 1,
					Needed = minPlayersForBoss
				};
			}
			float num = s.BossPresenceRadius * s.BossPresenceRadius;
			int num2 = 0;
			bool flag = false;
			for (int i = 0; i < players.Count; i++)
			{
				PlayerSample playerSample = players[i];
				if (!playerSample.Connected || !playerSample.Alive)
				{
					continue;
				}
				float num3 = playerSample.X - altarX;
				float num4 = playerSample.Y - altarY;
				float num5 = playerSample.Z - altarZ;
				if (!(num3 * num3 + num4 * num4 + num5 * num5 > num))
				{
					num2++;
					if (playerSample.Id == summonerId)
					{
						flag = true;
					}
				}
			}
			bool flag2 = num2 >= minPlayersForBoss && flag;
			return new BossDecision
			{
				Allowed = flag2,
				Present = num2,
				Needed = minPlayersForBoss,
				Message = (flag2 ? null : ("The altar stays silent. You need " + minPlayersForBoss + " players nearby."))
			};
		}
	}
	public struct CompassTick
	{
		public float Offset;

		public string Label;

		public bool Major;
	}
	public static class CompassMath
	{
		private static readonly string[] Names = new string[8] { "N", "NE", "E", "SE", "S", "SW", "W", "NW" };

		public static float Normalize(float deg)
		{
			deg %= 360f;
			if (!(deg < 0f))
			{
				return deg;
			}
			return deg + 360f;
		}

		public static float Delta(float from, float target)
		{
			float num = Normalize(target - from);
			if (!(num > 180f))
			{
				return num;
			}
			return num - 360f;
		}

		public static string Cardinal(float deg)
		{
			int num = (int)Math.Round(Normalize(deg) / 45f) % 8;
			return Names[num];
		}

		public static List<CompassTick> Ticks(float heading, float halfSpan, float step)
		{
			List<CompassTick> list = new List<CompassTick>();
			for (float num = (float)Math.Ceiling((heading - halfSpan) / step) * step; num <= heading + halfSpan + 0.001f; num += step)
			{
				float num2 = Normalize(num);
				bool flag = Math.Abs(num2 % 45f) < 0.001f || Math.Abs(num2 % 45f - 45f) < 0.001f;
				list.Add(new CompassTick
				{
					Offset = num - heading,
					Label = (flag ? Cardinal(num2) : ""),
					Major = flag
				});
			}
			return list;
		}
	}
	public static class DalIds
	{
		public const string Guid = "com.laoch.droppedandlost";

		public const string Name = "Dropped and Lost";

		public const string Version = "0.2.2";

		public const int ProtocolVersion = 1;
	}
	public sealed class DalRandom
	{
		private ulong _state;

		public DalRandom(ulong seed)
		{
			_state = seed;
		}

		public static ulong Seed(ulong a, ulong b, ulong c)
		{
			DalRandom dalRandom = new DalRandom((ulong)(((long)a * -7046029254386353131L) ^ -3335678366873096957L));
			dalRandom._state ^= b;
			dalRandom.NextU64();
			dalRandom._state ^= c;
			dalRandom.NextU64();
			return dalRandom.NextU64();
		}

		public static ulong HashString(string s)
		{
			ulong num = 14695981039346656037uL;
			string text = s ?? "";
			foreach (char c in text)
			{
				num ^= c;
				num *= 1099511628211L;
			}
			return num;
		}

		public ulong NextU64()
		{
			_state += 11400714819323198485uL;
			ulong state = _state;
			long num = (long)(state ^ (state >> 30)) * -4658895280553007687L;
			long num2 = (num ^ (num >>> 27)) * -7723592293110705685L;
			return (ulong)(num2 ^ (num2 >>> 31));
		}

		public double NextDouble()
		{
			return (double)(NextU64() >> 11) * 1.1102230246251565E-16;
		}

		public int NextInt(int exclusiveMax)
		{
			return (int)(NextU64() % (ulong)exclusiveMax);
		}
	}
	public enum RespawnMode
	{
		FirstSpawn,
		Stones,
		RandomEachDeath
	}
	public sealed class DalSettings
	{
		public bool SpawnEnabled = true;

		public float SpawnMinDistance = 3000f;

		public float SpawnMaxDistance = 6000f;

		public float NearbyBiomeRadius = 150f;

		public int MinNearbyBiomes = 1;

		public int MaxSearchAttempts = 500;

		public float MaxSlopeDegrees = 20f;

		public float MinHeightAboveWater = 2f;

		public float MinDistanceFromLocations = 75f;

		public float MinDistanceFromOtherSpawns = 500f;

		public float MinMeadowsRadius = 120f;

		public float MinMeadowsFraction = 0.8f;

		public RespawnMode RespawnMode;

		public int RespawnRerollAfterDeaths;

		public bool NoMap = true;

		public bool DisableCartographyTable = true;

		public int MinPlayersForBoss = 2;

		public float BossPresenceRadius = 50f;

		public bool DisableShout = true;

		public bool BlockPings = true;

		public bool ShowJoinMessage = true;

		public bool AnnouncePresence = true;

		public string ArrivedText = "{0} has arrived.";

		public string LeftText = "{0} has left.";

		public string JoinMessage = "You are lost. Find friends. Bosses need company.";

		public bool LimitPortals = true;

		public int MaxPortalsPerPlayer = 2;

		public bool LockPortalNames = true;

		public bool ProtectFromOthers = true;

		public bool TombstoneHint = true;

		public string ForcedKeys = "";

		public bool EnforceMods;

		public float ModGraceSeconds = 20f;

		public bool DebugLogging;

		public DalSettings Clone()
		{
			return (DalSettings)MemberwiseClone();
		}

		public DalSettings Validated(List<string> warnings)
		{
			DalSettings dalSettings = new DalSettings();
			DalSettings dalSettings2 = Clone();
			if (!(dalSettings2.SpawnMinDistance >= 0f) || !(dalSettings2.SpawnMaxDistance > dalSettings2.SpawnMinDistance) || float.IsInfinity(dalSettings2.SpawnMaxDistance))
			{
				Warn("Spawn.MinDistance (" + dalSettings2.SpawnMinDistance + ") must be >= 0 and lower than Spawn.MaxDistance (" + dalSettings2.SpawnMaxDistance + "); both reset to defaults.");
				dalSettings2.SpawnMinDistance = dalSettings.SpawnMinDistance;
				dalSettings2.SpawnMaxDistance = dalSettings.SpawnMaxDistance;
			}
			if (!(dalSettings2.NearbyBiomeRadius >= 0f))
			{
				Warn("Spawn.NearbyBiomeRadius reset.");
				dalSettings2.NearbyBiomeRadius = dalSettings.NearbyBiomeRadius;
			}
			if (dalSettings2.MinNearbyBiomes < 0 || dalSettings2.MinNearbyBiomes > 8)
			{
				Warn("Spawn.MinNearbyBiomes reset.");
				dalSettings2.MinNearbyBiomes = dalSettings.MinNearbyBiomes;
			}
			if (dalSettings2.MaxSearchAttempts < 1)
			{
				Warn("Spawn.MaxSearchAttempts reset.");
				dalSettings2.MaxSearchAttempts = dalSettings.MaxSearchAttempts;
			}
			if (!(dalSettings2.MaxSlopeDegrees > 0f) || !(dalSettings2.MaxSlopeDegrees <= 90f))
			{
				Warn("Spawn.MaxSlopeDegrees reset.");
				dalSettings2.MaxSlopeDegrees = dalSettings.MaxSlopeDegrees;
			}
			if (!(dalSettings2.MinHeightAboveWater >= 0f))
			{
				Warn("Spawn.MinHeightAboveWater reset.");
				dalSettings2.MinHeightAboveWater = dalSettings.MinHeightAboveWater;
			}
			if (!(dalSettings2.MinMeadowsRadius >= 0f) || !(dalSettings2.MinMeadowsRadius <= 1000f))
			{
				Warn("Spawn.MinMeadowsRadius must be 0-1000; reset.");
				dalSettings2.MinMeadowsRadius = dalSettings.MinMeadowsRadius;
			}
			if (!(dalSettings2.MinMeadowsFraction >= 0f) || !(dalSettings2.MinMeadowsFraction <= 1f))
			{
				Warn("Spawn.MinMeadowsFraction must be 0-1; reset.");
				dalSettings2.MinMeadowsFraction = dalSettings.MinMeadowsFraction;
			}
			if (!(dalSettings2.MinDistanceFromLocations >= 0f))
			{
				Warn("Spawn.MinDistanceFromLocations reset.");
				dalSettings2.MinDistanceFromLocations = dalSettings.MinDistanceFromLocations;
			}
			if (!(dalSettings2.MinDistanceFromOtherSpawns >= 0f) || float.IsInfinity(dalSettings2.MinDistanceFromOtherSpawns))
			{
				Warn("Spawn.MinDistanceFromOtherSpawns reset.");
				dalSettings2.MinDistanceFromOtherSpawns = dalSettings.MinDistanceFromOtherSpawns;
			}
			if (dalSettings2.RespawnRerollAfterDeaths < 0)
			{
				Warn("Respawn.RerollAfterDeaths reset.");
				dalSettings2.RespawnRerollAfterDeaths = dalSettings.RespawnRerollAfterDeaths;
			}
			if (dalSettings2.MinPlayersForBoss < 1)
			{
				Warn("Boss.MinPlayersForBoss must be >= 1; reset.");
				dalSettings2.MinPlayersForBoss = dalSettings.MinPlayersForBoss;
			}
			if (!(dalSettings2.BossPresenceRadius > 0f))
			{
				Warn("Boss.PresenceRadius reset.");
				dalSettings2.BossPresenceRadius = dalSettings.BossPresenceRadius;
			}
			if (dalSettings2.MaxPortalsPerPlayer < 1)
			{
				Warn("Portals.MaxPortalsPerPlayer must be >= 1; reset.");
				dalSettings2.MaxPortalsPerPlayer = dalSettings.MaxPortalsPerPlayer;
			}
			if (!(dalSettings2.ModGraceSeconds >= 5f) || !(dalSettings2.ModGraceSeconds <= 600f))
			{
				Warn("Mods.GraceSeconds must be 5-600; reset.");
				dalSettings2.ModGraceSeconds = dalSettings.ModGraceSeconds;
			}
			if (dalSettings2.JoinMessage == null)
			{
				dalSettings2.JoinMessage = dalSettings.JoinMessage;
			}
			if (string.IsNullOrWhiteSpace(dalSettings2.ArrivedText) || dalSettings2.ArrivedText.Length > 120)
			{
				dalSettings2.ArrivedText = dalSettings.ArrivedText;
			}
			if (string.IsNullOrWhiteSpace(dalSettings2.LeftText) || dalSettings2.LeftText.Length > 120)
			{
				dalSettings2.LeftText = dalSettings.LeftText;
			}
			return dalSettings2;
			void Warn(string m)
			{
				if (warnings != null)
				{
					warnings.Add(m);
				}
			}
		}
	}
	public static class ForcedKeys
	{
		public const int MaxEntries = 32;

		public static List<string> Parse(string raw, List<string> warnings)
		{
			List<string> list = new List<string>();
			HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
			if (string.IsNullOrWhiteSpace(raw))
			{
				return list;
			}
			string[] array = raw.Split(new char[4] { ';', ',', '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries);
			foreach (string text in array)
			{
				string text2 = string.Join(" ", text.Trim().Split(new char[2] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries)).ToLowerInvariant();
				if (text2.Length == 0)
				{
					continue;
				}
				bool flag = false;
				string text3 = text2;
				for (int j = 0; j < text3.Length; j++)
				{
					if (char.IsControl(text3[j]))
					{
						flag = true;
						break;
					}
				}
				if (flag)
				{
					warnings?.Add("World.ForcedKeys: ignored an entry with control characters.");
					continue;
				}
				string text4 = KeyName(text2);
				if (!hashSet.Add(text4))
				{
					warnings?.Add("World.ForcedKeys: '" + text4 + "' listed twice; the first one is used.");
					continue;
				}
				if (list.Count >= 32)
				{
					warnings?.Add("World.ForcedKeys: only the first " + 32 + " entries are used.");
					break;
				}
				list.Add(text2);
			}
			return list;
		}

		public static string KeyName(string entry)
		{
			int num = entry.IndexOf(' ');
			if (num <= 0)
			{
				return entry;
			}
			return entry.Substring(0, num);
		}

		public static List<string> Merge(IEnumerable<string> worldKeys, IList<string> forced)
		{
			HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
			foreach (string item in forced)
			{
				hashSet.Add(KeyName(item));
			}
			List<string> list = new List<string>();
			foreach (string worldKey in worldKeys)
			{
				if (!hashSet.Contains(KeyName((worldKey ?? "").ToLowerInvariant())))
				{
					list.Add(worldKey);
				}
			}
			list.AddRange(forced);
			return list;
		}
	}
	public static class GraveHint
	{
		private static readonly string[] Words = new string[8] { "north", "north-east", "east", "south-east", "south", "south-west", "west", "north-west" };

		public static float BearingDegrees(float fromX, float fromZ, float toX, float toZ)
		{
			return CompassMath.Normalize((float)(Math.Atan2(toX - fromX, toZ - fromZ) * 180.0 / Math.PI));
		}

		public static string Distance(float metres)
		{
			if (metres < 100f)
			{
				return (int)(Math.Round(metres / 10f) * 10.0) + " m";
			}
			if (metres < 1000f)
			{
				return (int)(Math.Round(metres / 50f) * 50.0) + " m";
			}
			return (Math.Round(metres / 100f) / 10.0).ToString("0.0", CultureInfo.InvariantCulture) + " km";
		}

		public static string Describe(float fromX, float fromZ, float toX, float toZ)
		{
			float num = toX - fromX;
			float num2 = toZ - fromZ;
			float num3 = (float)Math.Sqrt(num * num + num2 * num2);
			if (num3 < 15f)
			{
				return "Your grave is right here.";
			}
			int num4 = (int)Math.Round(BearingDegrees(fromX, fromZ, toX, toZ) / 45f) % 8;
			return "Your grave lies about " + Distance(num3) + " to the " + Words[num4] + ".";
		}
	}
	public struct ReportedMod
	{
		public string Guid;

		public string Version;

		public string Hash;
	}
	public sealed class ModVerdict
	{
		public readonly List<string> Extra = new List<string>();

		public readonly List<string> WrongVersion = new List<string>();

		public readonly List<string> WrongHash = new List<string>();

		public readonly List<string> Missing = new List<string>();

		public bool Ok
		{
			get
			{
				if (Extra.Count == 0 && WrongVersion.Count == 0 && WrongHash.Count == 0)
				{
					return Missing.Count == 0;
				}
				return false;
			}
		}

		public string Describe()
		{
			StringBuilder stringBuilder = new StringBuilder();
			Part(stringBuilder, "not allowed", Extra);
			Part(stringBuilder, "wrong version", WrongVersion);
			Part(stringBuilder, "modified file", WrongHash);
			Part(stringBuilder, "missing", Missing);
			if (stringBuilder.Length != 0)
			{
				return stringBuilder.ToString();
			}
			return "ok";
		}

		private static void Part(StringBuilder sb, string label, List<string> items)
		{
			if (items.Count == 0)
			{
				return;
			}
			if (sb.Length > 0)
			{
				sb.Append("; ");
			}
			sb.Append(label).Append(": ");
			for (int i = 0; i < items.Count && i < 4; i++)
			{
				if (i > 0)
				{
					sb.Append(", ");
				}
				sb.Append(items[i]);
			}
			if (items.Count > 4)
			{
				sb.Append(" (+").Append(items.Count - 4).Append(" more)");
			}
		}
	}
	public sealed class ModPolicy
	{
		private sealed class Rule
		{
			public string Version;

			public string Hash;

			public bool Required;
		}

		private readonly Dictionary<string, Rule> _rules = new Dictionary<string, Rule>(StringComparer.OrdinalIgnoreCase);

		public int Count => _rules.Count;

		public static ModPolicy Parse(string text, IEnumerable<string> alwaysAllowed, IEnumerable<string> alwaysRequired, List<string> warnings)
		{
			ModPolicy modPolicy = new ModPolicy();
			if (alwaysAllowed != null)
			{
				foreach (string item in alwaysAllowed)
				{
					modPolicy._rules[item] = new Rule();
				}
			}
			if (alwaysRequired != null)
			{
				foreach (string item2 in alwaysRequired)
				{
					modPolicy._rules[item2] = new Rule
					{
						Required = true
					};
				}
			}
			if (string.IsNullOrEmpty(text))
			{
				return modPolicy;
			}
			int num = 0;
			string[] array = text.Split(new char[1] { '\n' });
			foreach (string obj in array)
			{
				num++;
				string text2 = obj.Trim();
				int num2 = text2.IndexOf('#');
				if (num2 >= 0)
				{
					text2 = text2.Substring(0, num2).Trim();
				}
				if (text2.Length == 0)
				{
					continue;
				}
				bool flag = false;
				if (text2[0] == '+')
				{
					flag = true;
					text2 = text2.Substring(1).Trim();
				}
				string[] array2 = text2.Split(new char[1] { '|' });
				string text3 = array2[0].Trim();
				if (text3.Length == 0)
				{
					warnings?.Add("Mod list line " + num + " has no mod id; skipped.");
					continue;
				}
				string text4 = ((array2.Length > 1) ? array2[1].Trim() : "");
				string text5 = ((array2.Length > 2) ? array2[2].Trim().ToLowerInvariant() : "");
				if (array2.Length > 3)
				{
					warnings?.Add("Mod list line " + num + " has extra fields; ignored.");
				}
				Rule value;
				bool required = flag || (modPolicy._rules.TryGetValue(text3, out value) && value.Required);
				modPolicy._rules[text3] = new Rule
				{
					Version = ((text4.Length == 0 || text4 == "*") ? null : text4),
					Hash = ((text5.Length == 0) ? null : text5),
					Required = required
				};
			}
			return modPolicy;
		}

		public ModVerdict Evaluate(IEnumerable<ReportedMod> reported)
		{
			ModVerdict modVerdict = new ModVerdict();
			HashSet<string> hashSet = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
			foreach (ReportedMod item in reported)
			{
				string text = item.Guid ?? "";
				hashSet.Add(text);
				if (!_rules.TryGetValue(text, out var value))
				{
					modVerdict.Extra.Add(text);
					continue;
				}
				if (value.Version != null && !string.Equals(value.Version, item.Version, StringComparison.OrdinalIgnoreCase))
				{
					modVerdict.WrongVersion.Add(text + " " + item.Version + " (need " + value.Version + ")");
				}
				if (value.Hash != null && !string.Equals(value.Hash, item.Hash, StringComparison.OrdinalIgnoreCase))
				{
					modVerdict.WrongHash.Add(text);
				}
			}
			foreach (KeyValuePair<string, Rule> rule in _rules)
			{
				if (rule.Value.Required && !hashSet.Contains(rule.Key))
				{
					modVerdict.Missing.Add(rule.Key);
				}
			}
			return modVerdict;
		}
	}
	public sealed class PortalEntry
	{
		public string PortalId;

		public long OwnerId;

		public string Tag;
	}
	public struct PlacementResult
	{
		public bool Allowed;

		public string Tag;

		public bool Paired;

		public string Message;
	}
	public sealed class PortalLedger
	{
		private static readonly string[] Adjectives = new string[16]
		{
			"Ember", "Cinder", "Frost", "Raven", "Amber", "Iron", "Mist", "Ash", "Storm", "Pale",
			"Dusk", "Gilded", "Hollow", "Silent", "Wild", "Stone"
		};

		private static readonly string[] Nouns = new string[16]
		{
			"Hollow", "Marsh", "Ridge", "Fen", "Glade", "Crag", "Barrow", "Strand", "Moor", "Cairn",
			"Vale", "Tarn", "Reach", "Holt", "Ford", "Weald"
		};

		private readonly Dictionary<string, PortalEntry> _portals = new Dictionary<string, PortalEntry>();

		private readonly HashSet<string> _retired = new HashSet<string>();

		public readonly HashSet<string> Preexisting = new HashSet<string>();

		public bool Baselined;

		public int Count => _portals.Count;

		public IEnumerable<PortalEntry> Entries => _portals.Values;

		public bool TryGet(string portalId, out PortalEntry e)
		{
			return _portals.TryGetValue(portalId, out e);
		}

		public int CountOwned(long ownerId)
		{
			return _portals.Values.Count((PortalEntry p) => p.OwnerId == ownerId);
		}

		public PlacementResult OnPlaced(DalSettings s, long ownerId, string portalId, DalRandom rng)
		{
			if (_portals.TryGetValue(portalId, out var value))
			{
				return new PlacementResult
				{
					Allowed = true,
					Tag = value.Tag,
					Paired = (TagCount(value.Tag) >= 2)
				};
			}
			if (CountOwned(ownerId) >= s.MaxPortalsPerPlayer)
			{
				return new PlacementResult
				{
					Allowed = false,
					Message = "You already have " + s.MaxPortalsPerPlayer + " portals standing."
				};
			}
			string text = null;
			foreach (IGrouping<string, PortalEntry> item in from p in _portals.Values
				where p.OwnerId == ownerId
				group p by p.Tag)
			{
				if (item.Count() == 1)
				{
					text = item.Key;
					break;
				}
			}
			bool paired = text != null;
			if (text == null)
			{
				text = NewTag(rng);
			}
			_portals[portalId] = new PortalEntry
			{
				PortalId = portalId,
				OwnerId = ownerId,
				Tag = text
			};
			return new PlacementResult
			{
				Allowed = true,
				Tag = text,
				Paired = paired
			};
		}

		public void OnRemoved(string portalId)
		{
			if (_portals.TryGetValue(portalId, out var value))
			{
				_portals.Remove(portalId);
				if (TagCount(value.Tag) == 0)
				{
					_retired.Add(value.Tag);
				}
			}
		}

		public bool Replace(string oldId, string newId)
		{
			if (!_portals.TryGetValue(oldId, out var value) || _portals.ContainsKey(newId))
			{
				return false;
			}
			_portals.Remove(oldId);
			value.PortalId = newId;
			_portals[newId] = value;
			return true;
		}

		public int Reconcile(ICollection<string> livePortalIds)
		{
			List<string> list = _portals.Keys.Where((string k) => !livePortalIds.Contains(k)).ToList();
			foreach (string item in list)
			{
				OnRemoved(item);
			}
			return list.Count;
		}

		public bool MayRemove(DalSettings s, string portalId, long actorId)
		{
			if (!s.LimitPortals || !s.ProtectFromOthers)
			{
				return true;
			}
			if (!_portals.TryGetValue(portalId, out var value))
			{
				return true;
			}
			return value.OwnerId == actorId;
		}

		public bool TagMatches(string portalId, string tag)
		{
			if (_portals.TryGetValue(portalId, out var value))
			{
				return string.Equals(value.Tag, tag, StringComparison.Ordinal);
			}
			return false;
		}

		private int TagCount(string tag)
		{
			return _portals.Values.Count((PortalEntry p) => p.Tag == tag);
		}

		private string NewTag(DalRandom rng)
		{
			for (int i = 0; i < 10000; i++)
			{
				string text = Adjectives[rng.NextInt(Adjectives.Length)] + "-" + Nouns[rng.NextInt(Nouns.Length)] + "-" + rng.NextInt(100).ToString(CultureInfo.InvariantCulture);
				if (!_retired.Contains(text) && TagCount(text) == 0)
				{
					return text;
				}
			}
			string text2;
			do
			{
				text2 = "Lost-" + rng.NextU64().ToString("x", CultureInfo.InvariantCulture);
			}
			while (_retired.Contains(text2) || TagCount(text2) != 0);
			return text2;
		}

		public string Serialize()
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append("DALPORTAL 1\n");
			if (Baselined)
			{
				stringBuilder.Append("B\n");
			}
			foreach (string item in Preexisting)
			{
				stringBuilder.Append("X|").Append(item).Append('\n');
			}
			foreach (PortalEntry value in _portals.Values)
			{
				stringBuilder.Append("P|").Append(value.PortalId).Append('|')
					.Append(value.OwnerId.ToString(CultureInfo.InvariantCulture))
					.Append('|')
					.Append(value.Tag)
					.Append('\n');
			}
			foreach (string item2 in _retired)
			{
				stringBuilder.Append("R|").Append(item2).Append('\n');
			}
			return stringBuilder.ToString();
		}

		public static PortalLedger Deserialize(string text, out string error)
		{
			error = null;
			PortalLedger portalLedger = new PortalLedger();
			string[] array = (text ?? "").Split(new char[1] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
			if (array.Length == 0 || array[0].Trim() != "DALPORTAL 1")
			{
				error = "unrecognised portal ledger header";
				return null;
			}
			for (int i = 1; i < array.Length; i++)
			{
				string[] array2 = array[i].Trim().Split(new char[1] { '|' });
				if (array2[0] == "B" && array2.Length == 1)
				{
					portalLedger.Baselined = true;
				}
				else if (array2[0] == "X" && array2.Length == 2)
				{
					portalLedger.Preexisting.Add(array2[1]);
				}
				else if (array2[0] == "P" && array2.Length == 4)
				{
					if (!long.TryParse(array2[2], NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
					{
						error = "bad owner on line " + (i + 1);
						return null;
					}
					portalLedger._portals[array2[1]] = new PortalEntry
					{
						PortalId = array2[1],
						OwnerId = result,
						Tag = array2[3]
					};
				}
				else
				{
					if (!(array2[0] == "R") || array2.Length != 2)
					{
						error = "bad portal ledger line " + (i + 1);
						return null;
					}
					portalLedger._retired.Add(array2[1]);
				}
			}
			return portalLedger;
		}
	}
	public sealed class SpawnRecord
	{
		public string Key;

		public float X;

		public float Z;

		public bool HasPoint;

		public int Rolls;

		public int DeathsNoBed;

		public bool VanillaFallback;

		public bool Spawned;
	}
	public enum RespawnAction
	{
		UseAssigned,
		UseStones,
		RollNew
	}
	public sealed class SpawnLedger
	{
		private readonly Dictionary<string, SpawnRecord> _records = new Dictionary<string, SpawnRecord>();

		public HashSet<string> LegacyAccounts = new HashSet<string>(StringComparer.Ordinal);

		public IEnumerable<SpawnRecord> Records => _records.Values;

		public bool TryGet(string playerId, out SpawnRecord r)
		{
			return _records.TryGetValue(playerId, out r);
		}

		public bool Forget(string playerId)
		{
			return _records.Remove(playerId);
		}

		public SpawnRecord GetOrCreate(string playerId)
		{
			if (!_records.TryGetValue(playerId, out var value))
			{
				value = new SpawnRecord
				{
					Key = playerId
				};
				_records[playerId] = value;
			}
			return value;
		}

		public void Assign(string playerId, SpawnPoint p)
		{
			SpawnRecord orCreate = GetOrCreate(playerId);
			orCreate.X = p.X;
			orCreate.Z = p.Z;
			orCreate.HasPoint = true;
			orCreate.VanillaFallback = false;
			orCreate.Rolls++;
			orCreate.DeathsNoBed = 0;
		}

		public void AssignVanillaFallback(string playerId)
		{
			SpawnRecord orCreate = GetOrCreate(playerId);
			orCreate.HasPoint = false;
			orCreate.VanillaFallback = true;
			orCreate.Rolls++;
			orCreate.DeathsNoBed = 0;
		}

		public RespawnAction OnDeathWithoutBed(string playerId, DalSettings s)
		{
			SpawnRecord orCreate = GetOrCreate(playerId);
			orCreate.DeathsNoBed++;
			switch (s.RespawnMode)
			{
			case RespawnMode.Stones:
				return RespawnAction.UseStones;
			case RespawnMode.RandomEachDeath:
				return RespawnAction.RollNew;
			default:
				if (s.RespawnRerollAfterDeaths > 0 && orCreate.DeathsNoBed >= s.RespawnRerollAfterDeaths)
				{
					return RespawnAction.RollNew;
				}
				if (!orCreate.HasPoint)
				{
					return RespawnAction.UseStones;
				}
				return RespawnAction.UseAssigned;
			}
		}

		public void OnBedRespawn(string playerId)
		{
			if (_records.TryGetValue(playerId, out var value))
			{
				value.DeathsNoBed = 0;
			}
		}

		public string Serialize()
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.Append("DALSPAWN 1\n");
			foreach (string legacyAccount in LegacyAccounts)
			{
				stringBuilder.Append("L|").Append(Uri.EscapeDataString(legacyAccount)).Append('\n');
			}
			foreach (SpawnRecord value in _records.Values)
			{
				stringBuilder.Append("S|").Append(Uri.EscapeDataString(value.Key)).Append('|')
					.Append(value.HasPoint ? 1 : 0)
					.Append('|')
					.Append(value.X.ToString("R", CultureInfo.InvariantCulture))
					.Append('|')
					.Append(value.Z.ToString("R", CultureInfo.InvariantCulture))
					.Append('|')
					.Append(value.Rolls)
					.Append('|')
					.Append(value.DeathsNoBed)
					.Append('|')
					.Append(value.VanillaFallback ? 1 : 0)
					.Append('|')
					.Append(value.Spawned ? 1 : 0)
					.Append('\n');
			}
			return stringBuilder.ToString();
		}

		public static SpawnLedger Deserialize(string text, out string error)
		{
			error = null;
			SpawnLedger spawnLedger = new SpawnLedger();
			string[] array = (text ?? "").Split(new char[1] { '\n' }, StringSplitOptions.RemoveEmptyEntries);
			if (array.Length == 0 || array[0].Trim() != "DALSPAWN 1")
			{
				error = "unrecognised spawn ledger header";
				return null;
			}
			for (int i = 1; i < array.Length; i++)
			{
				string[] array2 = array[i].Trim().Split(new char[1] { '|' });
				if (array2.Length == 2 && array2[0] == "L")
				{
					spawnLedger.LegacyAccounts.Add(Uri.UnescapeDataString(array2[1]));
					continue;
				}
				if (array2.Length != 9 || array2[0] != "S")
				{
					error = "bad spawn ledger line " + (i + 1);
					return null;
				}
				try
				{
					SpawnRecord spawnRecord = new SpawnRecord
					{
						Key = Uri.UnescapeDataString(array2[1]),
						HasPoint = (array2[2] == "1"),
						X = float.Parse(array2[3], CultureInfo.InvariantCulture),
						Z = float.Parse(array2[4], CultureInfo.InvariantCulture),
						Rolls = int.Parse(array2[5], CultureInfo.InvariantCulture),
						DeathsNoBed = int.Parse(array2[6], CultureInfo.InvariantCulture),
						VanillaFallback = (array2[7] == "1"),
						Spawned = (array2[8] == "1")
					};
					spawnLedger._records[spawnRecord.Key] = spawnRecord;
				}
				catch (FormatException)
				{
					error = "bad number on spawn ledger line " + (i + 1);
					return null;
				}
			}
			return spawnLedger;
		}
	}
	public interface ITerrain
	{
		bool IsMeadows(float x, float z);

		float HeightAboveWater(float x, float z);

		float SlopeDegrees(float x, float z);

		int OtherBiomesWithin(float x, float z, float radius);

		float DistanceToNearestLocation(float x, float z);

		float MeadowsFractionWithin(float x, float z, float radius);
	}
	public struct SpawnPoint
	{
		public float X;

		public float Z;

		public SpawnPoint(float x, float z)
		{
			X = x;
			Z = z;
		}
	}
	public struct SearchStage
	{
		public float MinDistance;

		public float MaxDistance;

		public float NearbyRadius;

		public float MeadowsRadius;

		public float Spacing;

		public int MinNearbyBiomes;

		public string Label;
	}
	public enum SearchState
	{
		InProgress,
		Found,
		Exhausted
	}
	public sealed class SpawnSearch
	{
		private readonly DalSettings _s;

		private readonly DalRandom _rng;

		private readonly SpawnPoint _origin;

		private readonly List<SearchStage> _stages;

		private int _stage;

		private int _attemptsInStage;

		private readonly IList<SpawnPoint> _others;

		public SpawnPoint Result { get; private set; }

		public int TotalAttempts { get; private set; }

		public int StageIndex => _stage;

		public IReadOnlyList<SearchStage> Stages => _stages;

		public string StageLabel => _stages[Math.Min(_stage, _stages.Count - 1)].Label;

		public SpawnSearch(DalSettings settings, DalRandom rng, SpawnPoint origin, IList<SpawnPoint> others = null)
		{
			_s = settings;
			_rng = rng;
			_origin = origin;
			_others = ((others != null && others.Count > 0) ? others : null);
			_stages = BuildStages(settings, _others != null);
		}

		public static List<SearchStage> BuildStages(DalSettings s)
		{
			return BuildStages(s, hasOthers: false);
		}

		public static List<SearchStage> BuildStages(DalSettings s, bool hasOthers)
		{
			List<SearchStage> list = new List<SearchStage>();
			float spawnMinDistance = s.SpawnMinDistance;
			float spawnMaxDistance = s.SpawnMaxDistance;
			float num = s.NearbyBiomeRadius;
			float minMeadowsRadius = s.MinMeadowsRadius;
			int num2 = s.MinNearbyBiomes;
			if (hasOthers && s.MinDistanceFromOtherSpawns > 0f)
			{
				list.Add(new SearchStage
				{
					MinDistance = spawnMinDistance,
					MaxDistance = spawnMaxDistance,
					NearbyRadius = num,
					MeadowsRadius = minMeadowsRadius,
					MinNearbyBiomes = num2,
					Spacing = s.MinDistanceFromOtherSpawns,
					Label = "base, spacing " + s.MinDistanceFromOtherSpawns.ToString("F0") + "m"
				});
				list.Add(new SearchStage
				{
					MinDistance = spawnMinDistance,
					MaxDistance = spawnMaxDistance,
					NearbyRadius = num,
					MeadowsRadius = minMeadowsRadius,
					MinNearbyBiomes = num2,
					Spacing = s.MinDistanceFromOtherSpawns * 0.5f,
					Label = "base, spacing " + (s.MinDistanceFromOtherSpawns * 0.5f).ToString("F0") + "m"
				});
			}
			list.Add(new SearchStage
			{
				MinDistance = spawnMinDistance,
				MaxDistance = spawnMaxDistance,
				NearbyRadius = num,
				MeadowsRadius = minMeadowsRadius,
				MinNearbyBiomes = num2,
				Label = "base"
			});
			while (num2 > 0)
			{
				num2--;
				list.Add(new SearchStage
				{
					MinDistance = spawnMinDistance,
					MaxDistance = spawnMaxDistance,
					NearbyRadius = num,
					MeadowsRadius = minMeadowsRadius,
					MinNearbyBiomes = num2,
					Label = "nearby-biomes=" + num2
				});
			}
			if (num2 > 0)
			{
				num *= 2f;
				list.Add(new SearchStage
				{
					MinDistance = spawnMinDistance,
					MaxDistance = spawnMaxDistance,
					NearbyRadius = num,
					MeadowsRadius = minMeadowsRadius,
					MinNearbyBiomes = num2,
					Label = "nearby-radius x2"
				});
			}
			float minDistance = ((spawnMinDistance <= 1000f) ? spawnMinDistance : Math.Max(1000f, spawnMinDistance - 500f));
			list.Add(new SearchStage
			{
				MinDistance = minDistance,
				MaxDistance = spawnMaxDistance + 500f,
				NearbyRadius = num,
				MeadowsRadius = minMeadowsRadius * 0.5f,
				MinNearbyBiomes = num2,
				Label = "band widened 500m, meadows size halved"
			});
			return list;
		}

		public static SpawnPoint Sample(DalRandom rng, SpawnPoint origin, float min, float max)
		{
			double num = rng.NextDouble() * 2.0 * Math.PI;
			double num2 = Math.Sqrt(rng.NextDouble() * ((double)max * (double)max - (double)min * (double)min) + (double)min * (double)min);
			return new SpawnPoint(origin.X + (float)(Math.Cos(num) * num2), origin.Z + (float)(Math.Sin(num) * num2));
		}

		public SearchState Step(ITerrain t, int maxCandidates)
		{
			for (int i = 0; i < maxCandidates; i++)
			{
				if (_stage >= _stages.Count)
				{
					return SearchState.Exhausted;
				}
				if (_attemptsInStage >= _s.MaxSearchAttempts)
				{
					_stage++;
					_attemptsInStage = 0;
					continue;
				}
				SearchStage st = _stages[_stage];
				SpawnPoint spawnPoint = Sample(_rng, _origin, st.MinDistance, st.MaxDistance);
				_attemptsInStage++;
				TotalAttempts++;
				if (Passes(t, spawnPoint, st))
				{
					Result = spawnPoint;
					return SearchState.Found;
				}
			}
			if (_stage < _stages.Count)
			{
				return SearchState.InProgress;
			}
			return SearchState.Exhausted;
		}

		private bool Passes(ITerrain t, SpawnPoint p, SearchStage st)
		{
			if (st.Spacing > 0f && _others != null)
			{
				float num = st.Spacing * st.Spacing;
				for (int i = 0; i < _others.Count; i++)
				{
					float num2 = p.X - _others[i].X;
					float num3 = p.Z - _others[i].Z;
					if (num2 * num2 + num3 * num3 < num)
					{
						return false;
					}
				}
			}
			if (!t.IsMeadows(p.X, p.Z))
			{
				return false;
			}
			if (t.HeightAboveWater(p.X, p.Z) < _s.MinHeightAboveWater)
			{
				return false;
			}
			if (t.SlopeDegrees(p.X, p.Z) > _s.MaxSlopeDegrees)
			{
				return false;
			}
			if (st.MeadowsRadius > 0f && t.MeadowsFractionWithin(p.X, p.Z, st.MeadowsRadius) < _s.MinMeadowsFraction)
			{
				return false;
			}
			if (st.MinNearbyBiomes > 0 && t.OtherBiomesWithin(p.X, p.Z, st.NearbyRadius) < st.MinNearbyBiomes)
			{
				return false;
			}
			if (t.DistanceToNearestLocation(p.X, p.Z) < _s.MinDistanceFromLocations)
			{
				return false;
			}
			return true;
		}

		public SearchState RunToEnd(ITerrain t)
		{
			SearchState searchState;
			do
			{
				searchState = Step(t, 65536);
			}
			while (searchState == SearchState.InProgress);
			return searchState;
		}
	}
}

plugins/DroppedAndLost/DroppedAndLost.dll

Decompiled 2 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using DroppedAndLost.Core;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Splatform;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: InternalsVisibleTo("DroppedAndLost.Harness")]
[assembly: AssemblyCompany("Laoch")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright (c) Laoch")]
[assembly: AssemblyFileVersion("0.1.0.0")]
[assembly: AssemblyInformationalVersion("0.1.0+b6db23ff7b7805b7253c14474649d02fb69c35ec")]
[assembly: AssemblyProduct("DroppedAndLost")]
[assembly: AssemblyTitle("DroppedAndLost")]
[assembly: AssemblyVersion("0.1.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace DroppedAndLost
{
	internal static class BossAltars
	{
		internal sealed class Altar
		{
			public string Location;

			public string Boss;

			public string Item;

			public int Count;
		}

		private static readonly int LocationProxyHash = StringExtensionMethods.GetStableHashCode("LocationProxy");

		private static Dictionary<int, Altar> _byLocation;

		private static HashSet<int> _altarPrefabs;

		private static readonly Dictionary<int, Altar> _roomBosses = new Dictionary<int, Altar>();

		internal static string BuildSummary = "";

		private static List<ZoneLocation> _queue;

		private static int _next;

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

		internal static IReadOnlyDictionary<int, Altar> RoomBosses => _roomBosses;

		internal static bool Ready
		{
			get
			{
				if (_queue != null)
				{
					return _next >= _queue.Count;
				}
				return false;
			}
		}

		internal static IReadOnlyDictionary<int, Altar> Table
		{
			get
			{
				Step(int.MaxValue);
				return _byLocation;
			}
		}

		internal static void Step(int budgetMs)
		{
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_021a: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)ZoneSystem.instance == (Object)null || Ready)
			{
				return;
			}
			if (_queue == null)
			{
				_byLocation = new Dictionary<int, Altar>();
				_altarPrefabs = new HashSet<int>();
				_queue = new List<ZoneLocation>();
				foreach (ZoneLocation location in ZoneSystem.instance.m_locations)
				{
					if (location.m_enable && location.m_quantity > 0)
					{
						_queue.Add(location);
					}
				}
			}
			Stopwatch stopwatch = Stopwatch.StartNew();
			while (_next < _queue.Count && stopwatch.ElapsedMilliseconds < budgetMs)
			{
				ZoneLocation val = _queue[_next++];
				try
				{
					val.m_prefab.Load();
					GameObject asset = val.m_prefab.Asset;
					if ((Object)(object)asset != (Object)null)
					{
						OfferingBowl[] componentsInChildren = asset.GetComponentsInChildren<OfferingBowl>(true);
						foreach (OfferingBowl val2 in componentsInChildren)
						{
							if (!((Object)(object)val2.m_bossPrefab == (Object)null) && !((Object)(object)val2.m_bossPrefab.GetComponent<Character>() == (Object)null))
							{
								_byLocation[StringExtensionMethods.GetStableHashCode(val.m_prefab.Name)] = new Altar
								{
									Location = val.m_prefab.Name,
									Boss = ((Object)val2.m_bossPrefab).name,
									Item = (((Object)(object)val2.m_bossItem != (Object)null) ? ((Object)val2.m_bossItem).name : "(item stands)"),
									Count = val2.m_bossItems
								};
								break;
							}
						}
					}
					val.m_prefab.Release();
				}
				catch (Exception ex)
				{
					Log.Warn("Could not inspect location " + val.m_prefab.Name + ": " + ex.Message);
				}
			}
			if (!Ready)
			{
				return;
			}
			try
			{
				foreach (RoomData room in DungeonDB.GetRooms())
				{
					room.m_prefab.Load();
					GameObject asset2 = room.m_prefab.Asset;
					if ((Object)(object)asset2 != (Object)null)
					{
						OfferingBowl[] componentsInChildren = asset2.GetComponentsInChildren<OfferingBowl>(true);
						foreach (OfferingBowl val3 in componentsInChildren)
						{
							if (!((Object)(object)val3.m_bossPrefab == (Object)null) && !((Object)(object)val3.m_bossPrefab.GetComponent<Character>() == (Object)null))
							{
								_roomBosses[StringExtensionMethods.GetStableHashCode(((Object)val3.m_bossPrefab).name)] = new Altar
								{
									Location = room.m_prefab.Name,
									Boss = ((Object)val3.m_bossPrefab).name,
									Item = (((Object)(object)val3.m_bossItem != (Object)null) ? ((Object)val3.m_bossItem).name : "(item stands)"),
									Count = val3.m_bossItems
								};
							}
						}
					}
					room.m_prefab.Release();
				}
			}
			catch (Exception ex2)
			{
				Log.Warn("Could not inspect dungeon rooms: " + ex2.Message);
			}
			if ((Object)(object)ZNetScene.instance != (Object)null)
			{
				foreach (KeyValuePair<int, GameObject> item in (Dictionary<int, GameObject>)AccessTools.Field(typeof(ZNetScene), "m_namedPrefabs").GetValue(ZNetScene.instance))
				{
					if ((Object)(object)item.Value == (Object)null || (Object)(object)item.Value.GetComponent<ZNetView>() == (Object)null)
					{
						continue;
					}
					OfferingBowl[] componentsInChildren = item.Value.GetComponentsInChildren<OfferingBowl>(true);
					foreach (OfferingBowl val4 in componentsInChildren)
					{
						if ((Object)(object)val4.m_bossPrefab != (Object)null && (Object)(object)val4.m_bossPrefab.GetComponent<Character>() != (Object)null)
						{
							_altarPrefabs.Add(item.Key);
							break;
						}
					}
				}
			}
			BuildSummary = _byLocation.Count + " altar location(s) + " + _altarPrefabs.Count + " altar prefab(s) + " + _roomBosses.Count + " dungeon-room altar(s) from " + _queue.Count + " locations";
			Log.Info("Boss altars: " + BuildSummary);
			foreach (Altar value in _byLocation.Values)
			{
				Log.Debug("  altar " + value.Location + " -> " + value.Boss + " for " + value.Count + " x " + value.Item);
			}
		}

		internal static bool IsAltar(ZDO z)
		{
			if (_byLocation == null || _altarPrefabs == null)
			{
				return false;
			}
			int prefab = z.GetPrefab();
			if (prefab == LocationProxyHash)
			{
				return _byLocation.ContainsKey(z.GetInt(ZDOVars.s_location, 0));
			}
			return _altarPrefabs.Contains(prefab);
		}

		internal unsafe static void Enforce(List<ZDO> zdos)
		{
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0139: Unknown result type (might be due to invalid IL or missing references)
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			DalSettings current = ServerConfig.Current;
			if (current.MinPlayersForBoss <= 1 || zdos == null || zdos.Count == 0)
			{
				return;
			}
			List<PlayerSample> list = null;
			List<long> list2 = new List<long>();
			foreach (ZDO zdo in zdos)
			{
				if (!IsAltar(zdo))
				{
					continue;
				}
				if (list == null)
				{
					list = RpcGate.Samples();
				}
				Vector3 position = zdo.GetPosition();
				list2.Clear();
				int num = BossGate.CountPresent(current, position.x, position.y, position.z, (IList<PlayerSample>)list, list2);
				long owner = zdo.GetOwner();
				if (num >= current.MinPlayersForBoss)
				{
					if (owner == 0L && list2.Count > 0)
					{
						zdo.SetOwner(list2[0]);
						string[] obj = new string[7] { "altar ", null, null, null, null, null, null };
						ZDOID uid = zdo.m_uid;
						obj[1] = ((object)(*(ZDOID*)(&uid))/*cast due to .constrained prefix*/).ToString();
						obj[2] = " owned by ";
						obj[3] = list2[0].ToString();
						obj[4] = " (";
						obj[5] = num.ToString();
						obj[6] = " present)";
						Log.Debug(string.Concat(obj));
					}
				}
				else if (owner != 0L)
				{
					zdo.SetOwner(0L);
					string[] obj2 = new string[7] { "altar ", null, null, null, null, null, null };
					ZDOID uid = zdo.m_uid;
					obj2[1] = ((object)(*(ZDOID*)(&uid))/*cast due to .constrained prefix*/).ToString();
					obj2[2] = " ownership withdrawn (";
					obj2[3] = num.ToString();
					obj2[4] = "/";
					obj2[5] = current.MinPlayersForBoss.ToString();
					obj2[6] = " present)";
					Log.Debug(string.Concat(obj2));
				}
			}
		}

		internal static void Tick()
		{
			//IL_0031: 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_0046: 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_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			if (ServerConfig.Current.MinPlayersForBoss <= 1 || ZDOMan.instance == null || (Object)(object)ZNet.instance == (Object)null)
			{
				return;
			}
			foreach (PlayerSample item in RpcGate.Samples())
			{
				Near.Clear();
				ZDOMan.instance.FindSectorObjects(ZoneSystem.GetZone(new Vector3(item.X, item.Y, item.Z)), new SimulationDistance(1, 0, false), Near, (List<ZDO>)null);
				Enforce(Near);
			}
		}
	}
	[HarmonyPatch(typeof(ZDOMan), "ReleaseNearbyZDOS")]
	internal static class ReleaseNearbyPatch
	{
		private static readonly FieldInfo NearField = AccessTools.Field(typeof(ZDOMan), "m_tempNearObjects");

		private static void Postfix(ZDOMan __instance)
		{
			if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer())
			{
				BossAltars.Enforce((List<ZDO>)NearField.GetValue(__instance));
			}
		}
	}
	internal static class BossBackstop
	{
		private static readonly HashSet<ZDOID> Seen = new HashSet<ZDOID>();

		private static bool _baselined;

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

		private const float SpawnSlack = 45f;

		internal static void Tick()
		{
			//IL_0044: 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_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: 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_0074: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0103: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Expected O, but got Unknown
			//IL_01c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f2: Unknown result type (might be due to invalid IL or missing references)
			DalSettings current = ServerConfig.Current;
			if (current.MinPlayersForBoss <= 1 || !BossAltars.Ready || BossAltars.RoomBosses.Count == 0 || ZDOMan.instance == null)
			{
				return;
			}
			List<PlayerSample> list = RpcGate.Samples();
			HashSet<ZDOID> hashSet = new HashSet<ZDOID>();
			foreach (PlayerSample item in list)
			{
				Near.Clear();
				ZDOMan.instance.FindSectorObjects(ZoneSystem.GetZone(new Vector3(item.X, item.Y, item.Z)), new SimulationDistance(2, 0, false), Near, (List<ZDO>)null);
				foreach (ZDO item2 in Near)
				{
					if (!BossAltars.RoomBosses.TryGetValue(item2.GetPrefab(), out var value))
					{
						continue;
					}
					hashSet.Add(item2.m_uid);
					if (!Seen.Add(item2.m_uid) || !_baselined)
					{
						continue;
					}
					Vector3 position = item2.GetPosition();
					float bossPresenceRadius = current.BossPresenceRadius + 45f;
					int num = BossGate.CountPresent(new DalSettings
					{
						BossPresenceRadius = bossPresenceRadius
					}, position.x, position.y, position.z, (IList<PlayerSample>)list, (List<long>)null);
					if (num >= current.MinPlayersForBoss)
					{
						continue;
					}
					Log.Info("Boss " + value.Boss + " appeared with " + num + "/" + current.MinPlayersForBoss + " players near; removing it and returning " + value.Count + " x " + value.Item + ".");
					ItemRefund.Drop(value.Item, value.Count, position);
					item2.SetOwner(ZDOMan.GetSessionID());
					ZDOMan.instance.DestroyZDO(item2);
					foreach (PlayerSample item3 in list)
					{
						RpcGate.Notify(item3.Id, "The altar stays silent. You need " + current.MinPlayersForBoss + " players nearby.", (MessageType)2);
					}
				}
			}
			_baselined = true;
			Seen.IntersectWith(hashSet);
		}
	}
	internal static class ItemRefund
	{
		internal static void Drop(string itemPrefabName, int amount, Vector3 pos)
		{
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: 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)
			try
			{
				GameObject val = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab(itemPrefabName) : null);
				ItemDrop val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<ItemDrop>() : null);
				if ((Object)(object)val2 == (Object)null)
				{
					Log.Warn("Could not return " + itemPrefabName + ": no such item.");
					return;
				}
				ItemData obj = val2.m_itemData.Clone();
				obj.m_dropPrefab = val;
				ItemDrop.DropItem(obj, amount, pos + Vector3.up, Quaternion.identity);
			}
			catch (Exception ex)
			{
				Log.Error("Could not return " + itemPrefabName + ": " + ex.Message);
			}
		}
	}
	[BepInPlugin("com.laoch.droppedandlost", "Dropped and Lost", "0.2.2")]
	public class DroppedAndLostPlugin : BaseUnityPlugin
	{
		private Harmony _harmony;

		private float _tickTimer;

		internal static DroppedAndLostPlugin Instance { get; private set; }

		private void Awake()
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Expected O, but got Unknown
			Instance = this;
			Log.Init(((BaseUnityPlugin)this).Logger);
			ServerConfig.Bind(((BaseUnityPlugin)this).Config);
			_harmony = new Harmony("com.laoch.droppedandlost");
			_harmony.PatchAll(typeof(DroppedAndLostPlugin).Assembly);
			Log.Info("Dropped and Lost 0.2.2 loaded (server-side rules; inactive on pure clients).");
		}

		private void Update()
		{
			ServerConfig.PollReload();
			if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer() && ServerConfig.Current.MinPlayersForBoss > 1)
			{
				BossAltars.Step(6);
			}
			if (ServerConfig.TakeChanged())
			{
				OnSettingsChanged();
			}
			_tickTimer += Time.unscaledDeltaTime;
			if (!(_tickTimer < 0.5f))
			{
				_tickTimer = 0f;
				if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer())
				{
					SpawnService.Tick();
					PortalRules.Tick();
					JoinMessage.Tick();
					BossAltars.Tick();
					BossBackstop.Tick();
					GraveHints.Tick();
					ModGate.Tick();
					PresenceNotices.Tick();
				}
			}
		}

		private void OnSettingsChanged()
		{
			if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer() && !((Object)(object)ZoneSystem.instance == (Object)null))
			{
				AccessTools.Method(typeof(ZoneSystem), "SendGlobalKeys", (Type[])null, (Type[])null).Invoke(ZoneSystem.instance, new object[1] { 0L });
				AccessTools.Method(typeof(ZoneSystem), "SendLocationIcons", (Type[])null, (Type[])null).Invoke(ZoneSystem.instance, new object[1] { 0L });
				if ((Object)(object)Game.instance != (Object)null && (Object)(object)Minimap.instance != (Object)null && (Object)(object)Player.m_localPlayer != (Object)null)
				{
					Game.UpdateNoMap();
				}
			}
		}

		private void OnDestroy()
		{
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			ServerConfig.Unbind();
		}
	}
	internal sealed class GameTerrain : ITerrain
	{
		private readonly WorldGenerator _wg = WorldGenerator.instance;

		private readonly float _water = ZoneSystem.instance.m_waterLevel;

		private static readonly float[] Cos = new float[8] { 1f, 0.7071f, 0f, -0.7071f, -1f, -0.7071f, 0f, 0.7071f };

		private static readonly float[] Sin = new float[8] { 0f, 0.7071f, 1f, 0.7071f, 0f, -0.7071f, -1f, -0.7071f };

		private static long _cacheUid;

		private static Vector2[] _significant;

		public static bool IsSignificant(LocationInstance inst)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			ZoneLocation location = inst.m_location;
			if (!(location.m_interiorRadius > 0f) && !location.m_iconAlways && !location.m_iconPlaced)
			{
				return location.m_exteriorRadius >= 25f;
			}
			return true;
		}

		private static Vector2[] Significant()
		{
			//IL_004a: 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_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: 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)
			long num = ((ZNet.World != null) ? ZNet.World.m_uid : 0);
			if (_significant != null && _cacheUid == num)
			{
				return _significant;
			}
			List<Vector2> list = new List<Vector2>();
			foreach (LocationInstance value in ZoneSystem.instance.m_locationInstances.Values)
			{
				if (IsSignificant(value))
				{
					list.Add(new Vector2(value.m_position.x, value.m_position.z));
				}
			}
			_significant = list.ToArray();
			_cacheUid = num;
			Log.Info("Spawn avoids " + _significant.Length + " significant locations (of " + ZoneSystem.instance.m_locationInstances.Count + " generated).");
			return _significant;
		}

		public bool IsMeadows(float x, float z)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Invalid comparison between Unknown and I4
			return (int)_wg.GetBiome(x, z, 0.02f, false) == 1;
		}

		public float HeightAboveWater(float x, float z)
		{
			return _wg.GetHeight(x, z) - _water;
		}

		public float GroundHeight(float x, float z)
		{
			return _wg.GetHeight(x, z);
		}

		public float SlopeDegrees(float x, float z)
		{
			float num = _wg.GetHeight(x + 3f, z) - _wg.GetHeight(x - 3f, z);
			float num2 = _wg.GetHeight(x, z + 3f) - _wg.GetHeight(x, z - 3f);
			return Mathf.Atan(Mathf.Sqrt(num * num + num2 * num2) / 6f) * 57.29578f;
		}

		public int OtherBiomesWithin(float x, float z, float radius)
		{
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Invalid comparison between Unknown and I4
			//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
			int num = 0;
			float[] array = new float[2]
			{
				radius,
				radius * 0.5f
			};
			foreach (float num2 in array)
			{
				for (int j = 0; j < 8; j++)
				{
					Biome biome = _wg.GetBiome(x + Cos[j] * num2, z + Sin[j] * num2, 0.02f, false);
					if ((int)biome != 1 && (int)biome != 0)
					{
						num |= biome;
					}
				}
			}
			int num3 = 0;
			while (num != 0)
			{
				num3 += num & 1;
				num >>= 1;
			}
			return num3;
		}

		public float MeadowsFractionWithin(float x, float z, float radius)
		{
			int num = (IsMeadows(x, z) ? 1 : 0);
			int num2 = 1;
			for (int i = 1; i <= 3; i++)
			{
				for (int j = 0; j < 8; j++)
				{
					float num3 = radius * (float)i / 3f;
					num2++;
					if (IsMeadows(x + Cos[j] * num3, z + Sin[j] * num3))
					{
						num++;
					}
				}
			}
			return (float)num / (float)num2;
		}

		public float DistanceToNearestLocation(float x, float z)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			float num = float.MaxValue;
			Vector2[] array = Significant();
			foreach (Vector2 val in array)
			{
				float num2 = val.x - x;
				float num3 = val.y - z;
				float num4 = num2 * num2 + num3 * num3;
				if (num4 < num)
				{
					num = num4;
				}
			}
			if (num != float.MaxValue)
			{
				return Mathf.Sqrt(num);
			}
			return 1E+09f;
		}

		public static Vector3? StonesPosition()
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			foreach (LocationInstance value in ZoneSystem.instance.m_locationInstances.Values)
			{
				if (value.m_location.m_prefab.Name == "StartTemple")
				{
					return value.m_position;
				}
			}
			return null;
		}
	}
	internal static class GraveHints
	{
		private sealed class State
		{
			public ZDOID Char = ZDOID.None;

			public bool WasDead;

			public Vector3 DeathPos;

			public bool Pending;

			public float DueAt;
		}

		private static readonly int TombstoneHash = StringExtensionMethods.GetStableHashCode("Player_tombstone");

		private const float SendDelaySeconds = 3f;

		private const float FindRadius = 12f;

		private static readonly Dictionary<long, State> States = new Dictionary<long, State>();

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

		internal static void Tick()
		{
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Unknown result type (might be due to invalid IL or missing references)
			//IL_010f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0114: Unknown result type (might be due to invalid IL or missing references)
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			if (!ServerConfig.Current.TombstoneHint || ZDOMan.instance == null)
			{
				return;
			}
			HashSet<long> hashSet = new HashSet<long>();
			foreach (Participant item in SpawnService.All())
			{
				long num = (item.IsLocal ? ZNet.GetUID() : item.Uid);
				hashSet.Add(num);
				if (!States.TryGetValue(num, out var value))
				{
					value = new State();
					States[num] = value;
				}
				if (((ZDOID)(ref item.Character)).IsNone())
				{
					continue;
				}
				ZDO zDO = ZDOMan.instance.GetZDO(item.Character);
				if (zDO == null)
				{
					continue;
				}
				if (value.Char != item.Character)
				{
					if (value.WasDead)
					{
						value.Pending = true;
						value.DueAt = Time.unscaledTime + 3f;
					}
					value.Char = item.Character;
					value.WasDead = false;
				}
				if (zDO.GetBool(ZDOVars.s_dead, false) && !value.WasDead)
				{
					value.WasDead = true;
					value.DeathPos = zDO.GetPosition();
				}
				if (value.Pending && Time.unscaledTime >= value.DueAt)
				{
					value.Pending = false;
					Send(num, value, zDO.GetPosition());
				}
			}
			List<long> list = new List<long>();
			foreach (long key in States.Keys)
			{
				if (!hashSet.Contains(key))
				{
					list.Add(key);
				}
			}
			foreach (long item2 in list)
			{
				States.Remove(item2);
			}
		}

		private static void Send(long uid, State st, Vector3 from)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: 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_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			Near.Clear();
			ZDOMan.instance.FindSectorObjects(ZoneSystem.GetZone(st.DeathPos), new SimulationDistance(1, 0, false), Near, (List<ZDO>)null);
			ZDO val = null;
			float num = 12f;
			foreach (ZDO item in Near)
			{
				if (item.GetPrefab() == TombstoneHash)
				{
					float num2 = Vector3.Distance(item.GetPosition(), st.DeathPos);
					if (num2 <= num)
					{
						num = num2;
						val = item;
					}
				}
			}
			if (val == null)
			{
				Log.Debug("no tombstone near the death of peer " + uid + "; no hint");
				return;
			}
			Vector3 position = val.GetPosition();
			string text = GraveHint.Describe(from.x, from.z, position.x, position.z);
			Log.Debug("grave hint to peer " + uid + ": " + text);
			RpcGate.Notify(uid, text, (MessageType)2);
		}
	}
	internal static class JoinMessage
	{
		private static readonly HashSet<long> Greeted = new HashSet<long>();

		internal static void Tick()
		{
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			DalSettings current = ServerConfig.Current;
			HashSet<long> present = new HashSet<long>();
			foreach (ZNetPeer peer in ZNet.instance.GetPeers())
			{
				if (!peer.m_server && peer.IsReady())
				{
					present.Add(peer.m_uid);
					if (current.ShowJoinMessage && !string.IsNullOrEmpty(current.JoinMessage) && !((ZDOID)(ref peer.m_characterID)).IsNone() && !Greeted.Contains(peer.m_uid) && ZDOMan.instance.GetZDO(peer.m_characterID) != null)
					{
						Greeted.Add(peer.m_uid);
						RpcGate.Notify(peer.m_uid, current.JoinMessage, (MessageType)2);
						Log.Debug("Join message sent to peer " + peer.m_uid + " (" + peer.m_playerName + ")");
					}
				}
			}
			Greeted.RemoveWhere((long id) => !present.Contains(id));
		}
	}
	internal static class Log
	{
		private static ManualLogSource _src;

		internal static bool DebugEnabled;

		internal static void Init(ManualLogSource src)
		{
			_src = src;
		}

		internal static void Info(string m)
		{
			ManualLogSource src = _src;
			if (src != null)
			{
				src.LogInfo((object)m);
			}
		}

		internal static void Warn(string m)
		{
			ManualLogSource src = _src;
			if (src != null)
			{
				src.LogWarning((object)m);
			}
		}

		internal static void Error(string m)
		{
			ManualLogSource src = _src;
			if (src != null)
			{
				src.LogError((object)m);
			}
		}

		internal static void Debug(string m)
		{
			if (DebugEnabled)
			{
				ManualLogSource src = _src;
				if (src != null)
				{
					src.LogInfo((object)("[debug] " + m));
				}
			}
		}
	}
	internal static class ModGate
	{
		private sealed class Pending
		{
			public float Since;

			public bool Reported;

			public float KickAt = -1f;

			public float RejectedAt;

			public float NextWarn;

			public string Reason;

			public List<ReportedMod> Mods;
		}

		internal const string ReportRpc = "DAL_ModReport";

		internal const string VerdictRpc = "DAL_ModVerdict";

		internal const int ReportVersion = 1;

		private const int MaxMods = 512;

		private static bool _recheck;

		private static readonly Dictionary<ZNetPeer, Pending> Peers = new Dictionary<ZNetPeer, Pending>();

		private static ModPolicy _policy;

		private static DateTime _policyStamp;

		private static readonly string[] AlwaysAllowed = new string[2] { "com.laoch.droppedandlost", "com.laoch.lostcamps" };

		private static readonly string[] AlwaysRequired = new string[1] { "com.laoch.dalcompass" };

		private const string Template = "# Mods players may run on this server. One per line:  [+]mod.id[|version[|sha256]]\n#   +        marks a mod every player must have\n#   version  only when you write one: leave it out (or use *) and ANY version is accepted\n#   sha256   pins the exact file (lower-case hex); leave out to skip\n# DAL Compass (com.laoch.dalcompass) is always required; this mod and Lost Camps (com.laoch.lostcamps) are always allowed. You need not list them.\n# Players can find their own list, already in this format (ids only, any version), in BepInEx/config/DalCompass/my-mods.txt (written when they join a server).\n# Changes are picked up within a few seconds. Enforcement itself is Mods.EnforceModList in com.laoch.droppedandlost.cfg.\n\n";

		private const float KickDelaySeconds = 10f;

		private const float WaitForWorldSeconds = 60f;

		private const float WarnEverySeconds = 3f;

		internal static string ListPath => Path.Combine(Paths.ConfigPath, "DroppedAndLost", "ModAllowlist.txt");

		private static void LoadPolicy()
		{
			try
			{
				string listPath = ListPath;
				if (!File.Exists(listPath))
				{
					Directory.CreateDirectory(Path.GetDirectoryName(listPath));
					File.WriteAllText(listPath, "# Mods players may run on this server. One per line:  [+]mod.id[|version[|sha256]]\n#   +        marks a mod every player must have\n#   version  only when you write one: leave it out (or use *) and ANY version is accepted\n#   sha256   pins the exact file (lower-case hex); leave out to skip\n# DAL Compass (com.laoch.dalcompass) is always required; this mod and Lost Camps (com.laoch.lostcamps) are always allowed. You need not list them.\n# Players can find their own list, already in this format (ids only, any version), in BepInEx/config/DalCompass/my-mods.txt (written when they join a server).\n# Changes are picked up within a few seconds. Enforcement itself is Mods.EnforceModList in com.laoch.droppedandlost.cfg.\n\n");
				}
				DateTime lastWriteTimeUtc = File.GetLastWriteTimeUtc(listPath);
				if (_policy != null && lastWriteTimeUtc == _policyStamp)
				{
					return;
				}
				bool num = _policy != null;
				List<string> list = new List<string>();
				_policy = ModPolicy.Parse(File.ReadAllText(listPath), (IEnumerable<string>)AlwaysAllowed, (IEnumerable<string>)AlwaysRequired, list);
				_policyStamp = lastWriteTimeUtc;
				if (num)
				{
					_recheck = true;
				}
				foreach (string item in list)
				{
					Log.Warn("ModAllowlist: " + item);
				}
				Log.Info("Mod list loaded: " + _policy.Count + " entr" + ((_policy.Count == 1) ? "y" : "ies") + ", enforcement " + (ServerConfig.Current.EnforceMods ? "ON" : "off") + ".");
			}
			catch (Exception ex)
			{
				Log.Warn("Could not read the mod list (" + ex.Message + "); keeping the previous one.");
				if (_policy == null)
				{
					_policy = ModPolicy.Parse("", (IEnumerable<string>)AlwaysAllowed, (IEnumerable<string>)AlwaysRequired, (List<string>)null);
				}
			}
		}

		internal static void Track(ZNetPeer peer)
		{
			if (peer != null && !peer.m_server)
			{
				Peers[peer] = new Pending
				{
					Since = Time.unscaledTime
				};
				peer.m_rpc.Register<ZPackage>("DAL_ModReport", (Action<ZRpc, ZPackage>)delegate(ZRpc rpc, ZPackage pkg)
				{
					OnReport(peer, pkg);
				});
			}
		}

		private static void OnReport(ZNetPeer peer, ZPackage pkg)
		{
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			if (!Peers.TryGetValue(peer, out var value) || value.Reported)
			{
				return;
			}
			value.Reported = true;
			List<ReportedMod> list = new List<ReportedMod>();
			try
			{
				int num = pkg.ReadInt();
				int num2 = pkg.ReadInt();
				if (num != 1 || num2 < 0 || num2 > 512)
				{
					Reject(peer, value, "unreadable mod report");
					return;
				}
				for (int i = 0; i < num2; i++)
				{
					list.Add(new ReportedMod
					{
						Guid = pkg.ReadString(),
						Version = pkg.ReadString(),
						Hash = pkg.ReadString()
					});
				}
			}
			catch (Exception)
			{
				Reject(peer, value, "unreadable mod report");
				return;
			}
			LoadPolicy();
			value.Mods = list;
			ModVerdict val = _policy.Evaluate((IEnumerable<ReportedMod>)list);
			Log.Debug("Mod report from peer " + peer.m_uid + ": " + list.Count + " mod(s), " + (val.Ok ? "ok" : val.Describe()));
			if (!val.Ok && ServerConfig.Current.EnforceMods)
			{
				Reject(peer, value, val.Describe());
			}
		}

		private static void Reject(ZNetPeer peer, Pending st, string reason)
		{
			if (!(st.KickAt >= 0f))
			{
				Log.Warn("Kicking " + peer.m_playerName + " (peer " + peer.m_uid + ") for their mods: " + reason);
				st.Reason = reason;
				try
				{
					peer.m_rpc.Invoke("DAL_ModVerdict", new object[1] { "Server mod list: " + reason });
				}
				catch (Exception)
				{
				}
				st.RejectedAt = Time.unscaledTime;
				st.KickAt = st.RejectedAt + 10f;
			}
		}

		private static bool InWorld(ZNetPeer peer)
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			if (!((ZDOID)(ref peer.m_characterID)).IsNone())
			{
				return ZDOMan.instance.GetZDO(peer.m_characterID) != null;
			}
			return false;
		}

		private static string WarningFor(Pending st, int seconds)
		{
			string text = "You will be kicked in " + seconds + " second" + ((seconds == 1) ? "" : "s") + ".";
			if (st.Reason != null && st.Reason.StartsWith("no mod report", StringComparison.Ordinal))
			{
				return "You are missing a required mod (DAL Compass).\n" + text;
			}
			return "Your mods do not match this server's mod list.\n" + Wrap(st.Reason, 44) + "\n" + text;
		}

		internal static string Wrap(string text, int width)
		{
			if (string.IsNullOrEmpty(text))
			{
				return "";
			}
			StringBuilder stringBuilder = new StringBuilder();
			int num = 0;
			string[] array = text.Split(new char[1] { ' ' });
			foreach (string text2 in array)
			{
				if (num > 0 && num + 1 + text2.Length > width)
				{
					stringBuilder.Append('\n');
					num = 0;
				}
				else if (num > 0)
				{
					stringBuilder.Append(' ');
					num++;
				}
				stringBuilder.Append(text2);
				num += text2.Length;
			}
			return stringBuilder.ToString();
		}

		private static void Recheck()
		{
			foreach (KeyValuePair<ZNetPeer, Pending> peer in Peers)
			{
				Pending value = peer.Value;
				if (value.Reported && value.Mods != null && !(value.KickAt >= 0f))
				{
					ModVerdict val = _policy.Evaluate((IEnumerable<ReportedMod>)value.Mods);
					if (!val.Ok)
					{
						Reject(peer.Key, value, val.Describe());
					}
				}
			}
		}

		internal static void Tick()
		{
			DalSettings current = ServerConfig.Current;
			if (current.EnforceMods)
			{
				LoadPolicy();
				if (_recheck)
				{
					_recheck = false;
					Recheck();
				}
			}
			if (Peers.Count == 0)
			{
				return;
			}
			List<ZNetPeer> list = new List<ZNetPeer>();
			HashSet<ZNetPeer> hashSet = new HashSet<ZNetPeer>(ZNet.instance.GetPeers());
			float unscaledTime = Time.unscaledTime;
			foreach (KeyValuePair<ZNetPeer, Pending> peer in Peers)
			{
				ZNetPeer key = peer.Key;
				Pending value = peer.Value;
				if (!hashSet.Contains(key))
				{
					list.Add(key);
				}
				else if (value.KickAt >= 0f)
				{
					bool flag = InWorld(key);
					if (!flag && unscaledTime - value.RejectedAt < 60f)
					{
						value.KickAt = unscaledTime + 10f;
					}
					else if (flag && unscaledTime >= value.NextWarn)
					{
						value.NextWarn = unscaledTime + 3f;
						RpcGate.Notify(key.m_uid, WarningFor(value, Mathf.Max(1, Mathf.CeilToInt(value.KickAt - unscaledTime))), (MessageType)2);
					}
					if (unscaledTime >= value.KickAt)
					{
						try
						{
							key.m_rpc.Invoke("Kicked", Array.Empty<object>());
						}
						catch (Exception)
						{
						}
						ZNet.instance.Disconnect(key);
						list.Add(key);
					}
				}
				else if (current.EnforceMods && !value.Reported && unscaledTime - value.Since > current.ModGraceSeconds)
				{
					Reject(key, value, "no mod report (install the DAL Compass mod)");
				}
			}
			foreach (ZNetPeer item in list)
			{
				Peers.Remove(item);
			}
		}
	}
	[HarmonyPatch(typeof(ZNet), "OnNewConnection")]
	internal static class ModGateNewConnection
	{
		private static void Postfix(ZNet __instance, ZNetPeer peer)
		{
			if ((Object)(object)__instance != (Object)null && __instance.IsServer())
			{
				ModGate.Track(peer);
			}
		}
	}
	[HarmonyPatch(typeof(ZoneSystem), "SendGlobalKeys")]
	internal static class SendGlobalKeysPatch
	{
		private static readonly FieldInfo GlobalKeysField = AccessTools.Field(typeof(ZoneSystem), "m_globalKeys");

		private static bool Prefix(ZoneSystem __instance, long peer)
		{
			if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer())
			{
				return true;
			}
			IList<string> forced_ = ServerConfig.Forced_;
			if (!ServerConfig.Current.NoMap && forced_.Count == 0)
			{
				return true;
			}
			List<string> list = ForcedKeys.Merge((IEnumerable<string>)GlobalKeysField.GetValue(__instance), forced_);
			if (ServerConfig.Current.NoMap && !list.Contains("nomap"))
			{
				list.Add("nomap");
			}
			ZRoutedRpc.instance.InvokeRoutedRPC(peer, "GlobalKeys", new object[1] { list });
			if ((Object)(object)Player.m_localPlayer != (Object)null)
			{
				Player.m_localPlayer.UpdateEvents();
			}
			return false;
		}
	}
	internal static class NoMapKey
	{
		internal const string Name = "nomap";
	}
	[HarmonyPatch(typeof(ZoneSystem), "GetGlobalKey", new Type[] { typeof(GlobalKeys) })]
	internal static class HostNoMapPatch
	{
		private static void Postfix(GlobalKeys key, ref bool __result)
		{
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Invalid comparison between Unknown and I4
			if (!__result && (int)key == 31)
			{
				ZNet instance = ZNet.instance;
				if ((Object)(object)instance != (Object)null && instance.IsServer() && !instance.IsDedicated() && ServerConfig.Current.NoMap)
				{
					__result = true;
				}
			}
		}
	}
	[HarmonyPatch(typeof(ZNet), "RPC_ServerSyncedPlayerData")]
	internal static class ForcePrivatePositionPatch
	{
		private static void Postfix(ZRpc rpc)
		{
			if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || !ServerConfig.Current.NoMap)
			{
				return;
			}
			foreach (ZNetPeer peer in ZNet.instance.GetPeers())
			{
				if (peer.m_rpc == rpc)
				{
					peer.m_publicRefPos = false;
					break;
				}
			}
		}
	}
	[HarmonyPatch(typeof(ZNet), "IsReferencePositionPublic")]
	internal static class HostPrivatePositionPatch
	{
		private static void Postfix(ref bool __result)
		{
			if (__result && (Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer() && ServerConfig.Current.NoMap)
			{
				__result = false;
			}
		}
	}
	internal static class PortalRules
	{
		private sealed class Snapshot
		{
			public string Id;

			public Vector3 Pos;

			public int Prefab;

			public byte[] Data;

			public long Sender;
		}

		private static readonly int HSetTag = StringExtensionMethods.GetStableHashCode("RPC_SetTag");

		private static readonly int HRemove = StringExtensionMethods.GetStableHashCode("RPC_Remove");

		private static readonly int HDamage = StringExtensionMethods.GetStableHashCode("RPC_Damage");

		private static readonly HashSet<string> Rejected = new HashSet<string>();

		private static bool _internalSend;

		private static bool _baselined;

		private static readonly List<Snapshot> Pending = new List<Snapshot>();

		internal static bool IsWatched(int hash)
		{
			if (hash != HSetTag && hash != HRemove)
			{
				return hash == HDamage;
			}
			return true;
		}

		internal static long PlayerIdOfSender(long uid)
		{
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			if (uid == ZNet.GetUID() && (Object)(object)Player.m_localPlayer != (Object)null)
			{
				return Player.m_localPlayer.GetPlayerID();
			}
			ZNetPeer peer = ZNet.instance.GetPeer(uid);
			if (peer != null)
			{
				return PlayerIdOfCharacter(peer.m_characterID);
			}
			return 0L;
		}

		internal static long PlayerIdOfCharacter(ZDOID character)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			if (((ZDOID)(ref character)).IsNone())
			{
				return 0L;
			}
			ZDO zDO = ZDOMan.instance.GetZDO(character);
			if (zDO != null)
			{
				return zDO.GetLong(ZDOVars.s_playerID, 0L);
			}
			return 0L;
		}

		internal unsafe static bool Allow(DalSettings s, long sender, ZDOID target, int hash, ZPackage parameters)
		{
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			if (!s.LimitPortals)
			{
				return true;
			}
			if (hash == HSetTag)
			{
				if (s.LockPortalNames)
				{
					return _internalSend;
				}
				return true;
			}
			if (!WorldState.Ensure())
			{
				return true;
			}
			PortalEntry val = default(PortalEntry);
			if (!WorldState.Portals.TryGet(((object)(*(ZDOID*)(&target))/*cast due to .constrained prefix*/).ToString(), ref val))
			{
				return true;
			}
			long num;
			if (hash == HDamage)
			{
				if (parameters == null)
				{
					return true;
				}
				HitData val2 = new HitData();
				val2.Deserialize(ref parameters);
				num = PlayerIdOfCharacter(val2.m_attacker);
				if (num == 0L)
				{
					return true;
				}
			}
			else
			{
				num = PlayerIdOfSender(sender);
			}
			bool num2 = WorldState.Portals.MayRemove(s, ((object)(*(ZDOID*)(&target))/*cast due to .constrained prefix*/).ToString(), num);
			if (!num2 && hash == HRemove)
			{
				RpcGate.Notify(sender, "This portal belongs to someone else.", (MessageType)2);
			}
			return num2;
		}

		internal unsafe static void BeforeDestroy(long sender, ZPackage pkg)
		{
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Expected O, but got Unknown
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Expected O, but got Unknown
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			Pending.Clear();
			DalSettings current = ServerConfig.Current;
			if (!current.LimitPortals || !current.ProtectFromOthers || sender == ZNet.GetUID() || ZDOMan.instance == null || !WorldState.Ensure())
			{
				return;
			}
			ZPackage val = new ZPackage(pkg.GetArray());
			int num = val.ReadInt();
			if (num < 0 || num > 4096)
			{
				return;
			}
			long num2 = -1L;
			PortalEntry val3 = default(PortalEntry);
			for (int i = 0; i < num; i++)
			{
				ZDOID val2 = val.ReadZDOID();
				string text = ((object)(*(ZDOID*)(&val2))/*cast due to .constrained prefix*/).ToString();
				if (!WorldState.Portals.TryGet(text, ref val3))
				{
					continue;
				}
				if (num2 < 0)
				{
					num2 = PlayerIdOfSender(sender);
				}
				if (!WorldState.Portals.MayRemove(current, text, num2))
				{
					ZDO zDO = ZDOMan.instance.GetZDO(val2);
					if (zDO != null && !(zDO.GetFloat(ZDOVars.s_health, 1f) <= 0f))
					{
						ZPackage val4 = new ZPackage();
						zDO.Serialize(val4);
						Pending.Add(new Snapshot
						{
							Id = text,
							Pos = zDO.GetPosition(),
							Prefab = zDO.GetPrefab(),
							Data = val4.GetArray(),
							Sender = sender
						});
					}
				}
			}
		}

		internal static void AfterDestroy()
		{
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Expected O, but got Unknown
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			if (Pending.Count == 0)
			{
				return;
			}
			List<Snapshot> list = new List<Snapshot>(Pending);
			Pending.Clear();
			foreach (Snapshot item in list)
			{
				try
				{
					ZDO obj = ZDOMan.instance.CreateNewZDO(item.Pos, item.Prefab);
					obj.Deserialize(new ZPackage(item.Data));
					obj.SetPosition(item.Pos);
					string text = ((object)Unsafe.As<ZDOID, ZDOID>(ref obj.m_uid)/*cast due to .constrained prefix*/).ToString();
					WorldState.Portals.Replace(item.Id, text);
					WorldState.SavePortals();
					Log.Info("Portal " + item.Id + " was deleted by peer " + item.Sender + ", who may not; restored as " + text + ".");
					RpcGate.Notify(item.Sender, "This portal belongs to someone else.", (MessageType)2);
				}
				catch (Exception ex)
				{
					Log.Error("Could not restore portal " + item.Id + ": " + ex.Message);
				}
			}
		}

		internal static void Tick()
		{
			//IL_018d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0194: Expected O, but got Unknown
			//IL_019c: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_0226: Unknown result type (might be due to invalid IL or missing references)
			DalSettings current = ServerConfig.Current;
			if (!current.LimitPortals || ZDOMan.instance == null || !WorldState.Ensure())
			{
				return;
			}
			PortalLedger portals = WorldState.Portals;
			List<ZDO> portalList = ZDOMan.instance.GetPortalList();
			if (!_baselined)
			{
				_baselined = true;
				if (!portals.Baselined)
				{
					foreach (ZDO item in portalList)
					{
						portals.Preexisting.Add(((object)Unsafe.As<ZDOID, ZDOID>(ref item.m_uid)/*cast due to .constrained prefix*/).ToString());
					}
					portals.Baselined = true;
					WorldState.SavePortals();
					Log.Info("Portal baseline: " + portalList.Count + " existing portal(s) are not managed.");
					return;
				}
			}
			HashSet<string> liveIds = new HashSet<string>();
			bool flag = false;
			PortalEntry val = default(PortalEntry);
			foreach (ZDO item2 in portalList)
			{
				string text = ((object)Unsafe.As<ZDOID, ZDOID>(ref item2.m_uid)/*cast due to .constrained prefix*/).ToString();
				liveIds.Add(text);
				if (portals.TryGet(text, ref val))
				{
					if (current.LockPortalNames)
					{
						EnforceTag(item2, val.Tag);
					}
				}
				else
				{
					if (portals.Preexisting.Contains(text) || Rejected.Contains(text))
					{
						continue;
					}
					long num = item2.GetLong(ZDOVars.s_creator, 0L);
					if (num == 0L)
					{
						continue;
					}
					DalRandom val2 = new DalRandom(DalRandom.Seed((ulong)ZNet.World.m_uid, DalRandom.HashString(text), (ulong)portals.Count));
					PlacementResult val3 = portals.OnPlaced(current, num, text, val2);
					if (!val3.Allowed)
					{
						Reject(item2, text, num, val3.Message);
						continue;
					}
					flag = true;
					Log.Info("Portal " + text + " owner " + num + " -> tag " + val3.Tag + (val3.Paired ? " (paired)" : " (new)"));
					if (current.LockPortalNames)
					{
						SetTag(item2, val3.Tag);
					}
				}
			}
			if (portals.Reconcile((ICollection<string>)liveIds) > 0)
			{
				flag = true;
			}
			Rejected.RemoveWhere((string id) => !liveIds.Contains(id));
			portals.Preexisting.RemoveWhere((string id) => !liveIds.Contains(id));
			if (flag)
			{
				WorldState.SavePortals();
			}
		}

		private static void EnforceTag(ZDO z, string tag)
		{
			if (z.GetString(ZDOVars.s_tag, "") != tag)
			{
				SetTag(z, tag);
			}
		}

		private static void SetTag(ZDO z, string tag)
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			long owner = z.GetOwner();
			_internalSend = true;
			try
			{
				if (owner == 0L || owner == ZNet.GetUID())
				{
					z.Set(ZDOVars.s_tag, tag);
					return;
				}
				ZRoutedRpc.instance.InvokeRoutedRPC(owner, z.m_uid, "RPC_SetTag", new object[2] { tag, "Dropped and Lost" });
			}
			finally
			{
				_internalSend = false;
			}
		}

		private static void Reject(ZDO z, string id, long owner, string why)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			Rejected.Add(id);
			Vector3 position = z.GetPosition();
			Log.Info("Portal " + id + " refused for owner " + owner + ": " + why);
			Refund(z, position);
			z.SetOwner(ZDOMan.GetSessionID());
			ZDOMan.instance.DestroyZDO(z);
			foreach (ZNetPeer peer in ZNet.instance.GetPeers())
			{
				if (!peer.m_server && PlayerIdOfCharacter(peer.m_characterID) == owner)
				{
					RpcGate.Notify(peer.m_uid, why + " Your materials were returned.", (MessageType)2);
				}
			}
			if ((Object)(object)Player.m_localPlayer != (Object)null && Player.m_localPlayer.GetPlayerID() == owner)
			{
				RpcGate.Notify(ZNet.GetUID(), why + " Your materials were returned.", (MessageType)2);
			}
		}

		private static void Refund(ZDO z, Vector3 pos)
		{
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				GameObject val = (((Object)(object)ZNetScene.instance != (Object)null) ? ZNetScene.instance.GetPrefab(z.GetPrefab()) : null);
				Piece val2 = (((Object)(object)val != (Object)null) ? val.GetComponent<Piece>() : null);
				if ((Object)(object)val2 == (Object)null)
				{
					Log.Warn("Could not refund: portal prefab has no Piece.");
					return;
				}
				Requirement[] resources = val2.m_resources;
				foreach (Requirement val3 in resources)
				{
					if (!((Object)(object)val3.m_resItem == (Object)null) && val3.m_amount > 0)
					{
						GameObject prefab = ZNetScene.instance.GetPrefab(((Object)((Component)val3.m_resItem).gameObject).name);
						if ((Object)(object)prefab == (Object)null)
						{
							Log.Warn("Could not refund " + ((Object)((Component)val3.m_resItem).gameObject).name + ": no such prefab.");
							continue;
						}
						ItemData obj = val3.m_resItem.m_itemData.Clone();
						obj.m_dropPrefab = prefab;
						ItemDrop.DropItem(obj, val3.m_amount, pos + Vector3.up * 1f, Quaternion.identity);
					}
				}
			}
			catch (Exception ex)
			{
				Log.Error("Refund failed: " + ex.Message);
			}
		}
	}
	internal static class PresenceNotices
	{
		private static readonly Dictionary<long, string> Here = new Dictionary<long, string>();

		internal static void Tick()
		{
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			DalSettings current = ServerConfig.Current;
			Dictionary<long, string> dictionary = new Dictionary<long, string>();
			foreach (ZNetPeer peer in ZNet.instance.GetPeers())
			{
				if (peer.m_server || !peer.IsReady())
				{
					continue;
				}
				dictionary[peer.m_uid] = peer.m_playerName;
				if (!Here.ContainsKey(peer.m_uid) && !((ZDOID)(ref peer.m_characterID)).IsNone() && ZDOMan.instance.GetZDO(peer.m_characterID) != null)
				{
					Here[peer.m_uid] = peer.m_playerName;
					if (current.AnnouncePresence)
					{
						Send(peer.m_uid, current.ArrivedText, peer.m_playerName);
					}
				}
			}
			List<long> list = new List<long>();
			foreach (KeyValuePair<long, string> item in Here)
			{
				if (!dictionary.ContainsKey(item.Key))
				{
					list.Add(item.Key);
				}
			}
			foreach (long item2 in list)
			{
				string name = Here[item2];
				Here.Remove(item2);
				if (current.AnnouncePresence)
				{
					Send(item2, current.LeftText, name);
				}
			}
		}

		private static void Send(long who, string template, string name)
		{
			string text;
			try
			{
				text = string.Format(template, string.IsNullOrEmpty(name) ? "A player" : name);
			}
			catch (FormatException)
			{
				text = name + " (notice text is invalid)";
			}
			foreach (ZNetPeer peer in ZNet.instance.GetPeers())
			{
				if (!peer.m_server && peer.IsReady() && peer.m_uid != who)
				{
					RpcGate.Notify(peer.m_uid, text, (MessageType)1);
				}
			}
			if (ZNet.instance.IsServer() && !ZNet.instance.IsDedicated() && ZNet.GetUID() != who)
			{
				RpcGate.Notify(ZNet.GetUID(), text, (MessageType)1);
			}
			Log.Debug("Presence notice: " + text);
		}
	}
	internal static class RpcGate
	{
		internal static readonly int HChat = StringExtensionMethods.GetStableHashCode("ChatMessage");

		internal static readonly int HBoss = StringExtensionMethods.GetStableHashCode("RPC_SpawnBoss");

		internal static readonly int HMap = StringExtensionMethods.GetStableHashCode("MapData");

		internal static bool Enabled
		{
			get
			{
				if ((Object)(object)ZNet.instance != (Object)null)
				{
					return ZNet.instance.IsServer();
				}
				return false;
			}
		}

		internal static bool IsWatched(int hash)
		{
			if (hash != HChat && hash != HBoss && hash != HMap)
			{
				return PortalRules.IsWatched(hash);
			}
			return true;
		}

		internal static bool Allow(long sender, ZDOID targetZdo, int hash, ZPackage parameters, int chatTypeIfKnown)
		{
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			DalSettings current = ServerConfig.Current;
			if (hash == HChat)
			{
				int num = chatTypeIfKnown;
				if (num < 0 && parameters != null)
				{
					parameters.ReadVector3();
					num = parameters.ReadInt();
				}
				if (num == 2 && current.DisableShout)
				{
					Log.Debug("DROPPED shout from peer " + sender);
					return false;
				}
				if (num == 3 && current.BlockPings)
				{
					Log.Debug("DROPPED ping from peer " + sender);
					return false;
				}
				return true;
			}
			if (hash == HMap)
			{
				int num2;
				if (current.NoMap)
				{
					num2 = ((!current.DisableCartographyTable) ? 1 : 0);
					if (num2 == 0)
					{
						Log.Debug("DROPPED cartography table write from peer " + sender);
					}
				}
				else
				{
					num2 = 1;
				}
				return (byte)num2 != 0;
			}
			if (hash == HBoss)
			{
				return BossAllowed(current, sender, targetZdo);
			}
			return PortalRules.Allow(current, sender, targetZdo, hash, parameters);
		}

		private static bool BossAllowed(DalSettings s, long sender, ZDOID altarId)
		{
			//IL_0010: 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_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			if (s.MinPlayersForBoss <= 1)
			{
				return true;
			}
			ZDO zDO = ZDOMan.instance.GetZDO(altarId);
			if (zDO == null)
			{
				return true;
			}
			Vector3 position = zDO.GetPosition();
			BossDecision val = BossGate.Evaluate(s, position.x, position.y, position.z, sender, (IList<PlayerSample>)Samples());
			Log.Debug("boss summon by " + sender + " at altar: " + val.Present + "/" + val.Needed + " present -> " + (val.Allowed ? "allowed" : "blocked"));
			if (!val.Allowed)
			{
				Notify(sender, val.Message, (MessageType)2);
			}
			return val.Allowed;
		}

		internal static List<PlayerSample> Samples()
		{
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_011f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_0138: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_0154: Unknown result type (might be due to invalid IL or missing references)
			//IL_0178: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: 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_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			List<PlayerSample> list = new List<PlayerSample>();
			foreach (ZNetPeer peer in ZNet.instance.GetPeers())
			{
				if (!peer.m_server && peer.IsReady())
				{
					ZDO val = (((ZDOID)(ref peer.m_characterID)).IsNone() ? null : ZDOMan.instance.GetZDO(peer.m_characterID));
					Vector3 val2 = ((val != null) ? val.GetPosition() : peer.m_refPos);
					list.Add(new PlayerSample
					{
						Id = peer.m_uid,
						X = val2.x,
						Y = val2.y,
						Z = val2.z,
						Alive = (val != null && !val.GetBool(ZDOVars.s_dead, false)),
						Connected = true
					});
				}
			}
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer != (Object)null && !ZNet.instance.IsDedicated())
			{
				Vector3 position = ((Component)localPlayer).transform.position;
				list.Add(new PlayerSample
				{
					Id = ZNet.GetUID(),
					X = position.x,
					Y = position.y,
					Z = position.z,
					Alive = !((Character)localPlayer).IsDead(),
					Connected = true
				});
			}
			return list;
		}

		internal static void Notify(long uid, string text, MessageType type = (MessageType)2)
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Expected I4, but got Unknown
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			if (uid == ZNet.GetUID())
			{
				if ((Object)(object)MessageHud.instance != (Object)null)
				{
					MessageHud.instance.ShowMessage(type, text, 0, (Sprite)null, false, true);
				}
			}
			else
			{
				ZRoutedRpc.instance.InvokeRoutedRPC(uid, "ShowMessage", new object[2]
				{
					(int)type,
					text
				});
			}
		}
	}
	[HarmonyPatch(typeof(ZRoutedRpc), "RPC_RoutedRPC")]
	internal static class RelayPatch
	{
		private static bool Prefix(ZRpc rpc, ZPackage pkg)
		{
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Expected O, but got Unknown
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Expected O, but got Unknown
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			if (!RpcGate.Enabled)
			{
				return true;
			}
			ZNetPeer val = null;
			foreach (ZNetPeer peer in ZNet.instance.GetPeers())
			{
				if (peer.m_rpc == rpc)
				{
					val = peer;
					break;
				}
			}
			if (val == null)
			{
				return true;
			}
			RoutedRPCData val2 = new RoutedRPCData();
			val2.Deserialize(new ZPackage(pkg.GetArray()));
			if (val2.m_senderPeerID != val.m_uid)
			{
				Log.Warn("Dropped a routed RPC from '" + val.m_playerName + "' claiming to be peer " + val2.m_senderPeerID + " (real " + val.m_uid + ").");
				return false;
			}
			if (!RpcGate.IsWatched(val2.m_methodHash))
			{
				return true;
			}
			val2.m_parameters.SetPos(0);
			return RpcGate.Allow(val.m_uid, val2.m_targetZDO, val2.m_methodHash, val2.m_parameters, -1);
		}
	}
	[HarmonyPatch(typeof(ZRoutedRpc), "InvokeRoutedRPC", new Type[]
	{
		typeof(long),
		typeof(ZDOID),
		typeof(string),
		typeof(object[])
	})]
	internal static class LocalCallPatch
	{
		private static bool Prefix(long targetPeerID, ZDOID targetZDO, string methodName, object[] parameters)
		{
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			if (!RpcGate.Enabled)
			{
				return true;
			}
			int stableHashCode = StringExtensionMethods.GetStableHashCode(methodName);
			if (!RpcGate.IsWatched(stableHashCode))
			{
				return true;
			}
			int chatTypeIfKnown = -1;
			if (stableHashCode == RpcGate.HChat && parameters != null && parameters.Length > 1 && parameters[1] is int)
			{
				chatTypeIfKnown = (int)parameters[1];
			}
			return RpcGate.Allow(ZNet.GetUID(), targetZDO, stableHashCode, null, chatTypeIfKnown);
		}
	}
	[HarmonyPatch(typeof(ZDOMan), "RPC_DestroyZDO")]
	internal static class DestroyZdoPatch
	{
		private static void Prefix(long sender, ZPackage pkg)
		{
			if (!RpcGate.Enabled)
			{
				return;
			}
			try
			{
				PortalRules.BeforeDestroy(sender, pkg);
			}
			catch (Exception ex)
			{
				Log.Error("Portal delete check failed: " + ex.Message);
			}
		}

		private static void Postfix()
		{
			if (RpcGate.Enabled)
			{
				PortalRules.AfterDestroy();
			}
		}
	}
	internal static class ServerConfig
	{
		internal sealed class ConfigurationManagerAttributes
		{
			public bool? Browsable = false;
		}

		private static ConfigFile _cfg;

		private static FileSystemWatcher _watcher;

		private static volatile bool _dirty;

		private static DalSettings _override;

		private static volatile DalSettings _current = new DalSettings();

		private static volatile List<string> _forced = new List<string>();

		private static ConfigEntry<bool> Announce;

		private static ConfigEntry<bool> EnforceMods;

		private static ConfigEntry<bool> GraveOn;

		private static ConfigEntry<bool> SpawnEnabled;

		private static ConfigEntry<bool> ExistingAccounts;

		private static ConfigEntry<bool> NoMap;

		private static ConfigEntry<bool> CartoTable;

		private static ConfigEntry<bool> Shout;

		private static ConfigEntry<bool> Pings;

		private static ConfigEntry<bool> JoinMsgOn;

		private static ConfigEntry<bool> LimitPortals;

		private static ConfigEntry<bool> LockNames;

		private static ConfigEntry<bool> Protect;

		private static ConfigEntry<bool> Debug;

		private static ConfigEntry<float> MeadowR;

		private static ConfigEntry<float> MeadowFrac;

		private static ConfigEntry<float> ModGrace;

		private static ConfigEntry<float> MinD;

		private static ConfigEntry<float> MaxD;

		private static ConfigEntry<float> NearR;

		private static ConfigEntry<float> Slope;

		private static ConfigEntry<float> Height;

		private static ConfigEntry<float> LocDist;

		private static ConfigEntry<float> OtherDist;

		private static ConfigEntry<float> BossR;

		private static ConfigEntry<int> NearN;

		private static ConfigEntry<int> Attempts;

		private static ConfigEntry<int> RerollDeaths;

		private static ConfigEntry<int> BossMin;

		private static ConfigEntry<int> MaxPortals;

		private static ConfigEntry<RespawnMode> RMode;

		private static ConfigEntry<string> JoinMsg;

		private static ConfigEntry<string> Forced;

		private static ConfigEntry<string> ArrivedTxt;

		private static ConfigEntry<string> LeftTxt;

		internal static bool _overrideIncludeExisting;

		private static volatile bool _changed;

		public static DalSettings Current => _override ?? _current;

		public static IList<string> Forced_
		{
			get
			{
				if (_override == null)
				{
					return _forced;
				}
				return ForcedKeys.Parse(_override.ForcedKeys, (List<string>)null);
			}
		}

		internal static bool IncludeExistingAccounts
		{
			get
			{
				if (_override == null)
				{
					return ExistingAccounts.Value;
				}
				return _overrideIncludeExisting;
			}
		}

		internal static void SetOverride(DalSettings s)
		{
			_override = s;
		}

		internal static void ClearOverride()
		{
			_override = null;
		}

		private static ConfigEntry<T> Hid<T>(ConfigFile c, string section, string key, T def, string desc)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Expected O, but got Unknown
			return c.Bind<T>(section, key, def, new ConfigDescription(desc, (AcceptableValueBase)null, new object[1]
			{
				new ConfigurationManagerAttributes()
			}));
		}

		public static void Bind(ConfigFile c)
		{
			//IL_02aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f2: Unknown result type (might be due to invalid IL or missing references)
			_cfg = c;
			SpawnEnabled = Hid(c, "Spawn", "Enabled", def: true, "Random first spawn. Server-side only; clients need no mod.");
			ExistingAccounts = Hid(c, "Spawn", "IncludeExistingAccounts", def: false, "Accounts that had already played this world before the mod was installed keep vanilla spawning. Set true to also give them a random point (used after death or on a new character).");
			MinD = Hid(c, "Spawn", "MinDistance", 3000f, "Minimum metres from the Stones.");
			MaxD = Hid(c, "Spawn", "MaxDistance", 6000f, "Maximum metres from the Stones.");
			NearR = Hid(c, "Spawn", "NearbyBiomeRadius", 150f, "Metres within which other biomes count as nearby.");
			NearN = Hid(c, "Spawn", "MinNearbyBiomes", 1, "How many other biomes must be nearby.");
			Attempts = Hid(c, "Spawn", "MaxSearchAttempts", 500, "Candidate points tested at each relaxation level.");
			Slope = Hid(c, "Spawn", "MaxSlopeDegrees", 20f, "Steepest ground allowed.");
			Height = Hid(c, "Spawn", "MinHeightAboveWater", 2f, "Metres above the water line.");
			MeadowR = Hid(c, "Spawn", "MinMeadowsRadius", 120f, "The start must be in a Meadows at least this big: this many metres around it must be mostly Meadows. Keeps a start off a tiny meadow or a thin strip of land. 0 = no check. The last-resort search stage halves it.");
			MeadowFrac = Hid(c, "Spawn", "MinMeadowsFraction", 0.8f, "How much of that area must be Meadows (0 to 1).");
			LocDist = Hid(c, "Spawn", "MinDistanceFromLocations", 75f, "Metres kept from crypts, villages, altars.");
			OtherDist = Hid(c, "Spawn", "MinDistanceFromOtherSpawns", 500f, "Metres to keep from other characters' spawn points. A preference only: when no such spot is found it is halved, then dropped, before any other rule is relaxed. 0 = off.");
			RMode = Hid<RespawnMode>(c, "Respawn", "Mode", (RespawnMode)0, "FirstSpawn: return to your first spawn point when you die with no bed. Stones: vanilla. RandomEachDeath: new random point every death.");
			RerollDeaths = Hid(c, "Respawn", "RerollAfterDeaths", 0, "Re-roll the spawn point after this many deaths in a row with no bed. 0 = off.");
			NoMap = Hid(c, "Map", "NoMap", def: true, "Removes the map, minimap and position markers for everyone.");
			CartoTable = Hid(c, "Map", "DisableCartographyTable", def: true, "Blocks the cartography table when NoMap is on.");
			BossMin = Hid(c, "Boss", "MinPlayersForBoss", 2, "Living players needed near the altar to summon a boss. 1 = vanilla.");
			BossR = Hid(c, "Boss", "PresenceRadius", 50f, "Metres from the altar that count as present.");
			Shout = Hid(c, "Chat", "DisableShout", def: true, "Turns off the global shout channel.");
			Pings = Hid(c, "Chat", "BlockPings", def: true, "Drops map pings from other players.");
			JoinMsgOn = Hid(c, "Chat", "ShowJoinMessage", def: true, "Shows the rules message on login.");
			Announce = Hid(c, "Chat", "AnnouncePresence", def: true, "Tell everyone when a player arrives or leaves. Shows the name only, never a position.");
			ArrivedTxt = Hid(c, "Chat", "ArrivedText", new DalSettings().ArrivedText, "Arrival notice. {0} is the player name.");
			LeftTxt = Hid(c, "Chat", "LeftText", new DalSettings().LeftText, "Departure notice. {0} is the player name.");
			JoinMsg = Hid(c, "Chat", "JoinMessage", new DalSettings().JoinMessage, "Text of the join message.");
			LimitPortals = Hid(c, "Portals", "LimitPortals", def: true, "Turns the portal rules on or off.");
			MaxPortals = Hid(c, "Portals", "MaxPortalsPerPlayer", 2, "Portals one player may have standing.");
			LockNames = Hid(c, "Portals", "LockPortalNames", def: true, "Players cannot edit portal tags.");
			Protect = Hid(c, "Portals", "ProtectFromOthers", def: true, "Only the owner can destroy a portal.");
			Forced = Hid(c, "World", "ForcedKeys", "", "World keys to force on every player who joins, never saved into the world file. Separate entries with semicolons, for example: resourcerate 2; deathpenalty casual. Changes apply to connected players within seconds.");
			GraveOn = Hid(c, "Death", "TombstoneHint", def: true, "After you die and respawn, show the heading and rough distance to your tombstone (never coordinates). Only shown if a tombstone exists.");
			EnforceMods = Hid(c, "Mods", "EnforceModList", def: false, "Kick players whose mods do not match ModAllowlist.txt (in the DroppedAndLost config folder). Players need the DAL Compass mod, which reports what they run. Players without it are kicked after GraceSeconds. A modified client can lie, so this keeps honest players honest; it is not anti-cheat.");
			ModGrace = Hid(c, "Mods", "GraceSeconds", 20f, "Seconds a new connection has to report its mods before it is kicked.");
			Debug = Hid(c, "General", "DebugLogging", def: false, "Extra log detail for testing.");
			Rebuild("startup");
			try
			{
				_watcher = new FileSystemWatcher(Path.GetDirectoryName(c.ConfigFilePath), Path.GetFileName(c.ConfigFilePath));
				_watcher.NotifyFilter = NotifyFilters.Size | NotifyFilters.LastWrite;
				_watcher.Changed += delegate
				{
					_dirty = true;
				};
				_watcher.EnableRaisingEvents = true;
			}
			catch (Exception ex)
			{
				Log.Warn("Config live-reload unavailable: " + ex.Message);
			}
		}

		public static bool TakeChanged()
		{
			if (!_changed)
			{
				return false;
			}
			_changed = false;
			return true;
		}

		public static void Unbind()
		{
			if (_watcher != null)
			{
				_watcher.Dispose();
				_watcher = null;
			}
		}

		public static void PollReload()
		{
			if (!_dirty)
			{
				return;
			}
			_dirty = false;
			try
			{
				_cfg.Reload();
				Rebuild("reload");
				_changed = true;
			}
			catch (Exception ex)
			{
				Log.Warn("Config reload failed, keeping previous settings: " + ex.Message);
			}
		}

		private static void Rebuild(string why)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: 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_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: 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_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_0115: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			//IL_0145: Unknown result type (might be due to invalid IL or missing references)
			//IL_0155: Unknown result type (might be due to invalid IL or missing references)
			//IL_0165: Unknown result type (might be due to invalid IL or missing references)
			//IL_0175: Unknown result type (might be due to invalid IL or missing references)
			//IL_0185: Unknown result type (might be due to invalid IL or missing references)
			//IL_0195: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f5: Unknown result type (might be due to invalid IL or missing references)
			//IL_0205: Unknown result type (might be due to invalid IL or missing references)
			//IL_0215: Unknown result type (might be due to invalid IL or missing references)
			DalSettings val = new DalSettings
			{
				SpawnEnabled = SpawnEnabled.Value,
				SpawnMinDistance = MinD.Value,
				SpawnMaxDistance = MaxD.Value,
				NearbyBiomeRadius = NearR.Value,
				MinNearbyBiomes = NearN.Value,
				MaxSearchAttempts = Attempts.Value,
				MaxSlopeDegrees = Slope.Value,
				MinHeightAboveWater = Height.Value,
				MinDistanceFromLocations = LocDist.Value,
				MinDistanceFromOtherSpawns = OtherDist.Value,
				MinMeadowsRadius = MeadowR.Value,
				MinMeadowsFraction = MeadowFrac.Value,
				RespawnMode = RMode.Value,
				RespawnRerollAfterDeaths = RerollDeaths.Value,
				NoMap = NoMap.Value,
				DisableCartographyTable = CartoTable.Value,
				MinPlayersForBoss = BossMin.Value,
				BossPresenceRadius = BossR.Value,
				AnnouncePresence = Announce.Value,
				ArrivedText = ArrivedTxt.Value,
				LeftText = LeftTxt.Value,
				DisableShout = Shout.Value,
				BlockPings = Pings.Value,
				ShowJoinMessage = JoinMsgOn.Value,
				JoinMessage = JoinMsg.Value,
				LimitPortals = LimitPortals.Value,
				MaxPortalsPerPlayer = MaxPortals.Value,
				LockPortalNames = LockNames.Value,
				ProtectFromOthers = Protect.Value,
				TombstoneHint = GraveOn.Value,
				ForcedKeys = Forced.Value,
				EnforceMods = EnforceMods.Value,
				ModGraceSeconds = ModGrace.Value,
				DebugLogging = Debug.Value
			};
			List<string> list = new List<string>();
			_current = val.Validated(list);
			_forced = ForcedKeys.Parse(_current.ForcedKeys, list);
			Log.DebugEnabled = _current.DebugLogging;
			foreach (string item in list)
			{
				Log.Warn("Config: " + item);
			}
			Log.Info("Settings " + why + ": spawn " + (_current.SpawnEnabled ? "on" : "off") + " " + _current.SpawnMinDistance + "-" + _current.SpawnMaxDistance + "m, boss " + _current.MinPlayersForBoss + "p/" + _current.BossPresenceRadius + "m, portals " + (_current.LimitPortals ? _current.MaxPortalsPerPlayer.ToString() : "off") + ", nomap " + _current.NoMap);
		}
	}
	[HarmonyPatch(typeof(ZoneSystem), "SendLocationIcons")]
	internal static class SendLocationIconsPatch
	{
		private static bool Prefix(ZoneSystem __instance, long peer)
		{
			if (!SpawnService.Active)
			{
				return true;
			}
			if (peer == 0L)
			{
				foreach (ZNetPeer peer2 in ZNet.instance.GetPeers())
				{
					if (!peer2.m_server && peer2.IsReady())
					{
						SpawnService.SendIcons(__instance, peer2.m_uid);
					}
				}
				return false;
			}
			SpawnService.SendIcons(__instance, peer);
			return false;
		}
	}
	[HarmonyPatch(typeof(ZoneSystem), "GetLocationIcon")]
	internal static class LocalStartIconPatch
	{
		private static void Postfix(string name, ref Vector3 pos, ref bool __result)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			if (!(name != "StartTemple") && SpawnService.Active)
			{
				Participant participant = SpawnService.ForLocal();
				if (participant != null && SpawnService.TryGetIcon(participant, out var pos2))
				{
					pos = pos2;
					__result = true;
				}
			}
		}
	}
	internal sealed class Participant
	{
		public string Key;

		public string Account;

		public string Name;

		public long Uid;

		public bool IsLocal;

		public ZDOID Character = ZDOID.None;
	}
	internal static class SpawnService
	{
		private sealed class Track
		{
			public ZDOID Char = ZDOID.None;

			public bool WasDead;
		}

		internal const string StartIcon = "StartTemple";

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

		internal static string LastRollSummary = "";

		internal static double LastRollMs;

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

		internal static bool Active
		{
			get
			{
				if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer() && ServerConfig.Current.SpawnEnabled)
				{
					return WorldState.Ensure();
				}
				return false;
			}
		}

		internal static string AccountOf(ZNetPeer p)
		{
			return AccountId.Normalize(p.m_socket.GetHostName());
		}

		internal static Participant ForPeer(ZNetPeer p)
		{
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			if (p == null || p.m_server || string.IsNullOrEmpty(p.m_playerName))
			{
				return null;
			}
			string text = AccountOf(p);
			return new Participant
			{
				Account = text,
				Name = p.m_playerName,
				Key = text + "/" + p.m_playerName,
				Uid = p.m_uid,
				Character = p.m_characterID
			};
		}

		internal static Participant ForLocal()
		{
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)ZNet.instance == (Object)null || ZNet.instance.IsDedicated() || (Object)(object)Game.instance == (Object)null)
			{
				return null;
			}
			PlayerProfile playerProfile = Game.instance.GetPlayerProfile();
			if (playerProfile == null)
			{
				return null;
			}
			string text = AccountId.Normalize(((object)((IUser)PlatformManager.DistributionPlatform.LocalUser).PlatformUserID/*cast due to .constrained prefix*/).ToString());
			Participant participant = new Participant
			{
				Account = text,
				Name = playerProfile.GetName(),
				IsLocal = true,
				Uid = 0L
			};
			participant.Key = text + "/" + participant.Name;
			if ((Object)(object)Player.m_localPlayer != (Object)null)
			{
				participant.Character = ((Character)Player.m_localPlayer).GetZDOID();
			}
			return participant;
		}

		internal static IEnumerable<Participant> All()
		{
			Participant participant = ForLocal();
			if (participant != null)
			{
				yield return participant;
			}
			if ((Object)(object)ZNet.instance == (Object)null)
			{
				yield break;
			}
			foreach (ZNetPeer peer in ZNet.instance.GetPeers())
			{
				Participant participant2 = ForPeer(peer);
				if (participant2 != null)
				{
					yield return participant2;
				}
			}
		}

		internal static SpawnRecord EnsureRecord(Participant p)
		{
			SpawnLedger spawns = WorldState.Spawns;
			SpawnRecord result = default(SpawnRecord);
			if (spawns.TryGet(p.Key, ref result))
			{
				return result;
			}
			if (spawns.LegacyAccounts.Contains(p.Account) && !ServerConfig.IncludeExistingAccounts)
			{
				return null;
			}
			return Roll(p);
		}

		internal static SpawnRecord Roll(Participant p)
		{
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Expected O, but got Unknown
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Expected O, but got Unknown
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0131: Invalid comparison between Unknown and I4
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0172: Unknown result type (might be due to invalid IL or missing references)
			//IL_0192: Unknown result type (might be due to invalid IL or missing references)
			//IL_0197: Unknown result type (might be due to invalid IL or missing references)
			DalSettings current = ServerConfig.Current;
			SpawnLedger spawns = WorldState.Spawns;
			Vector3? val = GameTerrain.StonesPosition();
			if (!val.HasValue)
			{
				Log.Warn("No StartTemple in this world; spawn left vanilla.");
				return null;
			}
			SpawnRecord val2 = default(SpawnRecord);
			spawns.TryGet(p.Key, ref val2);
			int num = val2?.Rolls ?? 0;
			DalRandom val3 = new DalRandom(DalRandom.Seed((ulong)ZNet.World.m_uid, DalRandom.HashString(p.Key), (ulong)num));
			GameTerrain gameTerrain = new GameTerrain();
			List<SpawnPoint> list = new List<SpawnPoint>();
			foreach (SpawnRecord record in spawns.Records)
			{
				if (record.HasPoint && record.Key != p.Key)
				{
					list.Add(new SpawnPoint(record.X, record.Z));
				}
			}
			SpawnSearch val4 = new SpawnSearch(current, val3, new SpawnPoint(val.Value.x, val.Value.z), (IList<SpawnPoint>)list);
			Stopwatch stopwatch = Stopwatch.StartNew();
			SearchState val5 = val4.RunToEnd((ITerrain)(object)gameTerrain);
			stopwatch.Stop();
			LastRollMs = stopwatch.Elapsed.TotalMilliseconds;
			if ((int)val5 == 1)
			{
				spawns.Assign(p.Key, val4.Result);
				LastRollSummary = "spawn for '" + p.Name + "': (" + val4.Result.X.ToString("F0") + ", " + val4.Result.Z.ToString("F0") + ") after " + val4.TotalAttempts + " attempts, stage '" + val4.StageLabel + "', " + LastRollMs.ToString("F1") + " ms";
				Log.Info(LastRollSummary);
			}
			else
			{
				spawns.AssignVanillaFallback(p.Key);
				LastRollSummary = "spawn for '" + p.Name + "': NO valid point after " + val4.TotalAttempts + " attempts across " + val4.Stages.Count + " levels; using the vanilla Stones";
				Log.Warn(LastRollSummary);
			}
			WorldState.SaveSpawns();
			SpawnRecord result = default(SpawnRecord);
			spawns.TryGet(p.Key, ref result);
			return result;
		}

		internal static bool TryGetIcon(Participant p, out Vector3 pos)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Invalid comparison between Unknown and I4
			//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)
			pos = Vector3.zero;
			if (!Active)
			{
				return false;
			}
			SpawnRecord val = EnsureRecord(p);
			if (val == null || !val.HasPoint)
			{
				return false;
			}
			if ((int)ServerConfig.Current.RespawnMode == 1 && val.Spawned)
			{
				return false;
			}
			pos = new Vector3(val.X, new GameTerrain().GroundHeight(val.X, val.Z), val.Z);
			return true;
		}

		internal static void SendIcons(ZoneSystem zs, long uid)
		{
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Expected O, but got Unknown
			//IL_0108: 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_009b: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Unknown result type (might be due to invalid IL or missing references)
			Temp.Clear();
			zs.GetLocationIcons(Temp);
			Participant participant = ForPeer(ZNet.instance.GetPeer(uid));
			if (participant != null && TryGetIcon(participant, out var pos))
			{
				List<Vector3> list = new List<Vector3>();
				foreach (KeyValuePair<Vector3, string> item in Temp)
				{
					if (item.Value == "StartTemple")
					{
						list.Add(item.Key);
					}
				}
				foreach (Vector3 item2 in list)
				{
					Temp.Remove(item2);
				}
				Temp[pos] = "StartTemple";
			}
			ZPackage val = new ZPackage();
			val.Write(Temp.Count);
			foreach (KeyValuePair<Vector3, string> item3 in Temp)
			{
				val.Write(item3.Key);
				val.Write(item3.Value);
			}
			ZRoutedRpc.instance.InvokeRoutedRPC(uid, "LocationIcons", new object[1] { val });
		}

		internal unsafe static void Tick()
		{
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Invalid comparison between Unknown and I4
			//IL_0133: Unknown result type (might be due to invalid IL or missing references)
			//IL_0138: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0172: Unknown result type (might be due to invalid IL or missing references)
			//IL_019b: Unknown result type (might be due to invalid IL or missing references)
			//IL_019e: Invalid comparison between Unknown and I4
			//IL_00e9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			if (!Active)
			{
				return;
			}
			DalSettings current = ServerConfig.Current;
			SpawnLedger spawns = WorldState.Spawns;
			SpawnRecord val = default(SpawnRecord);
			foreach (Participant item in All())
			{
				if (!spawns.TryGet(item.Key, ref val))
				{
					continue;
				}
				if (!Tracks.TryGetValue(item.Key, out var value))
				{
					value = new Track();
					Tracks[item.Key] = value;
				}
				if (((ZDOID)(ref item.Character)).IsNone())
				{
					continue;
				}
				ZDO zDO = ZDOMan.instance.GetZDO(item.Character);
				if (zDO == null)
				{
					continue;
				}
				if (!val.Spawned)
				{
					val.Spawned = true;
					WorldState.SaveSpawns();
					if ((int)current.RespawnMode == 1 && !item.IsLocal)
					{
						zsResend(item);
					}
				}
				if (value.Char != item.Character)
				{
					if (value.WasDead && val.HasPoint)
					{
						Vector3 position = zDO.GetPosition();
						float num = position.x - val.X;
						float num2 = position.z - val.Z;
						if (num * num + num2 * num2 > 1600f)
						{
							spawns.OnBedRespawn(item.Key);
							WorldState.SaveSpawns();
						}
					}
					value.Char = item.Character;
					value.WasDead = false;
				}
				if (!zDO.GetBool(ZDOVars.s_dead, false) || value.WasDead)
				{
					continue;
				}
				value.WasDead = true;
				RespawnAction val2 = spawns.OnDeathWithoutBed(item.Key, current);
				Log.Debug("death of '" + item.Name + "' -> " + ((object)(*(RespawnAction*)(&val2))/*cast due to .constrained prefix*/).ToString());
				if ((int)val2 == 2)
				{
					Roll(item);
					if (!item.IsLocal)
					{
						zsResend(item);
					}
				}
				WorldState.SaveSpawns();
			}
		}

		private static void zsResend(Participant p)
		{
			if ((Object)(object)ZoneSystem.instance != (Object)null)
			{
				SendIcons(ZoneSystem.instance, p.Uid);
			}
		}
	}
	internal static class WorldState
	{
		private delegate T Parser<T>(string text, out string error) where T : class;

		private static long _uid;

		private static string _dir;

		internal static SpawnLedger Spawns;

		internal static PortalLedger Portals;

		private static string SpawnPath => Path.Combine(_dir, "spawns.txt");

		private static string PortalPath => Path.Combine(_dir, "portals.txt");

		internal unsafe static bool Ensure()
		{
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Expected O, but got Unknown
			//IL_018c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
			World world = ZNet.World;
			if (world == null)
			{
				return false;
			}
			if (Spawns != null && Portals != null && _uid == world.m_uid)
			{
				return true;
			}
			_uid = world.m_uid;
			_dir = Path.Combine(Path.Combine(Paths.ConfigPath, "DroppedAndLost"), Sanitize(world.m_name) + "-" + world.m_uid);
			Directory.CreateDirectory(_dir);
			Spawns = Load(SpawnPath, (Parser<SpawnLedger>)SpawnLedger.Deserialize, out bool fresh);
			if (Spawns == null)
			{
				Spawns = new SpawnLedger();
				foreach (CrossNetworkUserInfo item in world.m_playerHistory)
				{
					HashSet<string> legacyAccounts = Spawns.LegacyAccounts;
					PlatformUserID id = item.m_id;
					legacyAccounts.Add(AccountId.Normalize(((object)(*(PlatformUserID*)(&id))/*cast due to .constrained prefix*/).ToString()));
				}
				Log.Info("New spawn ledger for world '" + world.m_name + "'; " + Spawns.LegacyAccounts.Count + " existing account(s) keep vanilla spawning.");
				SaveSpawns();
			}
			Portals = (PortalLedger)(((object)Load(PortalPath, (Parser<PortalLedger>)PortalLedger.Deserialize, out fresh)) ?? ((object)new PortalLedger()));
			return true;
		}

		internal static void SaveSpawns()
		{
			Write(SpawnPath, Spawns.Serialize());
		}

		internal static void SavePortals()
		{
			Write(PortalPath, Portals.Serialize());
		}

		private static T Load<T>(string path, Parser<T> parse, out bool fresh) where T : class
		{
			fresh = false;
			string[] array = new string[2]
			{
				path,
				path + ".bak"
			};
			foreach (string text in array)
			{
				if (!File.Exists(text))
				{
					continue;
				}
				string error;
				T val = parse(File.ReadAllText(text, Encoding.UTF8), out error);
				if (val != null)
				{
					if (text != path)
					{
						Log.Warn("Recovered " + Path.GetFileName(path) + " from its .bak.");
					}
					return val;
				}
				string text2 = text + ".corrupt-" + DateTime.UtcNow.ToString("yyyyMMddHHmmss");
				File.Move(text, text2);
				Log.Error(Path.GetFileName(text) + " unreadable (" + error + "); moved to " + Path.GetFileName(text2) + ".");
			}
			fresh = true;
			return null;
		}

		private static void Write(string path, string text)
		{
			try
			{
				string text2 = path + ".tmp";
				File.WriteAllText(text2, text, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
				if (File.Exists(path))
				{
					File.Replace(text2, path, path + ".bak");
				}
				else
				{
					File.Move(text2, path);
				}
			}
			catch (Exception ex)
			{
				Log.Error("Could not save " + Path.GetFileName(path) + ": " + ex.Message);
			}
		}

		private static string Sanitize(string s)
		{
			char[] invalidFileNameChars = Path.GetInvalidFileNameChars();
			foreach (char oldChar in invalidFileNameChars)
			{
				s = s.Replace(oldChar, '_');
			}
			return s;
		}
	}
}