Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of SealedTombstone v1.0.0
IronLabs.SealedTombstone.dll
Decompiled 3 hours agousing 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 IronLabs.SharedLib; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: AssemblyTitle("IronLabs.SealedTombstone")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("IronLabs.SealedTombstone")] [assembly: AssemblyCopyright("Copyright © 2026")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("37181DCE-EEA5-4CC8-8DF3-32B9AB883DE8")] [assembly: AssemblyFileVersion("1.0.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: AssemblyVersion("1.0.0.2687")] namespace IronLabs.SealedTombstone { internal sealed class PendingRequest { internal long RequesterPlayerId { get; set; } internal string RequesterName { get; set; } internal ZDOID TombstoneId { get; set; } internal DateTime CreatedAt { get; set; } } internal static class RecentAttackers { private static readonly TimeSpan Window = TimeSpan.FromMinutes(2.0); 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); if (!((Object)(object)victim != (Object)(object)Player.m_localPlayer) && !((Object)(object)val == (Object)null)) { long playerID = val.GetPlayerID(); if (playerID != 0L && playerID != victim.GetPlayerID()) { LastHits[playerID] = DateTime.UtcNow; } } } internal static void SnapshotForDeath(Player player) { if (!((Object)(object)player != (Object)(object)Player.m_localPlayer)) { DateTime threshold = DateTime.UtcNow - Window; _deathSnapshot = (from entry in LastHits where entry.Value >= threshold select entry.Key).ToArray(); LastHits.Clear(); SealedTombstonePlugin.Log.LogDebug($"Captured {_deathSnapshot.Length} recent PvP attackers."); } } internal static string ConsumeSnapshot() { string result = string.Join(",", _deathSnapshot.Select((long id) => id.ToString(CultureInfo.InvariantCulture))); _deathSnapshot = new long[0]; return result; } 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 void Reset() { LastHits.Clear(); _deathSnapshot = new long[0]; } } internal static class TombstoneAccess { private const string LockDayKey = "IronLabs.SealedTombstone.LockDay"; private const string BlockedPlayersKey = "IronLabs.SealedTombstone.BlockedPlayers"; private const string RequestRpc = "IronLabs_SealedTombstone_Request"; private const string DecisionRpc = "IronLabs_SealedTombstone_Decision"; private const int UnlockAfterDays = 10; private const int RequestExpirySeconds = 30; private const int RequestCooldownSeconds = 120; private static PendingRequest _pendingRequest; private static DateTime _lastRequestAt = DateTime.MinValue; private static ZRoutedRpc _registeredRpc; internal static void RegisterRpcs() { ZRoutedRpc instance = ZRoutedRpc.instance; if (instance != null && instance != _registeredRpc) { instance.Register<long, string, ZDOID, long>("IronLabs_SealedTombstone_Request", (Method<long, string, ZDOID, long>)ReceiveRequest); instance.Register<long, ZDOID, bool, string>("IronLabs_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; RecentAttackers.Reset(); } internal static void RecordLockDay(TombStone tombstone, long ownerId) { //IL_005b: 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("IronLabs.SealedTombstone.LockDay", CurrentDay()); val.Set("IronLabs.SealedTombstone.BlockedPlayers", RecentAttackers.ConsumeSnapshot()); SealedTombstonePlugin.Log.LogDebug($"Locked tombstone {val.m_uid} for player {ownerId}."); } } internal static bool AllowInteraction(TombStone tombstone, Humanoid character) { //IL_00b1: 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 num = val2.GetLong(ZDOVars.s_owner, 0L); if (num == 0L || num == val.GetPlayerID() || IsExpired(val2)) { return true; } if (IsBlocked(val2, val.GetPlayerID())) { ((Character)val).Message((MessageType)2, "You cannot request access to this tombstone.", 0, (Sprite)null); SealedTombstonePlugin.Log.LogDebug($"Blocked tombstone request from recent attacker {val.GetPlayerID()}."); return false; } RequestAccess(val, num, val2.m_uid); return false; } internal static void Tick() { 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_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Unknown result type (might be due to invalid IL or missing references) if ((DateTime.UtcNow - _lastRequestAt).TotalSeconds < 120.0) { ((Character)requester).Message((MessageType)2, "Please wait before sending another request.", 0, (Sprite)null); return; } if (ZRoutedRpc.instance == null) { ((Character)requester).Message((MessageType)2, "The tombstone owner is offline.", 0, (Sprite)null); return; } _lastRequestAt = DateTime.UtcNow; ((Character)requester).Message((MessageType)2, "Access request sent to the tombstone owner.", 0, (Sprite)null); ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.Everybody, "IronLabs_SealedTombstone_Request", new object[4] { requester.GetPlayerID(), requester.GetPlayerName(), tombstoneId, ownerId }); SealedTombstonePlugin.Log.LogInfo($"Player {requester.GetPlayerID()} requested access to {tombstoneId}."); } private static void ReceiveRequest(long sender, long requesterId, string requesterName, ZDOID tombstoneId, long ownerId) { //IL_0013: 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) Player localPlayer = Player.m_localPlayer; ZDOMan instance = ZDOMan.instance; ZDO tombstone = ((instance != null) ? instance.GetZDO(tombstoneId) : null); if (!((Object)(object)localPlayer == (Object)null) && localPlayer.GetPlayerID() == ownerId && _pendingRequest == null && !IsBlocked(tombstone, requesterId)) { _pendingRequest = new PendingRequest { RequesterPlayerId = requesterId, RequesterName = SafeName(requesterName), TombstoneId = tombstoneId, CreatedAt = DateTime.UtcNow }; ShowDecisionPopup(_pendingRequest); } } private static void ShowDecisionPopup(PendingRequest request) { //IL_0062: 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_0079: Expected O, but got Unknown //IL_0079: Expected O, but got Unknown //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_007e: 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; } string text = "Allow " + request.RequesterName + " to loot this tombstone?"; UnifiedPopup.Push((PopupBase)new YesNoPopup("Tombstone access request", text, (PopupButtonCallback)delegate { Decide(request, accepted: true); }, (PopupButtonCallback)delegate { Decide(request, accepted: 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); } } private static void SendDecision(PendingRequest request, bool accepted) { //IL_0048: 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) { ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.Everybody, "IronLabs_SealedTombstone_Decision", new object[4] { request.RequesterPlayerId, request.TombstoneId, accepted, localPlayer.GetPlayerName() }); SealedTombstonePlugin.Log.LogInfo($"Tombstone request from {request.RequesterPlayerId} accepted={accepted}."); } } private static void ReceiveDecision(long sender, long requesterId, ZDOID tombstoneId, bool accepted, string ownerName) { //IL_002c: 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) { if (accepted) { Unlock(tombstoneId); } string text = (accepted ? (SafeName(ownerName) + " granted access to the tombstone.") : (SafeName(ownerName) + " denied or did not answer the request.")); ((Character)localPlayer).Message((MessageType)2, text, 0, (Sprite)null); } } private static void Unlock(ZDOID tombstoneId) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0044: 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) { return tombstone != null && RecentAttackers.Contains(tombstone.GetString("IronLabs.SealedTombstone.BlockedPlayers", ""), playerId); } private static bool IsExpired(ZDO tombstone) { long num = tombstone.GetLong("IronLabs.SealedTombstone.LockDay", -1L); long num2 = CurrentDay(); return num >= 0 && num2 >= 0 && num2 - num >= 10; } private static long CurrentDay() { return ((Object)(object)EnvMan.instance != (Object)null) ? EnvMan.instance.GetDay() : (-1); } private static bool HasExpired(PendingRequest request) { return (DateTime.UtcNow - request.CreatedAt).TotalSeconds > 30.0; } private static string SafeName(string name) { if (string.IsNullOrWhiteSpace(name)) { return "A player"; } string text = name.Replace("<", string.Empty).Replace(">", string.Empty); return (text.Length <= 64) ? text : text.Substring(0, 64); } private static void ClosePopup() { if (UnifiedPopup.IsAvailable() && UnifiedPopup.IsVisible()) { UnifiedPopup.Pop(); } } } [BepInPlugin("IronLabs.SealedTombstone", "IronLabs.SealedTombstone", "1.0.0")] public sealed class SealedTombstonePlugin : IronLabsPlugin { private const string PluginGuid = "IronLabs.SealedTombstone"; private const string PluginName = "IronLabs.SealedTombstone"; private const string PluginVersion = "1.0.0"; internal static ModLog Log { get; private set; } private void Awake() { Log = InitializePlugin("IronLabs.SealedTombstone"); Log.LogInfo("IronLabs.SealedTombstone 1.0.0 is loaded."); } private void Update() { TombstoneAccess.Tick(); } private void OnDestroy() { TombstoneAccess.ResetSession(); 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 IronLabs.SharedLib { public abstract class IronLabsPlugin : BaseUnityPlugin { private Harmony _harmony; private bool _patchesApplied; protected ModLog InitializePlugin(string pluginGuid) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Expected O, but got Unknown ModLog modLog = new ModLog(((BaseUnityPlugin)this).Logger); Version version = ((object)this).GetType().Assembly.GetName().Version; modLog.LogInfo($"AssemblyVersion: {version}."); _harmony = new Harmony(pluginGuid); PatchOwnNamespace(modLog); return modLog; } 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 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_001a: 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}"); } } }