using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Bootstrap;
using HarmonyLib;
using UnityEngine;
using UnityEngine.SceneManagement;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("TimeStepStabilizer")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("TimeStepStabilizer")]
[assembly: AssemblyCopyright("Copyright © 2025")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("bb671ece-09f3-4584-ad0c-c15b01188054")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("1.0.0.0")]
[BepInPlugin("com.yourname.TimeStepStabilizer", "TimeStep Stabilizer", "1.0.0")]
public class TimeStepStabilizer : BaseUnityPlugin
{
public GameMaster gameMaster;
public GeneralManager generalManager;
public List<float> fpsHistory = new List<float>();
public FieldInfo fpsField;
public MethodInfo setTimeScaleMethod;
public FieldInfo currentGameSpeedField;
public float baseTimeScale = 1f;
public float baseFixedDeltaTime;
private Coroutine adjustCoroutine;
public static TimeStepStabilizer Instance { get; private set; }
private void Awake()
{
((Object)Chainloader.ManagerObject).hideFlags = (HideFlags)61;
Instance = this;
Harmony.CreateAndPatchAll(typeof(TimeStepStabilizer), (string)null);
((BaseUnityPlugin)this).Logger.LogInfo((object)"AWAKE: Hooking into SceneManager.sceneLoaded");
SceneManager.sceneLoaded += OnSceneLoaded;
}
private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
{
((BaseUnityPlugin)this).Logger.LogInfo((object)("SCENE LOADED: " + ((Scene)(ref scene)).name));
fpsHistory.Clear();
if (adjustCoroutine != null)
{
((MonoBehaviour)this).StopCoroutine(adjustCoroutine);
adjustCoroutine = null;
}
((MonoBehaviour)this).StartCoroutine(DelayedSetup());
}
private IEnumerator DelayedSetup()
{
yield return (object)new WaitForSeconds(0.5f);
gameMaster = Object.FindObjectOfType<GameMaster>();
generalManager = Object.FindObjectOfType<GeneralManager>();
if ((Object)(object)gameMaster == (Object)null || (Object)(object)generalManager == (Object)null)
{
((BaseUnityPlugin)this).Logger.LogError((object)"GameMaster or GeneralManager not found in the scene.");
yield break;
}
fpsField = typeof(GeneralManager).GetField("fps", BindingFlags.Instance | BindingFlags.NonPublic);
if (fpsField == null)
{
((BaseUnityPlugin)this).Logger.LogError((object)"Failed to obtain the fps field from GeneralManager.");
}
setTimeScaleMethod = typeof(GameMaster).GetMethod("SetTimeScale", BindingFlags.Instance | BindingFlags.NonPublic);
if (setTimeScaleMethod == null)
{
((BaseUnityPlugin)this).Logger.LogError((object)"Failed to obtain the SetTimeScale method from GameMaster.");
}
currentGameSpeedField = typeof(GameMaster).GetField("currentGameSpeed", BindingFlags.Instance | BindingFlags.NonPublic);
if (currentGameSpeedField == null)
{
((BaseUnityPlugin)this).Logger.LogError((object)"Failed to obtain currentGameSpeed from GameMaster.");
}
else
{
baseFixedDeltaTime = Time.fixedDeltaTime;
baseTimeScale = (float)currentGameSpeedField.GetValue(gameMaster);
((BaseUnityPlugin)this).Logger.LogInfo((object)$"Base TimeScale captured: {baseTimeScale:F2}");
}
fpsHistory.Add(30f);
adjustCoroutine = ((MonoBehaviour)this).StartCoroutine(AdjustTimeScaleCoroutine());
}
private IEnumerator AdjustTimeScaleCoroutine()
{
while (true)
{
yield return (object)new WaitForSecondsRealtime(1f);
if ((Object)(object)generalManager == (Object)null || (Object)(object)gameMaster == (Object)null || fpsField == null || setTimeScaleMethod == null)
{
continue;
}
int currentFps = (int)fpsField.GetValue(generalManager);
fpsHistory.Add(currentFps);
if (fpsHistory.Count > 10)
{
fpsHistory.RemoveAt(0);
}
float avgFps = fpsHistory.Average();
Time.fixedDeltaTime = baseFixedDeltaTime;
if (avgFps < 29f && baseFixedDeltaTime < 1f / 51f)
{
float adjustment = 0.005f * (30f / avgFps);
float newFixedDeltaTime2 = baseFixedDeltaTime + adjustment;
if (newFixedDeltaTime2 > 0.02f)
{
newFixedDeltaTime2 = 0.02f;
}
newFixedDeltaTime2 = (float)Math.Round(newFixedDeltaTime2, 3);
((BaseUnityPlugin)this).Logger.LogInfo((object)$"Average FPS: {avgFps:F2} is below 30, setting fixedDeltaTime to: {newFixedDeltaTime2:F3} to relieve work from the CPU");
baseFixedDeltaTime = newFixedDeltaTime2;
fpsHistory.Add(50f);
fpsHistory.RemoveAt(0);
}
((BaseUnityPlugin)this).Logger.LogInfo((object)$"FixedDeltaTime is: {Time.fixedDeltaTime}");
}
}
}