Decompiled source of ShipCartRemoval v0.5.0

ShipCartRemoval.dll

Decompiled 8 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using JG224.ModCore.API;
using JG224.ShipCartRemoval.Core;
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("ShipCartRemoval")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.5.0.0")]
[assembly: AssemblyInformationalVersion("0.5.0")]
[assembly: AssemblyProduct("ShipCartRemoval")]
[assembly: AssemblyTitle("ShipCartRemoval")]
[assembly: AssemblyVersion("0.5.0.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 JG224.ShipCartRemoval
{
	[BepInPlugin("jg224.ShipCartRemoval", "Ship&CartRemoval", "0.5.0")]
	[BepInProcess("valheim.exe")]
	[BepInDependency("com.jg224.modcore", "0.5.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "jg224.ShipCartRemoval";

		public const string PluginName = "Ship&CartRemoval";

		public const string PluginVersion = "0.5.0";

		public const string ModCoreGuid = "com.jg224.modcore";

		public const string GeneralTweaksGuid = "JG224.GeneralTweaks";

		public const int ProtocolVersion = 1;

		public static readonly ModuleId ModuleId = new ModuleId("shipcartremoval");

		private readonly List<IDisposable> _registrations = new List<IDisposable>();

		private Harmony _harmony;

		private bool _shutDown;

		internal static ManualLogSource Log { get; private set; }

		internal static ShipCartRemovalConfig Settings { get; private set; }

		internal static ICoreServices Core { get; private set; }

		private void Awake()
		{
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Expected O, but got Unknown
			//IL_00be: Unknown result type (might be due to invalid IL or missing references)
			//IL_012d: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			Log = ((BaseUnityPlugin)this).Logger;
			Settings = ShipCartRemovalConfig.Load((BaseUnityPlugin)(object)this, Paths.ConfigPath);
			try
			{
				if (!ModCoreApi.IsAvailable)
				{
					throw new InvalidOperationException("ModCore did not initialize before Ship&CartRemoval.");
				}
				Core = ModCoreApi.Services;
				RegisterWithCore();
				if (TryGetLegacyOwner(out var version))
				{
					string text = "GeneralTweaks " + version?.ToString() + " still contains vehicle removal; Ship&CartRemoval is paused to prevent duplicate hooks.";
					Core.Modules.SetState(ModuleId, (ModuleRuntimeState)3, text);
					((BaseUnityPlugin)this).Logger.LogWarning((object)(text + " Update GeneralTweaks to 3.0.0 or newer."));
				}
				else
				{
					_harmony = new Harmony("jg224.ShipCartRemoval");
					_harmony.PatchAll(Assembly.GetExecutingAssembly());
					Core.Modules.SetState(ModuleId, (ModuleRuntimeState)2, "Safe ship and cart removal hooks ready.");
					Game.isModded = true;
					((BaseUnityPlugin)this).Logger.LogInfo((object)("Ship&CartRemoval 0.5.0 loaded; protocol " + 1 + "."));
				}
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Ship&CartRemoval failed to initialize safely: " + ex));
				if (Core != null)
				{
					Core.Modules.SetState(ModuleId, (ModuleRuntimeState)5, ex.Message);
				}
				Shutdown();
				throw;
			}
		}

		private void OnDestroy()
		{
			Shutdown();
		}

		private void RegisterWithCore()
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Expected O, but got Unknown
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			SemanticVersion val = default(SemanticVersion);
			if (!SemanticVersion.TryParse("0.5.0", ref val))
			{
				throw new InvalidOperationException("Invalid plugin version.");
			}
			_registrations.Add(Core.Modules.Register(new ModuleDescriptor(ModuleId, "jg224.ShipCartRemoval", "Ship&CartRemoval", val, 1, (ModuleSide)1, (ModuleRequirement)1, 0uL, 1, 1)));
			foreach (IDisposable item in Settings.RegisterMetadata(Core, ModuleId))
			{
				_registrations.Add(item);
			}
		}

		private static bool TryGetLegacyOwner(out Version version)
		{
			version = null;
			if (!Chainloader.PluginInfos.TryGetValue("JG224.GeneralTweaks", out var value))
			{
				return false;
			}
			version = value.Metadata.Version;
			if (version != null)
			{
				return version.CompareTo(new Version(3, 0, 0)) < 0;
			}
			return false;
		}

		private void Shutdown()
		{
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			if (_shutDown)
			{
				return;
			}
			_shutDown = true;
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			_harmony = null;
			for (int num = _registrations.Count - 1; num >= 0; num--)
			{
				try
				{
					_registrations[num].Dispose();
				}
				catch (Exception ex)
				{
					ManualLogSource logger = ((BaseUnityPlugin)this).Logger;
					if (logger != null)
					{
						logger.LogWarning((object)("Registration cleanup failed: " + ex.Message));
					}
				}
			}
			_registrations.Clear();
			if (Core != null)
			{
				Core.Metrics.RemoveOwner(ModuleId);
			}
			Core = null;
			Settings = null;
			Log = null;
		}
	}
	internal sealed class ShipCartRemovalConfig
	{
		internal ConfigEntry<bool> DebugLogging { get; }

		private ShipCartRemovalConfig(ConfigFile file)
		{
			DebugLogging = file.Bind<bool>("Diagnostics", "DebugLogging", false, "Enable verbose Ship&CartRemoval diagnostics.");
		}

		internal static ShipCartRemovalConfig Load(BaseUnityPlugin plugin, string configDirectory)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Expected O, but got Unknown
			if (!Directory.Exists(configDirectory))
			{
				Directory.CreateDirectory(configDirectory);
			}
			return new ShipCartRemovalConfig(PluginConfigFiles.Attach(plugin, new ConfigFile(Path.Combine(configDirectory, "jg224.ShipCartRemoval.cfg"), true, plugin.Info.Metadata)));
		}

		internal IEnumerable<IDisposable> RegisterMetadata(ICoreServices services, ModuleId owner)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			yield return services.Configuration.Register(new ConfigSettingDescriptor(owner, ((ConfigEntryBase)DebugLogging).Definition.Section, ((ConfigEntryBase)DebugLogging).Definition.Key, (ConfigScope)5, (Func<string>)(() => Convert.ToString(((ConfigEntryBase)DebugLogging).BoxedValue, CultureInfo.InvariantCulture)), 1));
		}
	}
}
namespace JG224.ShipCartRemoval.Patches
{
	internal static class RemovalFlagTranspiler
	{
		internal static IEnumerable<CodeInstruction> Replace(IEnumerable<CodeInstruction> instructions, FieldInfo flag, MethodInfo replacement)
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Expected O, but got Unknown
			if (flag == null || replacement == null)
			{
				throw new InvalidOperationException("Ship&CartRemoval could not resolve the hammer removal gate.");
			}
			List<CodeInstruction> list = new List<CodeInstruction>();
			int num = 0;
			foreach (CodeInstruction instruction in instructions)
			{
				CodeInstruction val = new CodeInstruction(instruction);
				if (val.opcode == OpCodes.Ldfld && object.Equals(val.operand, flag))
				{
					val.opcode = OpCodes.Call;
					val.operand = replacement;
					num++;
				}
				list.Add(val);
			}
			if (num != 1)
			{
				throw new InvalidOperationException("Ship&CartRemoval expected one hammer removal flag check; found " + num + ".");
			}
			return list;
		}
	}
	internal static class VehicleSafety
	{
		internal static bool IsTarget(Piece piece)
		{
			if (Object.op_Implicit((Object)(object)piece) && piece.IsPlacedByPlayer())
			{
				return VehicleRules.IsVanillaVehicle(((Object)((Component)piece).gameObject).name);
			}
			return false;
		}

		internal static bool CanTargetForRemoval(Piece piece)
		{
			if (Object.op_Implicit((Object)(object)piece))
			{
				return VehicleRules.CanTargetForRemoval(piece.m_canBeRemoved, IsTarget(piece));
			}
			return false;
		}

		internal static bool CanRemove(Piece piece)
		{
			return Evaluate(piece) == RemovalRefusal.None;
		}

		internal static RemovalRefusal Evaluate(Piece piece)
		{
			//IL_0133: Unknown result type (might be due to invalid IL or missing references)
			//IL_0138: Unknown result type (might be due to invalid IL or missing references)
			//IL_014f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0154: Unknown result type (might be due to invalid IL or missing references)
			if (!IsTarget(piece))
			{
				return RemovalRefusal.Unsupported;
			}
			ZNetView component = ((Component)piece).GetComponent<ZNetView>();
			if (!Object.op_Implicit((Object)(object)component) || !component.IsValid())
			{
				return RemovalRefusal.UnknownState;
			}
			if (!component.IsOwner())
			{
				return RemovalRefusal.NotOwner;
			}
			Container[] componentsInChildren = ((Component)piece).GetComponentsInChildren<Container>(true);
			for (int i = 0; i < componentsInChildren.Length; i++)
			{
				if (!Object.op_Implicit((Object)(object)componentsInChildren[i]))
				{
					return RemovalRefusal.UnknownState;
				}
				ZNetView containerView = GetContainerView(componentsInChildren[i]);
				if (!Object.op_Implicit((Object)(object)containerView) || !containerView.IsValid())
				{
					return RemovalRefusal.UnknownState;
				}
				if (!containerView.IsOwner())
				{
					return RemovalRefusal.NotOwner;
				}
				Inventory inventory = componentsInChildren[i].GetInventory();
				RemovalRefusal removalRefusal = VehicleRules.CheckInventory(inventory != null, (inventory != null) ? inventory.NrOfItems() : (-1), componentsInChildren[i].IsInUse());
				if (removalRefusal != RemovalRefusal.None)
				{
					return removalRefusal;
				}
			}
			Ship componentInChildren = ((Component)piece).GetComponentInChildren<Ship>(true);
			if (Object.op_Implicit((Object)(object)componentInChildren) && componentInChildren.HasPlayerOnboard())
			{
				return RemovalRefusal.Occupied;
			}
			Vagon componentInChildren2 = ((Component)piece).GetComponentInChildren<Vagon>(true);
			if (!Object.op_Implicit((Object)(object)componentInChildren) && !Object.op_Implicit((Object)(object)componentInChildren2))
			{
				return RemovalRefusal.UnknownState;
			}
			if (Object.op_Implicit((Object)(object)componentInChildren2) && (componentInChildren2.IsAttached() || componentInChildren2.InUse()))
			{
				return RemovalRefusal.Attached;
			}
			Rigidbody[] componentsInChildren2 = ((Component)piece).GetComponentsInChildren<Rigidbody>(true);
			if (componentsInChildren2.Length == 0)
			{
				return RemovalRefusal.UnknownState;
			}
			foreach (Rigidbody val in componentsInChildren2)
			{
				if (!Object.op_Implicit((Object)(object)val) || val.isKinematic)
				{
					return RemovalRefusal.UnknownState;
				}
				Vector3 linearVelocity = val.linearVelocity;
				linearVelocity.y = 0f;
				float magnitude = ((Vector3)(ref linearVelocity)).magnitude;
				Vector3 angularVelocity = val.angularVelocity;
				if (!VehicleRules.IsMotionSafe(magnitude, ((Vector3)(ref angularVelocity)).magnitude, Object.op_Implicit((Object)(object)componentInChildren)))
				{
					return RemovalRefusal.Moving;
				}
			}
			return RemovalRefusal.None;
		}

		internal static ZNetView GetContainerView(Container container)
		{
			if (!Object.op_Implicit((Object)(object)container.m_rootObjectOverride))
			{
				return ((Component)container).GetComponent<ZNetView>();
			}
			return container.m_rootObjectOverride;
		}
	}
	[HarmonyPatch(typeof(Player), "RemovePiece")]
	internal static class VehicleRemovalTargetPatch
	{
		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			return RemovalFlagTranspiler.Replace(instructions, AccessTools.Field(typeof(Piece), "m_canBeRemoved"), AccessTools.Method(typeof(VehicleSafety), "CanTargetForRemoval", (Type[])null, (Type[])null));
		}
	}
	[HarmonyPatch(typeof(Player), "CheckCanRemovePiece")]
	internal static class VehicleWorkbenchBypassPatch
	{
		[HarmonyPrefix]
		[HarmonyPriority(0)]
		private static bool Prefix(Player __instance, Piece piece, ref bool __result, bool __runOriginal)
		{
			if (!__runOriginal || !VehicleSafety.IsTarget(piece))
			{
				return true;
			}
			RemovalRefusal removalRefusal = VehicleSafety.Evaluate(piece);
			__result = removalRefusal == RemovalRefusal.None;
			if (!__result && (Object)(object)__instance == (Object)(object)Player.m_localPlayer)
			{
				((Character)__instance).Message((MessageType)2, VehicleRules.RefusalText(removalRefusal), 0, (Sprite)null, false);
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(Piece), "CanBeRemoved")]
	internal static class VehicleSafeRemovalPatch
	{
		[HarmonyPostfix]
		private static void Postfix(Piece __instance, ref bool __result)
		{
			if (VehicleSafety.IsTarget(__instance))
			{
				__result = VehicleRules.PreserveVeto(__result, VehicleSafety.CanRemove(__instance));
			}
		}
	}
}
namespace JG224.ShipCartRemoval.Core
{
	internal enum RemovalRefusal
	{
		None,
		Unsupported,
		UnknownState,
		NotOwner,
		Cargo,
		InUse,
		Occupied,
		Attached,
		Moving
	}
	internal static class VehicleRules
	{
		private static readonly HashSet<string> Vehicles = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "Cart", "Raft", "Karve", "VikingShip", "VikingShip_Ashlands" };

