Decompiled source of DeathNoSaveDelete v1.0.1

DeathNoSaveDelete.dll

Decompiled 6 days ago
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<string> 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 <HandleRetryRestore>d__63 : IEnumerator<object>, IEnumerator, IDisposable
	{
		private int <>1__state;

		private object <>2__current;

		object IEnumerator<object>.Current
		{
			[DebuggerHidden]
			get
			{
				return <>2__current;
			}
		}

		object IEnumerator.Current
		{
			[DebuggerHidden]
			get
			{
				return <>2__current;
			}
		}

		[DebuggerHidden]
		public <HandleRetryRestore>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 <SaveRetryStateDelayed>d__62 : IEnumerator<object>, IEnumerator, IDisposable
	{
		private int <>1__state;

		private object <>2__current;

		object IEnumerator<object>.Current
		{
			[DebuggerHidden]
			get
			{
				return <>2__current;
			}
		}

		object IEnumerator.Current
		{
			[DebuggerHidden]
			get
			{
				return <>2__current;
			}
		}

		[DebuggerHidden]
		public <SaveRetryStateDelayed>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<PlayerAvatar> 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<bool> EnableProtection;

	private static ConfigEntry<bool> ReloadAfterFailure;

	private static ConfigEntry<bool> BlockArenaPersistence;

	private static ConfigEntry<bool> VerboseLogging;

	private static ConfigEntry<float> ReloadDelaySeconds;

	private static ConfigEntry<bool> EnablePublicSaves;

	private static ConfigEntry<bool> EnablePublicGameContinueSave;

	private static ConfigEntry<bool> ShowPingHud;

	private static ConfigEntry<bool> EnableSameLevelRetry;

	private static ConfigEntry<bool> RestoreRetryHealth;

	private static ConfigEntry<bool> RestoreRetryMoney;

	private static ConfigEntry<bool> RestoreRetryEnergy;

	private static ConfigEntry<bool> 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<string>)
	}, (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<string, float> SavedItemCharges = new Dictionary<string, float>();

	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<bool>("0.总开关", "启用死亡不删档", true, "开启后死亡、团灭、失败时阻止删档和破坏性保存");
		ReloadAfterFailure = ((BaseUnityPlugin)this).Config.Bind<bool>("0.总开关", "失败后自动重新载入存档", true, "失败后等新场景生成完成,再重新载入当前存档");
		ReloadDelaySeconds = ((BaseUnityPlugin)this).Config.Bind<float>("0.总开关", "重新载入延迟秒数", 0.25f, "关卡生成完成后等待多久再重新载入存档");
		BlockArenaPersistence = ((BaseUnityPlugin)this).Config.Bind<bool>("1.保护范围", "阻止竞技场删档保存", true, "游戏失败后通常会进入竞技场/结算流程,开启后阻止该阶段删档、重置进度和覆盖保存");
		EnablePublicSaves = ((BaseUnityPlugin)this).Config.Bind<bool>("2.公开房存档", "允许公开房保存", true, "参考 PublicSaves:公开房退出到主菜单时也走正常保存,并阻止公开房删档");
		EnablePublicGameContinueSave = ((BaseUnityPlugin)this).Config.Bind<bool>("2.公开房存档", "公开房继续当前存档", true, "参考 OnlineGameTweak:创建公开房时可以使用当前选择的存档继续进度,而不是强制新档");
		ShowPingHud = ((BaseUnityPlugin)this).Config.Bind<bool>("3.联机显示", "显示实时 Ping", false, "参考 OnlineGameTweak:在游戏 HUD 上显示当前 Photon Ping");
		EnableSameLevelRetry = ((BaseUnityPlugin)this).Config.Bind<bool>("4.同关重试", "死亡后重开同一关", false, "参考 JustRetryPlus:失败时拦截原始失败切图,直接重开当前关卡。开启后会替代自动重新载入存档流程");
		RestoreRetryHealth = ((BaseUnityPlugin)this).Config.Bind<bool>("4.同关重试", "重试后恢复血量", true, "同关重试后恢复到本轮开始时记录的血量");
		RestoreRetryMoney = ((BaseUnityPlugin)this).Config.Bind<bool>("4.同关重试", "重试后恢复金钱", true, "同关重试后恢复到本轮开始时记录的金钱");
		RestoreRetryEnergy = ((BaseUnityPlugin)this).Config.Bind<bool>("4.同关重试", "重试后恢复充电站电量", true, "同关重试后恢复到本轮开始时记录的充电站电量");
		RestoreRetryItemBatteries = ((BaseUnityPlugin)this).Config.Bind<bool>("4.同关重试", "重试后恢复道具电量", true, "同关重试后恢复本轮开始时记录的道具电池电量");
		VerboseLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("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<Level>(RunLevelCurrentField, runManager);
		if ((Object)(object)val == (Object)null)
		{
			return false;
		}
		if (IsSameLevel(val, ReadField<Level>(RunLevelLobbyField, runManager)))
		{
			return false;
		}
		if (IsSameLevel(val, ReadField<Level>(RunLevelLobbyMenuField, runManager)))
		{
			return false;
		}
		if (IsSameLevel(val, ReadField<Level>(RunLevelMainMenuField, runManager)))
		{
			return false;
		}
		if (IsSameLevel(val, ReadField<Level>(RunLevelTutorialField, runManager)))
		{
			return false;
		}
		if (IsSameLevel(val, ReadField<Level>(RunLevelRecordingField, runManager)))
		{
			return false;
		}
		if (IsLevelInList(val, ReadField<List<Level>>(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<Level> 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<string>(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(<SaveRetryStateDelayed>d__62))]
	private static IEnumerator SaveRetryStateDelayed()
	{
		//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
		return new <SaveRetryStateDelayed>d__62(0);
	}

	[IteratorStateMachine(typeof(<HandleRetryRestore>d__63))]
	private static IEnumerator HandleRetryRestore()
	{
		//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
		return new <HandleRetryRestore>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<PlayerAvatar> 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<ItemBattery>();
		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<ItemBattery>();
		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<TextMeshProUGUI>();
		TMP_Text val2 = null;
		GameObject val3 = GameObject.Find("Tax Haul");
		if ((Object)(object)val3 != (Object)null)
		{
			val2 = val3.GetComponent<TMP_Text>();
		}
		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<RectTransform>();
		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<string>(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<string> 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<T>(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;
	}
}