Decompiled source of SealedTombstone v1.0.10

Landoria.SealedTombstone.dll

Decompiled 2 weeks 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.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using Landoria.SharedLib;
using Splatform;
using TMPro;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("Landoria.SealedTombstone")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Landoria.SealedTombstone")]
[assembly: AssemblyCopyright("Copyright ©  2026")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("37181DCE-EEA5-4CC8-8DF3-32B9AB883DE8")]
[assembly: AssemblyFileVersion("1.0.10")]
[assembly: AssemblyInformationalVersion("1.0.10")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = "")]
[assembly: AssemblyVersion("1.0.10.13992")]
namespace Landoria.SealedTombstone
{
	internal sealed class PendingRequest
	{
		internal long RequesterPlayerId { get; set; }

		internal string RequesterName { get; set; }

		internal long OwnerPlayerId { get; set; }

		internal ZDOID TombstoneId { get; set; }

		internal DateTime CreatedAt { get; set; }
	}
	internal static class RecentAttackerPolicy
	{
		internal static readonly TimeSpan Window = TimeSpan.FromMinutes(2.0);

		internal static bool ShouldRecord(bool victimIsLocal, bool hasPlayerAttacker, long victimId, long attackerId)
		{
			if (victimIsLocal && hasPlayerAttacker && attackerId != 0L)
			{
				return attackerId != victimId;
			}
			return false;
		}

		internal static long[] Snapshot(IReadOnlyDictionary<long, DateTime> lastHits, DateTime deathAt)
		{
			DateTime threshold = deathAt - Window;
			return (from entry in lastHits
				where entry.Value >= threshold
				select entry.Key).ToArray();
		}

		internal static string Serialize(IEnumerable<long> playerIds)
		{
			return string.Join(",", playerIds.Select((long id) => id.ToString(CultureInfo.InvariantCulture)));
		}

		internal static bool Contains(string serializedIds, long playerId)
		{
			if (string.IsNullOrEmpty(serializedIds))
			{
				return false;
			}
			string expected = playerId.ToString(CultureInfo.InvariantCulture);
			return serializedIds.Split(new char[1] { ',' }).Any((string id) => id == expected);
		}
	}
	internal static class RecentAttackers
	{
		private static readonly Dictionary<long, DateTime> LastHits = new Dictionary<long, DateTime>();

		private static long[] _deathSnapshot = new long[0];

		internal static void Record(Player victim, HitData hit)
		{
			Character obj = ((hit != null) ? hit.GetAttacker() : null);
			Player val = (Player)(object)((obj is Player) ? obj : null);
			long num = ((val != null) ? val.GetPlayerID() : 0);
			if (RecentAttackerPolicy.ShouldRecord((Object)(object)victim == (Object)(object)Player.m_localPlayer, (Object)(object)val != (Object)null, victim.GetPlayerID(), num))
			{
				LastHits[num] = DateTime.UtcNow;
			}
		}

		internal static void SnapshotForDeath(Player player)
		{
			if (!((Object)(object)player != (Object)(object)Player.m_localPlayer))
			{
				_deathSnapshot = RecentAttackerPolicy.Snapshot(LastHits, DateTime.UtcNow);
				LastHits.Clear();
				SealedTombstonePlugin.Log.LogDebug($"Captured {_deathSnapshot.Length} recent PvP attackers.");
			}
		}

		internal static string ConsumeSnapshot()
		{
			string result = RecentAttackerPolicy.Serialize(_deathSnapshot);
			_deathSnapshot = new long[0];
			return result;
		}

		internal static bool Contains(string serializedIds, long playerId)
		{
			return RecentAttackerPolicy.Contains(serializedIds, playerId);
		}

		internal static void Reset()
		{
			LastHits.Clear();
			_deathSnapshot = new long[0];
		}
	}
	internal enum TombstoneInteraction
	{
		Allow,
		RequestAccess,
		Block
	}
	internal static class TombstoneAccessPolicy
	{
		internal const int UnlockAfterDays = 10;

		internal static readonly TimeSpan RequestExpiry = TimeSpan.FromSeconds(30.0);

		internal static readonly TimeSpan RequestCooldown = TimeSpan.FromMinutes(2.0);

		internal static TombstoneInteraction Evaluate(bool hasPlayerAndTombstone, long ownerId, long playerId, long lockDay, long currentDay, bool blocked)
		{
			if (!hasPlayerAndTombstone || ownerId == 0L || ownerId == playerId)
			{
				return TombstoneInteraction.Allow;
			}
			if (blocked)
			{
				return TombstoneInteraction.Block;
			}
			if (!IsExpired(lockDay, currentDay))
			{
				return TombstoneInteraction.RequestAccess;
			}
			return TombstoneInteraction.Allow;
		}