		internal static bool CanTargetForRemoval(bool nativeAllowed, bool supportedPlayerVehicle)
		{
			return nativeAllowed || supportedPlayerVehicle;
		}

		internal static bool PreserveVeto(bool original, bool safe)
		{
			return original && safe;
		}

		internal static RemovalRefusal CheckInventory(bool available, int count, bool inUse)
		{
			if (available && count >= 0)
			{
				if (count <= 0)
				{
					if (!inUse)
					{
						return RemovalRefusal.None;
					}
					return RemovalRefusal.InUse;
				}
				return RemovalRefusal.Cargo;
			}
			return RemovalRefusal.UnknownState;
		}

		internal static string RefusalText(RemovalRefusal reason)
		{
			return reason switch
			{
				RemovalRefusal.NotOwner => "Cannot remove: vehicle state belongs to another player. Wait for local ownership.", 
				RemovalRefusal.Cargo => "Cannot remove: empty the vehicle's storage first.", 
				RemovalRefusal.InUse => "Cannot remove: close the vehicle's storage first.", 
				RemovalRefusal.Occupied => "Cannot remove: everyone must leave the ship first.", 
				RemovalRefusal.Attached => "Cannot remove: detach the cart first.", 
				RemovalRefusal.Moving => "Cannot remove: wait for the vehicle to stop moving.", 
				_ => "Cannot remove: vehicle state is not ready or cannot be verified.", 
			};
		}

		internal static bool IsVanillaVehicle(string prefabName)
		{
			return Vehicles.Contains(Clean(prefabName));
		}

		internal static bool IsMotionSafe(float horizontalSpeed, float angularSpeed, bool isShip)
		{
			float num = (isShip ? 0.35f : 0.25f);
			if (IsFinite(horizontalSpeed) && IsFinite(angularSpeed) && horizontalSpeed <= num)
			{
				return angularSpeed <= 0.35f;
			}
			return false;
		}

		private static bool IsFinite(float value)
		{
			if (!float.IsNaN(value) && !float.IsInfinity(value))
			{
				return value >= 0f;
			}
			return false;
		}

		private static string Clean(string value)
		{
			if (string.IsNullOrEmpty(value))
			{
				return string.Empty;
			}
			int num = value.IndexOf("(Clone)", StringComparison.OrdinalIgnoreCase);
			return ((num >= 0) ? value.Substring(0, num) : value).Trim();
		}
	}
}