Decompiled source of WeeklyTimerFix v1.0.0

BepInEx/plugins/WeeklyTimerFix.dll

Decompiled 2 weeks ago
using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Pigeon.Math;
using TMPro;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("Sparroh")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("WeeklyTimerFix")]
[assembly: AssemblyTitle("WeeklyTimerFix")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[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;
		}
	}
}
internal static class ConfigManager
{
	private static ConfigFile config;

	private static ManualLogSource log;

	private static FileSystemWatcher watcher;

	private static bool reloadPending;

	private static float reloadReadyTime;

	private const float ReloadDebounceSeconds = 0.25f;

	public static ConfigEntry<bool> UseUnifiedOvertimeSeed { get; private set; }

	public static bool UnifiedOvertimeSeedEnabled
	{
		get
		{
			if (UseUnifiedOvertimeSeed != null)
			{
				return UseUnifiedOvertimeSeed.Value;
			}
			return false;
		}
	}

	public static void Initialize(ConfigFile configFile, ManualLogSource logSource)
	{
		config = configFile;
		log = logSource;
		UseUnifiedOvertimeSeed = config.Bind<bool>("General", "Use Unified Overtime Seed", false, "When enabled, Weekly Overtime mission seed (and legacy weekData content) use the same Mycopunk week boundary as Incursion/Cranius/ResetWeekly (Thu 17:00 UTC). When disabled (default), overtime keeps the vanilla seed/roll so it matches unmodded clients. The weekly reset TIMER is always synced either way.");
		StartWatcher();
	}

	public static void Tick()
	{
		if (!reloadPending || config == null || Time.unscaledTime < reloadReadyTime)
		{
			return;
		}
		reloadPending = false;
		try
		{
			config.Reload();
			ManualLogSource obj = log;
			if (obj != null)
			{
				obj.LogInfo((object)"Config reloaded.");
			}
			WeeklyTimerPatches.OnConfigChanged();
		}
		catch (Exception ex)
		{
			ManualLogSource obj2 = log;
			if (obj2 != null)
			{
				obj2.LogError((object)("Config reload failed: " + ex.Message));
			}
		}
	}

	public static void Dispose()
	{
		if (watcher != null)
		{
			watcher.EnableRaisingEvents = false;
			watcher.Changed -= OnWatcherEvent;
			watcher.Created -= OnWatcherEvent;
			watcher.Renamed -= OnWatcherEvent;
			watcher.Dispose();
			watcher = null;
		}
	}

	private static void StartWatcher()
	{
		try
		{
			string configPath = Paths.ConfigPath;
			string fileName = Path.GetFileName(config.ConfigFilePath);
			if (!string.IsNullOrEmpty(configPath) && !string.IsNullOrEmpty(fileName))
			{
				watcher = new FileSystemWatcher(configPath, fileName)
				{
					NotifyFilter = (NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.LastWrite)
				};
				watcher.Changed += OnWatcherEvent;
				watcher.Created += OnWatcherEvent;
				watcher.Renamed += OnWatcherEvent;
				watcher.EnableRaisingEvents = true;
			}
		}
		catch (Exception ex)
		{
			ManualLogSource obj = log;
			if (obj != null)
			{
				obj.LogWarning((object)("Config file watcher not started: " + ex.Message));
			}
		}
	}

	private static void OnWatcherEvent(object sender, FileSystemEventArgs e)
	{
		reloadPending = true;
		reloadReadyTime = Time.unscaledTime + 0.25f;
	}
}
[BepInPlugin("sparroh.weeklytimerfix", "WeeklyTimerFix", "1.0.0")]
[MycoMod(/*Could not decode attribute arguments.*/)]
public class SparrohPlugin : BaseUnityPlugin
{
	public const string PluginGUID = "sparroh.weeklytimerfix";

	public const string PluginName = "WeeklyTimerFix";

	public const string PluginVersion = "1.0.0";

	internal static ManualLogSource Log;

	private Harmony _harmony;

