using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Logging;
using HG.Reflection;
using Microsoft.CodeAnalysis;
using MonoMod.RuntimeDetour;
using RoR2;
using RoR2.Networking;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.ResourceManagement.ResourceLocations;
using UnityEngine.SceneManagement;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: OptIn]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("StageStutterFix")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+f5231a32897f5294c51f5bfe080b8b9985b4f2ca")]
[assembly: AssemblyProduct("StageStutterFix")]
[assembly: AssemblyTitle("StageStutterFix")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
[module: UnverifiableCode]
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 StageStutterFix
{
public static class BetterPreloadPatch
{
private class NewScenePreloadData
{
public string sceneName;
public bool preserveInMenuScenes;
public AsyncOperationHandle<IList<IResourceLocation>> locationsHandle;
public List<AsyncOperationHandle<Object>> assetHandles;
public Coroutine preloadAssetsCoroutine;
public void ReleaseLocations()
{
//IL_000e: Unknown result type (might be due to invalid IL or missing references)
if (locationsHandle.IsValid())
{
Addressables.Release<IList<IResourceLocation>>(locationsHandle);
}
}
public void ReleaseAssets()
{
//IL_0019: Unknown result type (might be due to invalid IL or missing references)
//IL_001e: Unknown result type (might be due to invalid IL or missing references)
//IL_0028: Unknown result type (might be due to invalid IL or missing references)
if (assetHandles == null)
{
return;
}
foreach (AsyncOperationHandle<Object> assetHandle in assetHandles)
{
if (assetHandle.IsValid())
{
Addressables.Release<Object>(assetHandle);
}
}
assetHandles = null;
}
}
[CompilerGenerated]
private sealed class <DoScenePreloadCoroutine>d__7 : IEnumerator<object>, IEnumerator, IDisposable
{
private int <>1__state;
private object <>2__current;
public NewScenePreloadData scenePreloadData;
private IList<IResourceLocation> <preloadLocations>5__2;
private Stopwatch <frameStopwatch>5__3;
private int <i>5__4;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <DoScenePreloadCoroutine>d__7(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
<preloadLocations>5__2 = null;
<frameStopwatch>5__3 = null;
<>1__state = -2;
}
private bool MoveNext()
{
//IL_009c: Unknown result type (might be due to invalid IL or missing references)
int num = <>1__state;
if (num != 0)
{
if (num != 1)
{
return false;
}
<>1__state = -1;
<frameStopwatch>5__3.Restart();
goto IL_0080;
}
<>1__state = -1;
<preloadLocations>5__2 = scenePreloadData.locationsHandle.WaitForCompletion();
<frameStopwatch>5__3 = new Stopwatch();
<frameStopwatch>5__3.Start();
<i>5__4 = 0;
goto IL_00b6;
IL_0080:
scenePreloadData.assetHandles.Add(Addressables.LoadAssetAsync<Object>(<preloadLocations>5__2[<i>5__4]));
<i>5__4++;
goto IL_00b6;
IL_00b6:
if (<i>5__4 < <preloadLocations>5__2.Count)
{
if (<frameStopwatch>5__3.ElapsedMilliseconds >= StageStutterFixPlugin.PreloadBudgetPerFrame)
{
<>2__current = null;
<>1__state = 1;
return true;
}
goto IL_0080;
}
scenePreloadData.ReleaseLocations();
return false;
}
bool IEnumerator.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
return this.MoveNext();
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
}
[CompilerGenerated]
private sealed class <WaitBeforeUnloadingScenePreloads>d__9 : IEnumerator<object>, IEnumerator, IDisposable
{
private int <>1__state;
private object <>2__current;
object IEnumerator<object>.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
object IEnumerator.Current
{
[DebuggerHidden]
get
{
return <>2__current;
}
}
[DebuggerHidden]
public <WaitBeforeUnloadingScenePreloads>d__9(int <>1__state)
{
this.<>1__state = <>1__state;
}
[DebuggerHidden]
void IDisposable.Dispose()
{
<>1__state = -2;
}
private bool MoveNext()
{
//IL_0023: Unknown result type (might be due to invalid IL or missing references)
//IL_002d: Expected O, but got Unknown
//IL_004d: Unknown result type (might be due to invalid IL or missing references)
//IL_0053: Invalid comparison between Unknown and I4
switch (<>1__state)
{
default:
return false;
case 0:
<>1__state = -1;
unloadingScenePreloads = true;
<>2__current = (object)new WaitForSeconds(NetworkPreloadManager.delayBeforeReleasingScenePreloads);
<>1__state = 1;
return true;
case 1:
{
<>1__state = -1;
SceneDef sceneDefForCurrentScene = SceneCatalog.GetSceneDefForCurrentScene();
bool flag = (Object)(object)sceneDefForCurrentScene == (Object)null || (int)sceneDefForCurrentScene.sceneType <= 0;
for (int num = allScenePreloadData.Count - 1; num >= 0; num--)
{
NewScenePreloadData newScenePreloadData = allScenePreloadData[num];
if (!flag || !newScenePreloadData.preserveInMenuScenes)
{
newScenePreloadData.ReleaseAssets();
newScenePreloadData.ReleaseLocations();
if (newScenePreloadData.preloadAssetsCoroutine != null)
{
CoroutineManager.StopCoroutine(newScenePreloadData.preloadAssetsCoroutine);
}
allScenePreloadData.RemoveAt(num);
}
}
unloadingScenePreloads = false;
return false;
}
}
}
bool IEnumerator.MoveNext()
{
//ILSpy generated this explicit interface implementation from .override directive in MoveNext
return this.MoveNext();
}
[DebuggerHidden]
void IEnumerator.Reset()
{
throw new NotSupportedException();
}
}
private static readonly List<NewScenePreloadData> allScenePreloadData = new List<NewScenePreloadData>();
private static bool unloadingScenePreloads;
private static MonoBehaviour CoroutineManager => (MonoBehaviour)(object)StageStutterFixPlugin.Instance;
public static void Init()
{
//IL_006d: Unknown result type (might be due to invalid IL or missing references)
StageStutterFixPlugin.Logger.LogMessage((object)$"Using better scene preloading with a frame budget of {StageStutterFixPlugin.PreloadBudgetPerFrame}ms");
new Hook((MethodBase)typeof(NetworkPreloadManager).GetMethod("StartNewScenePreload", new Type[2]
{
typeof(string),
typeof(bool)
}), (Delegate)new Action<string, bool>(StartBetterScenePreload));
SceneManager.activeSceneChanged += OnActiveSceneChanged;
}
private static void StartBetterScenePreload(string sceneCachedName, bool preserveSceneInMenus)
{
//IL_0046: Unknown result type (might be due to invalid IL or missing references)
//IL_004b: Unknown result type (might be due to invalid IL or missing references)
if (!allScenePreloadData.Exists((NewScenePreloadData x) => x.sceneName == sceneCachedName))
{
NewScenePreloadData newScenePreloadData = new NewScenePreloadData
{
sceneName = sceneCachedName,
preserveInMenuScenes = preserveSceneInMenus,
locationsHandle = Addressables.LoadResourceLocationsAsync((object)sceneCachedName, (Type)null),
assetHandles = new List<AsyncOperationHandle<Object>>()
};
allScenePreloadData.Add(newScenePreloadData);
newScenePreloadData.preloadAssetsCoroutine = CoroutineManager.StartCoroutine(DoScenePreloadCoroutine(newScenePreloadData));
}
}
[IteratorStateMachine(typeof(<DoScenePreloadCoroutine>d__7))]
private static IEnumerator DoScenePreloadCoroutine(NewScenePreloadData scenePreloadData)
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <DoScenePreloadCoroutine>d__7(0)
{
scenePreloadData = scenePreloadData
};
}
private static void OnActiveSceneChanged(Scene oldScene, Scene newScene)
{
if (!unloadingScenePreloads && allScenePreloadData.Count > 0)
{
CoroutineManager.StartCoroutine(WaitBeforeUnloadingScenePreloads());
}
}
[IteratorStateMachine(typeof(<WaitBeforeUnloadingScenePreloads>d__9))]
private static IEnumerator WaitBeforeUnloadingScenePreloads()
{
//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
return new <WaitBeforeUnloadingScenePreloads>d__9(0);
}
}
public static class NoPreloadPatch
{
public static void Init()
{
//IL_005e: Unknown result type (might be due to invalid IL or missing references)
StageStutterFixPlugin.Logger.LogMessage((object)"Disabling scene preloading");
new Hook((MethodBase)typeof(NetworkPreloadManager).GetMethod("StartNewScenePreload", new Type[2]
{
typeof(string),
typeof(bool)
}), (Delegate)new Action<string, bool>(DoNothing));
static void DoNothing(string sceneCachedName, bool preserveSceneInMenus)
{
}
}
}
[BepInPlugin("groovesalad.StageStutterFix", "StageStutterFix", "1.0.0")]
public class StageStutterFixPlugin : BaseUnityPlugin
{
public const string GUID = "groovesalad.StageStutterFix";
public const string NAME = "StageStutterFix";
public const string VERSION = "1.0.0";
public static StageStutterFixPlugin Instance { get; private set; }
public static ManualLogSource Logger { get; private set; }
public static long PreloadBudgetPerFrame { get; private set; }
private void Awake()
{
Instance = this;
Logger = ((BaseUnityPlugin)this).Logger;
PreloadBudgetPerFrame = ((BaseUnityPlugin)this).Config.Bind<long>("Stage Stutter Fix", "Max preload time per frame", 1L, "The ideal maximum time per frame (in milliseconds) to spend preloading the next stage. A value of zero or less will disable stage preloading").Value;
if (PreloadBudgetPerFrame > 0)
{
BetterPreloadPatch.Init();
}
else
{
NoPreloadPatch.Init();
}
}
}
}