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 MicroOptimizer.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("MicroOptimizer")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Micro-optimizations for smoother gameplay")]
[assembly: AssemblyFileVersion("1.0.4.0")]
[assembly: AssemblyInformationalVersion("1.0.4")]
[assembly: AssemblyProduct("MicroOptimizer")]
[assembly: AssemblyTitle("MicroOptimizer")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.4.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 MicroOptimizer
{
[BepInPlugin("mrbub.microoptimizer", "MicroOptimizer", "1.0.4")]
public class Plugin : BaseUnityPlugin
{
internal static ManualLogSource Logger;
internal static ConfigFile ConfigFile;
internal static ConfigEntry<bool> OptimizeStringAllocations;
internal static ConfigEntry<bool> CacheRepeatedLookups;
internal static ConfigEntry<bool> OptimizeShipItemCollection;
internal static ConfigEntry<bool> EnableDebugLogging;
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.microoptimizer");
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");
}
if (OptimizeShipItemCollection.Value)
{
val.PatchAll(typeof(ShipItemOptimizationPatches));
Logger.LogInfo((object)"Ship item collection optimization enabled");
}
Logger.LogInfo((object)"MicroOptimizer v1.0.4 loaded successfully!");
}
private void InitializeConfig()
{
EnableDebugLogging = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "EnableDebugLogging", false, "Enable detailed logging for debugging (creates more log spam)");
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");
OptimizeShipItemCollection = ((BaseUnityPlugin)this).Config.Bind<bool>("Optimization", "OptimizeShipItemCollection", true, "Use HashSet instead of List.Contains() for O(1) scrap collection lookups");
}
}
public static class PluginInfo
{
public const string PLUGIN_GUID = "mrbub.microoptimizer";
public const string PLUGIN_NAME = "MicroOptimizer";
public const string PLUGIN_VERSION = "1.0.4";
}
}
namespace MicroOptimizer.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;
if (Plugin.EnableDebugLogging.Value)
{
Plugin.Logger.LogDebug((object)("Cached new PlayerControllerB for " + ((Object)gameObject).name));
}
}
}
else
{
if ((Object)(object)value == (Object)null)
{
playerControllerCache.Remove(gameObject);
if (Plugin.EnableDebugLogging.Value)
{
Plugin.Logger.LogDebug((object)("Removed destroyed PlayerControllerB from cache for " + ((Object)gameObject).name));
}
return null;
}
if (Plugin.EnableDebugLogging.Value)
{
Plugin.Logger.LogDebug((object)("Retrieved cached PlayerControllerB for " + ((Object)gameObject).name));
}
}
return value;
}
[HarmonyPatch(typeof(QuicksandTrigger), "OnTriggerStay")]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> QuicksandTriggerStayTranspiler(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(PlayerControllerB) });
MethodInfo methodInfo2 = AccessTools.Method(typeof(CollisionCachePatches), "GetCachedPlayerController", (Type[])null, (Type[])null);
if (methodInfo == null || methodInfo2 == null)
{
Plugin.Logger.LogWarning((object)"Could not find methods for PlayerControllerB caching in OnTriggerStay");
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<PlayerControllerB> calls in OnTriggerStay");
}
else if (Plugin.EnableDebugLogging.Value)
{
Plugin.Logger.LogWarning((object)"No GetComponent<PlayerControllerB> calls found in OnTriggerStay to replace");
}
return list;
}
[HarmonyPatch(typeof(QuicksandTrigger), "OnTriggerExit")]
[HarmonyTranspiler]
private static IEnumerable<CodeInstruction> QuicksandTriggerExitTranspiler(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(PlayerControllerB) });
MethodInfo methodInfo2 = AccessTools.Method(typeof(CollisionCachePatches), "GetCachedPlayerController", (Type[])null, (Type[])null);
if (methodInfo == null || methodInfo2 == null)
{
Plugin.Logger.LogWarning((object)"Could not find methods for PlayerControllerB caching in OnTriggerExit");
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<PlayerControllerB> calls in OnTriggerExit");
}
else if (Plugin.EnableDebugLogging.Value)
{
Plugin.Logger.LogWarning((object)"No GetComponent<PlayerControllerB> calls found in OnTriggerExit to replace");
}
return list;
}
[HarmonyPatch(typeof(RoundManager), "LoadNewLevel")]
[HarmonyPostfix]
private static void ClearCacheOnLevelChange()
{
int count = playerControllerCache.Count;
playerControllerCache.Clear();
if (Plugin.EnableDebugLogging.Value)
{
Plugin.Logger.LogDebug((object)$"Cleared {count} PlayerControllerB cache entries on level change");
}
}
[HarmonyPatch(typeof(StartOfRound), "ResetShip")]
[HarmonyPostfix]
private static void ClearCacheOnShipReset()
{
int count = playerControllerCache.Count;
playerControllerCache.Clear();
if (Plugin.EnableDebugLogging.Value)
{
Plugin.Logger.LogDebug((object)$"Cleared {count} PlayerControllerB cache entries on ship reset");
}
}
}
[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;
if (Plugin.EnableDebugLogging.Value)
{
Plugin.Logger.LogDebug((object)("Cached new NetworkObject for " + ((Object)gameObject).name));
}
}
}
else
{
if ((Object)(object)value == (Object)null)
{
networkObjectCache.Remove(gameObject);
if (Plugin.EnableDebugLogging.Value)
{
Plugin.Logger.LogDebug((object)("Removed destroyed NetworkObject from cache for " + ((Object)gameObject).name));
}
return null;
}
if (Plugin.EnableDebugLogging.Value)
{
Plugin.Logger.LogDebug((object)("Retrieved cached NetworkObject for " + ((Object)gameObject).name));
}
}
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;
if (Plugin.EnableDebugLogging.Value)
{
Plugin.Logger.LogDebug((object)("Cached new NetworkObject (from children) for " + ((Object)gameObject).name));
}
}
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");
}
else if (Plugin.EnableDebugLogging.Value)
{
Plugin.Logger.LogWarning((object)"No GetComponent<NetworkObject> calls found in SpawnScrapInLevel to replace");
}
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");
}
else if (Plugin.EnableDebugLogging.Value)
{
Plugin.Logger.LogWarning((object)"No GetComponentInChildren<NetworkObject> calls found in SpawnEnemyOnServer to replace");
}
return list;
}
[HarmonyPatch(typeof(RoundManager), "DespawnPropsAtEndOfRound")]
[HarmonyPostfix]
private static void ClearCacheOnRoundEnd()
{
int count = networkObjectCache.Count;
networkObjectCache.Clear();
if (Plugin.EnableDebugLogging.Value)
{
Plugin.Logger.LogDebug((object)$"Cleared {count} NetworkObject cache entries on round end");
}
}
[HarmonyPatch(typeof(StartOfRound), "ResetShip")]
[HarmonyPostfix]
private static void ClearCacheOnShipReset()
{
int count = networkObjectCache.Count;
networkObjectCache.Clear();
if (Plugin.EnableDebugLogging.Value)
{
Plugin.Logger.LogDebug((object)$"Cleared {count} NetworkObject cache entries on ship reset");
}
}
}
[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 (Plugin.EnableDebugLogging.Value)
{
Plugin.Logger.LogDebug((object)("CollectNewScrap called for " + (scrapObject?.itemProperties?.itemName ?? "null")));
}
if (!scrapObject.itemProperties.isScrap)
{
if (Plugin.EnableDebugLogging.Value)
{
Plugin.Logger.LogDebug((object)("Item " + scrapObject.itemProperties.itemName + " is not scrap, skipping"));
}
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 (Plugin.EnableDebugLogging.Value)
{
Plugin.Logger.LogDebug((object)$"Initialized scrap set with {scrapCollectedSet.Count} items");
}
}
if (scrapCollectedSet.Contains(scrapObject))
{
if (Plugin.EnableDebugLogging.Value)
{
Plugin.Logger.LogDebug((object)("Scrap " + scrapObject.itemProperties.itemName + " already collected, skipping"));
}
return false;
}
if (scrapObject.scrapPersistedThroughRounds)
{
if (Plugin.EnableDebugLogging.Value)
{
Plugin.Logger.LogDebug((object)("Scrap " + scrapObject.itemProperties.itemName + " persisted through rounds, skipping"));
}
return false;
}
__instance.scrapCollectedThisRound.Add(scrapObject);
scrapCollectedSet.Add(scrapObject);
HUDManager.Instance.AddNewScrapFoundToDisplay(scrapObject);
if (Plugin.EnableDebugLogging.Value)
{
Plugin.Logger.LogDebug((object)$"Added scrap {scrapObject.itemProperties.itemName} to collection (total: {scrapCollectedSet.Count})");
}
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()
{
int count = scrapCollectedSet.Count;
scrapCollectedSet.Clear();
setInitialized = false;
if (Plugin.EnableDebugLogging.Value)
{
Plugin.Logger.LogDebug((object)$"Cleared {count} scrap items from HashSet on round end");
}
}
[HarmonyPatch(typeof(StartOfRound), "ResetShip")]
[HarmonyPostfix]
private static void ClearScrapSetOnShipReset()
{
int count = scrapCollectedSet.Count;
scrapCollectedSet.Clear();
setInitialized = false;
if (Plugin.EnableDebugLogging.Value)
{
Plugin.Logger.LogDebug((object)$"Cleared {count} scrap items from HashSet on ship reset");
}
}
}
[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
{
if (Plugin.EnableDebugLogging.Value)
{
Plugin.Logger.LogDebug((object)string.Format("SetScrapValue called for {0} with value {1}", __instance?.itemProperties?.itemName ?? "null", setValueTo));
}
__instance.scrapValue = setValueTo;
ScanNodeProperties componentInChildren = ((Component)__instance).gameObject.GetComponentInChildren<ScanNodeProperties>();
if ((Object)(object)componentInChildren == (Object)null)
{
if (Plugin.EnableDebugLogging.Value)
{
Plugin.Logger.LogDebug((object)("No ScanNodeProperties found for " + __instance.itemProperties.itemName));
}
return true;
}
stringBuilder.Clear();
stringBuilder.Append("Value: $");
stringBuilder.Append(setValueTo);
componentInChildren.subText = stringBuilder.ToString();
componentInChildren.scrapValue = setValueTo;
if (Plugin.EnableDebugLogging.Value)
{
Plugin.Logger.LogDebug((object)("Set scrap value text to '" + componentInChildren.subText + "' for " + __instance.itemProperties.itemName));
}
return false;
}
catch (Exception ex)
{
Plugin.Logger.LogError((object)("Error in SetScrapValue patch: " + ex.Message));
return true;
}
}
}
}