	private void Awake()
	{
		//IL_0022: Unknown result type (might be due to invalid IL or missing references)
		//IL_002c: Expected O, but got Unknown
		Log = ((BaseUnityPlugin)this).Logger;
		ConfigManager.Initialize(((BaseUnityPlugin)this).Config, ((BaseUnityPlugin)this).Logger);
		_harmony = new Harmony("sparroh.weeklytimerfix");
		try
		{
			_harmony.PatchAll(typeof(WeeklyTimerPatches));
			WeeklyTimerPatches.OnConfigChanged();
			Log.LogInfo((object)("WeeklyTimerFix v1.0.0 loaded — overtime timer synced to Global.CurrentWeek; unified overtime seed=" + (ConfigManager.UnifiedOvertimeSeedEnabled ? "ON" : "OFF (vanilla)") + "; granular weekly countdowns enabled."));
		}
		catch (Exception arg)
		{
			Log.LogError((object)$"Error applying patches: {arg}");
		}
	}

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

	private void OnDestroy()
	{
		ConfigManager.Dispose();
		Harmony harmony = _harmony;
		if (harmony != null)
		{
			harmony.UnpatchSelf();
		}
	}
}
internal static class WeeklyTimerPatches
{
	private static readonly FieldInfo WeekDataField = AccessTools.Field(typeof(WeeklyOvertimeAssignment), "weekData");

	private static readonly Type WeekDataType = AccessTools.Inner(typeof(WeeklyOvertimeAssignment), "WeekData");

	private static bool weekDataCleared;

	private static bool warnedWeekDataNeedsRestart;

	private static readonly FieldInfo MissionSelectTimerField = AccessTools.Field(typeof(MissionSelectButton), "timer");

	private static readonly FieldInfo MissionSelectLastTimerField = AccessTools.Field(typeof(MissionSelectButton), "lastTimerUpdateTime");

	private static readonly FieldInfo MissionSelectMissionField = AccessTools.Field(typeof(MissionSelectButton), "mission");

	internal static long GetNextWeeklyResetUnix()
	{
		return 1752166800 + (long)(Global.CurrentWeek + 1) * 604800L;
	}

	internal static int GetSyncedOvertimeSeed()
	{
		return MathUtil.Squirrel3Hash((int)GetNextWeeklyResetUnix(), 47832154);
	}

	internal static void OnConfigChanged()
	{
		if (ConfigManager.UnifiedOvertimeSeedEnabled)
		{
			ClearLegacyWeekData();
		}
		else if (weekDataCleared && !warnedWeekDataNeedsRestart)
		{
			warnedWeekDataNeedsRestart = true;
			ManualLogSource log = SparrohPlugin.Log;
			if (log != null)
			{
				log.LogWarning((object)"Use Unified Overtime Seed was turned off after legacy weekData was cleared this session. Restart the game to fully restore vanilla overtime weekData content.");
			}
		}
	}

	internal static void ClearLegacyWeekData()
	{
		if (!ConfigManager.UnifiedOvertimeSeedEnabled || weekDataCleared || WeekDataField == null || WeekDataType == null)
		{
			return;
		}
		WeeklyOvertimeAssignment val = null;
		try
		{
			if (Global.Instance != null)
			{
				val = Global.Instance.OvertimeAssignment;
			}
		}
		catch (Exception)
		{
			return;
		}
		if (val != null)
		{
			Array value = Array.CreateInstance(WeekDataType, 0);
			WeekDataField.SetValue(val, value);
			weekDataCleared = true;
			ManualLogSource log = SparrohPlugin.Log;
			if (log != null)
			{
				log.LogInfo((object)"Cleared legacy WeeklyOvertimeAssignment.weekData; unified overtime seed uses Global.CurrentWeek.");
			}
		}
	}

	[HarmonyPatch(typeof(Global), "LoadInstance")]
	[HarmonyPostfix]
	private static void GlobalLoadInstancePostfix()
	{
		ClearLegacyWeekData();
	}

	[HarmonyPatch(typeof(WeeklyOvertimeAssignment), "GetResetDateTime")]
	[HarmonyPrefix]
	private static bool GetResetDateTimePrefix(out long unixTime, ref bool __result)
	{
		unixTime = GetNextWeeklyResetUnix();
		__result = true;
		return false;
	}

