Decompiled source of DecayControl v1.0.5

Landoria.DecayControl.dll

Decompiled a month ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using Landoria.SharedLib;
using Splatform;
using TMPro;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("Landoria.DecayControl")]
[assembly: AssemblyDescription("Controls rain wear and fuel consumption for player-built pieces.")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Landoria")]
[assembly: AssemblyProduct("Landoria.DecayControl")]
[assembly: AssemblyCopyright("Copyright © 2026 End3rbyte")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("0F691AC2-F9E1-45CB-B34E-7B57171ADCE7")]
[assembly: AssemblyFileVersion("1.0.5")]
[assembly: AssemblyInformationalVersion("1.0.5")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = "")]
[assembly: AssemblyVersion("1.0.5.14016")]
namespace Landoria.DecayControl
{
	internal static class CreatorActivityPolicy
	{
		internal static HashSet<long> GetActiveCreators(IEnumerable<long> onlinePlayers)
		{
			return new HashSet<long>(onlinePlayers);
		}

		internal static bool IsCreatorActive(long creator, ISet<long> onlinePlayers)
		{
			return onlinePlayers.Contains(creator);
		}
	}
	internal static class DecayControlArgumentPolicy
	{
		internal static DecayControlMode Resolve(string[] arguments, string name, DecayControlMode configured)
		{
			string text = ReadValue(arguments, name);
			if (text == null)
			{
				return configured;
			}
			if (string.Equals(text, "default", StringComparison.OrdinalIgnoreCase))
			{
				return DecayControlMode.Default;
			}
			if (string.Equals(text, "player-online", StringComparison.OrdinalIgnoreCase))
			{
				return DecayControlMode.PlayerOnline;
			}
			if (string.Equals(text, "disabled", StringComparison.OrdinalIgnoreCase))
			{
				return DecayControlMode.Disabled;
			}
			throw new InvalidOperationException("Command-line switch " + name + " requires default, player-online, or disabled.");
		}

		private static string ReadValue(string[] arguments, string name)
		{
			string text = null;
			for (int i = 0; i < arguments.Length; i++)
			{
				if (string.Equals(arguments[i], name, StringComparison.OrdinalIgnoreCase))
				{
					if (text != null || i + 1 >= arguments.Length)
					{
						throw new InvalidOperationException("Command-line switch " + name + " is missing or duplicated.");
					}
					text = arguments[++i];
				}
			}
			return text;
		}
	}
	internal static class DecayIndicators
	{
		internal static bool Enabled { get; private set; }

		internal static void Toggle()
		{
			Enabled = !Enabled;
		}

		internal static void Reset()
		{
			Enabled = false;
		}

		internal static string FireplaceLabel(Fireplace fireplace)
		{
			if (!Enabled || (Object)(object)fireplace == (Object)null)
			{
				return null;
			}
			ZNetView component = ((Component)fireplace).GetComponent<ZNetView>();
			if ((Object)(object)component == (Object)null || !component.IsValid())
			{
				return null;
			}
			float num = component.GetZDO().GetFloat(ZDOVars.s_fuel, 0f) * 1000f;
			float num2 = fireplace.m_maxFuel * 1000f;
			string text = Localization.instance.Localize("$piece_fire_fuel");
			string text2 = (DecayProtection.IsFuelDecayOff(((Component)fireplace).GetComponent<Piece>()) ? " (off)" : "");
			return $"( {text} {num:0}/{num2:0} ) {text2}";
		}

