Decompiled source of BoatRadiusGuard v0.1.2

BoatRadiusGuard.dll

Decompiled 19 hours 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 BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyCompany("BoatRadiusGuard")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.2.0")]
[assembly: AssemblyInformationalVersion("0.1.2+c212600daf543709f217a44cd4267220fdadb4f1")]
[assembly: AssemblyProduct("BoatRadiusGuard")]
[assembly: AssemblyTitle("BoatRadiusGuard")]
[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.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace BoatRadiusGuard
{
	[BepInPlugin("jg224.BoatRadiusGuard", "BoatRadiusGuard", "0.1.2")]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "jg224.BoatRadiusGuard";

		public const string PluginName = "BoatRadiusGuard";

		public const string PluginVersion = "0.1.2";

		public const float MinimumRadius = 10f;

		public const float MaximumRadius = 10000f;

		private Harmony _harmony;

		private ConfigurationManagerAttributes _radiusAttributes;

		private float _localRadius;

		private float _serverRadius;

		private bool _serverHasAuthority;

		private bool _applyingInternalValue;

		private bool _originalSaveOnConfigSet;

		internal static Plugin Instance { get; private set; }

		internal static ManualLogSource Log { get; private set; }

		internal static ConfigEntry<float> BoatExploreRadius { get; private set; }

		internal static float EffectiveBoatExploreRadius { get; private set; }

		private void Awake()
		{
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Expected O, but got Unknown
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Expected O, but got Unknown
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			_radiusAttributes = new ConfigurationManagerAttributes();
			BoatExploreRadius = ((BaseUnityPlugin)this).Config.Bind<float>("General", "BoatExploreRadius", 150f, new ConfigDescription("Map exploration radius in meters while the local player is aboard a boat.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(10f, 10000f), new object[1] { _radiusAttributes }));
			_localRadius = BoatExploreRadius.Value;
			EffectiveBoatExploreRadius = _localRadius;
			_originalSaveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet;
			BoatExploreRadius.SettingChanged += OnBoatExploreRadiusChanged;
			NetworkAuthority.Initialize();
			_harmony = new Harmony("jg224.BoatRadiusGuard");
			_harmony.PatchAll(Assembly.GetExecutingAssembly());
			((BaseUnityPlugin)this).Logger.LogInfo((object)("BoatRadiusGuard 0.1.2 loaded. " + $"Local boat exploration radius: {_localRadius:0.##}m."));
		}

		internal void ApplyServerRadius(float radius)
		{
			if (!_serverHasAuthority)
			{
				_localRadius = BoatExploreRadius.Value;
				_originalSaveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet;
			}
			_serverHasAuthority = true;
			_serverRadius = radius;
			EffectiveBoatExploreRadius = radius;
			_radiusAttributes.ReadOnly = true;
			((BaseUnityPlugin)this).Config.SaveOnConfigSet = false;
			SetDisplayedRadius(radius);
			((BaseUnityPlugin)this).Logger.LogInfo((object)$"Using server-authoritative boat exploration radius: {radius:0.##}m.");
		}

		internal void ReleaseServerAuthority()
		{
			if (_serverHasAuthority)
			{
				_serverHasAuthority = false;
				_radiusAttributes.ReadOnly = false;
				EffectiveBoatExploreRadius = _localRadius;
				SetDisplayedRadius(_localRadius);
				((BaseUnityPlugin)this).Config.SaveOnConfigSet = _originalSaveOnConfigSet;
				if (_originalSaveOnConfigSet)
				{
					((BaseUnityPlugin)this).Config.Save();
				}
				((BaseUnityPlugin)this).Logger.LogInfo((object)$"Restored local boat exploration radius: {_localRadius:0.##}m.");
			}
		}

		private void OnBoatExploreRadiusChanged(object sender, EventArgs eventArgs)
		{
			if (_applyingInternalValue)
			{
				return;
			}
			if (_serverHasAuthority)
			{
				float value = BoatExploreRadius.Value;
				SetDisplayedRadius(_serverRadius);
				((BaseUnityPlugin)this).Logger.LogWarning((object)($"Ignored client-side BoatExploreRadius={value:0.##}; " + $"the server requires {_serverRadius:0.##}."));
				return;
			}
			_localRadius = BoatExploreRadius.Value;
			EffectiveBoatExploreRadius = _localRadius;
			if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer())
			{
				NetworkAuthority.BroadcastServerConfig();
			}
		}

		private void SetDisplayedRadius(float radius)
		{
			_applyingInternalValue = true;
			try
			{
				BoatExploreRadius.Value = radius;
			}
			finally
			{
				_applyingInternalValue = false;
			}
		}

		private void OnDestroy()
		{
			ReleaseServerAuthority();
			if (BoatExploreRadius != null)
			{
				BoatExploreRadius.SettingChanged -= OnBoatExploreRadiusChanged;
			}
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			NetworkAuthority.Reset();
			Instance = null;
		}
	}
	internal sealed class ConfigurationManagerAttributes
	{
		public bool? ReadOnly = false;
	}
	internal static class NetworkAuthority
	{
		private const string HandshakeRpc = "jg224.BoatRadiusGuard.Handshake";

		private const int ClientRole = 0;

		private const int ServerRole = 1;

		private static readonly object Gate = new object();

		private static readonly HashSet<ZRpc> CompatibleClients = new HashSet<ZRpc>();

		private static ZNetPeer _serverPeer;

		internal static void Initialize()
		{
			Reset();
		}

		internal static void Reset()
		{
			lock (Gate)
			{
				CompatibleClients.Clear();
			}
			_serverPeer = null;
		}

		internal static void OnNewConnection(ZNet znet, ZNetPeer peer)
		{
			peer.m_rpc.Register<int, string, float>("jg224.BoatRadiusGuard.Handshake", (Action<ZRpc, int, string, float>)ReceiveHandshake);
			if (znet.IsServer())
			{
				lock (Gate)
				{
					CompatibleClients.Remove(peer.m_rpc);
					return;
				}
			}
			Plugin.Instance?.ReleaseServerAuthority();
			_serverPeer = peer;
			peer.m_rpc.Invoke("jg224.BoatRadiusGuard.Handshake", new object[3] { 0, "0.1.2", 0f });
		}

		internal static void OnDisconnect(ZNet znet, ZNetPeer peer)
		{
			if (znet.IsServer())
			{
				lock (Gate)
				{
					CompatibleClients.Remove(peer.m_rpc);
					return;
				}
			}
			if (peer == _serverPeer)
			{
				_serverPeer = null;
				Plugin.Instance?.ReleaseServerAuthority();
			}
		}

		internal static void OnShutdown(ZNet znet)
		{
			if (!znet.IsServer())
			{
				Plugin.Instance?.ReleaseServerAuthority();
			}
			Reset();
		}

		internal static void BroadcastServerConfig()
		{
			ZNet instance = ZNet.instance;
			if ((Object)(object)instance == (Object)null || !instance.IsServer())
			{
				return;
			}
			foreach (ZNetPeer connectedPeer in instance.GetConnectedPeers())
			{
				if (connectedPeer?.m_rpc != null && connectedPeer.m_rpc.IsConnected() && IsCompatibleClient(connectedPeer.m_rpc))
				{
					connectedPeer.m_rpc.Invoke("jg224.BoatRadiusGuard.Handshake", new object[3]
					{
						1,
						"0.1.2",
						Plugin.EffectiveBoatExploreRadius
					});
				}
			}
		}

		private static void ReceiveHandshake(ZRpc rpc, int senderRole, string version, float radius)
		{
			ZNet instance = ZNet.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return;
			}
			if (instance.IsServer())
			{
				if (senderRole != 0 || !VersionMatches(version))
				{
					Plugin.Log.LogInfo((object)("Client " + rpc.GetSocket().GetHostName() + " has an incompatible optional BoatRadiusGuard version '" + version + "'. Expected 0.1.2; connection allowed without the feature."));
					return;
				}
				lock (Gate)
				{
					CompatibleClients.Add(rpc);
				}
				Plugin.Log.LogInfo((object)("Validated BoatRadiusGuard " + version + " for " + rpc.GetSocket().GetHostName() + "."));
				rpc.Invoke("jg224.BoatRadiusGuard.Handshake", new object[3]
				{
					1,
					"0.1.2",
					Plugin.EffectiveBoatExploreRadius
				});
			}
			else if (senderRole != 1 || !VersionMatches(version) || !RadiusIsValid(radius))
			{
				Plugin.Log.LogWarning((object)("Ignored incompatible optional BoatRadiusGuard server data: " + $"version='{version}', radius={radius}. Connection remains allowed."));
			}
			else
			{
				Plugin.Instance?.ApplyServerRadius(radius);
			}
		}

		private static bool IsCompatibleClient(ZRpc rpc)
		{
			lock (Gate)
			{
				return CompatibleClients.Contains(rpc);
			}
		}

		private static bool VersionMatches(string version)
		{
			return string.Equals(version, "0.1.2", StringComparison.Ordinal);
		}

		private static bool RadiusIsValid(float radius)
		{
			if (!float.IsNaN(radius) && !float.IsInfinity(radius) && radius >= 10f)
			{
				return radius <= 10000f;
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(ZNet), "OnNewConnection")]
	internal static class ZNetOnNewConnectionPatch
	{
		[HarmonyPostfix]
		private static void Postfix(ZNet __instance, ZNetPeer peer)
		{
			NetworkAuthority.OnNewConnection(__instance, peer);
		}
	}
	[HarmonyPatch(typeof(ZNet), "Disconnect")]
	internal static class ZNetDisconnectPatch
	{
		[HarmonyPrefix]
		private static void Prefix(ZNet __instance, ZNetPeer peer)
		{
			NetworkAuthority.OnDisconnect(__instance, peer);
		}
	}
	[HarmonyPatch(typeof(ZNet), "Shutdown")]
	internal static class ZNetShutdownPatch
	{
		[HarmonyPrefix]
		private static void Prefix(ZNet __instance)
		{
			NetworkAuthority.OnShutdown(__instance);
		}
	}
	[HarmonyPatch(typeof(Minimap), "UpdateExplore")]
	internal static class MinimapUpdateExplorePatch
	{
		[HarmonyPrefix]
		private static void Prefix(Minimap __instance, out float __state)
		{
			__state = __instance.m_exploreRadius;
			if ((Object)(object)Ship.GetLocalShip() != (Object)null)
			{
				__instance.m_exploreRadius = Plugin.EffectiveBoatExploreRadius;
			}
		}

		[HarmonyPostfix]
		private static void Postfix(Minimap __instance, float __state)
		{
			__instance.m_exploreRadius = __state;
		}

		[HarmonyFinalizer]
		private static void Finalizer(Minimap __instance, float __state)
		{
			__instance.m_exploreRadius = __state;
		}
	}
}