Decompiled source of CaptainsLog v1.1.0

BepInEx/plugins/CaptainsLog.dll

Decompiled 2 months ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
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.1.0.0")]
[assembly: AssemblyInformationalVersion("1.1.0+a162c3bd2e79c37f805e1db9e91bdfa907dfec06")]
[assembly: AssemblyProduct("CaptainsLog")]
[assembly: AssemblyTitle("CaptainsLog")]
[assembly: AssemblyVersion("1.1.0.0")]
namespace CaptainsLog;

[BepInPlugin("DeathMonger.CaptainsLog", "Captain's Log", "1.1.0")]
[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<HudPosition> HudPosition;

	public ConfigEntry<float> HudNudgeX;

	public ConfigEntry<float> HudNudgeY;

	public ConfigEntry<float> HudBackgroundOpacity;

	public ConfigEntry<SpeedUnits> SpeedUnits;

	public ConfigEntry<bool> ShowWindAngle;

	public ConfigEntry<bool> ShowShipName;

	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_00c4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ce: Expected O, but got Unknown
		//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fe: Expected O, but got Unknown
		//IL_0124: Unknown result type (might be due to invalid IL or missing references)
		//IL_012e: Expected O, but got Unknown
		//IL_0162: Unknown result type (might be due to invalid IL or missing references)
		//IL_016c: Expected O, but got Unknown
		//IL_018e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0198: Expected O, but got Unknown
		//IL_01ba: Unknown result type (might be due to invalid IL or missing references)
		//IL_01c4: Expected O, but got Unknown
		//IL_01e6: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f0: 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>()));
		HudPosition = ((BaseUnityPlugin)this).Config.Bind<HudPosition>("HUD", "Position", CaptainsLog.HudPosition.BelowHotbar, new ConfigDescription("BelowHotbar: under the lowest hotbar row, including a second row added by a mod such as AzuExtendedPlayerInventory. Centered: beside the ship's rudder icon, following the ship, like Azumatt's Ship Stats. TopLeft: a fixed spot in the upper-left corner.", (AcceptableValueBase)null, Array.Empty<object>()));
		HudNudgeX = ((BaseUnityPlugin)this).Config.Bind<float>("HUD", "Nudge X (px)", 0f, new ConfigDescription("Moves the widget from its Position. Positive is right.", (AcceptableValueBase)null, Array.Empty<object>()));
		HudNudgeY = ((BaseUnityPlugin)this).Config.Bind<float>("HUD", "Nudge Y (px)", 0f, new ConfigDescription("Moves the widget from its Position. Positive is down.", (AcceptableValueBase)null, Array.Empty<object>()));
		HudBackgroundOpacity = ((BaseUnityPlugin)this).Config.Bind<float>("HUD", "Background Opacity", 0.5f, new ConfigDescription("Opacity of the dark panel behind the text. 0 hides it.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
		SpeedUnits = ((BaseUnityPlugin)this).Config.Bind<SpeedUnits>("HUD", "Speed Units", CaptainsLog.SpeedUnits.Knots, new ConfigDescription("Units for ship speed: Knots, MetersPerSecond, or KnotsWithMetersPerSecond (knots, then m/s in parentheses). The CSV always has both.", (AcceptableValueBase)null, Array.Empty<object>()));
		ShowWindAngle = ((BaseUnityPlugin)this).Config.Bind<bool>("HUD", "Show Wind Angle", true, new ConfigDescription("Show where the wind comes from relative to the ship (astern, port quarter, starboard beam, port bow, ahead...). Not a compass direction.", (AcceptableValueBase)null, Array.Empty<object>()));
		ShowShipName = ((BaseUnityPlugin)this).Config.Bind<bool>("HUD", "Show Ship Name", true, new ConfigDescription("Show the kind of ship you're aboard (Karve, Longship...) as the first line.", (AcceptableValueBase)null, Array.Empty<object>()));
		harmony.PatchAll();
		Log.LogInfo((object)"Captain's Log armed.");
	}

	private void Update()
	{
		if (!ShipTelemetry.IsDedicatedServer)
		{
			ShipHud.Tick();
		}
	}

	private void OnDestroy()
	{
		ShipTelemetry.Shutdown();
	}
}
public enum HudPosition
{
	BelowHotbar,
	Centered,
	TopLeft
}
public enum SpeedUnits
{
	Knots,
	MetersPerSecond,
	KnotsWithMetersPerSecond
}
internal static class ShipHud
{
	private const float UpdateInterval = 0.2f;

	private const float StaleAfter = 0.75f;

	private const float BarRescanInterval = 2f;

	private const float GapBelowHotbar = 6f;

	private static readonly Vector2 TopLeftBase = new Vector2(10f, 40f);

	private static readonly Vector2 CenteredBase = new Vector2(75f, -27f);

	private static GameObject _go;

	private static RectTransform _rect;

	private static RectTransform _parent;

	private static Image _background;

	private static Text _text;

	private static float _lastUpdate;

	private static float _lastBarScan = -999f;

	private static HotkeyBar[] _bars = (HotkeyBar[])(object)new HotkeyBar[0];

	private static readonly Vector3[] Corners = (Vector3[])(object)new Vector3[4];

	private static Ship _namedShip;

	private static string _shipName = string.Empty;

	private static CaptainsLogMod Mod => CaptainsLogMod.Instance;

	public static void Tick()
	{
		//IL_009c: Unknown result type (might be due to invalid IL or missing references)
		EnsureCreated();
		if ((Object)(object)_go == (Object)null)
		{
			return;
		}
		bool flag = Time.time - ShipTelemetry.LastSample.SampledAtTime < 0.75f;
		bool flag2 = Mod.ShowHud.Value && flag;
		if (_go.activeSelf != flag2)
		{
			_go.SetActive(flag2);
		}
		if (flag2)
		{
			Place();
			float time = Time.time;
			if (!(time - _lastUpdate < 0.2f))
			{
				_lastUpdate = time;
				((Graphic)_background).color = new Color(0f, 0f, 0f, Mod.HudBackgroundOpacity.Value);
				_text.text = Compose(ShipTelemetry.LastSample);
			}
		}
	}

	private static string Compose(ShipSample s)
	{
		//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c4: Expected I4, but got Unknown
		//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cd: Invalid comparison between Unknown and I4
		StringBuilder stringBuilder = new StringBuilder();
		if (Mod.ShowShipName.Value)
		{
			string text = ShipName(ShipTelemetry.LastShip);
			if (text.Length > 0)
			{
				stringBuilder.Append(text).Append('\n');
			}
		}
		stringBuilder.Append("Speed: ").Append(FormatSpeed(s.SpeedMs));
		stringBuilder.Append($"\nWind: {s.WindIntensity * 100f:F0}%");
		if (Mod.ShowWindAngle.Value)
		{
			stringBuilder.Append(" from ").Append(WindBearing(s.WindAngleDeg));
		}
		Speed speedSetting = s.SpeedSetting;
		switch (speedSetting - 1)
		{
		case 2:
		case 3:
		{
			string arg = (((int)s.SpeedSetting == 4) ? "Full" : "Half");
			stringBuilder.Append($"\nSail: {arg}, {s.WindAngleFactor * 100f:F0}% efficient");
			break;
		}
		case 1:
			stringBuilder.Append("\nOars: Forward");
			break;
		case 0:
			stringBuilder.Append("\nOars: Reverse");
			break;
		}
		if (Mathf.Abs(s.Rudder) >= 0.02f)
		{
			stringBuilder.Append(string.Format("\nRudder: {0:F0}% {1}", Mathf.Abs(s.Rudder) * 100f, (s.Rudder > 0f) ? "right" : "left"));
		}
		return stringBuilder.ToString();
	}

	private static string ShipName(Ship ship)
	{
		if ((Object)(object)ship == (Object)null)
		{
			return string.Empty;
		}
		if ((Object)(object)ship == (Object)(object)_namedShip)
		{
			return _shipName;
		}
		_namedShip = ship;
		string text = ((Component)ship).GetComponent<Piece>()?.m_name;
		_shipName = (string.IsNullOrEmpty(text) ? ((Object)((Component)ship).gameObject).name.Replace("(Clone)", string.Empty).Trim() : Localization.instance.Localize(text));
		return _shipName;
	}

	private static string WindBearing(float angle)
	{
		float num = Mathf.Abs(angle);
		if (num < 5f)
		{
			return "astern";
		}
		if (num > 175f)
		{
			return "ahead";
		}
		string obj = ((angle > 0f) ? "starboard" : "port");
		string text = ((num < 67.5f) ? "quarter" : ((num <= 112.5f) ? "beam" : "bow"));
		return obj + " " + text;
	}

	private static string FormatSpeed(float ms)
	{
		float num = ms * 1.9438444f;
		return Mod.SpeedUnits.Value switch
		{
			SpeedUnits.MetersPerSecond => $"{ms:F1} m/s", 
			SpeedUnits.KnotsWithMetersPerSecond => $"{num:F1} kn ({ms:F1} m/s)", 
			_ => $"{num:F1} kn", 
		};
	}

	private static void Place()
	{
		//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
		//IL_0101: Unknown result type (might be due to invalid IL or missing references)
		//IL_0126: Unknown result type (might be due to invalid IL or missing references)
		//IL_012b: Unknown result type (might be due to invalid IL or missing references)
		//IL_012c: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
		//IL_006b: Unknown result type (might be due to invalid IL or missing references)
		//IL_007e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0083: Unknown result type (might be due to invalid IL or missing references)
		//IL_0088: Unknown result type (might be due to invalid IL or missing references)
		//IL_008f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0091: Unknown result type (might be due to invalid IL or missing references)
		//IL_0096: Unknown result type (might be due to invalid IL or missing references)
		//IL_009b: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
		Vector2 val = default(Vector2);
		((Vector2)(ref val))..ctor(Mod.HudNudgeX.Value, 0f - Mod.HudNudgeY.Value);
		HudPosition value = Mod.HudPosition.Value;
		if (value == HudPosition.Centered)
		{
			Ship lastShip = ShipTelemetry.LastShip;
			Camera mainCamera = Utils.GetMainCamera();
			if ((Object)(object)lastShip != (Object)null && (Object)(object)mainCamera != (Object)null)
			{
				_rect.pivot = new Vector2(0f, 0.5f);
				Vector3 val2 = Utils.WorldToScreenPointScaled(mainCamera, lastShip.m_controlGuiPos.position);
				SetLocal(Vector2.op_Implicit(((Transform)_parent).InverseTransformPoint(val2)) + CenteredBase + val);
				return;
			}
		}
		_rect.pivot = new Vector2(0f, 1f);
		if (value == HudPosition.BelowHotbar && TryGetHotbarBottomLeft(out var bottomLeft))
		{
			SetLocal(bottomLeft + new Vector2(0f, -6f) + val);
			return;
		}
		Rect rect = _parent.rect;
		SetLocal(new Vector2(((Rect)(ref rect)).xMin + TopLeftBase.x, ((Rect)(ref rect)).yMax - TopLeftBase.y) + val);
	}

	private static void SetLocal(Vector2 p)
	{
		//IL_0005: Unknown result type (might be due to invalid IL or missing references)
		//IL_000b: 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)
		((Transform)_rect).localPosition = new Vector3(p.x, p.y, 0f);
	}

	private static bool TryGetHotbarBottomLeft(out Vector2 bottomLeft)
	{
		//IL_003f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0044: Unknown result type (might be due to invalid IL or missing references)
		//IL_0048: Unknown result type (might be due to invalid IL or missing references)
		//IL_0153: Unknown result type (might be due to invalid IL or missing references)
		//IL_0158: Unknown result type (might be due to invalid IL or missing references)
		//IL_009c: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a3: Expected O, but got Unknown
		//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00da: Unknown result type (might be due to invalid IL or missing references)
		//IL_00df: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
		//IL_010d: Unknown result type (might be due to invalid IL or missing references)
		if (Time.time - _lastBarScan > 2f)
		{
			_lastBarScan = Time.time;
			_bars = ((Component)_parent).GetComponentsInChildren<HotkeyBar>(false);
		}
		bool result = false;
		float num = float.MaxValue;
		float num2 = float.MaxValue;
		Rect rect = _parent.rect;
		float y = ((Rect)(ref rect)).center.y;
		HotkeyBar[] bars = _bars;
		foreach (HotkeyBar val in bars)
		{
			if ((Object)(object)val == (Object)null || !((Behaviour)val).isActiveAndEnabled)
			{
				continue;
			}
			foreach (Transform item in ((Component)val).transform)
			{
				Transform val2 = item;
				if (!((Component)val2).gameObject.activeSelf)
				{
					continue;
				}
				RectTransform val3 = (RectTransform)(object)((val2 is RectTransform) ? val2 : null);
				if (val3 != null)
				{
					val3.GetWorldCorners(Corners);
					Vector3 val4 = ((Transform)_parent).InverseTransformPoint(Corners[0]);
					if (!(((Transform)_parent).InverseTransformPoint(Corners[2]).y < y))
					{
						num = Mathf.Min(num, val4.x);
						num2 = Mathf.Min(num2, val4.y);
						result = true;
					}
				}
			}
		}
		bottomLeft = new Vector2(num, num2);
		return result;
	}

	private static void EnsureCreated()
	{
		//IL_0070: Unknown result type (might be due to invalid IL or missing references)
		//IL_007a: Expected O, but got Unknown
		//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
		//IL_0107: Unknown result type (might be due to invalid IL or missing references)
		//IL_0111: Expected O, but got Unknown
		//IL_015b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0160: Unknown result type (might be due to invalid IL or missing references)
		//IL_0176: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ab: Unknown result type (might be due to invalid IL or missing references)
		//IL_01e6: Unknown result type (might be due to invalid IL or missing references)
		if (!((Object)(object)_go != (Object)null) && !((Object)(object)Hud.instance == (Object)null) && !((Object)(object)Hud.instance.m_rootObject == (Object)null) && GUIManager.Instance != null)
		{
			_parent = Hud.instance.m_rootObject.GetComponent<RectTransform>();
			if (!((Object)(object)_parent == (Object)null))
			{
				_go = new GameObject("CaptainsLog_ShipHud", new Type[1] { typeof(RectTransform) });
				_go.transform.SetParent((Transform)(object)_parent, false);
				_rect = _go.GetComponent<RectTransform>();
				RectTransform rect = _rect;
				RectTransform rect2 = _rect;
				Vector2 val = default(Vector2);
				((Vector2)(ref val))..ctor(0.5f, 0.5f);
				rect2.anchorMax = val;
				rect.anchorMin = val;
				_rect.pivot = new Vector2(0f, 1f);
				_background = _go.AddComponent<Image>();
				((Graphic)_background).raycastTarget = false;
				VerticalLayoutGroup obj = _go.AddComponent<VerticalLayoutGroup>();
				((LayoutGroup)obj).padding = new RectOffset(8, 8, 5, 5);
				((HorizontalOrVerticalLayoutGroup)obj).childControlWidth = true;
				((HorizontalOrVerticalLayoutGroup)obj).childControlHeight = true;
				((HorizontalOrVerticalLayoutGroup)obj).childForceExpandWidth = false;
				((HorizontalOrVerticalLayoutGroup)obj).childForceExpandHeight = false;
				ContentSizeFitter obj2 = _go.AddComponent<ContentSizeFitter>();
				obj2.horizontalFit = (FitMode)2;
				obj2.verticalFit = (FitMode)2;
				GameObject val2 = new GameObject("Text", new Type[1] { typeof(RectTransform) });
				val2.transform.SetParent(_go.transform, false);
				_text = val2.AddComponent<Text>();
				_text.font = GUIManager.Instance.AveriaSerifBold;
				_text.fontSize = 16;
				((Graphic)_text).color = GUIManager.Instance.ValheimOrange;
				_text.horizontalOverflow = (HorizontalWrapMode)1;
				_text.verticalOverflow = (VerticalWrapMode)1;
				_text.alignment = (TextAnchor)0;
				((Graphic)_text).raycastTarget = false;
				((Shadow)val2.AddComponent<Outline>()).effectColor = Color.black;
				_go.SetActive(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
{
	internal const float MetersPerSecondToKnots = 1.9438444f;

	private static StreamWriter _writer;

	private static float _lastSampleTime = -999f;

	private static readonly Dictionary<ZDOID, float> _lastServerSampleTime = new Dictionary<ZDOID, float>();

	internal static ShipSample LastSample;

	internal static Ship LastShip;

	internal static bool IsDedicatedServer
	{
		get
		{
			if ((Object)(object)ZNet.instance != (Object)null)
			{
				return ZNet.instance.IsDedicated();
			}
			return false;
		}
	}

	private static float Interval => Mathf.Max(0.05f, CaptainsLogMod.Instance.SampleIntervalSeconds.Value);

	internal static void SampleLocal(Ship ship)
	{
		float time = Time.time;
		if (!(time - _lastSampleTime < Interval))
		{
			_lastSampleTime = time;
			ShipSample sample = (LastSample = Build(ship, ship.GetSpeed(), time));
			LastShip = ship;
			Write(ship, sample, Player.m_localPlayer.GetPlayerName());
		}
	}

	internal static void SampleServer(Ship ship)
	{
		//IL_0063: Unknown result type (might be due to invalid IL or missing references)
		//IL_0068: Unknown result type (might be due to invalid IL or missing references)
		//IL_006f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0054: Unknown result type (might be due to invalid IL or missing references)
		//IL_008b: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
		if (!CaptainsLogMod.Instance.LoggingEnabled.Value)
		{
			return;
		}
		ZNetView component = ((Component)ship).GetComponent<ZNetView>();
		if ((Object)(object)component == (Object)null || !component.IsValid())
		{
			return;
		}
		ZDO zDO = component.GetZDO();
		long num = (((Object)(object)ship.m_shipControlls != (Object)null) ? ship.m_shipControlls.GetUser() : 0);
		if (num != 0L || (int)ship.GetSpeedSetting() != 0)
		{
			float time = Time.time;
			ZDOID uid = zDO.m_uid;
			if (!_lastServerSampleTime.TryGetValue(uid, out var value) || !(time - value < Interval))
			{
				_lastServerSampleTime[uid] = time;
				float speedMs = (component.IsOwner() ? ship.GetSpeed() : Vector3.Dot(zDO.GetVec3(ZDOVars.s_velHash, Vector3.zero), ((Component)ship).transform.forward));
				Write(ship, Build(ship, speedMs, time), PlayerNameFor(num, zDO.GetOwner()));
			}
		}
	}

	private static string PlayerNameFor(long helmUser, long ownerSession)
	{
		if (helmUser != 0L)
		{
			foreach (Player allPlayer in Player.GetAllPlayers())
			{
				if ((Object)(object)allPlayer != (Object)null && allPlayer.GetPlayerID() == helmUser)
				{
					return allPlayer.GetPlayerName();
				}
			}
		}
		ZNetPeer peer = ZNet.instance.GetPeer(ownerSession);
		if (peer == null)
		{
			return string.Empty;
		}
		return peer.m_playerName;
	}

	private static ShipSample Build(Ship ship, float speedMs, float now)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_0046: Unknown result type (might be due to invalid IL or missing references)
		//IL_0047: Unknown result type (might be due to invalid IL or missing references)
		//IL_004e: Unknown result type (might be due to invalid IL or missing references)
		Speed speedSetting = ship.GetSpeedSetting();
		float windIntensity = (((Object)(object)EnvMan.instance != (Object)null) ? EnvMan.instance.GetWindIntensity() : 0f);
		return new ShipSample
		{
			SpeedMs = speedMs,
			SpeedKnots = speedMs * 1.9438444f,
			SpeedSetting = speedSetting,
			Propulsion = PropulsionLabel(speedSetting),
			Rudder = ship.GetRudderValue(),
			WindAngleDeg = NormalizeSignedAngle(ship.GetWindAngle()),
			WindAngleFactor = ship.GetWindAngleFactor(),
			WindIntensity = windIntensity,
			SampledAtTime = now
		};
	}

	private unsafe static void Write(Ship ship, ShipSample sample, string playerName)
	{
		//IL_0018: Unknown result type (might be due to invalid IL or missing references)
		//IL_001d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0024: 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_0041: Unknown result type (might be due to invalid IL or missing references)
		//IL_0052: Unknown result type (might be due to invalid IL or missing references)
		//IL_0053: Unknown result type (might be due to invalid IL or missing references)
		//IL_0059: Unknown result type (might be due to invalid IL or missing references)
		//IL_0070: 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_0076: Unknown result type (might be due to invalid IL or missing references)
		if (CaptainsLogMod.Instance.LoggingEnabled.Value)
		{
			Vector3 position = ((Component)ship).transform.position;
			float num = NormalizeAngle(((Component)ship).transform.eulerAngles.y);
			Vector3 val = (((Object)(object)EnvMan.instance != (Object)null) ? EnvMan.instance.GetWindDir() : Vector3.zero);
			float num2 = 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();
			EnsureWriter();
			_writer.WriteLine(string.Join(",", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff", CultureInfo.InvariantCulture), CsvField(playerName), text, position.x.ToString("F1", CultureInfo.InvariantCulture), position.z.ToString("F1", CultureInfo.InvariantCulture), ((object)(*(Biome*)(&val2))/*cast due to .constrained prefix*/).ToString(), sample.SpeedMs.ToString("F2", CultureInfo.InvariantCulture), sample.SpeedKnots.ToString("F2", CultureInfo.InvariantCulture), ((object)(*(Speed*)(&sample.SpeedSetting))/*cast due to .constrained prefix*/).ToString(), sample.Propulsion, sample.Rudder.ToString("F2", CultureInfo.InvariantCulture), num.ToString("F1", CultureInfo.InvariantCulture), sample.WindAngleDeg.ToString("F1", CultureInfo.InvariantCulture), sample.WindAngleFactor.ToString("F2", CultureInfo.InvariantCulture), sample.WindIntensity.ToString("F2", CultureInfo.InvariantCulture), num2.ToString("F1", CultureInfo.InvariantCulture)));
		}
	}

	private static string CsvField(string value)
	{
		if (string.IsNullOrEmpty(value))
		{
			return string.Empty;
		}
		if (value.IndexOfAny(new char[4] { ',', '"', '\r', '\n' }) < 0)
		{
			return value;
		}
		return "\"" + value.Replace("\"", "\"\"") + "\"";
	}

	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 arg = (IsDedicatedServer ? "shiplog-server" : "shiplog");
			string text2 = Path.Combine(text, $"{arg}-{DateTime.Now:yyyyMMdd-HHmmss}.csv");
			_writer = new StreamWriter(text2, append: false)
			{
				AutoFlush = true
			};
			_writer.WriteLine("Timestamp,Player,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 (ShipTelemetry.IsDedicatedServer)
		{
			ShipTelemetry.SampleServer(__instance);
		}
		else if (!((Object)(object)Player.m_localPlayer == (Object)null) && __instance.IsPlayerInBoat(Player.m_localPlayer))
		{
			ShipTelemetry.SampleLocal(__instance);
		}
	}
}