		internal static string BuildingLabel(Piece piece)
		{
			if (!Enabled || (Object)(object)piece == (Object)null)
			{
				return null;
			}
			WearNTear component = ((Component)piece).GetComponent<WearNTear>();
			ZNetView component2 = ((Component)piece).GetComponent<ZNetView>();
			if ((Object)(object)component == (Object)null || (Object)(object)component2 == (Object)null || !component2.IsValid())
			{
				return null;
			}
			float num = component2.GetZDO().GetFloat(ZDOVars.s_health, component.m_health);
			float health = component.m_health;
			Localization.instance.Localize("$se_health");
			string arg = (DecayProtection.IsEnvironmentalWearOff(piece) ? " (off)" : "");
			return $"\n{num:0}/{health:0} {arg}";
		}
	}
	[HarmonyPatch(typeof(Fireplace), "GetHoverText")]
	internal static class FireplaceDecayIndicatorPatch
	{
		private static void Postfix(Fireplace __instance, ref string __result)
		{
			string text = DecayIndicators.FireplaceLabel(__instance);
			if (!string.IsNullOrEmpty(text))
			{
				__result = (string.IsNullOrEmpty(__result) ? text : (__result + "\n" + text));
			}
		}
	}
	[HarmonyPatch(typeof(Hud), "UpdateCrosshair")]
	internal static class BuildingDecayIndicatorPatch
	{
		private static void Postfix(Hud __instance, Player player)
		{
			string text = DecayIndicators.BuildingLabel(((Object)(object)player == (Object)(object)Player.m_localPlayer) ? player.GetHoveringPiece() : null);
			if (!string.IsNullOrEmpty(text))
			{
				((TMP_Text)__instance.m_hoverName).text = (string.IsNullOrEmpty(((TMP_Text)__instance.m_hoverName).text) ? text : (((TMP_Text)__instance.m_hoverName).text + "\n" + text));
			}
		}
	}
	internal enum DecayControlMode
	{
		Default,
		PlayerOnline,
		Disabled
	}
	internal static class DecayEffectPolicy
	{
		internal static bool ShouldApplyEnvironmentalWear(bool isVanillaRainTick, bool isPlayerBuilt, DecayControlMode mode, float activityMultiplier)
		{
			if (!isVanillaRainTick || !isPlayerBuilt || mode != DecayControlMode.PlayerOnline)
			{
				return true;
			}
			return activityMultiplier > 0f;
		}

		internal static bool ShouldPauseFuel(bool isPlayerBuilt, bool firstUpdate, DecayControlMode mode, float activityMultiplier)
		{
			if (isPlayerBuilt)
			{
				switch (mode)
				{
				case DecayControlMode.Default:
					break;
				case DecayControlMode.PlayerOnline:
					if (!firstUpdate)
					{
						return activityMultiplier <= 0f;
					}
					return true;
				default:
					return false;
				}
			}
			return false;
		}

		internal static bool ShouldUseNativeInfiniteFuel(bool isPlayerBuilt, DecayControlMode mode)
		{
			if (isPlayerBuilt)
			{
				return mode == DecayControlMode.Disabled;
			}
			return false;
		}

		internal static bool ShouldDisableNativeRoofWear(bool isPlayerBuilt, DecayControlMode mode)
		{
			if (isPlayerBuilt)
			{
				return mode == DecayControlMode.Disabled;
			}
			return false;
		}
	}
	internal static class DecayProtection
	{
		[HarmonyPatch(typeof(WearNTear), "ApplyDamage")]
		private static class RainDamagePatch
		{
			private static bool Prefix(WearNTear __instance, float damage, HitData hitData)
			{
				DecayControlMode environmentalBuildingWear = DecayControlPlugin.Settings.EnvironmentalBuildingWear;
				if (environmentalBuildingWear != DecayControlMode.PlayerOnline)
				{
					return true;
				}
				float num = __instance.m_health * 0.05f;
				bool isVanillaRainTick = hitData == null && __instance.IsWet() && __instance.GetHealthPercentage() > 0.5f && Mathf.Approximately(damage, num);
				Piece component = ((Component)__instance).GetComponent<Piece>();
				bool isPlayerBuilt = (Object)(object)component != (Object)null && component.IsPlacedByPlayer();
				float activityMultiplier = GetActivityMultiplier(component);
				return DecayEffectPolicy.ShouldApplyEnvironmentalWear(isVanillaRainTick, isPlayerBuilt, environmentalBuildingWear, activityMultiplier);
			}
		}

		[HarmonyPatch(typeof(WearNTear), "UpdateWear")]
		private static class NativeWearPatch
		{
			private static void Prefix(WearNTear __instance)
			{
				DecayControlMode environmentalBuildingWear = DecayControlPlugin.Settings.EnvironmentalBuildingWear;
				if (environmentalBuildingWear == DecayControlMode.Disabled)
				{
					Piece component = ((Component)__instance).GetComponent<Piece>();
					if (DecayEffectPolicy.ShouldDisableNativeRoofWear((Object)(object)component != (Object)null && component.IsPlacedByPlayer(), environmentalBuildingWear))
					{
						__instance.m_noRoofWear = false;
					}
				}
			}
		}

