Please disclose if your mod was created primarily using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of MeasureTwice v0.1.0
MeasureTwice.dll
Decompiled a year agousing System; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using Configs; using HarmonyLib; using Jotunn.Configs; using Jotunn.Entities; using Jotunn.Extensions; using Jotunn.Managers; using Jotunn.Utils; using Logging; using MeasureTwice.Patches; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("MeasureTwice")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("MeasureTwice")] [assembly: AssemblyCopyright("Copyright © 2024")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("08a6f7d7-cf93-4931-aecd-abf2ce6ed34c")] [assembly: AssemblyFileVersion("0.1.0")] [assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.1.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.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 MeasureTwice { [BepInPlugin("Searica.Valheim.MeasureTwice", "MeasureTwice", "0.1.0")] [BepInDependency("com.jotunn.jotunn", "2.23.2")] [NetworkCompatibility(/*Could not decode attribute arguments.*/)] [SynchronizationMode(/*Could not decode attribute arguments.*/)] internal sealed class MeasureTwice : BaseUnityPlugin { public const string PluginName = "MeasureTwice"; internal const string Author = "Searica"; public const string PluginGUID = "Searica.Valheim.MeasureTwice"; public const string PluginVersion = "0.1.0"; internal static MeasureTwice Instance; internal static ConfigFile ConfigFile; internal static ConfigFileWatcher ConfigFileWatcher; internal CustomPiece SpacerBlockPiece; internal const string SpacerBlockName = "spacer_block"; internal const string SpacerBlockPiecceName = "Ruler"; internal const string GlobalSection = "Global"; public ConfigEntry<int> TimedDestruction; public ConfigEntry<float> ScrollSpeed; public ConfigEntry<KeyCode> LengthModifierKey; public void Awake() { Instance = this; ConfigFile = ((BaseUnityPlugin)this).Config; Log.Init(((BaseUnityPlugin)this).Logger); ((BaseUnityPlugin)this).Config.DisableSaveOnConfigSet(); SetUpConfigEntries(); ((BaseUnityPlugin)this).Config.Save(); ((BaseUnityPlugin)this).Config.SaveOnConfigSet = true; Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly(), "Searica.Valheim.MeasureTwice"); Game.isModded = true; PrefabManager.OnVanillaPrefabsAvailable += AddCustomPieces; ConfigFileWatcher = new ConfigFileWatcher(((BaseUnityPlugin)this).Config); } internal void SetUpConfigEntries() { Log.Verbosity = ConfigFileExtensions.BindConfigInOrder<Log.InfoLevel>(((BaseUnityPlugin)this).Config, "Global", "Verbosity", Log.InfoLevel.Low, "Logging level.", true, true, true, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null); LengthModifierKey = ConfigFileExtensions.BindConfigInOrder<KeyCode>(((BaseUnityPlugin)this).Config, "Global", "Length Modifier Key", (KeyCode)307, "Hold this key to scroll and adjust length of the spacer block", false, true, true, (AcceptableValueBase)null, (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null); ScrollSpeed = ConfigFileExtensions.BindConfigInOrder<float>(((BaseUnityPlugin)this).Config, "Global", "Scroll Speed", 0.05f, "Chnage in length for each tick of a the scroll wheel.", false, true, true, (AcceptableValueBase)(object)new AcceptableValueRange<float>(-1f, 1f), (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null); TimedDestruction = ConfigFileExtensions.BindConfigInOrder<int>(((BaseUnityPlugin)this).Config, "Global", "Timed Destroy", 20, "Number of seconds before spacer block self destructs.", false, true, true, (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 30), (Action<ConfigEntryBase>)null, (ConfigurationManagerAttributes)null); } public void OnDestroy() { ((BaseUnityPlugin)this).Config.Save(); } private void AddCustomPieces() { //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Expected O, but got Unknown //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Expected O, but got Unknown //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Expected O, but got Unknown GameObject val = PrefabManager.Instance.CreateClonedPrefab("spacer_block", "wood_beam_1"); val.AddComponent<CustomRuler>(); val.GetComponent<ZNetView>().m_persistent = false; PieceConfig val2 = new PieceConfig(); val2.Name = "Ruler"; val2.Category = "Ruler"; val2.Requirements = (RequirementConfig[])(object)new RequirementConfig[1] { new RequirementConfig("Wood", 0, 0, false) }; val2.CraftingStation = CraftingStations.None; val2.PieceTable = PieceTables.Hammer; PieceConfig val3 = val2; SpacerBlockPiece = new CustomPiece(val, true, val3); PieceManager.Instance.AddPiece(SpacerBlockPiece); PrefabManager.OnVanillaPrefabsAvailable -= AddCustomPieces; } } } namespace MeasureTwice.Patches { [HarmonyPatch] internal class CustomRuler : MonoBehaviour { public float m_timeout = 1f; private ZNetView m_nview; private static bool m_triggerOnPlaced; public void Awake() { m_nview = ((Component)this).GetComponent<ZNetView>(); m_timeout = MeasureTwice.Instance.TimedDestruction.Value; if (m_triggerOnPlaced) { ApplyScale(); ((MonoBehaviour)this).InvokeRepeating("DestroyNow", m_timeout, 1f); } } public static void SetTriggerOnPlaced(bool trigger) { m_triggerOnPlaced = trigger; } [HarmonyPrefix] [HarmonyPatch(typeof(Player), "PlacePiece")] private static void PlacePiecePrefix() { SetTriggerOnPlaced(trigger: true); } [HarmonyPostfix] [HarmonyPatch(typeof(Player), "PlacePiece")] private static void PlacePiecePostfix() { SetTriggerOnPlaced(trigger: false); } public void ApplyScale() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) ((Component)this).transform.localScale = ScaleManager.ScaleOnPlaced; } public void DestroyNow() { if (Object.op_Implicit((Object)(object)m_nview)) { if (m_nview.IsValid()) { if (!m_nview.HasOwner()) { m_nview.ClaimOwnership(); } if (m_nview.IsOwner()) { ZNetScene.instance.Destroy(((Component)this).gameObject); } } } else { Object.Destroy((Object)(object)((Component)this).gameObject); } } } [HarmonyPatch] internal static class ScaleManager { private const float MinLength = 0.1f; private const float MaxLength = 2f; private const float Tolerance = 0.01f; private static bool SpacerBlockIsInUse = false; private static float LastOriginalLength = 0f; private static float LastTotalDelta = 0f; private static Vector3 LastGhostScale = Vector3.one; private static bool DisabledScroll = false; public static Vector3 ScaleOnPlaced => LastGhostScale; [HarmonyPrefix] [HarmonyPatch(typeof(Player), "UpdatePlacement")] private static void UpdatePlacementPrefix(Player __instance) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) if (!Object.op_Implicit((Object)(object)__instance) || (Object)(object)__instance != (Object)(object)Player.m_localPlayer) { return; } if (!((Character)__instance).InPlaceMode() || Hud.IsPieceSelectionVisible()) { if (SpacerBlockIsInUse) { SpacerBlockIsInUse = false; LastOriginalLength = 0f; LastTotalDelta = 0f; LastGhostScale = Vector3.zero; } } else { if (!IsValidSelectedPiece(__instance, out var spacerBlock)) { return; } if (ShouldModifyLength()) { DisabledScroll = true; ZInput instance = ZInput.instance; if (instance != null) { instance.m_mouseScrollDeltaAction.Disable(); } SetLength(__instance, spacerBlock, Input.mouseScrollDelta.y * MeasureTwice.Instance.ScrollSpeed.Value); } RefreshGhostScale(__instance); } } [HarmonyPostfix] [HarmonyPriority(0)] [HarmonyPatch(typeof(Player), "UpdatePlacement")] private static void UpdatePlacementPostfix(Player __instance, ref int __state) { if (DisabledScroll) { DisabledScroll = false; ZInput instance = ZInput.instance; if (instance != null) { instance.m_mouseScrollDeltaAction.Enable(); } } } internal static bool IsValidSelectedPiece(Player player, out CustomRuler spacerBlock) { Piece selectedPiece = player.GetSelectedPiece(); if (!Object.op_Implicit((Object)(object)selectedPiece) || !((Component)selectedPiece).TryGetComponent<CustomRuler>(ref spacerBlock)) { spacerBlock = null; return false; } return Object.op_Implicit((Object)(object)spacerBlock); } internal static bool ShouldModifyLength() { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) if (Input.GetKey(MeasureTwice.Instance.LengthModifierKey.Value)) { return Input.mouseScrollDelta.y != 0f; } return false; } public static float ModifyLength(float length, float delta) { return Mathf.Clamp(length + delta, 0.1f, 2f); } private static void SetLength(Player player, CustomRuler spacerBlock, float delta) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) if (!SpacerBlockIsInUse && Object.op_Implicit((Object)(object)spacerBlock)) { SpacerBlockIsInUse = true; LastOriginalLength = ((Component)spacerBlock).transform.localScale.x; LastGhostScale = ((Component)spacerBlock).transform.localScale; } LastTotalDelta = Mathf.Clamp(LastTotalDelta + delta, 0.1f - LastOriginalLength, 2f - LastOriginalLength); LastGhostScale.x = ModifyLength(LastOriginalLength, LastTotalDelta); ((Character)player).Message((MessageType)2, $"Spacer Length: {LastGhostScale.x:#,0.000}", 0, (Sprite)null); } private static void RefreshGhostScale(Player player) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: 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) if (SpacerBlockIsInUse && Object.op_Implicit((Object)(object)player.m_placementGhost) && LastOriginalLength != 0f && Vector3.Distance(player.m_placementGhost.transform.localScale, LastGhostScale) > 0.01f) { player.m_placementGhost.transform.localScale = LastGhostScale; } } } } namespace Logging { internal static class Log { internal enum InfoLevel { Low, Medium, High } private static ManualLogSource logSource; internal static ConfigEntry<InfoLevel> Verbosity { get; set; } internal static InfoLevel VerbosityLevel => Verbosity.Value; internal static bool IsVerbosityLow => Verbosity.Value >= InfoLevel.Low; internal static bool IsVerbosityMedium => Verbosity.Value >= InfoLevel.Medium; internal static bool IsVerbosityHigh => Verbosity.Value >= InfoLevel.High; internal static void Init(ManualLogSource logSource) { Log.logSource = logSource; } internal static void LogDebug(object data) { logSource.LogDebug(data); } internal static void LogError(object data) { logSource.LogError(data); } internal static void LogFatal(object data) { logSource.LogFatal(data); } internal static void LogMessage(object data) { logSource.LogMessage(data); } internal static void LogWarning(object data) { logSource.LogWarning(data); } internal static void LogInfo(object data, InfoLevel level = InfoLevel.Low) { if (Verbosity == null || VerbosityLevel >= level) { logSource.LogInfo(data); } } internal static void LogGameObject(GameObject prefab, bool includeChildren = false) { //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Expected O, but got Unknown LogInfo("***** " + ((Object)prefab).name + " *****"); Component[] components = prefab.GetComponents<Component>(); for (int i = 0; i < components.Length; i++) { LogComponent(components[i]); } if (!includeChildren) { return; } LogInfo("***** " + ((Object)prefab).name + " (children) *****"); foreach (Transform item in prefab.transform) { Transform val = item; if (Object.op_Implicit((Object)(object)val)) { LogInfo(" - " + ((Object)val).name); components = ((Component)val).GetComponents<Component>(); for (int i = 0; i < components.Length; i++) { LogComponent(components[i]); } } } } internal static void LogComponent(Component compo) { if (!Object.op_Implicit((Object)(object)compo)) { return; } try { LogInfo("--- " + ((object)compo).GetType().Name + ": " + ((Object)compo).name + " ---"); } catch (Exception ex) { LogError(ex.ToString()); LogWarning("Could not get type name for component!"); return; } try { foreach (PropertyInfo declaredProperty in AccessTools.GetDeclaredProperties(((object)compo).GetType())) { try { LogInfo($" - {declaredProperty.Name} = {declaredProperty.GetValue(compo)}"); } catch (Exception ex2) { LogError(ex2.ToString()); LogWarning("Could not get property: " + declaredProperty.Name + " for component!"); } } } catch (Exception ex3) { LogError(ex3.ToString()); LogWarning("Could not get properties for component!"); } try { foreach (FieldInfo declaredField in AccessTools.GetDeclaredFields(((object)compo).GetType())) { try { LogInfo($" - {declaredField.Name} = {declaredField.GetValue(compo)}"); } catch (Exception ex4) { LogError(ex4.ToString()); LogWarning("Could not get field: " + declaredField.Name + " for component!"); } } } catch (Exception ex5) { LogError(ex5.ToString()); LogWarning("Could not get fields for component!"); } } } } namespace Configs { public static class ConfigFileExtensions { public static bool DisableSaveOnConfigSet(this ConfigFile configFile) { bool saveOnConfigSet = configFile.SaveOnConfigSet; configFile.SaveOnConfigSet = false; return saveOnConfigSet; } } internal sealed class ConfigFileWatcher { private const long RELOAD_DELAY = 10000000L; private DateTime lastReadTime = DateTime.MinValue; private readonly ConfigFile configFile; private readonly string ConfigFileDir; private readonly string ConfigFileName; internal event Action OnConfigFileReloaded; internal ConfigFileWatcher(ConfigFile configFile) { this.configFile = configFile; ConfigFileDir = Directory.GetParent(configFile.ConfigFilePath).FullName; ConfigFileName = Path.GetFileName(configFile.ConfigFilePath); FileSystemWatcher fileSystemWatcher = new FileSystemWatcher(ConfigFileDir, ConfigFileName); fileSystemWatcher.Changed += ReloadConfigFile; fileSystemWatcher.Created += ReloadConfigFile; fileSystemWatcher.Renamed += ReloadConfigFile; fileSystemWatcher.IncludeSubdirectories = true; fileSystemWatcher.SynchronizingObject = ThreadingHelper.SynchronizingObject; fileSystemWatcher.EnableRaisingEvents = true; } private void InvokeOnConfigFileReloaded() { this.OnConfigFileReloaded?.SafeInvoke(); } internal void ReloadConfigFile(object sender, FileSystemEventArgs eventArgs) { DateTime now = DateTime.Now; long num = now.Ticks - lastReadTime.Ticks; if (!File.Exists(configFile.ConfigFilePath) || num < 10000000) { return; } try { Log.LogInfo("Reloading " + configFile.ConfigFilePath); bool saveOnConfigSet = configFile.DisableSaveOnConfigSet(); configFile.Reload(); configFile.SaveOnConfigSet = saveOnConfigSet; lastReadTime = now; InvokeOnConfigFileReloaded(); } catch { Log.LogError("There was an issue loading " + configFile.ConfigFilePath); Log.LogError("Please check your config entries for spelling and format!"); } } } internal static class SafeInvokeEvent { internal static void SafeInvoke(this Action events) { if (events == null) { return; } Delegate[] invocationList = events.GetInvocationList(); for (int i = 0; i < invocationList.Length; i++) { Action action = (Action)invocationList[i]; try { action(); } catch (Exception arg) { Log.LogWarning("Exception thrown at event " + new StackFrame(1).GetMethod().Name + $" in {action.Method.DeclaringType.Name}.{action.Method.Name}:\n{arg}"); } } } } }