Please disclose if any significant portion of your mod was created 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 CaptainsLog v1.1.0
BepInEx/plugins/CaptainsLog.dll
Decompiled 2 months agousing 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); } } }