		[HarmonyPatch(typeof(Fireplace), "UpdateFireplace")]
		private static class FireplaceFuelPatch
		{
			private static void Prefix(Fireplace __instance, out float __state)
			{
				__state = __instance.m_secPerFuel;
				DecayControlMode fuelConsumption = DecayControlPlugin.Settings.FuelConsumption;
				if (fuelConsumption == DecayControlMode.Default)
				{
					return;
				}
				Piece component = ((Component)__instance).GetComponent<Piece>();
				bool flag = (Object)(object)component != (Object)null && component.IsPlacedByPlayer();
				if (DecayEffectPolicy.ShouldUseNativeInfiniteFuel(flag, fuelConsumption))
				{
					__instance.m_infiniteFuel = true;
					return;
				}
				object value;
				bool flag2 = flag && !initializedFireplaces.TryGetValue(__instance, out value);
				if (flag2)
				{
					initializedFireplaces.Add(__instance, new object());
				}
				float activityMultiplier = GetActivityMultiplier(component);
				if (DecayEffectPolicy.ShouldPauseFuel(flag, flag2, fuelConsumption, activityMultiplier))
				{
					__instance.m_secPerFuel = float.PositiveInfinity;
				}
			}

			private static void Postfix(Fireplace __instance, float __state)
			{
				__instance.m_secPerFuel = __state;
			}
		}

		private const float VanillaRainDamageFraction = 0.05f;

		private static readonly HashSet<long> ActiveCreators = new HashSet<long>();

		private static ConditionalWeakTable<Fireplace, object> initializedFireplaces = new ConditionalWeakTable<Fireplace, object>();

		private static bool hasServerState;

		internal static int ActiveCreatorCount => ActiveCreators.Count;

		internal static void Reset()
		{
			ActiveCreators.Clear();
			hasServerState = false;
			initializedFireplaces = new ConditionalWeakTable<Fireplace, object>();
		}

		internal static void WriteState(ZPackage package, IEnumerable<long> onlinePlayers)
		{
			HashSet<long> activeCreators = CreatorActivityPolicy.GetActiveCreators(onlinePlayers);
			package.Write(activeCreators.Count);
			foreach (long item in activeCreators)
			{
				package.Write(item);
			}
		}

		internal static void ReadState(ZPackage package)
		{
			ActiveCreators.Clear();
			int num = package.ReadInt();
			for (int i = 0; i < num; i++)
			{
				ActiveCreators.Add(package.ReadLong());
			}
			hasServerState = true;
		}

		internal static void ClearActivityState()
		{
			ActiveCreators.Clear();
			hasServerState = false;
		}

		internal static bool IsFuelDecayOff(Piece piece)
		{
			if ((Object)(object)piece == (Object)null || !piece.IsPlacedByPlayer())
			{
				return false;
			}
			return DecayControlPlugin.Settings.FuelConsumption switch
			{
				DecayControlMode.PlayerOnline => GetActivityMultiplier(piece) <= 0f, 
				DecayControlMode.Disabled => true, 
				_ => false, 
			};
		}

		internal static bool IsEnvironmentalWearOff(Piece piece)
		{
			if ((Object)(object)piece == (Object)null || !piece.IsPlacedByPlayer())
			{
				return false;
			}
			return DecayControlPlugin.Settings.EnvironmentalBuildingWear switch
			{
				DecayControlMode.PlayerOnline => GetActivityMultiplier(piece) <= 0f, 
				DecayControlMode.Disabled => true, 
				_ => false, 
			};
		}

		internal static float GetActivityMultiplier(Piece piece)
		{
			if ((Object)(object)piece == (Object)null || !piece.IsPlacedByPlayer())
			{
				return 1f;
			}
			long creator = piece.GetCreator();
			if (creator == 0L)
			{
				return 1f;
			}
			if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer())
			{
				if (!IsCreatorActive(creator, DecayStateRpc.GetOnlinePlayers()))
				{
					return 0f;
				}
				return 1f;
			}
			if (hasServerState && !ActiveCreators.Contains(creator))
			{
				return 0f;
			}
			return 1f;
		}