		internal static bool IsExpired(long lockDay, long currentDay)
		{
			if (lockDay >= 0 && currentDay >= 0)
			{
				return currentDay - lockDay >= 10;
			}
			return false;
		}

		internal static bool HasRequestExpired(DateTime createdAt, DateTime now)
		{
			return now - createdAt > RequestExpiry;
		}

		internal static bool IsCooldownActive(DateTime lastRequestAt, DateTime now)
		{
			return now - lastRequestAt < RequestCooldown;
		}

		internal static string SafeName(string name)
		{
			if (string.IsNullOrWhiteSpace(name))
			{
				return "A player";
			}
			string text = name.Replace("<", string.Empty).Replace(">", string.Empty);
			if (text.Length > 64)
			{
				return text.Substring(0, 64);
			}
			return text;
		}
	}
	internal sealed class TombstoneAvailabilityResult
	{
		internal DateTime LastRequestAt;

		internal string Message;
	}
	internal static class TombstoneRequestPolicy
	{
		internal static TombstoneAvailabilityResult ApplyAvailability(bool ownerOnline, DateTime lastRequestAt, DateTime now)
		{
			return new TombstoneAvailabilityResult
			{
				LastRequestAt = (ownerOnline ? now : lastRequestAt),
				Message = (ownerOnline ? "Access request sent to the tombstone owner." : "The tombstone owner is offline.")
			};
		}

		internal static string DecisionMessage(bool accepted, string ownerName)
		{
			if (!accepted)
			{
				return TombstoneAccessPolicy.SafeName(ownerName) + " denied or did not answer the request.";
			}
			return TombstoneAccessPolicy.SafeName(ownerName) + " granted access to the tombstone.";
		}
	}
	internal sealed class TombstoneRequestPresentation
	{
		internal string Title;

		internal string Message;
	}
	internal static class TombstonePresentationPolicy
	{
		internal static TombstoneRequestPresentation Build(string requesterName)
		{
			return new TombstoneRequestPresentation
			{
				Title = "Tombstone access request",
				Message = "Allow " + TombstoneAccessPolicy.SafeName(requesterName) + " to loot this tombstone?"
			};
		}
	}
	internal static class TombstoneAccess
	{
		private const string LockDayKey = "Landoria.SealedTombstone.LockDay";

		private const string BlockedPlayersKey = "Landoria.SealedTombstone.BlockedPlayers";

		private const string AccessCheckRpc = "Landoria_SealedTombstone_AccessCheck";

		private const string IdentityRpc = "Landoria_SealedTombstone_Identity";

		private const string RequestRpc = "Landoria_SealedTombstone_Request";

		private const string AvailabilityRpc = "Landoria_SealedTombstone_Availability";

		private const string DecisionSubmitRpc = "Landoria_SealedTombstone_DecisionSubmit";

		private const string DecisionRpc = "Landoria_SealedTombstone_Decision";

		private static PendingRequest _pendingRequest;

		private static DateTime _lastRequestAt = DateTime.MinValue;

		private static ZRoutedRpc _registeredRpc;

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

		private static long _identityServer;

		internal static void RegisterRpcs()
		{
			ZRoutedRpc instance = ZRoutedRpc.instance;
			if (instance != null && instance != _registeredRpc)
			{
				instance.Register<long, string, ZDOID, long>("Landoria_SealedTombstone_AccessCheck", (Method<long, string, ZDOID, long>)ReceiveAccessCheck);
				instance.Register<long>("Landoria_SealedTombstone_Identity", (Action<long, long>)ReceiveIdentity);
				instance.Register<long, string, ZDOID, long>("Landoria_SealedTombstone_Request", (Method<long, string, ZDOID, long>)ReceiveRequest);
				instance.Register<bool>("Landoria_SealedTombstone_Availability", (Action<long, bool>)ReceiveAvailability);
				instance.Register<long, ZDOID, bool, string, long>("Landoria_SealedTombstone_DecisionSubmit", (Method<long, ZDOID, bool, string, long>)ReceiveDecisionSubmission);
				instance.Register<long, ZDOID, bool, string>("Landoria_SealedTombstone_Decision", (Method<long, ZDOID, bool, string>)ReceiveDecision);
				_registeredRpc = instance;
				SealedTombstonePlugin.Log.LogDebug("Tombstone access RPCs registered for the current session.");
			}
		}

		internal static void ResetSession()
		{
			_pendingRequest = null;
			_lastRequestAt = DateTime.MinValue;
			_registeredRpc = null;
			PeerPlayers.Clear();
			_identityServer = 0L;
			RecentAttackers.Reset();
		}