	[HarmonyPatch(typeof(WeeklyOvertimeAssignment), "GetMissionSeed")]
	[HarmonyPrefix]
	private static bool GetMissionSeedPrefix(out int seed, ref bool __result)
	{
		if (!ConfigManager.UnifiedOvertimeSeedEnabled)
		{
			seed = 0;
			return true;
		}
		ClearLegacyWeekData();
		seed = GetSyncedOvertimeSeed();
		__result = true;
		return false;
	}

	[HarmonyPatch(typeof(MissionSelectButton), "Update")]
	[HarmonyPrefix]
	private static bool MissionSelectButtonUpdatePrefix(MissionSelectButton __instance)
	{
		//IL_005a: 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)
		if (MissionSelectTimerField == null || MissionSelectLastTimerField == null || MissionSelectMissionField == null)
		{
			return true;
		}
		object? value = MissionSelectTimerField.GetValue(__instance);
		TextMeshProUGUI val = (TextMeshProUGUI)((value is TextMeshProUGUI) ? value : null);
		if ((Object)(object)val == (Object)null)
		{
			return false;
		}
		long num = default(long);
		if (!(MissionSelectMissionField.GetValue(__instance) is MissionData val2) || ((MissionData)(ref val2)).Container == null || !((MissionData)(ref val2)).Container.GetResetDateTime(((MissionData)(ref val2)).Mission, ref num))
		{
			return false;
		}
		float unscaledTime = Time.unscaledTime;
		float num2 = (float)MissionSelectLastTimerField.GetValue(__instance);
		if (unscaledTime - num2 <= 1f)
		{
			return false;
		}
		MissionSelectLastTimerField.SetValue(__instance, unscaledTime);
		int num3 = FillGranularWeeklyDuration(num - Global.GlobalSeconds, Global.charBuffer);
		((TMP_Text)val).SetCharArray(Global.charBuffer, 0, num3);
		return false;
	}

	internal static int FillGranularWeeklyDuration(long totalSeconds, char[] buffer)
	{
		if (totalSeconds < 0)
		{
			totalSeconds = 0L;
		}
		int num = (int)(totalSeconds / 86400);
		int num2 = (int)(totalSeconds % 86400 / 3600);
		int value = (int)(totalSeconds % 3600 / 60);
		int value2 = (int)(totalSeconds % 60);
		int index = 0;
		if (num > 0)
		{
			index = WriteInt(num, buffer, index);
			buffer[index++] = 'd';
			buffer[index++] = ' ';
			index = WriteTwoDigits(num2, buffer, index);
			buffer[index++] = ':';
			index = WriteTwoDigits(value, buffer, index);
			buffer[index++] = ':';
			return WriteTwoDigits(value2, buffer, index);
		}
		if (num2 > 0)
		{
			index = WriteInt(num2, buffer, index);
			buffer[index++] = ':';
			index = WriteTwoDigits(value, buffer, index);
			buffer[index++] = ':';
			return WriteTwoDigits(value2, buffer, index);
		}
		index = WriteInt(value, buffer, index);
		buffer[index++] = ':';
		return WriteTwoDigits(value2, buffer, index);
	}

	private static int WriteInt(int value, char[] buffer, int index)
	{
		if (value == 0)
		{
			buffer[index++] = '0';
			return index;
		}
		int num = index;
		for (int num2 = value; num2 > 0; num2 /= 10)
		{
			buffer[index++] = (char)(48 + num2 % 10);
		}
		int num3 = index - 1;
		while (num < num3)
		{
			char c = buffer[num];
			buffer[num] = buffer[num3];
			buffer[num3] = c;
			num++;
			num3--;
		}
		return index;
	}

	private static int WriteTwoDigits(int value, char[] buffer, int index)
	{
		if (value < 0)
		{
			value = 0;
		}
		if (value > 99)
		{
			value %= 100;
		}
		buffer[index++] = (char)(48 + value / 10);
		buffer[index++] = (char)(48 + value % 10);
		return index;
	}
}
namespace WeeklyTimerFix
{
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "WeeklyTimerFix";

		public const string PLUGIN_NAME = "WeeklyTimerFix";

		public const string PLUGIN_VERSION = "1.0.0";
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}