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 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("General", "BoatExploreRadius", 150f, new ConfigDescription("Map exploration radius in meters while the local player is aboard a boat.", (AcceptableValueBase)(object)new AcceptableValueRange(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 CompatibleClients = new HashSet(); 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("jg224.BoatRadiusGuard.Handshake", (Action)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; } } }