Decompiled source of LethalOptimizer v1.0.1

BepInEx/plugins/LethalOptimizer.dll

Decompiled 8 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using GameNetcodeStuff;
using HarmonyLib;
using LethalOptimizer.Patches;
using Microsoft.CodeAnalysis;
using Unity.Netcode;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("LethalOptimizer")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Micro-optimizations for smoother gameplay")]
[assembly: AssemblyFileVersion("1.0.1.0")]
[assembly: AssemblyInformationalVersion("1.0.1")]
[assembly: AssemblyProduct("LethalOptimizer")]
[assembly: AssemblyTitle("LethalOptimizer")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.1.0")]
[module: UnverifiableCode]
[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 LethalOptimizer
{
	[BepInPlugin("mrbub.lethaloptimizer", "LethalOptimizer", "1.0.1")]
	public class Plugin : BaseUnityPlugin
	{
		internal static ManualLogSource Logger;

		internal static ConfigFile ConfigFile;

		internal static ConfigEntry<bool> CacheCollisionComponents;

		internal static ConfigEntry<bool> OptimizeStringAllocations;

		internal static ConfigEntry<bool> CacheRepeatedLookups;

		private void Awake()
		{
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Expected O, but got Unknown
			Logger = ((BaseUnityPlugin)this).Logger;
			ConfigFile = ((BaseUnityPlugin)this).Config;
			InitializeConfig();
			Harmony val = new Harmony("mrbub.lethaloptimizer");
			if (CacheCollisionComponents.Value)
			{
				val.PatchAll(typeof(CollisionCachePatches));
				Logger.LogInfo((object)"Collision component caching enabled");
			}
			if (OptimizeStringAllocations.Value)
			{
				val.PatchAll(typeof(StringOptimizationPatches));
				Logger.LogInfo((object)"String allocation optimization enabled");
			}
			if (CacheRepeatedLookups.Value)
			{
				val.PatchAll(typeof(ComponentLookupPatches));
				Logger.LogInfo((object)"Component lookup caching enabled");
			}
			val.PatchAll(typeof(ShipItemOptimizationPatches));
			Logger.LogInfo((object)"Ship item collection optimization enabled");
			Logger.LogInfo((object)"LethalOptimizer v1.0.1 loaded successfully!");
		}

		private void InitializeConfig()
		{
			CacheCollisionComponents = ((BaseUnityPlugin)this).Config.Bind<bool>("Caching", "CacheCollisionComponents", true, "Cache PlayerControllerB lookups in collision/trigger handlers");
			OptimizeStringAllocations = ((BaseUnityPlugin)this).Config.Bind<bool>("Strings", "OptimizeStringAllocations", true, "Use StringBuilder for string formatting to reduce allocations");
			CacheRepeatedLookups = ((BaseUnityPlugin)this).Config.Bind<bool>("Caching", "CacheRepeatedLookups", true, "Cache NetworkObject lookups during enemy and scrap spawning");
		}
	}
	public static class PluginInfo
	{
		public const string PLUGIN_GUID = "mrbub.lethaloptimizer";

		public const string PLUGIN_NAME = "LethalOptimizer";

		public const string PLUGIN_VERSION = "1.0.1";
	}
}
namespace LethalOptimizer.Patches
{
	[HarmonyPatch]
	internal class CollisionCachePatches
	{
		private static readonly Dictionary<GameObject, PlayerControllerB> playerControllerCache = new Dictionary<GameObject, PlayerControllerB>();

		public static PlayerControllerB GetCachedPlayerController(GameObject gameObject)
		{
			if ((Object)(object)gameObject == (Object)null)
			{
				return null;
			}
			if (!playerControllerCache.TryGetValue(gameObject, out var value))
			{
				value = gameObject.GetComponent<PlayerControllerB>();
				if ((Object)(object)value != (Object)null)
				{
					playerControllerCache[gameObject] = value;
				}
			}
			return value;
		}

		[HarmonyPatch(typeof(QuicksandTrigger), "OnTriggerStay")]
		[HarmonyPrefix]
		private static bool QuicksandTriggerStayPrefix(QuicksandTrigger __instance, Collider other)
		{
			try
			{
				if (__instance.movementHinderance < 1f)
				{
					return false;
				}
				PlayerControllerB cachedPlayerController = GetCachedPlayerController(((Component)other).gameObject);
				if ((Object)(object)cachedPlayerController != (Object)(object)GameNetworkManager.Instance.localPlayerController && (Object)(object)cachedPlayerController != (Object)null && (Object)(object)cachedPlayerController.underwaterCollider != (Object)(object)__instance)
				{
					cachedPlayerController.underwaterCollider = ((Component)__instance).gameObject.GetComponent<Collider>();
					return false;
				}
				if (!__instance.sinkingLocalPlayer)
				{
					return false;
				}
				PlayerControllerB cachedPlayerController2 = GetCachedPlayerController(((Component)other).gameObject);
				if ((Object)(object)cachedPlayerController2 != (Object)(object)GameNetworkManager.Instance.localPlayerController)
				{
					return false;
				}
				if (cachedPlayerController2.isClimbingLadder || cachedPlayerController2.inSpecialInteractAnimation || StartOfRound.Instance.suckingPlayersOutOfShip)
				{
					return false;
				}
				if (__instance.isWater && !cachedPlayerController2.isUnderwater)
				{
					cachedPlayerController2.underwaterCollider = ((Component)__instance).gameObject.GetComponent<Collider>();
					cachedPlayerController2.isUnderwater = true;
				}
				return false;
			}
			catch (Exception ex)
			{
				Plugin.Logger.LogError((object)("Error in QuicksandTriggerStay patch: " + ex.Message));
				return true;
			}
		}

		[HarmonyPatch(typeof(QuicksandTrigger), "OnTriggerExit")]
		[HarmonyPrefix]
		private static bool QuicksandTriggerExitPrefix(QuicksandTrigger __instance, Collider other)
		{
			try
			{
				if (__instance.sinkingLocalPlayer)
				{
					if (!__instance.isWater && ((Component)other).gameObject.CompareTag("Player"))
					{
						PlayerControllerB cachedPlayerController = GetCachedPlayerController(((Component)other).gameObject);
						if ((Object)(object)cachedPlayerController == (Object)(object)GameNetworkManager.Instance.localPlayerController)
						{
							return false;
						}
						if ((Object)(object)cachedPlayerController != (Object)null)
						{
							cachedPlayerController.isUnderwater = false;
						}
					}
					__instance.sinkingLocalPlayer = false;
					return false;
				}
				return false;
			}
			catch (Exception ex)
			{
				Plugin.Logger.LogError((object)("Error in QuicksandTriggerExit patch: " + ex.Message));
				return true;
			}
		}

		[HarmonyPatch(typeof(RoundManager), "LoadNewLevel")]
		[HarmonyPostfix]
		private static void ClearCacheOnLevelChange()
		{
			playerControllerCache.Clear();
		}

		[HarmonyPatch(typeof(StartOfRound), "ResetShip")]
		[HarmonyPostfix]
		private static void ClearCacheOnShipReset()
		{
			playerControllerCache.Clear();
		}
	}
	[HarmonyPatch]
	internal class ComponentLookupPatches
	{
		private static readonly Dictionary<GameObject, NetworkObject> networkObjectCache = new Dictionary<GameObject, NetworkObject>();

		public static NetworkObject GetCachedNetworkObject(GameObject gameObject)
		{
			if ((Object)(object)gameObject == (Object)null)
			{
				return null;
			}
			if (!networkObjectCache.TryGetValue(gameObject, out var value))
			{
				value = gameObject.GetComponent<NetworkObject>();
				if ((Object)(object)value != (Object)null)
				{
					networkObjectCache[gameObject] = value;
				}
			}
			return value;
		}

		public static NetworkObject GetCachedNetworkObjectInChildren(GameObject gameObject)
		{
			if ((Object)(object)gameObject == (Object)null)
			{
				return null;
			}
			NetworkObject cachedNetworkObject = GetCachedNetworkObject(gameObject);
			if ((Object)(object)cachedNetworkObject != (Object)null)
			{
				return cachedNetworkObject;
			}
			cachedNetworkObject = gameObject.GetComponentInChildren<NetworkObject>();
			if ((Object)(object)cachedNetworkObject != (Object)null)
			{
				networkObjectCache[gameObject] = cachedNetworkObject;
			}
			return cachedNetworkObject;
		}

		[HarmonyPatch(typeof(RoundManager), "SpawnScrapInLevel")]
		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> OptimizeNetworkObjectLookups(IEnumerable<CodeInstruction> instructions)
		{
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Expected O, but got Unknown
			List<CodeInstruction> list = new List<CodeInstruction>(instructions);
			MethodInfo methodInfo = AccessTools.Method(typeof(GameObject), "GetComponent", (Type[])null, new Type[1] { typeof(NetworkObject) });
			MethodInfo methodInfo2 = AccessTools.Method(typeof(ComponentLookupPatches), "GetCachedNetworkObject", (Type[])null, (Type[])null);
			if (methodInfo == null || methodInfo2 == null)
			{
				Plugin.Logger.LogWarning((object)"Could not find methods for NetworkObject caching in SpawnScrapInLevel");
				return list;
			}
			int num = 0;
			for (int i = 0; i < list.Count; i++)
			{
				if (CodeInstructionExtensions.Calls(list[i], methodInfo))
				{
					list[i] = new CodeInstruction(OpCodes.Call, (object)methodInfo2);
					num++;
				}
			}
			if (num > 0)
			{
				Plugin.Logger.LogInfo((object)$"Replaced {num} GetComponent<NetworkObject> calls in SpawnScrapInLevel");
			}
			return list;
		}

		[HarmonyPatch(typeof(RoundManager), "SpawnEnemyOnServer")]
		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> OptimizeEnemySpawnLookups(IEnumerable<CodeInstruction> instructions)
		{
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0094: Expected O, but got Unknown
			List<CodeInstruction> list = new List<CodeInstruction>(instructions);
			MethodInfo methodInfo = AccessTools.Method(typeof(GameObject), "GetComponentInChildren", (Type[])null, new Type[1] { typeof(NetworkObject) });
			MethodInfo methodInfo2 = AccessTools.Method(typeof(ComponentLookupPatches), "GetCachedNetworkObjectInChildren", (Type[])null, (Type[])null);
			if (methodInfo == null || methodInfo2 == null)
			{
				Plugin.Logger.LogWarning((object)"Could not find methods for NetworkObject caching in SpawnEnemyOnServer");
				return list;
			}
			int num = 0;
			for (int i = 0; i < list.Count; i++)
			{
				if (CodeInstructionExtensions.Calls(list[i], methodInfo))
				{
					list[i] = new CodeInstruction(OpCodes.Call, (object)methodInfo2);
					num++;
				}
			}
			if (num > 0)
			{
				Plugin.Logger.LogInfo((object)$"Replaced {num} GetComponentInChildren<NetworkObject> calls in SpawnEnemyOnServer");
			}
			return list;
		}

		[HarmonyPatch(typeof(RoundManager), "DespawnPropsAtEndOfRound")]
		[HarmonyPostfix]
		private static void ClearCacheOnRoundEnd()
		{
			networkObjectCache.Clear();
		}

		[HarmonyPatch(typeof(StartOfRound), "ResetShip")]
		[HarmonyPostfix]
		private static void ClearCacheOnShipReset()
		{
			networkObjectCache.Clear();
		}
	}
	[HarmonyPatch]
	internal class DebugLogPatches
	{
		[HarmonyPatch(typeof(RoundManager), "SpawnScrapInLevel")]
		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> RemoveDebugLogsFromSpawnScrap(IEnumerable<CodeInstruction> instructions)
		{
			List<CodeInstruction> list = new List<CodeInstruction>(instructions);
			MethodInfo methodInfo = AccessTools.Method(typeof(Debug), "Log", new Type[1] { typeof(object) }, (Type[])null);
			if (methodInfo == null)
			{
				Plugin.Logger.LogWarning((object)"Could not find Debug.Log method, skipping transpiler");
				return list;
			}
			int num = 0;
			for (int num2 = list.Count - 1; num2 >= 0; num2--)
			{
				if (CodeInstructionExtensions.Calls(list[num2], methodInfo))
				{
					int num3 = 1;
					int num4 = num2 - 1;
					while (num4 >= 0 && num3 < 10 && (list[num4].opcode == OpCodes.Ldstr || list[num4].opcode == OpCodes.Ldarg || list[num4].opcode == OpCodes.Ldloc))
					{
						num3++;
						num4--;
					}
					for (int i = 0; i < num3 && num2 - i >= 0; i++)
					{
						list[num2 - i].opcode = OpCodes.Nop;
					}
					num++;
				}
			}
			if (num > 0)
			{
				Plugin.Logger.LogInfo((object)$"Removed {num} Debug.Log calls from SpawnScrapInLevel");
			}
			return list;
		}
	}
	[HarmonyPatch]
	internal class ShipItemOptimizationPatches
	{
		private static readonly HashSet<GrabbableObject> scrapCollectedSet = new HashSet<GrabbableObject>();

		private static bool setInitialized = false;

		[HarmonyPatch(typeof(RoundManager), "CollectNewScrapForThisRound")]
		[HarmonyPrefix]
		private static bool CollectNewScrapPrefix(RoundManager __instance, GrabbableObject scrapObject)
		{
			try
			{
				if (!scrapObject.itemProperties.isScrap)
				{
					return false;
				}
				if (!setInitialized && __instance.scrapCollectedThisRound != null)
				{
					scrapCollectedSet.Clear();
					foreach (GrabbableObject item in __instance.scrapCollectedThisRound)
					{
						if ((Object)(object)item != (Object)null)
						{
							scrapCollectedSet.Add(item);
						}
					}
					setInitialized = true;
				}
				if (scrapCollectedSet.Contains(scrapObject))
				{
					return false;
				}
				if (scrapObject.scrapPersistedThroughRounds)
				{
					return false;
				}
				__instance.scrapCollectedThisRound.Add(scrapObject);
				scrapCollectedSet.Add(scrapObject);
				HUDManager.Instance.AddNewScrapFoundToDisplay(scrapObject);
				return false;
			}
			catch (Exception ex)
			{
				Plugin.Logger.LogError((object)("Error in CollectNewScrapForThisRound patch: " + ex.Message));
				return true;
			}
		}

		[HarmonyPatch(typeof(RoundManager), "DespawnPropsAtEndOfRound")]
		[HarmonyPostfix]
		private static void ClearScrapSetOnRoundEnd()
		{
			scrapCollectedSet.Clear();
			setInitialized = false;
		}

		[HarmonyPatch(typeof(StartOfRound), "ResetShip")]
		[HarmonyPostfix]
		private static void ClearScrapSetOnShipReset()
		{
			scrapCollectedSet.Clear();
			setInitialized = false;
		}
	}
	[HarmonyPatch]
	internal class StringOptimizationPatches
	{
		private static readonly StringBuilder stringBuilder = new StringBuilder(256);

		[HarmonyPatch(typeof(GrabbableObject), "SetScrapValue")]
		[HarmonyPrefix]
		private static bool OptimizeSetScrapValue(GrabbableObject __instance, int setValueTo)
		{
			try
			{
				__instance.scrapValue = setValueTo;
				ScanNodeProperties componentInChildren = ((Component)__instance).gameObject.GetComponentInChildren<ScanNodeProperties>();
				if ((Object)(object)componentInChildren == (Object)null)
				{
					return true;
				}
				stringBuilder.Clear();
				stringBuilder.Append("Value: $");
				stringBuilder.Append(setValueTo);
				componentInChildren.subText = stringBuilder.ToString();
				componentInChildren.scrapValue = setValueTo;
				return false;
			}
			catch (Exception ex)
			{
				Plugin.Logger.LogError((object)("Error in SetScrapValue patch: " + ex.Message));
				return true;
			}
		}
	}
}