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 _registrations = new List(); 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 DebugLogging { get; } private ShipCartRemovalConfig(ConfigFile file) { DebugLogging = file.Bind("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 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)(() => Convert.ToString(((ConfigEntryBase)DebugLogging).BoxedValue, CultureInfo.InvariantCulture)), 1)); } } } namespace JG224.ShipCartRemoval.Patches { internal static class RemovalFlagTranspiler { internal static IEnumerable Replace(IEnumerable 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 list = new List(); 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(); if (!Object.op_Implicit((Object)(object)component) || !component.IsValid()) { return RemovalRefusal.UnknownState; } if (!component.IsOwner()) { return RemovalRefusal.NotOwner; } Container[] componentsInChildren = ((Component)piece).GetComponentsInChildren(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(true); if (Object.op_Implicit((Object)(object)componentInChildren) && componentInChildren.HasPlayerOnboard()) { return RemovalRefusal.Occupied; } Vagon componentInChildren2 = ((Component)piece).GetComponentInChildren(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(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(); } return container.m_rootObjectOverride; } } [HarmonyPatch(typeof(Player), "RemovePiece")] internal static class VehicleRemovalTargetPatch { [HarmonyTranspiler] private static IEnumerable Transpiler(IEnumerable 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 Vehicles = new HashSet(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(); } } }