		private static bool IsCreatorActive(long creator, HashSet<long> onlinePlayers)
		{
			return CreatorActivityPolicy.IsCreatorActive(creator, onlinePlayers);
		}
	}
	[HarmonyPatch(typeof(Terminal), "InitTerminal")]
	internal static class ShowDecayCommandRegistrationPatch
	{
		private static void Postfix()
		{
			ShowDecayCommand.Register();
		}
	}
	[HarmonyPatch(typeof(Player), "OnSpawned")]
	internal static class DecayStateOnSpawnPatch
	{
		private static void Postfix(Player __instance)
		{
			if ((Object)(object)__instance == (Object)(object)Player.m_localPlayer)
			{
				DecayStateRpc.RequestOnSpawn();
			}
		}
	}
	[HarmonyPatch(typeof(ZNet), "OnDestroy")]
	internal static class DecayControlDisconnectPatch
	{
		private static void Prefix()
		{
			DecayStateRpc.ResetNetworkState();
			DecayIndicators.Reset();
		}
	}
	internal static class DecayStateRpc
	{
		private const string IdentityRpc = "Landoria_DecayControl_Identity";

		private const string SnapshotRpc = "Landoria_DecayControl_Snapshot";

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

		private static ZRoutedRpc registeredRpc;

		private static long identityServer;

		internal static void Update()
		{
			RegisterRpcs();
			SendLocalIdentity();
			if (UsesPlayerActivity() && (Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer() && RemoveDisconnectedPeers())
			{
				BroadcastSnapshot();
			}
		}

		internal static void RequestOnSpawn()
		{
			identityServer = 0L;
			Update();
		}

		internal static void ResetSession()
		{
			registeredRpc = null;
			ResetNetworkState();
		}

		internal static void ResetNetworkState()
		{
			identityServer = 0L;
			PeerPlayers.Clear();
			DecayControlPlugin.Settings?.ResetState();
			DecayProtection.Reset();
		}

		internal static HashSet<long> GetOnlinePlayers()
		{
			return new HashSet<long>(PeerPlayers.Values);
		}

		private static void RegisterRpcs()
		{
			ZRoutedRpc instance = ZRoutedRpc.instance;
			if (instance != null && instance != registeredRpc)
			{
				instance.Register<long>("Landoria_DecayControl_Identity", (Action<long, long>)ReceiveIdentity);
				instance.Register<ZPackage>("Landoria_DecayControl_Snapshot", (Action<long, ZPackage>)ReceiveSnapshot);
				PeerPlayers.Clear();
				DecayProtection.Reset();
				registeredRpc = instance;
				identityServer = 0L;
			}
		}

		private static void SendLocalIdentity()
		{
			ZNet instance = ZNet.instance;
			Player localPlayer = Player.m_localPlayer;
			if (!((Object)(object)instance == (Object)null) && !((Object)(object)localPlayer == (Object)null) && !instance.IsServer() && registeredRpc != null)
			{
				ZNetPeer serverPeer = instance.GetServerPeer();
				if (serverPeer != null && serverPeer.m_uid != identityServer)
				{
					identityServer = serverPeer.m_uid;
					registeredRpc.InvokeRoutedRPC(serverPeer.m_uid, "Landoria_DecayControl_Identity", new object[1] { localPlayer.GetPlayerID() });
				}
			}
		}

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

		private static bool RemoveDisconnectedPeers()
		{
			bool result = false;
			foreach (long item in new List<long>(PeerPlayers.Keys))
			{
				ZNetPeer peer = ZNet.instance.GetPeer(item);
				if (peer == null || !peer.IsReady())
				{
					PeerPlayers.Remove(item);
					result = true;
				}
			}
			return result;
		}

		private static void BroadcastSnapshot()
		{
			if (registeredRpc != null)
			{
				SendSnapshot(0L);
			}
		}

		private static void SendSnapshot(long target)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Expected O, but got Unknown
			ZPackage val = new ZPackage();
			DecayControlPlugin.Settings.WriteState(val);
			bool flag = UsesPlayerActivity();
			val.Write(flag);
			if (flag)
			{
				DecayProtection.WriteState(val, PeerPlayers.Values);
			}
			registeredRpc.InvokeRoutedRPC(target, "Landoria_DecayControl_Snapshot", new object[1] { val });
		}

		private static void ReceiveSnapshot(long sender, ZPackage package)
		{
			if (IsTrustedServer(sender))
			{
				DecayControlPlugin.Settings.ReadState(package);
				if (package.ReadBool())
				{
					DecayProtection.ReadState(package);
				}
				else
				{
					DecayProtection.ClearActivityState();
				}
				DecayControlPlugin.Log?.LogInfo("Received server decay settings: fuelConsumption=" + $"{DecayControlPlugin.Settings.FuelConsumption}, " + "environmentalBuildingWear=" + $"{DecayControlPlugin.Settings.EnvironmentalBuildingWear}, " + $"activeCreators={DecayProtection.ActiveCreatorCount}.");
			}
		}

		private static bool UsesPlayerActivity()
		{
			return DecayControlPlugin.Settings?.UsesPlayerActivity ?? false;
		}

		private static bool IsTrustedServer(long sender)
		{
			ZNet instance = ZNet.instance;
			if ((Object)(object)instance == (Object)null || instance.IsServer())
			{
				if ((Object)(object)instance != (Object)null)
				{
					return instance.IsServer();
				}
				return false;
			}
			ZNetPeer serverPeer = instance.GetServerPeer();
			if (serverPeer == null)
			{
				return false;
			}
			return serverPeer.m_uid == sender;
		}
	}
	[BepInPlugin("Landoria.DecayControl", "Landoria.DecayControl", "1.0.5")]
	public sealed class DecayControlPlugin : LandoriaPlugin
	{
		private const string PluginGuid = "Landoria.DecayControl";

		private const string PluginName = "Landoria.DecayControl";

		private const string PluginVersion = "1.0.5";

		internal static ModLog Log { get; private set; }

		internal static DecayControlSettings Settings { get; private set; }

		private void Awake()
		{
			Log = InitializePlugin("Landoria.DecayControl");
			Settings = new DecayControlSettings();
			ShowDecayCommand.Register();
			Settings.InitializeServer(Log);
			Log.LogInfo("Landoria.DecayControl 1.0.5 is loaded.");
		}

		private void Update()
		{
			Settings.InitializeServer(Log);
			DecayStateRpc.Update();
		}

		private void OnDestroy()
		{
			DecayStateRpc.ResetSession();
			DecayProtection.Reset();
			DecayIndicators.Reset();
			Log?.LogInfo("Landoria.DecayControl 1.0.5 is unloaded.");
			ShutdownPlugin();
			Settings = null;
			Log = null;
		}
	}
	internal sealed class DecayControlServerConfiguration
	{
		internal DecayControlMode FuelConsumption { get; private set; }

		internal DecayControlMode EnvironmentalBuildingWear { get; private set; }

		internal static DecayControlServerConfiguration FromArguments(string[] arguments)
		{
			return new DecayControlServerConfiguration
			{
				FuelConsumption = DecayControlArgumentPolicy.Resolve(arguments, "--decay-control-fuel-consumption", DecayControlMode.Default),
				EnvironmentalBuildingWear = DecayControlArgumentPolicy.Resolve(arguments, "--decay-control-environmental-building-wear", DecayControlMode.Default)
			};
		}
	}
	internal sealed class DecayControlSettings
	{
		private bool serverInitialized;

		internal DecayControlMode FuelConsumption { get; private set; }

		internal DecayControlMode EnvironmentalBuildingWear { get; private set; }

		internal bool UsesPlayerActivity
		{
			get
			{
				if (FuelConsumption != DecayControlMode.PlayerOnline)
				{
					return EnvironmentalBuildingWear == DecayControlMode.PlayerOnline;
				}
				return true;
			}
		}

		internal DecayControlSettings()
		{
			ResetState();
		}

		internal void InitializeServer(ModLog logger)
		{
			if (!serverInitialized && ServerRole.IsDedicatedServer)
			{
				DecayControlServerConfiguration decayControlServerConfiguration = DecayControlServerConfiguration.FromArguments(Environment.GetCommandLineArgs());
				FuelConsumption = decayControlServerConfiguration.FuelConsumption;
				EnvironmentalBuildingWear = decayControlServerConfiguration.EnvironmentalBuildingWear;
				serverInitialized = true;
				logger.LogInfo($"Effective decay settings: fuelConsumption={FuelConsumption}, " + $"environmentalBuildingWear={EnvironmentalBuildingWear}.");
			}
		}

		internal void WriteState(ZPackage package)
		{
			package.Write((int)FuelConsumption);
			package.Write((int)EnvironmentalBuildingWear);
		}

		internal void ReadState(ZPackage package)
		{
			FuelConsumption = (DecayControlMode)package.ReadInt();
			EnvironmentalBuildingWear = (DecayControlMode)package.ReadInt();
		}

		internal void ResetState()
		{
			if (!serverInitialized)
			{
				FuelConsumption = DecayControlMode.Default;
				EnvironmentalBuildingWear = DecayControlMode.Default;
			}
		}
	}
	internal static class ShowDecayCommand
	{
		internal static void Register()
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Expected O, but got Unknown
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			new ConsoleCommand("showdecay", "Show or hide fuel and building health indicators.", new ConsoleEvent(Execute), false, false, false, false, true, false, (ConsoleOptionsFetcher)null, false, false, false);
		}

		private static void Execute(ConsoleEventArgs args)
		{
			DecayIndicators.Toggle();
			args.Context.AddString("Decay indicators: " + (DecayIndicators.Enabled ? "on" : "off"));
		}
	}
}
namespace Landoria.SharedLib
{
	public abstract class LandoriaPlugin : BaseUnityPlugin
	{
		private Harmony _harmony;

		private bool _patchesApplied;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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