Decompiled source of BaseRaidTimer v1.0.1

BepInEx/plugins/BaseRaidTimer/BaseRaidTimer.dll

Decompiled a week ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BaseRaidTimer.Configuration;
using BaseRaidTimer.Localization;
using BaseRaidTimer.Runtime;
using BaseRaidTimer.UI;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using Fusion;
using HarmonyLib;
using ModSettingsMenu.Api;
using SunkenlandLocalizationAPI.Api;
using TMPro;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("BaseRaidTimer")]
[assembly: AssemblyDescription("Base raid countdown timer mod for Sunkenland by Ice Box Studio")]
[assembly: AssemblyCompany("Ice Box Studio")]
[assembly: AssemblyProduct("BaseRaidTimer")]
[assembly: AssemblyCopyright("Copyright © 2026 Ice Box Studio All rights reserved.")]
[assembly: ComVisible(false)]
[assembly: Guid("6d68fc4b-c0c5-4663-a743-f2da4e5a1268")]
[assembly: AssemblyFileVersion("1.0.1.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("1.0.1.0")]
namespace BaseRaidTimer
{
	[BepInPlugin("IceBoxStudio.Sunkenland.BaseRaidTimer", "BaseRaidTimer", "1.0.1")]
	[BepInDependency("IceBoxStudio.Sunkenland.ModSettingsMenu", "1.2.0")]
	[BepInDependency("IceBoxStudio.Sunkenland.LocalizationAPI", "1.1.0")]
	public sealed class BaseRaidTimer : BaseUnityPlugin
	{
		private Harmony _harmony;

		internal static ManualLogSource Log { get; private set; }

		private void Awake()
		{
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			LocalizationApi.LanguageChanged += OnLanguageChanged;
			try
			{
				Log.LogInfo((object)"=============================================");
				Log.LogInfo((object)("BaseRaidTimer " + I18n.Text("plugin.initializing")));
				Log.LogInfo((object)(I18n.Text("plugin.author_prefix") + "Ice Box Studio(https://steamcommunity.com/id/ibox666/)"));
				ConfigManager.Bind(((BaseUnityPlugin)this).Config);
				RegisterSettings();
				_harmony = new Harmony("IceBoxStudio.Sunkenland.BaseRaidTimer");
				_harmony.PatchAll(Assembly.GetExecutingAssembly());
				Log.LogInfo((object)("BaseRaidTimer " + I18n.Text("plugin.initialized")));
				Log.LogInfo((object)"=============================================");
			}
			catch (Exception arg)
			{
				Log.LogError((object)$"Failed to initialize Base Raid Timer: {arg}");
			}
		}

		private static void RegisterSettings()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Expected O, but got Unknown
			ModSettingsRegistry.Register("IceBoxStudio.Sunkenland.BaseRaidTimer", new ModSettingsModOptions
			{
				Name = "Base Raid Timer",
				LocalizedName = () => I18n.Text("mod.name"),
				Description = I18n.Text("mod.description"),
				Author = "Ice Box Studio",
				Version = "1.0.1",
				NexusModsId = 81,
				ThunderstoreTeam = "Ice_Box_Studio_Sunkenland",
				ThunderstoreModName = "BaseRaidTimer"
			});
		}

		private static void OnLanguageChanged(string language)
		{
			RegisterSettings();
		}
	}
	public static class PluginInfo
	{
		public const string PLUGIN_GUID = "IceBoxStudio.Sunkenland.BaseRaidTimer";

		public const string PLUGIN_NAME = "BaseRaidTimer";

		public const string PLUGIN_VERSION = "1.0.1";
	}
}
namespace BaseRaidTimer.UI
{
	internal static class RaidTimerDisplay
	{
		internal static void Append(TextMeshProUGUI timeText)
		{
			if (ConfigManager.Enabled && !((Object)(object)timeText == (Object)null) && RaidCountdown.TryGet(out var state, out var seconds))
			{
				((TMP_Text)timeText).enableWordWrapping = false;
				((TMP_Text)timeText).text = ((TMP_Text)timeText).text + "  |  " + GetText(state, seconds);
			}
		}

		private static string GetText(RaidState state, int seconds)
		{
			if (state == RaidState.HostDataUnavailable)
			{
				return I18n.Text("ui.raid.host_data_unavailable");
			}
			return I18n.Text("ui.raid.countdown", FormatTime(seconds));
		}

		private static string FormatTime(int totalSeconds)
		{
			int num = totalSeconds / 86400;
			int num2 = totalSeconds / 3600 % 24;
			int num3 = totalSeconds / 60 % 60;
			int num4 = totalSeconds % 60;
			if (num > 0)
			{
				return I18n.Text("ui.duration.days", num, num2, num3, num4);
			}
			return I18n.Text("ui.duration.time", num2, num3, num4);
		}
	}
}
namespace BaseRaidTimer.Runtime
{
	internal enum RaidState
	{
		Countdown,
		HostDataUnavailable
	}
	internal static class RaidCountdown
	{
		internal static bool TryGet(out RaidState state, out int seconds)
		{
			state = RaidState.Countdown;
			seconds = 0;
			GameLogics code = GameLogics.code;
			Mainframe code2 = Mainframe.code;
			if ((Object)(object)code == (Object)null || (Object)(object)code2 == (Object)null || (Object)(object)code2.Networking == (Object)null)
			{
				return false;
			}
			if (!code2.Networking.IsDedicatedServer && !code2.Networking.IsInSinglePlayerGameSession && !code2.Networking.IsServerAndInGameSession)
			{
				if (!RaidSync.TryGet(out var visible, out seconds))
				{
					state = RaidState.HostDataUnavailable;
					return true;
				}
				state = RaidState.Countdown;
				return visible;
			}
			return TryGetHost(code, out seconds);
		}

		internal static bool TryGetHost(GameLogics logic, out int seconds)
		{
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			seconds = 0;
			Mainframe code = Mainframe.code;
			if ((Object)(object)logic == (Object)null || (Object)(object)code == (Object)null || (Object)(object)code.WorldManager == (Object)null)
			{
				return false;
			}
			GlobalData globalData = code.GlobalData;
			if ((Object)(object)globalData == (Object)null || (Object)(object)((SimulationBehaviour)globalData).Object == (Object)null)
			{
				return false;
			}
			if ((int)code.WorldManager.EnemyRaidDifficulty == 0 && !logic.SlaverAttacking)
			{
				return false;
			}
			if (globalData.GameTime <= 1 && !logic.SlaverAttacking)
			{
				return false;
			}
			if (float.IsNaN(logic.nextSiegeTime) || float.IsInfinity(logic.nextSiegeTime))
			{
				return false;
			}
			seconds = Mathf.Max(0, Mathf.FloorToInt(logic.nextSiegeTime));
			return true;
		}
	}
	internal static class RaidSync
	{
		private enum MessageType : byte
		{
			Hello,
			Hidden,
			Countdown
		}

		private const int PacketSize = 12;

		private const byte ProtocolVersion = 1;

		private const float StateTimeout = 5f;

		private const float RequestInterval = 2f;

		private const int MaxRequests = 3;

		private static readonly HashSet<PlayerRef> Clients = new HashSet<PlayerRef>();

		private static NetworkRunner _runner;

		private static FusionCallbacksHandler _callbacks;

		private static bool _hasState;

		private static bool _visible;

		private static int _seconds;

		private static float _receivedAt;

		private static float _lastRequestAt = -2f;

		private static int _requestCount;

		internal static void Bind()
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: 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_005f: 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_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Expected O, but got Unknown
			NetworkRunner runner = GetRunner();
			if (_runner == runner)
			{
				return;
			}
			Unbind();
			if (!((Object)(object)runner == (Object)null))
			{
				_runner = runner;
				_callbacks = new FusionCallbacksHandler
				{
					PlayerLeft = OnPlayerLeft,
					Shutdown = OnShutdown,
					DisconnectedFromServer = OnDisconnected,
					HostMigration = OnHostMigration,
					ReliableDataReceived = OnData,
					SceneLoadStart = OnSceneLoadStart
				};
				runner.AddCallbacks((INetworkRunnerCallbacks[])(object)new INetworkRunnerCallbacks[1] { (INetworkRunnerCallbacks)_callbacks });
				if (runner.IsClient && !runner.IsServer)
				{
					Request(runner, force: true);
				}
			}
		}

		internal static void Tick(GameLogics logic)
		{
			Bind();
			NetworkRunner runner = _runner;
			if (!((Object)(object)runner == (Object)null))
			{
				if (runner.IsServer)
				{
					Broadcast(runner, logic);
				}
				else if (runner.IsClient && !IsFresh())
				{
					Request(runner, force: false);
				}
			}
		}

		internal static bool TryGet(out bool visible, out int seconds)
		{
			visible = false;
			seconds = 0;
			if (!IsFresh())
			{
				return false;
			}
			visible = _visible;
			seconds = _seconds;
			return true;
		}

		internal static bool IsPacket(ArraySegment<byte> data)
		{
			byte[] array = data.Array;
			if (array == null || data.Count < 4)
			{
				return false;
			}
			int offset = data.Offset;
			if (array[offset] == 66 && array[offset + 1] == 82 && array[offset + 2] == 84)
			{
				return array[offset + 3] == 83;
			}
			return false;
		}

		private static NetworkRunner GetRunner()
		{
			Mainframe code = Mainframe.code;
			if ((Object)(object)code == (Object)null || (Object)(object)code.Networking == (Object)null)
			{
				return null;
			}
			return code.Networking.CurrentRunner;
		}

		private static void Broadcast(NetworkRunner runner, GameLogics logic)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: 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_0045: Unknown result type (might be due to invalid IL or missing references)
			if (Clients.Count == 0)
			{
				return;
			}
			byte[] array = CreateState(logic);
			foreach (PlayerRef activePlayer in runner.ActivePlayers)
			{
				if (activePlayer != runner.LocalPlayer && Clients.Contains(activePlayer))
				{
					runner.SendReliableDataToPlayer(activePlayer, array);
				}
			}
		}

		private static void SendState(NetworkRunner runner, PlayerRef player)
		{
			//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_0010: Unknown result type (might be due to invalid IL or missing references)
			if (!(player == runner.LocalPlayer))
			{
				runner.SendReliableDataToPlayer(player, CreateState(GameLogics.code));
			}
		}

		private static byte[] CreateState(GameLogics logic)
		{
			if (!RaidCountdown.TryGetHost(logic, out var seconds))
			{
				return CreatePacket(MessageType.Hidden, 0);
			}
			return CreatePacket(MessageType.Countdown, seconds);
		}

		private static void Request(NetworkRunner runner, bool force)
		{
			float time = Time.time;
			if (force || (_requestCount < 3 && !(time - _lastRequestAt < 2f)))
			{
				runner.SendReliableDataToServer(CreatePacket(MessageType.Hello, 0));
				_lastRequestAt = time;
				_requestCount++;
			}
		}

		private static void OnData(NetworkRunner runner, PlayerRef player, ArraySegment<byte> data)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			if (runner != _runner || !TryRead(data, out var type, out var seconds))
			{
				return;
			}
			if (runner.IsServer)
			{
				if (type == MessageType.Hello && Clients.Add(player))
				{
					SendState(runner, player);
				}
			}
			else if (runner.IsClient && type != MessageType.Hello)
			{
				_hasState = true;
				_visible = type == MessageType.Countdown;
				_seconds = (_visible ? seconds : 0);
				_receivedAt = Time.time;
				_requestCount = 0;
			}
		}

		private static void OnPlayerLeft(NetworkRunner runner, PlayerRef player)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			if (runner == _runner && runner.IsServer)
			{
				Clients.Remove(player);
			}
		}

		private static void OnShutdown(NetworkRunner runner, ShutdownReason reason)
		{
			Drop(runner);
		}

		private static void OnDisconnected(NetworkRunner runner)
		{
			Drop(runner);
		}

		private static void OnHostMigration(NetworkRunner runner, HostMigrationToken token)
		{
			if (runner == _runner)
			{
				Reset();
			}
		}

		private static void OnSceneLoadStart(NetworkRunner runner)
		{
			if (runner == _runner)
			{
				ClearState();
				_lastRequestAt = -2f;
				_requestCount = 0;
			}
		}

		private static bool IsFresh()
		{
			if (_hasState)
			{
				return Time.time - _receivedAt <= 5f;
			}
			return false;
		}

		private static bool TryRead(ArraySegment<byte> data, out MessageType type, out int seconds)
		{
			type = MessageType.Hello;
			seconds = 0;
			if (data.Count != 12 || !IsPacket(data))
			{
				return false;
			}
			byte[] array = data.Array;
			int offset = data.Offset;
			if (array[offset + 4] != 1)
			{
				return false;
			}
			type = (MessageType)array[offset + 5];
			if (type != MessageType.Hello && type != MessageType.Hidden && type != MessageType.Countdown)
			{
				return false;
			}
			seconds = array[offset + 8] | (array[offset + 9] << 8) | (array[offset + 10] << 16) | (array[offset + 11] << 24);
			if (type == MessageType.Countdown)
			{
				return seconds >= 0;
			}
			return true;
		}

		private static byte[] CreatePacket(MessageType type, int seconds)
		{
			return new byte[12]
			{
				66,
				82,
				84,
				83,
				1,
				(byte)type,
				0,
				0,
				(byte)seconds,
				(byte)(seconds >> 8),
				(byte)(seconds >> 16),
				(byte)(seconds >> 24)
			};
		}

		private static void Unbind()
		{
			NetworkRunner runner = _runner;
			FusionCallbacksHandler callbacks = _callbacks;
			_runner = null;
			_callbacks = null;
			Reset();
			if ((Object)(object)runner != (Object)null && callbacks != null)
			{
				runner.RemoveCallbacks((INetworkRunnerCallbacks[])(object)new INetworkRunnerCallbacks[1] { (INetworkRunnerCallbacks)callbacks });
			}
		}

		private static void Drop(NetworkRunner runner)
		{
			if (runner == _runner)
			{
				_runner = null;
				_callbacks = null;
				Reset();
			}
		}

		private static void Reset()
		{
			Clients.Clear();
			ClearState();
			_lastRequestAt = -2f;
			_requestCount = 0;
		}

		private static void ClearState()
		{
			_hasState = false;
			_visible = false;
			_seconds = 0;
			_receivedAt = 0f;
		}
	}
}
namespace BaseRaidTimer.Patches
{
	[HarmonyPatch(typeof(GameLogics), "CS")]
	internal static class GameLogicsCsPatch
	{
		private static void Postfix(GameLogics __instance)
		{
			RaidSync.Tick(__instance);
		}
	}
	[HarmonyPatch(typeof(GameLogics), "Start")]
	internal static class GameLogicsStartPatch
	{
		private static void Postfix()
		{
			RaidSync.Bind();
		}
	}
	[HarmonyPatch(typeof(NetworkRunnerCallbackManager), "OnReliableDataReceived", new Type[]
	{
		typeof(NetworkRunner),
		typeof(PlayerRef),
		typeof(ArraySegment<byte>)
	})]
	internal static class ReliableDataLogPatch
	{
		private static bool Prefix(ArraySegment<byte> data)
		{
			return !RaidSync.IsPacket(data);
		}
	}
	[HarmonyPatch(typeof(UICombat), "Update")]
	internal static class UICombatUpdatePatch
	{
		private static void Postfix(TextMeshProUGUI ___tmpHour)
		{
			RaidTimerDisplay.Append(___tmpHour);
		}
	}
}
namespace BaseRaidTimer.Localization
{
	internal static class I18n
	{
		private const string FileName = "BaseRaidTimer.Localization.json";

		private static readonly ModLocalizer _localizer = Load();

		internal static ModLocalizer Localizer => _localizer;

		internal static string Text(string key, params object[] args)
		{
			return _localizer.GetLocalizedText(key, args);
		}

		private static ModLocalizer Load()
		{
			string directoryName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
			ModLocalizer obj = LocalizationApi.For("IceBoxStudio.Sunkenland.BaseRaidTimer");
			obj.RegisterJson(Path.Combine(directoryName, "BaseRaidTimer.Localization.json"));
			return obj;
		}
	}
}
namespace BaseRaidTimer.Configuration
{
	internal static class ConfigManager
	{
		private const string Section = "Base Raid Timer";

		private static ConfigEntry<bool> _enabled;

		internal static bool Enabled
		{
			get
			{
				if (_enabled != null)
				{
					return _enabled.Value;
				}
				return true;
			}
		}

		internal static void Bind(ConfigFile config)
		{
			_enabled = config.Bind<bool>("Base Raid Timer", "Enabled", true, I18n.Localizer.Config("config.enabled", 10, "Base Raid Timer", "config.section", 10, (AcceptableValueBase)null));
		}
	}
}