		internal static void RecordLockDay(TombStone tombstone, long ownerId)
		{
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			ZNetView component = ((Component)tombstone).GetComponent<ZNetView>();
			ZDO val = ((component != null) ? component.GetZDO() : null);
			if (ownerId != 0L && val != null && component.IsOwner())
			{
				val.Set("Landoria.SealedTombstone.LockDay", CurrentDay());
				val.Set("Landoria.SealedTombstone.BlockedPlayers", RecentAttackers.ConsumeSnapshot());
				SealedTombstonePlugin.Log.LogDebug($"Locked tombstone {val.m_uid} for player {ownerId}.");
			}
		}

		internal static bool AllowInteraction(TombStone tombstone, Humanoid character)
		{
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			Player val = (Player)(object)((character is Player) ? character : null);
			ZNetView component = ((Component)tombstone).GetComponent<ZNetView>();
			ZDO val2 = ((component != null) ? component.GetZDO() : null);
			if ((Object)(object)val == (Object)null || val2 == null)
			{
				return true;
			}
			long playerID = val.GetPlayerID();
			long ownerId = val2.GetLong(ZDOVars.s_owner, 0L);
			switch (TombstoneAccessPolicy.Evaluate(hasPlayerAndTombstone: true, ownerId, playerID, val2.GetLong("Landoria.SealedTombstone.LockDay", -1L), CurrentDay(), IsBlocked(val2, playerID)))
			{
			case TombstoneInteraction.Allow:
				return true;
			case TombstoneInteraction.Block:
				((Character)val).Message((MessageType)2, "You cannot request access to this tombstone.", 0, (Sprite)null, false);
				SealedTombstonePlugin.Log.LogDebug($"Blocked tombstone request from recent attacker {val.GetPlayerID()}.");
				return false;
			default:
				RequestAccess(val, ownerId, val2.m_uid);
				return false;
			}
		}

		internal static void Tick()
		{
			SyncIdentity();
			if (_pendingRequest != null && HasExpired(_pendingRequest))
			{
				SealedTombstonePlugin.Log.LogDebug($"Request from {_pendingRequest.RequesterPlayerId} expired.");
				ClosePopup();
				SendDecision(_pendingRequest, accepted: false);
				_pendingRequest = null;
			}
		}

		private static void RequestAccess(Player requester, long ownerId, ZDOID tombstoneId)
		{
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			if (TombstoneAccessPolicy.IsCooldownActive(_lastRequestAt, DateTime.UtcNow))
			{
				((Character)requester).Message((MessageType)2, "Please wait before sending another request.", 0, (Sprite)null, false);
				return;
			}
			ZRoutedRpc instance = ZRoutedRpc.instance;
			ZNet instance2 = ZNet.instance;
			if (instance == null || (Object)(object)instance2 == (Object)null)
			{
				((Character)requester).Message((MessageType)2, "The tombstone owner is offline.", 0, (Sprite)null, false);
				return;
			}
			long playerID = requester.GetPlayerID();
			if (instance2.IsServer())
			{
				ProcessAccessCheck(0L, playerID, requester.GetPlayerName(), tombstoneId, ownerId);
				return;
			}
			ZNetPeer serverPeer = instance2.GetServerPeer();
			if (serverPeer == null)
			{
				((Character)requester).Message((MessageType)2, "The tombstone owner is offline.", 0, (Sprite)null, false);
				return;
			}
			instance.InvokeRoutedRPC(serverPeer.m_uid, "Landoria_SealedTombstone_AccessCheck", new object[4]
			{
				playerID,
				requester.GetPlayerName(),
				tombstoneId,
				ownerId
			});
		}

		private static void ReceiveAccessCheck(long sender, long requesterId, string requesterName, ZDOID tombstoneId, long ownerId)
		{
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer() && ZNet.instance.GetPeer(sender) != null && requesterId != 0L)
			{
				PeerPlayers[sender] = requesterId;
				ProcessAccessCheck(sender, requesterId, requesterName, tombstoneId, ownerId);
			}
		}

		private static void ProcessAccessCheck(long requesterPeer, long requesterId, string requesterName, ZDOID tombstoneId, long ownerId)
		{
			//IL_0032: 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)
			long num = FindOnlinePeer(ownerId);
			Player localPlayer = Player.m_localPlayer;
			bool flag = localPlayer != null && localPlayer.GetPlayerID() == ownerId;
			SendAvailability(requesterPeer, num != 0 || flag);
			if (flag)
			{
				ReceiveRequest(0L, requesterId, requesterName, tombstoneId, ownerId);
			}
			else if (num != 0L)
			{
				ZRoutedRpc.instance.InvokeRoutedRPC(num, "Landoria_SealedTombstone_Request", new object[4] { requesterId, requesterName, tombstoneId, ownerId });
			}
		}

