using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using HarmonyLib; using Microsoft.CodeAnalysis; 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("HowToFish.ComprehensivePerformanceOptimization")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("HowToFish.ComprehensivePerformanceOptimization")] [assembly: AssemblyTitle("HowToFish.ComprehensivePerformanceOptimization")] [assembly: AssemblyVersion("1.0.0.0")] [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; } } } namespace Optimize { [BepInPlugin("HowToFish.ComprehensivePerformanceOptimization", "HowToFish Comprehensive Performance Optimization", "1.5.4")] [BepInProcess("How to Fish.exe")] public class OptimizePlugin : BaseUnityPlugin { private ConfigEntry _logTrim; private ConfigEntry _gentleGc; private ConfigEntry _gcInterval; private ConfigEntry _jitWarmup; private ConfigEntry _earlyRange; private ConfigEntry _rangeMul; internal static float IslandRangeMultiplier = 1f; internal int BatchPerFrame = 256; internal bool WarmStarted; internal bool WarmDone; internal IEnumerator WarmCoroutine; private float _gcTimer; private static readonly BindingFlags[] Groups = new BindingFlags[4] { BindingFlags.Static | BindingFlags.Public, BindingFlags.Static | BindingFlags.NonPublic, BindingFlags.Instance | BindingFlags.Public, BindingFlags.Instance | BindingFlags.NonPublic }; private void Awake() { //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_0181: Unknown result type (might be due to invalid IL or missing references) //IL_0198: Unknown result type (might be due to invalid IL or missing references) //IL_01a5: Expected O, but got Unknown _logTrim = ((BaseUnityPlugin)this).Config.Bind("Logging", "ReduceLogSpam", true, "削减每帧日志栈生成,降低GC与CPU占用"); _gentleGc = ((BaseUnityPlugin)this).Config.Bind("GC", "IdleCollect", true, "低负载时定期清理垃圾,平滑GC停顿"); _gcInterval = ((BaseUnityPlugin)this).Config.Bind("GC", "CollectIntervalSeconds", 20f, "垃圾清理间隔(秒)"); _jitWarmup = ((BaseUnityPlugin)this).Config.Bind("Warmup", "EnableJitWarmup", true, "启动时预编译模组方法,消除首次使用卡顿"); _earlyRange = ((BaseUnityPlugin)this).Config.Bind("IslandEarlyLoad", "EnableEarlyLoadRange", true, "加大岛屿预加载触发距离,降低跨岛卡顿"); _rangeMul = ((BaseUnityPlugin)this).Config.Bind("IslandEarlyLoad", "RangeMultiplier", 2f, "触发距离放大倍数"); if (_logTrim.Value) { try { Application.SetStackTraceLogType((LogType)3, (StackTraceLogType)0); Application.SetStackTraceLogType((LogType)2, (StackTraceLogType)0); Application.SetStackTraceLogType((LogType)0, (StackTraceLogType)0); Log("log trim enabled"); } catch { } } if (_jitWarmup.Value) { WarmupHost warmupHost = new GameObject("HowToFishOptimize.Warmup").AddComponent(); Object.DontDestroyOnLoad((Object)(object)((Component)warmupHost).gameObject); warmupHost.Init(this); } if (_earlyRange.Value) { float num = (IslandRangeMultiplier = Mathf.Max(1f, _rangeMul.Value)); try { MethodInfo methodInfo = AccessTools.Method("IslandManager:get_NearPlayerRange", (Type[])null, (Type[])null); if (methodInfo == null) { LogWarn("IslandManager.get_NearPlayerRange missing"); } else { new Harmony("HowToFish.ComprehensivePerformanceOptimization.island").Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(typeof(IslandRangePatches), "NearRangePostfix", (Type[])null), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); Log("early-load range x" + num); } } catch (Exception ex) { LogWarn("island patch: " + ex.Message); } } Log("loaded"); } private void Update() { if (!_gentleGc.Value) { return; } _gcTimer += Time.unscaledDeltaTime; if (!(_gcTimer >= _gcInterval.Value)) { return; } _gcTimer = 0f; try { GC.Collect(GC.MaxGeneration, GCCollectionMode.Optimized, blocking: false); } catch { } } internal IEnumerator WarmAll() { string pluginPath = Paths.PluginPath; List targets = new List(); HashSet hashSet = new HashSet(); Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { if (!assembly.IsDynamic) { string location; try { location = assembly.Location; } catch { continue; } if (!string.IsNullOrEmpty(location) && location.StartsWith(pluginPath, StringComparison.OrdinalIgnoreCase)) { hashSet.Add(Path.GetFileName(location)); Collect(assembly, targets); } } } Log("warming " + targets.Count + " across " + hashSet.Count); int prepared = 0; int skipped = 0; int idx = 0; while (idx < targets.Count) { for (int j = 0; j < BatchPerFrame; j++) { if (idx >= targets.Count) { break; } if (TryPrepare(targets[idx++])) { prepared++; } else { skipped++; } } yield return null; } WarmDone = true; Log("warmup prepared=" + prepared + " skipped=" + skipped); } private static void Collect(Assembly asm, List outList) { Type[] array; try { array = asm.GetTypes(); } catch (ReflectionTypeLoadException ex) { IEnumerable types = ex.Types; array = (types ?? Enumerable.Empty()).Where((Type t) => t != null).ToArray(); } catch { return; } Type[] array2 = array; foreach (Type type in array2) { if (type == null) { continue; } BindingFlags[] groups = Groups; foreach (BindingFlags bindingAttr in groups) { MethodInfo[] methods; try { methods = type.GetMethods(bindingAttr); } catch { continue; } MethodInfo[] array3 = methods; foreach (MethodInfo methodInfo in array3) { if (!(methodInfo.DeclaringType != type) && !methodInfo.IsGenericMethodDefinition && !methodInfo.ContainsGenericParameters && !methodInfo.IsAbstract && methodInfo.GetMethodBody() != null && !(methodInfo.DeclaringType?.FullName == "")) { outList.Add(methodInfo); } } } } } private static bool TryPrepare(MethodBase m) { try { RuntimeHelpers.PrepareMethod(m.MethodHandle); return true; } catch { return false; } } internal void Log(string s) { ((BaseUnityPlugin)this).Logger.LogInfo((object)("Optimize: " + s)); } internal void LogWarn(string s) { ((BaseUnityPlugin)this).Logger.LogWarning((object)("Optimize: " + s)); } } internal static class IslandRangePatches { public static void NearRangePostfix(ref float __result) { float islandRangeMultiplier = OptimizePlugin.IslandRangeMultiplier; if (islandRangeMultiplier > 1f) { __result *= islandRangeMultiplier; } } } internal class WarmupHost : MonoBehaviour { private OptimizePlugin _owner; private int _frames; public void Init(OptimizePlugin owner) { _owner = owner; Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject); } private void Update() { if (_owner.WarmDone) { return; } if (!_owner.WarmStarted) { if (_frames++ >= 30) { _owner.WarmStarted = true; _owner.WarmCoroutine = _owner.WarmAll(); } } else if (_owner.WarmCoroutine != null && !_owner.WarmCoroutine.MoveNext()) { _owner.WarmDone = true; } } } }