Decompiled source of OrbExplosionStartLevel v0.1.2

plugins/OrbExplosionStartLevel/OrbExplosionStartLevel.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using ExitGames.Client.Photon;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using Photon.Realtime;
using UnityEngine;

[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("saya")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.2.0")]
[assembly: AssemblyInformationalVersion("0.1.2")]
[assembly: AssemblyProduct("OrbExplosionStartLevel")]
[assembly: AssemblyTitle("OrbExplosionStartLevel")]
[assembly: AssemblyVersion("0.1.2.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace OrbExplosionStartLevel
{
	internal sealed class HostStartLevel
	{
		internal const string PropertyKey = "saya.OrbExplosionStartLevel.StartLevel.v2";

		internal const string GenerationKey = "saya.OrbExplosionStartLevel.Generation.v2";

		private object? _room;

		private float _nextPublishAt;

		private int _configuredAtLoad;

		internal int? Value { get; private set; }

		internal string? Generation { get; private set; }

		internal bool HasRoom => _room != null;

		internal bool SetRoom(object? room)
		{
			if (_room == room)
			{
				return false;
			}
			_room = room;
			Value = null;
			Generation = null;
			_nextPublishAt = float.NegativeInfinity;
			return true;
		}

		internal void BeginLoad(int configuredStartLevel)
		{
			_configuredAtLoad = configuredStartLevel;
			Value = null;
			Generation = null;
			_nextPublishAt = float.NegativeInfinity;
		}

		internal void ConfirmGeneration(string generation)
		{
			if (Generation == null)
			{
				string text = (Generation = generation);
			}
		}

		internal int? Refresh(bool isHost, object? receivedGeneration, object? receivedValue, float now)
		{
			if (_room == null || Generation == null || Value.HasValue)
			{
				return null;
			}
			if (receivedGeneration is string text && text == Generation && receivedValue is int num && num >= 1 && num <= 1000)
			{
				Value = num;
				return null;
			}
			if (!isHost || now < _nextPublishAt)
			{
				return null;
			}
			_nextPublishAt = now + 3f;
			return _configuredAtLoad;
		}
	}
	[BepInPlugin("saya.OrbExplosionStartLevel", "Orb Explosion Start Level", "0.1.2")]
	public sealed class Plugin : BaseUnityPlugin
	{
		[HarmonyPatch(typeof(LevelGenerator))]
		private static class LevelPatch
		{
			[HarmonyPatch("Start")]
			[HarmonyPrefix]
			private static void BeforeStart(LevelGenerator __instance)
			{
				Plugin instance = _instance;
				if (instance != null && Object.op_Implicit((Object)(object)instance) && Object.op_Implicit((Object)(object)GameManager.instance) && Object.op_Implicit((Object)(object)RunManager.instance))
				{
					instance.BeginLevel(__instance);
				}
			}

			[HarmonyPatch("GenerateDone")]
			[HarmonyPrefix]
			private static void BeforeGenerateDone(LevelGenerator __instance, PhotonMessageInfo _info)
			{
				//IL_0056: Unknown result type (might be due to invalid IL or missing references)
				//IL_0031: Unknown result type (might be due to invalid IL or missing references)
				//IL_0039: Unknown result type (might be due to invalid IL or missing references)
				//IL_0046: Unknown result type (might be due to invalid IL or missing references)
				Plugin instance = _instance;
				if (instance != null && Object.op_Implicit((Object)(object)instance) && Object.op_Implicit((Object)(object)GameManager.instance) && Object.op_Implicit((Object)(object)RunManager.instance) && (!SemiFunc.IsMultiplayer() || (_info.Sender != null && _info.Sender == PhotonNetwork.MasterClient && Object.op_Implicit((Object)(object)_info.photonView))))
				{
					instance.GenerationDone(__instance, _info);
				}
			}
		}

		[HarmonyPatch(typeof(EnemyValuable))]
		private static class OrbPatch
		{
			[HarmonyPatch("Start")]
			[HarmonyPostfix]
			private static void Postfix(EnemyValuable __instance)
			{
				Plugin instance = _instance;
				if (instance != null && Object.op_Implicit((Object)(object)instance) && Object.op_Implicit((Object)(object)RunManager.instance))
				{
					instance.InitializeOrb(__instance);
				}
			}

			[HarmonyPatch("DestroyImpulse")]
			[HarmonyPrefix]
			private static void BeforeDestroyImpulse(EnemyValuable __instance)
			{
				Plugin instance = _instance;
				if (instance != null && Object.op_Implicit((Object)(object)instance))
				{
					instance.BeforeDestroy(__instance);
				}
			}
		}

		public const string PluginGuid = "saya.OrbExplosionStartLevel";

		public const string PluginName = "Orb Explosion Start Level";

		public const string PluginVersion = "0.1.2";

		private static readonly FieldRef<EnemyValuable, bool> HasExplosion = AccessTools.FieldRefAccess<EnemyValuable, bool>("hasExplosion");

		private static readonly FieldRef<RunManager, int> LevelsCompleted = AccessTools.FieldRefAccess<RunManager, int>("levelsCompleted");

		private readonly Harmony _harmony = new Harmony("saya.OrbExplosionStartLevel");

		private static Plugin? _instance;

		private ConfigEntry<int> _startLevel;

		private int _levelStartLevel;

		private LevelGenerator? _levelGenerator;

		private readonly HostStartLevel _hostStartLevel = new HostStartLevel();

		private readonly List<(EnemyValuable Orb, int Level)> _pendingOrbs = new List<(EnemyValuable, int)>();

		private float _nextRefresh;

		private bool _loggedWaiting;

		private void Awake()
		{
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Expected O, but got Unknown
			((Component)this).transform.SetParent((Transform)null);
			((Object)((Component)this).gameObject).hideFlags = (HideFlags)61;
			Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject);
			_startLevel = ((BaseUnityPlugin)this).Config.Bind<int>("General", "StartLevel", 20, new ConfigDescription("First displayed level where enemy orbs explode. The host's setting is captured at each level load and shared with clients. Changes during play apply at the next level load; no restart required. All players need the same mod version. Default 20 matches vanilla.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 1000), Array.Empty<object>()));
			_instance = this;
			_harmony.PatchAll(typeof(OrbPatch));
			_harmony.PatchAll(typeof(LevelPatch));
			((BaseUnityPlugin)this).Logger.LogInfo((object)$"Loaded. Configured StartLevel={_startLevel.Value}; applied at the next level load using the host's setting.");
		}

		private void BeginLevel(LevelGenerator generator)
		{
			if (_levelGenerator == generator)
			{
				return;
			}
			RefreshRoom();
			_levelGenerator = generator;
			_pendingOrbs.Clear();
			_loggedWaiting = false;
			if (!SemiFunc.IsMultiplayer() || PhotonNetwork.IsMasterClient)
			{
				try
				{
					((BaseUnityPlugin)this).Config.Reload();
				}
				catch (Exception ex)
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)("Could not reload configuration; using the in-memory setting: " + ex.Message));
				}
			}
			_levelStartLevel = _startLevel.Value;
			_hostStartLevel.BeginLoad(_levelStartLevel);
			((BaseUnityPlugin)this).Logger.LogInfo((object)$"Level load started: configured StartLevel={_levelStartLevel}; multiplayer clients wait for the host.");
		}

		private void GenerationDone(LevelGenerator generator, PhotonMessageInfo info)
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			BeginLevel(generator);
			RefreshRoom();
			if (SemiFunc.IsMultiplayer())
			{
				string generation = FormattableString.Invariant($"{info.Sender.ActorNumber}:{info.photonView.ViewID}:{((PhotonMessageInfo)(ref info)).SentServerTimestamp}");
				_hostStartLevel.ConfirmGeneration(generation);
				RefreshRoom();
			}
		}

		private void Update()
		{
			if (Time.unscaledTime < _nextRefresh)
			{
				return;
			}
			_nextRefresh = Time.unscaledTime + 0.25f;
			RefreshRoom();
			if (!TryGetStartLevel(out var level))
			{
				_pendingOrbs.RemoveAll(((EnemyValuable Orb, int Level) entry) => !Object.op_Implicit((Object)(object)entry.Orb));
				return;
			}
			foreach (var pendingOrb in _pendingOrbs)
			{
				if (Object.op_Implicit((Object)(object)pendingOrb.Orb))
				{
					Apply(pendingOrb.Orb, pendingOrb.Level, level);
				}
			}
			_pendingOrbs.Clear();
		}

		private void RefreshRoom()
		{
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ea: Expected O, but got Unknown
			Room val = ((Object.op_Implicit((Object)(object)GameManager.instance) && SemiFunc.IsMultiplayer() && PhotonNetwork.InRoom) ? PhotonNetwork.CurrentRoom : null);
			bool hasRoom = _hostStartLevel.HasRoom;
			if (_hostStartLevel.SetRoom(val))
			{
				if (hasRoom)
				{
					_pendingOrbs.Clear();
				}
				_loggedWaiting = false;
			}
			if (val != null)
			{
				bool hasValue = _hostStartLevel.Value.HasValue;
				int? num = _hostStartLevel.Refresh(PhotonNetwork.IsMasterClient, ((RoomInfo)val).CustomProperties[(object)"saya.OrbExplosionStartLevel.Generation.v2"], ((RoomInfo)val).CustomProperties[(object)"saya.OrbExplosionStartLevel.StartLevel.v2"], Time.unscaledTime);
				if (num.HasValue)
				{
					bool flag = val.SetCustomProperties(new Hashtable
					{
						[(object)"saya.OrbExplosionStartLevel.Generation.v2"] = _hostStartLevel.Generation,
						[(object)"saya.OrbExplosionStartLevel.StartLevel.v2"] = num.Value
					}, (Hashtable)null, (WebFlags)null);
					((BaseUnityPlugin)this).Logger.LogInfo((object)$"Host StartLevel={num.Value}, generation={_hostStartLevel.Generation}, publication queued={flag}; waiting for room acknowledgement.");
				}
				if (!hasValue && _hostStartLevel.Value.HasValue)
				{
					((BaseUnityPlugin)this).Logger.LogInfo((object)$"Host StartLevel synchronized: {_hostStartLevel.Value.Value} (generation={_hostStartLevel.Generation}, local setting={_startLevel.Value}).");
				}
			}
		}

		private bool TryGetStartLevel(out int level)
		{
			if (Object.op_Implicit((Object)(object)_levelGenerator) && Object.op_Implicit((Object)(object)GameManager.instance) && !SemiFunc.IsMultiplayer())
			{
				level = _levelStartLevel;
				return true;
			}
			level = _hostStartLevel.Value.GetValueOrDefault();
			return _hostStartLevel.Value.HasValue;
		}

		private void Apply(EnemyValuable orb, int level, int startLevel)
		{
			bool flag = HasExplosion.Invoke(orb);
			bool flag2 = level >= startLevel;
			HasExplosion.Invoke(orb) = flag2;
			((BaseUnityPlugin)this).Logger.LogInfo((object)$"Orb initialized: level={level}, start={startLevel}, previous={flag}, explosion={flag2}.");
		}

		private void InitializeOrb(EnemyValuable orb)
		{
			RefreshRoom();
			int num = LevelsCompleted.Invoke(RunManager.instance) + 1;
			if (TryGetStartLevel(out var level))
			{
				Apply(orb, num, level);
				return;
			}
			_pendingOrbs.Add((orb, num));
			if (!_loggedWaiting)
			{
				_loggedWaiting = true;
				((BaseUnityPlugin)this).Logger.LogWarning((object)"Waiting for this level's host StartLevel; leaving orbs unchanged until received. All players need the same mod version.");
			}
		}

		private void BeforeDestroy(EnemyValuable orb)
		{
			RefreshRoom();
			int num = _pendingOrbs.FindIndex(((EnemyValuable Orb, int Level) entry) => (Object)(object)entry.Orb == (Object)(object)orb);
			if (num >= 0)
			{
				(EnemyValuable, int) tuple = _pendingOrbs[num];
				if (TryGetStartLevel(out var level))
				{
					Apply(orb, tuple.Item2, level);
				}
				else
				{
					((BaseUnityPlugin)this).Logger.LogWarning((object)"Orb destroyed before host StartLevel arrived; original explosion condition retained.");
				}
				_pendingOrbs.RemoveAt(num);
			}
		}

		private void OnDestroy()
		{
			_harmony.UnpatchSelf();
			_pendingOrbs.Clear();
			_instance = null;
		}
	}
}