Decompiled source of CaptainsLog v1.0.0

BepInEx/plugins/CaptainsLog.dll

Decompiled a day ago
using System;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn.Managers;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.6.2", FrameworkDisplayName = ".NET Framework 4.6.2")]
[assembly: AssemblyCompany("CaptainsLog")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+185e1c70765cc70e92e30acbece1449c14dba044")]
[assembly: AssemblyProduct("CaptainsLog")]
[assembly: AssemblyTitle("CaptainsLog")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace CaptainsLog;

[BepInPlugin("DeathMonger.CaptainsLog", "Captain's Log", "1.0.0")]
[BepInProcess("valheim.exe")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
public class CaptainsLogMod : BaseUnityPlugin
{
	public const string ModGuid = "DeathMonger.CaptainsLog";

	public static CaptainsLogMod Instance;

	internal static ManualLogSource Log;

	public ConfigEntry<bool> LoggingEnabled;

	public ConfigEntry<float> SampleIntervalSeconds;

	public ConfigEntry<bool> ShowHud;

	public ConfigEntry<float> HudOffsetX;

	public ConfigEntry<float> HudOffsetY;

	private readonly Harmony harmony = new Harmony("DeathMonger.CaptainsLog");

	private void Awake()
	{
		//IL_002e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0038: Expected O, but got Unknown
		//IL_006c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0076: Expected O, but got Unknown
		//IL_0098: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a2: Expected O, but got Unknown
		//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d2: Expected O, but got Unknown
		//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
		//IL_0102: Expected O, but got Unknown
		Instance = this;
		Log = ((BaseUnityPlugin)this).Logger;
		LoggingEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("Logging", "Enabled", true, new ConfigDescription("Log ship telemetry (speed, wind, heading) to a CSV file while sailing.", (AcceptableValueBase)null, Array.Empty<object>()));
		SampleIntervalSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Logging", "Sample Interval (s)", 0.5f, new ConfigDescription("Minimum time between logged samples.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 5f), Array.Empty<object>()));
		ShowHud = ((BaseUnityPlugin)this).Config.Bind<bool>("HUD", "Enabled", true, new ConfigDescription("Show a small HUD widget with live ship speed and wind data while sailing.", (AcceptableValueBase)null, Array.Empty<object>()));
		HudOffsetX = ((BaseUnityPlugin)this).Config.Bind<float>("HUD", "Offset X (px)", 10f, new ConfigDescription("Horizontal offset from the upper-left corner of the screen.", (AcceptableValueBase)null, Array.Empty<object>()));
		HudOffsetY = ((BaseUnityPlugin)this).Config.Bind<float>("HUD", "Offset Y (px)", 40f, new ConfigDescription("Vertical offset from the top of the screen.", (AcceptableValueBase)null, Array.Empty<object>()));
		harmony.PatchAll();
		Log.LogInfo((object)"Captain's Log armed.");
	}

	private void Update()
	{
		ShipHud.Tick();
	}

	private void OnDestroy()
	{
		ShipTelemetry.Shutdown();
	}
}
internal static class ShipHud
{
	private const float UpdateInterval = 0.2f;

	private const float StaleAfter = 0.75f;

	private const float Width = 280f;

	private const float Height = 70f;

	private static GameObject _go;

	private static Text _text;

	private static RectTransform _rect;

	private static float _lastUpdate;

	private static bool Enabled => CaptainsLogMod.Instance?.ShowHud?.Value ?? true;

	private static float OffsetX => CaptainsLogMod.Instance?.HudOffsetX?.Value ?? 10f;

	private static float OffsetY => CaptainsLogMod.Instance?.HudOffsetY?.Value ?? 40f;