		private static long FindOnlinePeer(long playerId)
		{
			foreach (KeyValuePair<long, long> peerPlayer in PeerPlayers)
			{
				ZNetPeer peer = ZNet.instance.GetPeer(peerPlayer.Key);
				if (peerPlayer.Value == playerId && peer != null && peer.IsReady())
				{
					return peerPlayer.Key;
				}
			}
			return 0L;
		}

		private static void SyncIdentity()
		{
			ZNet instance = ZNet.instance;
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)instance == (Object)null || (Object)(object)localPlayer == (Object)null || instance.IsServer())
			{
				return;
			}
			ZNetPeer serverPeer = instance.GetServerPeer();
			if (serverPeer != null && serverPeer.m_uid != _identityServer)
			{
				_identityServer = serverPeer.m_uid;
				ZRoutedRpc instance2 = ZRoutedRpc.instance;
				if (instance2 != null)
				{
					instance2.InvokeRoutedRPC(serverPeer.m_uid, "Landoria_SealedTombstone_Identity", new object[1] { localPlayer.GetPlayerID() });
				}
			}
		}

		private static void ReceiveIdentity(long sender, long playerId)
		{
			if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer() && ZNet.instance.GetPeer(sender) != null && playerId != 0L)
			{
				PeerPlayers[sender] = playerId;
			}
		}

		private static void SendAvailability(long requesterPeer, bool online)
		{
			if (requesterPeer == 0L)
			{
				ReceiveAvailability(0L, online);
				return;
			}
			ZRoutedRpc.instance.InvokeRoutedRPC(requesterPeer, "Landoria_SealedTombstone_Availability", new object[1] { online });
		}

		private static void ReceiveAvailability(long sender, bool ownerOnline)
		{
			Player localPlayer = Player.m_localPlayer;
			if (!((Object)(object)localPlayer == (Object)null) && IsServerResponse(sender))
			{
				TombstoneAvailabilityResult tombstoneAvailabilityResult = TombstoneRequestPolicy.ApplyAvailability(ownerOnline, _lastRequestAt, DateTime.UtcNow);
				_lastRequestAt = tombstoneAvailabilityResult.LastRequestAt;
				((Character)localPlayer).Message((MessageType)2, tombstoneAvailabilityResult.Message, 0, (Sprite)null, false);
				if (ownerOnline)
				{
					SealedTombstonePlugin.Log.LogInfo($"Player {localPlayer.GetPlayerID()} requested tombstone access.");
				}
			}
		}

		private static bool IsServerResponse(long sender)
		{
			ZNet instance = ZNet.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return false;
			}
			ZNetPeer serverPeer = instance.GetServerPeer();
			return TombstoneDecisionPolicy.IsTrustedResponse(instance.IsServer(), sender, serverPeer != null, serverPeer?.m_uid ?? 0);
		}

		private static void ReceiveRequest(long sender, long requesterId, string requesterName, ZDOID tombstoneId, long ownerId)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			ZDOMan instance = ZDOMan.instance;
			ZDO tombstone = ((instance != null) ? instance.GetZDO(tombstoneId) : null);
			if (IsServerResponse(sender) && !((Object)(object)localPlayer == (Object)null) && localPlayer.GetPlayerID() == ownerId && _pendingRequest == null && !IsBlocked(tombstone, requesterId))
			{
				_pendingRequest = new PendingRequest
				{
					RequesterPlayerId = requesterId,
					RequesterName = SafeName(requesterName),
					OwnerPlayerId = ownerId,
					TombstoneId = tombstoneId,
					CreatedAt = DateTime.UtcNow
				};
				ShowDecisionPopup(_pendingRequest);
			}
		}

		private static void ShowDecisionPopup(PendingRequest request)
		{
			//IL_0054: 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_006c: Expected O, but got Unknown
			//IL_006c: Expected O, but got Unknown
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Expected O, but got Unknown
			if (!UnifiedPopup.IsAvailable())
			{
				SealedTombstonePlugin.Log.LogWarning("The unlock request was rejected because the VV popup is unavailable.");
				Decide(request, accepted: false);
				return;
			}
			TombstoneRequestPresentation tombstoneRequestPresentation = TombstonePresentationPolicy.Build(request.RequesterName);
			UnifiedPopup.Push((PopupBase)new YesNoPopup(tombstoneRequestPresentation.Title, tombstoneRequestPresentation.Message, (PopupButtonCallback)delegate
			{
				Decide(request, accepted: true);
			}, (PopupButtonCallback)delegate
			{
				Decide(request, accepted: false);
			}, false, false));
		}

		private static void Decide(PendingRequest request, bool accepted)
		{
			if (request != _pendingRequest || HasExpired(request))
			{
				ClosePopup();
				_pendingRequest = null;
				return;
			}
			ClosePopup();
			_pendingRequest = null;
			SendDecision(request, accepted);
			Player localPlayer = Player.m_localPlayer;
			if (localPlayer != null)
			{
				((Character)localPlayer).Message((MessageType)2, accepted ? "Tombstone access granted." : "Tombstone access denied.", 0, (Sprite)null, false);
			}
		}

		private static void SendDecision(PendingRequest request, bool accepted)
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if ((Object)(object)localPlayer == (Object)null || ZRoutedRpc.instance == null)
			{
				return;
			}
			ZNet instance = ZNet.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return;
			}
			if (instance.IsServer())
			{
				ForwardDecision(0L, request.RequesterPlayerId, request.TombstoneId, accepted, localPlayer.GetPlayerName(), request.OwnerPlayerId);
			}
			else
			{
				ZNetPeer serverPeer = instance.GetServerPeer();
				if (serverPeer != null)
				{
					ZRoutedRpc.instance.InvokeRoutedRPC(serverPeer.m_uid, "Landoria_SealedTombstone_DecisionSubmit", new object[5]
					{
						request.RequesterPlayerId,
						request.TombstoneId,
						accepted,
						localPlayer.GetPlayerName(),
						request.OwnerPlayerId
					});
				}
			}
			SealedTombstonePlugin.Log.LogInfo($"Tombstone request from {request.RequesterPlayerId} accepted={accepted}.");
		}

		private static void ReceiveDecisionSubmission(long sender, long requesterId, ZDOID tombstoneId, bool accepted, string ownerName, long ownerId)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			ForwardDecision(sender, requesterId, tombstoneId, accepted, ownerName, ownerId);
		}

		private static void ForwardDecision(long sender, long requesterId, ZDOID tombstoneId, bool accepted, string ownerName, long ownerId)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			ZNet instance = ZNet.instance;
			ZDOMan instance2 = ZDOMan.instance;
			ZDO val = ((instance2 != null) ? instance2.GetZDO(tombstoneId) : null);
			long value;
			long mappedPlayerId = ((sender == 0L) ? ownerId : (PeerPlayers.TryGetValue(sender, out value) ? value : 0));
			bool senderPeerExists = sender == 0L || ((instance != null) ? instance.GetPeer(sender) : null) != null;
			if (!TombstoneDecisionPolicy.CanForward(instance != null && instance.IsServer(), senderPeerExists, mappedPlayerId, ownerId, (val != null) ? val.GetLong(ZDOVars.s_owner, 0L) : 0))
			{
				return;
			}
			long num = FindOnlinePeer(requesterId);
			if (num == 0L)
			{
				Player localPlayer = Player.m_localPlayer;
				if (localPlayer != null && localPlayer.GetPlayerID() == requesterId)
				{
					ReceiveDecision(0L, requesterId, tombstoneId, accepted, ownerName);
					return;
				}
			}
			if (num != 0L)
			{
				ZRoutedRpc.instance.InvokeRoutedRPC(num, "Landoria_SealedTombstone_Decision", new object[4] { requesterId, tombstoneId, accepted, ownerName });
			}
		}

		private static void ReceiveDecision(long sender, long requesterId, ZDOID tombstoneId, bool accepted, string ownerName)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			Player localPlayer = Player.m_localPlayer;
			if (!((Object)(object)localPlayer == (Object)null) && localPlayer.GetPlayerID() == requesterId && IsServerResponse(sender))
			{
				if (TombstoneDecisionPolicy.ShouldUnlock(accepted))
				{
					Unlock(tombstoneId);
				}
				((Character)localPlayer).Message((MessageType)2, TombstoneRequestPolicy.DecisionMessage(accepted, ownerName), 0, (Sprite)null, false);
			}
		}

		private static void Unlock(ZDOID tombstoneId)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			ZNetScene instance = ZNetScene.instance;
			object obj;
			if (instance == null)
			{
				obj = null;
			}
			else
			{
				GameObject obj2 = instance.FindInstance(tombstoneId);
				obj = ((obj2 != null) ? obj2.GetComponent<ZNetView>() : null);
			}
			ZNetView val = (ZNetView)obj;
			if ((Object)(object)val == (Object)null || !val.IsValid())
			{
				SealedTombstonePlugin.Log.LogWarning($"Cannot unlock unavailable tombstone {tombstoneId}.");
				return;
			}
			val.ClaimOwnership();
			val.GetZDO().Set(ZDOVars.s_owner, 0L);
			SealedTombstonePlugin.Log.LogInfo($"Tombstone {tombstoneId} was unlocked.");
		}

		private static bool IsBlocked(ZDO tombstone, long playerId)
		{
			if (tombstone != null)
			{
				return RecentAttackers.Contains(tombstone.GetString("Landoria.SealedTombstone.BlockedPlayers", ""), playerId);
			}
			return false;
		}

		private static long CurrentDay()
		{
			if (!((Object)(object)EnvMan.instance != (Object)null))
			{
				return -1L;
			}
			return EnvMan.instance.GetDay();
		}

		private static bool HasExpired(PendingRequest request)
		{
			return TombstoneAccessPolicy.HasRequestExpired(request.CreatedAt, DateTime.UtcNow);
		}

		private static string SafeName(string name)
		{
			return TombstoneAccessPolicy.SafeName(name);
		}

		private static void ClosePopup()
		{
			if (UnifiedPopup.IsAvailable() && UnifiedPopup.IsVisible())
			{
				UnifiedPopup.Pop();
			}
		}
	}
	internal static class TombstoneDecisionPolicy
	{
		internal static bool CanForward(bool isServer, bool senderPeerExists, long mappedPlayerId, long expectedOwnerId, long tombstoneOwnerId)
		{
			if (isServer && senderPeerExists && mappedPlayerId != 0L && mappedPlayerId == expectedOwnerId)
			{
				return tombstoneOwnerId == expectedOwnerId;
			}
			return false;
		}

		internal static bool IsTrustedResponse(bool isServerHost, long sender, bool hasServerPeer, long serverPeerId)
		{
			if (!isServerHost)
			{
				if (hasServerPeer)
				{
					return sender == serverPeerId;
				}
				return false;
			}
			return sender == 0;
		}

		internal static bool ShouldUnlock(bool accepted)
		{
			return accepted;
		}
	}
	[BepInPlugin("Landoria.SealedTombstone", "Landoria.SealedTombstone", "1.0.10")]
	public sealed class SealedTombstonePlugin : LandoriaPlugin
	{
		private const string PluginGuid = "Landoria.SealedTombstone";

		private const string PluginName = "Landoria.SealedTombstone";

		private const string PluginVersion = "1.0.10";

		internal static ModLog Log { get; private set; }

		private void Awake()
		{
			Log = InitializePlugin("Landoria.SealedTombstone");
			Log.LogInfo("Landoria.SealedTombstone 1.0.10 is loaded.");
		}

		private void Update()
		{
			TombstoneAccess.Tick();
		}

		private void OnDestroy()
		{
			TombstoneAccess.ResetSession();
			Log?.LogInfo("Landoria.SealedTombstone 1.0.10 is unloaded.");
			ShutdownPlugin();
			Log = null;
		}
	}
	[HarmonyPatch(typeof(Game), "Start")]
	internal static class RpcRegistrationPatch
	{
		private static void Postfix()
		{
			TombstoneAccess.RegisterRpcs();
		}
	}
	[HarmonyPatch(typeof(TombStone), "Setup")]
	internal static class TombstoneSetupPatch
	{
		private static void Postfix(TombStone __instance, long ownerUID)
		{
			TombstoneAccess.RecordLockDay(__instance, ownerUID);
		}
	}
	[HarmonyPatch(typeof(TombStone), "Interact")]
	internal static class TombstoneInteractPatch
	{
		private static bool Prefix(TombStone __instance, Humanoid character)
		{
			return TombstoneAccess.AllowInteraction(__instance, character);
		}
	}
	[HarmonyPatch(typeof(Player), "OnDamaged")]
	internal static class PlayerDamagedPatch
	{
		private static void Postfix(Player __instance, HitData hit)
		{
			RecentAttackers.Record(__instance, hit);
		}
	}
	[HarmonyPatch(typeof(Player), "OnDeath")]
	internal static class PlayerDeathPatch
	{
		private static void Prefix(Player __instance)
		{
			RecentAttackers.SnapshotForDeath(__instance);
		}
	}
}
namespace Landoria.SharedLib
{
	public abstract class LandoriaPlugin : BaseUnityPlugin
	{
		private Harmony _harmony;

