using 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}");
}
}
}
}
}