Decompiled source of SuperQoLity v0.8.760001
BepInEx/plugins/es.damntry.SuperQoLity/Damntry.Globals.BepInEx.dll
Decompiled a week ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using Damntry.Utils.ExtensionMethods; using Damntry.Utils.Logging; using Damntry.Utils.Maths; using Damntry.Utils.Reflection; using Damntry.UtilsBepInEx.Configuration.ConfigurationManager; using Damntry.UtilsBepInEx.Configuration.ConfigurationManager.Patch; using Damntry.UtilsBepInEx.Configuration.ConfigurationManager.SettingAttributes; using Damntry.UtilsBepInEx.HarmonyPatching.Attributes; using Damntry.UtilsBepInEx.HarmonyPatching.AutoPatching.Attributes; using Damntry.UtilsBepInEx.HarmonyPatching.AutoPatching.BaseClasses; using Damntry.UtilsBepInEx.HarmonyPatching.AutoPatching.Interfaces; using Damntry.UtilsBepInEx.HarmonyPatching.Exceptions; using Damntry.UtilsBepInEx.IL; using Damntry.UtilsBepInEx.MirrorNetwork.Attributes; using Damntry.UtilsBepInEx.MirrorNetwork.Components; using Damntry.UtilsBepInEx.MirrorNetwork.Helpers; using Damntry.UtilsBepInEx.MirrorNetwork.SyncVar; using HarmonyLib; using Microsoft.CodeAnalysis; using Mirror; using Mono.Cecil; using Mono.Cecil.Cil; using Mono.Collections.Generic; using MonoMod.Utils; using Newtonsoft.Json; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("Damntry BepInEx Globals")] [assembly: AssemblyDescription("Utils usable in any type of BepInEx project")] [assembly: AssemblyCompany("Damntry")] [assembly: AssemblyProduct("Damntry BepInEx Globals")] [assembly: AssemblyCopyright("Copyright © Damntry 2025")] [assembly: AssemblyFileVersion("0.5.9.3")] [assembly: TargetFramework(".NETFramework,Version=v4.8.1", FrameworkDisplayName = ".NET Framework 4.8.1")] [assembly: AssemblyVersion("0.5.9.3")] [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 Damntry.UtilsBepInEx.ModHelpers { public abstract class ExternalModHelper { protected enum ModLoadStatus { NotLoaded, DifferentVersion, LoadedOk } protected ModLoadStatus ModStatus { get; private set; } public bool IsModLoadedAndEnabled => ModStatus != ModLoadStatus.NotLoaded; public ModInfoData ModInfo { get; private set; } private ExternalModHelper() { } public ExternalModHelper(string GUID, string modName, Version supportedVersion) { checkArgsForErrors(GUID, modName, supportedVersion); Init(GUID, modName, supportedVersion); } public ExternalModHelper(string GUID, string modName, string supportedVersion) { checkArgsForErrors(GUID, modName, supportedVersion, out var parsedSupportedVersion); Init(GUID, modName, parsedSupportedVersion); } private void checkArgsForErrors(string GUID, string modName, string supportedVersion, out Version parsedSupportedVersion) { if (supportedVersion == null) { throw new ArgumentNullException("supportedVersion"); } if (!Version.TryParse(supportedVersion, out parsedSupportedVersion)) { throw new ArgumentException("The arg supportedVersion doesnt have a valid Version format."); } checkArgsForErrors(GUID, modName, parsedSupportedVersion); } private void checkArgsForErrors(string GUID, string modName, Version supportedVersion) { if (GUID == null) { throw new ArgumentNullException("GUID"); } if (modName == null) { throw new ArgumentNullException("modName"); } if (supportedVersion == null) { throw new ArgumentNullException("supportedVersion"); } } private void Init(string GUID, string modName, Version supportedVersion) { ModInfo = new ModInfoData(GUID, modName, supportedVersion); ModStatus = IsModLoaded(); } protected virtual ModLoadStatus IsModLoaded() { if (Chainloader.PluginInfos.TryGetValue(ModInfo.GUID, out var value)) { ModInfo.LoadedVersion = value.Metadata.Version; if (ModInfo.LoadedVersion != ModInfo.SupportedVersion) { return ModLoadStatus.DifferentVersion; } return ModLoadStatus.LoadedOk; } return ModLoadStatus.NotLoaded; } } public class ModInfoData { public string GUID { get; internal set; } public string Name { get; internal set; } public Version SupportedVersion { get; internal set; } public Version LoadedVersion { get; internal set; } internal ModInfoData(string GUID, string modName, Version SupportedVersion) { this.GUID = GUID; Name = modName; this.SupportedVersion = SupportedVersion; } public ModInfoData(ModInfoData modInfo) { GUID = modInfo.GUID; Name = modInfo.Name; SupportedVersion = modInfo.SupportedVersion; LoadedVersion = modInfo.LoadedVersion; } } } namespace Damntry.UtilsBepInEx.MirrorNetwork.SyncVar { public interface ISyncVar { void SetToDefaultValue(); bool Writable(); void InitializeSyncObject(NetworkBehaviour netBehaviour); } [Serializable] public class SyncVar<T> : SyncObject, ISyncVar, IEquatable<T> { [SerializeField] protected T _Value; private NetworkBehaviour networkBehaviourContainer; public Action<T, T> OnValueChangedCallback; private bool hookGuard; public virtual T Value { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return _Value; } set { SetValue(value, checkWritable: true); } } public T DefaultValue { get; private set; } protected void SetValue(T value, bool checkWritable) { if ((!checkWritable || Writable()) && !Equals(value)) { T value2 = _Value; _Value = value; if (base.OnDirty != null) { base.OnDirty(); } if (!hookGuard && (Object)(object)networkBehaviourContainer != (Object)null && !networkBehaviourContainer.authority) { hookGuard = true; InvokeCallback(value2, value); hookGuard = false; } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] protected virtual void InvokeCallback(T oldValue, T newValue) { OnValueChangedCallback?.Invoke(oldValue, newValue); } public override void ClearChanges() { } public override void Reset() { } public virtual void SetToDefaultValue() { NetworkSpawnManager.DebugLog(() => $"Setting SyncVar from {_Value} to its DefaultValue {DefaultValue}"); _Value = DefaultValue; } public SyncVar(T defaultValue) { InitValues(defaultValue, defaultValue, null); } public SyncVar(T value, T defaultValue) { InitValues(value, defaultValue, null); } public SyncVar(T value, T defaultValue, Action<T, T> onValueChangedCallback) { OnValueChangedCallback = (Action<T, T>)Delegate.Combine(OnValueChangedCallback, onValueChangedCallback); InitValues(value, defaultValue, null); } public SyncVar(NetworkBehaviour netBehaviour, T value, T defaultValue) { if ((Object)(object)netBehaviour == (Object)null) { throw new ArgumentNullException("netBehaviour"); } InitValues(value, defaultValue, netBehaviour); } public SyncVar(NetworkBehaviour netBehaviour, T value, T defaultValue, Action<T, T> onValueChangedCallback) { if ((Object)(object)netBehaviour == (Object)null) { throw new ArgumentNullException("netBehaviour"); } OnValueChangedCallback = (Action<T, T>)Delegate.Combine(OnValueChangedCallback, onValueChangedCallback); InitValues(value, defaultValue, netBehaviour); } private void InitValues(T value, T defaultValue, NetworkBehaviour netBehaviour) { _Value = value; DefaultValue = defaultValue; if (typeof(T).IsEnum) { RegisterCustomEnum(value); } if ((Object)(object)netBehaviour != (Object)null) { InitSyncObjectReflection(netBehaviour); } } private void RegisterCustomEnum<TEnum>(TEnum value) { if (Writer<TEnum>.write == null) { Writer<TEnum>.write = delegate(NetworkWriter writer, TEnum value) { NetworkWriterExtensions.WriteLong(writer, EnumExtension.EnumToLong<TEnum>(value)); }; } if (Reader<TEnum>.read == null) { Reader<TEnum>.read = (NetworkReader reader) => EnumExtension.LongToEnumUnconstrained<TEnum>(NetworkReaderExtensions.ReadLong(reader)); } } public bool Writable() { //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Invalid comparison between Unknown and I4 //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Invalid comparison between Unknown and I4 if ((Object)(object)networkBehaviourContainer == (Object)null) { bool isNetworkOffline = !NetworkSpawnManager.IsNetworkOnline(); NetworkSpawnManager.DebugLog(() => $"Writable returned {isNetworkOffline} for a non networked SyncVar!"); return isNetworkOffline; } if (NetworkServer.active && NetworkClient.active) { if ((int)networkBehaviourContainer.syncDirection != 0) { return networkBehaviourContainer.netIdentity.isOwned; } return true; } if (NetworkServer.active) { return (int)networkBehaviourContainer.syncDirection == 0; } if (NetworkClient.active) { if (networkBehaviourContainer.netIdentity.netId != 0) { if ((int)networkBehaviourContainer.syncDirection == 1) { return networkBehaviourContainer.netIdentity.isOwned; } return false; } return true; } return true; } public virtual void InitializeSyncObject(NetworkBehaviour netBehaviour) { InitSyncObjectReflection(netBehaviour); } protected void InitSyncObjectReflection(NetworkBehaviour netBehaviour) { networkBehaviourContainer = netBehaviour; NetworkSpawnManager.DebugLog(() => "Initializing SyncVar of Value type " + typeof(T).Name + " in " + ((Object)netBehaviour).name); ReflectionHelper.CallMethod((object)netBehaviour, "InitSyncObject", new object[1] { this }); NetworkSpawnManager.DebugLog(() => "Finished initializing SyncVar of Value type " + typeof(T).Name + " in " + ((Object)netBehaviour).name); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator T(SyncVar<T> field) { return field.Value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator SyncVar<T>(T value) { return new SyncVar<T>(value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override void OnSerializeAll(NetworkWriter writer) { writer.Write<T>(Value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override void OnSerializeDelta(NetworkWriter writer) { writer.Write<T>(Value); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override void OnDeserializeAll(NetworkReader reader) { SetValue(reader.Read<T>(), checkWritable: false); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public override void OnDeserializeDelta(NetworkReader reader) { SetValue(reader.Read<T>(), checkWritable: false); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(T other) { return EqualityComparer<T>.Default.Equals(Value, other); } public override string ToString() { return Value.ToString(); } } public class SyncVarSetting<T> : SyncVar<T>, ISyncVar { public ConfigEntry<T> ConfigEntry { get; private set; } public SyncVarSetting(T defaultValue, ConfigEntry<T> configEntry) : base(defaultValue, defaultValue) { Init(configEntry); } public SyncVarSetting(T defaultValue, Action<T, T> onValueChangedCallback, ConfigEntry<T> configEntry) : base(defaultValue, defaultValue, onValueChangedCallback) { Init(configEntry); } private void Init(ConfigEntry<T> configEntry) { if (configEntry == null) { throw new ArgumentNullException("configEntry", "SyncVarSetting ConfigEntry cannot be null. Make sure that Bepinex config binding has been completed successfully before this call. If not using a ConfigEntry is intended, use SyncVar instead."); } NetworkSpawnManager.DebugLog(() => "SyncVarSetting constructor. " + $"Setting SyncVar to its configEntry value ({configEntry.Value})."); ConfigEntry = configEntry; _Value = configEntry.Value; RegisterSyncvarSetting(); } public void RegisterSyncvarSetting() { UnregisterSyncvarSetting(); ConfigEntry.SettingChanged += SetValueFromConfig; } public void UnregisterSyncvarSetting() { ConfigEntry.SettingChanged -= SetValueFromConfig; } public override void InitializeSyncObject(NetworkBehaviour netBehaviour) { InitSyncObjectReflection(netBehaviour); NetworkSpawnManager.DebugLog(() => "SyncVarSetting fully initialized. " + $"Setting SyncVar from {_Value} to its configEntry value ({ConfigEntry.Value})."); SetValueFromConfig(); } private void SetValueFromConfig(object sender, EventArgs e) { SetValueFromConfig(); } public void SetValueFromConfig() { Value = ConfigEntry.Value; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static implicit operator T(SyncVarSetting<T> field) { return field.Value; } } } namespace Damntry.UtilsBepInEx.MirrorNetwork.Helpers { public static class NetworkPrefabHelper { private static readonly string NetworkRootObjName = "NetworkedObjects"; public static GameObject GetNetworkReadyPrefab<T>(string prefabName, out NetworkBehaviour networkBehaviour) where T : NetworkBehaviour { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Expected O, but got Unknown GameObject val = new GameObject(prefabName); val.SetActive(false); val.transform.parent = GetRootNetworkTransform(); val.AddComponent<NetworkIdentity>(); networkBehaviour = (NetworkBehaviour)(object)val.AddComponent<T>(); return val; } private static Transform GetRootNetworkTransform() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Expected O, but got Unknown GameObject val = GameObject.Find(NetworkRootObjName); if (val == null) { val = new GameObject(NetworkRootObjName); } return val.transform; } public static bool AssetIdExists(uint assetId) { return NetworkClient.prefabs.ContainsKey(assetId); } public static bool IsPrefabGameObjectRegistered(GameObject prefabObj, uint expectedAssetId) { if (NetworkClient.prefabs != null && NetworkClient.prefabs.TryGetValue(expectedAssetId, out var value)) { return prefabObj == value; } return false; } public static bool IsPrefabHandlerRegistered(SpawnHandlerDelegate spawnHandlerDelegate, uint expectedAssetId) { if (FindSpawnhandlerByAssetId(expectedAssetId, out var IsFieldNotFound, out var spawnDelegate)) { return spawnHandlerDelegate == spawnDelegate; } return IsFieldNotFound; } public static bool FindSpawnhandlerByAssetId(uint assetId, out bool IsFieldNotFound, out SpawnHandlerDelegate spawnDelegate) { spawnDelegate = null; IsFieldNotFound = false; try { string text = "spawnHandlers"; FieldInfo field = typeof(NetworkClient).GetField("spawnHandlers", AccessTools.all); if (field != null) { return ((Dictionary<uint, SpawnHandlerDelegate>)field.GetValue(null)).TryGetValue(assetId, out spawnDelegate); } TimeLogger.Logger.LogTimeError("The field " + text + " could not be found in type " + typeof(NetworkClient).Name + ".", (LogCategories)512); } catch (Exception ex) { TimeLogger.Logger.LogTimeException(ex, (LogCategories)512); } IsFieldNotFound = true; return false; } } public interface INetworkPrefabSpawner { Type NetworkBehaviourType { get; } uint DefinedAssetId { get; } void AddToPrefabs(); void Spawn(); } public class NetworkPrefabSpawner<T> : INetworkPrefabSpawner where T : NetworkBehaviour { [Serializable] [CompilerGenerated] private sealed class <>c { public static readonly <>c <>9 = new <>c(); public static UnSpawnDelegate <>9__15_1; internal void <AddToPrefabs>b__15_1(GameObject spawned) { } } private GameObject prefabObj; private NetworkBehaviour networkBehaviourInstance; public uint DefinedAssetId { get; init; } public bool PrefabRegisterOk { get; private set; } public Type NetworkBehaviourType { get; private set; } public NetworkPrefabSpawner(uint assetId) { NetworkBehaviourType = typeof(T); DefinedAssetId = assetId; } public void AddToPrefabs() { //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Expected O, but got Unknown //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Expected O, but got Unknown string typeName = NetworkBehaviourType.Name; prefabObj = NetworkPrefabHelper.GetNetworkReadyPrefab<T>(typeName, out networkBehaviourInstance); if ((Object)(object)prefabObj == (Object)null) { TimeLogger.Logger.LogTimeError("A prefab object couldnt be created from type " + typeName, (LogCategories)512); return; } uint definedAssetId = DefinedAssetId; if (NetworkPrefabHelper.AssetIdExists(definedAssetId)) { TimeLogger.Logger.LogTimeError($"The specified assetId \"{definedAssetId}\" for the NetworkBehaviour " + "class " + typeName + " was already found in the NetworkClient.", (LogCategories)512); return; } SpawnHandlerDelegate val = (SpawnHandlerDelegate)((SpawnMessage msg) => prefabObj); object obj = <>c.<>9__15_1; if (obj == null) { UnSpawnDelegate val2 = delegate { }; <>c.<>9__15_1 = val2; obj = (object)val2; } UnSpawnDelegate val3 = (UnSpawnDelegate)obj; NetworkClient.RegisterPrefab(prefabObj, DefinedAssetId, val, val3); PrefabRegisterOk = NetworkPrefabHelper.IsPrefabHandlerRegistered(val, DefinedAssetId); if (!PrefabRegisterOk) { NetworkSpawnManager.DebugLog(() => "Prefab NOT registered for NetworkBehaviour " + typeName + "."); return; } NetworkSpawnManager.DebugLog(() => "Prefab was registered correctly for NetworkBehaviour " + typeName + "."); TriggerStartNetworkSession(NetworkSpawnManager.GetCurrentNetworkMode()); } private void TriggerStartNetworkSession(NetworkMode networkMode) { if (typeof(ISyncVarBehaviour).IsAssignableFrom(typeof(T))) { ((ISyncVarBehaviour)networkBehaviourInstance).StartNetworkSession(networkMode); } } public void Spawn() { if (PrefabRegisterOk) { if ((Object)(object)prefabObj == (Object)null) { TimeLogger.Logger.LogTimeFatal("The prefab object to spawn for type " + NetworkBehaviourType.Name + ". is null. Make sure to call AddToPrefabs() before spawning happens.", (LogCategories)512); } else { NetworkServer.Spawn(prefabObj, (NetworkConnection)null); } } } } public enum NetworkMode { NotInitialized, Offline, ServerOnly, ClientOnly, Host } [HarmonyPatch] public class NetworkSpawnManager { private static readonly Harmony harmony; private static readonly Dictionary<Type, INetworkPrefabSpawner> networkBehaviourRegistry; private static uint _assetIdSignature; public static bool NetworkDebugLog; static NetworkSpawnManager() { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown NetworkDebugLog = true; harmony = new Harmony(typeof(NetworkSpawnManager).FullName); networkBehaviourRegistry = new Dictionary<Type, INetworkPrefabSpawner>(); } public static bool IsNetworkActive() { if (!NetworkServer.active) { return NetworkClient.active; } return true; } public static bool IsNetworkOnline() { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Invalid comparison between Unknown and I4 if ((Object)(object)NetworkManager.singleton != (Object)null) { return (int)NetworkManager.singleton.mode > 0; } return false; } public static bool IsNetworkOnlineHosting() { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Invalid comparison between Unknown and I4 //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Invalid comparison between Unknown and I4 if (!((Object)(object)NetworkManager.singleton != (Object)null) || (int)NetworkManager.singleton.mode != 3) { return (int)NetworkManager.singleton.mode == 1; } return true; } public static bool IsNetworkClientOnly() { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Invalid comparison between Unknown and I4 if ((Object)(object)NetworkManager.singleton != (Object)null) { return (int)NetworkManager.singleton.mode == 2; } return false; } public static NetworkMode GetCurrentNetworkMode() { //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Expected I4, but got Unknown if ((Object)(object)NetworkManager.singleton == (Object)null) { return NetworkMode.NotInitialized; } NetworkManagerMode mode = NetworkManager.singleton.mode; return (int)mode switch { 0 => NetworkMode.Offline, 1 => NetworkMode.ServerOnly, 2 => NetworkMode.ClientOnly, 3 => NetworkMode.Host, _ => throw new NotImplementedException(), }; } public static void Initialize(int assetIdSignature) { if (assetIdSignature < 1 || assetIdSignature > 4293) { TimeLogger.Logger.LogTimeFatal($"The value of assetIdSignature is {assetIdSignature} " + "but it must be between 1 and 4293, both inclusive. All related network functionality will not work.", (LogCategories)512); return; } _assetIdSignature = (uint)assetIdSignature; harmony.PatchAll(typeof(NetworkSpawnManager)); if (!harmony.GetPatchedMethods().Any()) { TimeLogger.Logger.LogTimeFatal("NetworkSpawnManager patch failed. Modded network features wont work.", (LogCategories)512); } } [HarmonyPatch(typeof(NetworkServer), "SpawnObjects")] [HarmonyPatch(typeof(NetworkClient), "PrepareToSpawnSceneObjects")] [HarmonyPostfix] public static void OnStartNetworkSessionPrefix(MethodBase __originalMethod) { DebugLog(() => "OnStartNetworkSessionPrefix - Coming from method " + $"{__originalMethod.Name} - Network mode: {NetworkManager.singleton.mode}"); if (IsNetworkActive()) { DebugLog("Mirror network system is active. Adding network spawns"); AddNetworkSpawns(); } else { DebugLog("Mirror network system is NOT active. Skipping this session initialization"); } } public static void RegisterNetwork<T>(uint assetId) where T : NetworkBehaviour { if (assetId == 0) { throw new ArgumentException("The assetId parameter cant be 0."); } if (MathMethods.CountDigits(assetId) > 6) { throw new ArgumentException("The assetId parameter must have less than 7 digits [1 to 999999]."); } Type typeFromHandle = typeof(T); if (networkBehaviourRegistry.ContainsKey(typeFromHandle)) { throw new InvalidOperationException("The NetworkBehaviour of type " + typeFromHandle.Name + " was already registered."); } NetworkPrefabSpawner<T> value = new NetworkPrefabSpawner<T>(GetAssetIdFromSignature(assetId)); networkBehaviourRegistry.Add(typeFromHandle, value); } private static void AddNetworkSpawns() { foreach (KeyValuePair<Type, INetworkPrefabSpawner> item in networkBehaviourRegistry) { item.Value.AddToPrefabs(); } if (NetworkServer.active) { SpawnHost(); } } private static void SpawnHost() { DebugLog(() => $"Spawning objects - NetworkServer active? {NetworkServer.active}"); if (!NetworkServer.active) { return; } foreach (KeyValuePair<Type, INetworkPrefabSpawner> item in networkBehaviourRegistry) { item.Value.Spawn(); } } private static uint GetAssetIdFromSignature(uint assetId) { return _assetIdSignature * 1000000 + assetId; } public static void DebugLog(Func<string> textLambda) { } public static void DebugLog(string text) { } } } namespace Damntry.UtilsBepInEx.MirrorNetwork.Components { internal interface ISyncVarBehaviour { void StartNetworkSession(NetworkMode networkMode); } public abstract class SyncVarNetworkBehaviour<T> : NetworkBehaviour, ISyncVarBehaviour where T : SyncVarNetworkBehaviour<T> { private static readonly BindingFlags SearchFlags = BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; private readonly Type derivedType = typeof(T); private readonly List<ISyncVar> validSyncVarList = new List<ISyncVar>(); private static readonly Dictionary<MethodBase, MethodInfo> methodRedirects = new Dictionary<MethodBase, MethodInfo>(); private readonly Harmony harmony = new Harmony(typeof(SyncVarNetworkBehaviour<T>).FullName); private const bool IS_DEBUG_NETWORK_TESTS__ENABLED = false; protected virtual void OnStartNetworkSession(NetworkMode networkMode) { } protected virtual void OnSyncVarValuesDefaulted() { } protected virtual void OnSyncVarsNetworkReady() { } void ISyncVarBehaviour.StartNetworkSession(NetworkMode networkMode) { NetworkSpawnManager.DebugLog(() => "OnStartNetworkSession call begins for type " + derivedType.Name + "."); OnStartNetworkSession(networkMode); validSyncVarList.Clear(); foreach (MemberInfoHelper item in GetDerivedSyncVarsOfType(typeof(SyncVar<>))) { if (CheckSyncVarValidity(item, out var syncVarInstance)) { validSyncVarList.Add(syncVarInstance); syncVarInstance.SetToDefaultValue(); } } OnSyncVarValuesDefaulted(); } private void Awake() { foreach (ISyncVar validSyncVar in validSyncVarList) { InitializeSyncVar(validSyncVar, (T)this); } validSyncVarList.Clear(); NetworkSpawnManager.DebugLog(() => "OnSyncVarsNetworkReady call begins for type " + derivedType.Name + "."); OnSyncVarsNetworkReady(); } private List<MemberInfoHelper> GetDerivedSyncVarsOfType(Type searchType) { IEnumerable<MemberInfo> enumerable = derivedType.GetFields(SearchFlags).Cast<MemberInfo>().Concat(derivedType.GetProperties(SearchFlags)) .Cast<MemberInfo>(); if (enumerable == null || enumerable.Count() == 0) { return new List<MemberInfoHelper>(); } return (from mif in enumerable.Where((MemberInfo mi) => ReflectionExtension.HasCustomAttribute<SyncVarNetworkAttribute>(mi)).Select((Func<MemberInfo, MemberInfoHelper>)((MemberInfo mi) => new MemberInfoHelper(mi))) where ReflectionExtension.IsSubclassOfRawGeneric(mif.MemberInfoType, searchType) select mif).ToList(); } private bool CheckSyncVarValidity(MemberInfoHelper syncVarInfoHelper, out ISyncVar syncVarInstance) { syncVarInstance = null; Type memberInfoType = syncVarInfoHelper.MemberInfoType; if (!memberInfoType.IsSubclassOf(typeof(SyncObject))) { TimeLogger.Logger.LogTimeWarning("The var " + derivedType.Name + "." + ((MemberInfo)(object)syncVarInfoHelper).Name + " does not inherit from SyncObject and will be skipped. Make sure that the SyncVarNetworkAttribute annotation was intended.", (LogCategories)512); return false; } if (!typeof(ISyncVar).IsAssignableFrom(memberInfoType)) { TimeLogger.Logger.LogTimeWarning("The var " + derivedType.Name + "." + ((MemberInfo)(object)syncVarInfoHelper).Name + " does not inherit from ISyncVar and will be skipped. Make sure that the SyncVarNetworkAttribute annotation was intended.", (LogCategories)512); return false; } if (memberInfoType.GetGenericArguments() == null || memberInfoType.GetGenericArguments().Length == 0) { TimeLogger.Logger.LogTimeWarning("The var " + derivedType.Name + "." + ((MemberInfo)(object)syncVarInfoHelper).Name + " does not declare any generic type parameters and will be skipped. Make sure that the type derives from SyncVar.", (LogCategories)512); return false; } syncVarInstance = (ISyncVar)syncVarInfoHelper.GetValueStaticAgnostic((object)(T)this); if (syncVarInstance == null) { TimeLogger.Logger.LogTimeWarning("The var " + derivedType.Name + "." + ((MemberInfo)(object)syncVarInfoHelper).Name + " has not been instantiated and will be skipped. Make sure to call its constructor.", (LogCategories)512); return false; } return true; } private void InitializeSyncVar(ISyncVar syncVarInstance, T netBehaviourInstance) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown if (!IsSyncVarNetworkInitialized((SyncObject)syncVarInstance)) { syncVarInstance.InitializeSyncObject((NetworkBehaviour)(object)netBehaviourInstance); } } private bool IsSyncVarNetworkInitialized(SyncObject syncVarInstance) { return base.syncObjects.Contains(syncVarInstance); } private Delegate GetOnChangeDelegate(string onChangeMethodName, T netBehaviourInstance, Type syncVarType) { Type type = syncVarType.GetGenericArguments()[0]; MethodInfo method = derivedType.GetMethod(onChangeMethodName, SearchFlags, null, new Type[2] { type, type }, null); if (method == null) { TimeLogger.Logger.LogTimeWarning("Method \"" + onChangeMethodName + "\" could not be found in type " + derivedType.Name + ", or does not have the required signature: " + onChangeMethodName + "(" + syncVarType.Name + " oldValue, " + syncVarType.Name + " newValue).", (LogCategories)512); return null; } Delegate @delegate = Delegate.CreateDelegate(typeof(Action<, >).MakeGenericType(type, type), netBehaviourInstance, method); if ((object)@delegate == null) { TimeLogger.Logger.LogTimeWarning("A delegate for method \"" + derivedType.Name + "." + onChangeMethodName + "\" could not be created.", (LogCategories)512); return null; } return @delegate; } private void InitializeRedirectsRPC_CMD() { //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Expected O, but got Unknown List<(MethodInfo, MethodInfo)> rPC_MethodTargets = GetRPC_MethodTargets(); LOG.TEMPWARNING($"{rPC_MethodTargets.Count} RPC methods", true); foreach (var item in rPC_MethodTargets) { if (!methodRedirects.ContainsKey(item.Item1)) { methodRedirects.Add(item.Item1, item.Item2); MethodInfo method = new Func<IEnumerable<CodeInstruction>, ILGenerator, MethodBase, IEnumerable>(TranspileRPC_Call).Method; harmony.Patch((MethodBase)item.Item1, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(method), (HarmonyMethod)null, (HarmonyMethod)null); } } LOG.TEMPWARNING("Method patching finished.", true); } public override void OnStopClient() { harmony.UnpatchSelf(); methodRedirects.Clear(); } private List<(MethodInfo origMethod, MethodInfo targetMethod)> GetRPC_MethodTargets() { return (from mi in derivedType.GetMethods(SearchFlags) where ReflectionExtension.HasCustomAttribute<RPC_CallOnClientAttribute>((MemberInfo)mi) select (mi, GetMethodInfoFromRPCAttribute(mi)) into tup where tup.Item2 != null select tup).ToList(); } private MethodInfo GetMethodInfoFromRPCAttribute(MethodInfo methodInfo) { RPC_CallOnClientAttribute customAttribute = methodInfo.GetCustomAttribute<RPC_CallOnClientAttribute>(); if (Type.GetType(customAttribute.declaringType.AssemblyQualifiedName) == null) { TimeLogger.Logger.LogTimeError("The type FullName could not be found.", (LogCategories)512); } MethodInfo methodInfo2 = AccessTools.Method(customAttribute.declaringType, customAttribute.targetMethodName, customAttribute.parameters, customAttribute.generics); if (methodInfo2 != null && !CompareMethodSignatures(methodInfo, methodInfo2)) { methodInfo2 = null; } return methodInfo2; } public static bool CompareMethodSignatures(MethodInfo mi1, MethodInfo mi2, bool compareDeclaringType = false) { List<string> list = new List<string>(); if (mi1.IsGenericMethod || mi2.IsGenericMethod) { list.Add("generic methods not supported"); } if (mi1.ReturnType != mi2.ReturnType) { list.Add("return type"); } if (mi1.GetParameters().Length != mi2.GetParameters().Length) { list.Add("number of parameters"); } else { for (int i = 0; i < mi1.GetParameters().Length; i++) { if (mi1.GetParameters()[i].ParameterType != mi2.GetParameters()[i].ParameterType) { list.Add($"param {i + 1} type"); } } } if (mi1.IsStatic != mi2.IsStatic) { list.Add("static modifier"); } if (list.Count > 0) { TimeLogger.Logger.LogTimeError("The methods " + mi1.DeclaringType.Name + "." + mi1.Name + " and " + mi2.DeclaringType.Name + "." + mi2.Name + " need to have the same method signature. Fix the following differences: " + string.Join(", ", list), (LogCategories)32); } return list.Count == 0; } [HarmonyDebug] private static IEnumerable TranspileRPC_Call(IEnumerable<CodeInstruction> instructions, ILGenerator generator, MethodBase originalMethod) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Expected O, but got Unknown methodRedirects.TryGetValue(originalMethod, out var value); CodeMatcher val = new CodeMatcher(instructions, (ILGenerator)null); LOG.TEMPWARNING("Before emitting delegate " + val.InstructionEnumeration().GetFormattedIL(), true); if (NetworkServer.active) { LOG.TEMPWARNING("Host. RPC network send logic.", true); val.Advance(1); InsertRPC_GenerationCall(val, generator, value.GetParameters(), value.IsStatic); } else if (NetworkClient.active) { LOG.TEMPWARNING("Client. Executing next.", true); EmitCallTargetMethod(val, value); } else { TimeLogger.Logger.LogTimeError("RPC method " + originalMethod.Name + " was called when no Mirror network component was active.", (LogCategories)512); } LOG.TEMPWARNING("After emitting delegate " + val.InstructionEnumeration().GetFormattedIL(), true); return val.InstructionEnumeration(); } private static void InsertRPC_GenerationCall(CodeMatcher codeMatcher, ILGenerator generator, ParameterInfo[] parameters, bool isStatic) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Expected O, but got Unknown //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Expected O, but got Unknown //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Expected O, but got Unknown //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_008c: Expected O, but got Unknown //IL_00e4: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Expected O, but got Unknown //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00cf: Expected O, but got Unknown int num = parameters.Length; if (num > 0) { codeMatcher.InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1] { new CodeInstruction(OpCodes.Ldc_I4, (object)num) }).InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1] { new CodeInstruction(OpCodes.Newarr, (object)typeof(object)) }); int num2 = ((!isStatic) ? 1 : 0); for (int i = 0; i < num; i++) { codeMatcher.InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1] { new CodeInstruction(OpCodes.Dup, (object)null) }).InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1] { new CodeInstruction(OpCodes.Ldc_I4, (object)i) }).InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1] { CodeInstructionNew.LoadArgument(i + num2) }); Type parameterType = parameters[i].ParameterType; if (parameterType.IsValueType) { codeMatcher.InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1] { new CodeInstruction(OpCodes.Box, (object)parameterType) }); } codeMatcher.InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1] { new CodeInstruction(OpCodes.Stelem_Ref, (object)null) }); } } codeMatcher.Insert((CodeInstruction[])(object)new CodeInstruction[1] { Transpilers.EmitDelegate<Action<object[]>>((Action<object[]>)MakeRPC_Call) }); } private static void MakeRPC_Call(object[] args) { LOG.TEMPWARNING(string.Format("MakeRPC_Call - {0} ({1}) params: {2}", args.Length, args[0], string.Join(", ", args)), true); NetworkWriterPooled val = NetworkWriterPool.Get(); for (int i = 0; i < args.Length; i++) { _ = args[i]; } NetworkWriterPool.Return(val); LOG.TEMPWARNING("MakeRPC_Call finished", true); } private static void EmitCallTargetMethod(CodeMatcher codeMatcher, MethodInfo targetMethod) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Expected O, but got Unknown codeMatcher.End().Advance(-1); LoadMethodArgIntoStack(codeMatcher, targetMethod); codeMatcher.Insert((CodeInstruction[])(object)new CodeInstruction[1] { new CodeInstruction(OpCodes.Call, (object)targetMethod) }); } private static void LoadMethodArgIntoStack(CodeMatcher codeMatcher, MethodInfo targetMethod) { int num = targetMethod.GetParameters().Count() + ((!targetMethod.IsStatic) ? 1 : 0); for (int i = 0; i < num; i++) { codeMatcher.InsertAndAdvance((CodeInstruction[])(object)new CodeInstruction[1] { CodeInstructionNew.LoadArgument(i) }); } } [RPC_CallOnClient(typeof(SyncVarNetworkAttribute), "UpdateSupermarketNameOnClient", null)] private void UpdateSupermarketName(string marketName) { } private static void UpdateSupermarketNameOnClient() { } } } namespace Damntry.UtilsBepInEx.MirrorNetwork.Attributes { [AttributeUsage(AttributeTargets.Method)] public class RPC_CallOnClientAttribute : Attribute { public Type declaringType; public string targetMethodName; public Type[] parameters; public Type[] generics; public RPC_CallOnClientAttribute(Type declaringType, string targetMethodName, Type[] parameters = null) { SetTargetMethod(declaringType, targetMethodName, parameters); } public RPC_CallOnClientAttribute(Type declaringType, string targetMethodName, Type[] parameters, Type[] generics) { SetTargetMethod(declaringType, targetMethodName, parameters, generics); } private void SetTargetMethod(Type declaringType, string targetMethodName, Type[] parameters = null, Type[] generics = null) { if (declaringType == null) { TimeLogger.Logger.LogTimeError("Parameter declaringType is null.", (LogCategories)512); return; } if (string.IsNullOrEmpty(targetMethodName)) { TimeLogger.Logger.LogTimeError("Parameter targetMethodName is null or empty.", (LogCategories)512); return; } this.declaringType = declaringType; this.targetMethodName = targetMethodName; this.parameters = parameters; this.generics = generics; } } [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)] public class SyncVarNetworkAttribute : Attribute { } } namespace Damntry.UtilsBepInEx.Logging { public sealed class BepInExTimeLogger : TimeLogger { private static ManualLogSource bepinexLogger; protected override void InitializeLogger(params object[] args) { if (args == null) { throw new ArgumentNullException("args"); } if (args.Length == 0 || !(args[0] is string)) { throw new ArgumentException("The argument is empty or its first index is not a string"); } bepinexLogger = Logger.CreateLogSource((string)args[0]); } protected override void LogMessage(string message, LogTier logLevel) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) bepinexLogger.Log(convertLogLevel(logLevel), (object)message); } private LogLevel convertLogLevel(LogTier logLevel) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Invalid comparison between Unknown and I4 //IL_0007: Unknown result type (might be due to invalid IL or missing references) if ((int)logLevel == -1) { return (LogLevel)63; } return (LogLevel)logLevel; } } } namespace Damntry.UtilsBepInEx.IL { public static class ILExtensionMethods { public static int LocalIndex(this CodeInstruction code) { if (code.opcode == OpCodes.Ldloc_0 || code.opcode == OpCodes.Stloc_0) { return 0; } if (code.opcode == OpCodes.Ldloc_1 || code.opcode == OpCodes.Stloc_1) { return 1; } if (code.opcode == OpCodes.Ldloc_2 || code.opcode == OpCodes.Stloc_2) { return 2; } if (code.opcode == OpCodes.Ldloc_3 || code.opcode == OpCodes.Stloc_3) { return 3; } if (code.opcode == OpCodes.Ldloc_S || code.opcode == OpCodes.Ldloc) { return GetLocalOperandIndex(code.operand); } if (code.opcode == OpCodes.Stloc_S || code.opcode == OpCodes.Stloc) { return GetLocalOperandIndex(code.operand); } if (code.opcode == OpCodes.Ldloca_S || code.opcode == OpCodes.Ldloca) { return GetLocalOperandIndex(code.operand); } throw new ArgumentException("Instruction is not a load or store", "code"); } private static int GetLocalOperandIndex(object operand) { if (operand.GetType() == typeof(LocalBuilder)) { return ((LocalBuilder)operand).LocalIndex; } return Convert.ToInt32(operand); } public static int ArgumentIndex(this CodeInstruction code) { if (code.opcode == OpCodes.Ldarg_0) { return 0; } if (code.opcode == OpCodes.Ldarg_1) { return 1; } if (code.opcode == OpCodes.Ldarg_2) { return 2; } if (code.opcode == OpCodes.Ldarg_3) { return 3; } if (code.opcode == OpCodes.Ldarg_S || code.opcode == OpCodes.Ldarg) { return Convert.ToInt32(code.operand); } if (code.opcode == OpCodes.Starg_S || code.opcode == OpCodes.Starg) { return Convert.ToInt32(code.operand); } if (code.opcode == OpCodes.Ldarga_S || code.opcode == OpCodes.Ldarga) { return Convert.ToInt32(code.operand); } throw new ArgumentException("Instruction is not a load or store", "code"); } public static string GetFormattedIL(this IEnumerable<CodeInstruction> instrList) { return instrList.Aggregate("", (string combinedText, CodeInstruction instr) => combinedText + "\n" + GetFormattedILSingleLine(instr)); } private static string GetFormattedILSingleLine(CodeInstruction instruction) { string text = FormatArgument(instruction.operand); string text2 = ((text.Length > 0) ? " " : ""); string text3 = instruction.opcode.ToString(); if (instruction.opcode.FlowControl == FlowControl.Branch || instruction.opcode.FlowControl == FlowControl.Cond_Branch) { text3 += " =>"; } text3 = text3.PadRight(10); return $"{CodePos(instruction)}{text3}{text2}{text}"; } private static string CodePos(CodeInstruction instruction) { return ""; } private static string FormatArgument(object argument, string extra = null) { if (argument == null) { return ""; } Type type = argument.GetType(); if (argument is MethodBase methodBase) { return GeneralExtensions.FullDescription(methodBase) + ((extra != null) ? (" " + extra) : ""); } if (argument is FieldInfo fieldInfo) { return GeneralExtensions.FullDescription(fieldInfo.FieldType) + " " + GeneralExtensions.FullDescription(fieldInfo.DeclaringType) + "::" + fieldInfo.Name; } if (type == typeof(Label)) { return $"Label{((Label)argument).GetHashCode()}"; } if (type == typeof(Label[])) { return "Labels" + string.Join(",", ((Label[])argument).Select((Label l) => l.GetHashCode().ToString()).ToArray()); } if (type == typeof(LocalBuilder)) { return $"{((LocalBuilder)argument).LocalIndex} ({((LocalBuilder)argument).LocalType})"; } if (type == typeof(string)) { return GeneralExtensions.ToLiteral(argument.ToString(), "\""); } return argument.ToString().Trim(); } } public static class CodeInstructionNew { public static CodeInstruction LoadLocal(int index, bool useAddress = false) { //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Expected O, but got Unknown //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Expected O, but got Unknown //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected O, but got Unknown //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Expected O, but got Unknown //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Expected O, but got Unknown //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Expected O, but got Unknown //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Expected O, but got Unknown //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Expected O, but got Unknown if (useAddress) { if (index >= 256) { return new CodeInstruction(OpCodes.Ldloca, (object)index); } return new CodeInstruction(OpCodes.Ldloca_S, (object)Convert.ToByte(index)); } if (index != 0) { if (index != 1) { if (index != 2) { if (index != 3) { if (index >= 256) { return new CodeInstruction(OpCodes.Ldloc, (object)index); } return new CodeInstruction(OpCodes.Ldloc_S, (object)Convert.ToByte(index)); } return new CodeInstruction(OpCodes.Ldloc_3, (object)null); } return new CodeInstruction(OpCodes.Ldloc_2, (object)null); } return new CodeInstruction(OpCodes.Ldloc_1, (object)null); } return new CodeInstruction(OpCodes.Ldloc_0, (object)null); } public static CodeInstruction StoreLocal(int index) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Expected O, but got Unknown //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Expected O, but got Unknown //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Expected O, but got Unknown //IL_0068: Unknown result type (might be due to invalid IL or missing references) //IL_006e: Expected O, but got Unknown //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Expected O, but got Unknown if (index != 0) { if (index != 1) { if (index != 2) { if (index != 3) { if (index >= 256) { return new CodeInstruction(OpCodes.Stloc, (object)index); } return new CodeInstruction(OpCodes.Stloc_S, (object)Convert.ToByte(index)); } return new CodeInstruction(OpCodes.Stloc_3, (object)null); } return new CodeInstruction(OpCodes.Stloc_2, (object)null); } return new CodeInstruction(OpCodes.Stloc_1, (object)null); } return new CodeInstruction(OpCodes.Stloc_0, (object)null); } public static CodeInstruction LoadArgument(int index, bool useAddress = false) { //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Expected O, but got Unknown //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Expected O, but got Unknown //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected O, but got Unknown //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Expected O, but got Unknown //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Expected O, but got Unknown //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Expected O, but got Unknown //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Expected O, but got Unknown //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Expected O, but got Unknown if (useAddress) { if (index >= 256) { return new CodeInstruction(OpCodes.Ldarga, (object)index); } return new CodeInstruction(OpCodes.Ldarga_S, (object)Convert.ToByte(index)); } if (index != 0) { if (index != 1) { if (index != 2) { if (index != 3) { if (index >= 256) { return new CodeInstruction(OpCodes.Ldarg, (object)index); } return new CodeInstruction(OpCodes.Ldarg_S, (object)Convert.ToByte(index)); } return new CodeInstruction(OpCodes.Ldarg_3, (object)null); } return new CodeInstruction(OpCodes.Ldarg_2, (object)null); } return new CodeInstruction(OpCodes.Ldarg_1, (object)null); } return new CodeInstruction(OpCodes.Ldarg_0, (object)null); } public static CodeInstruction StoreArgument(int index) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Expected O, but got Unknown //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Expected O, but got Unknown if (index >= 256) { return new CodeInstruction(OpCodes.Starg, (object)index); } return new CodeInstruction(OpCodes.Starg_S, (object)Convert.ToByte(index)); } } } namespace Damntry.UtilsBepInEx.HarmonyPatching { public class HarmonyInstancePatcher { private enum PatchRecursiveAction { StopAll, SkipAndContinueNested, PatchAndContinueNested } private readonly Type harmonyInstanceType; private readonly Lazy<Harmony> harmonyPatch; public HarmonyInstancePatcher(Type harmonyInstanceType) { this.harmonyInstanceType = harmonyInstanceType; harmonyPatch = new Lazy<Harmony>((Func<Harmony>)(() => new Harmony(GetHarmonyInstanceId()))); } internal string GetHarmonyInstanceId() { return harmonyInstanceType.FullName; } public List<MethodInfo> PatchInstance() { return StartRecursivePatching(harmonyPatch.Value); } public void UnpatchInstance() { harmonyPatch.Value.UnpatchSelf(); } public int GetPatchedCount() { return harmonyPatch.Value.GetPatchedMethods().Count(); } private List<MethodInfo> StartRecursivePatching(Harmony harmonyPatch) { List<MethodInfo> list = new List<MethodInfo>(); if (PatchClass(harmonyInstanceType, harmonyPatch, list)) { PatchNestedClassesRecursive(new List<Type>(), harmonyInstanceType, harmonyPatch, list); } return list; } private void PatchNestedClassesRecursive(List<Type> classList, Type classType, Harmony harmony, List<MethodInfo> listPatchedMethods) { Type[] nestedTypes = classType.GetNestedTypes(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); foreach (Type type in nestedTypes) { if (type.IsClass && PatchClass(type, harmony, listPatchedMethods)) { PatchNestedClassesRecursive(classList, type, harmony, listPatchedMethods); } } } private bool PatchClass(Type classType, Harmony harmonyPatch, List<MethodInfo> listPatchedMethods) { switch (CheckAttributesForAllowedAction(classType)) { case PatchRecursiveAction.StopAll: return false; case PatchRecursiveAction.PatchAndContinueNested: { PatchClassProcessor val = harmonyPatch.CreateClassProcessor(classType, true); listPatchedMethods.AddRange(val.Patch()); break; } } return true; } private PatchRecursiveAction CheckAttributesForAllowedAction(Type classType) { if (ReflectionExtension.HasCustomAttribute<AutoPatchIgnoreClassAndNested>((MemberInfo)classType)) { return PatchRecursiveAction.StopAll; } if (ReflectionExtension.HasCustomAttribute<AutoPatchIgnoreClass>((MemberInfo)classType)) { return PatchRecursiveAction.SkipAndContinueNested; } return PatchRecursiveAction.PatchAndContinueNested; } public List<MethodInfo> PatchClassByType(Type classType) { ThrowIfNotOwnInstanceNestedClass(classType); return harmonyPatch.Value.CreateClassProcessor(classType).Patch(); } public void UnpatchMethod(Type originalClassType, string originalMethodName) { if (originalMethodName == null) { throw new ArgumentNullException("originalMethodName"); } MethodInfo methodInfo = AccessTools.Method(originalClassType, originalMethodName, (Type[])null, (Type[])null); if (methodInfo == null) { throw new InvalidOperationException("The method \"" + originalMethodName + "\" couldnt be found in the type " + originalClassType.FullName + "."); } harmonyPatch.Value.Unpatch((MethodBase)methodInfo, (HarmonyPatchType)0, harmonyPatch.Value.Id); } private void ThrowIfNotOwnInstanceNestedClass(Type classType) { if (classType == null) { throw new ArgumentNullException("classType"); } if (classType == harmonyInstanceType) { throw new InvalidOperationException("Use the method PatchInstance/UnpatchInstance() instead."); } if (GetTopClassOfNested(classType) != harmonyInstanceType) { throw new InvalidOperationException("Class must be a nested class of " + harmonyInstanceType.FullName + ". If you want to do this action on another Instance, use that instance methods instead."); } } private Type GetTopClassOfNested(Type classType) { while (classType.IsNested) { classType = classType.DeclaringType; } return classType; } } public class HarmonyMonoMethods { public static MethodDefinition GetMethodDefinition(MethodInfo methodInfo) { MethodDefinition val = MethodBaseToMethodDefinition(methodInfo); if (val == null) { AssemblyDefinition val2 = AssemblyDefinition.ReadAssembly(AssemblyUtils.GetAssemblyDllFilePath(methodInfo.DeclaringType)); try { val = Extensions.FindMethod(val2.MainModule.GetType(methodInfo.DeclaringType.FullName), Extensions.GetID((MethodBase)methodInfo, (string)null, (string)null, true, false, false), false); } catch (Exception ex) { TimeLogger.Logger.LogTimeDebug(TimeLogger.FormatException(ex, "Error while trying to convert MethodInfo to MethodDefinition. You can safely ignore this error if you are not the dev."), (LogCategories)4194304); } } return val; } public static MethodDefinition MethodBaseToMethodDefinition(MethodBase method) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Expected O, but got Unknown return (MethodDefinition)((MemberReference)(TypeDefinition)ModuleDefinition.ReadModule((Stream)new MemoryStream(File.ReadAllBytes(method.DeclaringType.Module.FullyQualifiedName))).LookupToken(method.DeclaringType.MetadataToken)).Module.LookupToken(method.MetadataToken); } public static IEnumerable<(MethodInfo methodInfo, HarmonyMethod harmonyMethod)> GetAllPatchMethodTargets(Type assemblyType, bool skipNonExecutablePatches) { HarmonyMethod harmonyMethod; return (from type in Assembly.GetAssembly(assemblyType).GetTypes() where !skipNonExecutablePatches || IsPatchExecutable(type) select type).SelectMany((Type type) => from mInfo in type.GetMethods(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic) where IsHarmonyAttribute(mInfo) select (GetTargetMethodFromHarmonyPatchMethod(type, mInfo, out harmonyMethod), harmonyMethod)); } private static bool IsPatchExecutable(Type methodClassType) { MethodInfo methodInfo = (from m in methodClassType.GetMethods(AccessTools.all) where m.ReturnType == typeof(bool) select m).FirstOrDefault((MethodInfo m) => m.Name == "Prepare" || m.GetCustomAttributes(inherit: true).Any((object attr) => attr.GetType().FullName == typeof(HarmonyPrepare).FullName)); if (methodInfo != null) { object[] array = AccessTools.ActualParameters((MethodBase)methodInfo, Array.Empty<object>()); return ReflectionHelper.CallMethod<bool>((object)null, methodInfo, array); } return true; } public static bool IsHarmonyAttribute(MethodInfo methodInfo) { IEnumerable<Attribute> enumerable = null; try { enumerable = methodInfo.GetCustomAttributes(); } catch (TypeNotFoundInAssemblyException) { return false; } return enumerable.Any((Attribute attr) => attr is HarmonyAttribute); } public static MethodInfo GetTargetMethodFromHarmonyPatchMethod(Type methodClassType, MethodInfo patchMethodInfo, out HarmonyMethod harmonyMethod) { //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) IEnumerable<HarmonyMethod> source = from attr in patchMethodInfo.GetCustomAttributes(inherit: true) where attr is HarmonyAttribute select ((HarmonyAttribute)attr).info; harmonyMethod = HarmonyMethod.Merge(source.ToList()); if (harmonyMethod.method == null && harmonyMethod.declaringType == null) { HarmonyMethod val = HarmonyMethod.Merge(HarmonyMethodExtensions.GetFromType(methodClassType)); harmonyMethod = HarmonyMethodExtensions.Merge(val, harmonyMethod); } HarmonyMethod val2 = harmonyMethod; MethodType valueOrDefault = val2.methodType.GetValueOrDefault(); if (!val2.methodType.HasValue) { valueOrDefault = (MethodType)0; val2.methodType = valueOrDefault; } MethodInfo methodInfo = AccessTools.Method("HarmonyLib.PatchTools:GetOriginalMethod", new Type[1] { typeof(HarmonyMethod) }, (Type[])null); if (methodInfo != null) { return (MethodInfo)methodInfo.Invoke(null, new object[1] { harmonyMethod }); } TimeLogger.Logger.LogTimeWarning("Reflection access to \"HarmonyLib.PatchTools:GetOriginalMethod\" returned null. Using backup method.", (LogCategories)4194304); return AccessTools.Method(harmonyMethod.declaringType, harmonyMethod.methodName, harmonyMethod.argumentTypes, (Type[])null); } } public class MethodSignatureChecker { internal class MethodSignature { public string Arguments { get; set; } public string ReturnType { get; set; } public int IL_BodyHashcode { get; set; } public void Set_IL_BodyHashcode(Collection<Instruction> instructions) { //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) if (instructions == null) { throw new ArgumentNullException("instructions"); } int num = 17; Enumerator<Instruction> enumerator = instructions.GetEnumerator(); try { while (enumerator.MoveNext()) { Instruction current = enumerator.Current; num = num * 71 + ((object)current).ToString().GetHashCode(); } } finally { ((IDisposable)enumerator).Dispose(); } IL_BodyHashcode = num; } internal bool Equals(MethodSignature other, out string errorDetail) { errorDetail = null; if (other == null) { errorDetail = "\tWrong method call or no longer exists."; } else if (Arguments != other.Arguments) { errorDetail = "\tArguments are different"; } else if (ReturnType != other.ReturnType) { errorDetail = "\tReturn types are different"; } else if (IL_BodyHashcode != other.IL_BodyHashcode) { errorDetail = "\tIL method body hashcode is different"; } if (errorDetail != null) { errorDetail = errorDetail.PadRight(38); } return errorDetail == null; } } private readonly struct MethodInfoData { private readonly struct FullMethodName { private readonly string method; private readonly string declaringType; private readonly string[] parameters; public FullMethodName(string declaredTypeName, string methodName, string[] typeParameterNames = null) { method = methodName; declaringType = ReflectionHelper.ConvertFullTypeToNormal(declaredTypeName); parameters = ReflectionHelper.ConvertFullTypesToNormal(typeParameterNames); } public FullMethodName(string declaredTypeName, string methodName, Type[] typeParameter = null) { method = methodName; declaringType = ReflectionHelper.ConvertFullTypeToNormal(declaredTypeName); string[] array = null; if (typeParameter != null && typeParameter.Length != 0) { array = (from t in typeParameter where t != null select t.Name).ToArray(); } parameters = array; } public string GetText() { string text = ""; if (parameters != null && parameters.Length != 0) { text = string.Join(", ", parameters); } return declaringType + "." + method + "(" + text + ")"; } } private readonly bool isMethodInfo; private readonly MethodInfo methodInfo; private readonly Type declaringType; private readonly string methodName; private readonly Type[] parameters; private readonly Type[] generics; private readonly string fullTypeName; private readonly string[] fullTypeParameters; private readonly FullMethodName fullMethodName; public MethodInfoData(MethodInfo methodInfo, string declaredTypeName, string methodName, string[] typeParameterNames = null) { declaringType = null; this.methodName = null; parameters = null; generics = null; fullTypeName = null; fullTypeParameters = null; if (string.IsNullOrEmpty(declaredTypeName)) { throw new ArgumentNullException("declaredTypeName cannot be empty."); } if (string.IsNullOrEmpty(methodName)) { throw new ArgumentNullException("methodName cannot be empty."); } this.methodInfo = methodInfo; fullMethodName = new FullMethodName(declaredTypeName, methodName, typeParameterNames); isMethodInfo = true; } public MethodInfoData(MethodInfo methodInfo, Type declaringType, string methodName, Type[] parameters = null, Type[] generics = null) { this.declaringType = null; this.methodName = null; this.parameters = null; this.generics = null; fullTypeName = null; fullTypeParameters = null; if (declaringType == null) { throw new ArgumentNullException("declaringType cannot be null."); } if (string.IsNullOrEmpty(methodName)) { throw new ArgumentException("methodName cannot be null or empty."); } this.methodInfo = methodInfo; fullMethodName = new FullMethodName(declaringType.Name, methodName, parameters); isMethodInfo = true; } public MethodInfoData(Type declaringType, string methodName, Type[] parameters = null, Type[] generics = null) { isMethodInfo = false; methodInfo = null; fullTypeName = null; fullTypeParameters = null; if (declaringType == null) { throw new ArgumentNullException("declaringType cannot be null."); } if (string.IsNullOrEmpty(methodName)) { throw new ArgumentException("methodName cannot be null or empty."); } this.declaringType = declaringType; this.methodName = methodName; this.parameters = parameters; this.generics = generics; fullMethodName = new FullMethodName(declaringType.Name, methodName, parameters); } public MethodInfoData(Type declaringType, string methodName, string[] fullTypeParameters = null, Type[] generics = null) { isMethodInfo = false; methodInfo = null; parameters = null; fullTypeName = null; if (declaringType == null) { throw new ArgumentNullException("declaringType cannot be null."); } if (string.IsNullOrEmpty(methodName)) { throw new ArgumentException("methodName cannot be null or empty."); } this.declaringType = declaringType; this.methodName = methodName; this.fullTypeParameters = fullTypeParameters; this.generics = generics; fullMethodName = new FullMethodName(declaringType.Name, methodName, fullTypeParameters); } public MethodInfoData(string fullTypeName, string methodName, string[] fullTypeParameters = null, Type[] generics = null) { isMethodInfo = false; methodInfo = null; declaringType = null; parameters = null; if (string.IsNullOrEmpty(fullTypeName)) { throw new ArgumentNullException("fullTypeName cannot be null or empty."); } if (string.IsNullOrEmpty(methodName)) { throw new ArgumentException("methodName cannot be null or empty."); } this.fullTypeName = fullTypeName; this.methodName = methodName; this.fullTypeParameters = fullTypeParameters; this.generics = generics; fullMethodName = new FullMethodName(fullTypeName, methodName, fullTypeParameters); } public MethodInfo GetMethodInfo() { if (isMethodInfo) { return methodInfo; } Type obj = ((declaringType != null) ? declaringType : AssemblyUtils.GetTypeFromLoadedAssemblies(fullTypeName, true)); Type[] array = ((parameters != null) ? parameters : AssemblyUtils.GetTypesFromLoadedAssemblies(true, fullTypeParameters)); return AccessTools.Method(obj, methodName, array, generics); } public string GetFullMethodName() { return fullMethodName.GetText(); } } private List<MethodInfoData> listMethodInfoData; private Dictionary<string, MethodSignature> previousMethodSignatures; private readonly string pathFileMethodSignatures; private readonly Type pluginType; public CheckResult LastCheckResult { get; private set; } public MethodSignatureChecker(Type pluginType) { this.pluginType = pluginType; LastCheckResult = new CheckResult(); string combinedPathFromAssemblyFolder = AssemblyUtils.GetCombinedPathFromAssemblyFolder(pluginType, "MethodSignatureChecker"); if (!Directory.Exists(combinedPathFromAssemblyFolder)) { Directory.CreateDirectory(combinedPathFromAssemblyFolder); } char directorySeparatorChar = Path.DirectorySeparatorChar; pathFileMethodSignatures = combinedPathFromAssemblyFolder + directorySeparatorChar + "methodsignatures.json"; listMethodInfoData = new List<MethodInfoData>(); } public static MethodSignatureChecker StartSignatureCheck(Type pluginType) { MethodSignatureChecker methodSignatureChecker = new MethodSignatureChecker(pluginType); methodSignatureChecker.PopulateMethodSignaturesFromHarmonyPatches(); methodSignatureChecker.StartSignatureCheck(); return methodSignatureChecker; } public void PopulateMethodSignaturesFromHarmonyPatches() { foreach (var (methodInfo, val) in HarmonyMonoMethods.GetAllPatchMethodTargets(pluginType, skipNonExecutablePatches: true)) { AddMethod(methodInfo, val.declaringType, val.methodName, val.argumentTypes); } } public void AddMethod(MethodInfo methodInfo, string declaredTypeName, string methodName, string[] typeParameterNames = null) { listMethodInfoData.Add(new MethodInfoData(methodInfo, declaredTypeName, methodName, typeParameterNames)); } public void AddMethod(MethodInfo methodInfo, Type declaringType, string methodName, Type[] parameters = null) { listMethodInfoData.Add(new MethodInfoData(methodInfo, declaringType, methodName, parameters)); } public void AddMethod(string fullTypeName, string methodName, string[] fullTypeParameters = null, Type[] generics = null) { listMethodInfoData.Add(new MethodInfoData(fullTypeName, methodName, fullTypeParameters, generics)); } public void AddMethod(Type declaringType, string methodName, string[] fullTypeParameters = null, Type[] generics = null) { listMethodInfoData.Add(new MethodInfoData(declaringType, methodName, fullTypeParameters, generics)); } public void AddMethod(Type declaringType, string methodName) { listMethodInfoData.Add(new MethodInfoData(declaringType, methodName, (Type[])null, (Type[])null)); } public void AddMethod(Type declaringType, string methodName, Type[] parameters = null, Type[] generics = null) { listMethodInfoData.Add(new MethodInfoData(declaringType, methodName, parameters, generics)); } private MethodSignature CreateMethodSignature(MethodInfo methodInfo, MethodDefinition methodDef) { MethodSignature methodSignature = new MethodSignature(); object[] parameters = methodInfo.GetParameters(); methodSignature.Arguments = string.Join("", parameters); methodSignature.ReturnType = methodInfo.ReturnType.FullName; methodSignature.Set_IL_BodyHashcode(methodDef.Body.Instructions); return methodSignature; } public CheckResult StartSignatureCheck() { CheckResult checkResult = new CheckResult(CheckResult.SignatureCheckResult.Started, ""); Dictionary<string, MethodSignature> dictionary = new Dictionary<string, MethodSignature>(); foreach (MethodInfoData listMethodInfoDatum in listMethodInfoData) { if (GetMethodSignature(listMethodInfoDatum, out var methodSigEntry) && !dictionary.ContainsKey(methodSigEntry.Key)) { dictionary.Add(methodSigEntry.Key, methodSigEntry.Value); } } if (dictionary == null || !dictionary.Any()) { return checkResult.SetValues(CheckResult.SignatureCheckResult.NoSignaturesAdded, "No method signature has been added. Signature comparison will be skipped."); } if (TryLoadSignaturesFromFile(checkResult)) { CompareOldSignaturesAgainstNew(checkResult, dictionary); } if (checkResult.Result != CheckResult.SignatureCheckResult.SignaturesDifferent) { SaveSignaturesToFile(dictionary); } if (checkResult.Result == CheckResult.SignatureCheckResult.Started) { throw new InvalidOperationException("Something went wrong. Method check finished with result \"Started\"."); } return LastCheckResult = checkResult; } private bool GetMethodSignature(MethodInfoData methodInfoData, out KeyValuePair<string, MethodSignature> methodSigEntry) { string text = ""; MethodSignature value = null; MethodInfo methodInfo = methodInfoData.GetMethodInfo(); if (methodInfo != null) { MethodDefinition methodDefinition = HarmonyMonoMethods.GetMethodDefinition(methodInfo); if (methodDefinition == null) { methodSigEntry = default(KeyValuePair<string, MethodSignature>); return false; } value = CreateMethodSignature(methodInfo, methodDefinition); } text = methodInfoData.GetFullMethodName(); methodSigEntry = new KeyValuePair<string, MethodSignature>(text, value); return true; } private bool TryLoadSignaturesFromFile(CheckResult checkResult) { if (!File.Exists(pathFileMethodSignatures)) { checkResult.SetValues(CheckResult.SignatureCheckResult.NoPreviousSignatures, "No method signature file. Skipped check."); return false; } try { string text = File.ReadAllText(pathFileMethodSignatures, Encoding.Unicode); previousMethodSignatures = JsonConvert.DeserializeObject<Dictionary<string, MethodSignature>>(text); } catch (Exception ex) { checkResult.SetValues(CheckResult.SignatureCheckResult.FileError, TimeLogger.FormatException(ex, "Error while reading and deserializing from file \"" + pathFileMethodSignatures + "\". It might be corrupted. Trying to delete file and skipping loading signatures.")); try { File.Delete(pathFileMethodSignatures); } catch { } return false; } return true; } private void SaveSignaturesToFile(Dictionary<string, MethodSignature> currentMethodSignatures) { string contents = JsonConvert.SerializeObject((object)currentMethodSignatures, (Formatting)1); File.WriteAllText(pathFileMethodSignatures, contents, Encoding.Unicode); } private void CompareOldSignaturesAgainstNew(CheckResult checkResult, Dictionary<string, MethodSignature> currentMethodSignatures) { Dictionary<string, MethodSignature> dictionary = previousMethodSignatures; if (dictionary == null || !dictionary.Any() || currentMethodSignatures == null || !currentMethodSignatures.Any()) { return; } foreach (KeyValuePair<string, MethodSignature> currentMethodSignature in currentMethodSignatures) { if (previousMethodSignatures.TryGetValue(currentMethodSignature.Key, out var value)) { MethodSignature value2 = currentMethodSignature.Value; if (value != null && !value.Equals(value2, out var errorDetail)) { checkResult.Result = CheckResult.SignatureCheckResult.SignaturesDifferent; checkResult.AddErrorMessage(errorDetail + " | " + currentMethodSignature.Key); } } } if (checkResult.Result != CheckResult.SignatureCheckResult.SignaturesDifferent) { checkResult.SetValues(CheckResult.SignatureCheckResult.SignaturesOk, "Method signature check ok."); } } } public class CheckResult { public enum SignatureCheckResult { Unchecked, Started, FileError, NoSignaturesAdded, NoPreviousSignatures, SignaturesDifferent, SignaturesOk } public static CheckResult UnknownError = new CheckResult(SignatureCheckResult.Unchecked, "Unknown Error due to exception"); private string _result; public SignatureCheckResult Result { get; internal set; } public string ResultMessage { get { return _result; } private set { if (value == null) { _result = ""; } _result = value; } } internal CheckResult() { ResultMessage = ""; Result = SignatureCheckResult.Unchecked; } internal CheckResult(SignatureCheckResult result, string resultMessage) { ResultMessage = resultMessage; Result = result; } internal CheckResult SetValues(SignatureCheckResult result, string resultMessage) { ResultMessage = resultMessage; Result = result; return this; } internal void AddErrorMessage(string resultMessage) { if (string.IsNullOrEmpty(ResultMessage)) { ResultMessage = "Method signatures have changed since last successful check:"; } ResultMessage = ResultMessage + "\n" + resultMessage; } public void LogResultMessage(LogTier logLevel, bool onlyWhenNotOk, bool showInGame) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) if (ShouldLogMessage(onlyWhenNotOk)) { TimeLogger.Logger.LogTime(logLevel, ResultMessage, (LogCategories)4194304, showInGame); } } private bool ShouldLogMessage(bool onlyWhenNotOk) { if (string.IsNullOrEmpty(ResultMessage)) { if (Result == SignatureCheckResult.Unchecked) { return false; } ResultMessage = "Result message was empty for a result in which this is not allowed."; return true; } if (onlyWhenNotOk && (Result == SignatureCheckResult.SignaturesOk || Result == SignatureCheckResult.NoPreviousSignatures)) { return false; } return true; } } } namespace Damntry.UtilsBepInEx.HarmonyPatching.Helpers { public class ArgumentHelper<T> { public T Value { get; set; } public CodeInstruction LoadFieldArgHelper_IL { get; private set; } public CodeInstruction GetterValue_IL { get; private set; } public CodeInstruction SetterValue_IL { get; private set; } public ArgumentHelper(Type declaringClassType, string instanceName, T argumentValue) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Expected O, but got Unknown //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Expected O, but got Unknown //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Expected O, but got Unknown Value = argumentValue; FieldInfo fieldInfo = AccessTools.Field(declaringClassType, instanceName); if (fieldInfo == null) { throw new InvalidOperationException($"No field was found in the type {declaringClassType} with name {instanceName}."); } LoadFieldArgHelper_IL = new CodeInstruction(OpCodes.Ldsfld, (object)fieldInfo); if (!fieldInfo.IsStatic) { LoadFieldArgHelper_IL.opcode = OpCodes.Ldfld; } PropertyInfo propertyInfo = AccessTools.Property(typeof(ArgumentHelper<T>), "Value"); GetterValue_IL = new CodeInstruction(OpCodes.Callvirt, (object)propertyInfo.GetGetMethod()); SetterValue_IL = new CodeInstruction(OpCodes.Callvirt, (object)propertyInfo.GetSetMethod()); } } } namespace Damntry.UtilsBepInEx.HarmonyPatching.Exceptions { public class TranspilerDefaultMsgException : Exception { private const string defaultErrorText = "Transpiler couldnt perform its changes due to unexpected source code changes."; public TranspilerDefaultMsgException() : base(composeErrorText()) { } public TranspilerDefaultMsgException(string errorDetail) : base(composeErrorText(errorDetail)) { } public TranspilerDefaultMsgException(string errorDetail, Exception inner) : base(composeErrorText(errorDetail), inner) { } private static string composeErrorText(string errorDetail = "") { return "Transpiler couldnt perform its changes due to unexpected source code changes.\n" + errorDetail; } } public class TypeNotFoundInAssemblyException : Exception { public TypeNotFoundInAssemblyException() { } public TypeNotFoundInAssemblyException(string errorDetail) : base(errorDetail) { } public TypeNotFoundInAssemblyException(string errorDetail, Exception inner) : base(errorDetail, inner) { } } } namespace Damntry.UtilsBepInEx.HarmonyPatching.AutoPatching { public static class Container<T> where T : AutoPatchedInstanceBase { public static T Instance => AutoPatchContainer.GetInstance<T>(); } internal static class AutoPatchContainer { private static Dictionary<Type, AutoPatchedInstanceBase> registeredInstances = new Dictionary<Type, AutoPatchedInstanceBase>(); internal static T GetInstance<T>() where T : AutoPatchedInstanceBase { ThrowIfTypeInvalidOrNotRegistered<T>(); return (T)registeredInstances[typeof(T)]; } internal static AutoPatchedInstanceBase GetAbstractInstance(Type patchType) { ThrowIfTypeInvalidOrNotRegistered(patchType); return registeredInstances[patchType]; } internal static IReadOnlyDictionary<Type, AutoPatchedInstanceBase> GetRegisteredAutoPatches() { return registeredInstances; } internal static void RegisterPatchClass(Type autoPatchType) { ThrowIfTypeInvalidOrAlreadyRegistered(autoPatchType); object obj = Activator.CreateInstance(autoPatchType); registeredInstances.Add(autoPatchType, (AutoPatchedInstanceBase)obj); } internal static void UnregisterPatchClass(Type autoPatchClass) { ThrowIfTypeInvalidOrNotRegistered(autoPatchClass); registeredInstances.Remove(autoPatchClass); } private static void ThrowIfTypeInvalidOrAlreadyRegistered<T>() { ThrowIfTypeInvalidOrAlreadyRegistered(typeof(T)); } private static void ThrowIfTypeInvalidOrAlreadyRegistered(Type patchClassType) { if (patchClassType.IsAbstract || !patchClassType.IsSubclassOf(typeof(AutoPatchedInstanceBase))) { throw new InvalidOperationException("The type " + patchClassType.FullName + " must be a non abstract subclass of AutoPatchedInstanceBase."); } if (registeredInstances.ContainsKey(patchClassType)) { throw new InvalidOperationException("The type " + patchClassType.FullName + " has already been registered."); } } private static void ThrowIfTypeInvalidOrNotRegistered<T>() { ThrowIfTypeInvalidOrNotRegistered(typeof(T)); } private static void ThrowIfTypeInvalidOrNotRegistered(Type patchClassType) { if (patchClassType.IsAbstract || !patchClassType.IsSubclassOf(typeof(AutoPatchedInstanceBase))) { throw new InvalidOperationException("The type " + patchClassType.FullName + " must be a non abstract subclass of AutoPatchedInstanceBase."); } if (!registeredInstances.ContainsKey(patchClassType)) { throw new InvalidOperationException("The type " + patchClassType.FullName + " is not registered."); } } } public static class AutoPatcher { public enum AutoPatchResult { none, error, disabled, success } public static bool RegisterAllAutoPatchContainers() { List<Type> list = GetAutoPatchClasses(Assembly.GetCallingAssembly()).ToList(); if (list.Count == 0) { return false; } foreach (Type item in list) { AutoPatchContainer.RegisterPatchClass(item); } return true; } public static bool StartAutoPatcher() { int num = 0; int num2 = 0; int num3 = 0; foreach (KeyValuePair<Type, AutoPatchedInstanceBase> registeredAutoPatch in AutoPatchContainer.GetRegisteredAutoPatches()) { AutoPatchResult autoPatchResult = Patch(registeredAutoPatch.Key, registeredAutoPatch.Value); if (autoPatchResult == AutoPatchResult.disabled) { num2++; continue; } num3++; if (autoPatchResult == AutoPatchResult.error) { num++; } } if (num == 0) { TimeLogger.Logger.LogTimeInfo($"All {num3} auto-patches applied successfully.", (LogCategories)8); return true; } TimeLogger.Logger.LogTimeFatal($"Oh oh, {num} out of {num3} auto-patches failed. " + "Check above for errors.", (LogCategories)8); return false; } private static AutoPatchResult Patch(Type autoPatchType, AutoPatchedInstanceBase autoPatchInstance) { AutoPatchResult autoPatchResult = AutoPatchResult.none; try { if (autoPatchInstance == null) { throw new InvalidOperationException("Auto patch received a null instance from the registered type " + autoPatchType.FullName + "."); } if (autoPatchInstance.IsAutoPatchEnabled) { autoPatchInstance.PatchInstance(); autoPatchResult = AutoPatchResult.success; } else { autoPatchResult = AutoPatchResult.disabled; } } catch (Exception ex) { autoPatchResult = AutoPatchResult.error; TimeLogger.Logger.LogTimeExceptionWithMessage("Error auto patching class " + autoPatchType.FullName + ".", ex, (LogCategories)8); if (autoPatchInstance != null) { TimeLogger.Logger.LogTimeError(autoPatchInstance.ErrorMessageOnAutoPatchFail, (LogCategories)2097152); if (autoPatchInstance.IsRollbackOnAutoPatchFail && autoPatchInstance.GetPatchedCount() > 0) { autoPatchInstance.UnpatchInstance(); } } } finally { try { autoPatchInstance?.RaiseEventOnAutoPatchFinish(autoPatchResult); } catch (Exception ex2) { TimeLogger.Logger.LogTimeExceptionWithMessage("Error calling RaiseEventOnAutoPatchFinish:", ex2, (LogCategories)2097152); } } return autoPatchResult; } public static bool IsPatchActiveFromResult(AutoPatchResult autoPatchResult) { switch (autoPatchResult) { case AutoPatchResult.success: return true; case AutoPatchResult.error: case AutoPatchResult.disabled: return false; default: throw new NotImplementedException($"The switch case {autoPatchResult} is not implemented or should not have happened."); } } private static IEnumerable<Type> GetAutoPatchClasses(Assembly assembly) { return from type in assembly.GetTypes() where type.IsClass && !type.IsAbstract && type.IsSubclassOf(typeof(AutoPatchedInstanceBase)) select type; } public static string[] GetHarmonyInstanceIdsForAttribute(Type[] autoPatchTypes) { return autoPatchTypes.Select((Type autoPatchType) => AutoPatchContainer.GetAbstractInstance(autoPatchType).harmonyPatchInstance.Value.GetHarmonyInstanceId()).ToArray(); } } } namespace Damntry.UtilsBepInEx.HarmonyPatching.AutoPatching.Interfaces { public interface IAutoPatchSupport : ISelfPatch { bool IsAutoPatchEnabled { get; } bool IsRollbackOnAutoPatchFail { get; } string ErrorMessageOnAutoPatchFail { get; } void RaiseEventOnAutoPatchFinish(AutoPatcher.AutoPatchResult patchResult); } public interface IConfigPatchDependence { void SetSettingPatchDependence<T>(ConfigEntry<T> configEntry); } public interface ISelfPatch { bool IsPatchActive { get; } event Action<bool> OnPatchFinished; List<MethodInfo> PatchInstance(); void UnpatchInstance(); } } namespace Damntry.UtilsBepInEx.HarmonyPatching.AutoPatching.BaseClasses { public abstract class AutoPatchedInstanceBase : IAutoPatchSupport, ISelfPatch, IConfigPatchDependence { internal readonly Lazy<HarmonyInstancePatcher> harmonyPatchInstance; public abstract bool IsAutoPatchEnabled { get; } public abstract bool IsRollbackOnAutoPatchFail { get; } public abstract bool IsPatchActive { get; protected set; } public abstract string ErrorMessageOnAutoPatchFail { get; protected set; } public abstract event Action<bool> OnPatchFinished; internal AutoPatchedInstanceBase() { harmonyPatchInstance = new Lazy<HarmonyInstancePatcher>(() => new HarmonyInstancePatcher(GetType())); } public List<MethodInfo> PatchInstance() { return harmonyPatchInstance.Value.PatchInstance(); } public void UnpatchInstance() { harmonyPatchInstance.Value.UnpatchInstance(); } public int GetPatchedCount() { return harmonyPatchInstance.Value.GetPatchedCount(); } public abstract void RaiseEventOnAutoPatchFinish(AutoPatcher.AutoPatchResult autoPatchResult); public void SetSettingPatchDependence<U>(ConfigEntry<U> configEntry) { OnPatchFinished += delegate(bool IsPatchActive) { configEntry.SetConfigAttribute<U, bool>(ConfigurationManagerAttributes.ConfigAttributes.Browsable, IsPatchActive); }; } } } namespace Damntry.UtilsBepInEx.HarmonyPatching.AutoPatching.BaseClasses.Inheritable { public abstract class FullyAutoPatchedInstance : AutoPatchedInstanceBase { public override bool IsRollbackOnAutoPatchFail => true; public override bool IsPatchActive { get; protected set; } public override event Action<bool> OnPatchFinished; public void AutoPatchResultEvent(AutoPatcher.AutoPatchResult autoPatchResult) { IsPatchActive = AutoPatcher.IsPatchActiveFromResult(autoPatchResult); } public virtual void OnPatchFinishedVirtual(bool IsActive) { } public override void RaiseEventOnAutoPatchFinish(AutoPatcher.AutoPatchResult autoPatchResult) { AutoPatchResultEvent(autoPatchResult); if (OnPatchFinished != null) { OnPatchFinished(IsPatchActive); } OnPatchFinishedVirtual(IsPatchActive); } } public abstract class HybridPatchedInstance : AutoPatchedInstanceBase { public override void RaiseEventOnAutoPatchFinish(AutoPatcher.AutoPatchResult autoPatchResult) { } public List<MethodInfo> PatchClassByType(Type classType) { return harmonyPatchInstance.Value.PatchClassByType(classType); } public void UnpatchMethod(Type classType, string methodName) { harmonyPatchInstance.Value.UnpatchMethod(classType, methodName); } } } namespace Damntry.UtilsBepInEx.HarmonyPatching.AutoPatching.Attributes { [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] public class AutoPatchIgnoreClass : AutoPatchAttribute { } [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] public class AutoPatchIgnoreClassAndNested : AutoPatchAttribute { } public abstract class AutoPatchAttribute : Attribute { } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] public class HarmonyBeforeInstance : HarmonyPatch { private HarmonyBeforeInstance() { } public HarmonyBeforeInstance(params Type[] beforeInstances) { ((HarmonyAttribute)this).info.before = AutoPatcher.GetHarmonyInstanceIdsForAttribute(beforeInstances); } } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] public class HarmonyAfterInstance : HarmonyPatch { private HarmonyAfterInstance() { } public HarmonyAfterInstance(params Type[] afterInstances) { ((HarmonyAttribute)this).info.after = AutoPatcher.GetHarmonyInstanceIdsForAttribute(afterInstances); } } } namespace Damntry.UtilsBepInEx.HarmonyPatching.Attributes { [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Delegate, AllowMultiple = true)] public class HarmonyPatchStringTypes : HarmonyAttribute { public HarmonyPatchStringTypes(string fullTypeName, string methodName) { SetMethodInfo(fullTypeName, methodName, (Type[])null); } public HarmonyPatchStringTypes(string fullTypeName, string methodName, Type[] argumentTypes) { SetMethodInfo(fullTypeName, methodName, argumentTypes); } private void SetMethodInfo(string fullTypeName, string methodName, string[] argumentFullTypeNames) { Type[] typesFromLoadedAssemblies = AssemblyUtils.GetTypesFromLoadedAssemblies(true, argumentFullTypeNames); SetMethodInfo(fullTypeName, methodName, typesFromLoadedAssemblies); } private void SetMethodInfo(string fullTypeName, string methodName, Type[] argumentTypes) { Type typeFromLoadedAssemblies = AssemblyUtils.GetTypeFromLoadedAssemblies(fullTypeName, true); base.info.declaringType = typeFromLoadedAssemblies; base.info.methodName = methodName; base.info.argumentTypes = argumentTypes; } } } namespace Damntry.UtilsBepInEx.Configuration { public static class BepinexConfigExtensions { public static T GetMaxValue<T>(this AcceptableValueBase acceptableVal) where T : IComparable, IEquatable<T> { if (acceptableVal is AcceptableValueRange<T>) { return ((AcceptableValueRange<T>)(object)acceptableVal).GetMaxNumericValue<T>(); } if (acceptableVal is AcceptableValueList<T>) { return ((AcceptableValueList<T>)(object)acceptableVal).GetMaxNumericValue<T>(); } return default(T); } public static T GetMaxNumericValue<T>(this AcceptableValueRange<T> acceptableVal) where T : IComparable { return acceptableVal.MaxValue; } public static T GetMaxNumericValue<T>(this AcceptableValueList<T> acceptableVal) where T : IEquatable<T>, IComparable { if (!NumberExtensionMethods.IsNumeric(typeof(T))) { throw new InvalidOperationException("Only numeric types are allowed, but received a " + typeof(T).FullName); } return acceptableVal.AcceptableValues.OrderBy((T x) => x).Last(); } } } namespace Damntry.UtilsBepInEx.Configuration.ConfigurationManager { public enum MultiplayerModInstallSide { Unspecified, Any, HostSideOnly, HostAndOptionalClient, HostAndSoftClient, HostAndHardClient } public class ConfigManagerController { internal const string ConfigMngFullTypeName = "ConfigurationManager.ConfigurationManager"; private const string InstallSideInitialDescription = "[Multiplayer requirements] This setting requires that the mod "; private ConfigFile configFile; private string currentSectionName; private int currentSectionOrder; private int currentConfigOrder; public ConfigManagerController(ConfigFile configFile) { this.configFile = configFile ?? throw new ArgumentNullException("configFile"); currentSectionOrder = 0; currentConfigOrder = int.MaxValue; if (AssemblyUtils.GetTypeFromLoadedAssemblies("ConfigurationManager.ConfigurationManager", true) != null) { ConfigurationManagerPatch.PatchSelf(); } ConfigEntryBasePatch.PatchSelf(); } private void SetSection(string sectionName, bool skipSectionOrder = false) { if (!(sectionName == currentSectionName)) { if (!skipSectionOrder) { currentSectionOrder++; } currentConfigOrder = int.MaxValue; currentSectionName = sectionName; } } public static bool RefreshGUI() { if (ConfigurationManagerPatch.ConfigMngInstance != null) { string text = "BuildSettingList"; try { ReflectionHelper.CallMethod(ConfigurationManagerPatch.ConfigMngInstance, text, (object[])null); return true; } catch (Exception ex) { TimeLogger.Logger.LogTimeExceptionWithMessage("Error when trying to call the method to refresh ConfigurationManager GUI.", ex, (LogCategories)64); } } return false; } public ConfigEntry<bool> AddSectionNote(string sectionText, string description = null, IConfigPatchDependence patchInstanceDependency = null, bool hidden = false, bool isAdvanced = false) { string text = "\u200b"; ConfigEntryBasePatch.AddNote(text); return AddConfig(sectionText, text, defaultValue: true, description, patchInstanceDependency, MultiplayerModInstallSide.Unspecified, hidden, disabled: true, null, showRangeAsPercent: false, hideDefaultButton: true, isAdvanced, skipSectionIncrease: true); } public ConfigEntry<string> AddQuasiNote(string sectionName, string key, string textboxMessage, string description = null, IConfigPatchDependence patchInstanceDependency = null, bool hidden = false, bool isAdvanced = false) { return AddQuasiNote(sectionName, key, textboxMessage, description, patchInstanceDependency, hidden, isAdvanced, skipSectionIncrease: false); } public ConfigEntry<string> AddGUIHiddenNote(string sectionName, string key, string description = null, IConfigPatchDependence patchInstanceDependency = null, bool isAdvanced = false) { return AddQuasiNote(sectionName, key, null, description, patchInstanceDependency, hidden: true, isAdvanced, skipSectionIncrease: true); } private ConfigEntry<string> AddQuasiNote(string sectionName, string key, string textboxMessage, string description = null, IConfigPatchDependence patchInstanceDependency = null, bool hidden = false, bool isAdvanced = false, bool skipSectionIncrease = false) { if (textboxMessage == null) { textboxMessage = ""; } ConfigEntryBasePatch.AddNote(key); return AddConfig(sectionName, key, textboxMessage, description, patchInstanceDependency, MultiplayerModInstallSide.Unspecified, hidden, disabled: true, null, showRangeAsPercent: false, hideDefaultButton: true, isAdvanced, skipSectionIncrease); } public ConfigEntry<T> AddConfig<T>(string sectionName, string key, T defaultValue, string description = null, IConfigPatchDependence patchInstanceDependency = null, MultiplayerModInstallSide modInstallSide = MultiplayerModInstallSide.Unspecified, bool hidden = false, bool disabled = false, bool hideDefaultButton = false, bool isAdvanced = false) { return AddConfig(sectionName, key, defaultValue, description, patchInstanceDependency, modInstallSide, hidden, disabled, null, showRangeAsPercent: false, hideDefaultButton, isAdvanced); } public ConfigEntry<T> AddConfigWithAcceptableValues<T>(string sectionName, string key, T defaultValue, string description = null, IConfigPatchDependence patchInstanceDependency = null, MultiplayerModInstallSide modInstallSide = MultiplayerModInstallSide.Unspecified, bool hidden = false, bool disabled = false, AcceptableValueList<T> acceptableValueList = null, bool hideDefaultButton = false, bool isAdvanced = false) where T : IEquatable<T> { return AddConfig(sectionName, key, defaultValue, description, patchInstanceDependency, modInstallSide, hidden, disabled, (AcceptableValueBase)(object)acceptableValueList, showRangeAsPercent: false, hideDefaultButton, isAdvanced); } public ConfigEntry<T> AddConfigWithAcceptableValues<T>(string sectionName, string key, T defaultValue, string description = null, IConfigPatchDependence patchInstanceDependency = null, MultiplayerModInstallSide modInstallSide = MultiplayerModInstallSide.Unspecified, bool hidden = false, bool disabled = false, AcceptableValueRange<T> acceptableValueRange = null, bool showRangeAsPercent = false, bool hideDefaultButton = false, bool isAdvanced = false) where T : IComparable { return AddConfig(sectionName, key, defaultValue, description, patchInstanceDependency, modInstallSide, hidden, disabled, (AcceptableValueBase)(object)acceptableValueRange, showRangeAsPercent, hideDefaultButton, isAdvanced); } private ConfigEntry<T> AddConfig<T>(string sectionName, string key, T defaultValue, string description = null, IConfigPatchDependence patchInstanceDependency = null, MultiplayerModInstallSide modInstallSide = MultiplayerModInstallSide.Unspecified, bool hidden = false, bool disabled = false, AcceptableValueBase acceptableVal = null, bool showRangeAsPercent = false, bool hideDefaultButton = false, bool isAdvanced = false, bool skipSectionIncrease = false) { SetSection(sectionName, skipSectionIncrease); return AddConfig(key, defaultValue, description, patchInstanceDependency, modInstallSide, hidden, disabled, acceptableVal, showRangeAsPercent, hideDefaultButton, isAdvanced); } private ConfigEntry<T> AddConfig<T>(string key, T defaultValue, string description = null, IConfigPatchDependence patchInstanceDependency = null, MultiplayerModInstallSide modInstallSide = MultiplayerModInstallSide.Unspecified, bool hidden = false, bool disabled = false, AcceptableValueBase acceptableVal = null, bool showRangeAsPercent = false, bool hideDefaultButton = false, bool isAdvanced = false) { //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Expected O, but got Unknown if (description == null) { description = ""; } if (patchInstanceDependency != null) { hidden = true; } if (modInstallSide != 0) { string descriptionFromInstallSide = GetDescriptionFromInstallSide(modInstallSide); if (!Utility.IsNullOrWhiteSpace(description)) { description += "\n\n"; } description += descriptionFromInstallSide; } ConfigDescription val = new ConfigDescription(description, acceptableVal, new object[1] { new ConfigurationManagerAttributes { IsAdvanced = isAdvanced, Order = currentConfigOrder--, Browsable = !hidden, DefaultValue = defaultValue, ShowRangeAsPercent = showRangeAsPercent, ReadOnly = disabled, HideDefaultButton = hideDefaultButton, Category = currentSectionOrder.ToString("D2") + ". " + currentSectionName } }); ConfigEntry<T> val2 = configFile.Bind<T>(currentSectionName, key, defaultValue, val); patchInstanceDependency?.SetSettingPatchDependence<T>(val2); return val2; } public bool Remove(string section, string key) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected O, but got Unknown return configFile.Remove(new ConfigDefinition(section, key)); } public bool Remove(ConfigDefinition key) { return configFile.Remove(key); } public void ClearAllConfigs() { configFile.Clear(); } public int Count() { return configFile.Count; } private string GetDescriptionFromInstallSide(MultiplayerModInstallSide modInstallSide) { return "[Multiplayer requirements] This setting requires that the mod " + modInstallSide switch { MultiplayerModInstallSide.Any => "is installed for the player/s that wants its functionality, hosting or not.", MultiplayerModInstallSide.HostSideOnly => "is installed in the host. It is not needed in the client.", MultiplayerModInstallSide.HostAndOptionalClient => "is installed in the host. Clients can also install it.", MultiplayerModInstallSide.HostAndSoftClient => "is installed in the host, and should be installed in the clients.", MultiplayerModInstallSide.HostAndHardClient => "is installed in both the host and the clients.", _ => "", }; } public bool SetConfigAttribute<T1, T2>(string section, string key, ConfigurationManagerAttributes.ConfigAttributes configAttribute, T2 attrValue) { return GetConfigEntry<T2>(section, key).SetConfigAttribute<T2, T2>(configAttribute, attrValue); } public R GetConfigAttribute<T, R>(string section, string key, ConfigurationManagerAttributes.ConfigAttributes configAttribute) { return GetConfigEntry<T>(section, key).GetConfigAttribute<T, R>(configAttribute); } private ConfigEntry<T> GetConfigEntry<T>(string section, string key) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Expected O, but got Unknown ConfigEntry<T> val = default(ConfigEntry<T>); if (!configFile.TryGetEntry<T>(new ConfigDefinition(section, key), ref val) || ((ConfigEntryBase)val).Description == null || ((ConfigEntryBase)val).Description.Tags == null) { throw new InvalidOperationException("The config entry with section \"" + section + "\" and key \"" + key + "\" could not be found."); } return val; } } public static class ConfigManagerExtension { public static bool SetConfigAttribute
BepInEx/plugins/es.damntry.SuperQoLity/Damntry.Globals.dll
Decompiled a week agousing System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; using System.Diagnostics; using System.IO; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Serialization; using System.Runtime.Versioning; using System.Text; using System.Threading; using System.Threading.Tasks; using Damntry.Utils.Collections.Queues.Interfaces; using Damntry.Utils.ExtensionMethods; using Damntry.Utils.Logging; using Damntry.Utils.Tasks; using Damntry.Utils.Tasks.AsyncDelay; using Damntry.Utils.Tasks.TaskTimeout; using Damntry.Utils.Timers.StopwatchImpl; using Microsoft.CodeAnalysis; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("Damntry Globals")] [assembly: AssemblyDescription("Utils usable in any type of project")] [assembly: AssemblyCompany("Damntry")] [assembly: AssemblyProduct("Damntry Globals")] [assembly: AssemblyCopyright("Copyright © Damntry 2025")] [assembly: AssemblyFileVersion("0.4.6")] [assembly: TargetFramework(".NETFramework,Version=v4.8.1", FrameworkDisplayName = ".NET Framework 4.8.1")] [assembly: AssemblyVersion("0.4.6.0")] [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 HighResolutionTimer { public class HighResolutionTimer { public static readonly double TickLength = 1000f / (float)Stopwatch.Frequency; public static readonly double Frequency = Stopwatch.Frequency; public static bool IsHighResolution = Stopwatch.IsHighResolution; private volatile float _interval; private volatile bool _isRunning; private Thread _thread; public float Interval { get { return _interval; } set { if (value < 0f || float.IsNaN(value)) { throw new ArgumentOutOfRangeException("value"); } _interval = value; } } public bool IsRunning => _isRunning; public bool UseHighPriorityThread { get; set; } public event EventHandler<HighResolutionTimerElapsedEventArgs> Elapsed; public HighResolutionTimer() : this(1f) { } public HighResolutionTimer(float interval) { Interval = interval; } public void Start() { if (!_isRunning) { _isRunning = true; _thread = new Thread(ExecuteTimer) { IsBackground = true }; if (UseHighPriorityThread) { _thread.Priority = ThreadPriority.Highest; } _thread.Start(); } } public void Stop(bool joinThread = true) { _isRunning = false; if (joinThread && Thread.CurrentThread != _thread) { _thread.Join(); } } private void ExecuteTimer() { float num = 0f; Stopwatch stopwatch = new Stopwatch(); stopwatch.Start(); while (_isRunning) { num += _interval; double num2; while (true) { num2 = ElapsedHiRes(stopwatch); double num3 = (double)num - num2; if (num3 <= 0.0) { break; } if (num3 < 1.0) { Thread.SpinWait(10); } else if (num3 < 5.0) { Thread.SpinWait(100); } else if (num3 < 15.0) { Thread.Sleep(1); } else { Thread.Sleep(10); } if (!_isRunning) { return; } } double delay = num2 - (double)num; this.Elapsed?.Invoke(this, new HighResolutionTimerElapsedEventArgs(delay)); if (!_isRunning) { return; } if (stopwatch.Elapsed.TotalHours >= 1.0) { stopwatch.Restart(); num = 0f; } } stopwatch.Stop(); } private static double ElapsedHiRes(Stopwatch stopwatch) { return (double)stopwatch.ElapsedTicks * TickLength; } } public class HighResolutionTimerElapsedEventArgs : EventArgs { public double Delay { get; } internal HighResolutionTimerElapsedEventArgs(double delay) { Delay = delay; } } } namespace System.Runtime.CompilerServices { public class IsExternalInit { } } namespace Damntry.Utils.Timers { public class PeriodicTimeLimitedCounter<T> where T : IStopwatch, new() { private IStopwatch swLimitHit; private bool constantPeriodTimer; private int hitCounter; private int hitCounterMax; private double maxPeriodTimeMillis; private bool triggerOncePerPeriod; public PeriodicTimeLimitedCounter(bool constantPeriodTimer, int hitCounterMax, double maxPeriodTimeMillis, bool ignoreFirstHit, bool triggerOncePerPeriod = true) { this.constantPeriodTimer = constantPeriodTimer; this.hitCounterMax = hitCounterMax; this.maxPeriodTimeMillis = maxPeriodTimeMillis; hitCounter = (ignoreFirstHit ? (-1) : 0); this.triggerOncePerPeriod = triggerOncePerPeriod; swLimitHit = new T(); } public bool TryIncreaseCounter() { bool flag = false; bool flag2 = false; hitCounter++; if (swLimitHit.ElapsedMillisecondsPrecise <= maxPeriodTimeMillis) { if (!constantPeriodTimer) { flag = true; } flag2 = (triggerOncePerPeriod ? (hitCounter == hitCounterMax) : (hitCounter >= hitCounterMax)); } else { flag = true; } if (flag) { swLimitHit.Restart(); hitCounter = 0; } if (!swLimitHit.IsRunning) { swLimitHit.Start(); } return !flag2; } public void StartTime() { swLimitHit.Restart(); } public void ResetAll() { hitCounter = 0; swLimitHit.Reset(); } } } namespace Damntry.Utils.Timers.StopwatchImpl { public interface IStopwatch { bool IsRunning { get; } long ElapsedMilliseconds { get; } long ElapsedSeconds { get; } double ElapsedMillisecondsPrecise { get; } double ElapsedSecondsPrecise { get; } void Start(); void Stop(); void Reset(); void Restart(); } public class StopwatchDiag : Stopwatch, IStopwatch { public long ElapsedSeconds => base.ElapsedMilliseconds * 1000; public double ElapsedMillisecondsPrecise => base.Elapsed.TotalMilliseconds; public double ElapsedSecondsPrecise => base.Elapsed.TotalSeconds; public new static StopwatchDiag StartNew() { StopwatchDiag stopwatchDiag = new StopwatchDiag(); stopwatchDiag.Start(); return stopwatchDiag; } bool IStopwatch.get_IsRunning() { return base.IsRunning; } long IStopwatch.get_ElapsedMilliseconds() { return base.ElapsedMilliseconds; } void IStopwatch.Start() { Start(); } void IStopwatch.Stop() { Stop(); } void IStopwatch.Reset() { Reset(); } void IStopwatch.Restart() { Restart(); } } } namespace Damntry.Utils.Tasks { public class CancellableSingleTask<T> where T : AsyncDelayBase<T> { private SemaphoreSlim semaphoreLock; private Task task; private string taskLogName; private CancellationTokenSource cancelTokenSource; private bool isSemaphoreAcquiredManually; private bool logEvents; private int maxExternalSemaphoreAcquireTimeMillis; public bool IsTaskRunning { get { if (task != null) { return task.Status == TaskStatus.Running; } return false; } } public bool IsCancellationRequested { get { if (cancelTokenSource != null) { return cancelTokenSource.IsCancellationRequested; } return false; } } public CancellationToken CancellationToken { get { if (cancelTokenSource != null) { return cancelTokenSource.Token; } throw new InvalidOperationException("There is no cancellation token as no task has been started."); } } public CancellableSingleTask(bool logEvents = true) { this.logEvents = logEvents; semaphoreLock = new SemaphoreSlim(1, 1); isSemaphoreAcquiredManually = false; } public async Task StartTaskAsync(Func<Task> asyncWorkerFunction, string taskLogName, bool throwExceptionIfRunning) { await StartTaskAsync(asyncWorkerFunction, taskLogName, awaitTask: false, throwExceptionIfRunning, newThread: false); } public async Task StartThreadedTaskAsync(Func<Task> asyncWorkerFunction, string taskLogName, bool throwExceptionIfRunning) { await StartTaskAsync(asyncWorkerFunction, taskLogName, awaitTask: false, throwExceptionIfRunning, newThread: true); } public async Task StartThreadedTaskAsync(Action workerFunction, string taskLogName, bool throwExceptionIfRunning) { await StartTaskAsync(delegate { workerFunction(); return Task.CompletedTask; }, taskLogName, awaitTask: false, throwExceptionIfRunning, newThread: true); } public async Task StartAwaitableTaskAsync(Func<Task> asyncWorkerFunction, string taskLogName, bool throwExceptionIfRunning) { await StartTaskAsync(asyncWorkerFunction, taskLogName, awaitTask: true, throwExceptionIfRunning, newThread: false); } public async Task StartAwaitableThreadedTaskAsync(Func<Task> asyncWorkerFunction, string taskLogName, bool throwExceptionIfRunning) { await StartTaskAsync(asyncWorkerFunction, taskLogName, awaitTask: true, throwExceptionIfRunning, newThread: true); } private async Task StartTaskAsync(Func<Task> asyncWorkerFunction, string taskLogName, bool awaitTask, bool throwIfAlreadyRunning, bool newThread) { await GetSemaphoreLock(); try { if (task != null && !task.IsCompleted) { if (throwIfAlreadyRunning) { throw new InvalidOperationException(GetTextAlreadyRunningTask(taskLogName)); } if (logEvents) { TimeLogger.Logger.LogTimeDebugFunc(() => GetTextAlreadyRunningTask(taskLogName), LogCategories.Task); } return; } this.taskLogName = taskLogName; cancelTokenSource = new CancellationTokenSource(); if (logEvents) { TimeLogger.Logger.LogTimeDebugFunc(() => "Task \"" + taskLogName + "\" is now going to run " + (newThread ? "in a new thread" : "asynchronously") + ".", LogCategories.Task); } if (newThread) { task = Task.Run(() => asyncWorkerFunction()); } else { task = asyncWorkerFunction(); } } finally { semaphoreLock.Release(); } if (awaitTask) { await task; } else { task.FireAndForgetCancels(LogCategories.Task); } } private string GetTextAlreadyRunningTask(string taskLogName) { StringBuilder stringBuilder = new StringBuilder(25); if (this.taskLogName != taskLogName) { stringBuilder.Append("Cant start task \""); stringBuilder.Append(taskLogName); stringBuilder.Append("\": "); } stringBuilder.Append("Task \""); stringBuilder.Append(this.taskLogName); stringBuilder.Append("\" is already running."); return stringBuilder.ToString(); } public async Task StopTaskAndWaitAsync() { await StopTaskAndWaitAsync(null, null, -1); } public async Task StopTaskAndWaitAsync(int maxStopTimeMillis) { await StopTaskAndWaitAsync(null, null, maxStopTimeMillis); } public async Task StopTaskAndWaitAsync(Action onTaskStopped, int maxStopTimeMillis) { await StopTaskAndWaitAsync(onTaskStopped, null, maxStopTimeMillis); } public async Task StopTaskAndWaitAsync(Func<Task> onTaskStoppedAsync, int maxStopTimeMillis) { await StopTaskAndWaitAsync(null, onTaskStoppedAsync, maxStopTimeMillis); } private async Task StopTaskAndWaitAsync(Action onTaskStopped, Func<Task> onTaskStoppedAsync, int maxStopTimeMillis) { await GetSemaphoreLock(); try { Task obj = new Task(async delegate { if (task == null) { if (logEvents) { TimeLogger.Logger.LogTimeDebugFunc(() => "Cant stop task \"" + taskLogName + "\". It was never started, or already stopped.", LogCategories.Task); } } else { if (cancelTokenSource != null && !task.IsTaskEnded()) { if (logEvents) { TimeLogger.Logger.LogTimeDebugFunc(() => "Canceling task \"" + taskLogName + "\"", LogCategories.Task); } cancelTokenSource.Cancel(); } else if (logEvents) { TimeLogger.Logger.LogTimeDebugFunc(() => "Cant stop task \"" + taskLogName + "\". It is already finished.", LogCategories.Task); } try { await task; } catch (Exception ex) { if (!(ex is TaskCanceledException) && !(ex is OperationCanceledException)) { throw; } if (logEvents) { TimeLogger.Logger.LogTimeDebugFunc(() => "Task \"" + taskLogName + "\" successfully canceled.", LogCategories.Task); } } task.Dispose(); task = null; if (onTaskStopped != null) { onTaskStopped(); } if (onTaskStoppedAsync != null) { await onTaskStoppedAsync(); } } }); obj.Start(); await TaskTimeoutMethods<T>.AwaitTaskWithTimeoutAsync(obj, taskLogName, maxStopTimeMillis, throwTimeoutException: true); } finally { semaphoreLock.Release(); } } private async Task GetSemaphoreLock() { int millisecondsTimeout = -1; if (isSemaphoreAcquiredManually) { millisecondsTimeout = maxExternalSemaphoreAcquireTimeMillis; } if (!(await semaphoreLock.WaitAsync(millisecondsTimeout))) { throw new ExternalSemaphoreHeldException("The operation could not complete because the semaphore was held externally for too long."); } } public async Task WaitSemaphoreAsync(int maxSemaphoreAcquireTimeMillis) { await WaitSemaphoreTimeoutAsync(-1, maxSemaphoreAcquireTimeMillis); } public async Task<bool> WaitSemaphoreTimeoutAsync(int millisecondTimeout, int maxSemaphoreAcquireTimeMillis) { if (isSemaphoreAcquiredManually) { throw new InvalidOperationException("Semaphore lock has already been manually acquired. You must release it first by calling ReleaseSemaphore()."); } maxExternalSemaphoreAcquireTimeMillis = maxSemaphoreAcquireTimeMillis; isSemaphoreAcquiredManually = await semaphoreLock.WaitAsync(millisecondTimeout); return isSemaphoreAcquiredManually; } public void ReleaseSemaphore() { if (isSemaphoreAcquiredManually) { isSemaphoreAcquiredManually = false; semaphoreLock.Release(); return; } throw new InvalidOperationException("No lock for the semaphore was acquired manually. Call WaitSemaphoreAsync() first."); } } public class ExternalSemaphoreHeldException : Exception { public ExternalSemaphoreHeldException() { } public ExternalSemaphoreHeldException(string message) : base(message) { } public ExternalSemaphoreHeldException(string message, Exception inner) : base(message, inner) { } } public class DelayedSingleTask<T> where T : AsyncDelayBase<T> { private Task delayedTask; private CancellationTokenSource taskCancel; private Action actionTask; public DelayedSingleTask(Action actionTask) { if (actionTask == null) { throw new ArgumentNullException("actionTask"); } this.actionTask = actionTask; taskCancel = new CancellationTokenSource(); } public async void Start(int delayMillis) { if (delayedTask != null && !delayedTask.IsCompleted && !delayedTask.IsCanceled) { taskCancel.Cancel(); try { await delayedTask; } catch (Exception ex) { if (!(ex is TaskCanceledException) && !(ex is OperationCanceledException) && !(ex is ThreadAbortException)) { TimeLogger.Logger.LogTimeExceptionWithMessage("Exception while starting and executing delayed task.", ex, LogCategories.Task); } } taskCancel = new CancellationTokenSource(); } delayedTask = StartDelayedCancellableTask(delayMillis); delayedTask.FireAndForgetCancels(LogCategories.Task, dismissCancelLog: true); } private async Task StartDelayedCancellableTask(int delayMillis) { await AsyncDelayBase<T>.Instance.Delay(delayMillis, taskCancel.Token); if (!taskCancel.IsCancellationRequested) { actionTask(); } } } } namespace Damntry.Utils.Tasks.TaskTimeout { public class TaskTimeout<T> where T : AsyncDelayBase<T> { public static async Task StartAwaitableTaskWithTimeoutAsync(Func<CancellationToken, Task> asyncWorkerFunction, string taskLogName, int maxCompletionTimeMillis) { await StartTaskStaticAndWaitWithTimeoutAsync(asyncWorkerFunction, taskLogName, newThread: false, maxCompletionTimeMillis); } public static async Task StartAwaitableThreadedTaskWithTimeoutAsync(Func<CancellationToken, Task> asyncWorkerFunction, string taskLogName, int maxCompletionTimeMillis) { await StartTaskStaticAndWaitWithTimeoutAsync(asyncWorkerFunction, taskLogName, newThread: true, maxCompletionTimeMillis); } private static async Task StartTaskStaticAndWaitWithTimeoutAsync(Func<CancellationToken, Task> asyncWorkerFunction, string taskLogName, bool newThread, int maxCompletionTimeMillis) { TimeLogger.Logger.LogTimeDebugFunc(() => "Task \"" + taskLogName + "\" is now going to run " + (newThread ? "in a new thread" : "asynchronously") + ".", LogCategories.Task); CancellationTokenSource cancelWorker = new CancellationTokenSource(); Task workTask = ((!newThread) ? asyncWorkerFunction(cancelWorker.Token) : Task.Run(() => asyncWorkerFunction(cancelWorker.Token))); if (!(await TaskTimeoutMethods<T>.AwaitTaskWithTimeoutAsync(workTask, taskLogName, maxCompletionTimeMillis, throwTimeoutException: false))) { cancelWorker.Cancel(); await workTask; throw new TimeoutException("Task \"" + taskLogName + "\" is finished but it took longer than " + maxCompletionTimeMillis + "ms."); } } } internal class TaskTimeoutMethods<T> where T : AsyncDelayBase<T> { internal static async Task<bool> AwaitTaskWithTimeoutAsync(Task task, string taskLogName, int maxStopTimeMillis, bool throwTimeoutException) { CancellationTokenSource cancelDelay = new CancellationTokenSource(); try { await AwaitTaskWithTimeoutAsync(task, taskLogName, maxStopTimeMillis, cancelDelay.Token); if (!task.IsCompleted) { if (throwTimeoutException) { throw new TimeoutException($"Task \"{taskLogName}\" took longer than the specified {maxStopTimeMillis} ms to stop."); } return false; } return true; } finally { cancelDelay.Cancel(); } } private static async Task AwaitTaskWithTimeoutAsync(Task task, string taskLogName, int maxStopTimeMillis, CancellationToken cancelToken) { try { await Task.WhenAny(new Task[2] { task, AsyncDelayBase<T>.Instance.Delay(maxStopTimeMillis, cancelToken) }); } catch (Exception ex) { if (ex is TaskCanceledException || ex is OperationCanceledException) { TimeLogger.Logger.LogTimeDebugFunc(() => "Task \"" + taskLogName + "\" successfully canceled.", LogCategories.Task); return; } throw; } } } } namespace Damntry.Utils.Tasks.AsyncDelay { public class AsyncDelay : AsyncDelayBase<AsyncDelay> { public override Task Delay(int millisecondsDelay) { return Task.Delay(millisecondsDelay); } public override Task Delay(int millisecondsDelay, CancellationToken cancellationToken) { return Task.Delay(millisecondsDelay, cancellationToken); } } public abstract class AsyncDelayBase<T> where T : AsyncDelayBase<T> { private static AsyncDelayBase<T> instance = Activator.CreateInstance<T>(); public static AsyncDelayBase<T> Instance => instance; public abstract Task Delay(int millisecondsDelay); public abstract Task Delay(int millisecondsDelay, CancellationToken cancellationToken); } } namespace Damntry.Utils.Reflection { public static class AssemblyUtils { private static Assembly[] assemblyCache; public static MethodInfo GetMethodFromLoadedAssembly(string fullTypeName, string methodName, bool refreshCache = true) { return GetTypeFromLoadedAssemblies(fullTypeName, refreshCache).GetMethod(methodName, ReflectionHelper.AllBindings); } public static Type GetTypeFromLoadedAssemblies(string fullTypeName, bool refreshCache = true) { if (refreshCache || assemblyCache == null) { assemblyCache = AppDomain.CurrentDomain.GetAssemblies(); } return (from a in assemblyCache select a.GetType(fullTypeName, throwOnError: false) into t where t != null select t).FirstOrDefault(); } public static Type[] GetTypesFromLoadedAssemblies(bool refreshCache, params string[] argumentFullTypeNames) { if (argumentFullTypeNames == null) { return null; } bool flag = true; List<Type> list = new List<Type>(argumentFullTypeNames.Length); foreach (string text in argumentFullTypeNames) { Type typeFromLoadedAssemblies = GetTypeFromLoadedAssemblies(text, flag && refreshCache); if (typeFromLoadedAssemblies == null) { throw new ArgumentException("The type with value \"" + text + "\" couldnt be found in the assembly."); } list.Add(typeFromLoadedAssemblies); flag = false; } return list?.ToArray(); } public static string GetAssemblyDllFilePath(Type assemblyType) { return Assembly.GetAssembly(assemblyType).Location; } public static string GetAssemblyDllFolderPath(Type assemblyType) { return Path.GetDirectoryName(GetAssemblyDllFilePath(assemblyType)); } public static string GetCombinedPathFromAssemblyFolder(Type assemblyType, string addedPath) { string text = GetAssemblyDllFolderPath(assemblyType); char directorySeparatorChar = Path.DirectorySeparatorChar; if (!addedPath.StartsWith(directorySeparatorChar.ToString())) { string text2 = text; directorySeparatorChar = Path.DirectorySeparatorChar; text = text2 + directorySeparatorChar; } return text + addedPath; } } public class MemberInfoHelper : MemberInfo { private readonly MemberInfo member; private FieldInfo Field => (FieldInfo)member; private PropertyInfo Property => (PropertyInfo)member; public bool IsStatic => member.MemberType switch { MemberTypes.Field => Field.IsStatic, MemberTypes.Property => Property.IsStatic(), _ => throw new NotImplementedException(), }; public Type MemberInfoType => member.MemberType switch { MemberTypes.Field => Field.FieldType, MemberTypes.Property => Property.PropertyType, _ => throw new NotImplementedException(), }; public override MemberTypes MemberType => member.MemberType; public override string Name => member.Name; public override Type DeclaringType => member.DeclaringType; public override Type ReflectedType => member.ReflectedType; public MemberInfoHelper(MemberInfo memberInfo) { if (memberInfo == null) { throw new ArgumentNullException("member"); } if (!(memberInfo is FieldInfo) && !(memberInfo is PropertyInfo)) { throw new NotSupportedException("Only FieldInfo and PropertyInfo members are supported."); } member = memberInfo; } public void SetValue(object obj, object value) { if (member.MemberType == MemberTypes.Field) { Field.SetValue(obj, value); } else if (member.MemberType == MemberTypes.Property) { Property.SetValue(obj, value); } } public object GetValue(object obj) { if (member.MemberType == MemberTypes.Field) { return Field.GetValue(obj); } if (member.MemberType == MemberTypes.Property) { return Property.GetValue(obj); } throw new NotImplementedException(); } public object GetValueStaticAgnostic(object obj) { obj = (IsStatic ? null : obj); if (member.MemberType == MemberTypes.Field) { return Field.GetValue(obj); } if (member.MemberType == MemberTypes.Property) { return Property.GetValue(obj); } throw new NotImplementedException(); } public override object[] GetCustomAttributes(bool inherit) { return member.GetCustomAttributes(inherit); } public override object[] GetCustomAttributes(Type attributeType, bool inherit) { return member.GetCustomAttributes(attributeType, inherit); } public override bool IsDefined(Type attributeType, bool inherit) { return member.IsDefined(attributeType, inherit); } } public static class ReflectionHelper { public enum MemberType { Property, Field, Both } public static BindingFlags AllBindings { get; } = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.GetField | BindingFlags.SetField | BindingFlags.GetProperty | BindingFlags.SetProperty; public static void CallMethod(object classInstance, string methodName, object[] args = null) { CallMethod<object>(classInstance, methodName, args); } public static R CallStaticMethod<R>(Type classType, string methodName, object[] args = null) { return CallMethod<R>(null, classType, methodName, args); } public static R CallMethod<R>(object classInstance, string methodName, object[] args = null) { return CallMethod<R>(classInstance, classInstance.GetType(), methodName, args); } private static R CallMethod<R>(object classInstance, Type classType, string methodName, object[] args = null) { MethodInfo method = classType.GetMethod(methodName, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); return CallMethod<R>(classInstance, method, args); } public static R CallMethod<R>(object classInstance, MethodInfo methodInfo, object[] args = null) { try { return (R)methodInfo.Invoke(classInstance, args); } catch (TargetParameterCountException e) { TimeLogger.Logger.LogTimeExceptionWithMessage("Parameter count error while calling method " + methodInfo.Name, e, LogCategories.Reflect); throw; } } public static string ListMemberValues(object target, MemberType memberType, bool showPrivate = true) { BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Public; if (showPrivate) { bindingFlags |= BindingFlags.NonPublic; } StringBuilder sbMembers = new StringBuilder(); MemberInfo[] array = Array.Empty<MemberInfo>(); if (memberType == MemberType.Both || memberType == MemberType.Property) { MemberInfo[] properties = target.GetType().GetProperties(bindingFlags); array = properties; } if (memberType == MemberType.Both || memberType == MemberType.Field) { if (array.Length != 0) { array = array.Concat(target.GetType().GetFields(bindingFlags)).ToArray(); } else { MemberInfo[] properties = target.GetType().GetFields(bindingFlags); array = properties; } } Array.ForEach(array, delegate(MemberInfo minfo) { sbMembers.Append(minfo.Name); sbMembers.Append(" = "); if (minfo is PropertyInfo) { sbMembers.Append(((PropertyInfo)minfo).GetValue(target, null)); } else if (minfo is FieldInfo) { sbMembers.Append(((FieldInfo)minfo).GetValue(target)); } sbMembers.AppendLine(); }); return sbMembers.ToString(); } public static string ConvertFullTypeToNormal(string type) { if (string.IsNullOrEmpty(type) || !type.Contains(".")) { return type; } return type.Substring(type.LastIndexOf(".") + 1); } public static string[] ConvertFullTypesToNormal(params string[] types) { string[] result = null; if (types == null || !types.Any()) { result = types.Select((string s) => ConvertFullTypeToNormal(s)).ToArray(); } return result; } public static object GetDefaultValue(this Type type) { return FormatterServices.GetUninitializedObject(type); } } } namespace Damntry.Utils.Maths { public static class MathMethods { public static ulong GreatestCommonDivisor(ulong n1, ulong n2) { while (n1 != 0L && n2 != 0L) { if (n1 > n2) { n1 %= n2; } else { n2 %= n1; } } return n1 | n2; } public static int CountDigits(uint num) { if ((long)num != 0L) { return (((long)num > 0L) ? 1 : 2) + (int)Math.Log10(Math.Abs((double)num)); } return 1; } public static int CountDigits(int num) { return CountDigits((uint)num); } } } namespace Damntry.Utils.Logging { public class LOG { public static void TEMPDEBUG(string message, bool onlyIfTrue = true) { } public static void TEMPDEBUG_FUNC(Func<string> textLambda, bool onlyIfTrue = true) { } public static void Debug(string message, LogCategories logCategory, bool onlyIfTrue = true) { } public static void Debug_func(Func<string> textLambda, LogCategories logCategory, bool onlyIfTrue = true) { } public static void TEMPWARNING(string message, bool onlyIfTrue = true) { } public static void TEMPWARNING_FUNC(Func<string> textLambda, bool onlyIfTrue = true) { } public static void TEMPFATAL(string message, bool onlyIfTrue = true) { } public static void TEMP(LogTier logLevel, string message, bool onlyIfTrue = true) { } private static void Log(LogTier logLevel, string message, bool onlyIfTrue = true, LogCategories logCategory = LogCategories.TempTest) { } private static void LogFunc(LogTier logLevel, Func<string> textLambda, bool onlyIfTrue = true, LogCategories logCategory = LogCategories.TempTest) { } } public sealed class DefaultTimeLogger : TimeLogger { protected override void InitializeLogger(params object[] args) { } protected override void LogMessage(string message, LogTier logLevel) { ConsoleColor consoleColor = ConsoleColor.Gray; switch (logLevel) { case LogTier.All: consoleColor = ConsoleColor.White; break; case LogTier.Debug: consoleColor = ConsoleColor.DarkGray; break; case LogTier.Info: consoleColor = ConsoleColor.Gray; break; case LogTier.Warning: consoleColor = ConsoleColor.Yellow; break; case LogTier.Error: consoleColor = ConsoleColor.DarkRed; break; case LogTier.Fatal: consoleColor = ConsoleColor.Red; break; case LogTier.Message: consoleColor = ConsoleColor.Gray; break; case LogTier.None: return; default: throw new InvalidOperationException("Invalid log level."); } Console.ForegroundColor = consoleColor; Console.WriteLine(message); Console.ResetColor(); } } public static class Performance { private enum StopResetAction { Stop, Reset, Record, Log } public static class PerformanceFileLoggingMethods { public static async Task BeginOrResumePerfFileLoggingWithTextFunc(Func<string> getFirstPerfLogLineFunc) { if (TimeLogger.DebugEnabled) { string firstPerfLogText = null; if (getFirstPerfLogLineFunc != null) { firstPerfLogText = getFirstPerfLogLineFunc(); } await PerfLogger.DLog.BeginPerfFileLoggingWithText(firstPerfLogText); } } public static async Task BeginOrResumePerfFileLoggingWithText(string firstPerfLogLine) { if (TimeLogger.DebugEnabled) { await PerfLogger.DLog.BeginPerfFileLoggingWithText(firstPerfLogLine); } } public static async Task BeginOrResumePerfFileLogging() { if (TimeLogger.DebugEnabled) { await PerfLogger.DLog.BeginPerfFileLoggingWithText(null); } } public static async Task StopPerfFileLogging() { if (PerfLogger.IsInstanced) { await PerfLogger.DLog.StopPerfFileLogging(); } } } public static class PerformanceTableLoggingMethods { public static async Task StartLogPerformanceTableNewThread(int logTableIntervalMillis) { await logTableTask.StartThreadedTaskAsync(() => LogPerformanceTableInterval(logTableIntervalMillis), "Logger performance table", throwExceptionIfRunning: true); } public static async Task StopThreadLogPerformanceTable() { await logTableTask.StopTaskAndWaitAsync(5000); } public static void LogPerformanceTable() { Performance.LogPerformanceTable(); } } private class StopwatchMeasure : Stopwatch { private string name; private bool showTotals; private double lastRunMilli; private int runCount; private double min; private double max; private TrimmedMean trimmedMean; public double TotalMilli { get; private set; } public StopwatchMeasure(string measureName, bool showTotals) { this.showTotals = showTotals; name = measureName; trimmedMean = new TrimmedMean(); initializeMeasure(); } private void initializeMeasure() { TotalMilli = 0.0; runCount = 0; min = 2147483647.0; max = 0.0; trimmedMean.Initialize(); } [Obsolete("Use Start(resetRunValues) instead.", true)] public new void Start() { } public void Start(bool resetRunValues) { if (resetRunValues) { initializeMeasure(); } base.Start(); } public void RecordRun() { lastRunMilli = base.Elapsed.TotalMilliseconds; if (showTotals) { TotalMilli += lastRunMilli; runCount++; if (lastRunMilli < min) { min = lastRunMilli; } if (lastRunMilli > max) { max = lastRunMilli; } trimmedMean.addNewValue(lastRunMilli); } Reset(); } public string GetLogString() { string text = $"{name} has taken {lastRunMilli:F3} ms"; if (showTotals) { text += $", with a total of {TotalMilli:F3}ms spent in {runCount} run/s. Avg run: {GetAverageRun(formatTiming: false)}ms, Min: {min:F3}ms, Max: {max:F3}ms"; } return text + "."; } public (string name, string total, string runs, string avg, string meanTrim, string min, string max) GetFormattedRunValues(int measureNameMaxLength) { return (name.PadRight(measureNameMaxLength), FormatTiming(TotalMilli), ((base.IsRunning ? "*" : "") + runCount).PadLeft(RunCountPadding), GetAverageRun(formatTiming: true), GetMeanTrimmedRun(), FormatTiming(min), FormatTiming(max)); } private string GetAverageRun(bool formatTiming) { double number = Math.Round(TotalMilli / (double)runCount, MeasureDecimals); if (!formatTiming) { return number.ToString(); } return FormatTiming(number); } private string GetMeanTrimmedRun() { string result = ""; (double, TrimmedMean.CalculationResult) tuple = trimmedMean.GetTrimmedMean(); if (tuple.Item2 == TrimmedMean.CalculationResult.NotEnoughData) { result = "*Collecting*".PadSides(MeasureTimingsPadding); } else if (tuple.Item2 == TrimmedMean.CalculationResult.Partial) { result = FormatTiming(tuple.Item1, "~"); } else if (tuple.Item2 == TrimmedMean.CalculationResult.Full) { result = FormatTiming(tuple.Item1); } return result; } private string FormatTiming(double number, string leftSymbol = null) { string text = string.Format($"{{0:F{MeasureDecimals}}}ms", number); return (((leftSymbol != null) ? leftSymbol : "") + text).PadLeft(MeasureTimingsPadding); } } private class TrimmedMean { public enum CalculationResult { NotEnoughData, Partial, Full } private List<double> historyValues; private int rollingIndex; private readonly int maxNumValues; private readonly int minNumValues; private readonly int trimPercentage; public TrimmedMean(int minValuesHistoric = 5, int maxValuesHistoric = 30, int trimPercentage = 5) { if (minValuesHistoric > maxValuesHistoric) { throw new ArgumentException("minValuesHistoric must be less or equal than maxValuesHistoric."); } minNumValues = minValuesHistoric.ClampReturn(5, int.MaxValue); maxNumValues = maxValuesHistoric.ClampReturn(5, int.MaxValue); this.trimPercentage = trimPercentage.ClampReturn(0, 99); Initialize(); } public void Initialize() { if (historyValues == null) { historyValues = new List<double>(maxNumValues); } else { historyValues.Clear(); } rollingIndex = 0; } public void addNewValue(double value) { if (historyValues.Count > maxNumValues) { throw new InvalidOperationException("The trimmed mean cant contain more values that the specified limit."); } if (historyValues.Count == maxNumValues) { historyValues[rollingIndex] = value; } else { historyValues.Add(value); } rollingIndex++; if (rollingIndex >= maxNumValues) { rollingIndex = 0; } } public (double trimmedMean, CalculationResult calcResult) GetTrimmedMean() { CalculationResult item = CalculationResult.NotEnoughData; if (historyValues.Count < minNumValues) { return (0.0, item); } item = ((historyValues.Count < maxNumValues) ? CalculationResult.Partial : CalculationResult.Full); List<double> list = new List<double>(historyValues); list.Sort(); int num = (int)Math.Round((double)maxNumValues * ((double)trimPercentage / 100.0), 0); double num2 = 0.0; for (int i = num; i < list.Count - num; i++) { num2 += list[i]; } return (num2 / (double)(list.Count - num * 2), item); } } private class PerfLogger { private static readonly Lazy<PerfLogger> instance = new Lazy<PerfLogger>(() => new PerfLogger()); private static readonly Lazy<string> pathLogFile = new Lazy<string>(() => GetLogPathFile()); private static readonly string folderPerformanceLogs = "PerformanceLogs"; private static readonly int logIntervalTime = 5000; private static Queue<string> logQueue; private static object queueLock; private static CancellableSingleTask<AsyncDelay> threadedTask; private StringBuilder sbLogText; public static PerfLogger DLog => instance.Value; public static bool IsInstanced => instance != null; private PerfLogger() { logQueue = new Queue<string>(); queueLock = new object(); threadedTask = new CancellableSingleTask<AsyncDelay>(); sbLogText = new StringBuilder(); } public async Task BeginPerfFileLoggingWithText(string firstPerfLogText) { if (firstPerfLogText != null && firstPerfLogText != "") { QueueAtFront(firstPerfLogText); } await threadedTask.StartThreadedTaskAsync((Func<Task>)LogConsumer, "Write performance log", throwExceptionIfRunning: true); } public async Task StopPerfFileLogging() { await threadedTask.StopTaskAndWaitAsync(5000); WriteBacklogToDisk(checkCancellation: false); } private void QueueAtFront(string firstPerfLogText) { lock (queueLock) { string[] array = logQueue.ToArray(); logQueue.Clear(); logQueue.Enqueue(firstPerfLogText); string[] array2 = array; foreach (string item in array2) { logQueue.Enqueue(item); } } } public void LogPerformance(string message) { lock (queueLock) { logQueue.Enqueue(message); } } private async Task LogConsumer() { if (TimeLogger.DebugEnabled) { TimeLogger.Logger.LogTimeDebugFunc(() => "Beginning performance logging on file \"" + pathLogFile.Value + "\"", LogCategories.Loading); while (!threadedTask.IsCancellationRequested) { WriteBacklogToDisk(checkCancellation: true); await Task.Delay(logIntervalTime, threadedTask.CancellationToken); } TimeLogger.Logger.LogTimeDebug("Performance logging stopped.", LogCategories.Loading); } } private void WriteBacklogToDisk(bool checkCancellation) { lock (queueLock) { if (logQueue.Count > 0) { do { sbLogText.AppendLine(logQueue.Dequeue()); } while (logQueue.Count > 0); } } if ((!checkCancellation || !threadedTask.IsCancellationRequested) && sbLogText.Length > 0) { LogToDisk(sbLogText.ToString()); sbLogText.Clear(); } } private void LogToDisk(string text) { string directoryName = Path.GetDirectoryName(pathLogFile.Value); if (!Directory.Exists(directoryName)) { Directory.CreateDirectory(directoryName); } using StreamWriter streamWriter = new StreamWriter(pathLogFile.Value, append: true); streamWriter.Write(text); } private static string GetLogPathFile() { return Path.Combine(Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), folderPerformanceLogs), "PerformanceLog_" + DateTime.Now.ToString("yyyy.MM.dd-HH.mm.ss") + ".log"); } } private static Dictionary<string, StopwatchMeasure> mapMeasures = new Dictionary<string, StopwatchMeasure>(); private static int measureNameMaxLength; private static readonly int MeasureDecimals = 3; private static readonly int MeasureTimingsPadding = MeasureDecimals + 9; private static readonly int MeasureTotalTimingPadding = MeasureTimingsPadding + 2; private static readonly int RunCountPadding = 8; private static Stopwatch swPerfTotalRunTime; private static readonly Lazy<string> StaticHeaderText = new Lazy<string>(() => GetStaticHeaderText()); private static readonly Lazy<string> StaticHorizontalSeparatorText = new Lazy<string>(() => GetStaticHorizontalSeparatorText()); private static Action<string> logPerfAction = delegate(string text) { PerfLogger.DLog.LogPerformance(text); }; private static CancellableSingleTask<AsyncDelay> logTableTask = new CancellableSingleTask<AsyncDelay>(); public static void Start(string measureName, bool logTotals = true) { GetCreateMeasure(measureName, logTotals).Start(resetRunValues: false); } public static void StartOver(string measureName, bool logTotals = true) { GetCreateMeasure(measureName, logTotals).Start(resetRunValues: true); } public static void Reset(string measureName, bool doNotWarn = false) { StopOrReset(measureName, StopResetAction.Reset, doNotWarn); } public static void Stop(string measureName, bool doNotWarn = false) { StopOrReset(measureName, StopResetAction.Stop, doNotWarn); } public static void StopAndLog(string measureName, bool doNotWarn = false) { StopOrReset(measureName, StopResetAction.Log, doNotWarn); } public static void StopAndRecord(string measureName, bool doNotWarn = false) { StopOrReset(measureName, StopResetAction.Record, doNotWarn); } private static void StopOrReset(string measureName, StopResetAction stopResetAction, bool doNotWarn) { StopwatchMeasure swMeasure = GetMeasure(measureName, doNotWarn); if (swMeasure == null) { return; } if (stopResetAction == StopResetAction.Reset) { swMeasure.Reset(); return; } if (swMeasure.IsRunning) { swMeasure.Stop(); } if (stopResetAction != StopResetAction.Record && stopResetAction != StopResetAction.Log) { return; } swMeasure.RecordRun(); if (stopResetAction == StopResetAction.Log) { LogPerformance(() => swMeasure.GetLogString()); } } private static StopwatchMeasure GetCreateMeasure(string measureName, bool logTotals) { mapMeasures.TryGetValue(measureName, out var value); if (value == null) { value = new StopwatchMeasure(measureName, logTotals); mapMeasures.Add(measureName, value); measureNameMaxLength = Math.Max(measureNameMaxLength, measureName.Length); if (swPerfTotalRunTime == null) { swPerfTotalRunTime = Stopwatch.StartNew(); } } return value; } private static StopwatchMeasure GetMeasure(string measureName, bool doNotWarn) { if (!mapMeasures.TryGetValue(measureName, out var value) && !doNotWarn) { TimeLogger.Logger.LogTimeWarning("The specified stopwatch \"" + measureName + "\" doesnt exist.", LogCategories.PerfTest); } return value; } private static async Task LogPerformanceTableInterval(int logTableIntervalMillis) { while (!logTableTask.IsCancellationRequested) { await Task.Delay(logTableIntervalMillis, logTableTask.CancellationToken); LogPerformanceTable(); } } private static void LogPerformanceTable() { LogPerformance(() => GetAllMeasuresTotalsSorted()); } private static void LogPerformance(Func<string> logTextFunc) { TimeLogger.Logger.LogTimeDebugFunc(logTextFunc, LogCategories.PerfTest, (logPerfAction, true)); } private static string GetAllMeasuresTotalsSorted() { List<StopwatchMeasure> list = (from x in mapMeasures.Values.ToList() orderby x.TotalMilli descending select x).ToList(); string value = "".PadRight(5, '\t'); string logStringSummaryHorizontalSeparator = GetLogStringSummaryHorizontalSeparator(); string logStringSummaryHeader = GetLogStringSummaryHeader(); StringBuilder stringBuilder = new StringBuilder(100 + 125 * list.Count); stringBuilder.Append("Performance table, sorted by total time. "); if (swPerfTotalRunTime != null) { stringBuilder.AppendLine("The total run time since the Start of the first measure is " + swPerfTotalRunTime.Elapsed.ToString("hh':'mm':'ss'.'fff")); } else { stringBuilder.AppendLine("No measures have been created yet."); } stringBuilder.Append(value); stringBuilder.AppendLine(logStringSummaryHorizontalSeparator); stringBuilder.Append(value); stringBuilder.AppendLine(logStringSummaryHeader); stringBuilder.Append(value); stringBuilder.Append(logStringSummaryHorizontalSeparator); foreach (StopwatchMeasure item in list) { stringBuilder.AppendLine(); stringBuilder.Append(value); stringBuilder.Append(GetLogStringSummaryRunValues(item)); } stringBuilder.AppendLine(); stringBuilder.Append(value); stringBuilder.AppendLine(logStringSummaryHorizontalSeparator); return stringBuilder.ToString(); } private static string GetLogStringSummaryHeader() { return "| " + "Measure name".PadSides(measureNameMaxLength) + " " + StaticHeaderText.Value; } private static string GetLogStringSummaryHorizontalSeparator() { return "|-" + "".PadSides(measureNameMaxLength, '-') + "-" + StaticHorizontalSeparatorText.Value; } private static string GetLogStringSummaryRunValues(StopwatchMeasure measure) { (string, string, string, string, string, string, string) formattedRunValues = measure.GetFormattedRunValues(measureNameMaxLength); return "| " + formattedRunValues.Item1 + " | " + formattedRunValues.Item2 + " | " + formattedRunValues.Item3 + " | " + formattedRunValues.Item4 + " | " + formattedRunValues.Item5 + " | " + formattedRunValues.Item6 + " | " + formattedRunValues.Item7 + " |"; } private static string GetStaticHeaderText() { return "| " + "Total".PadSides(MeasureTotalTimingPadding) + " | " + "Runs".PadSides(RunCountPadding) + " | " + "Average".PadSides(MeasureTimingsPadding) + " | " + "Mean Trimmed".PadSides(MeasureTimingsPadding) + " | " + "Min".PadSides(MeasureTimingsPadding) + " | " + "Max".PadSides(MeasureTimingsPadding) + " |"; } private static string GetStaticHorizontalSeparatorText() { return "|-" + "".PadSides(MeasureTotalTimingPadding, '-') + "-|-" + "".PadSides(RunCountPadding, '-') + "-|-" + "".PadSides(MeasureTimingsPadding, '-') + "-|-" + "".PadSides(MeasureTimingsPadding, '-') + "-|-" + "".PadSides(MeasureTimingsPadding, '-') + "-|-" + "".PadSides(MeasureTimingsPadding, '-') + "-|"; } } public static class StringExtensions { public static string PadSides(this string str, int totalWidth, char paddingChar = ' ', bool padLeftOnUneven = true) { int num = totalWidth - str.Length; if (num % 2 == 1) { str = (padLeftOnUneven ? str.PadLeft(str.Length + 1, paddingChar) : str.PadRight(str.Length + 1, paddingChar)); num--; } if (num < 1) { return str; } int totalWidth2 = num / 2 + str.Length; return str.PadLeft(totalWidth2, paddingChar).PadRight(totalWidth, paddingChar); } } public enum PreprocessType { FileLogging, GameNotification } [Flags] public enum LogTier { None = 0, Fatal = 1, Error = 2, Warning = 4, Message = 8, Info = 0x10, Debug = 0x20, All = -1 } [Flags] public enum LogCategories { Null = 0, TempTest = 1, Vanilla = 2, PerfTest = 4, Loading = 8, Task = 0x10, Reflect = 0x20, Config = 0x40, PerfCheck = 0x80, Events = 0x100, Network = 0x200, Cache = 0x400, UI = 0x800, Notifs = 0x100000, AutoPatch = 0x200000, MethodChk = 0x400000, OtherMod = 0x800000, JobSched = 0x1000000, KeyMouse = 0x2000000, Highlight = 0x4000000, AI = 0x8000000, Visuals = 0x10000000, Other = int.MinValue, All = -1 } public abstract class TimeLogger { public delegate string PreprocessMessageFunc(string message, LogTier logLevel, LogCategories category, bool showInGameNotification, PreprocessType preprocType); private static TimeLogger instance; private Dictionary<LogCategories, string> logCategoryStringCache; private static string notificationMsgPrefix; private static Action<string, LogTier> notificationAction; private static PreprocessMessageFunc globalPreprocessMessageFunc; private int maxCategoryLength = Enum.GetNames(typeof(LogCategories)).Aggregate("", (string max, string cur) => (max.Length <= cur.Length) ? cur : max).Length; private LogCategories AllowedCategories; public static TimeLogger Logger { get { if (instance == null) { InitializeTimeLogger<DefaultTimeLogger>(debugEnabled: false, Array.Empty<object>()); instance.LogTimeWarning("TimeLogger has been automatically initialized with a DefaultTimeLogger. If you want to use a different custom logger, call a InitializeTimeLogger...() method earlier.", LogCategories.Loading); } return instance; } } public static bool DebugEnabled { get; set; } protected TimeLogger() { logCategoryStringCache = new Dictionary<LogCategories, string>(); } protected abstract void LogMessage(string logMessage, LogTier logLevel); protected abstract void InitializeLogger(params object[] argsT); public static void InitializeTimeLogger<T>(bool debugEnabled = false, params object[] argsT) where T : TimeLogger { InitializeTimeLogger<T>(null, debugEnabled, argsT); } public static void InitializeTimeLogger<T>(PreprocessMessageFunc preprocessMessageFunc, bool debugEnabled = false, params object[] argsT) where T : TimeLogger { DebugEnabled = debugEnabled; globalPreprocessMessageFunc = preprocessMessageFunc; instance = Activator.CreateInstance<T>(); instance.InitializeLogger(argsT); } public static void InitializeTimeLoggerWithGameNotifications<T>(Action<string, LogTier> notificationAction, string notificationMsgPrefix, bool debugEnabled = false, params object[] argsT) where T : TimeLogger { InitializeTimeLoggerWithGameNotifications<T>(null, notificationAction, notificationMsgPrefix, debugEnabled, argsT); } public static void InitializeTimeLoggerWithGameNotifications<T>(PreprocessMessageFunc preprocessMessageFunc, Action<string, LogTier> notificationAction, string notificationMsgPrefix, bool debugEnabled = false, params object[] argsT) where T : TimeLogger { if (notificationAction == null) { throw new ArgumentNullException("notificationAction", "The argument notificationAction cannot be null. Call InitializeTimeLogger(...) instead."); } InitializeTimeLogger<T>(preprocessMessageFunc, debugEnabled, argsT); AddGameNotificationSupport(notificationAction, notificationMsgPrefix); } public static void AddGameNotificationSupport(Action<string, LogTier> notificationAction, string notificationMsgPrefix) { if (notificationAction == null) { throw new ArgumentNullException("notificationAction"); } TimeLogger.notificationAction = notificationAction; TimeLogger.notificationMsgPrefix = ((notificationMsgPrefix != null) ? notificationMsgPrefix : ""); } public static void RemoveGameNotificationSupport() { notificationAction = null; notificationMsgPrefix = null; } public void LogTimeInfo(string text, LogCategories category) { LogTimeInternal(LogTier.Info, text, category, showInGameNotification: false, null); } public void LogTimeInfoShowInGame(string text, LogCategories category) { LogTimeInternal(LogTier.Info, text, category, showInGameNotification: true, null); } public void LogTimeMessage(string text, LogCategories category) { LogTimeInternal(LogTier.Message, text, category, showInGameNotification: false, null); } public void LogTimeMessageShowInGame(string text, LogCategories category) { LogTimeInternal(LogTier.Message, text, category, showInGameNotification: true, null); } public void LogTimeDebug(string text, LogCategories category) { LogTimeInternal(LogTier.Debug, text, category, showInGameNotification: false, null); } public void LogTimeDebugShowInGame(string text, LogCategories category) { LogTimeInternal(LogTier.Debug, text, category, showInGameNotification: true, null); } public void LogTimeDebug(string text, LogCategories category, bool showInGameNotification) { LogTimeInternal(LogTier.Debug, text, category, showInGameNotification, null); } public void LogTimeDebugFunc(Func<string> textLambda, LogCategories category) { LogTimeInternalFunc(LogTier.Debug, textLambda, category, false); } public void LogTimeDebugFunc(Func<string> textLambda, LogCategories category, params (Action<string> action, bool useFullLog)[] actionsArgs) { LogTimeInternalFunc(LogTier.Debug, textLambda, category, showInGameNotification: false, actionsArgs); } public void LogTimeDebugFuncShowInGame(Func<string> textLambda, LogCategories category) { LogTimeInternalFunc(LogTier.Debug, textLambda, category, true); } public void LogTimeDebugFunc(Func<string> textLambda, LogCategories category, bool showInGameNotification) { LogTimeInternalFunc(LogTier.Debug, textLambda, category, showInGameNotification); } public void LogTimeDebugFunc(Func<string> textLambda, LogCategories category, bool showInGameNotification, params (Action<string> action, bool useFullLog)[] actionsArgs) { LogTimeInternalFunc(LogTier.Debug, textLambda, category, showInGameNotification, actionsArgs); } public void LogTimeWarning(string text, LogCategories category) { LogTimeInternal(LogTier.Warning, text, category, showInGameNotification: false, null); } public void LogTimeWarningShowInGame(string text, LogCategories category) { LogTimeInternal(LogTier.Warning, text, category, showInGameNotification: true, null); } public void LogTimeException(Exception e, LogCategories category) { LogTimeExceptionInternal(null, e, category, showInGame: false); } public void LogTimeExceptionWithMessage(string text, Exception e, LogCategories category) { LogTimeExceptionInternal(text, e, category, showInGame: false); } public void LogTimeExceptionShowInGame(Exception e, LogCategories category) { LogTimeExceptionInternal(null, e, category, showInGame: true); } public void LogTimeExceptionShowInGameWithMessage(string text, Exception e, LogCategories category) { LogTimeExceptionInternal(text, e, category, showInGame: true); } private void LogTimeExceptionInternal(string text, Exception e, LogCategories category, bool showInGame) { PreprocessMessageFunc preprocessMsgFunc = (string msg, LogTier _, LogCategories _, bool _, PreprocessType prepType) => prepType switch { PreprocessType.FileLogging => FormatException(e, msg), PreprocessType.GameNotification => msg ?? e.Message, _ => null, }; LogTimeInternal(LogTier.Fatal, text, category, preprocessMsgFunc, showInGame); } public void LogTimeFatal(string text, LogCategories category) { LogTimeInternal(LogTier.Fatal, text, category, showInGameNotification: false, null); } public void LogTimeFatalShowInGame(string text, LogCategories category) { LogTimeInternal(LogTier.Fatal, text, category, showInGameNotification: true, null); } public void LogTimeError(string text, LogCategories category) { LogTimeInternal(LogTier.Error, text, category, showInGameNotification: false, null); } public void LogTimeErrorShowInGame(string text, LogCategories category) { LogTimeInternal(LogTier.Error, text, category, showInGameNotification: true, null); } public void LogTimeFunc(LogTier logLevel, Func<string> textLambda, LogCategories category, bool showInGameNotification, params (Action<string> action, bool useFullLog)[] actionsArgs) { LogTimeInternalFunc(logLevel, textLambda, category, showInGameNotification, actionsArgs); } private void LogTimeInternalFunc(LogTier logLevel, Func<string> textLambda, LogCategories category, bool showInGameNotification, params (Action<string> action, bool useFullLog)[] actionsArgs) { if (logLevel != LogTier.Debug || DebugEnabled) { LogTimeInternal(logLevel, textLambda(), category, showInGameNotification, actionsArgs); } } public void LogTime(LogTier logLevel, string text, LogCategories category) { LogTimeInternal(logLevel, text, category, showInGameNotification: false, null); } public void LogTime(LogTier logLevel, string text, LogCategories category, bool showInGameNotification) { LogTimeInternal(logLevel, text, category, showInGameNotification, null); } public void LogTime(LogTier logLevel, string text, LogCategories category, PreprocessMessageFunc preprocessMsgFunc, bool showInGameNotification, params (Action<string> action, bool useFullLog)[] actionsArgs) { LogTimeInternal(logLevel, text, category, preprocessMsgFunc, showInGameNotification, null); } private void LogTimeInternal(LogTier logLevel, string originalText, LogCategories category, bool showInGameNotification, params (Action<string> action, bool useFullLog)[] actionsArgs) { LogTimeInternal(logLevel, originalText, category, globalPreprocessMessageFunc, showInGameNotification, actionsArgs); } private void LogTimeInternal(LogTier logLevel, string originalText, LogCategories category, PreprocessMessageFunc preprocessMsgFunc, bool showInGameNotification, params (Action<string> action, bool useFullLog)[] actionsArgs) { if ((logLevel == LogTier.Debug && !DebugEnabled) || (AllowedCategories != 0 && !AllowedCategories.HasFlag(category))) { return; } string text = PreprocessMessage(originalText, logLevel, category, showInGameNotification, preprocessMsgFunc, PreprocessType.FileLogging); string text2 = FormatLogMessageWithTime(text, category); LogMessage(text2, logLevel); if (showInGameNotification) { string message = PreprocessMessage(originalText, logLevel, category, showInGameNotification: true, preprocessMsgFunc, PreprocessType.GameNotification); SendMessageNotification(logLevel, message); } if (actionsArgs != null) { for (int i = 0; i < actionsArgs.Length; i++) { (Action<string>, bool) tuple = actionsArgs[i]; tuple.Item1(tuple.Item2 ? text2 : text); } } } private string PreprocessMessage(string text, LogTier logLevel, LogCategories category, bool showInGameNotification, PreprocessMessageFunc preprocessMsgFunc, PreprocessType prepType) { if (preprocessMsgFunc != null) { return preprocessMsgFunc(text, logLevel, category, showInGameNotification, prepType) ?? text; } return text; } public string GetCategoryStringFromCache(LogCategories category) { if (!logCategoryStringCache.TryGetValue(category, out var value)) { value = category.ToString(); logCategoryStringCache.Add(category, value); } return value; } private string FormatLogMessageWithTime(string text, LogCategories category) { StringBuilder stringBuilder = new StringBuilder(100); string text2 = ((category == LogCategories.Null) ? "" : GetCategoryStringFromCache(category)); stringBuilder.Append("["); stringBuilder.Append(text2.PadRight(maxCategoryLength, ' ')); stringBuilder.Append("] "); stringBuilder.Append(DateTime.Now.ToString("HH:mm:ss.fff")); stringBuilder.Append(" - "); stringBuilder.Append(text); return stringBuilder.ToString(); } public static string FormatException(Exception e, string text = null) { string text2 = ((text != null) ? (text + "\n" + e.Message) : e.Message); text2 += $"(In {e.TargetSite})\n{e.StackTrace}"; if (e.InnerException != null) { text2 = text2 + "\n * InnerException: " + e.InnerException.Message + " " + $"(In {e.InnerException.TargetSite})\n{e.InnerException.StackTrace}"; } return text2; } public void SendMessageNotificationError(string message) { SendMessageNotification(LogTier.Error, message); } public void SendMessageNotification(LogTier logLevel, string message) { if ((logLevel != LogTier.Debug || DebugEnabled) && notificationAction != null) { notificationAction(message, logLevel); } } } } namespace Damntry.Utils.ExtensionMethods { public static class EnumExtension { private static class EnumLambdaCompiled<TEnum> { public static Func<TEnum, long> EnumToLongFunc = GenerateEnumToLongFunc(); public static Func<long, TEnum> LongToEnumFunc = GenerateLongToEnumFunc(); private static Func<TEnum, long> GenerateEnumToLongFunc() { ParameterExpression parameterExpression = Expression.Parameter(typeof(TEnum)); return Expression.Lambda<Func<TEnum, long>>(Expression.Convert(parameterExpression, typeof(long)), new ParameterExpression[1] { parameterExpression }).Compile(); } private static Func<long, TEnum> GenerateLongToEnumFunc() { ParameterExpression parameterExpression = Expression.Parameter(typeof(long)); return Expression.Lambda<Func<long, TEnum>>(Expression.Convert(parameterExpression, typeof(TEnum)), new ParameterExpression[1] { parameterExpression }).Compile(); } } public static string GetDescription(this Enum enumValue) { FieldInfo field = enumValue.GetType().GetField(enumValue.ToString()); if (field == null) { return enumValue.ToString(); } field.GetCustomAttributes(typeof(DescriptionAttribute), inherit: false); if (Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute)) is DescriptionAttribute descriptionAttribute) { return descriptionAttribute.Description; } return enumValue.ToString(); } public static long ToLong<TEnum>(this TEnum enumValue) where TEnum : struct, Enum { return EnumLambdaCompiled<TEnum>.EnumToLongFunc(enumValue); } public static long EnumToLong<T>(T enumValue) { if (typeof(T).IsEnum) { return EnumLambdaCompiled<T>.EnumToLongFunc(enumValue); } throw new Exception("The type is not an enum."); } public static TEnum LongToEnum<TEnum>(long value) where TEnum : Enum { return EnumLambdaCompiled<TEnum>.LongToEnumFunc(value); } public static T LongToEnumUnconstrained<T>(long value) { if (typeof(T).IsEnum) { return EnumLambdaCompiled<T>.LongToEnumFunc(value); } throw new Exception("The type is not an enum."); } } public static class NumberExtensionMethods { public const double EpsilonTolerance = 1E-09; private static readonly HashSet<Type> NumericTypes = new HashSet<Type> { typeof(int), typeof(double), typeof(decimal), typeof(long), typeof(short), typeof(sbyte), typeof(byte), typeof(ulong), typeof(ushort), typeof(uint), typeof(float) }; public static bool IsNumeric(this Type myType) { return NumericTypes.Contains(Nullable.GetUnderlyingType(myType) ?? myType); } public static bool IsInteger(this float num) { return (double)Math.Abs(num - (float)(int)num) < 1E-09; } public static bool IsInteger(this double num) { return Math.Abs(num - (double)(long)num) < 1E-09; } public static bool IsInteger(this float num, float epsilonTolerance) { return Math.Abs(num - (float)(int)num) < epsilonTolerance; } public static bool IsInteger(this double num, float epsilonTolerance) { return Math.Abs(num - (double)(long)num) < (double)epsilonTolerance; } public static bool Clamp(this ref float value, float min, float max) { return ClampIComparable(ref value, min, max); } public static bool Clamp(this ref double value, double min, double max) { return ClampIComparable(ref value, min, max); } public static bool Clamp(this ref int value, int min, int max) { return ClampIComparable(ref value, min, max); } public static bool Clamp(this ref byte value, byte min, byte max) { return ClampIComparable(ref value, min, max); } public static float ClampReturn(this float value, float min, float max) { return ClampIComparable(value, min, max); } public static double ClampReturn(this double value, double min, double max) { return ClampIComparable(value, min, max); } public static int ClampReturn(this int value, int min, int max) { return ClampIComparable(value, min, max); } public static byte ClampReturn(this byte value, byte min, byte max) { return ClampIComparable(value, min, max); } private static T ClampIComparable<T>(T value, T min, T max) where T : IComparable<T> { if (min.CompareTo(min) > 0) { throw new ArgumentException("Clamp: The max value is lower than the min value."); } if (value.CompareTo(min) < 0) { return min; } if (value.CompareTo(max) > 0) { return max; } return value; } private static bool ClampIComparable<T>(ref T value, T min, T max) where T : IComparable<T> { if (min.CompareTo(min) > 0) { throw new ArgumentException("Clamp: The max value is lower than the min value."); } if (value.CompareTo(min) < 0) { value = min; return false; } if (value.CompareTo(max) > 0) { value = max; return false; } return true; } } public static class ReflectionExtension { public static bool IsSubclassOfRawGeneric(this Type toCheck, Type baseType) { while (toCheck != typeof(object)) { Type type = (toCheck.IsGenericType ? toCheck.GetGenericTypeDefinition() : toCheck); if (baseType == type) { return true; } toCheck = toCheck.BaseType; } return false; } public static bool HasCustomAttribute<T>(this MemberInfo memberInfo) where T : Attribute { return (T)Attribute.GetCustomAttribute(memberInfo, typeof(T)) != null; } public static bool IsStatic(this PropertyInfo source, bool nonPublic = true) { return source.GetAccessors(nonPublic).Any((MethodInfo x) => x.IsStatic); } } public static class TaskExtensionMethods { public static bool IsTaskEnded(this Task task) { if (!task.IsCompleted) { return task.IsFaulted; } return true; } public static async void FireAndForgetCancels(this Task task, LogCategories category, bool dismissCancelLog = false) { try { if (!task.IsCompleted || task.IsFaulted) { await task.ConfigureAwait(continueOnCapturedContext: false); } } catch (Exception ex) { if (ex is TaskCanceledException || ex is OperationCanceledException || ex is ThreadAbortException) { if (!dismissCancelLog) { TimeLogger.Logger.LogTimeDebug("\"Fire and Forget\" task successfully canceled.", category); } } else { TimeLogger.Logger.LogTimeExceptionWithMessage("Error while awaiting \"Fire and Forget\" type of task:", ex, category); } } } public static async void FireAndForget(this Task task, LogCategories category) { try { if (!task.IsCompleted || task.IsFaulted) { await task.ConfigureAwait(continueOnCapturedContext: false); } } catch (Exception e) { TimeLogger.Logger.LogTimeExceptionWithMessage("Error while awaiting \"Fire and Forget\" type of task:", e, category); } } } } namespace Damntry.Utils.Events { public static class EventMethods { public static bool TryTriggerEvents(Action ev) { bool result = true; if (ev != null) { Delegate[] invocationList = ev.GetInvocationList(); for (int i = 0; i < invocationList.Length; i++) { Action action = (Action)invocationList[i]; try { action(); } catch (Exception e) { result = false; TimeLogger.Logger.LogTimeExceptionWithMessage("Exception while invoking method \"" + action.Method.Name + "\" " + $"from event {ev.GetType()}", e, LogCategories.Events); } } } return result; } public static bool TryTriggerEvents<T>(Action<T> ev, T arg1) { bool result = true; if (ev != null) { Delegate[] invocationList = ev.GetInvocationList(); for (int i = 0; i < invocationList.Length; i++) { Action<T> action = (Action<T>)invocationList[i]; try { action(arg1); } catch (Exception e) { result = false; TimeLogger.Logger.LogTimeExceptionWithMessage("Exception while invoking method \"" + action.Method.Name + "\" " + $"from event {ev.GetType()}", e, LogCategories.Events); } } } return result; } public static bool TryTriggerEvents<T1, T2>(Action<T1, T2> ev, T1 arg1, T2 arg2) { bool result = true; if (ev != null) { Delegate[] invocationList = ev.GetInvocationList(); for (int i = 0; i < invocationList.Length; i++) { Action<T1, T2> action = (Action<T1, T2>)invocationList[i]; try { action(arg1, arg2); } catch (Exception e) { result = false; TimeLogger.Logger.LogTimeExceptionWithMessage("Exception while invoking method \"" + action.Method.Name + "\" " + $"from event {ev.GetType()}", e, LogCategories.Events); } } } return result; } public static bool TryTriggerEvents<T1, T2, T3>(Action<T1, T2> ev, T1 arg1, T2 arg2, T3 arg3) { bool result = true; if (ev != null) { Delegate[] invocationList = ev.GetInvocationList(); for (int i = 0; i < invocationList.Length; i++) { Action<T1, T2, T3> action = (Action<T1, T2, T3>)invocationList[i]; try { action(arg1, arg2, arg3); } catch (Exception e) { result = false; TimeLogger.Logger.LogTimeExceptionWithMessage("Exception while invoking method \"" + action.Method.Name + "\" " + $"from event {ev.GetType()}", e, LogCategories.Events); } } } return result; } public static bool TryTriggerEvents<T1, T2, T3, T4>(Action<T1, T2> ev, T1 arg1, T2 arg2, T3 arg3, T4 arg4) { bool result = true; if (ev != null) { Delegate[] invocationList = ev.GetInvocationList(); for (int i = 0; i < invocationList.Length; i++) { Action<T1, T2, T3, T4> action = (Action<T1, T2, T3, T4>)invocationList[i]; try { action(arg1, arg2, arg3, arg4); } catch (Exception e) { result = false; TimeLogger.Logger.LogTimeExceptionWithMessage("Exception while invoking method \"" + action.Method.Name + "\" " + $"from event {ev.GetType()}", e, LogCategories.Events); } } } return result; } public static bool TryTriggerEvents<T1, T2, T3, T4, T5>(Action<T1, T2> ev, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5) { bool result = true; if (ev != null) { Delegate[] invocationList = ev.GetInvocationList(); for (int i = 0; i < invocationList.Length; i++) { Action<T1, T2, T3, T4, T5> action = (Action<T1, T2, T3, T4, T5>)invocationList[i]; try { action(arg1, arg2, arg3, arg4, arg5); } catch (Exception e) { result = false; TimeLogger.Logger.LogTimeExceptionWithMessage("Exception while invoking method \"" + action.Method.Name + "\" " + $"from event {ev.GetType()}", e, LogCategories.Events); } } } return result; } public static bool TryTriggerEvents<T1, T2, T3, T4, T5, T6>(Action<T1, T2> ev, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6) { bool result = true; if (ev != null) { Delegate[] invocationList = ev.GetInvocationList(); for (int i = 0; i < invocationList.Length; i++) { Action<T1, T2, T3, T4, T5, T6> action = (Action<T1, T2, T3, T4, T5, T6>)invocationList[i]; try { action(arg1, arg2, arg3, arg4, arg5, arg6); } catch (Exception e) { result = false; TimeLogger.Logger.LogTimeExceptionWithMessage("Exception while invoking method \"" + action.Method.Name + "\" " + $"from event {ev.GetType()}", e, LogCategories.Events); } } } return result; } [Obsolete("Barely faster than TryTriggerEventsDelegate. Use TryTriggerEvents instead.")] private static bool TryTriggerEventsLambda<T>(Action<T> ev, T arg1) { return TryTriggerEventsLambda(ev, delegate(Delegate del) { ((Action<T>)del)(arg1); }); } private static bool TryTriggerEventsLambda(Delegate ev, Action<Delegate> action) { bool result = true; if ((object)ev != null) { Delegate[] invocationList = ev.GetInvocationList(); foreach (Delegate @delegate in invocationList) { try { action(@delegate); } catch (Exception e) { result = false; TimeLogger.Logger.LogTimeExceptionWithMessage("Exception while invoking method \"" + @delegate.Method.Name + "\" " + $"from event {ev.GetType()}", e, LogCategories.Events); } } } return result; } [Obsolete("Too slow.")] private static bool TryTriggerEventsDelegate(Delegate ev, params object[] args) { bool result = true; if ((object)ev != null) { Delegate[] invocationList = ev.GetInvocationList(); foreach (Delegate @delegate in invocationList) { try { @delegate?.Method.Invoke(@delegate.Target, args); } catch (Exception e) { result = false; TimeLogger.Logger.LogTimeExceptionWithMessage("Exception while invoking method \"" + @delegate.Method.Name + "\" " + $"from event {ev.GetType()}", e, LogCategories.Events); } } } return result; } } } namespace Damntry.Utils.Collections { public class TreeNode<T> { private readonly T _value; private readonly List<TreeNode<T>> _children = new List<TreeNode<T>>(); public TreeNode<T> this[int i] => _children[i]; public TreeNode<T> Parent { get; private set; } public T Value => _value; public ReadOnlyCollection<TreeNode<T>> Children => _children.AsReadOnly(); public TreeNode(T value) { _value = value; } private TreeNode(T value, TreeNode<T> parent) { _value = value; Parent = parent; } public TreeNode<T> AddChild(T value) { TreeNode<T> treeNode = new TreeNode<T>(value, this); _children.Add(treeNode); return treeNode; } public TreeNode<T>[] AddChildren(params T[] values) { return values.Select(AddChild).ToArray(); } public bool RemoveChild(TreeNode<T> node) { return _children.Remove(node); } public void TransverseAndDo(Action<T> action) { action(Value); foreach (TreeNode<T> child in _children) { child.TransverseAndDo(action); } } public IEnumerable<T> Flatten() { return new T[1] { Value }.Concat(_children.SelectMany((TreeNode<T> x) => x.Flatten())); } } } namespace Damntry.Utils.Collections.Queues { public class CommonConcurrentQueue<T> : ConcurrentQueue<T>, ICommonQueue<T> { public bool TryEnqueue(T item) { Enqueue(item); return true; } public new bool TryDequeue(out T item) { return base.TryDequeue(out item); } } public class CommonQueue<T> : Queue<T>, ICommonQueue<T> { public bool TryEnqueue(T item) { Enqueue(item); return true; } public new bool TryDequeue(out T item) { item = Dequeue(); return true; } } public class ConcurrentFixedCapacityQueue<T> : ConcurrentQueue<T>, ICommonQueue<T> { private readonly int maxCapacity; private readonly bool keepOld; private readonly object queueLock; public ConcurrentFixedCapacityQueue(int maxCapacity, bool keepOld) { if (maxCapacity <= 0) { throw new ArgumentOutOfRangeException("Only values over zero are allowed."); } this.maxCapacity = maxCapacity; this.keepOld = keepOld; queueLock = new object(); } [Obsolete("Use \"Enqueue(T item, out T dequeuedItem)\" instead.")] public new T Enqueue(T item) { throw new NotImplementedException("Use \"Enqueue(T item, out T dequeuedItem)\" instead."); } public bool TryEnqueue(T item) { T dequeuedItem; return TryEnqueue(item, out dequeuedItem); } public bool TryEnqueue(T item, out T dequeuedItem) { dequeuedItem = default(T); lock (queueLock) { if (base.Count > maxCapacity) { throw new InvalidOperationException("This LimitedQueue contains more items than allowed. Avoid casting this FixedCapacityQueue instance into a Queue to call the base Enqueue(T) method."); } if (!keepOld && base.Count == maxCapacity) { base.TryDequeue(out dequeuedItem); } if (base.Count >= maxCapacity) { return false; } base.Enqueue(item); } return true; } public new bool TryDequeue(out T item) { lock (queueLock) { return base.TryDequeue(out item); } } } public class FixedCapacityQueue<T> : Queue<T>, ICommonQueue<T> { private readonly int maxCapacity; private readonly bool keepOld; public FixedCapacityQueue(int maxCapacity, bool keepOld) : base(maxCapacity) { if (maxCapacity <= 0) { throw new ArgumentOutOfRangeException("Only values over zero are allowed."); } this.maxCapacity = maxCapacity; this.keepOld = keepOld; } [Obsolete("Use \"Enqueue(T item, out T dequeuedItem)\" instead.")] public new T Enqueue(T item) { throw new NotImplementedException("Use \"Enqueue(T item, out T dequeuedItem)\" instead."); } public bool TryEnqueue(T item) { T dequeuedItem; return TryEnqueue(item, out dequeuedItem); } public bool TryEnqueue(T item, out T dequeuedItem) { dequeuedItem = default(T); if (base.Count > maxCapacity) { throw new InvalidOperationException("This LimitedQueue contains more items than allowed. Avoid casting this FixedCapacityQueue instance into a Queue to call the base Enqueue(T) method."); } if (!keepOld && base.Count == maxCapacity) { dequeuedItem = Dequeue(); } if (base.Count >= maxCapacity) { return false; } base.Enqueue(item); return true; } public new bool TryDequeue(out T item) { item = Dequeue(); return true; } } public class FixedCapacityUniqueQueue<T> : FixedCapacityQueue<T> { public FixedCapacityUniqueQueue(int maxCapacity, bool keepOld) : base(maxCapacity, keepOld) { } [Obsolete("Use \"Enqueue(T item, out T dequeuedItem)\" instead.")] public new T Enqueue(T item) { throw new NotImplementedException("Use \"Enqueue(T item, out T dequeuedItem)\" instead."); } public new bool TryEnqueue(T item, out T dequeuedItem) { dequeuedItem = default(T); if (base.Count > 0 && Contains(item)) { return false; } return base.TryEnqueue(item, out dequeuedItem); } } } namespace Damntry.Utils.Collections.Queues.Interfaces { public interface ICommonQueue<T> { int Count { get; } bool TryEnqueue(T item); bool TryDequeue(out T item); } }
BepInEx/plugins/es.damntry.SuperQoLity/Damntry.Globals.Unity.dll
Decompiled a week agousing System; 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 System.Threading; using System.Threading.Tasks; using Cysharp.Threading.Tasks; using Damntry.Utils.ExtensionMethods; using Damntry.Utils.Logging; using Damntry.Utils.Reflection; using Damntry.Utils.Tasks.AsyncDelay; using Damntry.Utils.Timers.StopwatchImpl; using Damntry.UtilsUnity.UI.Extensions; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("Damntry Globals unity")] [assembly: AssemblyDescription("Utils for projects using Unity")] [assembly: AssemblyCompany("Damntry")] [assembly: AssemblyProduct("Damntry Globals unity")] [assembly: AssemblyCopyright("Copyright © Damntry 2025")] [assembly: AssemblyFileVersion("0.1.3")] [assembly: TargetFramework(".NETFramework,Version=v4.8.1", FrameworkDisplayName = ".NET Framework 4.8.1")] [assembly: AssemblyVersion("0.1.3.0")] [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; } } } public class GpuInstancingFrustumCulling : MonoBehaviour { [SerializeField] private Camera _camera; [SerializeField] private MeshRenderer[] _meshRenderers = (MeshRenderer[])(object)new MeshRenderer[0]; public bool meshesAreStill = true; private Dictionary<(Mesh mesh, Material material), (List<Transform> transforms, Bounds aabb)> _sources = new Dictionary<(Mesh, Material), (List<Transform>, Bounds)>(); private Dictionary<(Mesh mesh, Material material), (Matrix4x4[] matrices, Bounds aabb)> _batches = new Dictionary<(Mesh, Material), (Matrix4x4[], Bounds)>(); private Dictionary<int, Stack<Matrix4x4[]>> _freeMatrices = new Dictionary<int, Stack<Matrix4x4[]>>(); private Plane[] _frustum = (Plane[])(object)new Plane[6]; private void Start() { Initialize(); UpdateMatrices(); if ((Object)(object)_camera == (Object)null) { _camera = Camera.main; } if ((Object)(object)_camera == (Object)null) { Debug.LogError((object)"no camera, can't continue", (Object)(object)this); ((Behaviour)this).enabled = false; } } private void Update() { //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) //IL_0053: Unknown result type (might be due to invalid IL or missing references) if (!meshesAreStill) { UpdateMatrices(); } GeometryUtility.CalculateFrustumPlanes(_camera, _frustum); foreach (KeyValuePair<(Mesh, Material), (Matrix4x4[], Bounds)> batch in _batches) { (Mesh, Material) key = batch.Key; (Matrix4x4[], Bounds) value = batch.Value; Bounds item = value.Item2; if (GeometryUtility.TestPlanesAABB(_frustum, item)) { Graphics.DrawMeshInstanced(key.Item1, 0, key.Item2, value.Item1); } } } private void Initialize() { //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_0137: Unknown result type (might be due to invalid IL or missing references) //IL_00f6: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) _sources.Clear(); MeshRenderer[] meshRenderers = _meshRenderers; foreach (MeshRenderer val in meshRenderers) { if ((Object)(object)val == (Object)null) { continue; } MeshFilter component = ((Component)val).GetComponent<MeshFilter>(); if ((Object)(object)component == (Object)null) { continue; } Mesh sharedMesh = component.sharedMesh; if ((Object)(object)sharedMesh == (Object)null) { continue; } Material[] sharedMaterials = ((Renderer)val).sharedMaterials; foreach (Material val2 in sharedMaterials) { if (!val2.enableInstancing && Application.isPlaying) { Debug.LogWarning((object)("\"" + ((Object)val2).name + "\" material won't be rendered as it's <b>GPU Instancing</b> is not enabled"), (Object)(object)val); } else if (!((Object)(object)val2 == (Object)null)) { Bounds bounds = ((Renderer)val).bounds; (Mesh, Material) key = (sharedMesh, val2); if (_sources.ContainsKey(key)) { List<Transform> item = _sources[key].transforms; item.Add(((Component)val).transform); Bounds item2 = _sources[key].aabb; ((Bounds)(ref item2)).Encapsulate(bounds); _sources[key] = (item, item2); } else { _sources.Add(key, (new List<Transform> { ((Component)val).transform }, bounds)); } } } if (Application.isPlaying) { ((Renderer)val).enabled = false; } } } private void UpdateMatrices() { //IL_011f: Unknown result type (might be due to invalid IL or missing references) //IL_0124: Unknown result type (might be due to invalid IL or missing references) //IL_0141: Unknown result type (might be due to invalid IL or missing references) foreach (KeyValuePair<(Mesh, Material), (Matrix4x4[], Bounds)> batch in _batches) { Matrix4x4[] item = batch.Value.Item1; if (_freeMatrices.ContainsKey(item.Length)) { _freeMatrices[item.Length].Push(item); continue; } Stack<Matrix4x4[]> stack = new Stack<Matrix4x4[]>(); stack.Push(item); _freeMatrices.Add(item.Length, stack); } _batches.Clear(); foreach (KeyValuePair<(Mesh, Material), (List<Transform>, Bounds)> source in _sources) { (Mesh, Material) key = source.Key; (List<Transform>, Bounds) value = source.Value; List<Transform> item2 = value.Item1; int count = item2.Count; Matrix4x4[] array = null; array = (Matrix4x4[])((!_freeMatrices.ContainsKey(count) || _freeMatrices[count].Count == 0) ? ((Array)new Matrix4x4[count]) : ((Array)_freeMatrices[count].Pop())); for (int i = 0; i < count; i++) { array[i] = item2[i].localToWorldMatrix; } _batches.Add(key, (array, value.Item2)); } } } namespace Damntry.UtilsUnity.UI { public static class CanvasMethods { public static void AttachPanelToCanvas(GameObject mainPanel, Transform canvasTransformParent) { CheckAttachArgs(mainPanel, canvasTransformParent); mainPanel.transform.SetParent(canvasTransformParent); } public static void AttachPanelToCanvasWithAnchor(GameObject mainPanel, Transform canvasTransformParent) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) CheckAttachArgs(mainPanel, canvasTransformParent); mainPanel.transform.SetParent(canvasTransformParent); RectTransform component = mainPanel.GetComponent<RectTransform>(); component.SetAnchor(AnchorPresets.TopCenter); component.sizeDelta = Vector2.zero; component.offsetMin = Vector2.zero; component.offsetMax = Vector2.zero; component.anchoredPosition = Vector2.zero; } private static void CheckAttachArgs(GameObject mainPanel, Transform canvasTransformParent) { if ((Object)(object)mainPanel == (Object)null) { throw new ArgumentNullException("mainPanel"); } if ((Object)(object)canvasTransformParent == (Object)null) { throw new ArgumentNullException("canvasTransformParent"); } } } } namespace Damntry.UtilsUnity.UI.Extensions { public enum AnchorPresets { TopLeft, TopCenter, TopRight, MiddleLeft, MiddleCenter, MiddleRight, BottomLeft, BottonCenter, BottomRight, BottomStretch, VertStretchLeft, VertStretchRight, VertStretchCenter, HorStretchTop, HorStretchMiddle, HorStretchBottom, StretchAll } public enum PivotPresets { TopLeft, TopCenter, TopRight, MiddleLeft, MiddleCenter, MiddleRight, BottomLeft, BottomCenter, BottomRight } public static class RectTransformExtensions { public static void SetAnchor(this RectTransform source, AnchorPresets alignment, int offsetX = 0, int offsetY = 0) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0084: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00af: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Unknown result type (might be due to invalid IL or missing references) //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_011b: Unknown result type (might be due to invalid IL or missing references) //IL_0130: Unknown result type (might be due to invalid IL or missing references) //IL_0146: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Unknown result type (might be due to invalid IL or missing references) //IL_0186: Unknown result type (might be due to invalid IL or missing references) //IL_019c: Unknown result type (might be due to invalid IL or missing references) //IL_01b1: Unknown result type (might be due to invalid IL or missing references) //IL_01c7: Unknown result type (might be due to invalid IL or missing references) //IL_01dc: Unknown result type (might be due to invalid IL or missing references) //IL_0273: Unknown result type (might be due to invalid IL or missing references) //IL_0288: Unknown result type (might be due to invalid IL or missing references) //IL_02c9: Unknown result type (might be due to invalid IL or missing references) //IL_02de: Unknown result type (might be due to invalid IL or missing references) //IL_029e: Unknown result type (might be due to invalid IL or missing references) //IL_02b3: Unknown result type (might be due to invalid IL or missing references) //IL_01f2: Unknown result type (might be due to invalid IL or missing references) //IL_0207: Unknown result type (might be due to invalid IL or missing references) //IL_021d: Unknown result type (might be due to invalid IL or missing references) //IL_0232: Unknown result type (might be due to invalid IL or missing references) //IL_0248: Unknown result type (might be due to invalid IL or missing references) //IL_025d: Unknown result type (might be due to invalid IL or missing references) //IL_02f4: Unknown result type (might be due to invalid IL or missing references) //IL_0309: Unknown result type (might be due to invalid IL or missing references) source.anchoredPosition = Vector2.op_Implicit(new Vector3((float)offsetX, (float)offsetY, 0f)); switch (alignment) { case AnchorPresets.TopLeft: source.anchorMin = new Vector2(0f, 1f); source.anchorMax = new Vector2(0f, 1f); break; case AnchorPresets.TopCenter: source.anchorMin = new Vector2(0.5f, 1f); source.anchorMax = new Vector2(0.5f, 1f); break; case AnchorPresets.TopRight: source.anchorMin = new Vector2(1f, 1f); source.anchorMax = new Vector2(1f, 1f); break; case AnchorPresets.MiddleLeft: source.anchorMin = new Vector2(0f, 0.5f); source.anchorMax = new Vector2(0f, 0.5f); break; case AnchorPresets.MiddleCenter: source.anchorMin = new Vector2(0.5f, 0.5f); source.anchorMax = new Vector2(0.5f, 0.5f); break; case AnchorPresets.MiddleRight: source.anchorMin = new Vector2(1f, 0.5f); source.anchorMax = new Vector2(1f, 0.5f); break; case AnchorPresets.BottomLeft: source.anchorMin = new Vector2(0f, 0f); source.anchorMax = new Vector2(0f, 0f); break; case AnchorPresets.BottonCenter: source.anchorMin = new Vector2(0.5f, 0f); source.anchorMax = new Vector2(0.5f, 0f); break; case AnchorPresets.BottomRight: source.anchorMin = new Vector2(1f, 0f); source.anchorMax = new Vector2(1f, 0f); break; case AnchorPresets.HorStretchTop: source.anchorMin = new Vector2(0f, 1f); source.anchorMax = new Vector2(1f, 1f); break; case AnchorPresets.HorStretchMiddle: source.anchorMin = new Vector2(0f, 0.5f); source.anchorMax = new Vector2(1f, 0.5f); break; case AnchorPresets.HorStretchBottom: source.anchorMin = new Vector2(0f, 0f); source.anchorMax = new Vector2(1f, 0f); break; case AnchorPresets.VertStretchLeft: source.anchorMin = new Vector2(0f, 0f); source.anchorMax = new Vector2(0f, 1f); break; case AnchorPresets.VertStretchCenter: source.anchorMin = new Vector2(0.5f, 0f); source.anchorMax = new Vector2(0.5f, 1f); break; case AnchorPresets.VertStretchRight: source.anchorMin = new Vector2(1f, 0f); source.anchorMax = new Vector2(1f, 1f); break; case AnchorPresets.StretchAll: source.anchorMin = new Vector2(0f, 0f); source.anchorMax = new Vector2(1f, 1f); break; case AnchorPresets.BottomStretch: break; } } public static void SetPivot(this RectTransform source, PivotPresets pivotPreset) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_00a4: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00d0: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) switch (pivotPreset) { case PivotPresets.TopLeft: source.pivot = new Vector2(0f, 1f); break; case PivotPresets.TopCenter: source.pivot = new Vector2(0.5f, 1f); break; case PivotPresets.TopRight: source.pivot = new Vector2(1f, 1f); break; case PivotPresets.MiddleLeft: source.pivot = new Vector2(0f, 0.5f); break; case PivotPresets.MiddleCenter: source.pivot = new Vector2(0.5f, 0.5f); break; case PivotPresets.MiddleRight: source.pivot = new Vector2(1f, 0.5f); break; case PivotPresets.BottomLeft: source.pivot = new Vector2(0f, 0f); break; case PivotPresets.BottomCenter: source.pivot = new Vector2(0.5f, 0f); break; case PivotPresets.BottomRight: source.pivot = new Vector2(1f, 0f); break; } } public static void SetAnchorAndPivot(this RectTransform source, AnchorPresets alignment, PivotPresets pivotPreset, int offsetX = 0, int offsetY = 0) { source.SetAnchor(alignment, offsetX, offsetY); source.SetPivot(pivotPreset); } } } namespace Damntry.UtilsUnity.Timers { public class CachedTimeValues { private static UnityTimeStopwatch unitySW; private static float fixedDeltaTime; private static int fixedUpdateCycleMax; public static (float fixedDeltaTime, int fixedUpdateCycleMax) GetFixedTimeCachedValues() { if (unitySW == null || unitySW.ElapsedSecondsPrecise > 1.0) { if (unitySW == null) { unitySW = UnityTimeStopwatch.StartNew(UnityTimeStopwatch.TimeType.FixedTime); } fixedDeltaTime = Time.fixedDeltaTime; fixedUpdateCycleMax = (int)Math.Round(1f / fixedDeltaTime); } return (fixedDeltaTime, fixedUpdateCycleMax); } } public class UnityTimeStopwatch : IStopwatch { public enum TimeType { Time, FixedTime, UnscaledTime, UnscaledFixedTime } private double elapsed; private double startTimeStamp; private bool isRunning; private TimeType timeType; public bool IsRunning => isRunning; public long ElapsedMilliseconds => (long)GetElapsedSeconds(highPrecision: false) * 1000; public long ElapsedSeconds => (long)GetElapsedSeconds(highPrecision: false); public double ElapsedMillisecondsPrecise => GetElapsedSeconds(highPrecision: true) * 1000.0; public double ElapsedSecondsPrecise => GetElapsedSeconds(highPrecision: true); static UnityTimeStopwatch() { } public UnityTimeStopwatch() { timeType = TimeType.Time; Reset(); } public UnityTimeStopwatch(TimeType timeType) { this.timeType = timeType; Reset(); } public void Start() { if (!isRunning) { startTimeStamp = GetTimestampDouble(); isRunning = true; } } public static UnityTimeStopwatch StartNew() { UnityTimeStopwatch unityTimeStopwatch = new UnityTimeStopwatch(); unityTimeStopwatch.Start(); return unityTimeStopwatch; } public static UnityTimeStopwatch StartNew(TimeType timeType) { UnityTimeStopwatch unityTimeStopwatch = new UnityTimeStopwatch(timeType); unityTimeStopwatch.Start(); return unityTimeStopwatch; } public void Stop() { if (isRunning) { double num = GetTimestampDouble() - startTimeStamp; elapsed += num; isRunning = false; if (elapsed < 0.0) { elapsed = 0.0; } } } public void Reset() { elapsed = 0.0; isRunning = false; startTimeStamp = 0.0; } public void Restart() { elapsed = 0.0; startTimeStamp = GetTimestampDouble(); isRunning = true; } private float GetTimestamp() { return timeType switch { TimeType.Time => Time.time, TimeType.FixedTime => Time.fixedTime, TimeType.UnscaledTime => Time.unscaledTime, TimeType.UnscaledFixedTime => Time.fixedUnscaledTime, _ => throw new NotImplementedException(), }; } private double GetTimestampDouble() { return timeType switch { TimeType.Time => Time.timeAsDouble, TimeType.FixedTime => Time.fixedTimeAsDouble, TimeType.UnscaledTime => Time.unscaledTimeAsDouble, TimeType.UnscaledFixedTime => Time.fixedUnscaledTimeAsDouble, _ => throw new NotImplementedException(), }; } private double GetElapsedSeconds(bool highPrecision) { double num = elapsed; if (isRunning) { num += (highPrecision ? GetTimestampDouble() : ((double)GetTimestamp())) - startTimeStamp; } return num; } } } namespace Damntry.UtilsUnity.Tasks.AsyncDelay { public class UniTaskDelay : AsyncDelayBase<UniTaskDelay> { public override Task Delay(int millisecondsDelay) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) return UniTaskExtensions.AsTask(UniTask.Delay(AdaptToUnitask(millisecondsDelay), false, (PlayerLoopTiming)8, default(CancellationToken), false)); } public override Task Delay(int millisecondsDelay, CancellationToken cancellationToken) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) return UniTaskExtensions.AsTask(UniTask.Delay(AdaptToUnitask(millisecondsDelay), false, (PlayerLoopTiming)8, cancellationToken, false)); } private int AdaptToUnitask(int millisecondsDelay) { if (millisecondsDelay < 0) { return int.MaxValue; } return millisecondsDelay; } } } namespace Damntry.UtilsUnity.Resources { public class AssetBundleElement { private static Dictionary<string, AssetBundle> bundleCacheStorage = new Dictionary<string, AssetBundle>(); private readonly string assetBundlePath; private AssetBundle loadedBundle; public AssetBundleElement(Type assemblyType, string assetName) { if (assemblyType == null) { throw new ArgumentNullException("assemblyType"); } if (string.IsNullOrEmpty(assetName)) { throw new ArgumentNullException("The parameter assetName cant be null or empty."); } assetBundlePath = AssemblyUtils.GetCombinedPathFromAssemblyFolder(assemblyType, assetName); if (!File.Exists(assetBundlePath)) { throw new FileNotFoundException(null, assetBundlePath); } } public AssetBundleElement(string assetFullPath) { if (string.IsNullOrEmpty(assetFullPath)) { throw new ArgumentNullException("The parameter assetFullPath cant be null or empty."); } if (!File.Exists(assetFullPath)) { throw new FileNotFoundException(null, assetFullPath); } assetBundlePath = assetFullPath; } public bool PreloadBundle() { if ((Object)(object)loadedBundle == (Object)null) { if (!bundleCacheStorage.TryGetValue(assetBundlePath, out loadedBundle) || (Object)(object)loadedBundle == (Object)null) { loadedBundle = AssetBundle.LoadFromFile(assetBundlePath); if ((Object)(object)loadedBundle == (Object)null) { throw new LoadBundleFileException(assetBundlePath); } TimeLogger.Logger.LogTimeDebug("Bundle loaded successfully from " + assetBundlePath, (LogCategories)8); bundleCacheStorage.Add(assetBundlePath, loadedBundle); } else { TimeLogger.Logger.LogTimeDebug("Bundle loaded successfully from storage cache.", (LogCategories)8); } } return true; } public string[] GetAllAssetNames() { if (!PreloadBundle()) { return null; } return loadedBundle.GetAllAssetNames(); } public string GetAllAssetNamesString(string separator = "\n") { string[] allAssetNames = GetAllAssetNames(); if (allAssetNames == null) { return null; } return string.Join(separator, allAssetNames); } public async Task<T[]> LoadAllAssetsAsync<T>() where T : Object { if (!PreloadBundle()) { return null; } AssetBundleRequest assetRequest = loadedBundle.LoadAllAssetsAsync(typeof(T)); ResourceRequestAwaiter val = UnityAsyncExtensions.GetAwaiter((ResourceRequest)(object)assetRequest); if (!((ResourceRequestAwaiter)(ref val)).IsCompleted) { await val; ResourceRequestAwaiter val2 = default(ResourceRequestAwaiter); val = val2; } ((ResourceRequestAwaiter)(ref val)).GetResult(); return assetRequest.allAssets.Cast<T>().ToArray(); } public T[] LoadAllAssets<T>() where T : Object { if (!PreloadBundle()) { return null; } return loadedBundle.LoadAllAssets<T>().ToArray(); } public bool TryLoadNewPrefabInstance(string prefabName, out GameObject prefabInstance) { prefabInstance = null; string bundleInternalUnityPath = "assets/" + prefabName + ".prefab"; if (!this.TryLoadObject<GameObject>(bundleInternalUnityPath, out GameObject objectInstance)) { return false; } prefabInstance = Object.Instantiate<GameObject>(objectInstance); return true; } public bool TryLoadObject<T>(string bundleInternalUnityPath, out T objectInstance) where T : Object { objectInstance = default(T); if (!TryLoadPrefabObject<Object>(bundleInternalUnityPath, out Object loadedObject)) { return false; } if (!(loadedObject is T)) { TimeLogger.Logger.LogTimeError("The object at " + bundleInternalUnityPath + " is not a " + typeof(T).Name + " type.", (LogCategories)8); return false; } objectInstance = (T)(object)loadedObject; return true; } private bool TryLoadPrefabObject<T>(string bundleInternalUnityPath, out T loadedObject) where T : Object { loadedObject = default(T); if (!PreloadBundle()) { return false; } loadedObject = loadedBundle.LoadAsset<T>(bundleInternalUnityPath); if ((Object)(object)loadedObject == (Object)null) { TimeLogger.Logger.LogTimeDebug("Asset loaded with path \"" + bundleInternalUnityPath + "\" not found.", (LogCategories)8); return false; } return true; } public void UnloadBundle() { if ((Object)(object)loadedBundle == (Object)null) { TimeLogger.Logger.LogTimeDebug("No loaded bundle was found while trying to unload.", (LogCategories)8); return; } loadedBundle.Unload(true); loadedBundle = null; bundleCacheStorage.Remove(assetBundlePath); TimeLogger.Logger.LogTimeDebug("Asset bundle at " + assetBundlePath + " unloaded successfully.", (LogCategories)8); } } public class LoadBundleFileException : Exception { public LoadBundleFileException(string assetPath) : base("Failed to load asset bundle from path: " + assetPath + ".") { } } } namespace Damntry.UtilsUnity.ExtensionMethods { public static class ComponentExtensions { public static GameObject GameObject(this Component comp) { if ((Object)(object)comp == (Object)null) { return null; } if (!Object.op_Implicit((Object)(object)comp)) { return null; } return comp.gameObject; } } } namespace Damntry.UtilsUnity.Components { public enum KeyPressAction { KeyDown, KeyUp, KeyHeld } public class KeyPressDetection : MonoBehaviour { private class KeyPressData { private readonly double cooldownSeconds; private double lastKeyPressTime; internal Action Action { get; private set; } internal KeyPressAction KeyAction { get; private set; } public KeyPressData(Action action, KeyPressAction keyAction, int cooldownMillis) { Action = action; cooldownSeconds = ((cooldownSeconds > 0.0) ? ((double)cooldownMillis / 1000.0) : (-1.0)); lastKeyPressTime = double.MinValue; KeyAction = keyAction; } internal bool IsNotInCooldown() { if (!(cooldownSeconds <= 0.0)) { return lastKeyPressTime + cooldownSeconds < Time.timeAsDouble; } return true; } internal void UpdateKeyPressTime() { lastKeyPressTime = Time.timeAsDouble; } } private static KeyPressDetection instance; private static Dictionary<KeyCode, KeyPressData> keyPressActions; private static Func<bool> preKeyCheckFunc; private static Func<MonoBehaviour> getBehaviourAttachFunc; private const int DefaultKeyPressCooldown = 65; static KeyPressDetection() { keyPressActions = new Dictionary<KeyCode, KeyPressData>(); } private void Awake() { instance = this; } private void Start() { BehaviourEnabledCheck(); } private void Update() { //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) if (!Input.anyKey) { return; } foreach (KeyValuePair<KeyCode, KeyPressData> keyPressAction in keyPressActions) { KeyPressData value = keyPressAction.Value; if (value.KeyAction switch { KeyPressAction.KeyDown => Input.GetKeyDown(keyPressAction.Key), KeyPressAction.KeyUp => Input.GetKeyUp(keyPressAction.Key), KeyPressAction.KeyHeld => Input.GetKey(keyPressAction.Key), _ => throw new NotImplementedException(EnumExtension.GetDescription((Enum)value.KeyAction)), } && (preKeyCheckFunc == null || preKeyCheckFunc()) && value.IsNotInCooldown()) { value.UpdateKeyPressTime(); value.Action(); } } } private void OnDestroy() { TaskExtensionMethods.FireAndForget(BeginGetBehaviour(getBehaviourAttachFunc), (LogCategories)33554432); } public static async Task InitializeAsync(Func<MonoBehaviour> getBehaviourAttachFunc, Func<bool> preKeyCheckFunc = null) { KeyPressDetection.getBehaviourAttachFunc = getBehaviourAttachFunc; await BeginGetBehaviour(getBehaviourAttachFunc); } private static async Task BeginGetBehaviour(Func<MonoBehaviour> getBehaviourAttachFunc) { MonoBehaviour val; while ((Object)(object)(val = getBehaviourAttachFunc()) == (Object)null || !((Component)val).gameObject.activeInHierarchy) { await Task.Delay(500); } Initialize(val, preKeyCheckFunc); } public static void Initialize(MonoBehaviour unityObject, Func<bool> preKeyCheckFunc = null) { if ((Object)(object)unityObject == (Object)null) { throw new ArgumentNullException("unityObject"); } Initialize(((Component)unityObject).gameObject, preKeyCheckFunc); } public static void Initialize(GameObject unityGameObject, Func<bool> preKeyCheckFunc = null) { if ((Object)(object)unityGameObject == (Object)null) { throw new ArgumentNullException("unityGameObject"); } unityGameObject.AddComponent<KeyPressDetection>(); KeyPressDetection.preKeyCheckFunc = preKeyCheckFunc; } public static bool TryAddHotkey(KeyCode keyCode, KeyPressAction keyAction, Action action) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return AddHotkeyInternal(keyCode, 65, keyAction, throwIfExists: false, action); } public static bool TryAddHotkey(KeyCode keyCode, KeyPressAction keyAction, int cooldownMillis, Action action) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) return AddHotkeyInternal(keyCode, cooldownMillis, keyAction, throwIfExists: false, action); } public static void AddHotkey(KeyCode keyCode, KeyPressAction keyAction, Action action) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) AddHotkeyInternal(keyCode, 65, keyAction, throwIfExists: true, action); } public static void AddHotkey(KeyCode keyCode, KeyPressAction keyAction, int cooldownMillis, Action action) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) AddHotkeyInternal(keyCode, cooldownMillis, keyAction, throwIfExists: true, action); } private static bool AddHotkeyInternal(KeyCode keyCode, int cooldownMillis, KeyPressAction keyAction, bool throwIfExists, Action action) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Unknown result type (might be due to invalid IL or missing references) if (action == null) { throw new ArgumentNullException("action"); } if (!keyPressActions.TryGetValue(keyCode, out var value)) { keyPressActions.Add(keyCode, new KeyPressData(action, keyAction, cooldownMillis)); } else { if (keyAction == value.KeyAction || (keyAction != KeyPressAction.KeyUp && value.KeyAction != KeyPressAction.KeyUp)) { string text = $"Hotkey {keyCode} is already defined."; if (throwIfExists) { throw new InvalidOperationException(text); } TimeLogger.Logger.LogTimeError(text, (LogCategories)33554432); return false; } keyPressActions.Add(keyCode, new KeyPressData(action, keyAction, cooldownMillis)); } instance?.BehaviourEnabledCheck(); return true; } public static void RemoveHotkey(KeyCode keyCode) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) if (keyPressActions.ContainsKey(keyCode)) { keyPressActions.Remove(keyCode); instance?.BehaviourEnabledCheck(); } } public static string GetRegisteredHotkeys() { return string.Join(", ", keyPressActions.Keys); } private void BehaviourEnabledCheck() { Dictionary<KeyCode, KeyPressData> dictionary = keyPressActions; ((Behaviour)this).enabled = dictionary != null && dictionary.Count > 0; } } }
BepInEx/plugins/es.damntry.SuperQoLity/SuperQoLity-Release.dll
Decompiled a week ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.CodeDom.Compiler; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Configuration; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using System.Threading; using System.Threading.Tasks; using BepInEx; using BepInEx.Configuration; using Cysharp.Threading.Tasks; using Damntry.Utils.Collections.Queues; using Damntry.Utils.Collections.Queues.Interfaces; using Damntry.Utils.Events; using Damntry.Utils.ExtensionMethods; using Damntry.Utils.Logging; using Damntry.Utils.Reflection; using Damntry.Utils.Tasks; using Damntry.Utils.Tasks.AsyncDelay; using Damntry.Utils.Timers; using Damntry.Utils.Timers.StopwatchImpl; using Damntry.UtilsBepInEx.Configuration; using Damntry.UtilsBepInEx.Configuration.ConfigurationManager; using Damntry.UtilsBepInEx.Configuration.ConfigurationManager.SettingAttributes; using Damntry.UtilsBepInEx.HarmonyPatching; using Damntry.UtilsBepInEx.HarmonyPatching.Attributes; using Damntry.UtilsBepInEx.HarmonyPatching.AutoPatching; using Damntry.UtilsBepInEx.HarmonyPatching.AutoPatching.Attributes; using Damntry.UtilsBepInEx.HarmonyPatching.AutoPatching.BaseClasses; using Damntry.UtilsBepInEx.HarmonyPatching.AutoPatching.BaseClasses.Inheritable; using Damntry.UtilsBepInEx.HarmonyPatching.AutoPatching.Interfaces; using Damntry.UtilsBepInEx.HarmonyPatching.Exceptions; using Damntry.UtilsBepInEx.IL; using Damntry.UtilsBepInEx.Logging; using Damntry.UtilsBepInEx.MirrorNetwork.Attributes; using Damntry.UtilsBepInEx.MirrorNetwork.Components; using Damntry.UtilsBepInEx.MirrorNetwork.Helpers; using Damntry.UtilsBepInEx.MirrorNetwork.SyncVar; using Damntry.UtilsBepInEx.ModHelpers; using Damntry.UtilsUnity.Components; using Damntry.UtilsUnity.ExtensionMethods; using Damntry.UtilsUnity.Resources; using Damntry.UtilsUnity.Tasks.AsyncDelay; using Damntry.UtilsUnity.Timers; using Damntry.UtilsUnity.UI; using Damntry.UtilsUnity.UI.Extensions; using HarmonyLib; using HighlightPlus; using HutongGames.PlayMaker; using HutongGames.PlayMaker.Actions; using Microsoft.CodeAnalysis; using Mirror; using Rewired; using StarterAssets; using SuperQoLity.SuperMarket.ModUtils; using SuperQoLity.SuperMarket.ModUtils.ExternalMods; using SuperQoLity.SuperMarket.ModUtils.UI; using SuperQoLity.SuperMarket.PatchClassHelpers.Components; using SuperQoLity.SuperMarket.PatchClassHelpers.ContainerEntities; using SuperQoLity.SuperMarket.PatchClassHelpers.ContainerEntities.Search; using SuperQoLity.SuperMarket.PatchClassHelpers.ContainerEntities.ShelfSlotInfo; using SuperQoLity.SuperMarket.PatchClassHelpers.NPCs; using SuperQoLity.SuperMarket.PatchClassHelpers.NPCs.Employees.JobScheduler; using SuperQoLity.SuperMarket.PatchClassHelpers.NPCs.Employees.JobScheduler.AutoMode; using SuperQoLity.SuperMarket.PatchClassHelpers.NPCs.Employees.JobScheduler.AutoMode.DataDefinition; using SuperQoLity.SuperMarket.PatchClassHelpers.NPCs.Employees.JobScheduler.Helpers; using SuperQoLity.SuperMarket.PatchClassHelpers.NPCs.Employees.RestockMatch; using SuperQoLity.SuperMarket.PatchClassHelpers.NPCs.Employees.RestockMatch.Component; using SuperQoLity.SuperMarket.PatchClassHelpers.NPCs.Employees.RestockMatch.Helpers; using SuperQoLity.SuperMarket.PatchClassHelpers.NPCs.Employees.RestockMatch.Models; using SuperQoLity.SuperMarket.PatchClassHelpers.NPCs.Employees.TargetMarking; using SuperQoLity.SuperMarket.PatchClassHelpers.NPCs.Movement; using SuperQoLity.SuperMarket.PatchClassHelpers.Networking.SyncVarBehaviours; using SuperQoLity.SuperMarket.Patches; using SuperQoLity.SuperMarket.Patches.BetterSMT_Module; using SuperQoLity.SuperMarket.Patches.EmployeeModule; using SuperQoLity.SuperMarket.Patches.Misc; using SuperQoLity.SuperMarket.Patches.TransferItemsModule; using TMPro; using UnityEngine; using UnityEngine.AI; using UnityEngine.Networking; using UnityEngine.SceneManagement; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("Assembly-CSharp-firstpass")] [assembly: IgnoresAccessChecksTo("Assembly-CSharp")] [assembly: TargetFramework(".NETFramework,Version=v4.8.1", FrameworkDisplayName = ".NET Framework 4.8.1")] [assembly: AssemblyCompany("None")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Bepinex plugin for Supermarket Together adding extra QoL features")] [assembly: AssemblyFileVersion("0.8.7.60001")] [assembly: AssemblyInformationalVersion("0.8.7.60001+05cd54f610dbae45caf75a1971c4824e7fbef97f")] [assembly: AssemblyProduct("es.damntry.SuperQoLity")] [assembly: AssemblyTitle("SuperQoLity")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.8.7.60001")] [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.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [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 SuperQoLity { [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInDependency(/*Could not decode attribute arguments.*/)] [BepInPlugin("es.damntry.SuperQoLity", "SuperQoLity", "0.8.7.60001")] public class Plugin : BaseUnityPlugin { [CompilerGenerated] private static class <>O { public static PreprocessMessageFunc <0>__RemoveSpecialNotifNewLinesForLog; } public static bool IsSolutionInDebugMode = false; private static int AssetIdModSignature { get; } = 3071; public void Awake() { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Expected O, but got Unknown object obj = <>O.<0>__RemoveSpecialNotifNewLinesForLog; if (obj == null) { PreprocessMessageFunc val = GameNotifications.RemoveSpecialNotifNewLinesForLog; <>O.<0>__RemoveSpecialNotifNewLinesForLog = val; obj = (object)val; } TimeLogger.InitializeTimeLogger<BepInExTimeLogger>((PreprocessMessageFunc)obj, false, new object[1] { "SuperQoLity" }); SolutionDebugModeHandler(); ModConfig.Instance.InitializeConfig((BaseUnityPlugin)(object)this); ConfigDebugModeHandler(); AutoPatcher.RegisterAllAutoPatchContainers(); ModConfig.Instance.StartConfigBinding(); GameNotifications.Instance.InitializeGameNotifications(); TaskExtensionMethods.FireAndForget(KeyPressDetection.InitializeAsync((Func<MonoBehaviour>)(() => (MonoBehaviour)(object)FirstPersonController.Instance), (Func<bool>)(() => !AuxUtils.IsChatOpen())), (LogCategories)8); NetworkSpawnManager.Initialize(AssetIdModSignature); TimeLogger.Logger.LogTimeDebug("SuperQoLity (es.damntry.SuperQoLity) initialization finished.", (LogCategories)8); bool flag = AutoPatcher.StartAutoPatcher(); JobSchedulerManager.InitializeJobSchedulerEvents(); RestockMatcher.Enable(); StartingMessage.InitStartingMessages(flag); TimeLogger.Logger.LogTimeMessage("Mod SuperQoLity (es.damntry.SuperQoLity) loaded " + (flag ? "" : "(not quite) ") + "successfully.", (LogCategories)8); } private void SolutionDebugModeHandler() { } public static void ConfigDebugModeHandler() { bool flag = ModConfig.Instance.IsDebugEnabledConfig(); if (flag != TimeLogger.DebugEnabled) { TimeLogger.DebugEnabled = flag; TimeLogger.Logger.LogTimeWarning("SuperQoLity Debug Dev mode " + (flag ? "enabled" : "disabled") + ".", (LogCategories)8); } } public static void HookChangeDebugSetting() { ModConfig.Instance.EnabledDevMode.SettingChanged += delegate { ConfigDebugModeHandler(); }; } private CheckResult StartMethodSignatureCheck() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown try { MethodSignatureChecker val = new MethodSignatureChecker(((object)this).GetType()); val.PopulateMethodSignaturesFromHarmonyPatches(); val.AddMethod(typeof(NPC_Manager), "EmployeeNPCControl", new Type[1] { typeof(int) }, (Type[])null); val.AddMethod(typeof(NPC_Manager), "GetFreeStorageContainer", new Type[1] { typeof(int) }, (Type[])null); val.AddMethod(typeof(NPC_Manager), "MainRestockUpdate"); val.AddMethod(typeof(NPC_Manager), "CheckIfProdShelfWithSameProduct"); val.AddMethod(typeof(NPC_Manager), "GetRandomGroundBox"); val.AddMethod(typeof(NPC_Manager), "GetRandomGroundBoxAllowedInStorage"); val.AddMethod(typeof(NPC_Manager), "DropBoxOnGround"); val.AddMethod(typeof(NPC_Manager), "UnequipBox"); val.AddMethod(typeof(NPC_Manager), "AttemptToGetRestPosition"); val.AddMethod(typeof(NPC_Manager), "CashierGetAvailableCheckout"); val.AddMethod(typeof(NPC_Manager), "UpdateEmployeeCheckouts"); val.AddMethod(typeof(NPC_Manager), "CheckIfCustomerInQueue"); val.AddMethod(typeof(NPC_Manager), "GetThiefTarget"); val.AddMethod(typeof(NPC_Manager), "IsFirstSecurityEmployee"); val.AddMethod(typeof(NPC_Manager), "GetClosestDropProduct"); val.AddMethod(typeof(NPC_Manager), "RetrieveCorrectPatrolPoint"); val.AddMethod(typeof(NPC_Manager), "UpdateEmployeeStats"); return val.StartSignatureCheck(); } catch (Exception ex) { TimeLogger.Logger.LogTimeException(ex, (LogCategories)4194304); return CheckResult.UnknownError; } } } public static class MyPluginInfo { public const string PLUGIN_GUID = "es.damntry.SuperQoLity"; public const string PLUGIN_NAME = "SuperQoLity"; public const string PLUGIN_VERSION = "0.8.7.60001"; public const string BETTERSMT_SUPPORTED_VERSION = "2.4.0"; } } namespace SuperQoLity.SuperMarket { public enum GameOnlineMode { None, Host, Client } public enum GameWorldEvent { QuitOrMenu, LoadingWorld, CanvasAwake, FPControllerStarted, WorldStarted } public static class WorldState { public static class BuildingsEvents { public static Action<Data_Container> OnProductShelfLoadedOrUpdated; public static Action<Data_Container> OnStorageLoadedOrUpdated; public static Action<NetworkSpawner, int> OnShelfBuilt; } public static GameWorldEvent CurrentGameWorldState { get; private set; } public static GameOnlineMode CurrenOnlineMode { get; set; } public static bool IsGameWorldStarted => CurrentGameWorldState == GameWorldEvent.WorldStarted; public static event Action<GameWorldEvent> OnGameWorldChange; public static event Action OnQuitOrMenu; public static event Action OnLoadingWorld; public static event Action OnCanvasAwake; public static event Action OnFPControllerStarted; public static event Action OnWorldStarted; public static bool IsGameWorldAtOrAfter(GameWorldEvent worldState) { return CurrentGameWorldState >= worldState; } public static void SetGameWorldState(GameWorldEvent state) { TimeLogger.Logger.LogTimeDebugFunc((Func<string>)(() => $"World state change from {CurrentGameWorldState} to {state}"), (LogCategories)8); CurrentGameWorldState = state; EventMethods.TryTriggerEvents<GameWorldEvent>(WorldState.OnGameWorldChange, state); EventMethods.TryTriggerEvents(GetSubscribersForWorldState(state)); } public static void SubscribeToWorldStateEvent(GameWorldEvent state, Action actionOnEvent) { switch (state) { case GameWorldEvent.QuitOrMenu: OnQuitOrMenu += actionOnEvent; break; case GameWorldEvent.LoadingWorld: OnLoadingWorld += actionOnEvent; break; case GameWorldEvent.CanvasAwake: OnCanvasAwake += actionOnEvent; break; case GameWorldEvent.FPControllerStarted: OnFPControllerStarted += actionOnEvent; break; case GameWorldEvent.WorldStarted: OnWorldStarted += actionOnEvent; break; } } public static Action GetSubscribersForWorldState(GameWorldEvent state) { return state switch { GameWorldEvent.QuitOrMenu => WorldState.OnQuitOrMenu, GameWorldEvent.LoadingWorld => WorldState.OnLoadingWorld, GameWorldEvent.CanvasAwake => WorldState.OnCanvasAwake, GameWorldEvent.FPControllerStarted => WorldState.OnFPControllerStarted, GameWorldEvent.WorldStarted => WorldState.OnWorldStarted, _ => null, }; } } } namespace SuperQoLity.SuperMarket.Patches { public class GameWorldEventsPatch : FullyAutoPatchedInstance { private class DetectGameLoadFinished { private enum DetectionState { Initial, Success, Failed } private static readonly Stopwatch sw = Stopwatch.StartNew(); private static GameObject transitionBCKobj; private static DetectionState state = DetectionState.Initial; [HarmonyPatch(typeof(NetworkManager), "OnStartServer")] [HarmonyPrefix] public static void OnStartHostPrefixPatch() { SetLoadingWorld(); } [HarmonyPatch(typeof(NetworkManager), "OnStartClient")] [HarmonyPrefix] public static void OnStartClientPrefixPatch() { if (WorldState.CurrenOnlineMode != GameOnlineMode.Host) { SetLoadingWorld(); } } private static void SetLoadingWorld() { WorldState.CurrenOnlineMode = (NetworkServer.activeHost ? GameOnlineMode.Host : GameOnlineMode.Client); WorldState.SetGameWorldState(GameWorldEvent.LoadingWorld); } [HarmonyPatch(typeof(GameCanvas), "Awake")] [HarmonyPostfix] private static void GameCanvasAwake(GameCanvas __instance) { WorldState.SetGameWorldState(GameWorldEvent.CanvasAwake); } [HarmonyPatch(typeof(GameCanvas), "Update")] [HarmonyPostfix] private static void GameCanvasUpdatePostFix(GameCanvas __instance) { if (sw.ElapsedMilliseconds < 100) { return; } try { if ((Object)(object)transitionBCKobj == (Object)null) { state = DetectionState.Initial; transitionBCKobj = GameObject.Find("MasterOBJ/MasterCanvas/TransitionBCK"); } if ((Object)(object)transitionBCKobj != (Object)null && !transitionBCKobj.activeSelf && state == DetectionState.Initial) { state = DetectionState.Success; WorldState.SetGameWorldState(GameWorldEvent.WorldStarted); } } catch (Exception ex) { state = DetectionState.Failed; TimeLogger.Logger.LogTimeExceptionWithMessage("Error while trying to detect game finished loading.", ex, (LogCategories)8); } finally { GameWorldEventsPatch instance = Container<GameWorldEventsPatch>.Instance; if (state == DetectionState.Failed) { TimeLogger.Logger.LogTimeError(((AutoPatchedInstanceBase)instance).ErrorMessageOnAutoPatchFail, (LogCategories)8); ((AutoPatchedInstanceBase)instance).UnpatchInstance(); sw.Stop(); } else { sw.Restart(); } } } } private class FirstPersonControllerStart { [HarmonyPatch(typeof(FirstPersonController), "Start")] [HarmonyPostfix] public static void OnClientDisconnectPatch(FirstPersonController __instance) { WorldState.SetGameWorldState(GameWorldEvent.FPControllerStarted); } } private class DetectQuitToMainMenu { [HarmonyPatch(typeof(CustomNetworkManager), "OnClientDisconnect")] [HarmonyPostfix] public static void OnClientDisconnectPatch(CustomNetworkManager __instance) { WorldState.CurrenOnlineMode = GameOnlineMode.None; WorldState.SetGameWorldState(GameWorldEvent.QuitOrMenu); } } private class BuildingEvents { private class ShelfLoadedOrBuilt { [HarmonyPatch(typeof(Data_Container), "BoxSpawner")] [HarmonyPostfix] private static void BoxSpawnerPatch(Data_Container __instance) { EventMethods.TryTriggerEvents<Data_Container>(WorldState.BuildingsEvents.OnStorageLoadedOrUpdated, __instance); } [HarmonyPatch(typeof(Data_Container), "ItemSpawner")] [HarmonyPostfix] private static void ItemSpawnerPatch(Data_Container __instance) { EventMethods.TryTriggerEvents<Data_Container>(WorldState.BuildingsEvents.OnProductShelfLoadedOrUpdated, __instance); } [HarmonyPatch(typeof(NetworkSpawner), "UserCode_CmdSpawn__Int32__Vector3__Vector3")] [HarmonyPostfix] private static void NewBuildableConstructed(NetworkSpawner __instance, int prefabID) { EventMethods.TryTriggerEvents<NetworkSpawner, int>(WorldState.BuildingsEvents.OnShelfBuilt, __instance, prefabID); } } } public override bool IsAutoPatchEnabled => true; public override string ErrorMessageOnAutoPatchFail { get; protected set; } = "SuperQoLity - Detection of world events patch failed. Disabled"; } public class PerformanceCachingPatch : FullyAutoPatchedInstance { public class PopulateCacheOnBuilderInitialization { [CompilerGenerated] private sealed class <WaitEndOfIEnumerable>d__0 : IEnumerator<object>, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public IEnumerator result; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <WaitEndOfIEnumerable>d__0(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { switch (<>1__state) { default: return false; case 0: <>1__state = -1; break; case 1: <>1__state = -1; break; } if (result.MoveNext()) { <>2__current = result.Current; <>1__state = 1; return true; } PopulateMaxProductsPerRowCache(); 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(); } } [IteratorStateMachine(typeof(<WaitEndOfIEnumerable>d__0))] [HarmonyPatch(typeof(Builder_Main), "RetrieveInitialBehaviours")] [HarmonyPostfix] public static IEnumerator WaitEndOfIEnumerable(IEnumerator result) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <WaitEndOfIEnumerable>d__0(0) { result = result }; } } private static readonly Dictionary<(int containerClass, int containerId, int productId), int> maxProductsPerRowCache = new Dictionary<(int, int, int), int>(); private static bool cachePopulated = false; public override bool IsAutoPatchEnabled => ModConfig.Instance.EnableEmployeeChanges.Value; public override string ErrorMessageOnAutoPatchFail { get; protected set; } = "SuperQoLity - AI caching patch failed. Disabled"; private static void PopulateMaxProductsPerRowCache() { if (cachePopulated) { return; } GameObject[] buildables = ((Component)GameData.Instance).GetComponent<NetworkSpawner>().buildables; Data_Container val2 = default(Data_Container); foreach (GameObject val in buildables) { if ((Object)(object)val == (Object)null || !val.TryGetComponent<Data_Container>(ref val2) || val2.GetContainerType() != 0) { continue; } GameObject[] productPrefabs = ProductListing.Instance.productPrefabs; foreach (GameObject val3 in productPrefabs) { if (!((Object)(object)val3 == (Object)null)) { int productID = val3.GetComponent<Data_Product>().productID; int maxProductsPerRow = GetMaxProductsPerRow(val2, productID); maxProductsPerRowCache.Add((val2.containerClass, val2.containerID, productID), maxProductsPerRow); } } } cachePopulated = true; } public static int GetMaxProductsPerRowCachedThreaded(Data_Container dataContainer, int shelfProductId, int shelfIndex) { return GetMaxProductsPerRowCached(null, dataContainer, shelfProductId, shelfIndex, isThreaded: true); } public static int GetMaxProductsPerRowCached(NPC_Manager __instance, Data_Container dataContainer, int shelfProductId, int shelfIndex) { return GetMaxProductsPerRowCached(null, dataContainer, shelfProductId, shelfIndex, isThreaded: false); } public static int GetMaxProductsPerRowCached(NPC_Manager __instance, Data_Container dataContainer, int shelfProductId, int shelfIndex, bool isThreaded) { bool flag = false; int value = 0; if (cachePopulated) { flag = maxProductsPerRowCache.TryGetValue((dataContainer.containerClass, dataContainer.containerID, shelfProductId), out value); } else { TimeLogger.Logger.LogTimeWarning("MaxProductsPerRow has been requested from the cache, but it hasnt been populated yet.", (LogCategories)1024); } if (!flag) { TimeLogger.Logger.LogTimeWarning($"A maxProductsPerRow cache entry doesnt exist for containerID: {dataContainer.containerID} " + $"and productId: {shelfProductId}. Attempting to obtain it manually.", (LogCategories)8); if (((AutoPatchedInstanceBase)Container<PerformanceCachingPatch>.Instance).IsPatchActive) { value = GetMaxProductsPerRow(dataContainer, shelfProductId); } else if ((Object)(object)__instance != (Object)null) { value = __instance.GetMaxProductsPerRow(shelfIndex, shelfProductId); } else if (isThreaded) { TimeLogger.Logger.LogTimeWarning("This method is threaded and the amount cannot be calculated from the source Unity objects. Returning 0.", (LogCategories)8); } else { TimeLogger.Logger.LogTimeError("The NPC_Manager instance is null and the value could not be obtained manually.", (LogCategories)8); } maxProductsPerRowCache.Add((dataContainer.containerClass, dataContainer.containerID, shelfProductId), value); } return value; } public static int GetMaxProductsPerRow(Data_Container dataContainer, int shelfProductId) { return GetMaxProductsPerRow(NPC_Manager.Instance, -1, dataContainer, shelfProductId); } private static int GetMaxProductsPerRow(NPC_Manager __instance, int shelfIndex, Data_Container dataContainer, int ProductID) { //IL_0023: 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) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) if (ProductID >= ProductListing.Instance.productPrefabs.Length) { return -1; } GameObject obj = ProductListing.Instance.productPrefabs[ProductID]; Vector3 size = obj.GetComponent<BoxCollider>().size; bool isStackable = obj.GetComponent<Data_Product>().isStackable; int num = Mathf.Clamp(Mathf.FloorToInt(dataContainer.shelfLength / (size.x * 1.1f)), 1, 100); int num2 = Mathf.FloorToInt(dataContainer.shelfWidth / (size.z * 1.1f)); num2 = Mathf.Clamp(num2, 1, 100); int num3 = num * num2; if (isStackable) { int num4 = Mathf.FloorToInt(dataContainer.shelfHeight / (size.y * 1.1f)); num4 = Mathf.Clamp(num4, 1, 100); num3 *= num4; } return num3; } } public class ShelfItemCulling { public static void Initialize() { } public static void ShelfBuilt(NetworkSpawner instance, int prefabID) { instance.buildables[prefabID].GetComponent<Data_Container>(); } public static void CreateProductShelfBounds(Data_Container instance) { //IL_001e: Unknown result type (might be due to invalid IL or missing references) foreach (Transform item in ((Component)instance).transform.Find("ProductContainer")) { _ = item; } } public static void OnStorageLoadedOrUpdated(Data_Container __instance) { } public static void NewBuildableConstructed(NetworkSpawner __instance, int prefabID) { } } public class StoreOpenStatusPatch : FullyAutoPatchedInstance { private static CancellableSingleTask<UniTaskDelay> storeClosedTask = new CancellableSingleTask<UniTaskDelay>(true); private static SemaphoreSlim semaphoreLock = new SemaphoreSlim(1, 1); public override bool IsAutoPatchEnabled { get { if (!ModConfig.Instance.EnableEmployeeChanges.Value) { return ModConfig.Instance.EnableTransferProducts.Value; } return true; } } public override string ErrorMessageOnAutoPatchFail { get; protected set; } = "SuperQoLity - Store opening state detection patch failed. Disabled"; public static event Action<bool> OnSupermarketOpenStateChanged; public override void OnPatchFinishedVirtual(bool IsActive) { if (IsActive) { NetworkSpawnManager.RegisterNetwork<StoreStatusNetwork>(918219u); WorldState.OnQuitOrMenu += async delegate { await storeClosedTask.StopTaskAndWaitAsync(500); }; } } [HarmonyPatch(/*Could not decode attribute arguments.*/)] [HarmonyPriority(600)] [HarmonyPrefix] private static bool NetworkisSupermarketOpenPrefixPatch(ref bool __state) { __state = GameData.Instance.isSupermarketOpen; return true; } [HarmonyPatch(/*Could not decode attribute arguments.*/)] [HarmonyPostfix] private static async void NetworkisSupermarketOpenPostfixPatch(bool __state) { if (GameData.Instance.isSupermarketOpen == __state) { return; } await storeClosedTask.StopTaskAndWaitAsync(); await semaphoreLock.WaitAsync(); try { bool value = true; if (!GameData.Instance.isSupermarketOpen) { await storeClosedTask.StartAwaitableTaskAsync((Func<Task>)CheckCustomersPendingActions, "Wait for end of customers actions", true); if (storeClosedTask.IsCancellationRequested) { TimeLogger.Logger.LogTimeDebugFunc((Func<string>)(() => "Skipped to next day or quitting to main menu before customers left the store."), (LogCategories)16); } else { TimeLogger.Logger.LogTimeDebugFunc((Func<string>)(() => "All customers are leaving or already left the supermarket."), (LogCategories)16); } value = false; } StoreStatusNetwork.IsStoreOpenOrCustomersInsideSync.Value = value; if (StoreOpenStatusPatch.OnSupermarketOpenStateChanged != null) { StoreOpenStatusPatch.OnSupermarketOpenStateChanged(SyncVar<bool>.op_Implicit(StoreStatusNetwork.IsStoreOpenOrCustomersInsideSync)); } } finally { semaphoreLock.Release(); } } private static async Task CheckCustomersPendingActions() { UnityTimeStopwatch safetyTimeout = UnityTimeStopwatch.StartNew(); try { while (HasCustomersWithPendingActions(storeClosedTask.CancellationToken) && safetyTimeout.ElapsedSeconds < 180) { await UniTask.Delay(500, false, (PlayerLoopTiming)8, storeClosedTask.CancellationToken, false); } } catch (OperationCanceledException) { } } private static bool HasCustomersWithPendingActions(CancellationToken cancelToken = default(CancellationToken)) { //IL_0028: Unknown result type (might be due to invalid IL or missing references) if (cancelToken.IsCancellationRequested) { return false; } foreach (Transform item in NPC_Manager.Instance.customersnpcParentOBJ.transform) { if (((Component)item).gameObject.GetComponent<NPC_Info>().state < 99) { return true; } } return false; } } } namespace SuperQoLity.SuperMarket.Patches.TransferItemsModule { public enum EnumItemTransferMode { [Description("Disabled")] Disabled, [Description("Only while store is closed")] OnlyWhileStoreClosed, [Description("Always on")] AlwaysOn } [HarmonyPatch(typeof(Data_Container))] public class IncreasedItemTransferPatch : FullyAutoPatchedInstance { private enum RowActionType { Add, Remove } public enum CharacterType { Player, Employee } public override bool IsAutoPatchEnabled => ModConfig.Instance.EnableTransferProducts.Value; public override string ErrorMessageOnAutoPatchFail { get; protected set; } = "SuperQoLity - Transfer patch failed. Item Transfer Module inactive"; public override void OnPatchFinishedVirtual(bool IsPatchActive) { if (IsPatchActive) { NetworkSpawnManager.RegisterNetwork<ItemTransferNetwork>(871623u); } } public static int GetNumTransferItems(int giverItemCount, int receiverItemCount, int receiverMaxCapacity, CharacterType charType) { return GetNumTransferItems(giverItemCount, receiverItemCount, receiverMaxCapacity, charType, 1); } public static int GetNumTransferItems(int giverItemCount, int receiverItemCount, int receiverMaxCapacity, CharacterType charType, int numMovedProducts) { if (IsItemTransferEnabledFor(charType, numMovedProducts)) { int val = receiverMaxCapacity - receiverItemCount; numMovedProducts = Math.Min(Math.Min((charType == CharacterType.Player) ? ((SyncVar<int>)(object)ItemTransferNetwork.ItemTransferQuantitySync).Value : int.MaxValue, giverItemCount), val); } return numMovedProducts; } public static bool IsItemTransferEnabledFor(CharacterType charType, int numMovedProducts) { if ((charType == CharacterType.Player && ((SyncVar<EnumItemTransferMode>)(object)ItemTransferNetwork.ItemTransferModeSync).Value == EnumItemTransferMode.Disabled) || (charType == CharacterType.Employee && !ModConfig.Instance.ClosedStoreEmployeeItemTransferMaxed.Value)) { return false; } if (!SyncVar<bool>.op_Implicit(StoreStatusNetwork.IsStoreOpenOrCustomersInsideSync)) { return true; } switch (charType) { case CharacterType.Player: if (ModConfig.Instance.EnableTransferProducts.Value && ((SyncVar<int>)(object)ItemTransferNetwork.ItemTransferQuantitySync).Value != numMovedProducts) { return ((SyncVar<EnumItemTransferMode>)(object)ItemTransferNetwork.ItemTransferModeSync).Value != EnumItemTransferMode.OnlyWhileStoreClosed; } return false; case CharacterType.Employee: return false; default: throw new InvalidOperationException($"{charType} is not a supported value in this method."); } } [HarmonyPatch("AddItemToRow")] [HarmonyTranspiler] private static IEnumerable<CodeInstruction> AddItemToRowTranspiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator) { return TranspilerChangeProductTransferCount(instructions, generator, RowActionType.Add); } [HarmonyPatch("RemoveItemFromRow")] [HarmonyTranspiler] private static IEnumerable<CodeInstruction> RemoveItemFromRowTranspiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator) { return TranspilerChangeProductTransferCount(instructions, generator, RowActionType.Remove); } private static IEnumerable<CodeInstruction> TranspilerChangeProductTransferCount(IEnumerable<CodeInstruction> instructions, ILGenerator generator, RowActionType rowActionType) { //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) List<CodeInstruction> list = instructions.ToList(); int num = -1; int num2 = -1; FieldInfo extraParameter2 = AccessTools.Field(typeof(PlayerNetwork), "extraParameter2"); int num3 = list.FindLastIndex((CodeInstruction instruction) => CodeInstructionExtensions.LoadsField(instruction, extraParameter2, false)); if (num3 < 0) { throw new TranspilerDefaultMsgException("Couldnt find the instruction loading the value of the field extraParameter2"); } int num4 = list.FindLastIndex(num3, num3, (CodeInstruction instruction) => instruction.opcode == OpCodes.Br || instruction.opcode == OpCodes.Br_S); if (num4 < 0) { throw new TranspilerDefaultMsgException($"Couldnt find the Br/Br_S instruction when looking up to IL line {num3}"); } Label endElseLabel = (Label)list[num4].operand; int num5 = list.Count - 1; int num6 = list.FindLastIndex(num5, num5 - num3, (CodeInstruction instruction) => instruction.labels.FirstOrDefault() == endElseLabel); if (num6 < 0) { throw new TranspilerDefaultMsgException($"Couldnt find IL Label {endElseLabel} when searching from IL line {num3}"); } num = num4 + 1; num2 = num6 - 1; ReplaceConstantsWithCallResult(list, generator, num, num2, rowActionType); return list; } private static void ReplaceConstantsWithCallResult(List<CodeInstruction> instrList, ILGenerator generator, int indexStart, int indexEnd, RowActionType rowActionType) { //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) List<CodeInstruction> list = CreateILCall_GetNumTransferItems(instrList, indexStart, indexEnd, rowActionType); LocalBuilder localBuilder = generator.DeclareLocal(typeof(int)); CodeInstruction value = CodeInstructionNew.LoadLocal(localBuilder.LocalIndex, false); CodeInstructionExtensions.MoveLabelsTo(instrList[indexStart], list[0]); if (rowActionType == RowActionType.Add) { if (instrList[indexStart + 3].operand == null || !instrList[indexStart + 3].operand.ToString().Contains("AchievementPoint")) { throw new TranspilerDefaultMsgException($"The call to a method containing the text \"AchievementPoint\" wasnt found at the expected line {indexStart + 3}"); } if (instrList[indexStart + 2].opcode != OpCodes.Ldc_I4_1) { throw new TranspilerDefaultMsgException($"The opcode Ldc_I4_1 for achievements wasnt found at the expected line {indexStart + 2}."); } instrList[indexStart + 2] = value; instrList.InsertRange(indexEnd, instrList.GetRange(indexStart, 4)); instrList.RemoveRange(indexStart, 4); } int num = instrList.FindIndex(indexStart, indexEnd - indexStart, (CodeInstruction instruction) => instruction.opcode == OpCodes.Ldc_I4_1); if (num == -1) { throw new TranspilerDefaultMsgException("Couldnt find first line with Ldc_I4_1 to replace with our new local variable numItemsTransfer"); } int num2 = instrList.FindIndex(num + 1, indexEnd - num + 1, (CodeInstruction instruction) => instruction.opcode == OpCodes.Ldc_I4_1); if (num2 == -1) { throw new TranspilerDefaultMsgException("Couldnt find second line with Ldc_I4_1 to replace with our new local variable numItemsTransfer"); } instrList.InsertRange(indexStart, list); instrList.Insert(indexStart + list.Count, CodeInstructionNew.StoreLocal(localBuilder.LocalIndex)); num += list.Count + 1; num2 += list.Count + 1; instrList[num] = value; instrList[num2] = value; } private static List<CodeInstruction> CreateILCall_GetNumTransferItems(List<CodeInstruction> instrList, int indexStart, int indexEnd, RowActionType rowActionType) { //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Expected O, but got Unknown List<CodeInstruction> list = new List<CodeInstruction>(); List<CodeInstruction> boxCountILInstructions = GetBoxCountILInstructions(instrList, indexStart, indexEnd); switch (rowActionType) { case RowActionType.Add: list.AddRange(GetILParametersAddingItems(instrList, boxCountILInstructions)); break; case RowActionType.Remove: list.AddRange(GetILParametersRemovingItems(instrList, boxCountILInstructions)); break; default: throw new TranspilerDefaultMsgException($"Initial RowActionType: {rowActionType}"); } list.Add(new CodeInstruction(OpCodes.Ldc_I4_S, (object)0)); list.Add(Transpilers.EmitDelegate<Func<int, int, int, CharacterType, int>>((Func<int, int, int, CharacterType, int>)GetNumTransferItems)); return list; } private static List<CodeInstruction> GetILParametersAddingItems(List<CodeInstruction> instrList, List<CodeInstruction> getBoxCountILInstr) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) List<CodeInstruction> list = new List<CodeInstruction>(); int num = instrList.FindIndex((CodeInstruction instruction) => instruction.opcode == OpCodes.Ldstr && instruction.operand.ToString() == "message1"); if (num == -1) { throw new TranspilerDefaultMsgException("Couldnt find the line loading the string \"message1\"."); } int num2 = instrList.FindLastIndex(num, (CodeInstruction instruction) => CodeInstructionExtensions.IsLdloc(instruction, (LocalBuilder)null)); if (num2 == -1) { throw new TranspilerDefaultMsgException("Couldnt find the line loading the local var \"num3\"."); } int index = num2 - 1; int num3 = ILExtensionMethods.LocalIndex(instrList[num2]); int num4 = ILExtensionMethods.LocalIndex(instrList[index]); list.AddRange(getBoxCountILInstr); list.Add(CodeInstructionNew.LoadLocal(num4, false)); list.Add(CodeInstructionNew.LoadLocal(num3, false)); return list; } private static List<CodeInstruction> GetILParametersRemovingItems(List<CodeInstruction> instrList, List<CodeInstruction> getBoxCountILInstr) { //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_0135: Unknown result type (might be due to invalid IL or missing references) //IL_0150: Unknown result type (might be due to invalid IL or missing references) List<CodeInstruction> list = new List<CodeInstruction>(); int num = -1; int num2 = 0; int num3 = int.MinValue; FieldInfo fieldInfo = AccessTools.Field(typeof(Data_Container), "productInfoArray"); FieldInfo fieldInfo2 = AccessTools.Field(typeof(Data_Product), "maxItemsPerBox"); if (fieldInfo == null) { throw new TranspilerDefaultMsgException("The field Data_Container.productInfoArray could not be found."); } if (fieldInfo2 == null) { throw new TranspilerDefaultMsgException("The field Data_Product.maxItemsPerBox could not be found."); } int num4 = -1; int num5 = -1; for (int i = 0; i < instrList.Count; i++) { CodeInstruction val = instrList[i]; if (num2 < 2 && CodeInstructionExtensions.LoadsField(val, fieldInfo, false)) { num2++; if (num2 == 2) { num3 = i + 1; } } else if (num5 == -1 && i >= num3 && i < num3 + 10 && CodeInstructionExtensions.IsStloc(val, (LocalBuilder)null)) { num5 = ILExtensionMethods.LocalIndex(val); } else if (num < 0 && CodeInstructionExtensions.LoadsField(val, fieldInfo2, false)) { num = i + 1; } else if (i == num && CodeInstructionExtensions.IsStloc(val, (LocalBuilder)null)) { num4 = ILExtensionMethods.LocalIndex(val); } } if (num2 < 2) { throw new TranspilerDefaultMsgException($"Couldnt reach the appropiate amount of productInfoArray usages (productInfoArrayInstance = {num2})"); } if (num5 == -1) { throw new TranspilerDefaultMsgException($"Couldnt find the num3 local var reference (num3IndexBeginSearch = {num3})"); } if (num4 == -1) { throw new TranspilerDefaultMsgException($"Couldnt find the maxItemsPerBox local var reference (maxItemsPerBoxLocalVarIndex = {num})"); } list.Add(CodeInstructionNew.LoadLocal(num5, false)); list.AddRange(getBoxCountILInstr); list.Add(CodeInstructionNew.LoadLocal(num4, false)); return list; } private static List<CodeInstruction> GetBoxCountILInstructions(List<CodeInstruction> instrList, int indexStart, int indexEnd) { //IL_0044: Unknown result type (might be due to invalid IL or missing references) int num = instrList.FindIndex(indexStart, indexEnd - indexStart, (CodeInstruction instruction) => instruction.opcode == OpCodes.Dup); if (num == -1) { throw new TranspilerDefaultMsgException($"Couldnt find the Dup instruction between: (indexStart = {indexStart}, indexEnd = {indexEnd})"); } return new List<CodeInstruction>((IEnumerable<CodeInstruction>)(object)new CodeInstruction[2] { instrList[num - 1], instrList[num + 1] }); } } } namespace SuperQoLity.SuperMarket.Patches.Misc { public class CheckoutAutoClickScanner : FullyAutoPatchedInstance { [CompilerGenerated] private sealed class <CreateProductObject>d__19 : IEnumerator<object>, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public IEnumerator result; public ProductCheckoutSpawn __instance; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <CreateProductObject>d__19(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { switch (<>1__state) { default: return false; case 0: <>1__state = -1; break; case 1: <>1__state = -1; break; } if (result.MoveNext()) { <>2__current = result.Current; <>1__state = 1; return true; } __instance.isFinished = 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 LayerMask interactableMask = LayerMask.op_Implicit(-1); private FixedCapacityUniqueQueue<ProductCheckoutSpawn> checkoutItemQueue; private static readonly int MaxItemsQueue = 2; private float nextCheckoutProductPickTime; private float nextCheckoutProductQueueTime; private float DequeueTimeLimit; private static readonly float CheckoutProductPickInterval = 0.175f; private static readonly float CheckoutProductQueueInterval = 0.025f; private static readonly float NoCashierPermissionDelay = 0.5f; private static readonly float AllowedDequeueTimeSinceLastRaycast = 0.2f; private static readonly float NoHitDelay = CheckoutProductQueueInterval; private bool wasButtonDown; public override bool IsAutoPatchEnabled => ModConfig.Instance.EnableMiscPatches.Value; public override string ErrorMessageOnAutoPatchFail { get; protected set; } = "SuperQoLity - Hold click to scan checkout products patch failed. Disabled."; public override void OnPatchFinishedVirtual(bool IsPatchActive) { if (IsPatchActive) { checkoutItemQueue = new FixedCapacityUniqueQueue<ProductCheckoutSpawn>(MaxItemsQueue, true); ModConfig.Instance.EnableCheckoutAutoClicker.SettingChanged += delegate { ManageState(); }; ManageState(); } } [IteratorStateMachine(typeof(<CreateProductObject>d__19))] [HarmonyPatch(typeof(ProductCheckoutSpawn), "CreateProductObject")] [HarmonyPostfix] public static IEnumerator CreateProductObject(IEnumerator result, ProductCheckoutSpawn __instance) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <CreateProductObject>d__19(0) { result = result, __instance = __instance }; } private void ManageState() { if (ModConfig.Instance.EnableCheckoutAutoClicker.Value) { WorldState.OnFPControllerStarted += InitState; InputBehaviour.RegisterClickAction<CheckoutAutoClickScanner>(ProcessCheckoutProductPickup, GameWorldEvent.FPControllerStarted); } else { WorldState.OnFPControllerStarted -= InitState; InputBehaviour.UnregisterClickAction<CheckoutAutoClickScanner>(); } if (WorldState.IsGameWorldAtOrAfter(GameWorldEvent.FPControllerStarted)) { InitState(); } } private void InitState() { //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) SetVanillaCheckoutScanState(!ModConfig.Instance.EnableCheckoutAutoClicker.Value); if (LayerMask.op_Implicit(interactableMask) < 0) { interactableMask = SMTComponentInstances.PlayerNetworkInstance().interactableMask; } } private static void SetVanillaCheckoutScanState(bool enable) { ChangeVanillaClickEnabled(NPC_Manager.Instance.productCheckoutPrefab, enable); ChangeClickAllSpawnProductsInScene(enable); } private static void ChangeClickAllSpawnProductsInScene(bool enable) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) if (!WorldState.IsGameWorldAtOrAfter(GameWorldEvent.LoadingWorld)) { return; } Scene activeScene = SceneManager.GetActiveScene(); ProductCheckoutSpawn val = default(ProductCheckoutSpawn); foreach (GameObject item in from g in ((Scene)(ref activeScene)).GetRootGameObjects() where g.TryGetComponent<ProductCheckoutSpawn>(ref val) select g) { ChangeVanillaClickEnabled(item, enable); } } private static void ChangeVanillaClickEnabled(GameObject spawnObject, bool enable) { ((Behaviour)((IEnumerable<PlayMakerFSM>)((Component)spawnObject.GetComponent<ProductCheckoutSpawn>()).GetComponents<PlayMakerFSM>()).FirstOrDefault((Func<PlayMakerFSM, bool>)((PlayMakerFSM fsm) => fsm.FsmName == "Behaviour"))).enabled = enable; } public void ProcessCheckoutProductPickup(float currentTime, Player mainPlayerControl) { //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) int mainActionId = KeyActions.MainActionId; if (wasButtonDown && mainPlayerControl.GetButtonUp(mainActionId)) { wasButtonDown = false; ((Queue<ProductCheckoutSpawn>)(object)checkoutItemQueue).Clear(); nextCheckoutProductQueueTime = 0f; nextCheckoutProductPickTime = 0f; } if (nextCheckoutProductQueueTime > currentTime || !mainPlayerControl.GetButton(mainActionId)) { return; } wasButtonDown = true; nextCheckoutProductQueueTime = currentTime + NoHitDelay; RaycastHit val = default(RaycastHit); ProductCheckoutSpawn productBelt = default(ProductCheckoutSpawn); if (!((Component)FirstPersonController.Instance).GetComponent<PlayerPermissions>().RequestCP()) { nextCheckoutProductQueueTime = currentTime + NoCashierPermissionDelay; } else if (Physics.Raycast(((Component)Camera.main).transform.position, ((Component)Camera.main).transform.forward, ref val, 4f, LayerMask.op_Implicit(interactableMask)) && ((Component)((RaycastHit)(ref val)).transform).TryGetComponent<ProductCheckoutSpawn>(ref productBelt)) { DequeueTimeLimit = currentTime + AllowedDequeueTimeSinceLastRaycast; CheckoutProductAimed(currentTime, productBelt); } else if (nextCheckoutProductPickTime < currentTime && ((Queue<ProductCheckoutSpawn>)(object)checkoutItemQueue).Count > 0) { if (DequeueTimeLimit >= currentTime) { PerformCheckoutProductClick(((Queue<ProductCheckoutSpawn>)(object)checkoutItemQueue).Dequeue(), currentTime); } else { ((Queue<ProductCheckoutSpawn>)(object)checkoutItemQueue).Clear(); } } } private void CheckoutProductAimed(float currentTime, ProductCheckoutSpawn productBelt) { ProductCheckoutSpawn val = default(ProductCheckoutSpawn); if (nextCheckoutProductPickTime < currentTime) { PerformCheckoutProductClick(productBelt, currentTime); ((Queue<ProductCheckoutSpawn>)(object)checkoutItemQueue).Clear(); } else if (!productBelt.isFinished && checkoutItemQueue.TryEnqueue(productBelt, ref val)) { nextCheckoutProductQueueTime = currentTime + CheckoutProductQueueInterval; } } private void PerformCheckoutProductClick(ProductCheckoutSpawn productBelt, float currentTime) { productBelt.isFinished = true; if (((NetworkBehaviour)productBelt).netId == 0) { TimeLogger.Logger.LogTimeWarning("Attempted to scan product with netId 0. Skipping.", (LogCategories)int.MinValue); return; } productBelt.CmdAddProductValueToCheckout(); nextCheckoutProductPickTime = currentTime + CheckoutProductPickInterval; } } public class ExpandProductOrderClickArea : FullyAutoPatchedInstance { public override bool IsAutoPatchEnabled => ModConfig.Instance.EnableMiscPatches.Value; public override string ErrorMessageOnAutoPatchFail { get; protected set; } = "SuperQoLity - Increased clickable area in product order patch failed. Disabled."; public override void OnPatchFinishedVirtual(bool IsPatchActive) { if (IsPatchActive) { ManageState(); ModConfig.Instance.EnableExpandedProdOrderClickArea.SettingChanged += delegate { ManageState(); }; } } private void ManageState() { if (ModConfig.Instance.EnableExpandedProdOrderClickArea.Value) { WorldState.OnFPControllerStarted += ExpandOrderClickableArea; if (WorldState.IsGameWorldAtOrAfter(GameWorldEvent.FPControllerStarted) && IsOrderUiPrefabLoaded(out var managerBlackboard)) { ExpandOrderClickableArea(managerBlackboard); } } else { WorldState.OnFPControllerStarted -= ExpandOrderClickableArea; if (WorldState.IsGameWorldAtOrAfter(GameWorldEvent.FPControllerStarted) && IsOrderUiPrefabLoaded(out var managerBlackboard2)) { RestoreOrderClickableArea(managerBlackboard2); } } } private void ExpandOrderClickableArea() { if (!IsOrderUiPrefabLoaded(out var managerBlackboard)) { TimeLogger.Logger.LogTimeError("ManagerBlackboard.UIShopItemPrefab should be instanced by now but its not.", (LogCategories)2048); } else { ExpandOrderClickableArea(managerBlackboard); } } private void ExpandOrderClickableArea(ManagerBlackboard managerBlackboard) { //IL_0016: Unknown result type (might be due to invalid IL or missing references) SetButtonClickableArea(managerBlackboard, new Vector4(-175f, -5f, -3f, -50f)); GameObject gameObject = ((Component)managerBlackboard.UIShopItemPrefab.transform.Find("ContainerTypeBCK")).gameObject; ((Graphic)gameObject.GetComponent<Image>()).raycastTarget = false; ((Graphic)((Component)gameObject.transform.Find("ContainerImage")).GetComponent<Image>()).raycastTarget = false; } private void RestoreOrderClickableArea(ManagerBlackboard managerBlackboard) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) SetButtonClickableArea(managerBlackboard, Vector4.zero); } private void SetButtonClickableArea(ManagerBlackboard managerBlackboard, Vector4 padding) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) ((Graphic)((Component)managerBlackboard.UIShopItemPrefab.transform.Find("AddButton")).GetComponent<Image>()).raycastPadding = padding; } private bool IsOrderUiPrefabLoaded(out ManagerBlackboard managerBlackboard) { managerBlackboard = null; if ((Object)(object)GameData.Instance != (Object)null && ((Component)GameData.Instance).TryGetComponent<ManagerBlackboard>(ref managerBlackboard)) { return (Object)(object)managerBlackboard.UIShopItemPrefab != (Object)null; } return false; } } public class PricingGunFixPatch : FullyAutoPatchedInstance { public override bool IsAutoPatchEnabled => ModConfig.Instance.EnableMiscPatches.Value; public override string ErrorMessageOnAutoPatchFail { get; protected set; } = "SuperQoLity - Pricing Gun price patch failed. Disabled."; [HarmonyPatch(typeof(NPC_Manager), "CustomerNPCControl")] [HarmonyTranspiler] public static IEnumerable<CodeInstruction> CustomerNPCControlTranspiler(IEnumerable<CodeInstruction> instructions, ILGenerator generator) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Expected O, but got Unknown //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Expected O, but got Unknown //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c5: Expected O, but got Unknown //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Expected O, but got Unknown //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) CodeMatcher codeMatcher = new CodeMatcher(instructions, (ILGenerator)null); MethodInfo removeAtMethod = typeof(List<int>).GetMethod("RemoveAt"); if (MoveToRemoveAt(useEnd: false).IsInvalid) { throw new TranspilerDefaultMsgException("IL assigning customer max buy price before line \"component.productsIDToBuy.RemoveAt(0);\" could not be found."); } object num3_Operand = codeMatcher.Instruction.operand; codeMatcher.MatchBack(true, (CodeMatch[])(object)new CodeMatch[1] { new CodeMatch((Func<CodeInstruction, bool>)((CodeInstruction inst) => inst.opcode == OpCodes.Ret), (string)null) }).MatchForward(true, (CodeMatch[])(object)new CodeMatch[1] { new CodeMatch((Func<CodeInstruction, bool>)((CodeInstruction inst) => CodeInstructionExtensions.IsStloc(inst, (LocalBuilder)null)), (string)null) }); if (codeMatcher.IsInvalid) { throw new TranspilerDefaultMsgException("IL finding \"num\" productId var could not be found."); } int num = ILExtensionMethods.LocalIndex(codeMatcher.Instruction); MoveToRemoveAt(useEnd: true).MatchForward(true, (CodeMatch[])(object)new CodeMatch[1] { new CodeMatch((Func<CodeInstruction, bool>)((CodeInstruction inst) => CodeInstructionExtensions.IsLdloc(inst, (LocalBuilder)null) && inst.operand == num3_Operand), (string)null) }); if (codeMatcher.IsInvalid) { throw new TranspilerDefaultMsgException("IL loading num3 value into the stack could not be found."); } codeMatcher.SetInstructionAndAdvance(CodeInstructionNew.LoadLocal(num, false)).Insert((CodeInstruction[])(object)new CodeInstruction[1] { Transpilers.EmitDelegate<Func<int, float>>((Func<int, float>)GetRandomizedMarketPrice) }); return codeMatcher.InstructionEnumeration(); CodeMatcher MoveToRemoveAt(bool useEnd) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Expected O, but got Unknown //IL_0057: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Expected O, but got Unknown //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Expected O, but got Unknown //IL_00a7: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Expected O, but got Unknown //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00c2: Expected O, but got Unknown return codeMatcher.MatchForward(useEnd, (CodeMatch[])(object)new CodeMatch[5] { new CodeMatch((Func<CodeInstruction, bool>)((CodeInstruction inst) => CodeInstructionExtensions.IsStloc(inst, (LocalBuilder)null)), (string)null), new CodeMatch((Func<CodeInstruction, bool>)((CodeInstruction inst) => CodeInstructionExtensions.IsLdloc(inst, (LocalBuilder)null)), (string)null), new CodeMatch((Func<CodeInstruction, bool>)((CodeInstruction inst) => inst.opcode == OpCodes.Ldfld), (string)null), new CodeMatch((Func<CodeInstruction, bool>)((CodeInstruction inst) => CodeInstructionExtensions.LoadsConstant(inst)), (string)null), new CodeMatch((Func<CodeInstruction, bool>)((CodeInstruction inst) => CodeInstructionExtensions.Calls(inst, removeAtMethod)), (string)null) }); } } private static float GetRandomizedMarketPrice(int productID) { Data_Product component = ProductListing.Instance.productPrefabs[productID].GetComponent<Data_Product>(); float num = component.basePricePerUnit * ProductListing.Instance.tierInflation[component.productTier]; if (ModConfig.Instance.EnablePriceGunFix.Value) { num = RoundPrice(num); } return num * Random.Range(2f, 2.5f); } private static float RoundPrice(float value) { return Mathf.Round(value * 100f) / 100f; } } public class SharedSavePatch : FullyAutoPatchedInstance { public override bool IsAutoPatchEnabled => Plugin.IsSolutionInDebugMode; public override string ErrorMessageOnAutoPatchFail { get; protected set; } = "SuperQoLity - TestAndDebugPatch FAILED. Disabled"; public override void OnPatchFinishedVirtual(bool IsPatchActive) { if (IsPatchActive) { KeyPressDetection.AddHotkey((KeyCode)267, (KeyPressAction)0, 1000, (Action)delegate { SaveAsClientX(); }); } } public static async void SaveAsClientX() { LOG.TEMPWARNING("Save started", true); if ((Object)(object)NetworkManager.singleton == (Object)null) { TimeLogger.Logger.LogTimeWarning("You can only save in a loaded world.", (LogCategories)int.MinValue); } if (((Component)GameData.Instance).GetComponent<NetworkSpawner>().isSaving) { TimeLogger.Logger.LogTimeWarning("Saving is already in progress.", (LogCategories)int.MinValue); return; } _ = NetworkManager.singleton.mode; _ = 2; string loadedSaveFileName = FsmVariables.GlobalVariables.GetFsmString("CurrentFilename").Value; string value = "StoreFile12.es3"; LOG.TEMPWARNING("CurrentFilename: " + loadedSaveFileName + " - City save name (host only) " + GetCityName(loadedSaveFileName), true); FsmVariables.GlobalVariables.GetFsmString("CurrentFilename").Value = value; await SavePersistentValues(); await SavePropsCoroutine(); FsmVariables.GlobalVariables.GetFsmString("CurrentFilename").Value = loadedSaveFileName; } private static string GetCityName(string loadedSaveFileName) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Expected O, but got Unknown //IL_0019: Unknown result type (might be due to invalid IL or missing references) string text = Path.Combine(Application.persistentDataPath, loadedSaveFileName); ES3Settings val = new ES3Settings((EncryptionType)1, "g#asojrtg@omos)^yq"); return new ES3File(text, val, false).Load<string>("StoreName", (string)null); } private static async Task SavePersistentValues() { LOG.TEMPDEBUG("1. " + FsmVariables.GlobalVariables.GetFsmString("CurrentFilename").Value, true); PlayMakerFSM fsm = GameData.Instance.SaveOBJ.GetComponent<PlayMakerFSM>(); fsm.FsmVariables.GetFsmBool("IsSaving").Value = true; LOG.TEMPDEBUG("2. " + FsmVariables.GlobalVariables.GetFsmString("CurrentFilename").Value, true); fsm.SendEvent("Send_Data"); LOG.TEMPDEBUG("3. " + FsmVariables.GlobalVariables.GetFsmString("CurrentFilename").Value, true); while (fsm.FsmVariables.GetFsmBool("IsSaving").Value) { LOG.TEMPDEBUG("4. " + FsmVariables.GlobalVariables.GetFsmString("CurrentFilename").Value, true); await Task.Delay(10); LOG.TEMPDEBUG("5. " + FsmVariables.GlobalVariables.GetFsmString("CurrentFilename").Value, true); } } public static async Task SavePropsCoroutine() { NetworkSpawner instance = GameObject.Find("GameDataManager").GetComponent<NetworkSpawner>(); instance.isSaving = true; ((Component)((Component)GameCanvas.Instance).transform.Find("SavingContainer")).gameObject.SetActive(true); await Task.Delay(500); int num = 0; string value = FsmVariables.GlobalVariables.GetFsmString("CurrentFilename").Value; string text = Application.persistentDataPath + "/" + value; LOG.TEMPWARNING("We will save props in file \"" + value + "\"", true); ES3Settings val = new ES3Settings((EncryptionType)1, "g#asojrtg@omos)^yq"); ES3.CacheFile(text, val); ES3Settings val2 = new ES3Settings(text, new Enum[1] { (Enum)(object)(Location)4 }); CultureInfo cultureInfo = new CultureInfo(Thread.CurrentThread.CurrentCulture.Name); if (cultureInfo.NumberFormat.NumberDecimalSeparator != ",") { cultureInfo.NumberFormat.NumberDecimalSeparator = ","; Thread.CurrentThread.CurrentCulture = cultureInfo; } Quaternion rotation; for (int i = 0; i < 4; i++) { GameObject gameObject = ((Component)instance.levelPropsOBJ.transform.GetChild(i)).gameObject; if (gameObject.transform.childCount != 0) { LOG.TEMPWARNING($"Saving {gameObject.transform.childCount} levelPropsOBJ objects for index {i}.", true); for (int j = 0; j < gameObject.transform.childCount; j++) { GameObject gameObject2 = ((Component)gameObject.transform.GetChild(j)).gameObject; string[] obj = new string[11] { i.ToString(), "|", gameObject2.GetComponent<Data_Container>().containerID.ToString(), "|", gameObject2.transform.position.x.ToString(), "|", gameObject2.transform.position.y.ToString(), "|", gameObject2.transform.position.z.ToString(), "|", null }; rotation = gameObject2.transform.rotation; obj[10] = ((Quaternion)(ref rotation)).eulerAngles.y.ToString(); string text2 = string.Concat(obj); ES3.Save<string>("propdata" + num, text2, text, val2); string text3 = "propinfoproduct" + num; int[] productInfoArray = gameObject2.GetComponent<Data_Container>().productInfoArray; ES3.Save<int[]>(text3, productInfoArray, text, val2); num++; } } } for (int k = num; (float)k < float.PositiveInfinity; k++) { string text4 = "propdata" + num; if (!ES3.KeyExists(text4, text, val2)) { break; } ES3.DeleteKey(text4, text, val2); } num = 0; int num2 = 0; GameObject gameObject3 = ((Component)instance.levelPropsOBJ.transform.GetChild(7)).gameObject; for (int l = 0; (float)l < float.PositiveInfinity; l++) { string text5 = "decopropdata" + num2; if (!ES3.KeyExists(text5, text, val2)) { break; } ES3.DeleteKey(text5, text, val2); num2++; } LOG.TEMPWARNING($"Saving {gameObject3.transform.childCount} decorative objects.", true); for (int m = 0; m < gameObject3.transform.childCount; m++) { GameObject gameObject4 = ((Component)gameObject3.transform.GetChild(m)).gameObject; string[] obj2 = new string[10] { "7|", gameObject4.GetComponent<BuildableInfo>().decorationID.ToString(), "|", gameObject4.transform.position.x.ToString(), "|", gameObject4.transform.position.y.ToString(), "|", gameObject4.transform.position.z.ToString(), "|", null }; rotation = gameObject4.transform.rotation; obj2[9] = ((Quaternion)(ref rotation)).eulerAngles.y.ToString(); string text6 = string.Concat(obj2); ES3.Save<string>("decopropdata" + num, text6, text, val2); if (gameObject4.GetComponent<BuildableInfo>().decorationID == 4) { string text7 = "decopropdataextra" + num; string text8 = gameObject4.GetComponent<DecorationExtraData>().intValue + "|" + gameObject4.GetComponent<DecorationExtraData>().stringValue; ES3.Save<string>(text7, text8, text, val2); } if (Object.op_Implicit((Object)(object)gameObject4.GetComponent<PaintableDecoration>())) { string text9 = "decopaintabledata" + num; string text10 = gameObject4.GetComponent<PaintableDecoration>().mainValue + "|" + gameObject4.GetComponent<PaintableDecoration>().secondaryValue; ES3.Save<string>(text9, text10, text, val2); } num++; } ES3.StoreCachedFile(text, val); await Task.Delay(20); ((Component)((Component)GameCanvas.Instance).transform.Find("SavingContainer")).gameObject.SetActive(false); LOG.TEMPWARNING("Prop saving finished.", true); instance.isSaving = false; } } } namespace SuperQoLity.SuperMarket.Patches.EmployeeModule { public enum EmployeeJob { Unassigned = 0, Cashier = 1, Restocker = 2, Storage = 3, Security = 4, Technician = 5, OnlineOrder = 6, Manufacturer = 7, Any = 99 } public enum EnumSecurityPickUp { [Description("Disabled")] Disabled, [Description("Reduced")] Reduced, [Description("Normal")] Normal, [Description("Always Maxed")] AlwaysMaxed } public enum EnumSecurityEmployeeThiefChase { [Description("Disabled")] Disabled, [Description("AllChaseButLastOne")] AllChaseButLastOne, [Description("OnlyOnePerThief")] OnlyOnePerThief } public class EmployeeJobAIPatch : FullyAutoPatchedInstance { [CompilerGenerated] private sealed class <PickupMarkedStolenProductsLoop>d__27 : IEnumerator<object>, IDisposable, IEnumerator { private int <>1__state; private object <>2__current; public EmployeeJobAIPatch <>4__this; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <PickupMarkedStolenProductsLoop>d__27(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { int num = <>1__state; EmployeeJobAIPatch employeeJobAIPatch = <>4__this; if (num != 0) { if (num != 1) { return false; } <>1__state = -1; if (WorldState.CurrentGameWorldState == GameWorldEvent.QuitOrMenu) { stolenProdPickups.Clear(); return false; } } else { <>1__state = -1; } employeeJobAIPatch.destroyCounter += 5f; StolenProductSpawn val = default(StolenProductSpawn); while (employeeJobAIPatch.destroyCounter >= 1f) { employeeJobAIPatch.destroyCounter -= 1f; if (stolenProdPickups.Count <= 0) { break; } if (Extensions.TryDequeue<StolenProductSpawn>(stolenProdPickups, ref val)) { NetworkServer.UnSpawn(((Component)val).gameObject); } } <>2__current = null; <>1__state = 1; return true; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } public const bool LogEmployeeActions = false; private float destroyCounter; private static Queue<StolenProductSpawn> stolenProdPickups; public override bool IsAutoPatchEnabled => ModConfig.Instance.EnableEmployeeChanges.Value; public override string ErrorMessageOnAutoPatchFail { get; protected set; } = "SuperQoLity - Employee patch failed. Employee Module inactive"; public static int NumTransferItemsBase { get; } = 1; public static int MaxSecurityPickUpLevel { get; } = 200; public static float SecurityPickUpRangeLevelMult { get; } = 1.25f; public static int SecurityPickUpLayer { get; } = 25; public static float LevelsForExtraPickUp { get; } = 10f; [HarmonyPatch(typeof(NPC_Manager), "Awake")] [HarmonyPostfix] public static void AwakePatch(NPC_Manager __instance) { stolenProdPickups = new Queue<StolenProductSpawn>(); GenericNPC.AddSuperQolNpcObjects(__instance.npcAgentPrefab, NPCType.Employee); GameObject npcAgentPrefab = __instance.npcAgentPrefab; GameObject val = ((npcAgentPrefab == null) ? null : npcAgentPrefab.GetComponent<NPC_Info>()?.stolenProductPrefab); if (!Object.op_Implicit((Object)(object)val)) { TimeLogger.Logger.LogTimeFatal("An object in the chain \"NPC_Manager.npcAgentPrefab.NPC_Info.stolenProductPrefab\" is null. It was probably renamed of changed places. The employee patches cant be used and will be disabled.", (LogCategories)134217728); ((AutoPatchedInstanceBase)Container<EmployeeJobAIPatch>.Instance).UnpatchInstance(); TimeLogger.Logger.SendMessageNotificationError("Something changed due to a game update and the employee module can no longer work. It was disabled so the rest of the mod can still work"); } else { val.layer = SecurityPickUpLayer; ((MonoBehaviour)__instance).StartCoroutine(Container<EmployeeJobAIPatch>.Instance.PickupMarkedStolenProductsLoop()); } } public static bool EmployeeNPCControl(NPC_Manager __instance, GameObject employeeObj, NPC_Info employee) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_27ae: Unknown result type (might be due to invalid IL or missing references) //IL_0672: Unknown result type (might be due to invalid IL or missing references) //IL_0682: Unknown result type (might be due to invalid IL or missing references) //IL_0ed6: Unknown result type (might be due to invalid IL or missing references) //IL_0efe: Unknown result type (might be due to invalid IL or missing references) //IL_0f03: Unknown result type (might be due to invalid IL or missing references) //IL_0f08: Unknown result type (might be due to invalid IL or missing references) //IL_0f14: Unknown result type (might be due to invalid IL or missing references) //IL_1339: Unknown result type (might be due to invalid IL or missing references) //IL_133e: Unknown result type (might be due to invalid IL or missing references) //IL_1585: Unknown result type (might be due to invalid IL or missing references) //IL_15ad: Unknown result type (might be due to invalid IL or missing references) //IL_15b2: Unknown result type (might be due to invalid IL or missing references) //IL_15b7: Unknown result type (might be due to invalid IL or missing references) //IL_15c3: Unknown result type (might be due to invalid IL or missing references) //IL_167c: Unknown result type (might be due to invalid IL or missing references) //IL_168c: Unknown result type (might be due to invalid IL or missing references) //IL_2583: Unknown result type (might be due to invalid IL or missing references) //IL_258e: Unknown result type (might be due to invalid IL or missing references) //IL_1f9b: Unknown result type (might be due to invalid IL or missing references) //IL_1fab: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_11e2: Unknown result type (might be due to invalid IL or missing references) //IL_1201: Unknown result type (might be due to invalid IL or missing references) //IL_120b: Unknown result type (might be due to invalid IL or missing references) //IL_121d: Unknown result type (might be due to invalid IL or missing references) //IL_1237: Unknown result type (might be due to invalid IL or missing references) //IL_1246: Unknown result type (might be due to invalid IL or missing references) //IL_2525: Unknown result type (might be due to invalid IL or missing references) //IL_02f7: Unknown result type (might be due to invalid IL or missing references) //IL_0814: Unknown result type (might be due to invalid IL or missing references) //IL_0824: Unknown result type (might be due to invalid IL or missing references) //IL_07d8: Unknown result type (might be due to invalid IL or missing references) //IL_07dd: Unknown result type (might be due to invalid IL or missing references) //IL_07e0: Unknown result type (might be due to invalid IL or missing references) //IL_1119: Unknown result type (might be due to invalid IL or missing references) //IL_1134: Unknown result type (might be due to invalid IL or missing references) //IL_113e: Unknown result type (might be due to invalid IL or missing references) //IL_18ea: Unknown result type (might be due to invalid IL or missing references) //IL_18f1: Unknown result type (might be due to invalid IL or missing references) //IL_1b5a: Unknown result type (might be due to invalid IL or missing references) //IL_1b9e: Unknown result type (might be due to invalid IL or missing references) //IL_1bae: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_0c89: Unknown result type (might be due to invalid IL or missing references) //IL_0d72: Unknown result type (might be due to invalid IL or missing references) //IL_0d77: Unknown result type (might be due to invalid IL or missing references) //IL_0d7a: Unknown result type (might be due to invalid IL or missing references) //IL_1179: Unknown result type (might be due to invalid IL or missing references) //IL_1434: Unknown result type (might be due to invalid IL or missing references) //IL_1439: Unknown result type (might be due to invalid IL or missing references) //IL_143c: Unknown result type (might be due to invalid IL or missing references) //IL_1905: Unknown result type (might be due to invalid IL or missing references) //IL_1d6f: Unknown result type (might be due to invalid IL or missing references) //IL_1d8e: Unknown result type (might be due to invalid IL or missing references) //IL_1d98: Unknown result type (might be due to invalid IL or missing references) //IL_1daa: Unknown result type (might be due to invalid IL or missing references) //IL_1dc4: Unknown result type (might be due to invalid IL or missing references) //IL_1dd3: Unknown result type (might be due to invalid IL or missing references) //IL_09a3: Unknown result type (might be due to invalid IL or missing references) //IL_2279: Unknown result type (might be due to invalid IL or missing references) //IL_227e: Unknown result type (might be due to invalid IL or missing references) //IL_2281: Unknown result type (might be due to invalid IL or missing references) //IL_1f63: Unknown result type (might be due to invalid IL or missing references) //IL_2445: Unknown result type (might be due to invalid IL or missing references) int state = employee.state; if (employee.employeeDismissed) { if (Vector3.Distance(employeeObj.transform.position, __instance.employeeSpawnpoint.transform.position) < 2f) { NetworkServer.Destroy(employeeObj); return true; } return false; } if (__instance.employeesOnRiot) { if (employee.equippedItem > 0) { __instance.DropBoxOnGround(employee); __instance.UnequipBox(employee); } if (state != 0) { employee.state = 0; } return true; } if (state == -1) { return false; } if (!EmployeeNPC.TryGetEmployeeFrom(employeeObj, out var employeeNPC)) { return true; } int taskPriority = employee.taskPriority; if (taskPriority == 4 && state == 2) { if (Object.op_Implicit((Object)(object)employee.currentChasedThiefOBJ)) { if (employee.currentChasedThiefOBJ.transform.position.x < -15f || employee.currentChasedThiefOBJ.transform.position.x > 38f || employee.currentChasedThiefOBJ.GetComponent<NPC_Info>().productsIDCarrying.Count == 0) { employee.state = 0; return true; } if (Vector3.Distance(employeeObj.transform.position, employee.currentChasedThiefOBJ.transform.position) < 2f) { employeeNPC.MoveEmployeeTo(employeeObj, employee.currentChasedThiefOBJ); employee.state = 3; } else { employee.CallPathing(); } } else { employee.state = 0; } } NavMeshAgent component = employeeObj.GetComponent<NavMeshAgent>(); if (IsEmployeeAtDestination(component, out var stoppingDistance)) { employeeNPC.StartLookProcess(RotationSpeedMode.EmployeeTarget); switch (taskPriority) { case 0: break; case 1: switch (state) { case 0: case 1: { if (employee.equippedItem > 0) { __instance.DropBoxOnGround(employee); UnequipBox(employee); return true; } int num13 = __instance.CashierGetAvailableCheckout(); if (num13 != -1) { employee.employeeAssignedCheckoutIndex = num13; __instance.UpdateEmployeeCheckouts(); GameObject gameObject2 = ((Component)__instance.checkoutOBJ.transform.GetChild(num13)).gameObject; employeeNPC.MoveEmployeeTo(((Component)gameObject2.transform.Find("EmployeePosition")).transform.position, gameObject2); employee.state = 2; return true; } employee.state = 10; return true; } case 2: employee.RPCNotificationAboveHead("NPCemployee0", ""); employee.StartWaitState(ModConfig.Instance.EmployeeNextActionWait.Value, 3); employee.state = -1; return true; case 3: if (__instance.CheckIfCustomerInQueue(employee.employeeAssignedCheckoutIndex)) { if (!((Component)__instance.checkoutOBJ.transform.GetChild(employee.employeeAssignedCheckoutIndex)).GetComponent<Data_Container>().checkoutQueue[0]) { employee.state = 4; return true; } if (((Component)__instance.checkoutOBJ.transform.GetChild(employee.employeeAssignedCheckoutIndex)).GetComponent<Data_Container>().productsLeft > 0) { employee.state = 5; return true; } employee.state = 4; return true; } if (((Component)__instance.checkoutOBJ.transform.GetChild(employee.employeeAssignedCheckoutIndex)).GetComponent<Data_Container>().isCheckoutClosed) { employee.employeeAssignedCheckoutIndex = -1; employee.state = 0; return true; } employee.state = 4; return true; case 4: employee.StartWaitState(ModConfig.Instance.EmployeeNextActionWait.Value, 3); employee.state = -1; return true; case 5: { if (((Component)__instance.checkoutOBJ.transform.GetChild(employee.employeeAssignedCheckoutIndex)).GetComponent<Data_Container>().productsLeft == 0) { employee.state = 7; return true; } float num12 = Mathf.Clamp(__instance.productCheckoutWait - (float)employee.cashierLevel * 0.01f, 0.05f, 1f); employee.StartWaitState(num12, 6); employee.state = -1; return true; } case 6: { List<GameObject> internalProductListForEmployees = ((Component)__instance.checkoutOBJ.transform.GetChild(employee.employeeAssignedCheckoutIndex)).GetComponent<Data_Container>().internalProductListForEmployees; int num8 = employee.cashierLevel / 15; num8 = Mathf.Clamp(num8, 1, 10); int num9 = Mathf.Clamp(employee.cashierValue - num8 - 1, 2, 10); int num10 = 0; for (int m = 0; m < internalProductListForEmployees.Count; m++) { GameObject val10 = internalProductListForEmployees[m]; if (Object.op_Implicit((Object)(object)val10)) { NPC_Info obj6 = employee; obj6.cashierExperience += num9; val10.GetComponent<ProductCheckoutSpawn>().AddProductFromNPCEmployee(); num10++; if (num10 >= num8) { break; } } } employee.state = 5; return true; } case 7: { GameObject currentNPC = ((Component)__instance.checkoutOBJ.transform.GetChild(employee.employeeAssignedCheckoutIndex)).GetComponent<Data_Container>().currentNPC; if (!Object.op_Implicit((Object)(object)currentNPC)) { employee.state = 3; } if (currentNPC.GetComponent<NPC_Info>().alreadyGaveMoney) { ((Component)__instance.checkoutOBJ.transform.GetChild(employee.employeeAssignedCheckoutIndex)).GetComponent<Data_Container>().AuxReceivePayment(0f, true); employee.state = 3; return true; } float num11 = Mathf.Clamp(__instance.productCheckoutWait - (float)employee.cashierLevel * 0.01f, 0.05f, 1f); employee.StartWaitState(num11, 7); employee.state = -1; return true; } case 10: if (Vector3.Distance(((Component)employee).transform.position, __instance.restSpotOBJ.transform.position) > 3f) { employeeNPC.MoveEmployeeToRestPosition(); return true; } employee.StartWaitState(ModConfig.Instance.EmployeeIdleWait.Value, 0); employee.state = -1; return true; default: employee.state = 0; return true; } case 2: switch (state) { case 0: { LOG.TEMPDEBUG_FUNC((Func<string>)(() => "Restocker #" + GetUniqueId(employee) + " logic begin."), false); if (employee.equippedItem > 0) { LOG.TEMPDEBUG_FUNC((Func<string>)(() => "Restocker #" + GetUniqueId(employee) + ": Box in hand. Dropping."), false); __instance.DropBoxOnGround(employee); UnequipBox(employee); return true; } RestockJobInfo restockJob; bool availableRestockJob = RestockJobsManager.GetAvailableRestockJob(__instance, out restockJob); employee.SetRestockJobInfo(restockJob); if (availableRestockJob) { LOG.TEMPDEBUG_FUNC((Func<string>)(() => "Restocker #" + GetUniqueId(employee) + ": Products available, moving to storage."), false); Vector3 position2 = ((Component)__instance.storageOBJ.transform.GetChild(restockJob.Storage.ShelfIndex).Find("Standspot")).transform.position; employeeNPC.MoveEmployeeToStorage(position2, restockJob.Storage); employeeNPC.AddExtraProductShelfTarget(restockJob.ProdShelf); employee.state = 2; return true; } if (Vector3.Distance(((Component)employee).transform.position, __instance.restSpotOBJ.transform.position) > 6.5f) { LOG.TEMPDEBUG_FUNC((Func<string>)(() => "Restocker #" + GetUniqueId(employee) + ": Moving to rest spot."), false); employeeNPC.MoveEmployeeToRestPosition(); return true; } employeeNPC.ClearNPCReservations(); employee.StartWaitState(ModConfig.Instance.EmployeeIdleWait.Value, 0); employee.state = -1; return true; } case 1: return true; case 2: { LOG.TEMPDEBUG_FUNC((Func<string>)(() => "Restocker #" + GetUniqueId(employee) + ": Storage reached."), false); RestockJobInfo restockJobInfo2 = employee.GetRestockJobInfo(); if (employeeNPC.RefreshAndCheckValidTargetedStorage(__instance, clearReservation: false, out var storageSlotInfo4) && employeeNPC.RefreshAndCheckValidTargetedProductShelf(__instance, restockJobInfo2)) { LOG.TEMPDEBUG_FUNC((Func<string>)(() => "Restocker #" + GetUniqueId(employee) + ": Picking box."), false); Transform child = __instance.storageOBJ.transform.GetChild(storageSlotInfo4.ShelfIndex); Data_Container component4 = ((Component)child).GetComponent<Data_Container>(); if (Object.op_Implicit((Object)(object)child.Find("CanvasSigns"))) { component4.EmployeeUpdateArrayValuesStorage(storageSlotInfo4.SlotIndex * 2, storageSlotInfo4.ExtraData.ProductId, -1); } else { component4.EmployeeUpdateArrayValuesStorage(storageSlotInfo4.SlotIndex * 2, -1, -1); } employee.NetworkboxProductID = storageSlotInfo4.ExtraData.ProductId; employee.NetworkboxNumberOfProducts = storageSlotInfo4.ExtraData.Quantity; employee.EquipNPCItem(1); GameObject gameObject = ((Component)__instance.shelvesOBJ.transform.GetChild(restockJobInfo2.ProdShelf.ShelfIndex)).gameObject; employeeNPC.MoveEmployeeToShelf(((Component)gameObject.transform.Find("Standspot")).transform.position, restockJobInfo2.ProdShelf); employee.state = 3; return true; } LOG.TEMPDEBUG_FUNC((Func<string>)(() => "Restocker #" + GetUniqueId(employee) + ": Either storage or shelf reservations didnt match."), false); employee.StartWaitState(ModConfig.Instance.EmployeeNextActionWait.Value, 0); employee.state = -1; return true; } case 3: { LOG.TEMPDEBUG_FUNC((Func<string>)(() => "Restocker #" + GetUniqueId(employee) + ": Product shelf reached."), false); RestockJobInfo restockJobInfo3 = employee.GetRestockJobInfo(); if (((Component)__instance.shelvesOBJ.transform.GetChild(restockJobInfo3.ProdShelf.ShelfIndex)).GetComponent<Data_Container>().productInfoArray[restockJobInfo3.ShelfProdInfoIndex] == restockJobInfo3.ProdShelf.ExtraData.ProductId) { LOG.TEMPDEBUG_FUNC((Func<string>)(() => "Restocker #" + GetUniqueId(employee) + ": Shelf reached has same product as box. So far so good."), false); employee.state = 4; return true; } LOG.TEMPDEBUG_FUNC((Func<string>)(() => "Restocker #" + GetUniqueId(employee) + ": Shelf reached has now different product than box."), false); employee.state = 5; return true; } case 4: { LOG.TEMPDEBUG_FUNC((Func<string>)(() => "Restocker #" + GetUniqueId(employee) + ": Checking if shelf is valid."), false); RestockJobInfo restockJobInfo = employee.GetRestockJobInfo(); if (employeeNPC.RefreshAndCheckValidTargetedProductShelf(__instance, restockJobInfo)) { LOG.TEMPDEBUG_FUNC((Func<string>)(() => "Restocker #" + GetUniqueId(employee) + ": Shelf reached fully valid."), false); Data_Container component3 = ((Component)__instance.shelvesOBJ.transform.GetChild(restockJobInfo.ProdShelf.ShelfIndex)).GetComponent<Data_Container>(); int maxProductsPerRow = restockJobInfo.MaxProductsPerRow; int quantity = restockJobInfo.ProdShelf.ExtraData.Quantity; if (employee.NetworkboxNumberOfProducts > 0 && quantity < maxProductsPerRow) { LOG.TEMPDEBUG_FUNC((Func<string>)(() => "Restocker #" + GetUniqueId(employee) + ": Adding products to shelf row."), false); int num = Mathf.Clamp(maxProductsPerRow - quantity, NumTransferItemsBase, employee.restockerLevel); int num2 = Mathf.Clamp(employee.boxNumberOfProducts, NumTransferItemsBase, employee.restockerLevel); int numMovedProducts = Mathf.Min(num, num2); numMovedProducts = IncreasedItemTransferPatch.GetNumTransferItems(employee.boxNumberOfProducts, quantity, maxProductsPerRow, IncreasedItemTransferPatch.CharacterType.Employee, numMovedProducts); component3.EmployeeAddsItemToRow(restockJobInfo.ShelfProdInfoIndex, numMovedProducts); employee.NetworkboxNumberOfProducts = employee.boxNumberOfProducts - numMovedProducts; employee.StartWaitState(__instance.employeeItemPlaceWait, 4); employee.state = -1; NPC_Info obj2 = employee; obj2.restockerExperience += employee.restockerValue; return true; } } LOG.TEMPDEBUG_FUNC((Func<string>)(() => "Restocker #" + GetUniqueId(employee) + ": Target shelf is already full or not valid. Searching for a different one."), false); int productId = restockJobInfo.ProdShelf.ExtraData.ProductId; if (employee.NetworkboxNumberOfProducts > 0 && ContainerSearch.CheckIfProdShelfWithSameProduct(__instance, productId, employee, out (ProductShelfSlotInfo, int) result)) { LOG.TEMPDEBUG_FUNC((Func<string>)(() => "Restocker #" + GetUniqueId(employee) + ": Found another different shelf to add products."), false); employee.UpdateRestockJobInfo(result.Item1, result.Item2); employeeNPC.MoveEmployeeToShelf(result.Item1.ExtraData.Position, result.Item1); employee.state = 3; return true; } LOG.TEMPDEBUG_FUNC((Func<string>)(() => "Restocker #" + GetUniqueId(employee) + ": Box is empty or there is no other shelf with the same product."), false); employee.state = 5; return true; } case 5: { LOG.TEMPDEBUG_FUNC((Func<string>)(() => "Restocker #" + GetUniqueId(employee) + ": Box in hand but cant restock anymore. Deciding what to do."), false); if (employee.NetworkboxNumberOfProducts <= 0) { MoveToBalerOrGarbageContainer(__instance, employeeObj, employee, employeeNPC, 30, 6, 9); return true; } if (GetStorageContainerWithBoxToMerge(__instance, employee, employeeNPC)) { return true; } StorageSlotInfo freeStorageContainer = ContainerSearch.GetFreeStorageContainer(__instance, employeeObj.transform, employee.NetworkboxProductID); if (freeStorageContainer.ShelfFound) { LOG.TEMPDEBUG_FUNC((Func<string>)(() => "Restocker #" + GetUniqueId(employee) + ": Moving to storage to place box."), false); Vector3 position = ((Component)((Component)__instance.storageOBJ.transform.GetChild(freeStorageContainer.ShelfIndex)).gameObject.transform.Find("Standspot")).transform.position; employeeNPC.MoveEmployeeToStorage(position, freeStorageContainer); employee.state = 7; return true; } LOG.TEMPDEBUG_FUNC((Func<string>)(() => "Restocker #" + GetUniqueId(employee) + ": Moving to left over boxes spot."), false); employeeNPC.MoveEmployeeTo(__instance.leftoverBoxesSpotOBJ, null); employee.state = 8; return true; } case 6: LOG.TEMPDEBUG_FUNC((Func<string>)(() => "Restocker #" + GetUniqueId(employee) + ": Removing box in hand"), false); UnequipBox(employee); return true; case 7: { LOG.TEMPDEBUG_FUNC((Func<string>)(() => "Restocker #" + GetUniqueId(employee) + ": Arrived at storage, but checking again if there is some other slot to merge with."), false); if (GetStorageContainerWithBoxToMerge(__instance, employee, employeeNPC)) { return true; } LOG.TEMPDEBUG_FUNC((Func<string>)(() => "Restocker #" + GetUniqueId(employee) + ": No merge possible."), false); if (employeeNPC.RefreshAndCheckValidTargetedStorage(__instance, clearReservation: false, out var storageSlotInfo3)) { LOG.TEMPDEBUG_FUNC((Func<string>)(() => "Restocker #" + GetUniqueId(employee) + ": Placing box in storage."), false); ((Component)__instance.storageOBJ.transform.GetChild(storageSlotInfo3.ShelfIndex)).GetComponent<Data_Container>().EmployeeUpdateArrayValuesStorage(storageSlotInfo3.SlotIndex * 2, employee.NetworkboxProductID, employee.NetworkboxNumberOfProducts); employee.state = 6; return true; } LOG.TEMPDEBUG_FUNC((Func<string>)(() => "Restocker #" + GetUniqueId(employee) + ": Target storage is not valid anymore."), false); employee.StartWaitState(ModConfig.Instance.EmployeeNextActionWait.Value, 5); employee.state = -1; return true; } case 8: { LOG.TEMPDEBUG_FUNC((Func<string>)(() => "Restocker #" + GetUniqueId(employee) + ": Dropping box at left over spot."), false); Vector3 val5 = __instance.leftoverBoxesSpotOBJ.transform.position + new Vector3(Random.Range(-1f, 1f), 4f, Random.Range(-1f, 1f)); ((Component)GameData.Instance).GetComponent<ManagerBlackboard>().SpawnBoxFromEmployee(val5, employee.NetworkboxProductID, employee.NetworkboxNumberOfProducts); employee.state = 6; return true; } case 9: { LOG.TEMPDEBUG_FUNC((Func<string>)(() => "Restocker #" + GetUniqueId(employee) + ": Recycling box."), false); float funds2 = 1.5f * (float)((Component)GameData.Instance).GetComponent<UpgradesManager>().boxRecycleFactor; AchievementsManager.Instance.CmdAddAchievementPoint(2, 1); SMTAntiCheat_Helper.Instance.CmdAlterFunds(funds2); employee.state = 6; return true; } case 20: LOG.TEMPDEBUG_FUNC((Func<string>)(() => "Restocker #" + GetUniqueId(employee) + ": Reached storage and merging contents."), false); EmployeeTryMergeBoxContents(__instance, employee, employeeNPC, 5); return true; case 30: if (Object.op_Implicit((Object)(object)employee.closestCardboardBaler)) { LOG.TEMPDEBUG_FUNC((Func<string>)(() => "Restocker #" + GetUniqueId(employee) + ": Adding empty box to cardboard baler."), false); employee.closestCardboardBaler.GetComponent<CardboardBaler>().AuxiliarAddBoxToBaler(employee.boxProductID); UnequipBox(employee); } else { LOG.TEMPDEBUG_FUNC((Func<string>)(() => "Restocker #" + GetUniqueId(employee) + ": Cardboard baler not there anymore."), false); employee.StartWaitState(1f, 0); employee.state = -1; } return true; default: employee.state = 0; return true; } case 3: switch (state) { case 0: { LOG.TEMPDEBUG_FUNC((Func<string>)(() => "Storage #" + GetUniqueId(employee) + " logic begin."), false); if (employee.equippedItem > 0) { LOG.TEMPDEBUG_FUNC((Func<string>)(() => "Storage #" + GetUniqueId(employee) + ": Dropping current box."), false); __instance.DropBoxOnGround(employee); UnequipBox(employee); return true; } GroundBoxStorageTarget closestGroundBox = GroundBoxSearch.GetClosestGroundBox(__instance, employeeObj.transform); NavMeshHit val = default(NavMeshHit); if (closestGroundBox.FoundGroundBox && NavMesh.SamplePosition(new Vector3(closestGroundBox.GroundBoxObject.transform.position.x, 0f, closestGroundBox.GroundBoxObject.transform.position.z), ref val, 1f, -1)) { LOG.TEMPDEBUG_FUNC((Func<string>)(() => "Storage #" + GetUniqueId(employee) + ": Going to pick up box."), false); employee.randomBox = closestGroundBox.GroundBoxObject; employeeNPC.MoveEmployeeToBox(((NavMeshHit)(ref val)).position, closestGroundBox.GroundBoxObject); if (closestGroundBox.HasStorageTarget) { employeeNPC.AddExtraStorageTarget(closestGroundBox.StorageSlot); } employee.state = 1; return true; } employee.state = 10; return true; } case 1: if (Object.op_Implicit((Object)(object)employee.randomBox)) { Vector3 val3 = new Vector3(employee.randomBox.transform.position.x, 0f, employee.randomBox.transform.position.z); Vector3 val4 = default(Vector3); ((Vector3)(ref val4))..ctor(((Component)employee).transform.position.x, 0f, ((Component)employee).transform.position.z); if (Vector3.Distance(val3, val4) < 2f) { LOG.TEMPDEBUG_FUNC((Func<string>)(() => "Storage #" + GetUniqueId(employee) + ": Picking up box."), false); BoxData component2 = employee.randomBox.GetComponent<BoxData>(); employee.NetworkboxProductID = component2.productID; employee.NetworkboxNumberOfProducts = component2.numberOfProducts; employee.EquipNPCItem(1); ((Component)GameData.Instance).GetComponent<NetworkSpawner>().EmployeeDestroyBox(employee.randomBox); if (component2.numberOfProducts > 0) { employee.state = 18; return true; } employee.state = 6; return true; } } LOG.TEMPDEBUG_FUNC((Func<string>)(() => "Storage #" + GetUniqueId(employee) + ": Box doesnt exist or is not at pick up range anymore."), false); employee.StartWaitState(ModConfig.Instance.EmployeeNextActionWait.Value, 0); employee.state = -1; return true; case 2: { StorageSlotInfo storageSlotInfo = null; Vector3 zero = Vector3.zero; bool validStorageFound = false; if (employeeNPC.HasTargetedStorage()) { LOG.TEMPDEBUG_FUNC((Func<string>)(() => "Storage #" + GetUniqueId(employee) + ": Checking pre-reserved storage."), false); validStorageFound = employeeNPC.RefreshAndCheckValidTargetedStorage(__instance, clearReservation: true, out storageSlotInfo); LOG.TEMPDEBUG_FUNC((Func<string>)(() => "Storage #" + GetUniqueId(employee) + ": Pre-reserved storage is " + (validStorageFound ? "" : "no longer ") + "valid."), false); } if (!validStorageFound) { LOG.TEMPDEBUG_FUNC((Func<string>)(() => "Storage #" + GetUniqueId(employee) + ": Searching for storage to place held box."), false); storageSlotInfo = ContainerSearch.GetFreeStorageContainer(__instance, employeeObj.transform, employee.NetworkboxProductID); validStorageFound = storageSlotInfo.ShelfFound; LOG.TEMPDEBUG_FUNC((Func<string>)(() => "Storage #" + GetUniqueId(employee) + ": Free storage " + (validStorageFound ? "" : "couldnt be ") + "found."), false); } if (validStorageFound) { LOG.TEMPDEBUG_FUNC((Func<string>)(() => "Storage #" + GetUniqueId(employee) + ": Moving to storage to place box."), false); zero = ((Component)__instance.storageOBJ.transform.GetChild(storageSlotInfo.ShelfIndex).Find("Standspot")).transform.position; employeeNPC.MoveEmployeeToStorage(zero, storageSlotInfo); employee.state = 3; return true; } LOG.TEMPDEBUG_FUNC((Func<string>)(() => "Storage #" + GetUniqueId(employee) + ": Moving to drop box at left over spot."), false); employeeNPC.MoveEmployeeTo(__instance.leftoverBoxesSpotOBJ, null); employee.state = 4; return true; } case 3: { LOG.TEMPDEBUG_FUNC((Func<string>)(() => "Storage #" + GetUniqueId(employee) + ": Arrived at storage."), false); if (employeeNPC.RefreshAndCheckValidTargetedStorage(__instance, clearReservation: false, out var storageSlotInfo2)) { LOG.TEMPDEBUG_FUNC((Func<string>)(() => "Storage #" + GetUniqueId(employee) + ": Placing box in storage."), false); ((Component)__instance.storageOBJ.transform.GetChild(storageSlotInfo2.ShelfIndex)).GetComponent<Data_Container>().EmployeeUpdateArrayValuesStorage(storageSlotInfo2.SlotIndex * 2, employee.NetworkboxProductID, employee.NetworkboxNumberOfProducts); employee.state = 5; NPC_Info obj = employee; obj.storageExperience += employee.storageValue; return true; } LOG.TEMPDEBUG_FUNC((Func<string>)(() => "Storage #" + GetUniqueId(employee) + ": Storage no longer valid."), false); employee.StartWaitState(ModConfig.Instance.EmployeeNextActionWait.Value, 2); employee.state = -1; return true; } case 4: { LOG.TEMPDEBUG_FUNC((Func<string>)(() => "Storage #" + GetUniqueId(employee) + ": At left over spot. Spawning box at drop."), false); Vector3 val2 = __instance.leftoverBoxesSpotOBJ.transform.position + new Vector3(Random.Range(-1f, 1f), 3f, Random.Range(-1f, 1f)); ((Component)GameData.Instance).GetComponent<ManagerBlackboard>().SpawnBoxFromEmployee(val2, employee.NetworkboxProductID, employee.NetworkboxNumberOfProducts); employee.state = 5; return true; } case 5: LOG.TEMPDEBUG_FUNC((Func<string>)(() => "Storage #" + GetUniqueId(employee) + ": Removing box in hand."), false); UnequipBox(employee); return true; case 6: MoveToBalerOrGarbageContainer(__instance, employeeObj, employee, employeeNPC, 33, 5, 7); return true; case 7: { LOG.TEMPDEBUG_FUNC((Func<string>)(() => "Storage #" + GetUniqueId(employee) + ": Recycling."), false); float funds = 1.5f * (float)((Component)GameData.Instance).GetComponent<UpgradesManager>().boxRecycleFactor; AchievementsManager.Instance.CmdAddAchievementPoint(2, 1); SMTAntiCheat_Helper.Instance.CmdAlterFunds(funds); employee.state = 5; return true; } case 10: if ((double)Vector3.Distance(((Component)employee).transform.position, __instance.restSpotOBJ.transform.position) > 6.5) { LOG.TEMPDEBUG_FUNC((Func<string>)(() => "Storage #" + GetUniqueId(employee) + ": Moving to rest spot."), false); employeeNPC.MoveEmployeeToRestPosition(); return true; } employeeNPC.ClearNPCReservations(); employee.StartWaitState(ModConfig.Instance.EmployeeIdleWait.Value, 0); employe
BepInEx/plugins/es.damntry.SuperQoLity/UniTask.dll
Decompiled a week ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.ExceptionServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using Cysharp.Threading.Tasks.CompilerServices; using Cysharp.Threading.Tasks.Internal; using Cysharp.Threading.Tasks.Triggers; using UnityEngine; using UnityEngine.Events; using UnityEngine.LowLevel; using UnityEngine.PlayerLoop; using UnityEngine.Rendering; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: AssemblyTitle("UniTask")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("UniTask")] [assembly: AssemblyCopyright("Copyright © 2024")] [assembly: AssemblyTrademark("")] [assembly: ComVisible(false)] [assembly: Guid("45d5d502-bd1f-4164-8f95-a940087bd189")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: InternalsVisibleTo("UniTask.Linq")] [assembly: InternalsVisibleTo("UniTask.Addressables")] [assembly: InternalsVisibleTo("UniTask.DOTween")] [assembly: InternalsVisibleTo("UniTask.TextMeshPro")] [assembly: TargetFramework(".NETFramework,Version=v4.8.1", FrameworkDisplayName = ".NET Framework 4.8.1")] [assembly: AssemblyVersion("1.0.0.0")] namespace System.Runtime.CompilerServices { internal sealed class AsyncMethodBuilderAttribute : Attribute { public Type BuilderType { get; } public AsyncMethodBuilderAttribute(Type builderType) { BuilderType = builderType; } } } namespace Cysharp.Threading.Tasks { public class AsyncLazy { private static Action<object> continuation = SetCompletionSource; private Func<UniTask> taskFactory; private UniTaskCompletionSource completionSource; private UniTask.Awaiter awaiter; private object syncLock; private bool initialized; public UniTask Task { get { EnsureInitialized(); return completionSource.Task; } } public AsyncLazy(Func<UniTask> taskFactory) { this.taskFactory = taskFactory; completionSource = new UniTaskCompletionSource(); syncLock = new object(); initialized = false; } internal AsyncLazy(UniTask task) { taskFactory = null; completionSource = new UniTaskCompletionSource(); syncLock = null; initialized = true; UniTask.Awaiter awaiter = task.GetAwaiter(); if (awaiter.IsCompleted) { SetCompletionSource(in awaiter); return; } this.awaiter = awaiter; awaiter.SourceOnCompleted(continuation, this); } public UniTask.Awaiter GetAwaiter() { return Task.GetAwaiter(); } private void EnsureInitialized() { if (!Volatile.Read(ref initialized)) { EnsureInitializedCore(); } } private void EnsureInitializedCore() { lock (syncLock) { if (Volatile.Read(ref initialized)) { return; } Func<UniTask> func = Interlocked.Exchange(ref taskFactory, null); if (func != null) { UniTask.Awaiter awaiter = func().GetAwaiter(); if (awaiter.IsCompleted) { SetCompletionSource(in awaiter); } else { this.awaiter = awaiter; awaiter.SourceOnCompleted(continuation, this); } Volatile.Write(ref initialized, value: true); } } } private void SetCompletionSource(in UniTask.Awaiter awaiter) { try { awaiter.GetResult(); completionSource.TrySetResult(); } catch (Exception exception) { completionSource.TrySetException(exception); } } private static void SetCompletionSource(object state) { AsyncLazy asyncLazy = (AsyncLazy)state; try { asyncLazy.awaiter.GetResult(); asyncLazy.completionSource.TrySetResult(); } catch (Exception exception) { asyncLazy.completionSource.TrySetException(exception); } finally { asyncLazy.awaiter = default(UniTask.Awaiter); } } } public class AsyncLazy<T> { private static Action<object> continuation = SetCompletionSource; private Func<UniTask<T>> taskFactory; private UniTaskCompletionSource<T> completionSource; private UniTask<T>.Awaiter awaiter; private object syncLock; private bool initialized; public UniTask<T> Task { get { EnsureInitialized(); return completionSource.Task; } } public AsyncLazy(Func<UniTask<T>> taskFactory) { this.taskFactory = taskFactory; completionSource = new UniTaskCompletionSource<T>(); syncLock = new object(); initialized = false; } internal AsyncLazy(UniTask<T> task) { taskFactory = null; completionSource = new UniTaskCompletionSource<T>(); syncLock = null; initialized = true; UniTask<T>.Awaiter awaiter = task.GetAwaiter(); if (awaiter.IsCompleted) { SetCompletionSource(in awaiter); return; } this.awaiter = awaiter; awaiter.SourceOnCompleted(continuation, this); } public UniTask<T>.Awaiter GetAwaiter() { return Task.GetAwaiter(); } private void EnsureInitialized() { if (!Volatile.Read(ref initialized)) { EnsureInitializedCore(); } } private void EnsureInitializedCore() { lock (syncLock) { if (Volatile.Read(ref initialized)) { return; } Func<UniTask<T>> func = Interlocked.Exchange(ref taskFactory, null); if (func != null) { UniTask<T>.Awaiter awaiter = func().GetAwaiter(); if (awaiter.IsCompleted) { SetCompletionSource(in awaiter); } else { this.awaiter = awaiter; awaiter.SourceOnCompleted(continuation, this); } Volatile.Write(ref initialized, value: true); } } } private void SetCompletionSource(in UniTask<T>.Awaiter awaiter) { try { T result = awaiter.GetResult(); completionSource.TrySetResult(result); } catch (Exception exception) { completionSource.TrySetException(exception); } } private static void SetCompletionSource(object state) { AsyncLazy<T> asyncLazy = (AsyncLazy<T>)state; try { T result = asyncLazy.awaiter.GetResult(); asyncLazy.completionSource.TrySetResult(result); } catch (Exception exception) { asyncLazy.completionSource.TrySetException(exception); } finally { asyncLazy.awaiter = default(UniTask<T>.Awaiter); } } } public interface IReadOnlyAsyncReactiveProperty<T> : IUniTaskAsyncEnumerable<T> { T Value { get; } IUniTaskAsyncEnumerable<T> WithoutCurrent(); UniTask<T> WaitAsync(CancellationToken cancellationToken = default(CancellationToken)); } public interface IAsyncReactiveProperty<T> : IReadOnlyAsyncReactiveProperty<T>, IUniTaskAsyncEnumerable<T> { new T Value { get; set; } } [Serializable] public class AsyncReactiveProperty<T> : IAsyncReactiveProperty<T>, IReadOnlyAsyncReactiveProperty<T>, IUniTaskAsyncEnumerable<T>, IDisposable { private sealed class WaitAsyncSource : IUniTaskSource<T>, IUniTaskSource, ITriggerHandler<T>, ITaskPoolNode<WaitAsyncSource> { private static Action<object> cancellationCallback; private static TaskPool<WaitAsyncSource> pool; private WaitAsyncSource nextNode; private AsyncReactiveProperty<T> parent; private CancellationToken cancellationToken; private CancellationTokenRegistration cancellationTokenRegistration; private UniTaskCompletionSourceCore<T> core; ref WaitAsyncSource ITaskPoolNode<WaitAsyncSource>.NextNode => ref nextNode; ITriggerHandler<T> ITriggerHandler<T>.Prev { get; set; } ITriggerHandler<T> ITriggerHandler<T>.Next { get; set; } static WaitAsyncSource() { cancellationCallback = CancellationCallback; TaskPool.RegisterSizeGetter(typeof(WaitAsyncSource), () => pool.Size); } private WaitAsyncSource() { } public static IUniTaskSource<T> Create(AsyncReactiveProperty<T> parent, CancellationToken cancellationToken, out short token) { if (cancellationToken.IsCancellationRequested) { return AutoResetUniTaskCompletionSource<T>.CreateFromCanceled(cancellationToken, out token); } if (!pool.TryPop(out var result)) { result = new WaitAsyncSource(); } result.parent = parent; result.cancellationToken = cancellationToken; if (cancellationToken.CanBeCanceled) { result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationCallback, result); } result.parent.triggerEvent.Add(result); token = result.core.Version; return result; } private bool TryReturn() { core.Reset(); cancellationTokenRegistration.Dispose(); cancellationTokenRegistration = default(CancellationTokenRegistration); parent.triggerEvent.Remove(this); parent = null; cancellationToken = default(CancellationToken); return pool.TryPush(this); } private static void CancellationCallback(object state) { WaitAsyncSource obj = (WaitAsyncSource)state; obj.OnCanceled(obj.cancellationToken); } public T GetResult(short token) { try { return core.GetResult(token); } finally { TryReturn(); } } void IUniTaskSource.GetResult(short token) { GetResult(token); } public void OnCompleted(Action<object> continuation, object state, short token) { core.OnCompleted(continuation, state, token); } public UniTaskStatus GetStatus(short token) { return core.GetStatus(token); } public UniTaskStatus UnsafeGetStatus() { return core.UnsafeGetStatus(); } public void OnCanceled(CancellationToken cancellationToken) { core.TrySetCanceled(cancellationToken); } public void OnCompleted() { core.TrySetCanceled(CancellationToken.None); } public void OnError(Exception ex) { core.TrySetException(ex); } public void OnNext(T value) { core.TrySetResult(value); } } private sealed class WithoutCurrentEnumerable : IUniTaskAsyncEnumerable<T> { private readonly AsyncReactiveProperty<T> parent; public WithoutCurrentEnumerable(AsyncReactiveProperty<T> parent) { this.parent = parent; } public IUniTaskAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default(CancellationToken)) { return new Enumerator(parent, cancellationToken, publishCurrentValue: false); } } private sealed class Enumerator : MoveNextSource, IUniTaskAsyncEnumerator<T>, IUniTaskAsyncDisposable, ITriggerHandler<T> { private static Action<object> cancellationCallback = CancellationCallback; private readonly AsyncReactiveProperty<T> parent; private readonly CancellationToken cancellationToken; private readonly CancellationTokenRegistration cancellationTokenRegistration; private T value; private bool isDisposed; private bool firstCall; public T Current => value; ITriggerHandler<T> ITriggerHandler<T>.Prev { get; set; } ITriggerHandler<T> ITriggerHandler<T>.Next { get; set; } public Enumerator(AsyncReactiveProperty<T> parent, CancellationToken cancellationToken, bool publishCurrentValue) { this.parent = parent; this.cancellationToken = cancellationToken; firstCall = publishCurrentValue; parent.triggerEvent.Add(this); if (cancellationToken.CanBeCanceled) { cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationCallback, this); } } public UniTask<bool> MoveNextAsync() { if (firstCall) { firstCall = false; value = parent.Value; return CompletedTasks.True; } completionSource.Reset(); return new UniTask<bool>(this, completionSource.Version); } public UniTask DisposeAsync() { if (!isDisposed) { isDisposed = true; completionSource.TrySetCanceled(cancellationToken); parent.triggerEvent.Remove(this); } return default(UniTask); } public void OnNext(T value) { this.value = value; completionSource.TrySetResult(result: true); } public void OnCanceled(CancellationToken cancellationToken) { DisposeAsync().Forget(); } public void OnCompleted() { completionSource.TrySetResult(result: false); } public void OnError(Exception ex) { completionSource.TrySetException(ex); } private static void CancellationCallback(object state) { ((Enumerator)state).DisposeAsync().Forget(); } } private TriggerEvent<T> triggerEvent; [SerializeField] private T latestValue; private static bool isValueType; public T Value { get { return latestValue; } set { latestValue = value; triggerEvent.SetResult(value); } } public AsyncReactiveProperty(T value) { latestValue = value; triggerEvent = default(TriggerEvent<T>); } public IUniTaskAsyncEnumerable<T> WithoutCurrent() { return new WithoutCurrentEnumerable(this); } public IUniTaskAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken) { return new Enumerator(this, cancellationToken, publishCurrentValue: true); } public void Dispose() { triggerEvent.SetCompleted(); } public static implicit operator T(AsyncReactiveProperty<T> value) { return value.Value; } public override string ToString() { if (isValueType) { return latestValue.ToString(); } return latestValue?.ToString(); } public UniTask<T> WaitAsync(CancellationToken cancellationToken = default(CancellationToken)) { short token; return new UniTask<T>(WaitAsyncSource.Create(this, cancellationToken, out token), token); } static AsyncReactiveProperty() { isValueType = typeof(T).IsValueType; } } public class ReadOnlyAsyncReactiveProperty<T> : IReadOnlyAsyncReactiveProperty<T>, IUniTaskAsyncEnumerable<T>, IDisposable { private sealed class WaitAsyncSource : IUniTaskSource<T>, IUniTaskSource, ITriggerHandler<T>, ITaskPoolNode<WaitAsyncSource> { private static Action<object> cancellationCallback; private static TaskPool<WaitAsyncSource> pool; private WaitAsyncSource nextNode; private ReadOnlyAsyncReactiveProperty<T> parent; private CancellationToken cancellationToken; private CancellationTokenRegistration cancellationTokenRegistration; private UniTaskCompletionSourceCore<T> core; ref WaitAsyncSource ITaskPoolNode<WaitAsyncSource>.NextNode => ref nextNode; ITriggerHandler<T> ITriggerHandler<T>.Prev { get; set; } ITriggerHandler<T> ITriggerHandler<T>.Next { get; set; } static WaitAsyncSource() { cancellationCallback = CancellationCallback; TaskPool.RegisterSizeGetter(typeof(WaitAsyncSource), () => pool.Size); } private WaitAsyncSource() { } public static IUniTaskSource<T> Create(ReadOnlyAsyncReactiveProperty<T> parent, CancellationToken cancellationToken, out short token) { if (cancellationToken.IsCancellationRequested) { return AutoResetUniTaskCompletionSource<T>.CreateFromCanceled(cancellationToken, out token); } if (!pool.TryPop(out var result)) { result = new WaitAsyncSource(); } result.parent = parent; result.cancellationToken = cancellationToken; if (cancellationToken.CanBeCanceled) { result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationCallback, result); } result.parent.triggerEvent.Add(result); token = result.core.Version; return result; } private bool TryReturn() { core.Reset(); cancellationTokenRegistration.Dispose(); cancellationTokenRegistration = default(CancellationTokenRegistration); parent.triggerEvent.Remove(this); parent = null; cancellationToken = default(CancellationToken); return pool.TryPush(this); } private static void CancellationCallback(object state) { WaitAsyncSource obj = (WaitAsyncSource)state; obj.OnCanceled(obj.cancellationToken); } public T GetResult(short token) { try { return core.GetResult(token); } finally { TryReturn(); } } void IUniTaskSource.GetResult(short token) { GetResult(token); } public void OnCompleted(Action<object> continuation, object state, short token) { core.OnCompleted(continuation, state, token); } public UniTaskStatus GetStatus(short token) { return core.GetStatus(token); } public UniTaskStatus UnsafeGetStatus() { return core.UnsafeGetStatus(); } public void OnCanceled(CancellationToken cancellationToken) { core.TrySetCanceled(cancellationToken); } public void OnCompleted() { core.TrySetCanceled(CancellationToken.None); } public void OnError(Exception ex) { core.TrySetException(ex); } public void OnNext(T value) { core.TrySetResult(value); } } private sealed class WithoutCurrentEnumerable : IUniTaskAsyncEnumerable<T> { private readonly ReadOnlyAsyncReactiveProperty<T> parent; public WithoutCurrentEnumerable(ReadOnlyAsyncReactiveProperty<T> parent) { this.parent = parent; } public IUniTaskAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default(CancellationToken)) { return new Enumerator(parent, cancellationToken, publishCurrentValue: false); } } private sealed class Enumerator : MoveNextSource, IUniTaskAsyncEnumerator<T>, IUniTaskAsyncDisposable, ITriggerHandler<T> { private static Action<object> cancellationCallback = CancellationCallback; private readonly ReadOnlyAsyncReactiveProperty<T> parent; private readonly CancellationToken cancellationToken; private readonly CancellationTokenRegistration cancellationTokenRegistration; private T value; private bool isDisposed; private bool firstCall; public T Current => value; ITriggerHandler<T> ITriggerHandler<T>.Prev { get; set; } ITriggerHandler<T> ITriggerHandler<T>.Next { get; set; } public Enumerator(ReadOnlyAsyncReactiveProperty<T> parent, CancellationToken cancellationToken, bool publishCurrentValue) { this.parent = parent; this.cancellationToken = cancellationToken; firstCall = publishCurrentValue; parent.triggerEvent.Add(this); if (cancellationToken.CanBeCanceled) { cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationCallback, this); } } public UniTask<bool> MoveNextAsync() { if (firstCall) { firstCall = false; value = parent.Value; return CompletedTasks.True; } completionSource.Reset(); return new UniTask<bool>(this, completionSource.Version); } public UniTask DisposeAsync() { if (!isDisposed) { isDisposed = true; completionSource.TrySetCanceled(cancellationToken); parent.triggerEvent.Remove(this); } return default(UniTask); } public void OnNext(T value) { this.value = value; completionSource.TrySetResult(result: true); } public void OnCanceled(CancellationToken cancellationToken) { DisposeAsync().Forget(); } public void OnCompleted() { completionSource.TrySetResult(result: false); } public void OnError(Exception ex) { completionSource.TrySetException(ex); } private static void CancellationCallback(object state) { ((Enumerator)state).DisposeAsync().Forget(); } } private TriggerEvent<T> triggerEvent; private T latestValue; private IUniTaskAsyncEnumerator<T> enumerator; private static bool isValueType; public T Value => latestValue; public ReadOnlyAsyncReactiveProperty(T initialValue, IUniTaskAsyncEnumerable<T> source, CancellationToken cancellationToken) { latestValue = initialValue; ConsumeEnumerator(source, cancellationToken).Forget(); } public ReadOnlyAsyncReactiveProperty(IUniTaskAsyncEnumerable<T> source, CancellationToken cancellationToken) { ConsumeEnumerator(source, cancellationToken).Forget(); } private async UniTaskVoid ConsumeEnumerator(IUniTaskAsyncEnumerable<T> source, CancellationToken cancellationToken) { enumerator = source.GetAsyncEnumerator(cancellationToken); try { while (await enumerator.MoveNextAsync()) { T result = (latestValue = enumerator.Current); triggerEvent.SetResult(result); } } finally { await enumerator.DisposeAsync(); enumerator = null; } } public IUniTaskAsyncEnumerable<T> WithoutCurrent() { return new WithoutCurrentEnumerable(this); } public IUniTaskAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken) { return new Enumerator(this, cancellationToken, publishCurrentValue: true); } public void Dispose() { if (enumerator != null) { enumerator.DisposeAsync().Forget(); } triggerEvent.SetCompleted(); } public static implicit operator T(ReadOnlyAsyncReactiveProperty<T> value) { return value.Value; } public override string ToString() { if (isValueType) { return latestValue.ToString(); } return latestValue?.ToString(); } public UniTask<T> WaitAsync(CancellationToken cancellationToken = default(CancellationToken)) { short token; return new UniTask<T>(WaitAsyncSource.Create(this, cancellationToken, out token), token); } static ReadOnlyAsyncReactiveProperty() { isValueType = typeof(T).IsValueType; } } public static class StateExtensions { public static ReadOnlyAsyncReactiveProperty<T> ToReadOnlyAsyncReactiveProperty<T>(this IUniTaskAsyncEnumerable<T> source, CancellationToken cancellationToken) { return new ReadOnlyAsyncReactiveProperty<T>(source, cancellationToken); } public static ReadOnlyAsyncReactiveProperty<T> ToReadOnlyAsyncReactiveProperty<T>(this IUniTaskAsyncEnumerable<T> source, T initialValue, CancellationToken cancellationToken) { return new ReadOnlyAsyncReactiveProperty<T>(initialValue, source, cancellationToken); } } [StructLayout(LayoutKind.Sequential, Size = 1)] public readonly struct AsyncUnit : IEquatable<AsyncUnit> { public static readonly AsyncUnit Default; public override int GetHashCode() { return 0; } public bool Equals(AsyncUnit other) { return true; } public override string ToString() { return "()"; } } public class CancellationTokenEqualityComparer : IEqualityComparer<CancellationToken> { public static readonly IEqualityComparer<CancellationToken> Default = new CancellationTokenEqualityComparer(); public bool Equals(CancellationToken x, CancellationToken y) { return x.Equals(y); } public int GetHashCode(CancellationToken obj) { return obj.GetHashCode(); } } public static class CancellationTokenExtensions { private static readonly Action<object> cancellationTokenCallback = Callback; private static readonly Action<object> disposeCallback = DisposeCallback; public static CancellationToken ToCancellationToken(this UniTask task) { CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(); ToCancellationTokenCore(task, cancellationTokenSource).Forget(); return cancellationTokenSource.Token; } public static CancellationToken ToCancellationToken(this UniTask task, CancellationToken linkToken) { if (linkToken.IsCancellationRequested) { return linkToken; } if (!linkToken.CanBeCanceled) { return task.ToCancellationToken(); } CancellationTokenSource cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(new CancellationToken[1] { linkToken }); ToCancellationTokenCore(task, cancellationTokenSource).Forget(); return cancellationTokenSource.Token; } public static CancellationToken ToCancellationToken<T>(this UniTask<T> task) { return task.AsUniTask().ToCancellationToken(); } public static CancellationToken ToCancellationToken<T>(this UniTask<T> task, CancellationToken linkToken) { return task.AsUniTask().ToCancellationToken(linkToken); } private static async UniTaskVoid ToCancellationTokenCore(UniTask task, CancellationTokenSource cts) { try { await task; } catch (Exception ex) { UniTaskScheduler.PublishUnobservedTaskException(ex); } cts.Cancel(); cts.Dispose(); } public static (UniTask, CancellationTokenRegistration) ToUniTask(this CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) { return (UniTask.FromCanceled(cancellationToken), default(CancellationTokenRegistration)); } UniTaskCompletionSource uniTaskCompletionSource = new UniTaskCompletionSource(); return (uniTaskCompletionSource.Task, cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationTokenCallback, uniTaskCompletionSource)); } private static void Callback(object state) { ((UniTaskCompletionSource)state).TrySetResult(); } public static CancellationTokenAwaitable WaitUntilCanceled(this CancellationToken cancellationToken) { return new CancellationTokenAwaitable(cancellationToken); } public static CancellationTokenRegistration RegisterWithoutCaptureExecutionContext(this CancellationToken cancellationToken, Action callback) { bool flag = false; if (!ExecutionContext.IsFlowSuppressed()) { ExecutionContext.SuppressFlow(); flag = true; } try { return cancellationToken.Register(callback, useSynchronizationContext: false); } finally { if (flag) { ExecutionContext.RestoreFlow(); } } } public static CancellationTokenRegistration RegisterWithoutCaptureExecutionContext(this CancellationToken cancellationToken, Action<object> callback, object state) { bool flag = false; if (!ExecutionContext.IsFlowSuppressed()) { ExecutionContext.SuppressFlow(); flag = true; } try { return cancellationToken.Register(callback, state, useSynchronizationContext: false); } finally { if (flag) { ExecutionContext.RestoreFlow(); } } } public static CancellationTokenRegistration AddTo(this IDisposable disposable, CancellationToken cancellationToken) { return cancellationToken.RegisterWithoutCaptureExecutionContext(disposeCallback, disposable); } private static void DisposeCallback(object state) { ((IDisposable)state).Dispose(); } } public struct CancellationTokenAwaitable { public struct Awaiter : ICriticalNotifyCompletion, INotifyCompletion { private CancellationToken cancellationToken; public bool IsCompleted { get { if (cancellationToken.CanBeCanceled) { return cancellationToken.IsCancellationRequested; } return true; } } public Awaiter(CancellationToken cancellationToken) { this.cancellationToken = cancellationToken; } public void GetResult() { } public void OnCompleted(Action continuation) { UnsafeOnCompleted(continuation); } public void UnsafeOnCompleted(Action continuation) { cancellationToken.RegisterWithoutCaptureExecutionContext(continuation); } } private CancellationToken cancellationToken; public CancellationTokenAwaitable(CancellationToken cancellationToken) { this.cancellationToken = cancellationToken; } public Awaiter GetAwaiter() { return new Awaiter(cancellationToken); } } public static class CancellationTokenSourceExtensions { private static readonly Action<object> CancelCancellationTokenSourceStateDelegate = CancelCancellationTokenSourceState; private static void CancelCancellationTokenSourceState(object state) { ((CancellationTokenSource)state).Cancel(); } public static IDisposable CancelAfterSlim(this CancellationTokenSource cts, int millisecondsDelay, DelayType delayType = DelayType.DeltaTime, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update) { return cts.CancelAfterSlim(TimeSpan.FromMilliseconds(millisecondsDelay), delayType, delayTiming); } public static IDisposable CancelAfterSlim(this CancellationTokenSource cts, TimeSpan delayTimeSpan, DelayType delayType = DelayType.DeltaTime, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update) { return PlayerLoopTimer.StartNew(delayTimeSpan, periodic: false, delayType, delayTiming, cts.Token, CancelCancellationTokenSourceStateDelegate, cts); } public static void RegisterRaiseCancelOnDestroy(this CancellationTokenSource cts, Component component) { cts.RegisterRaiseCancelOnDestroy(component.gameObject); } public static void RegisterRaiseCancelOnDestroy(this CancellationTokenSource cts, GameObject gameObject) { gameObject.GetAsyncDestroyTrigger().CancellationToken.RegisterWithoutCaptureExecutionContext(CancelCancellationTokenSourceStateDelegate, cts); } } public static class Channel { public static Channel<T> CreateSingleConsumerUnbounded<T>() { return new SingleConsumerUnboundedChannel<T>(); } } public abstract class Channel<TWrite, TRead> { public ChannelReader<TRead> Reader { get; protected set; } public ChannelWriter<TWrite> Writer { get; protected set; } public static implicit operator ChannelReader<TRead>(Channel<TWrite, TRead> channel) { return channel.Reader; } public static implicit operator ChannelWriter<TWrite>(Channel<TWrite, TRead> channel) { return channel.Writer; } } public abstract class Channel<T> : Channel<T, T> { } public abstract class ChannelReader<T> { public abstract UniTask Completion { get; } public abstract bool TryRead(out T item); public abstract UniTask<bool> WaitToReadAsync(CancellationToken cancellationToken = default(CancellationToken)); public virtual UniTask<T> ReadAsync(CancellationToken cancellationToken = default(CancellationToken)) { if (TryRead(out var item)) { return UniTask.FromResult(item); } return ReadAsyncCore(cancellationToken); } private async UniTask<T> ReadAsyncCore(CancellationToken cancellationToken = default(CancellationToken)) { if (await WaitToReadAsync(cancellationToken) && TryRead(out var item)) { return item; } throw new ChannelClosedException(); } public abstract IUniTaskAsyncEnumerable<T> ReadAllAsync(CancellationToken cancellationToken = default(CancellationToken)); } public abstract class ChannelWriter<T> { public abstract bool TryWrite(T item); public abstract bool TryComplete(Exception error = null); public void Complete(Exception error = null) { if (!TryComplete(error)) { throw new ChannelClosedException(); } } } public class ChannelClosedException : InvalidOperationException { public ChannelClosedException() : base("Channel is already closed.") { } public ChannelClosedException(string message) : base(message) { } public ChannelClosedException(Exception innerException) : base("Channel is already closed", innerException) { } public ChannelClosedException(string message, Exception innerException) : base(message, innerException) { } } internal class SingleConsumerUnboundedChannel<T> : Channel<T> { private sealed class SingleConsumerUnboundedChannelWriter : ChannelWriter<T> { private readonly SingleConsumerUnboundedChannel<T> parent; public SingleConsumerUnboundedChannelWriter(SingleConsumerUnboundedChannel<T> parent) { this.parent = parent; } public override bool TryWrite(T item) { bool isWaiting; lock (parent.items) { if (parent.closed) { return false; } parent.items.Enqueue(item); isWaiting = parent.readerSource.isWaiting; } if (isWaiting) { parent.readerSource.SingalContinuation(); } return true; } public override bool TryComplete(Exception error = null) { lock (parent.items) { if (parent.closed) { return false; } parent.closed = true; bool isWaiting = parent.readerSource.isWaiting; if (parent.items.Count == 0) { if (error == null) { if (parent.completedTaskSource != null) { parent.completedTaskSource.TrySetResult(); } else { parent.completedTask = UniTask.CompletedTask; } } else if (parent.completedTaskSource != null) { parent.completedTaskSource.TrySetException(error); } else { parent.completedTask = UniTask.FromException(error); } if (isWaiting) { parent.readerSource.SingalCompleted(error); } } parent.completionError = error; } return true; } } private sealed class SingleConsumerUnboundedChannelReader : ChannelReader<T>, IUniTaskSource<bool>, IUniTaskSource { private sealed class ReadAllAsyncEnumerable : IUniTaskAsyncEnumerable<T>, IUniTaskAsyncEnumerator<T>, IUniTaskAsyncDisposable { private readonly Action<object> CancellationCallback1Delegate = CancellationCallback1; private readonly Action<object> CancellationCallback2Delegate = CancellationCallback2; private readonly SingleConsumerUnboundedChannelReader parent; private CancellationToken cancellationToken1; private CancellationToken cancellationToken2; private CancellationTokenRegistration cancellationTokenRegistration1; private CancellationTokenRegistration cancellationTokenRegistration2; private T current; private bool cacheValue; private bool running; public T Current { get { if (cacheValue) { return current; } parent.TryRead(out current); return current; } } public ReadAllAsyncEnumerable(SingleConsumerUnboundedChannelReader parent, CancellationToken cancellationToken) { this.parent = parent; cancellationToken1 = cancellationToken; } public IUniTaskAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default(CancellationToken)) { if (running) { throw new InvalidOperationException("Enumerator is already running, does not allow call GetAsyncEnumerator twice."); } if (cancellationToken1 != cancellationToken) { cancellationToken2 = cancellationToken; } if (cancellationToken1.CanBeCanceled) { cancellationTokenRegistration1 = cancellationToken1.RegisterWithoutCaptureExecutionContext(CancellationCallback1Delegate, this); } if (cancellationToken2.CanBeCanceled) { cancellationTokenRegistration2 = cancellationToken2.RegisterWithoutCaptureExecutionContext(CancellationCallback2Delegate, this); } running = true; return this; } public UniTask<bool> MoveNextAsync() { cacheValue = false; return parent.WaitToReadAsync(CancellationToken.None); } public UniTask DisposeAsync() { cancellationTokenRegistration1.Dispose(); cancellationTokenRegistration2.Dispose(); return default(UniTask); } private static void CancellationCallback1(object state) { ReadAllAsyncEnumerable readAllAsyncEnumerable = (ReadAllAsyncEnumerable)state; readAllAsyncEnumerable.parent.SingalCancellation(readAllAsyncEnumerable.cancellationToken1); } private static void CancellationCallback2(object state) { ReadAllAsyncEnumerable readAllAsyncEnumerable = (ReadAllAsyncEnumerable)state; readAllAsyncEnumerable.parent.SingalCancellation(readAllAsyncEnumerable.cancellationToken2); } } private readonly Action<object> CancellationCallbackDelegate = CancellationCallback; private readonly SingleConsumerUnboundedChannel<T> parent; private CancellationToken cancellationToken; private CancellationTokenRegistration cancellationTokenRegistration; private UniTaskCompletionSourceCore<bool> core; internal bool isWaiting; public override UniTask Completion { get { if (parent.completedTaskSource != null) { return parent.completedTaskSource.Task; } if (parent.closed) { return parent.completedTask; } parent.completedTaskSource = new UniTaskCompletionSource(); return parent.completedTaskSource.Task; } } public SingleConsumerUnboundedChannelReader(SingleConsumerUnboundedChannel<T> parent) { this.parent = parent; } public override bool TryRead(out T item) { lock (parent.items) { if (parent.items.Count == 0) { item = default(T); return false; } item = parent.items.Dequeue(); if (parent.closed && parent.items.Count == 0) { if (parent.completionError != null) { if (parent.completedTaskSource != null) { parent.completedTaskSource.TrySetException(parent.completionError); } else { parent.completedTask = UniTask.FromException(parent.completionError); } } else if (parent.completedTaskSource != null) { parent.completedTaskSource.TrySetResult(); } else { parent.completedTask = UniTask.CompletedTask; } } } return true; } public override UniTask<bool> WaitToReadAsync(CancellationToken cancellationToken) { if (cancellationToken.IsCancellationRequested) { return UniTask.FromCanceled<bool>(cancellationToken); } lock (parent.items) { if (parent.items.Count != 0) { return CompletedTasks.True; } if (parent.closed) { if (parent.completionError == null) { return CompletedTasks.False; } return UniTask.FromException<bool>(parent.completionError); } cancellationTokenRegistration.Dispose(); core.Reset(); isWaiting = true; this.cancellationToken = cancellationToken; if (this.cancellationToken.CanBeCanceled) { cancellationTokenRegistration = this.cancellationToken.RegisterWithoutCaptureExecutionContext(CancellationCallbackDelegate, this); } return new UniTask<bool>(this, core.Version); } } public void SingalContinuation() { core.TrySetResult(result: true); } public void SingalCancellation(CancellationToken cancellationToken) { core.TrySetCanceled(cancellationToken); } public void SingalCompleted(Exception error) { if (error != null) { core.TrySetException(error); } else { core.TrySetResult(result: false); } } public override IUniTaskAsyncEnumerable<T> ReadAllAsync(CancellationToken cancellationToken = default(CancellationToken)) { return new ReadAllAsyncEnumerable(this, cancellationToken); } bool IUniTaskSource<bool>.GetResult(short token) { return core.GetResult(token); } void IUniTaskSource.GetResult(short token) { core.GetResult(token); } UniTaskStatus IUniTaskSource.GetStatus(short token) { return core.GetStatus(token); } void IUniTaskSource.OnCompleted(Action<object> continuation, object state, short token) { core.OnCompleted(continuation, state, token); } UniTaskStatus IUniTaskSource.UnsafeGetStatus() { return core.UnsafeGetStatus(); } private static void CancellationCallback(object state) { SingleConsumerUnboundedChannelReader obj = (SingleConsumerUnboundedChannelReader)state; obj.SingalCancellation(obj.cancellationToken); } } private readonly Queue<T> items; private readonly SingleConsumerUnboundedChannelReader readerSource; private UniTaskCompletionSource completedTaskSource; private UniTask completedTask; private Exception completionError; private bool closed; public SingleConsumerUnboundedChannel() { items = new Queue<T>(); base.Writer = new SingleConsumerUnboundedChannelWriter(this); readerSource = new SingleConsumerUnboundedChannelReader(this); base.Reader = readerSource; } } public static class EnumerableAsyncExtensions { public static IEnumerable<UniTask> Select<T>(this IEnumerable<T> source, Func<T, UniTask> selector) { return Enumerable.Select(source, selector); } public static IEnumerable<UniTask<TR>> Select<T, TR>(this IEnumerable<T> source, Func<T, UniTask<TR>> selector) { return Enumerable.Select(source, selector); } public static IEnumerable<UniTask> Select<T>(this IEnumerable<T> source, Func<T, int, UniTask> selector) { return Enumerable.Select(source, selector); } public static IEnumerable<UniTask<TR>> Select<T, TR>(this IEnumerable<T> source, Func<T, int, UniTask<TR>> selector) { return Enumerable.Select(source, selector); } } public static class EnumeratorAsyncExtensions { private sealed class EnumeratorPromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<EnumeratorPromise> { private static TaskPool<EnumeratorPromise> pool; private EnumeratorPromise nextNode; private IEnumerator innerEnumerator; private CancellationToken cancellationToken; private int initialFrame; private bool loopRunning; private bool calledGetResult; private UniTaskCompletionSourceCore<object> core; private static readonly FieldInfo waitForSeconds_Seconds; public ref EnumeratorPromise NextNode => ref nextNode; static EnumeratorPromise() { waitForSeconds_Seconds = typeof(WaitForSeconds).GetField("m_Seconds", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.GetField); TaskPool.RegisterSizeGetter(typeof(EnumeratorPromise), () => pool.Size); } private EnumeratorPromise() { } public static IUniTaskSource Create(IEnumerator innerEnumerator, PlayerLoopTiming timing, CancellationToken cancellationToken, out short token) { if (cancellationToken.IsCancellationRequested) { return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token); } if (!pool.TryPop(out var result)) { result = new EnumeratorPromise(); } result.innerEnumerator = ConsumeEnumerator(innerEnumerator); result.cancellationToken = cancellationToken; result.loopRunning = true; result.calledGetResult = false; result.initialFrame = -1; token = result.core.Version; if (result.MoveNext()) { PlayerLoopHelper.AddAction(timing, result); } return result; } public void GetResult(short token) { try { calledGetResult = true; core.GetResult(token); } finally { if (!loopRunning) { TryReturn(); } } } public UniTaskStatus GetStatus(short token) { return core.GetStatus(token); } public UniTaskStatus UnsafeGetStatus() { return core.UnsafeGetStatus(); } public void OnCompleted(Action<object> continuation, object state, short token) { core.OnCompleted(continuation, state, token); } public bool MoveNext() { if (calledGetResult) { loopRunning = false; TryReturn(); return false; } if (innerEnumerator == null) { return false; } if (cancellationToken.IsCancellationRequested) { loopRunning = false; core.TrySetCanceled(cancellationToken); return false; } if (initialFrame == -1) { if (PlayerLoopHelper.IsMainThread) { initialFrame = Time.frameCount; } } else if (initialFrame == Time.frameCount) { return true; } try { if (innerEnumerator.MoveNext()) { return true; } } catch (Exception error) { loopRunning = false; core.TrySetException(error); return false; } loopRunning = false; core.TrySetResult(null); return false; } private bool TryReturn() { core.Reset(); innerEnumerator = null; cancellationToken = default(CancellationToken); return pool.TryPush(this); } private static IEnumerator ConsumeEnumerator(IEnumerator enumerator) { while (enumerator.MoveNext()) { object current = enumerator.Current; if (current == null) { yield return null; continue; } CustomYieldInstruction cyi = (CustomYieldInstruction)((current is CustomYieldInstruction) ? current : null); if (cyi != null) { while (cyi.keepWaiting) { yield return null; } continue; } if (current is YieldInstruction) { IEnumerator innerCoroutine2 = null; AsyncOperation val = (AsyncOperation)((current is AsyncOperation) ? current : null); if (val == null) { WaitForSeconds val2 = (WaitForSeconds)((current is WaitForSeconds) ? current : null); if (val2 != null) { innerCoroutine2 = UnwrapWaitForSeconds(val2); } } else { innerCoroutine2 = UnwrapWaitAsyncOperation(val); } if (innerCoroutine2 != null) { while (innerCoroutine2.MoveNext()) { yield return null; } continue; } } else if (current is IEnumerator enumerator2) { IEnumerator innerCoroutine2 = ConsumeEnumerator(enumerator2); while (innerCoroutine2.MoveNext()) { yield return null; } continue; } Debug.LogWarning((object)("yield " + current.GetType().Name + " is not supported on await IEnumerator or IEnumerator.ToUniTask(), please use ToUniTask(MonoBehaviour coroutineRunner) instead.")); yield return null; } } private static IEnumerator UnwrapWaitForSeconds(WaitForSeconds waitForSeconds) { float second = (float)waitForSeconds_Seconds.GetValue(waitForSeconds); float elapsed = 0f; do { yield return null; elapsed += Time.deltaTime; } while (!(elapsed >= second)); } private static IEnumerator UnwrapWaitAsyncOperation(AsyncOperation asyncOperation) { while (!asyncOperation.isDone) { yield return null; } } } public static UniTask.Awaiter GetAwaiter<T>(this T enumerator) where T : IEnumerator { object obj = enumerator; Error.ThrowArgumentNullException((IEnumerator)obj, "enumerator"); short token; return new UniTask(EnumeratorPromise.Create((IEnumerator)obj, PlayerLoopTiming.Update, CancellationToken.None, out token), token).GetAwaiter(); } public static UniTask WithCancellation(this IEnumerator enumerator, CancellationToken cancellationToken) { Error.ThrowArgumentNullException(enumerator, "enumerator"); short token; return new UniTask(EnumeratorPromise.Create(enumerator, PlayerLoopTiming.Update, cancellationToken, out token), token); } public static UniTask ToUniTask(this IEnumerator enumerator, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken)) { Error.ThrowArgumentNullException(enumerator, "enumerator"); short token; return new UniTask(EnumeratorPromise.Create(enumerator, timing, cancellationToken, out token), token); } public static UniTask ToUniTask(this IEnumerator enumerator, MonoBehaviour coroutineRunner) { AutoResetUniTaskCompletionSource autoResetUniTaskCompletionSource = AutoResetUniTaskCompletionSource.Create(); coroutineRunner.StartCoroutine(Core(enumerator, coroutineRunner, autoResetUniTaskCompletionSource)); return autoResetUniTaskCompletionSource.Task; } private static IEnumerator Core(IEnumerator inner, MonoBehaviour coroutineRunner, AutoResetUniTaskCompletionSource source) { yield return coroutineRunner.StartCoroutine(inner); source.TrySetResult(); } } public static class ExceptionExtensions { public static bool IsOperationCanceledException(this Exception exception) { return exception is OperationCanceledException; } } public static class TaskTracker { private static List<KeyValuePair<IUniTaskSource, (string formattedType, int trackingId, DateTime addTime, string stackTrace)>> listPool = new List<KeyValuePair<IUniTaskSource, (string, int, DateTime, string)>>(); private static readonly WeakDictionary<IUniTaskSource, (string formattedType, int trackingId, DateTime addTime, string stackTrace)> tracking = new WeakDictionary<IUniTaskSource, (string, int, DateTime, string)>(); private static bool dirty; [Conditional("UNITY_EDITOR")] public static void TrackActiveTask(IUniTaskSource task, int skipFrame) { } [Conditional("UNITY_EDITOR")] public static void RemoveTracking(IUniTaskSource task) { } public static bool CheckAndResetDirty() { bool result = dirty; dirty = false; return result; } public static void ForEachActiveTask(Action<int, string, UniTaskStatus, DateTime, string> action) { lock (listPool) { int num = tracking.ToList(ref listPool, clear: false); try { for (int i = 0; i < num; i++) { action(listPool[i].Value.trackingId, listPool[i].Value.formattedType, listPool[i].Key.UnsafeGetStatus(), listPool[i].Value.addTime, listPool[i].Value.stackTrace); listPool[i] = default(KeyValuePair<IUniTaskSource, (string, int, DateTime, string)>); } } catch { listPool.Clear(); throw; } } } private static void TypeBeautify(Type type, StringBuilder sb) { if (type.IsNested) { sb.Append(type.DeclaringType.Name.ToString()); sb.Append("."); } if (type.IsGenericType) { int num = type.Name.IndexOf("`"); if (num != -1) { sb.Append(type.Name.Substring(0, num)); } else { sb.Append(type.Name); } sb.Append("<"); bool flag = true; Type[] genericArguments = type.GetGenericArguments(); foreach (Type type2 in genericArguments) { if (!flag) { sb.Append(", "); } flag = false; TypeBeautify(type2, sb); } sb.Append(">"); } else { sb.Append(type.Name); } } } public interface IUniTaskAsyncEnumerable<out T> { IUniTaskAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default(CancellationToken)); } public interface IUniTaskAsyncEnumerator<out T> : IUniTaskAsyncDisposable { T Current { get; } UniTask<bool> MoveNextAsync(); } public interface IUniTaskAsyncDisposable { UniTask DisposeAsync(); } public interface IUniTaskOrderedAsyncEnumerable<TElement> : IUniTaskAsyncEnumerable<TElement> { IUniTaskOrderedAsyncEnumerable<TElement> CreateOrderedEnumerable<TKey>(Func<TElement, TKey> keySelector, IComparer<TKey> comparer, bool descending); IUniTaskOrderedAsyncEnumerable<TElement> CreateOrderedEnumerable<TKey>(Func<TElement, UniTask<TKey>> keySelector, IComparer<TKey> comparer, bool descending); IUniTaskOrderedAsyncEnumerable<TElement> CreateOrderedEnumerable<TKey>(Func<TElement, CancellationToken, UniTask<TKey>> keySelector, IComparer<TKey> comparer, bool descending); } public interface IConnectableUniTaskAsyncEnumerable<out T> : IUniTaskAsyncEnumerable<T> { IDisposable Connect(); } public static class UniTaskAsyncEnumerableExtensions { public static UniTaskCancelableAsyncEnumerable<T> WithCancellation<T>(this IUniTaskAsyncEnumerable<T> source, CancellationToken cancellationToken) { return new UniTaskCancelableAsyncEnumerable<T>(source, cancellationToken); } } [StructLayout(LayoutKind.Auto)] public readonly struct UniTaskCancelableAsyncEnumerable<T> { [StructLayout(LayoutKind.Auto)] public readonly struct Enumerator { private readonly IUniTaskAsyncEnumerator<T> enumerator; public T Current => enumerator.Current; internal Enumerator(IUniTaskAsyncEnumerator<T> enumerator) { this.enumerator = enumerator; } public UniTask<bool> MoveNextAsync() { return enumerator.MoveNextAsync(); } public UniTask DisposeAsync() { return enumerator.DisposeAsync(); } } private readonly IUniTaskAsyncEnumerable<T> enumerable; private readonly CancellationToken cancellationToken; internal UniTaskCancelableAsyncEnumerable(IUniTaskAsyncEnumerable<T> enumerable, CancellationToken cancellationToken) { this.enumerable = enumerable; this.cancellationToken = cancellationToken; } public Enumerator GetAsyncEnumerator() { return new Enumerator(enumerable.GetAsyncEnumerator(cancellationToken)); } } public enum UniTaskStatus { Pending, Succeeded, Faulted, Canceled } public interface IUniTaskSource { UniTaskStatus GetStatus(short token); void OnCompleted(Action<object> continuation, object state, short token); void GetResult(short token); UniTaskStatus UnsafeGetStatus(); } public interface IUniTaskSource<out T> : IUniTaskSource { new T GetResult(short token); } public static class UniTaskStatusExtensions { [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsCompleted(this UniTaskStatus status) { return status != UniTaskStatus.Pending; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsCompletedSuccessfully(this UniTaskStatus status) { return status == UniTaskStatus.Succeeded; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsCanceled(this UniTaskStatus status) { return status == UniTaskStatus.Canceled; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static bool IsFaulted(this UniTaskStatus status) { return status == UniTaskStatus.Faulted; } } public abstract class MoveNextSource : IUniTaskSource<bool>, IUniTaskSource { protected UniTaskCompletionSourceCore<bool> completionSource; public bool GetResult(short token) { return completionSource.GetResult(token); } public UniTaskStatus GetStatus(short token) { return completionSource.GetStatus(token); } public void OnCompleted(Action<object> continuation, object state, short token) { completionSource.OnCompleted(continuation, state, token); } public UniTaskStatus UnsafeGetStatus() { return completionSource.UnsafeGetStatus(); } void IUniTaskSource.GetResult(short token) { completionSource.GetResult(token); } protected bool TryGetResult<T>(UniTask<T>.Awaiter awaiter, out T result) { try { result = awaiter.GetResult(); return true; } catch (Exception error) { completionSource.TrySetException(error); result = default(T); return false; } } protected bool TryGetResult(UniTask.Awaiter awaiter) { try { awaiter.GetResult(); return true; } catch (Exception error) { completionSource.TrySetException(error); return false; } } } public static class UniTaskLoopRunners { [StructLayout(LayoutKind.Sequential, Size = 1)] public struct UniTaskLoopRunnerInitialization { } [StructLayout(LayoutKind.Sequential, Size = 1)] public struct UniTaskLoopRunnerEarlyUpdate { } [StructLayout(LayoutKind.Sequential, Size = 1)] public struct UniTaskLoopRunnerFixedUpdate { } [StructLayout(LayoutKind.Sequential, Size = 1)] public struct UniTaskLoopRunnerPreUpdate { } [StructLayout(LayoutKind.Sequential, Size = 1)] public struct UniTaskLoopRunnerUpdate { } [StructLayout(LayoutKind.Sequential, Size = 1)] public struct UniTaskLoopRunnerPreLateUpdate { } [StructLayout(LayoutKind.Sequential, Size = 1)] public struct UniTaskLoopRunnerPostLateUpdate { } [StructLayout(LayoutKind.Sequential, Size = 1)] public struct UniTaskLoopRunnerLastInitialization { } [StructLayout(LayoutKind.Sequential, Size = 1)] public struct UniTaskLoopRunnerLastEarlyUpdate { } [StructLayout(LayoutKind.Sequential, Size = 1)] public struct UniTaskLoopRunnerLastFixedUpdate { } [StructLayout(LayoutKind.Sequential, Size = 1)] public struct UniTaskLoopRunnerLastPreUpdate { } [StructLayout(LayoutKind.Sequential, Size = 1)] public struct UniTaskLoopRunnerLastUpdate { } [StructLayout(LayoutKind.Sequential, Size = 1)] public struct UniTaskLoopRunnerLastPreLateUpdate { } [StructLayout(LayoutKind.Sequential, Size = 1)] public struct UniTaskLoopRunnerLastPostLateUpdate { } [StructLayout(LayoutKind.Sequential, Size = 1)] public struct UniTaskLoopRunnerYieldInitialization { } [StructLayout(LayoutKind.Sequential, Size = 1)] public struct UniTaskLoopRunnerYieldEarlyUpdate { } [StructLayout(LayoutKind.Sequential, Size = 1)] public struct UniTaskLoopRunnerYieldFixedUpdate { } [StructLayout(LayoutKind.Sequential, Size = 1)] public struct UniTaskLoopRunnerYieldPreUpdate { } [StructLayout(LayoutKind.Sequential, Size = 1)] public struct UniTaskLoopRunnerYieldUpdate { } [StructLayout(LayoutKind.Sequential, Size = 1)] public struct UniTaskLoopRunnerYieldPreLateUpdate { } [StructLayout(LayoutKind.Sequential, Size = 1)] public struct UniTaskLoopRunnerYieldPostLateUpdate { } [StructLayout(LayoutKind.Sequential, Size = 1)] public struct UniTaskLoopRunnerLastYieldInitialization { } [StructLayout(LayoutKind.Sequential, Size = 1)] public struct UniTaskLoopRunnerLastYieldEarlyUpdate { } [StructLayout(LayoutKind.Sequential, Size = 1)] public struct UniTaskLoopRunnerLastYieldFixedUpdate { } [StructLayout(LayoutKind.Sequential, Size = 1)] public struct UniTaskLoopRunnerLastYieldPreUpdate { } [StructLayout(LayoutKind.Sequential, Size = 1)] public struct UniTaskLoopRunnerLastYieldUpdate { } [StructLayout(LayoutKind.Sequential, Size = 1)] public struct UniTaskLoopRunnerLastYieldPreLateUpdate { } [StructLayout(LayoutKind.Sequential, Size = 1)] public struct UniTaskLoopRunnerLastYieldPostLateUpdate { } [StructLayout(LayoutKind.Sequential, Size = 1)] public struct UniTaskLoopRunnerTimeUpdate { } [StructLayout(LayoutKind.Sequential, Size = 1)] public struct UniTaskLoopRunnerLastTimeUpdate { } [StructLayout(LayoutKind.Sequential, Size = 1)] public struct UniTaskLoopRunnerYieldTimeUpdate { } [StructLayout(LayoutKind.Sequential, Size = 1)] public struct UniTaskLoopRunnerLastYieldTimeUpdate { } } public enum PlayerLoopTiming { Initialization, LastInitialization, EarlyUpdate, LastEarlyUpdate, FixedUpdate, LastFixedUpdate, PreUpdate, LastPreUpdate, Update, LastUpdate, PreLateUpdate, LastPreLateUpdate, PostLateUpdate, LastPostLateUpdate, TimeUpdate, LastTimeUpdate } [Flags] public enum InjectPlayerLoopTimings { All = 0xFFFF, Standard = 0x7555, Minimum = 0x2110, Initialization = 1, LastInitialization = 2, EarlyUpdate = 4, LastEarlyUpdate = 8, FixedUpdate = 0x10, LastFixedUpdate = 0x20, PreUpdate = 0x40, LastPreUpdate = 0x80, Update = 0x100, LastUpdate = 0x200, PreLateUpdate = 0x400, LastPreLateUpdate = 0x800, PostLateUpdate = 0x1000, LastPostLateUpdate = 0x2000, TimeUpdate = 0x4000, LastTimeUpdate = 0x8000 } public interface IPlayerLoopItem { bool MoveNext(); } public static class PlayerLoopHelper { private static readonly ContinuationQueue ThrowMarkerContinuationQueue; private static readonly PlayerLoopRunner ThrowMarkerPlayerLoopRunner; private static int mainThreadId; private static string applicationDataPath; private static SynchronizationContext unitySynchronizationContext; private static ContinuationQueue[] yielders; private static PlayerLoopRunner[] runners; public static SynchronizationContext UnitySynchronizationContext => unitySynchronizationContext; public static int MainThreadId => mainThreadId; internal static string ApplicationDataPath => applicationDataPath; public static bool IsMainThread => Thread.CurrentThread.ManagedThreadId == mainThreadId; internal static bool IsEditorApplicationQuitting { get; private set; } private static PlayerLoopSystem[] InsertRunner(PlayerLoopSystem loopSystem, bool injectOnFirst, Type loopRunnerYieldType, ContinuationQueue cq, Type loopRunnerType, PlayerLoopRunner runner) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Expected O, but got Unknown //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0025: 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) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Expected O, but got Unknown //IL_004b: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0083: Unknown result type (might be due to invalid IL or missing references) PlayerLoopSystem val = default(PlayerLoopSystem); val.type = loopRunnerYieldType; val.updateDelegate = new UpdateFunction(cq.Run); PlayerLoopSystem val2 = val; val = default(PlayerLoopSystem); val.type = loopRunnerType; val.updateDelegate = new UpdateFunction(runner.Run); PlayerLoopSystem val3 = val; PlayerLoopSystem[] array = RemoveRunner(loopSystem, loopRunnerYieldType, loopRunnerType); PlayerLoopSystem[] array2 = (PlayerLoopSystem[])(object)new PlayerLoopSystem[array.Length + 2]; Array.Copy(array, 0, array2, injectOnFirst ? 2 : 0, array.Length); if (injectOnFirst) { array2[0] = val2; array2[1] = val3; } else { array2[^2] = val2; array2[^1] = val3; } return array2; } private static PlayerLoopSystem[] RemoveRunner(PlayerLoopSystem loopSystem, Type loopRunnerYieldType, Type loopRunnerType) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) return loopSystem.subSystemList.Where((PlayerLoopSystem ls) => ls.type != loopRunnerYieldType && ls.type != loopRunnerType).ToArray(); } private static PlayerLoopSystem[] InsertUniTaskSynchronizationContext(PlayerLoopSystem loopSystem) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected O, but got Unknown //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_00b7: Unknown result type (might be due to invalid IL or missing references) PlayerLoopSystem val = default(PlayerLoopSystem); val.type = typeof(UniTaskSynchronizationContext); val.updateDelegate = new UpdateFunction(UniTaskSynchronizationContext.Run); PlayerLoopSystem item = val; List<PlayerLoopSystem> list = new List<PlayerLoopSystem>(loopSystem.subSystemList.Where((PlayerLoopSystem ls) => ls.type != typeof(UniTaskSynchronizationContext)).ToArray()); int num = list.FindIndex((PlayerLoopSystem x) => x.type.Name == "ScriptRunDelayedTasks"); if (num == -1) { num = list.FindIndex((PlayerLoopSystem x) => x.type.Name == "UniTaskLoopRunnerUpdate"); } list.Insert(num + 1, item); return list.ToArray(); } [RuntimeInitializeOnLoadMethod(/*Could not decode attribute arguments.*/)] private static void Init() { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) unitySynchronizationContext = SynchronizationContext.Current; mainThreadId = Thread.CurrentThread.ManagedThreadId; try { applicationDataPath = Application.dataPath; } catch { } if (runners == null) { PlayerLoopSystem playerLoop = PlayerLoop.GetCurrentPlayerLoop(); Initialize(ref playerLoop); } } static PlayerLoopHelper() { ThrowMarkerContinuationQueue = new ContinuationQueue(PlayerLoopTiming.Initialization); ThrowMarkerPlayerLoopRunner = new PlayerLoopRunner(PlayerLoopTiming.Initialization); Init(); } private static int FindLoopSystemIndex(PlayerLoopSystem[] playerLoopList, Type systemType) { for (int i = 0; i < playerLoopList.Length; i++) { if (playerLoopList[i].type == systemType) { return i; } } throw new Exception("Target PlayerLoopSystem does not found. Type:" + systemType.FullName); } private static void InsertLoop(PlayerLoopSystem[] copyList, InjectPlayerLoopTimings injectTimings, Type loopType, InjectPlayerLoopTimings targetTimings, int index, bool injectOnFirst, Type loopRunnerYieldType, Type loopRunnerType, PlayerLoopTiming playerLoopTiming) { //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) int num = FindLoopSystemIndex(copyList, loopType); if ((injectTimings & targetTimings) == targetTimings) { copyList[num].subSystemList = InsertRunner(copyList[num], injectOnFirst, loopRunnerYieldType, yielders[index] = new ContinuationQueue(playerLoopTiming), loopRunnerType, runners[index] = new PlayerLoopRunner(playerLoopTiming)); } else { copyList[num].subSystemList = RemoveRunner(copyList[num], loopRunnerYieldType, loopRunnerType); } } public static void Initialize(ref PlayerLoopSystem playerLoop, InjectPlayerLoopTimings injectTimings = InjectPlayerLoopTimings.All) { //IL_0303: Unknown result type (might be due to invalid IL or missing references) //IL_031a: Unknown result type (might be due to invalid IL or missing references) yielders = new ContinuationQueue[16]; runners = new PlayerLoopRunner[16]; PlayerLoopSystem[] array = playerLoop.subSystemList.ToArray(); InsertLoop(array, injectTimings, typeof(Initialization), InjectPlayerLoopTimings.Initialization, 0, injectOnFirst: true, typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldInitialization), typeof(UniTaskLoopRunners.UniTaskLoopRunnerInitialization), PlayerLoopTiming.Initialization); InsertLoop(array, injectTimings, typeof(Initialization), InjectPlayerLoopTimings.LastInitialization, 1, injectOnFirst: false, typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldInitialization), typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastInitialization), PlayerLoopTiming.LastInitialization); InsertLoop(array, injectTimings, typeof(EarlyUpdate), InjectPlayerLoopTimings.EarlyUpdate, 2, injectOnFirst: true, typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldEarlyUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerEarlyUpdate), PlayerLoopTiming.EarlyUpdate); InsertLoop(array, injectTimings, typeof(EarlyUpdate), InjectPlayerLoopTimings.LastEarlyUpdate, 3, injectOnFirst: false, typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldEarlyUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastEarlyUpdate), PlayerLoopTiming.LastEarlyUpdate); InsertLoop(array, injectTimings, typeof(FixedUpdate), InjectPlayerLoopTimings.FixedUpdate, 4, injectOnFirst: true, typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldFixedUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerFixedUpdate), PlayerLoopTiming.FixedUpdate); InsertLoop(array, injectTimings, typeof(FixedUpdate), InjectPlayerLoopTimings.LastFixedUpdate, 5, injectOnFirst: false, typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldFixedUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastFixedUpdate), PlayerLoopTiming.LastFixedUpdate); InsertLoop(array, injectTimings, typeof(PreUpdate), InjectPlayerLoopTimings.PreUpdate, 6, injectOnFirst: true, typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldPreUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerPreUpdate), PlayerLoopTiming.PreUpdate); InsertLoop(array, injectTimings, typeof(PreUpdate), InjectPlayerLoopTimings.LastPreUpdate, 7, injectOnFirst: false, typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldPreUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastPreUpdate), PlayerLoopTiming.LastPreUpdate); InsertLoop(array, injectTimings, typeof(Update), InjectPlayerLoopTimings.Update, 8, injectOnFirst: true, typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerUpdate), PlayerLoopTiming.Update); InsertLoop(array, injectTimings, typeof(Update), InjectPlayerLoopTimings.LastUpdate, 9, injectOnFirst: false, typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastUpdate), PlayerLoopTiming.LastUpdate); InsertLoop(array, injectTimings, typeof(PreLateUpdate), InjectPlayerLoopTimings.PreLateUpdate, 10, injectOnFirst: true, typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldPreLateUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerPreLateUpdate), PlayerLoopTiming.PreLateUpdate); InsertLoop(array, injectTimings, typeof(PreLateUpdate), InjectPlayerLoopTimings.LastPreLateUpdate, 11, injectOnFirst: false, typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldPreLateUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastPreLateUpdate), PlayerLoopTiming.LastPreLateUpdate); InsertLoop(array, injectTimings, typeof(PostLateUpdate), InjectPlayerLoopTimings.PostLateUpdate, 12, injectOnFirst: true, typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldPostLateUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerPostLateUpdate), PlayerLoopTiming.PostLateUpdate); InsertLoop(array, injectTimings, typeof(PostLateUpdate), InjectPlayerLoopTimings.LastPostLateUpdate, 13, injectOnFirst: false, typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldPostLateUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastPostLateUpdate), PlayerLoopTiming.LastPostLateUpdate); InsertLoop(array, injectTimings, typeof(TimeUpdate), InjectPlayerLoopTimings.TimeUpdate, 14, injectOnFirst: true, typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldTimeUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerTimeUpdate), PlayerLoopTiming.TimeUpdate); InsertLoop(array, injectTimings, typeof(TimeUpdate), InjectPlayerLoopTimings.LastTimeUpdate, 15, injectOnFirst: false, typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldTimeUpdate), typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastTimeUpdate), PlayerLoopTiming.LastTimeUpdate); int num = FindLoopSystemIndex(array, typeof(Update)); array[num].subSystemList = InsertUniTaskSynchronizationContext(array[num]); playerLoop.subSystemList = array; PlayerLoop.SetPlayerLoop(playerLoop); } public static void AddAction(PlayerLoopTiming timing, IPlayerLoopItem action) { PlayerLoopRunner obj = runners[(int)timing]; if (obj == null) { ThrowInvalidLoopTiming(timing); } obj.AddAction(action); } private static void ThrowInvalidLoopTiming(PlayerLoopTiming playerLoopTiming) { throw new InvalidOperationException("Target playerLoopTiming is not injected. Please check PlayerLoopHelper.Initialize. PlayerLoopTiming:" + playerLoopTiming); } public static void AddContinuation(PlayerLoopTiming timing, Action continuation) { ContinuationQueue obj = yielders[(int)timing]; if (obj == null) { ThrowInvalidLoopTiming(timing); } obj.Enqueue(continuation); } public static void DumpCurrentPlayerLoop() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) PlayerLoopSystem currentPlayerLoop = PlayerLoop.GetCurrentPlayerLoop(); StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("PlayerLoop List"); PlayerLoopSystem[] subSystemList = currentPlayerLoop.subSystemList; for (int i = 0; i < subSystemList.Length; i++) { PlayerLoopSystem val = subSystemList[i]; stringBuilder.AppendFormat("------{0}------", val.type.Name); stringBuilder.AppendLine(); if (val.subSystemList == null) { stringBuilder.AppendFormat("{0} has no subsystems!", ((object)(PlayerLoopSystem)(ref val)).ToString()); stringBuilder.AppendLine(); continue; } PlayerLoopSystem[] subSystemList2 = val.subSystemList; foreach (PlayerLoopSystem val2 in subSystemList2) { stringBuilder.AppendFormat("{0}", val2.type.Name); stringBuilder.AppendLine(); if (val2.subSystemList != null) { Debug.LogWarning((object)("More Subsystem:" + val2.subSystemList.Length)); } } } Debug.Log((object)stringBuilder.ToString()); } public static bool IsInjectedUniTaskPlayerLoop() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) PlayerLoopSystem[] subSystemList = PlayerLoop.GetCurrentPlayerLoop().subSystemList; foreach (PlayerLoopSystem val in subSystemList) { if (val.subSystemList == null) { continue; } PlayerLoopSystem[] subSystemList2 = val.subSystemList; for (int j = 0; j < subSystemList2.Length; j++) { if (subSystemList2[j].type == typeof(UniTaskLoopRunners.UniTaskLoopRunnerInitialization)) { return true; } } } return false; } } public abstract class PlayerLoopTimer : IDisposable, IPlayerLoopItem { private readonly CancellationToken cancellationToken; private readonly Action<object> timerCallback; private readonly object state; private readonly PlayerLoopTiming playerLoopTiming; private readonly bool periodic; private bool isRunning; private bool tryStop; private bool isDisposed; protected PlayerLoopTimer(bool periodic, PlayerLoopTiming playerLoopTiming, CancellationToken cancellationToken, Action<object> timerCallback, object state) { this.periodic = periodic; this.playerLoopTiming = playerLoopTiming; this.cancellationToken = cancellationToken; this.timerCallback = timerCallback; this.state = state; } public static PlayerLoopTimer Create(TimeSpan interval, bool periodic, DelayType delayType, PlayerLoopTiming playerLoopTiming, CancellationToken cancellationToken, Action<object> timerCallback, object state) { return delayType switch { DelayType.UnscaledDeltaTime => new IgnoreTimeScalePlayerLoopTimer(interval, periodic, playerLoopTiming, cancellationToken, timerCallback, state), DelayType.Realtime => new RealtimePlayerLoopTimer(interval, periodic, playerLoopTiming, cancellationToken, timerCallback, state), _ => new DeltaTimePlayerLoopTimer(interval, periodic, playerLoopTiming, cancellationToken, timerCallback, state), }; } public static PlayerLoopTimer StartNew(TimeSpan interval, bool periodic, DelayType delayType, PlayerLoopTiming playerLoopTiming, CancellationToken cancellationToken, Action<object> timerCallback, object state) { PlayerLoopTimer playerLoopTimer = Create(interval, periodic, delayType, playerLoopTiming, cancellationToken, timerCallback, state); playerLoopTimer.Restart(); return playerLoopTimer; } public void Restart() { if (isDisposed) { throw new ObjectDisposedException(null); } ResetCore(null); if (!isRunning) { isRunning = true; PlayerLoopHelper.AddAction(playerLoopTiming, this); } tryStop = false; } public void Restart(TimeSpan interval) { if (isDisposed) { throw new ObjectDisposedException(null); } ResetCore(interval); if (!isRunning) { isRunning = true; PlayerLoopHelper.AddAction(playerLoopTiming, this); } tryStop = false; } public void Stop() { tryStop = true; } protected abstract void ResetCore(TimeSpan? newInterval); public void Dispose() { isDisposed = true; } bool IPlayerLoopItem.MoveNext() { if (isDisposed) { isRunning = false; return false; } if (tryStop) { isRunning = false; return false; } CancellationToken cancellationToken = this.cancellationToken; if (cancellationToken.IsCancellationRequested) { isRunning = false; return false; } if (!MoveNextCore()) { timerCallback(state); if (periodic) { ResetCore(null); return true; } isRunning = false; return false; } return true; } protected abstract bool MoveNextCore(); } internal sealed class DeltaTimePlayerLoopTimer : PlayerLoopTimer { private int initialFrame; private float elapsed; private float interval; public DeltaTimePlayerLoopTimer(TimeSpan interval, bool periodic, PlayerLoopTiming playerLoopTiming, CancellationToken cancellationToken, Action<object> timerCallback, object state) : base(periodic, playerLoopTiming, cancellationToken, timerCallback, state) { ResetCore(interval); } protected override bool MoveNextCore() { if (elapsed == 0f && initialFrame == Time.frameCount) { return true; } elapsed += Time.deltaTime; if (elapsed >= interval) { return false; } return true; } protected override void ResetCore(TimeSpan? interval) { elapsed = 0f; initialFrame = (PlayerLoopHelper.IsMainThread ? Time.frameCount : (-1)); if (interval.HasValue) { this.interval = (float)interval.Value.TotalSeconds; } } } internal sealed class IgnoreTimeScalePlayerLoopTimer : PlayerLoopTimer { private int initialFrame; private float elapsed; private float interval; public IgnoreTimeScalePlayerLoopTimer(TimeSpan interval, bool periodic, PlayerLoopTiming playerLoopTiming, CancellationToken cancellationToken, Action<object> timerCallback, object state) : base(periodic, playerLoopTiming, cancellationToken, timerCallback, state) { ResetCore(interval); } protected override bool MoveNextCore() { if (elapsed == 0f && initialFrame == Time.frameCount) { return true; } elapsed += Time.unscaledDeltaTime; if (elapsed >= interval) { return false; } return true; } protected override void ResetCore(TimeSpan? interval) { elapsed = 0f; initialFrame = (PlayerLoopHelper.IsMainThread ? Time.frameCount : (-1)); if (interval.HasValue) { this.interval = (float)interval.Value.TotalSeconds; } } } internal sealed class RealtimePlayerLoopTimer : PlayerLoopTimer { private ValueStopwatch stopwatch; private long intervalTicks; public RealtimePlayerLoopTimer(TimeSpan interval, bool periodic, PlayerLoopTiming playerLoopTiming, CancellationToken cancellationToken, Action<object> timerCallback, object state) : base(periodic, playerLoopTiming, cancellationToken, timerCallback, state) { ResetCore(interval); } protected override bool MoveNextCore() { if (stopwatch.ElapsedTicks >= intervalTicks) { return false; } return true; } protected override void ResetCore(TimeSpan? interval) { stopwatch = ValueStopwatch.StartNew(); if (interval.HasValue) { intervalTicks = interval.Value.Ticks; } } } public static class Progress { private sealed class NullProgress<T> : IProgress<T> { public static readonly IProgress<T> Instance = new NullProgress<T>(); private NullProgress() { } public void Report(T value) { } } private sealed class AnonymousProgress<T> : IProgress<T> { private readonly Action<T> action; public AnonymousProgress(Action<T> action) { this.action = action; } public void Report(T value) { action(value); } } private sealed class OnlyValueChangedProgress<T> : IProgress<T> { private readonly Action<T> action; private readonly IEqualityComparer<T> comparer; private bool isFirstCall; private T latestValue; public OnlyValueChangedProgress(Action<T> action, IEqualityComparer<T> comparer) { this.action = action; this.comparer = comparer; isFirstCall = true; } public void Report(T value) { if (isFirstCall) { isFirstCall = false; } else if (comparer.Equals(value, latestValue)) { return; } latestValue = value; action(value); } } public static IProgress<T> Create<T>(Action<T> handler) { if (handler == null) { return NullProgress<T>.Instance; } return new AnonymousProgress<T>(handler); } public static IProgress<T> CreateOnlyValueChanged<T>(Action<T> handler, IEqualityComparer<T> comparer = null) { if (handler == null) { return NullProgress<T>.Instance; } return new OnlyValueChangedProgress<T>(handler, comparer ?? UnityEqualityComparer.GetDefault<T>()); } } public static class TaskPool { internal static int MaxPoolSize; private static Dictionary<Type, Func<int>> sizes; static TaskPool() { sizes = new Dictionary<Type, Func<int>>(); try { string environmentVariable = Environment.GetEnvironmentVariable("UNITASK_MAX_POOLSIZE"); if (environmentVariable != null && int.TryParse(environmentVariable, out var result)) { MaxPoolSize = result; return; } } catch { } MaxPoolSize = int.MaxValue; } public static void SetMaxPoolSize(int maxPoolSize) { MaxPoolSize = maxPoolSize; } public static IEnumerable<(Type, int)> GetCacheSizeInfo() { lock (sizes) { foreach (KeyValuePair<Type, Func<int>> size in sizes) { yield return (size.Key, size.Value()); } } } public static void RegisterSizeGetter(Type type, Func<int> getSize) { lock (sizes) { sizes[type] = getSize; } } } public interface ITaskPoolNode<T> { ref T NextNode { get; } } [StructLayout(LayoutKind.Auto)] public struct TaskPool<T> where T : class, ITaskPoolNode<T> { private int gate; private int size; private T root; public int Size => size; [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool TryPop(out T result) { if (Interlocked.CompareExchange(ref gate, 1, 0) == 0) { T val = root; if (val != null) { ref T nextNode = ref val.NextNode; root = nextNode; nextNode = null; size--; result = val; Volatile.Write(ref gate, 0); return true; } Volatile.Write(ref gate, 0); } result = null; return false; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool TryPush(T item) { if (Interlocked.CompareExchange(ref gate, 1, 0) == 0) { if (size < TaskPool.MaxPoolSize) { item.NextNode = root; root = item; size++; Volatile.Write(ref gate, 0); return true; } Volatile.Write(ref gate, 0); } return false; } } public sealed class TimeoutController : IDisposable { private static readonly Action<object> CancelCancellationTokenSourceStateDelegate = CancelCancellationTokenSourceState; private CancellationTokenSource timeoutSource; private CancellationTokenSource linkedSource; private PlayerLoopTimer timer; private bool isDisposed; private readonly DelayType delayType; private readonly PlayerLoopTiming delayTiming; private readonly CancellationTokenSource originalLinkCancellationTokenSource; private static void CancelCancellationTokenSourceState(object state) { ((CancellationTokenSource)state).Cancel(); } public TimeoutController(DelayType delayType = DelayType.DeltaTime, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update) { timeoutSource = new CancellationTokenSource(); originalLinkCancellationTokenSource = null; linkedSource = null; this.delayType = delayType; this.delayTiming = delayTiming; } public TimeoutController(CancellationTokenSource linkCancellationTokenSource, DelayType delayType = DelayType.DeltaTime, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update) { timeoutSource = new CancellationTokenSource(); originalLinkCancellationTokenSource = linkCancellationTokenSource; linkedSource = CancellationTokenSource.CreateLinkedTokenSource(timeoutSource.Token, linkCancellationTokenSource.Token); this.delayType = delayType; this.delayTiming = delayTiming; } public CancellationToken Timeout(int millisecondsTimeout) { return Timeout(TimeSpan.FromMilliseconds(millisecondsTimeout)); } public CancellationToken Timeout(TimeSpan timeout) { if (originalLinkCancellationTokenSource != null && originalLinkCancellationTokenSource.IsCancellationRequested) { return originalLinkCancellationTokenSource.Token; } if (timeoutSource.IsCancellationRequested) { timeoutSource.Dispose(); timeoutSource = new CancellationTokenSource(); if (linkedSource != null) { linkedSource.Cancel(); linkedSource.Dispose(); linkedSource = CancellationTokenSource.CreateLinkedTokenSource(timeoutSource.Token, originalLinkCancellationTokenSource.Token); } timer?.Dispose(); timer = null; } CancellationToken token = ((linkedSource != null) ? linkedSource : timeoutSource).Token; if (timer == null) { timer = PlayerLoopTimer.StartNew(timeout, periodic: false, delayType, delayTiming, token, CancelCancellationTokenSourceStateDelegate, timeoutSource); } else { timer.Restart(timeout); } return token; } public bool IsTimeout() { return timeoutSource.IsCancellationRequested; } public void Reset() { timer?.Stop(); } public void Dispose() { if (isDisposed) { return; } try { timer?.Dispose(); timeoutSource.Cancel(); timeoutSource.Dispose(); if (linkedSource != null) { linkedSource.Cancel(); linkedSource.Dispose(); } } finally { isDisposed = true; } } } public interface ITriggerHandler<T> { ITriggerHandler<T> Prev { get; set; } ITriggerHandler<T> Next { get; set; } void OnNext(T value); void OnError(Exception ex); void OnCompleted(); void OnCanceled(CancellationToken cancellationToken); } public struct TriggerEvent<T> { private ITriggerHandler<T> head; private ITriggerHandler<T> iteratingHead; private ITriggerHandler<T> iteratingNode; private void LogError(Exception ex) { Debug.LogException(ex); } public void SetResult(T value) { if (iteratingNode != null) { throw new InvalidOperationException("Can not trigger itself in iterating."); } for (ITriggerHandler<T> triggerHandler = head; triggerHandler != null; triggerHandler = ((triggerHandler == iteratingNode) ? triggerHandler.Next : iteratingNode)) { iteratingNode = triggerHandler; try { triggerHandler.OnNext(value); } catch (Exception ex) { LogError(ex); Remove(triggerHandler); } } iteratingNode = null; if (iteratingHead != null) { Add(iteratingHead); iteratingHead = null; } } public void SetCanceled(CancellationToken cancellationToken) { if (iteratingNode != null) { throw new InvalidOperationException("Can not trigger itself in iterating."); } ITriggerHandler<T> triggerHandler = head; while (triggerHandler != null) { iteratingNode = triggerHandler; try { triggerHandler.OnCanceled(cancellationToken); } catch (Exception ex) { LogError(ex); } ITriggerHandler<T> obj = ((triggerHandler == iteratingNode) ? triggerHandler.Next : iteratingNode); iteratingNode = null; Remove(triggerHandler); triggerHandler = obj; } iteratingNode = null; if (iteratingHead != null) { Add(iteratingHead); iteratingHead = null; } } public void SetCompleted() { if (iteratingNode != null) { throw new InvalidOperationException("Can not trigger itself in iterating."); } ITriggerHandler<T> triggerHandler = head; while (triggerHandler != null) { iteratingNode = triggerHandler; try { triggerHandler.OnCompleted(); } catch (Exception ex) { LogError(ex); } ITriggerHandler<T> obj = ((triggerHandler == iteratingNode) ? triggerHandler.Next : iteratingNode); iteratingNode = null; Remove(triggerHandler); triggerHandler = obj; } iteratingNode = null; if (iteratingHead != null) { Add(iteratingHead); iteratingHead = null; } } public void SetError(Exception exception) { if (iteratingNode != null) { throw new InvalidOperationException("Can not trigger itself in iterating."); } ITriggerHandler<T> triggerHandler = head; while (triggerHandler != null) { iteratingNode = triggerHandler; try { triggerHandler.OnError(exception); } catch (Exception ex) { LogError(ex); } ITriggerHandler<T> obj = ((triggerHandler == iteratingNode) ? triggerHandler.Next : iteratingNode); iteratingNode = null; Remove(triggerHandler); triggerHandler = obj; } iteratingNode = null; if (iteratingHead != null) { Add(iteratingHead); iteratingHead = null; } } public void Add(ITriggerHandler<T> handler) { if (handler == null) { throw new ArgumentNullException("handler"); } if (head == null) { head = handler; } else if (iteratingNode != null) { if (iteratingHead == null) { iteratingHead = handler; return; } ITriggerHandler<T> prev = iteratingHead.Prev; if (prev == null) { iteratingHead.Prev = handler; iteratingHead.Next = handler; handler.Prev = iteratingHead; } else { iteratingHead.Prev = handler; prev.Next = handler; handler.Prev = prev; } } else { ITriggerHandler<T> prev2 = head.Prev; if (prev2 == null) { head.Prev = handler; head.Next = handler; handler.Prev = head; } else { head.Prev = handler; prev2.Next = handler; handler.Prev = prev2; } } } public void Remove(ITriggerHandler<T> handler) { if (handler == null) { throw new ArgumentNullException("handler"); } ITriggerHandler<T> prev = handler.Prev; ITriggerHandler<T> next = handler.Next; if (next != null) { next.Prev = prev; } if (handler == head) { head = next; } else if (prev != null) { prev.Next = next; } if (handler == iteratingNode) { iteratingNode = next; } if (handler == iteratingHead) { iteratingHead = next; } if (head != null && head.Prev == handler) { if (prev != head) { head.Prev = prev; } else { head.Prev = null; } } if (iteratingHead != null && iteratingHead.Prev == handler) { if (prev != iteratingHead.Prev) { iteratingHead.Prev = prev; } else { iteratingHead.Prev = null; } } handler.Prev = null; handler.Next = null; } } public static class UniTaskCancellationExtensions { public static CancellationToken GetCancellationTokenOnDestroy(this GameObject gameObject) { return gameObject.GetAsyncDestroyTrigger().CancellationToken; } public static CancellationToken GetCancellationTokenOnDestroy(this Component component) { return component.GetAsyncDestroyTrigger().CancellationToken; } } [StructLayout(LayoutKind.Auto)] [AsyncMethodBuilder(typeof(AsyncUniTaskMethodBuilder))] public readonly struct UniTask { private sealed class AsyncUnitSource : IUniTaskSource<AsyncUnit>, IUniTaskSource { private readonly IUniTaskSource source; public AsyncUnitSource(IUniTaskSource source) { this.source = source; } public AsyncUnit GetResult(short token) { source.GetResult(token); return AsyncUnit.Default; } public UniTaskStatus GetStatus(short token) { return source.GetStatus(token); } public void OnCompleted(Action<object> continuation, object state, short token) { source.OnCompleted(continuation, state, token); } public UniTaskStatus UnsafeGetStatus() { return source.UnsafeGetStatus(); } void IUniTaskSource.GetResult(short token) { GetResult(token); } } private sealed class IsCanceledSource : IUniTaskSource<bool>, IUniTaskSource { private readonly IUniTaskSource source; public IsCanceledSource(IUniTaskSource source) { this.source = source; } public bool GetResult(short token) { if (source.GetStatus(token) == UniTaskStatus.Canceled) { return true; } source.GetResult(token); return false; } void IUniTaskSource.GetResult(short token) { GetResult(token); } public UniTaskStatus GetStatus(short token) { return source.GetStatus(token); } public UniTaskStatus UnsafeGetStatus() { return source.UnsafeGetStatus(); } public void OnCompleted(Action<object> continuation, object state, short token) { source.OnCompleted(continuation, state, token); } } private sealed class MemoizeSource : IUniTaskSource { private IUniTaskSource source; private ExceptionDispatchInfo exception; private UniTaskStatus status; public MemoizeSource(IUniTaskSource source) { this.source = source; } public void GetResult(short token) { if (source == null) { if (exception != null) { exception.Throw(); } return; } try { source.GetResult(token); status = UniTaskStatus.Succeeded; } catch (Exception ex) { exception = ExceptionDispatchInfo.Capture(ex); if (ex is OperationCanceledException) { status = UniTaskStatus.Canceled; } else { status = UniTaskStatus.Faulted; } throw; } finally { source = null; } } public UniTaskStatus GetStatus(short token) { if (source == null) { return status; } return source.GetStatus(token); } public void OnCompleted(Action<object> continuation, object state, short token) { if (source == null) { continuation(state); } else { source.OnCompleted(continuation, state, token); } } public UniTaskStatus UnsafeGetStatus() { if (source == null) { return status; } return source.UnsafeGetStatus(); } } public readonly struct Awaiter : ICriticalNotifyCompletion, INotifyCompletion { private readonly UniTask task; public bool IsCompleted { [MethodImpl(MethodImplOptions.AggressiveInlining)] [DebuggerHidden] get { return task.Status.IsCompleted(); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] [DebuggerHidden] public Awaiter(in UniTask task) { this.task = task; } [MethodImpl(MethodImplOptions.AggressiveInlining)] [DebuggerHidden] public void GetResult() { if (task.source != null) { task.source.GetResult(task.token); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] [DebuggerHidden] public void OnCompleted(Action continuation) { if (task.source == null) { continuation(); } else { task.source.OnCompleted(AwaiterActions.InvokeContinuationDelegate, continuation, task.token); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] [DebuggerHidden] public void UnsafeOnCompleted(Action continuation) { if (task.source == null) { continuation(); } else { task.source.OnCompleted(AwaiterActions.InvokeContinuationDelegate, continuation, task.token); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] [DebuggerHidden] public void SourceOnCompleted(Action<object> continuation, object state) { if (task.source == null) { continuation(state); } else { task.source.OnCompleted(continuation, state, task.token); } } } private sealed class YieldPromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<YieldPromise> { private static TaskPool<YieldPromise> pool; private YieldPromise nextNode; private CancellationToken cancellationToken; private CancellationTokenRegistration cancellationTokenRegistration; private bool cancelImmediately; private UniTaskCompletionSourceCore<object> core; public ref YieldPromise NextNode => ref nextNode; static YieldPromise() { TaskPool.RegisterSizeGetter(typeof(YieldPromise), () => pool.Size); } private YieldPromise() { } public static IUniTaskSource Create(PlayerLoopTiming timing, CancellationToken cancellationToken, bool cancelImmediately, out short token) { if (cancellationToken.IsCancellationRequested) { return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token); } if (!pool.TryPop(out var result)) { result = new YieldPromise(); } result.cancellationToken = cancellationToken; result.cancelImmediately = cancelImmediately; if (cancelImmediately && cancellationToken.CanBeCanceled) { result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(delegate(object state) { YieldPromise yieldPromise = (YieldPromise)state; yieldPromise.core.TrySetCanceled(yieldPromise.cancellationToken); }, result); } PlayerLoopHelper.AddAction(timing, result); token = result.core.Version; return result; } public void GetResult(short token) { try { core.GetResult(token); } finally { if (!cancelImmediately || !cancellationToken.IsCancellationRequested) { TryReturn(); } } } public UniTaskStatus GetStatus(short token) { return core.GetStatus(token); } public UniTaskStatus UnsafeGetStatus() { return core.UnsafeGetStatus(); } public void OnCompleted(Action<object> continuation, object state, short token) { core.OnCompleted(continuation, state, token); } public bool MoveNext() { if (cancellationToken.IsCancellationRequested) { core.TrySetCanceled(cancellationToken); return false; } core.TrySetResult(null); return false; } private bool TryReturn() { core.Reset(); cancellationToken = default(CancellationToken); cancellationTokenRegistration.Dispose(); cancelImmediately = false; return pool.TryPush(this); } } private sealed class NextFramePromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<NextFramePromise> { private static TaskPool<NextFramePromise> pool; private NextFramePromise nextNode; private int frameCount; private UniTaskCompletionSourceCore<AsyncUnit> core; private CancellationToken cancellationToken; private CancellationTokenRegistration cancellationTokenRegistration; private bool cancelImmediately; public ref NextFramePromise NextNode => ref nextNode; static NextFramePromise() { TaskPool.RegisterSizeGetter(typeof(NextFramePromise), () => pool.Size); } private NextFramePromise() { } public static IUniTaskSource Create(PlayerLoopTiming timing, CancellationToken cancellationToken, bool cancelImmediately, out short token) { if (cancellationToken.IsCancellationRequested) { return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token); } if (!pool.TryPop(out var result)) { result = new NextFramePromise(); } result.frameCount = (PlayerLoopHelper.IsMainThread ? Time.frameCount : (-1)); result.cancellationToken = cancellationToken; result.cancelImmediately = cancelImmediately; if (cancelImmediately && cancellationToken.CanBeCanceled) { result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(delegate(object state) { NextFramePromise nextFramePromise = (NextFramePromise)state; nextFramePromise.core.TrySetCanceled(nextFramePromise.cancellationToken); }, result); } PlayerLoopHelper.AddAction(timing, result); token = result.core.Version; return result; } public void GetResult(short token) { try { core.GetResult(token); } finally { if (!cancelImmediately || !cancellationToken.IsCancellationRequested) { TryReturn(); } } } public UniTaskStatus GetStatus(short token) { return core.GetStatus(token); } public UniTa