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> _entries = new List>(); internal ConfigEntry Enabled { get; } internal ConfigEntry PrioritizeBuild { get; } internal ConfigEntry ExcludedPrefabs { get; } internal ConfigEntry DebugLogging { get; } private BuildInteractConfig(ConfigFile file) { //IL_0052: Unknown result type (might be due to invalid IL or missing references) _file = file; Enabled = Add(file.Bind("Interaction", "InteractWhileBuilding", true, "Keep Valheim's normal Use interactions available while any placement tool is active."), (ConfigScope)1); PrioritizeBuild = Add(file.Bind("Interaction", "PrioritizeBuild", new KeyboardShortcut((KeyCode)308, Array.Empty()), "Hold this shortcut to prioritize vanilla building instead of interacting. None disables the shortcut."), (ConfigScope)1); ExcludedPrefabs = Add(file.Bind("Interaction", "ExcludedPrefabs", "", "Semicolon-separated prefab names whose interactions remain disabled while building, for example portal_wood;piece_bed02."), (ConfigScope)1); DebugLogging = Add(file.Bind("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 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 pair in _entries) { yield return services.Configuration.Register(new ConfigSettingDescriptor(owner, pair.Item1.Definition.Section, pair.Item1.Definition.Key, pair.Item2, (Func)(() => 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("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 Add(ConfigEntry entry, ConfigScope scope) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) _entries.Add(Tuple.Create((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 _registrations = new List(); 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 _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() != 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 ParseExclusions(string value) { return new HashSet(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 candidatePaths) { string[] paths = ((candidatePaths == null) ? Array.Empty() : 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 paths, string fileName) { return paths.FirstOrDefault((string path) => string.Equals(Path.GetFileName(path), fileName, StringComparison.Ordinal)); } } }