	public static void Tick()
	{
		//IL_005f: Unknown result type (might be due to invalid IL or missing references)
		EnsureCreated();
		if ((Object)(object)_text == (Object)null)
		{
			return;
		}
		bool flag = Time.time - ShipTelemetry.LastSample.SampledAtTime < 0.75f;
		bool flag2 = Enabled && flag;
		if (_go.activeSelf != flag2)
		{
			_go.SetActive(flag2);
		}
		if (flag2)
		{
			_rect.anchoredPosition = new Vector2(OffsetX, 0f - OffsetY);
			float time = Time.time;
			if (!(time - _lastUpdate < 0.2f))
			{
				_lastUpdate = time;
				ShipSample lastSample = ShipTelemetry.LastSample;
				_text.text = $"Speed: {lastSample.SpeedMs:F1} m/s ({lastSample.SpeedKnots:F1} kn)  {lastSample.Propulsion}\n" + $"Wind: {lastSample.WindIntensity * 100f:F1}%  @ {lastSample.WindAngleDeg:F0}°\n" + $"Sail: {lastSample.WindAngleFactor * 100f:F0}%  Rudder: {lastSample.Rudder:F1}";
			}
		}
	}

	private static void EnsureCreated()
	{
		//IL_0041: 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_0060: Unknown result type (might be due to invalid IL or missing references)
		//IL_0076: Unknown result type (might be due to invalid IL or missing references)
		//IL_007c: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
		if (!((Object)(object)_go != (Object)null) && GUIManager.Instance != null && !((Object)(object)GUIManager.CustomGUIFront == (Object)null))
		{
			_go = GUIManager.Instance.CreateText(string.Empty, GUIManager.CustomGUIFront.transform, new Vector2(0f, 1f), new Vector2(0f, 1f), new Vector2(OffsetX, 0f - OffsetY), GUIManager.Instance.AveriaSerifBold, 16, GUIManager.Instance.ValheimOrange, true, Color.black, 280f, 70f, false);
			((Object)_go).name = "CaptainsLog_ShipHud";
			_rect = _go.GetComponent<RectTransform>();
			_rect.pivot = new Vector2(0f, 1f);
			_rect.anchoredPosition = new Vector2(OffsetX, 0f - OffsetY);
			_text = _go.GetComponent<Text>();
			_text.alignment = (TextAnchor)0;
			((Graphic)_text).raycastTarget = false;
		}
	}
}
internal struct ShipSample
{
	public float SpeedMs;

	public float SpeedKnots;

	public Speed SpeedSetting;

	public string Propulsion;

	public float Rudder;

	public float WindAngleDeg;

	public float WindAngleFactor;

	public float WindIntensity;

	public float SampledAtTime;
}
internal static class ShipTelemetry
{
	private const float MetersPerSecondToKnots = 1.9438444f;

	private static StreamWriter _writer;

	private static float _lastSampleTime = -999f;

	internal static ShipSample LastSample;

