using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Photon.Pun; using TMPro; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("DeathNoSaveDelete")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+272c8cd2fe7287a90d058372cd13fd27b710942e")] [assembly: AssemblyProduct("DeathNoSaveDelete")] [assembly: AssemblyTitle("DeathNoSaveDelete")] [assembly: AssemblyVersion("1.0.0.0")] namespace DeathNoSaveDelete; [BepInPlugin("com.hermit.deathnosavedelete", "死亡不删档", "1.1.0")] public sealed class DeathNoSaveDeletePlugin : BaseUnityPlugin { [HarmonyPatch(typeof(RunManager), "ChangeLevel")] private static class RunManagerChangeLevelPatch { private static bool Prefix(bool _levelFailed, RunManager __instance) { if (!_levelFailed) { return true; } if (!ShouldProtectFailure(__instance)) { return true; } if (EnableSameLevelRetry.Value) { return !TryStartSameLevelRetry(__instance); } BeginProtection("ChangeLevel(levelFailed=true)"); return true; } } [HarmonyPatch(typeof(RunManager), "ResetProgress")] private static class RunManagerResetProgressPatch { private static bool Prefix() { if (!EnableProtection.Value || !BlockArenaPersistence.Value || !IsRunArenaSafe()) { return true; } BeginProtection("ResetProgress in arena"); return AllowOriginalOrLog("RunManager.ResetProgress"); } } [HarmonyPatch(typeof(RoundDirector), "StartRoundLogic")] private static class RoundDirectorStartRoundLogicPatch { private static void Prefix(RoundDirector __instance) { if (!EnableSameLevelRetry.Value || (Object)(object)__instance == (Object)null) { return; } if (_retryInProgress) { if (VerboseLogging.Value) { Log.LogInfo((object)"同关重试进行中,跳过本次状态记录。"); } } else { ((MonoBehaviour)__instance).StartCoroutine(SaveRetryStateDelayed()); } } private static void Postfix(RoundDirector __instance) { if (EnableSameLevelRetry.Value && _retryInProgress && !((Object)(object)__instance == (Object)null)) { ((MonoBehaviour)__instance).StartCoroutine(HandleRetryRestore()); } } } [HarmonyPatch(typeof(MenuPageServerListCreateNew), "ButtonConfirm")] private static class MenuPageServerListCreateNewButtonConfirmPatch { private static bool Prefix(MenuPageServerListCreateNew __instance) { return !TryHandlePublicGameHost(__instance); } } [HarmonyPatch(typeof(SemiFunc), "MenuActionHostGame")] private static class SemiFuncMenuActionHostGamePatch { private static bool Prefix(ref string saveFileName, ref List saveFileBackups) { return !TryContinuePublicGame(ref saveFileName, ref saveFileBackups); } } [HarmonyPatch(typeof(SemiFunc), "SaveFileSave")] private static class SemiFuncSaveFileSavePatch { private static bool Prefix() { if (ShouldBlockDangerousPersistence()) { if (VerboseLogging.Value) { Log.LogInfo((object)"已拦截 SemiFunc.SaveFileSave"); } return false; } if (!EnablePublicSaves.Value || !IsPublicLobbySafe()) { return true; } if ((Object)(object)StatsManager.instance != (Object)null) { StatsManager.instance.SaveFileSave(); } return false; } } [HarmonyPatch(typeof(SemiFunc), "SaveFileDelete")] private static class SemiFuncSaveFileDeletePatch { private static bool Prefix(string saveFileName) { if (ShouldBlockDangerousPersistence()) { if (VerboseLogging.Value) { Log.LogInfo((object)("已拦截 SemiFunc.SaveFileDelete(" + saveFileName + ")")); } return false; } if (EnablePublicSaves.Value && IsPublicLobbySafe()) { if (VerboseLogging.Value) { Log.LogInfo((object)("公开房已阻止删档: " + saveFileName)); } return false; } return true; } } [HarmonyPatch(typeof(DataDirector), "SaveDeleteCheck")] private static class DataDirectorSaveDeleteCheckPatch { private static bool Prefix() { return AllowOriginalOrLog("DataDirector.SaveDeleteCheck"); } } [HarmonyPatch(typeof(StatsManager), "SaveFileDelete")] private static class StatsManagerSaveFileDeletePatch { private static bool Prefix(string saveFileName) { return AllowOriginalOrLog("StatsManager.SaveFileDelete(" + saveFileName + ")"); } } [HarmonyPatch(typeof(StatsManager), "SaveGame")] private static class StatsManagerSaveGamePatch { private static bool Prefix(string fileName) { return AllowOriginalOrLog("StatsManager.SaveGame(" + fileName + ")"); } } [HarmonyPatch(typeof(StatsManager), "SaveFileSave")] private static class StatsManagerSaveFileSavePatch { private static bool Prefix() { return AllowOriginalOrLog("StatsManager.SaveFileSave"); } } [CompilerGenerated] private sealed class d__63 : IEnumerator, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__63(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>2__current = (object)new WaitForSeconds(2f); <>1__state = 1; return true; case 1: <>1__state = -1; try { RestoreRetryState(); Log.LogInfo((object)"同关重试状态已恢复。"); } catch (Exception ex) { Log.LogWarning((object)("恢复同关重试状态失败: " + ex.Message)); } finally { _retryInProgress = false; ClearProtectionState(startCooldown: true); } return false; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } [CompilerGenerated] private sealed class d__62 : IEnumerator, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public d__62(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: <>1__state = -1; <>2__current = (object)new WaitForSeconds(1.5f); <>1__state = 1; return true; case 1: <>1__state = -1; if (!EnableSameLevelRetry.Value || _retryInProgress) { return false; } try { List list = SemiFunc.PlayerGetAll(); if (list == null || list.Count == 0) { return false; } PlayerHealth playerHealth = list[0].playerHealth; _savedHealth = ReadIntField(AccessTools.Field(typeof(PlayerHealth), "health"), playerHealth, _savedHealth); _savedMoney = GetRunMoney(); SaveChargingStationState(); SaveItemBatteryCharges(); _hasRetrySnapshot = true; if (VerboseLogging.Value) { Log.LogInfo((object)$"已记录重试状态: 血量={_savedHealth}, 金钱={_savedMoney}, 充电站电量={_savedChargeTotal}, 道具电池={SavedItemCharges.Count}"); } } catch (Exception ex) { Log.LogWarning((object)("记录同关重试状态失败: " + ex.Message)); } return false; } } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } private const string PluginGuid = "com.hermit.deathnosavedelete"; private const float ProtectionRearmCooldownSeconds = 2f; private static ManualLogSource Log; private static ConfigEntry EnableProtection; private static ConfigEntry ReloadAfterFailure; private static ConfigEntry BlockArenaPersistence; private static ConfigEntry VerboseLogging; private static ConfigEntry ReloadDelaySeconds; private static ConfigEntry EnablePublicSaves; private static ConfigEntry EnablePublicGameContinueSave; private static ConfigEntry ShowPingHud; private static ConfigEntry EnableSameLevelRetry; private static ConfigEntry RestoreRetryHealth; private static ConfigEntry RestoreRetryMoney; private static ConfigEntry RestoreRetryEnergy; private static ConfigEntry RestoreRetryItemBatteries; private static readonly FieldInfo StatsSaveFileCurrentField = AccessTools.Field(typeof(StatsManager), "saveFileCurrent"); private static readonly MethodInfo StatsLoadGameMethod = AccessTools.Method(typeof(StatsManager), "LoadGame", new Type[2] { typeof(string), typeof(List) }, (Type[])null); private static readonly FieldInfo LevelGeneratorInstanceField = AccessTools.Field(typeof(LevelGenerator), "Instance"); private static readonly FieldInfo LevelGeneratorGeneratedField = AccessTools.Field(typeof(LevelGenerator), "Generated"); private static readonly FieldInfo RunLevelCurrentField = AccessTools.Field(typeof(RunManager), "levelCurrent"); private static readonly FieldInfo RunLevelLobbyField = AccessTools.Field(typeof(RunManager), "levelLobby"); private static readonly FieldInfo RunLevelLobbyMenuField = AccessTools.Field(typeof(RunManager), "levelLobbyMenu"); private static readonly FieldInfo RunLevelMainMenuField = AccessTools.Field(typeof(RunManager), "levelMainMenu"); private static readonly FieldInfo RunLevelTutorialField = AccessTools.Field(typeof(RunManager), "levelTutorial"); private static readonly FieldInfo RunLevelRecordingField = AccessTools.Field(typeof(RunManager), "levelRecording"); private static readonly FieldInfo RunLevelArenaField = AccessTools.Field(typeof(RunManager), "levelArena"); private static readonly FieldInfo RunWaitToChangeSceneField = AccessTools.Field(typeof(RunManager), "waitToChangeScene"); private static readonly FieldInfo RunLobbyJoinField = AccessTools.Field(typeof(RunManager), "lobbyJoin"); private static readonly FieldInfo DataNetworkServerNameField = AccessTools.Field(typeof(DataDirector), "networkServerName"); private static readonly FieldInfo MenuTextInputTextCurrentField = AccessTools.Field(typeof(MenuTextInput), "textCurrent"); private static bool _blockSaves; private static bool _needsReload; private static float _reloadTimer; private static float _ignoreProtectionUntil; private static string _activeProtectionReason; private static bool _hostedPublicGame; private static bool _retryInProgress; private static int _savedChargeTotal = -1; private static int _savedHealth = -1; private static int _savedMoney; private static bool _hasRetrySnapshot; private static readonly Dictionary SavedItemCharges = new Dictionary(); private static GameObject _pingHudObject; private static TextMeshProUGUI _pingText; private static DeathNoSaveDeletePlugin _instance; private Harmony _harmony; private void Awake() { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; _instance = this; BindConfig(); _harmony = new Harmony("com.hermit.deathnosavedelete"); _harmony.PatchAll(); Log.LogInfo((object)"死亡不删档 1.1.0 已加载。已融合公开房存档、公开房继续存档、同关重试和 Ping 显示功能。"); } private void BindConfig() { EnableProtection = ((BaseUnityPlugin)this).Config.Bind("0.总开关", "启用死亡不删档", true, "开启后死亡、团灭、失败时阻止删档和破坏性保存"); ReloadAfterFailure = ((BaseUnityPlugin)this).Config.Bind("0.总开关", "失败后自动重新载入存档", true, "失败后等新场景生成完成,再重新载入当前存档"); ReloadDelaySeconds = ((BaseUnityPlugin)this).Config.Bind("0.总开关", "重新载入延迟秒数", 0.25f, "关卡生成完成后等待多久再重新载入存档"); BlockArenaPersistence = ((BaseUnityPlugin)this).Config.Bind("1.保护范围", "阻止竞技场删档保存", true, "游戏失败后通常会进入竞技场/结算流程,开启后阻止该阶段删档、重置进度和覆盖保存"); EnablePublicSaves = ((BaseUnityPlugin)this).Config.Bind("2.公开房存档", "允许公开房保存", true, "参考 PublicSaves:公开房退出到主菜单时也走正常保存,并阻止公开房删档"); EnablePublicGameContinueSave = ((BaseUnityPlugin)this).Config.Bind("2.公开房存档", "公开房继续当前存档", true, "参考 OnlineGameTweak:创建公开房时可以使用当前选择的存档继续进度,而不是强制新档"); ShowPingHud = ((BaseUnityPlugin)this).Config.Bind("3.联机显示", "显示实时 Ping", false, "参考 OnlineGameTweak:在游戏 HUD 上显示当前 Photon Ping"); EnableSameLevelRetry = ((BaseUnityPlugin)this).Config.Bind("4.同关重试", "死亡后重开同一关", false, "参考 JustRetryPlus:失败时拦截原始失败切图,直接重开当前关卡。开启后会替代自动重新载入存档流程"); RestoreRetryHealth = ((BaseUnityPlugin)this).Config.Bind("4.同关重试", "重试后恢复血量", true, "同关重试后恢复到本轮开始时记录的血量"); RestoreRetryMoney = ((BaseUnityPlugin)this).Config.Bind("4.同关重试", "重试后恢复金钱", true, "同关重试后恢复到本轮开始时记录的金钱"); RestoreRetryEnergy = ((BaseUnityPlugin)this).Config.Bind("4.同关重试", "重试后恢复充电站电量", true, "同关重试后恢复到本轮开始时记录的充电站电量"); RestoreRetryItemBatteries = ((BaseUnityPlugin)this).Config.Bind("4.同关重试", "重试后恢复道具电量", true, "同关重试后恢复本轮开始时记录的道具电池电量"); VerboseLogging = ((BaseUnityPlugin)this).Config.Bind("9.调试", "显示详细日志", false, "开启后在 BepInEx 控制台打印被拦截的方法和联动功能细节"); } private void Update() { UpdatePingHud(); if (!EnableProtection.Value) { ClearProtectionState(startCooldown: false); _ignoreProtectionUntil = 0f; } else if (!_needsReload) { ClearProtectionAfterGeneratedWhenReloadDisabled(); } else if (!ReloadAfterFailure.Value) { ClearProtectionAfterGeneratedWhenReloadDisabled(); } else if (IsLevelGenerated()) { _reloadTimer += Time.deltaTime; if (!(_reloadTimer < Mathf.Max(0.01f, ReloadDelaySeconds.Value))) { TryReloadCurrentSave(); } } } private static void ClearProtectionAfterGeneratedWhenReloadDisabled() { if (!_retryInProgress && _blockSaves && !ReloadAfterFailure.Value && IsLevelGenerated()) { _reloadTimer += Time.deltaTime; if (!(_reloadTimer < Mathf.Max(0.01f, ReloadDelaySeconds.Value))) { ClearProtectionState(startCooldown: true); Log.LogInfo((object)"死亡不删档临时保存拦截已解除。"); } } } private void OnDestroy() { Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } if ((Object)(object)_pingHudObject != (Object)null) { Object.Destroy((Object)(object)_pingHudObject); } } private static void BeginProtection(string reason) { if (!EnableProtection.Value) { return; } if (Time.unscaledTime < _ignoreProtectionUntil) { if (VerboseLogging.Value) { Log.LogInfo((object)("忽略重载后的重复失败流程: " + reason)); } } else if (_blockSaves || _needsReload) { if (VerboseLogging.Value && !string.Equals(_activeProtectionReason, reason, StringComparison.Ordinal)) { Log.LogInfo((object)("死亡不删档保护已在执行中,忽略重复流程: " + reason)); } } else { _blockSaves = true; _needsReload = ReloadAfterFailure.Value; _reloadTimer = 0f; _activeProtectionReason = reason; Log.LogInfo((object)("检测到失败流程,已启用死亡不删档保护: " + reason)); } } private static void ClearProtectionState(bool startCooldown) { _blockSaves = false; _needsReload = false; _reloadTimer = 0f; _activeProtectionReason = null; if (startCooldown) { _ignoreProtectionUntil = Time.unscaledTime + 2f; } } private static bool TryStartSameLevelRetry(RunManager runManager) { if (!EnableProtection.Value || (Object)(object)runManager == (Object)null) { return false; } if (Time.unscaledTime < _ignoreProtectionUntil) { return true; } if (_retryInProgress) { return true; } _blockSaves = true; _needsReload = false; _reloadTimer = 0f; _activeProtectionReason = "同关重试"; _retryInProgress = true; try { Log.LogInfo((object)"检测到失败流程,已启用同关重试保护。"); runManager.RestartScene(); return true; } catch (Exception ex) { _retryInProgress = false; _blockSaves = false; _activeProtectionReason = null; BeginProtection("同关重试失败,回退到存档保护"); Log.LogWarning((object)("同关重试启动失败,已回退到死亡不删档保护: " + ex.Message)); return false; } } private static bool ShouldBlockDangerousPersistence() { if (!EnableProtection.Value) { return false; } if (_blockSaves) { return true; } if (BlockArenaPersistence.Value) { return IsRunArenaSafe(); } return false; } private static bool ShouldProtectFailure(RunManager runManager) { if ((Object)(object)runManager == (Object)null) { return false; } Level val = ReadField(RunLevelCurrentField, runManager); if ((Object)(object)val == (Object)null) { return false; } if (IsSameLevel(val, ReadField(RunLevelLobbyField, runManager))) { return false; } if (IsSameLevel(val, ReadField(RunLevelLobbyMenuField, runManager))) { return false; } if (IsSameLevel(val, ReadField(RunLevelMainMenuField, runManager))) { return false; } if (IsSameLevel(val, ReadField(RunLevelTutorialField, runManager))) { return false; } if (IsSameLevel(val, ReadField(RunLevelRecordingField, runManager))) { return false; } if (IsLevelInList(val, ReadField>(RunLevelArenaField, runManager))) { return false; } try { if (SemiFunc.IsLevelShop(val)) { return false; } } catch { } return true; } private static bool IsSameLevel(Level left, Level right) { if ((Object)(object)left == (Object)null || (Object)(object)right == (Object)null) { return false; } return (Object)(object)left == (Object)(object)right; } private static bool IsLevelInList(Level level, List levels) { if ((Object)(object)level == (Object)null || levels == null) { return false; } for (int i = 0; i < levels.Count; i++) { if (IsSameLevel(level, levels[i])) { return true; } } return false; } private static bool IsRunArenaSafe() { try { return SemiFunc.RunIsArena(); } catch { return false; } } private static bool IsLevelGenerated() { object obj = ReadStaticField(LevelGeneratorInstanceField); if (obj == null) { return false; } return ReadBool(LevelGeneratorGeneratedField, obj); } private static void TryReloadCurrentSave() { StatsManager instance = StatsManager.instance; if ((Object)(object)instance == (Object)null) { return; } string text = ReadField(StatsSaveFileCurrentField, instance); if (string.IsNullOrWhiteSpace(text)) { Log.LogWarning((object)"无法重新载入存档:当前存档名为空。"); ClearProtectionState(startCooldown: true); return; } if (StatsLoadGameMethod == null) { Log.LogError((object)"无法重新载入存档:找不到 StatsManager.LoadGame。"); return; } try { _blockSaves = false; _ignoreProtectionUntil = Time.unscaledTime + 2f; StatsLoadGameMethod.Invoke(instance, new object[2] { text, null }); _needsReload = false; _reloadTimer = 0f; _activeProtectionReason = null; Log.LogInfo((object)("已重新载入存档: " + text)); } catch (Exception ex) { _blockSaves = true; _reloadTimer = 0f; Log.LogWarning((object)("重新载入存档失败,将稍后重试: " + ex.Message)); } } [IteratorStateMachine(typeof(d__62))] private static IEnumerator SaveRetryStateDelayed() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__62(0); } [IteratorStateMachine(typeof(d__63))] private static IEnumerator HandleRetryRestore() { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new d__63(0); } private static void RestoreRetryState() { if (!_hasRetrySnapshot) { Log.LogWarning((object)"没有可用的同关重试状态快照,本次只重开关卡,不恢复血量、金钱和电量。"); return; } if (RestoreRetryHealth.Value) { RestorePlayerHealth(); } if (RestoreRetryMoney.Value) { SetRunMoney(_savedMoney); } if (RestoreRetryEnergy.Value) { RestoreChargingStationState(); } if (RestoreRetryItemBatteries.Value) { RestoreItemBatteryCharges(); } } private static void SaveChargingStationState() { ChargingStation instance = ChargingStation.instance; if (!((Object)(object)instance == (Object)null)) { _savedChargeTotal = ReadIntField(AccessTools.Field(typeof(ChargingStation), "chargeTotal"), instance, _savedChargeTotal); } } private static void RestoreChargingStationState() { if (_savedChargeTotal >= 0) { StatsManager instance = StatsManager.instance; if ((Object)(object)instance != (Object)null && instance.runStats != null) { instance.runStats["chargingStationChargeTotal"] = _savedChargeTotal; } ChargingStation instance2 = ChargingStation.instance; if (!((Object)(object)instance2 == (Object)null)) { SetFieldValue(AccessTools.Field(typeof(ChargingStation), "chargeTotal"), instance2, _savedChargeTotal); SetFieldValue(AccessTools.Field(typeof(ChargingStation), "chargeFloat"), instance2, (float)_savedChargeTotal / 100f); SetFieldValue(AccessTools.Field(typeof(ChargingStation), "chargeSegmentCurrent"), instance2, Mathf.RoundToInt((float)_savedChargeTotal / 100f * 40f)); } } } private static int GetRunMoney() { StatsManager instance = StatsManager.instance; if ((Object)(object)instance == (Object)null || instance.runStats == null) { return 0; } if (!instance.runStats.TryGetValue("currency", out var value)) { return 0; } return value; } private static void SetRunMoney(int value) { StatsManager instance = StatsManager.instance; if (!((Object)(object)instance == (Object)null) && instance.runStats != null) { instance.runStats["currency"] = value; } } private static void RestorePlayerHealth() { if (_savedHealth <= 0) { return; } List list = SemiFunc.PlayerGetAll(); if (list == null) { return; } FieldInfo field = AccessTools.Field(typeof(PlayerHealth), "health"); FieldInfo field2 = AccessTools.Field(typeof(PlayerHealth), "maxHealth"); foreach (PlayerAvatar item in list) { PlayerHealth val = item?.playerHealth; if (!((Object)(object)val == (Object)null)) { int num = ReadIntField(field2, val, _savedHealth); SetFieldValue(field, val, Mathf.Clamp(_savedHealth, 1, Mathf.Max(1, num))); } } } private static void SaveItemBatteryCharges() { SavedItemCharges.Clear(); ItemBattery[] array = Object.FindObjectsOfType(); foreach (ItemBattery val in array) { FieldInfo fieldInfo = AccessTools.Field(((object)val).GetType(), "batteryLife"); if (!(fieldInfo == null)) { string batteryKey = GetBatteryKey(val); SavedItemCharges[batteryKey] = ReadFloatField(fieldInfo, val, 0f); } } } private static void RestoreItemBatteryCharges() { if (SavedItemCharges.Count == 0) { return; } ItemBattery[] array = Object.FindObjectsOfType(); foreach (ItemBattery val in array) { FieldInfo fieldInfo = AccessTools.Field(((object)val).GetType(), "batteryLife"); if (!(fieldInfo == null)) { string batteryKey = GetBatteryKey(val); if (SavedItemCharges.TryGetValue(batteryKey, out var value)) { SetFieldValue(fieldInfo, val, value); } } } } private static string GetBatteryKey(ItemBattery battery) { //IL_0018: Unknown result type (might be due to invalid IL or missing references) Transform transform = ((Component)battery).transform; return $"{((Object)((Component)battery).gameObject).name}_{transform.position}"; } private static void UpdatePingHud() { if (!ShowPingHud.Value || !SafeRunIsLevel()) { if ((Object)(object)_pingHudObject != (Object)null) { _pingHudObject.SetActive(false); } } else if (EnsurePingHud()) { int ping = PhotonNetwork.GetPing(); ((TMP_Text)_pingText).SetText($"Ping {ping}", true); _pingHudObject.SetActive(true); } } private static bool EnsurePingHud() { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Expected O, but got Unknown //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_0109: 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_0133: Unknown result type (might be due to invalid IL or missing references) //IL_0147: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_pingHudObject != (Object)null && (Object)(object)_pingText != (Object)null) { return true; } GameObject val = GameObject.Find("Game Hud"); if ((Object)(object)val == (Object)null) { return false; } _pingHudObject = new GameObject("DeathNoSaveDelete Ping HUD"); _pingHudObject.SetActive(false); _pingHudObject.transform.SetParent(val.transform, false); _pingText = _pingHudObject.AddComponent(); TMP_Text val2 = null; GameObject val3 = GameObject.Find("Tax Haul"); if ((Object)(object)val3 != (Object)null) { val2 = val3.GetComponent(); } if ((Object)(object)val2 != (Object)null) { ((TMP_Text)_pingText).font = val2.font; } ((Graphic)_pingText).color = Color.cyan; ((TMP_Text)_pingText).fontSize = 24f; ((TMP_Text)_pingText).enableWordWrapping = false; ((TMP_Text)_pingText).alignment = (TextAlignmentOptions)260; RectTransform component = _pingHudObject.GetComponent(); component.pivot = new Vector2(1f, 1f); component.anchorMin = new Vector2(1f, 0f); component.anchorMax = new Vector2(1f, 0f); component.anchoredPosition = new Vector2(-24f, 225f); component.sizeDelta = new Vector2(260f, 40f); return true; } private static bool SafeRunIsLevel() { try { return SemiFunc.RunIsLevel(); } catch { return false; } } private static bool TryHandlePublicGameHost(MenuPageServerListCreateNew page) { if (!EnablePublicGameContinueSave.Value || (Object)(object)page == (Object)null) { return false; } _hostedPublicGame = true; string value = ReadField(MenuTextInputTextCurrentField, page.menuTextInput) ?? string.Empty; SetFieldValue(DataNetworkServerNameField, DataDirector.instance, value); SemiFunc.MainMenuSetMultiplayer(); MenuManager.instance.PageCloseAll(); MenuManager.instance.PageOpen((MenuPageIndex)11, false); Log.LogInfo((object)"已进入公开房存档选择流程。"); return true; } private static bool TryContinuePublicGame(ref string saveFileName, ref List saveFileBackups) { if (!EnablePublicGameContinueSave.Value || !_hostedPublicGame) { return false; } _hostedPublicGame = false; RunManager.instance.ResetProgress(); if (!string.IsNullOrWhiteSpace(saveFileName)) { Log.LogInfo((object)("公开房继续存档: " + saveFileName)); SemiFunc.SaveFileLoad(saveFileName, saveFileBackups); } else { Log.LogInfo((object)"公开房创建新存档。"); SemiFunc.SaveFileCreate(); } GameManager.instance.localTest = false; SetFieldValue(RunWaitToChangeSceneField, RunManager.instance, true); RunManager.instance.ChangeLevel(true, false, (ChangeLevelType)3); GameManager.instance.SetConnectRandom(true); SetFieldValue(RunLobbyJoinField, RunManager.instance, true); return true; } private static bool IsPublicLobbySafe() { if (TryGetSingletonBool("GameManager", "instance", out var value, "connectRandom", "isConnectRandom", "quickJoin", "openLobby")) { return value; } if (TryGetStaticOrSingletonBool("NetworkConnect", out var value2, "connectRandom", "isConnectRandom")) { return value2; } if (TryGetStaticOrSingletonBool("NetworkController", out var value3, "connectRandom", "isConnectRandom")) { return value3; } return false; } private static int ReadIntField(FieldInfo field, object instance, int fallback) { if (field == null || instance == null) { return fallback; } try { return (field.GetValue(instance) is int num) ? num : fallback; } catch { return fallback; } } private static float ReadFloatField(FieldInfo field, object instance, float fallback) { if (field == null || instance == null) { return fallback; } try { return (field.GetValue(instance) is float num) ? num : fallback; } catch { return fallback; } } private static void SetFieldValue(FieldInfo field, object instance, object value) { if (field == null || instance == null) { return; } try { field.SetValue(instance, value); } catch (Exception ex) { if (VerboseLogging.Value) { Log.LogWarning((object)("写入字段失败 " + field.Name + ": " + ex.Message)); } } } private static bool TryGetSingletonBool(string typeName, string instanceMember, out bool value, params string[] candidates) { value = false; Type type = AccessTools.TypeByName(typeName); if (type == null) { return false; } object obj = null; FieldInfo fieldInfo = AccessTools.Field(type, instanceMember); if (fieldInfo != null) { obj = fieldInfo.GetValue(null); } if (obj == null) { PropertyInfo propertyInfo = AccessTools.Property(type, instanceMember); if (propertyInfo != null) { obj = propertyInfo.GetValue(null, null); } } if (obj == null) { return false; } foreach (string name in candidates) { if (TryGetBoolMember(obj, name, out value)) { return true; } } return false; } private static bool TryGetStaticOrSingletonBool(string typeName, out bool value, params string[] candidates) { value = false; Type type = AccessTools.TypeByName(typeName); if (type == null) { return false; } string[] array = candidates; foreach (string name in array) { if (TryGetStaticBoolMember(type, name, out value)) { return true; } } array = new string[3] { "Instance", "instance", "s_instance" }; foreach (string instanceMember in array) { if (TryGetSingletonBool(typeName, instanceMember, out value, candidates)) { return true; } } return false; } private static bool TryGetBoolMember(object instance, string name, out bool value) { value = false; if (instance == null) { return false; } Type type = instance.GetType(); FieldInfo fieldInfo = AccessTools.Field(type, name); if (fieldInfo != null && fieldInfo.FieldType == typeof(bool)) { value = (bool)fieldInfo.GetValue(instance); return true; } PropertyInfo propertyInfo = AccessTools.Property(type, name); if (propertyInfo != null && propertyInfo.PropertyType == typeof(bool)) { value = (bool)propertyInfo.GetValue(instance, null); return true; } return false; } private static bool TryGetStaticBoolMember(Type type, string name, out bool value) { value = false; FieldInfo fieldInfo = AccessTools.Field(type, name); if (fieldInfo != null && fieldInfo.IsStatic && fieldInfo.FieldType == typeof(bool)) { value = (bool)fieldInfo.GetValue(null); return true; } PropertyInfo propertyInfo = AccessTools.Property(type, name); if (propertyInfo != null && propertyInfo.GetMethod != null && propertyInfo.GetMethod.IsStatic && propertyInfo.PropertyType == typeof(bool)) { value = (bool)propertyInfo.GetValue(null, null); return true; } return false; } private static T ReadField(FieldInfo field, object instance) where T : class { if (field == null || instance == null) { return null; } try { return field.GetValue(instance) as T; } catch { return null; } } private static object ReadStaticField(FieldInfo field) { if (field == null) { return null; } try { return field.GetValue(null); } catch { return null; } } private static bool ReadBool(FieldInfo field, object instance) { if (field == null || instance == null) { return false; } try { object value = field.GetValue(instance); bool flag = default(bool); int num; if (value is bool) { flag = (bool)value; num = 1; } else { num = 0; } return (byte)((uint)num & (flag ? 1u : 0u)) != 0; } catch { return false; } } private static bool AllowOriginalOrLog(string methodName) { bool num = ShouldBlockDangerousPersistence(); if (num && VerboseLogging.Value) { Log.LogInfo((object)("已拦截 " + methodName)); } return !num; } }