		private bool _patchesApplied;

		protected ModLog InitializePlugin(string pluginGuid)
		{
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Expected O, but got Unknown
			ModLog modLog = new ModLog(((BaseUnityPlugin)this).Logger);
			Version version = ((object)this).GetType().Assembly.GetName().Version;
			modLog.LogInfo($"AssemblyVersion: {version}.");
			EnsureSharedPatches(modLog);
			_harmony = new Harmony(pluginGuid);
			PatchOwnNamespace(modLog);
			return modLog;
		}

		protected ModLog InitializePlugin(string pluginGuid, Type[] patchTypes)
		{
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0045: Expected O, but got Unknown
			ModLog modLog = new ModLog(((BaseUnityPlugin)this).Logger);
			Version version = ((object)this).GetType().Assembly.GetName().Version;
			modLog.LogInfo($"AssemblyVersion: {version}.");
			EnsureSharedPatches(modLog);
			_harmony = new Harmony(pluginGuid);
			foreach (Type type in patchTypes)
			{
				_harmony.CreateClassProcessor(type).Patch();
				modLog.LogInfo("Server patch: " + type.Name);
			}
			_patchesApplied = true;
			return modLog;
		}

		private static void EnsureSharedPatches(ModLog log)
		{
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			lock (AppDomain.CurrentDomain)
			{
				if (AppDomain.CurrentDomain.GetData("Landoria.SharedLib.ConnectionFailureMenuPatch.v1") == null)
				{
					new Harmony("Landoria.SharedLib").CreateClassProcessor(typeof(ConnectionFailureMenuPatch)).Patch();
					AppDomain.CurrentDomain.SetData("Landoria.SharedLib.ConnectionFailureMenuPatch.v1", true);
					log.LogDebug("Shared connection failure menu patch was applied.");
				}
			}
		}