	internal unsafe static void Sample(Ship ship)
	{
		//IL_0037: Unknown result type (might be due to invalid IL or missing references)
		//IL_003c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0043: Unknown result type (might be due to invalid IL or missing references)
		//IL_006c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0060: Unknown result type (might be due to invalid IL or missing references)
		//IL_0071: Unknown result type (might be due to invalid IL or missing references)
		//IL_0093: Unknown result type (might be due to invalid IL or missing references)
		//IL_009a: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
		//IL_010e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0110: Unknown result type (might be due to invalid IL or missing references)
		//IL_0117: Unknown result type (might be due to invalid IL or missing references)
		float time = Time.time;
		float num = Mathf.Max(0.05f, CaptainsLogMod.Instance.SampleIntervalSeconds.Value);
		if (!(time - _lastSampleTime < num))
		{
			_lastSampleTime = time;
			Vector3 position = ((Component)ship).transform.position;
			float num2 = NormalizeAngle(((Component)ship).transform.eulerAngles.y);
			Vector3 val = (((Object)(object)EnvMan.instance != (Object)null) ? EnvMan.instance.GetWindDir() : Vector3.zero);
			float windIntensity = (((Object)(object)EnvMan.instance != (Object)null) ? EnvMan.instance.GetWindIntensity() : 0f);
			float num3 = NormalizeAngle(Mathf.Atan2(val.x, val.z) * 57.29578f);
			Biome val2 = Heightmap.FindBiome(position);
			string text = ((Object)((Component)ship).gameObject).name.Replace("(Clone)", string.Empty).Trim();
			float speed = ship.GetSpeed();
			Speed speedSetting = ship.GetSpeedSetting();
			ShipSample shipSample = (LastSample = new ShipSample
			{
				SpeedMs = speed,
				SpeedKnots = speed * 1.9438444f,
				SpeedSetting = speedSetting,
				Propulsion = PropulsionLabel(speedSetting),
				Rudder = ship.GetRudder(),
				WindAngleDeg = NormalizeSignedAngle(ship.GetWindAngle()),
				WindAngleFactor = ship.GetWindAngleFactor(),
				WindIntensity = windIntensity,
				SampledAtTime = time
			});
			if (CaptainsLogMod.Instance.LoggingEnabled.Value)
			{
				EnsureWriter();
				_writer.WriteLine(string.Join(",", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff", CultureInfo.InvariantCulture), text, position.x.ToString("F1", CultureInfo.InvariantCulture), position.z.ToString("F1", CultureInfo.InvariantCulture), ((object)(*(Biome*)(&val2))/*cast due to .constrained prefix*/).ToString(), shipSample.SpeedMs.ToString("F2", CultureInfo.InvariantCulture), shipSample.SpeedKnots.ToString("F2", CultureInfo.InvariantCulture), ((object)(*(Speed*)(&shipSample.SpeedSetting))/*cast due to .constrained prefix*/).ToString(), shipSample.Propulsion, shipSample.Rudder.ToString("F2", CultureInfo.InvariantCulture), num2.ToString("F1", CultureInfo.InvariantCulture), shipSample.WindAngleDeg.ToString("F1", CultureInfo.InvariantCulture), shipSample.WindAngleFactor.ToString("F2", CultureInfo.InvariantCulture), shipSample.WindIntensity.ToString("F2", CultureInfo.InvariantCulture), num3.ToString("F1", CultureInfo.InvariantCulture)));
			}
		}
	}

	private static float NormalizeAngle(float deg)
	{
		deg %= 360f;
		if (!(deg < 0f))
		{
			return deg;
		}
		return deg + 360f;
	}

	private static float NormalizeSignedAngle(float deg)
	{
		deg = NormalizeAngle(deg);
		if (!(deg > 180f))
		{
			return deg;
		}
		return deg - 360f;
	}

	private static string PropulsionLabel(Speed speed)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		//IL_0002: Unknown result type (might be due to invalid IL or missing references)
		//IL_0018: Expected I4, but got Unknown
		return (speed - 1) switch
		{
			0 => "Oars-Reverse", 
			1 => "Oars-Forward", 
			2 => "Sail-Half", 
			3 => "Sail-Full", 
			_ => "Idle", 
		};
	}

	private static void EnsureWriter()
	{
		if (_writer == null)
		{
			string text = Path.Combine(Paths.BepInExRootPath, "CaptainsLog");
			Directory.CreateDirectory(text);
			string text2 = Path.Combine(text, $"shiplog-{DateTime.Now:yyyyMMdd-HHmmss}.csv");
			_writer = new StreamWriter(text2, append: false)
			{
				AutoFlush = true
			};
			_writer.WriteLine("Timestamp,ShipName,PosX,PosZ,Biome,SpeedMs,SpeedKnots,SpeedSetting,Propulsion,Rudder,HeadingDeg,WindAngleDeg,WindAngleFactor,WindIntensity,WindDirWorldDeg");
			CaptainsLogMod.Log.LogInfo((object)("Logging ship telemetry to " + text2));
		}
	}

	internal static void Shutdown()
	{
		_writer?.Flush();
		_writer?.Dispose();
		_writer = null;
	}
}
[HarmonyPatch(typeof(Ship), "CustomFixedUpdate")]
internal static class Ship_CustomFixedUpdate_Telemetry
{
	[HarmonyPostfix]
	private static void Postfix(Ship __instance)
	{
		if (!((Object)(object)Player.m_localPlayer == (Object)null) && __instance.IsPlayerInBoat(Player.m_localPlayer))
		{
			ShipTelemetry.Sample(__instance);
		}
	}
}