Decompiled source of Vanilla Fix v1.0.6

sch1bugfix.dll

Decompiled 2 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using HarmonyLib;
using MelonLoader;
using Microsoft.CodeAnalysis;
using SCH1BugFix;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: MelonInfo(typeof(SCH1BugFixMod), "SCH1BugFix", "1.0.6", "Virtunerd", null)]
[assembly: MelonGame("TVGS", "Schedule I")]
[assembly: AssemblyVersion("0.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.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;
		}
	}
}
namespace SCH1BugFix
{
	public class SCH1BugFixMod : MelonMod
	{
		private const string AuthorName = "Virtunerd";

		private static readonly Type[] PatchTypes = new Type[11]
		{
			typeof(NpcOnDestroyPatch),
			typeof(EmployeeOnDestroyPatch),
			typeof(CustomerOnDestroyPatch),
			typeof(DealerOnDestroyPatch),
			typeof(WheelOnWeatherChangePatch),
			typeof(EnvironmentManagerUpdateWeatherEntitiesPatch),
			typeof(ConfigurationServiceNetworkerOnDestroyPatch),
			typeof(ContractUpdateTimingPatch),
			typeof(RectTransformLerpLocalScalePatch),
			typeof(NpcMovementFaceDirectionProcessMoveNextPatch),
			typeof(SetupCoroutineInvokeMoveNextPatch)
		};

		private static readonly Dictionary<string, DateTime> LastSuppressedLogUtcByLocation = new Dictionary<string, DateTime>(StringComparer.Ordinal);

		private static readonly object SuppressedLogLock = new object();

		private static Harmony harmony;

		public override void OnInitializeMelon()
		{
			try
			{
				harmony = ((MelonBase)this).HarmonyInstance;
				int value = ApplyPatchesSafely();
				MelonLogger.Msg($"[SCH1BugFix] sch1bugfix v{((MelonBase)this).Info.Version} by {"Virtunerd"} initialized. Applied {value}/{PatchTypes.Length} patch classes.");
			}
			catch (Exception value2)
			{
				MelonLogger.Error($"[SCH1BugFix] Critical init failure: {value2}");
			}
		}

		internal static Exception SuppressKnownNullRef(Exception exception, string location)
		{
			if (exception == null)
			{
				return null;
			}
			try
			{
				if (exception is NullReferenceException)
				{
					LogSuppressedOncePerWindow(location, "known game-side NRE");
					return null;
				}
				string text = exception.GetType().FullName ?? string.Empty;
				if (text.IndexOf("Il2CppException", StringComparison.OrdinalIgnoreCase) >= 0)
				{
					string text2 = exception.Message ?? string.Empty;
					if (text2.IndexOf("NullReferenceException", StringComparison.OrdinalIgnoreCase) >= 0)
					{
						LogSuppressedOncePerWindow(location, "wrapped game-side NRE");
						return null;
					}
				}
			}
			catch
			{
			}
			return exception;
		}

		internal static Exception SuppressKnownGameCoroutineExitError(Exception exception)
		{
			if (exception == null)
			{
				return null;
			}
			try
			{
				string text = exception.ToString();
				if (text.IndexOf("NullReferenceException", StringComparison.OrdinalIgnoreCase) < 0)
				{
					return exception;
				}
				if (text.IndexOf("FaceDirection_Process", StringComparison.OrdinalIgnoreCase) >= 0)
				{
					LogSuppressedOncePerWindow("NPCMovement.FaceDirection_Process.MoveNext", "wrapped game-side NRE (SetupCoroutine fallback)");
					return null;
				}
				if (text.IndexOf("StartConversate", StringComparison.OrdinalIgnoreCase) >= 0)
				{
					LogSuppressedOncePerWindow("NPCEvent_Conversate.StartConversate.MoveNext", "wrapped game-side NRE (SetupCoroutine fallback)");
					return null;
				}
				if (text.IndexOf("SetSeat", StringComparison.OrdinalIgnoreCase) >= 0)
				{
					LogSuppressedOncePerWindow("AvatarAnimation.SetSeat.Lerp.MoveNext", "wrapped game-side NRE (SetupCoroutine fallback)");
					return null;
				}
				if (text.IndexOf("ScheduleOne.", StringComparison.OrdinalIgnoreCase) >= 0)
				{
					LogSuppressedOncePerWindow("ScheduleOne (coroutine)", "wrapped game-side NRE (SetupCoroutine fallback)");
					return null;
				}
			}
			catch
			{
			}
			return exception;
		}