		protected void PatchOwnNamespace(ModLog log)
		{
			if (_patchesApplied)
			{
				log.LogDebug("Harmony patches are already active; skipping registration.");
				return;
			}
			string text = ((object)this).GetType().Namespace;
			Type[] types = Assembly.GetExecutingAssembly().GetTypes();
			foreach (Type type in types)
			{
				if (type.Namespace == text)
				{
					_harmony.CreateClassProcessor(type).Patch();
				}
			}
			_patchesApplied = true;
			log.LogDebug("Harmony patches were applied for the plugin namespace.");
		}

		protected void ShutdownPlugin()
		{
			if (_patchesApplied)
			{
				Harmony harmony = _harmony;
				if (harmony != null)
				{
					harmony.UnpatchSelf();
				}
				_patchesApplied = false;
			}
		}
	}
	public static class ConnectionFailureMessages
	{
		private const string StateKey = "Landoria.SharedLib.ConnectionFailureMessages.v1";

		private static readonly ManualLogSource Log = Logger.CreateLogSource("Landoria.ConnectionFailureMessages");

		public static void Push(string source, string message)
		{
			Push(source, message, (string)null);
		}

		public static void Push(string source, string userMessage, string systemMessage)
		{
			Push(source, userMessage, systemMessage, null);
		}

