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<bool> _logTrim;
private ConfigEntry<bool> _gentleGc;
private ConfigEntry<float> _gcInterval;
private ConfigEntry<bool> _jitWarmup;
private ConfigEntry<bool> _earlyRange;
private ConfigEntry<float> _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<bool>("Logging", "ReduceLogSpam", true, "削减每帧日志栈生成,降低GC与CPU占用");
_gentleGc = ((BaseUnityPlugin)this).Config.Bind<bool>("GC", "IdleCollect", true, "低负载时定期清理垃圾,平滑GC停顿");
_gcInterval = ((BaseUnityPlugin)this).Config.Bind<float>("GC", "CollectIntervalSeconds", 20f, "垃圾清理间隔(秒)");
_jitWarmup = ((BaseUnityPlugin)this).Config.Bind<bool>("Warmup", "EnableJitWarmup", true, "启动时预编译模组方法,消除首次使用卡顿");
_earlyRange = ((BaseUnityPlugin)this).Config.Bind<bool>("IslandEarlyLoad", "EnableEarlyLoadRange", true, "加大岛屿预加载触发距离,降低跨岛卡顿");
_rangeMul = ((BaseUnityPlugin)this).Config.Bind<float>("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<WarmupHost>();
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<MethodBase> targets = new List<MethodBase>();
HashSet<string> hashSet = new HashSet<string>();
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<MethodBase> outList)
{
Type[] array;
try
{
array = asm.GetTypes();
}
catch (ReflectionTypeLoadException ex)
{
IEnumerable<Type> types = ex.Types;
array = (types ?? Enumerable.Empty<Type>()).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 == "<Module>"))
{
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;
}
}
}
}