		private static int ApplyPatchesSafely()
		{
			int num = 0;
			if (harmony == null)
			{
				MelonLogger.Error("[SCH1BugFix] Harmony instance not available. No patches were applied.");
				return num;
			}
			for (int i = 0; i < PatchTypes.Length; i++)
			{
				Type type = PatchTypes[i];
				try
				{
					harmony.CreateClassProcessor(type).Patch();
					num++;
				}
				catch (Exception ex)
				{
					MelonLogger.Warning("[SCH1BugFix] Skipping patch " + type.Name + ": " + ex.Message);
				}
			}
			return num;
		}

		private static void LogSuppressedOncePerWindow(string location, string reason)
		{
			try
			{
				DateTime utcNow = DateTime.UtcNow;
				bool flag = false;
				lock (SuppressedLogLock)
				{
					if (!LastSuppressedLogUtcByLocation.TryGetValue(location, out var value) || (utcNow - value).TotalSeconds >= 5.0)
					{
						LastSuppressedLogUtcByLocation[location] = utcNow;
						flag = true;
					}
				}
				if (flag)
				{
					MelonLogger.Warning($"[SCH1BugFix] Suppressed {reason} in {location}.");
				}
			}
			catch
			{
			}
		}

		internal static MethodBase ResolveMethod(string[] typeNames, string methodName, int parameterCount)
		{
			try
			{
				for (int i = 0; i < typeNames.Length; i++)
				{
					Type type = AccessTools.TypeByName(typeNames[i]);
					if (!(type == null))
					{
						MethodBase methodBase = type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).FirstOrDefault((MethodInfo m) => m.Name == methodName && m.GetParameters().Length == parameterCount);
						if (methodBase != null)
						{
							return methodBase;
						}
					}
				}
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("[SCH1BugFix] ResolveMethod error for " + methodName + ": " + ex.Message);
			}
			MelonLogger.Warning($"[SCH1BugFix] Target not found: {string.Join(" | ", typeNames)}::{methodName}/{parameterCount}");
			return null;
		}
	}
	[HarmonyPatch]
	internal static class NpcOnDestroyPatch
	{
		private static MethodBase cachedTarget;

		private static bool Prepare()
		{
			cachedTarget = SCH1BugFixMod.ResolveMethod(new string[2] { "ScheduleOne.NPCs.NPC", "Il2CppScheduleOne.NPCs.NPC" }, "OnDestroy", 0);
			return cachedTarget != null;
		}

		private static MethodBase TargetMethod()
		{
			return cachedTarget;
		}

		[HarmonyFinalizer]
		[HarmonyWrapSafe]
		[HarmonyPriority(0)]
		private static Exception Finalizer(Exception __exception)
		{
			return SCH1BugFixMod.SuppressKnownNullRef(__exception, "NPC.OnDestroy");
		}
	}
	[HarmonyPatch]
	internal static class EmployeeOnDestroyPatch
	{
		private static MethodBase cachedTarget;

		private static bool Prepare()
		{
			cachedTarget = SCH1BugFixMod.ResolveMethod(new string[2] { "ScheduleOne.Employees.Employee", "Il2CppScheduleOne.Employees.Employee" }, "OnDestroy", 0);
			return cachedTarget != null;
		}

		private static MethodBase TargetMethod()
		{
			return cachedTarget;
		}

		[HarmonyFinalizer]
		[HarmonyWrapSafe]
		[HarmonyPriority(0)]
		private static Exception Finalizer(Exception __exception)
		{
			return SCH1BugFixMod.SuppressKnownNullRef(__exception, "Employee.OnDestroy");
		}
	}
	[HarmonyPatch]
	internal static class CustomerOnDestroyPatch
	{
		private static MethodBase cachedTarget;

		private static bool Prepare()
		{
			cachedTarget = SCH1BugFixMod.ResolveMethod(new string[2] { "ScheduleOne.Economy.Customer", "Il2CppScheduleOne.Economy.Customer" }, "OnDestroy", 0);
			return cachedTarget != null;
		}

		private static MethodBase TargetMethod()
		{
			return cachedTarget;
		}

		[HarmonyFinalizer]
		[HarmonyWrapSafe]
		[HarmonyPriority(0)]
		private static Exception Finalizer(Exception __exception)
		{
			return SCH1BugFixMod.SuppressKnownNullRef(__exception, "Customer.OnDestroy");
		}
	}
	[HarmonyPatch]
	internal static class DealerOnDestroyPatch
	{
		private static MethodBase cachedTarget;

		private static bool Prepare()
		{
			cachedTarget = SCH1BugFixMod.ResolveMethod(new string[2] { "ScheduleOne.Economy.Dealer", "Il2CppScheduleOne.Economy.Dealer" }, "OnDestroy", 0);
			return cachedTarget != null;
		}

		private static MethodBase TargetMethod()
		{
			return cachedTarget;
		}

		[HarmonyFinalizer]
		[HarmonyWrapSafe]
		[HarmonyPriority(0)]
		private static Exception Finalizer(Exception __exception)
		{
			return SCH1BugFixMod.SuppressKnownNullRef(__exception, "Dealer.OnDestroy");
		}
	}
	[HarmonyPatch]
	internal static class WheelOnWeatherChangePatch
	{
		private static MethodBase cachedTarget;

		private static bool Prepare()
		{
			cachedTarget = SCH1BugFixMod.ResolveMethod(new string[2] { "ScheduleOne.Vehicles.Wheel", "Il2CppScheduleOne.Vehicles.Wheel" }, "OnWeatherChange", 1);
			return cachedTarget != null;
		}

		private static MethodBase TargetMethod()
		{
			return cachedTarget;
		}

		[HarmonyFinalizer]
		[HarmonyWrapSafe]
		[HarmonyPriority(0)]
		private static Exception Finalizer(Exception __exception)
		{
			return SCH1BugFixMod.SuppressKnownNullRef(__exception, "Wheel.OnWeatherChange");
		}
	}
	[HarmonyPatch]
	internal static class EnvironmentManagerUpdateWeatherEntitiesPatch
	{
		private static MethodBase cachedTarget;

		private static bool Prepare()
		{
			cachedTarget = SCH1BugFixMod.ResolveMethod(new string[2] { "ScheduleOne.Weather.EnvironmentManager", "Il2CppScheduleOne.Weather.EnvironmentManager" }, "UpdateWeatherEntities", 0);
			return cachedTarget != null;
		}

		private static MethodBase TargetMethod()
		{
			return cachedTarget;
		}

		[HarmonyFinalizer]
		[HarmonyWrapSafe]
		[HarmonyPriority(0)]
		private static Exception Finalizer(Exception __exception)
		{
			return SCH1BugFixMod.SuppressKnownNullRef(__exception, "EnvironmentManager.UpdateWeatherEntities");
		}
	}
	[HarmonyPatch]
	internal static class ConfigurationServiceNetworkerOnDestroyPatch
	{
		private static MethodBase cachedTarget;

		private static bool Prepare()
		{
			cachedTarget = SCH1BugFixMod.ResolveMethod(new string[2] { "ScheduleOne.Configuration.ConfigurationServiceNetworker", "Il2CppScheduleOne.Configuration.ConfigurationServiceNetworker" }, "OnDestroy", 0);
			return cachedTarget != null;
		}

		private static MethodBase TargetMethod()
		{
			return cachedTarget;
		}

		[HarmonyFinalizer]
		[HarmonyWrapSafe]
		[HarmonyPriority(0)]
		private static Exception Finalizer(Exception __exception)
		{
			return SCH1BugFixMod.SuppressKnownNullRef(__exception, "ConfigurationServiceNetworker.OnDestroy");
		}
	}
	[HarmonyPatch]
	internal static class ContractUpdateTimingPatch
	{
		private static MethodBase cachedTarget;

		private static bool Prepare()
		{
			cachedTarget = SCH1BugFixMod.ResolveMethod(new string[2] { "ScheduleOne.Quests.Contract", "Il2CppScheduleOne.Quests.Contract" }, "UpdateTiming", 0);
			return cachedTarget != null;
		}

		private static MethodBase TargetMethod()
		{
			return cachedTarget;
		}

		[HarmonyFinalizer]
		[HarmonyWrapSafe]
		[HarmonyPriority(0)]
		private static Exception Finalizer(Exception __exception)
		{
			return SCH1BugFixMod.SuppressKnownNullRef(__exception, "Contract.UpdateTiming");
		}
	}
	[HarmonyPatch]
	internal static class RectTransformLerpLocalScalePatch
	{
		private static MethodBase cachedTarget;

		private static bool Prepare()
		{
			cachedTarget = SCH1BugFixMod.ResolveMethod(new string[2] { "ScheduleOne.UI.RectTransformLerp", "Il2CppScheduleOne.UI.RectTransformLerp" }, "LerpLocalScale", 2);
			return cachedTarget != null;
		}

		private static MethodBase TargetMethod()
		{
			return cachedTarget;
		}

		[HarmonyFinalizer]
		[HarmonyWrapSafe]
		[HarmonyPriority(0)]
		private static Exception Finalizer(Exception __exception)
		{
			return SCH1BugFixMod.SuppressKnownNullRef(__exception, "RectTransformLerp.LerpLocalScale");
		}
	}
	[HarmonyPatch]
	internal static class NpcMovementFaceDirectionProcessMoveNextPatch
	{
		private static MethodBase cachedTarget;

		private static bool Prepare()
		{
			cachedTarget = FindTarget();
			if (cachedTarget == null)
			{
				MelonLogger.Warning("[SCH1BugFix] NPCMovement FaceDirection iterator target not found. Skipping this optional patch.");
				return false;
			}
			return true;
		}

		private static MethodBase TargetMethod()
		{
			return cachedTarget;
		}

		private static MethodBase FindTarget()
		{
			string[] array = new string[2] { "ScheduleOne.NPCs.NPCMovement", "Il2CppScheduleOne.NPCs.NPCMovement" };
			try
			{
				for (int i = 0; i < array.Length; i++)
				{
					Type type = AccessTools.TypeByName(array[i]);
					if (type == null)
					{
						continue;
					}
					Type[] array2;
					try
					{
						array2 = type.GetNestedTypes(BindingFlags.Public | BindingFlags.NonPublic);
					}
					catch (ReflectionTypeLoadException ex)
					{
						array2 = ex.Types;
					}
					if (array2 == null)
					{
						continue;
					}
					foreach (Type type2 in array2)
					{
						if (!(type2 == null) && type2.Name.StartsWith("<FaceDirection_Process>d__", StringComparison.Ordinal))
						{
							MethodInfo method = type2.GetMethod("MoveNext", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
							if (method != null)
							{
								return method;
							}
						}
					}
				}
			}
			catch (Exception ex2)
			{
				MelonLogger.Warning("[SCH1BugFix] FindTarget error: " + ex2.Message);
			}
			return null;
		}

		[HarmonyFinalizer]
		[HarmonyWrapSafe]
		[HarmonyPriority(0)]
		private static Exception Finalizer(Exception __exception)
		{
			return SCH1BugFixMod.SuppressKnownNullRef(__exception, "NPCMovement.FaceDirection_Process.MoveNext");
		}
	}
	[HarmonyPatch]
	internal static class SetupCoroutineInvokeMoveNextPatch
	{
		private static MethodBase cachedTarget;

		private static bool Prepare()
		{
			try
			{
				cachedTarget = AccessTools.Method("UnityEngine.SetupCoroutine:InvokeMoveNext", (Type[])null, (Type[])null);
			}
			catch (Exception ex)
			{
				MelonLogger.Warning("[SCH1BugFix] SetupCoroutine target resolution error: " + ex.Message);
			}
			if (cachedTarget == null)
			{
				MelonLogger.Warning("[SCH1BugFix] SetupCoroutine.InvokeMoveNext not found. Skipping fallback patch.");
				return false;
			}
			return true;
		}

		private static MethodBase TargetMethod()
		{
			return cachedTarget;
		}

		[HarmonyFinalizer]
		[HarmonyWrapSafe]
		[HarmonyPriority(0)]
		private static Exception Finalizer(Exception __exception)
		{
			return SCH1BugFixMod.SuppressKnownGameCoroutineExitError(__exception);
		}
	}
}