		public static void Push(string source, string userMessage, Exception exception)
		{
			Push(source, userMessage, exception?.Message, exception);
		}

		public static void Push(string source, string userMessage, string systemMessage, Exception exception)
		{
			string text = DisplayMessage(userMessage, systemMessage ?? exception?.Message);
			if (string.IsNullOrWhiteSpace(source) || text == null)
			{
				return;
			}
			bool flag = false;
			lock (AppDomain.CurrentDomain)
			{
				Stack<Tuple<string, string, string>> messages = GetMessages();
				Tuple<string, string, string> item = Tuple.Create(source, text, systemMessage);
				if (!messages.Contains(item))
				{
					messages.Push(item);
					flag = true;
				}
			}
			if (flag)
			{
				LogQueued(source, text, systemMessage, exception);
			}
		}

		public static void Clear(string source)
		{
			if (string.IsNullOrWhiteSpace(source))
			{
				return;
			}
			lock (AppDomain.CurrentDomain)
			{
				Stack<Tuple<string, string, string>> messages = GetMessages();
				Stack<Tuple<string, string, string>> stack = new Stack<Tuple<string, string, string>>();
				while (messages.Count > 0)
				{
					Tuple<string, string, string> tuple = messages.Pop();
					if (tuple.Item1 != source)
					{
						stack.Push(tuple);
					}
				}
				while (stack.Count > 0)
				{
					messages.Push(stack.Pop());
				}
			}
		}

		internal static bool TryPopAll(out string message)
		{
			lock (AppDomain.CurrentDomain)
			{
				Stack<Tuple<string, string, string>> messages = GetMessages();
				if (messages.Count > 0)
				{
					List<string> list = new List<string>();
					while (messages.Count > 0)
					{
						list.Add(messages.Pop().Item2);
					}
					message = string.Join("\n", list);
					return true;
				}
			}
			message = null;
			return false;
		}

