Decompiled source of BuildInteract v0.5.0

BuildInteract.dll

Decompiled 8 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using JG224.BuildInteract.Core;
using JG224.ModCore.API;
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("BuildInteract")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.5.0.0")]
[assembly: AssemblyInformationalVersion("0.5.0")]
[assembly: AssemblyProduct("BuildInteract")]
[assembly: AssemblyTitle("BuildInteract")]
[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.BuildInteract
{
	internal sealed class BuildInteractConfig
	{
		private readonly ConfigFile _file;

		private readonly List<Tuple<ConfigEntryBase, ConfigScope>> _entries = new List<Tuple<ConfigEntryBase, ConfigScope>>();

		internal ConfigEntry<bool> Enabled { get; }

		internal ConfigEntry<KeyboardShortcut> PrioritizeBuild { get; }

		internal ConfigEntry<string> ExcludedPrefabs { get; }

		internal ConfigEntry<bool> DebugLogging { get; }

		private BuildInteractConfig(ConfigFile file)
		{
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			_file = file;
			Enabled = Add<bool>(file.Bind<bool>("Interaction", "InteractWhileBuilding", true, "Keep Valheim's normal Use interactions available while any placement tool is active."), (ConfigScope)1);
			PrioritizeBuild = Add<KeyboardShortcut>(file.Bind<KeyboardShortcut>("Interaction", "PrioritizeBuild", new KeyboardShortcut((KeyCode)308, Array.Empty<KeyCode>()), "Hold this shortcut to prioritize vanilla building instead of interacting. None disables the shortcut."), (ConfigScope)1);
			ExcludedPrefabs = Add<string>(file.Bind<string>("Interaction", "ExcludedPrefabs", "", "Semicolon-separated prefab names whose interactions remain disabled while building, for example portal_wood;piece_bed02."), (ConfigScope)1);
			DebugLogging = Add<bool>(file.Bind<bool>("Diagnostics", "DebugLogging", false, "Enable verbose BuildInteract diagnostics."), (ConfigScope)5);
		}

		internal static BuildInteractConfig Load(BaseUnityPlugin plugin, string configDirectory, ManualLogSource log)
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Expected O, but got Unknown
			if (!Directory.Exists(configDirectory))
			{
				Directory.CreateDirectory(configDirectory);
			}
			string text = LegacyBuildInteractConfigRules.FindImportSource(Directory.GetFiles(configDirectory, "*.cfg", SearchOption.TopDirectoryOnly));
			BuildInteractConfig buildInteractConfig = new BuildInteractConfig(PluginConfigFiles.Attach(plugin, new ConfigFile(Path.Combine(configDirectory, "jg224.BuildInteract.cfg"), true, plugin.Info.Metadata)));
			if (text != null)
			{
				buildInteractConfig.ImportLegacy(text, log);
			}
			return buildInteractConfig;
		}

		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)
			foreach (Tuple<ConfigEntryBase, ConfigScope> pair in _entries)
			{
				yield return services.Configuration.Register(new ConfigSettingDescriptor(owner, pair.Item1.Definition.Section, pair.Item1.Definition.Key, pair.Item2, (Func<string>)(() => Convert.ToString(pair.Item1.BoxedValue, CultureInfo.InvariantCulture)), 1));
			}
		}

		private void ImportLegacy(string path, ManualLogSource log)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Expected O, but got Unknown
			try
			{
				ConfigFile val = new ConfigFile(path, false);
				Enabled.Value = val.Bind<bool>("Interaction", "InteractWhileBuilding", Enabled.Value, (ConfigDescription)null).Value;
				_file.Save();
				if (log != null)
				{
					log.LogInfo((object)("Imported Build Interact settings from " + Path.GetFileName(path) + "."));
				}
			}
			catch (Exception ex)
			{
				if (log != null)
				{
					log.LogWarning((object)("Could not import GeneralTweaks Build Interact settings; using defaults. " + ex.Message));
				}
			}
		}

		private ConfigEntry<T> Add<T>(ConfigEntry<T> entry, ConfigScope scope)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			_entries.Add(Tuple.Create<ConfigEntryBase, ConfigScope>((ConfigEntryBase)(object)entry, scope));
			return entry;
		}
	}
	[BepInPlugin("jg224.BuildInteract", "BuildInteract", "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.BuildInteract";

		public const string PluginName = "BuildInteract";

		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("buildinteract");

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

		private Harmony _harmony;

		private bool _shutDown;

		internal static ManualLogSource Log { get; private set; }

		internal static BuildInteractConfig Settings { get; private set; }

		internal static ICoreServices Core { get; private set; }

		private void Awake()
		{
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Expected O, but got Unknown
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_0133: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			Log = ((BaseUnityPlugin)this).Logger;
			Settings = BuildInteractConfig.Load((BaseUnityPlugin)(object)this, Paths.ConfigPath, ((BaseUnityPlugin)this).Logger);
			try
			{
				if (!ModCoreApi.IsAvailable)
				{
					throw new InvalidOperationException("ModCore did not initialize before BuildInteract.");
				}
				Core = ModCoreApi.Services;
				RegisterWithCore();
				if (TryGetLegacyOwner(out var version))
				{
					string text = "GeneralTweaks " + version?.ToString() + " still contains Build Interact; BuildInteract 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.BuildInteract");
					_harmony.PatchAll(Assembly.GetExecutingAssembly());
					Core.Modules.SetState(ModuleId, (ModuleRuntimeState)2, "Client-local build interaction hooks ready.");
					Game.isModded = true;
					((BaseUnityPlugin)this).Logger.LogInfo((object)("BuildInteract 0.5.0 loaded; protocol " + 1 + "."));
				}
			}
			catch (Exception ex)
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("BuildInteract 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("BuildInteract has invalid release version metadata.");
			}
			_registrations.Add(Core.Modules.Register(new ModuleDescriptor(ModuleId, "jg224.BuildInteract", "BuildInteract", 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;
		}
	}
}
namespace JG224.BuildInteract.Patches
{
	internal static class InteractionContext
	{
		private static string _raw;

		private static HashSet<string> _excluded = InteractionRules.ParseExclusions("");

		internal static bool Allowed(Player player)
		{
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			BuildInteractConfig settings = Plugin.Settings;
			if (!Object.op_Implicit((Object)(object)player))
			{
				return false;
			}
			bool enabled = settings?.Enabled.Value ?? false;
			bool local = (Object)(object)player == (Object)(object)Player.m_localPlayer;
			bool building = ((Character)player).InPlaceMode();
			bool dead = ((Character)player).IsDead();
			bool controllingDoodad = player.GetDoodadController() != null;
			int prioritizeBuild;
			if (settings != null)
			{
				KeyboardShortcut value = settings.PrioritizeBuild.Value;
				if ((int)((KeyboardShortcut)(ref value)).MainKey != 0)
				{
					value = settings.PrioritizeBuild.Value;
					prioritizeBuild = (((KeyboardShortcut)(ref value)).IsPressed() ? 1 : 0);
					goto IL_006d;
				}
			}
			prioritizeBuild = 0;
			goto IL_006d;
			IL_006d:
			return InteractionRules.AllowInteraction(enabled, local, building, dead, controllingDoodad, (byte)prioritizeBuild != 0);
		}

		internal static bool Excluded(GameObject hover)
		{
			string text = Plugin.Settings?.ExcludedPrefabs.Value ?? "";
			if (_raw != text)
			{
				_raw = text;
				_excluded = InteractionRules.ParseExclusions(text);
			}
			if (!Object.op_Implicit((Object)(object)hover) || _excluded.Count == 0)
			{
				return false;
			}
			Transform val = hover.transform;
			while (Object.op_Implicit((Object)(object)val))
			{
				if (_excluded.Contains(InteractionRules.NormalizePrefab(((Object)val).name)))
				{
					return true;
				}
				val = val.parent;
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(Player), "UpdateHover")]
	internal static class InteractWhileBuildingHoverPatch
	{
		[HarmonyPostfix]
		private static void Postfix(Player __instance, ref GameObject ___m_hovering, ref Character ___m_hoveringCreature)
		{
			if (InteractionContext.Allowed(__instance))
			{
				FindHoverObject(__instance, out ___m_hovering, out ___m_hoveringCreature);
				if (InteractionContext.Excluded(___m_hovering))
				{
					___m_hovering = null;
					___m_hoveringCreature = null;
				}
			}
		}

		[HarmonyReversePatch(/*Could not decode attribute arguments.*/)]
		[HarmonyPatch(typeof(Player), "FindHoverObject")]
		private static void FindHoverObject(Player instance, out GameObject hover, out Character hoverCreature)
		{
			hover = null;
			hoverCreature = null;
			throw new NotImplementedException("Harmony reverse patch stub");
		}
	}
	[HarmonyPatch(typeof(Player), "UpdateBuildGuiInput")]
	internal static class InteractWhileBuildingControllerPatch
	{
		[HarmonyPrefix]
		private static bool Prefix(Player __instance)
		{
			if (!InteractionContext.Allowed(__instance))
			{
				return true;
			}
			GameObject hoverObject = ((Humanoid)__instance).GetHoverObject();
			bool eligibleHover = Object.op_Implicit((Object)(object)hoverObject) && hoverObject.GetComponentInParent<Interactable>() != null && !InteractionContext.Excluded(hoverObject);
			return !InteractionRules.ShouldRouteControllerUse(ZInput.IsGamepadActive(), ZInput.IsNonClassicFunctionality(), Hud.IsPieceSelectionVisible(), ZInput.GetButtonDown("JoyUse"), eligibleHover);
		}
	}
}
namespace JG224.BuildInteract.Core
{
	internal static class InteractionRules
	{
		internal static bool ShouldRefreshHover(bool enabled, bool isLocalPlayer, bool inPlaceMode, bool isDead)
		{
			if (enabled && isLocalPlayer && inPlaceMode)
			{
				return !isDead;
			}
			return false;
		}

		internal static bool ShouldLeaveBuildInputAlone(bool enabled, bool pieceSelectionVisible, bool joyUseDown, bool hasInteractableHover)
		{
			if (!(!enabled || pieceSelectionVisible) && joyUseDown)
			{
				return !hasInteractableHover;
			}
			return true;
		}

		internal static bool AllowInteraction(bool enabled, bool local, bool building, bool dead, bool controllingDoodad, bool prioritizeBuild)
		{
			if (ShouldRefreshHover(enabled, local, building, dead) && !controllingDoodad)
			{
				return !prioritizeBuild;
			}
			return false;
		}

		internal static bool ShouldRouteControllerUse(bool gamepadActive, bool alternateScheme, bool pieceSelectionVisible, bool useDown, bool eligibleHover)
		{
			if (gamepadActive && !alternateScheme)
			{
				return !ShouldLeaveBuildInputAlone(enabled: true, pieceSelectionVisible, useDown, eligibleHover);
			}
			return false;
		}

		internal static HashSet<string> ParseExclusions(string value)
		{
			return new HashSet<string>(from name in (value ?? "").Split(new char[1] { ';' }).Select(NormalizePrefab)
				where name.Length > 0
				select name, StringComparer.OrdinalIgnoreCase);
		}

		internal static string NormalizePrefab(string name)
		{
			return (name ?? "").Replace("(Clone)", "").Trim();
		}
	}
	internal static class LegacyBuildInteractConfigRules
	{
		internal const string CurrentFileName = "jg224.BuildInteract.cfg";

		internal const string GeneralTweaksFileName = "jg224.GeneralTweaks.cfg";

		internal const string LegacyGeneralTweaksFileName = "JG224.GeneralTweaks.cfg";

		internal static string FindImportSource(IEnumerable<string> candidatePaths)
		{
			string[] paths = ((candidatePaths == null) ? Array.Empty<string>() : candidatePaths.ToArray());
			if (FindExact(paths, "jg224.BuildInteract.cfg") != null)
			{
				return null;
			}
			return FindExact(paths, "jg224.GeneralTweaks.cfg") ?? FindExact(paths, "JG224.GeneralTweaks.cfg");
		}

		private static string FindExact(IEnumerable<string> paths, string fileName)
		{
			return paths.FirstOrDefault((string path) => string.Equals(Path.GetFileName(path), fileName, StringComparison.Ordinal));
		}
	}
}