		private static Stack<Tuple<string, string, string>> GetMessages()
		{
			if (AppDomain.CurrentDomain.GetData("Landoria.SharedLib.ConnectionFailureMessages.v1") is Stack<Tuple<string, string, string>> result)
			{
				return result;
			}
			Stack<Tuple<string, string, string>> stack = new Stack<Tuple<string, string, string>>();
			AppDomain.CurrentDomain.SetData("Landoria.SharedLib.ConnectionFailureMessages.v1", stack);
			return stack;
		}

		private static string DisplayMessage(string userMessage, string systemMessage)
		{
			if (!string.IsNullOrWhiteSpace(userMessage))
			{
				return userMessage;
			}
			if (!string.IsNullOrWhiteSpace(systemMessage))
			{
				return systemMessage;
			}
			return null;
		}

		private static void LogQueued(string source, string userMessage, string systemMessage, Exception exception)
		{
			string text = exception?.ToString() ?? Environment.StackTrace;
			if (string.IsNullOrWhiteSpace(systemMessage) || systemMessage == userMessage)
			{
				Log.LogWarning((object)("Queued connection failure message from " + source + ": " + userMessage + "\nDiagnostic stack:\n" + text));
			}
			else
			{
				Log.LogWarning((object)("Queued connection failure message from " + source + ": userMessage=" + userMessage + "; systemMessage=" + systemMessage + "\nDiagnostic stack:\n" + text));
			}
		}
	}
	[HarmonyPatch]
	internal static class ConnectionFailureMenuPatch
	{
		[HarmonyPostfix]
		[HarmonyPatch(typeof(FejdStartup), "ShowConnectError")]
		private static void ShowConnectError(TMP_Text ___m_connectionFailedError)
		{
			if (ConnectionFailureMessages.TryPopAll(out var message))
			{
				___m_connectionFailedError.text = message;
			}
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(FejdStartup), "Start")]
		private static void Start(GameObject ___m_connectionFailedPanel, TMP_Text ___m_connectionFailedError)
		{
			ShowNext(___m_connectionFailedPanel, ___m_connectionFailedError);
		}

		[HarmonyPostfix]
		[HarmonyPatch(typeof(FejdStartup), "OnConnectionFailedOk")]
		private static void OnConnectionFailedOk(GameObject ___m_connectionFailedPanel, TMP_Text ___m_connectionFailedError)
		{
			ShowNext(___m_connectionFailedPanel, ___m_connectionFailedError);
		}

		private static void ShowNext(GameObject panel, TMP_Text text)
		{
			if (ConnectionFailureMessages.TryPopAll(out var message))
			{
				text.text = message;
				panel.SetActive(true);
			}
		}
	}
	public sealed class ModLog
	{
		private readonly ManualLogSource _logger;

		public ModLog(ManualLogSource logger)
		{
			_logger = logger;
		}

		public void LogFatal(object message)
		{
			Write((LogLevel)1, message);
		}

		public void LogError(object message)
		{
			Write((LogLevel)2, message);
		}

		public void LogWarning(object message)
		{
			Write((LogLevel)4, message);
		}

		public void LogMessage(object message)
		{
			Write((LogLevel)8, message);
		}

		public void LogInfo(object message)
		{
			Write((LogLevel)16, message);
		}

		public void LogDebug(object message)
		{
			Write((LogLevel)32, message);
		}

		public void Log(LogLevel level, object message)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			Write(level, message);
		}

		private void Write(LogLevel level, object message)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			string arg = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
			_logger.Log(level, (object)$"[{arg}] {message}");
		}
	}
	public static class PlatformPlayerIdentity
	{
		public unsafe static string Resolve(PlayerInfo player)
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			string text = ((object)(*(PlatformUserID*)(&player.m_userInfo.m_id))/*cast due to .constrained prefix*/).ToString();
			if (!string.IsNullOrWhiteSpace(text))
			{
				return text;
			}
			ZNetPeer obj = FindPeer(player);
			if (obj == null)
			{
				return null;
			}
			ISocket socket = obj.m_socket;
			if (socket == null)
			{
				return null;
			}
			return socket.GetHostName();
		}

		public static ZNetPeer FindPeer(PlayerInfo player)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)ZNet.instance == (Object)null || ((ZDOID)(ref player.m_characterID)).IsNone())
			{
				return null;
			}
			return ((IEnumerable<ZNetPeer>)ZNet.instance.GetPeers()).FirstOrDefault((Func<ZNetPeer, bool>)((ZNetPeer peer) => peer != null && ((ZDOID)(ref peer.m_characterID)).Equals(player.m_characterID)));
		}
	}
	public static class ServerRole
	{
		public static bool IsDedicatedServer
		{
			get
			{
				if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer())
				{
					return ZNet.instance.IsDedicated();
				}
				return false;
			}
		}
	}
}