using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using BepInEx.Core.Logging.Interpolation; using BepInEx.Logging; using BepInEx.Unity.IL2CPP; using HarmonyLib; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.SceneManagement; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("Exothermic.AllSleepNightSkip")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.1.6.0")] [assembly: AssemblyInformationalVersion("0.1.6+d7c1a503fe21d48de2ab96880f13536f394ff5ff")] [assembly: AssemblyProduct("Exothermic.AllSleepNightSkip")] [assembly: AssemblyTitle("Exothermic.AllSleepNightSkip")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.1.5.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace Exothermic.AllSleepNightSkip { [BepInPlugin("com.exothermic.aska.allsleepnightskip", "Exothermic All-Sleep Night Skip", "0.1.6")] public sealed class Plugin : BasePlugin { public const string PluginGuid = "com.exothermic.aska.allsleepnightskip"; public const string PluginName = "Exothermic All-Sleep Night Skip"; public const string PluginVersion = "0.1.5"; internal static ManualLogSource Log; internal static ConfigEntry Enabled; internal static ConfigEntry RequiredSleepFraction; internal static ConfigEntry MinimumPlayers; internal static ConfigEntry StartDelaySeconds; internal static ConfigEntry FastForwardScale; internal static ConfigEntry MaximumFastForwardRealSeconds; internal static ConfigEntry OnlyAtNight; internal static ConfigEntry AllowUnknownNightState; internal static ConfigEntry MorningHour; internal static ConfigEntry PlayerScanInterval; internal static ConfigEntry AuthorityOnly; internal static ConfigEntry DiagnosticLogging; internal static ConfigEntry DumpDiscoveryOnWorldLoad; public override void Load() { //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Expected O, but got Unknown //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Expected O, but got Unknown //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Expected O, but got Unknown //IL_0105: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Expected O, but got Unknown //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Expected O, but got Unknown //IL_01bf: Unknown result type (might be due to invalid IL or missing references) //IL_01c9: Expected O, but got Unknown //IL_01fc: Unknown result type (might be due to invalid IL or missing references) //IL_0206: Expected O, but got Unknown //IL_0281: Unknown result type (might be due to invalid IL or missing references) //IL_0287: Expected O, but got Unknown Log = ((BasePlugin)this).Log; Enabled = ((BasePlugin)this).Config.Bind("General", "Enabled", true, "Enable automatic night fast-forwarding when the required players are asleep."); RequiredSleepFraction = ((BasePlugin)this).Config.Bind("Sleep", "RequiredSleepFraction", 1f, new ConfigDescription("Fraction of active players that must be asleep. 1.0 means everyone.", (AcceptableValueBase)(object)new AcceptableValueRange(0.25f, 1f), Array.Empty())); MinimumPlayers = ((BasePlugin)this).Config.Bind("Sleep", "MinimumPlayers", 1, new ConfigDescription("Minimum number of active players required.", (AcceptableValueBase)(object)new AcceptableValueRange(1, 64), Array.Empty())); StartDelaySeconds = ((BasePlugin)this).Config.Bind("Sleep", "StartDelaySeconds", 3f, new ConfigDescription("All players must remain asleep for this many real seconds before fast-forward starts.", (AcceptableValueBase)(object)new AcceptableValueRange(0.5f, 30f), Array.Empty())); FastForwardScale = ((BasePlugin)this).Config.Bind("FastForward", "TimeScale", 20f, new ConfigDescription("Unity simulation speed while skipping. Lower this if the server stutters.", (AcceptableValueBase)(object)new AcceptableValueRange(2f, 100f), Array.Empty())); MaximumFastForwardRealSeconds = ((BasePlugin)this).Config.Bind("FastForward", "MaximumRealSeconds", 120f, new ConfigDescription("Safety timeout measured in real seconds. The normal time scale is always restored afterward.", (AcceptableValueBase)(object)new AcceptableValueRange(5f, 600f), Array.Empty())); OnlyAtNight = ((BasePlugin)this).Config.Bind("FastForward", "OnlyAtNight", true, "Do not begin fast-forwarding when the discovered world clock says it is daytime."); AllowUnknownNightState = ((BasePlugin)this).Config.Bind("FastForward", "AllowUnknownNightState", true, "Allow the skip when the mod cannot identify ASKA's clock. Disable this to prevent daytime naps from triggering it."); MorningHour = ((BasePlugin)this).Config.Bind("FastForward", "MorningHour", 6f, new ConfigDescription("Hour at which the skip should stop when a 0-24 world clock is discovered.", (AcceptableValueBase)(object)new AcceptableValueRange(4f, 10f), Array.Empty())); PlayerScanInterval = ((BasePlugin)this).Config.Bind("Advanced", "PlayerScanIntervalSeconds", 1f, new ConfigDescription("Real-time interval between player and sleep-state checks.", (AcceptableValueBase)(object)new AcceptableValueRange(0.25f, 5f), Array.Empty())); AuthorityOnly = ((BasePlugin)this).Config.Bind("Advanced", "AuthorityOnly", true, "Only alter time on a dedicated server, single-player host, Fusion server, or shared-mode master client."); DiagnosticLogging = ((BasePlugin)this).Config.Bind("Diagnostics", "VerboseLogging", false, "Write player, sleep-state, clock, and authority details to BepInEx/LogOutput.log."); DumpDiscoveryOnWorldLoad = ((BasePlugin)this).Config.Bind("Diagnostics", "DumpDiscoveryOnWorldLoad", false, "Dump sleep- and clock-related components/members once after entering the world."); CharacterSpawnTracker.Install(); ((BasePlugin)this).AddComponent(); ManualLogSource log = Log; bool flag = default(bool); BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(96, 7, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendFormatted("Exothermic All-Sleep Night Skip"); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" v"); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted("0.1.5"); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" loaded. Enabled="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(Enabled.Value); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(", AuthorityOnly="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(AuthorityOnly.Value); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(", VerboseLogging="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(DiagnosticLogging.Value); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(", DumpDiscoveryOnWorldLoad="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(DumpDiscoveryOnWorldLoad.Value); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(", ScanInterval="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(PlayerScanInterval.Value, "0.##"); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("s."); } log.LogInfo(val); } } internal static class CharacterSpawnTracker { private static readonly object Sync = new object(); private static readonly List SpawnedCharacters = new List(); private static bool _installAttempted; public static void Install() { //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0103: Expected O, but got Unknown //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Expected O, but got Unknown //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00ab: Expected O, but got Unknown if (_installAttempted) { return; } _installAttempted = true; bool flag = default(bool); try { Type resolvedCharacterType = PlayerProbe.GetResolvedCharacterType(); if (resolvedCharacterType == null) { Plugin.Log.LogWarning((object)"Character.Spawned tracker not installed: SSSGame.Character wrapper was not found. Exact-type scanning will still be used."); return; } MethodInfo methodInfo = AccessTools.Method(resolvedCharacterType, "Spawned", Type.EmptyTypes, (Type[])null); MethodInfo methodInfo2 = AccessTools.Method(typeof(CharacterSpawnTracker), "AfterCharacterSpawned", (Type[])null, (Type[])null); if (methodInfo == null || methodInfo2 == null) { Plugin.Log.LogWarning((object)"Character.Spawned tracker not installed: Spawned method or postfix was not found."); return; } new Harmony("com.exothermic.aska.allsleepnightskip.character-tracker").Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); ManualLogSource log = Plugin.Log; BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(41, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Character.Spawned tracker installed on "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(resolvedCharacterType.FullName); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("."); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(methodInfo.Name); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("."); } log.LogInfo(val); } catch (Exception ex) { ManualLogSource log2 = Plugin.Log; BepInExWarningLogInterpolatedStringHandler val2 = new BepInExWarningLogInterpolatedStringHandler(49, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral("Character.Spawned tracker installation failed: "); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted(ex.GetType().Name); ((BepInExLogInterpolatedStringHandler)val2).AppendLiteral(": "); ((BepInExLogInterpolatedStringHandler)val2).AppendFormatted(ex.Message); } log2.LogWarning(val2); } } private static void AfterCharacterSpawned(object __instance) { Component val = (Component)((__instance is Component) ? __instance : null); if (val == null || (Object)(object)val == (Object)null) { return; } lock (Sync) { for (int num = SpawnedCharacters.Count - 1; num >= 0; num--) { object? target = SpawnedCharacters[num].Target; Component val2 = (Component)((target is Component) ? target : null); if (val2 == null || (Object)(object)val2 == (Object)null) { SpawnedCharacters.RemoveAt(num); } else if (((Object)val2).GetInstanceID() == ((Object)val).GetInstanceID()) { return; } } SpawnedCharacters.Add(new WeakReference(val)); } } public static List Snapshot() { List list = new List(); lock (Sync) { for (int num = SpawnedCharacters.Count - 1; num >= 0; num--) { object? target = SpawnedCharacters[num].Target; Component val = (Component)((target is Component) ? target : null); if (val == null || (Object)(object)val == (Object)null) { SpawnedCharacters.RemoveAt(num); } else { list.Add(val); } } return list; } } } public sealed class NightSkipBehaviour : MonoBehaviour { private readonly ReflectionCache _reflection = new ReflectionCache(); private readonly WorldClockProbe _clock = new WorldClockProbe(); private float _nextScanRealtime; private float _allSleepingSince = -1f; private float _fastForwardStartedRealtime; private float _previousTimeScale = 1f; private float _previousFixedDeltaTime = 0.02f; private bool _fastForwarding; private bool _sleepCycleLatched; private int _discoveryDumpsForScene; private float _nextDiscoveryDumpRealtime; private string _lastSceneName = string.Empty; private bool _sawNightDuringFastForward; private void Awake() { Plugin.Log.LogInfo((object)"NightSkipBehaviour is active; Update loop started."); } private void Update() { //IL_02d4: Unknown result type (might be due to invalid IL or missing references) //IL_02db: Expected O, but got Unknown //IL_0590: Unknown result type (might be due to invalid IL or missing references) //IL_0597: Expected O, but got Unknown if (!Plugin.Enabled.Value) { StopFastForward("mod disabled"); ResetSleepReadiness(); return; } float realtimeSinceStartup = Time.realtimeSinceStartup; if (realtimeSinceStartup < _nextScanRealtime) { return; } _nextScanRealtime = realtimeSinceStartup + Plugin.PlayerScanInterval.Value; string text = SafeActiveSceneName(); if (!string.Equals(text, _lastSceneName, StringComparison.Ordinal)) { StopFastForward("scene changed"); ResetSleepReadiness(); _clock.Reset(); _reflection.ResetSceneCaches(); _discoveryDumpsForScene = 0; _nextDiscoveryDumpRealtime = 0f; _lastSceneName = text; } bool flag = LooksLikeGameplayScene(text); AuthorityState authorityState = AuthorityProbe.GetAuthorityState(_reflection); List list = PlayerProbe.FindActivePlayers(_reflection); int num = 0; foreach (PlayerRecord item in list) { if ((item.SleepState = SleepProbe.GetSleepState(item, _reflection)).IsSleeping) { num++; } } int num2 = ((list.Count > 0) ? Mathf.CeilToInt((float)list.Count * Plugin.RequiredSleepFraction.Value) : Plugin.MinimumPlayers.Value); bool flag2 = list.Count >= Plugin.MinimumPlayers.Value && num >= num2; ClockState clockState = _clock.Sample(_reflection, Plugin.MorningHour.Value); bool flag3 = list.Count > 0 && list.All((PlayerRecord p) => p.SleepState.Confidence <= 0f); bool flag4 = _discoveryDumpsForScene == 0 || (flag3 && _discoveryDumpsForScene < 3 && realtimeSinceStartup >= _nextDiscoveryDumpRealtime); if (Plugin.DumpDiscoveryOnWorldLoad.Value && list.Count > 0 && flag4) { _discoveryDumpsForScene++; _nextDiscoveryDumpRealtime = realtimeSinceStartup + 15f; DiscoveryDump.Write(list, authorityState, clockState, _reflection); } bool flag5 = default(bool); if (Plugin.DiagnosticLogging.Value) { string text2 = string.Join(", ", list.Select((PlayerRecord p) => $"{p.Name}:{p.SleepState}")); string text3 = ((Plugin.AuthorityOnly.Value && authorityState == AuthorityState.NotAuthority) ? "blocked:not-authority" : ((list.Count < Plugin.MinimumPlayers.Value) ? $"waiting:players<{Plugin.MinimumPlayers.Value}" : (flag2 ? "ready:all-sleeping" : "waiting:sleep"))); ManualLogSource log = Plugin.Log; BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(112, 12, ref flag5); if (flag5) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("scan gate="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(text3); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" playerProbe="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(PlayerProbe.LastProbeSummary); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" activeScene='"); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(text); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("' sceneHint="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(flag); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" loadedScenes=["); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(SafeLoadedSceneNames()); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("] authority="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(authorityState); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" players="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(list.Count); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" sleeping="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(num); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("/"); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(num2); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" clock="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(clockState); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" fast="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(_fastForwarding); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" ["); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(text2); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("]"); } log.LogInfo(val); } if (Plugin.AuthorityOnly.Value && authorityState == AuthorityState.NotAuthority) { StopFastForward("this peer is not the world authority"); ResetSleepReadiness(); } else if (list.Count < Plugin.MinimumPlayers.Value) { StopFastForward("not enough active players"); ResetSleepReadiness(); } else if (_fastForwarding) { if (clockState.IsNight == true) { _sawNightDuringFastForward = true; } if (!flag2) { StopFastForward("a required player woke up or disconnected"); _sleepCycleLatched = true; } else if (clockState.IsMorning == true || (_sawNightDuringFastForward && clockState.IsNight == false)) { StopFastForward("morning reached"); _sleepCycleLatched = true; } else if (realtimeSinceStartup - _fastForwardStartedRealtime >= Plugin.MaximumFastForwardRealSeconds.Value) { StopFastForward("safety timeout reached"); _sleepCycleLatched = true; } else { EnsureFastForwardScale(); } } else if (!flag2) { ResetSleepReadiness(); _sleepCycleLatched = false; } else { if (_sleepCycleLatched) { return; } if (Plugin.OnlyAtNight.Value) { bool? isNight = clockState.IsNight; flag5 = false; if (isNight == flag5) { ResetSleepReadiness(); return; } if (!clockState.IsNight.HasValue && !Plugin.AllowUnknownNightState.Value) { ResetSleepReadiness(); return; } } if (_allSleepingSince < 0f) { _allSleepingSince = realtimeSinceStartup; ManualLogSource log2 = Plugin.Log; BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(59, 3, ref flag5); if (flag5) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Sleep threshold reached ("); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(num); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("/"); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(list.Count); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("); starting "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(Plugin.StartDelaySeconds.Value, "0.0"); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("s confirmation delay."); } log2.LogInfo(val); } else if (realtimeSinceStartup - _allSleepingSince >= Plugin.StartDelaySeconds.Value) { StartFastForward(list.Count, num, clockState, authorityState); } } } private void StartFastForward(int playerCount, int sleepingCount, ClockState clockState, AuthorityState authority) { //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Expected O, but got Unknown if (!_fastForwarding) { _previousTimeScale = Time.timeScale; _previousFixedDeltaTime = Time.fixedDeltaTime; _fastForwardStartedRealtime = Time.realtimeSinceStartup; _sawNightDuringFastForward = clockState.IsNight == true; _fastForwarding = true; EnsureFastForwardScale(); ManualLogSource log = Plugin.Log; bool flag = default(bool); BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(68, 5, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Night fast-forward started: sleeping="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(sleepingCount); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("/"); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(playerCount); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(", scale="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(Plugin.FastForwardScale.Value, "0.##"); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("x, authority="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(authority); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(", clock="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(clockState); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("."); } log.LogWarning(val); } } private void EnsureFastForwardScale() { float value = Plugin.FastForwardScale.Value; if (!Mathf.Approximately(Time.timeScale, value)) { Time.timeScale = value; } if (!Mathf.Approximately(Time.fixedDeltaTime, _previousFixedDeltaTime)) { Time.fixedDeltaTime = _previousFixedDeltaTime; } } private void StopFastForward(string reason) { //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Expected O, but got Unknown if (_fastForwarding) { Time.timeScale = Mathf.Max(0.01f, _previousTimeScale); Time.fixedDeltaTime = Mathf.Max(0.0001f, _previousFixedDeltaTime); _fastForwarding = false; _allSleepingSince = -1f; ManualLogSource log = Plugin.Log; bool flag = default(bool); BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(50, 2, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Night fast-forward stopped: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(reason); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(". Restored timeScale="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(Time.timeScale, "0.###"); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("."); } log.LogWarning(val); } } private void ResetSleepReadiness() { _allSleepingSince = -1f; } private void OnDestroy() { StopFastForward("plugin behaviour destroyed"); } private void OnApplicationQuit() { StopFastForward("application quitting"); } private static bool LooksLikeGameplayScene(string sceneName) { if (string.IsNullOrWhiteSpace(sceneName)) { return false; } string text = sceneName.ToLowerInvariant(); if (!text.Contains("streamingworld") && !text.Contains("world")) { return text.Contains("game"); } return true; } private static string SafeActiveSceneName() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) try { Scene activeScene = SceneManager.GetActiveScene(); return ((Scene)(ref activeScene)).name ?? string.Empty; } catch { return string.Empty; } } private static string SafeLoadedSceneNames() { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) try { List list = new List(); for (int i = 0; i < SceneManager.sceneCount; i++) { Scene sceneAt = SceneManager.GetSceneAt(i); if (((Scene)(ref sceneAt)).IsValid()) { list.Add(((Scene)(ref sceneAt)).name ?? string.Empty); } } return string.Join(",", list); } catch (Exception ex) { return ""; } } } internal enum AuthorityState { Unknown, Authority, NotAuthority } internal sealed class PlayerRecord { public GameObject Root { get; set; } public Component CharacterComponent { get; set; } public string Name { get; set; } = string.Empty; public SleepState SleepState { get; set; } = SleepState.Unknown("not sampled"); } internal readonly struct SleepState { public bool IsSleeping { get; } public float Confidence { get; } public string Reason { get; } public SleepState(bool isSleeping, float confidence, string reason) { IsSleeping = isSleeping; Confidence = confidence; Reason = reason; } public static SleepState Sleeping(float confidence, string reason) { return new SleepState(isSleeping: true, confidence, reason); } public static SleepState Awake(float confidence, string reason) { return new SleepState(isSleeping: false, confidence, reason); } public static SleepState Unknown(string reason) { return new SleepState(isSleeping: false, 0f, reason); } public override string ToString() { return $"{(IsSleeping ? "sleep" : ((Confidence > 0f) ? "awake" : "unknown"))}({Confidence:0.00}:{Reason})"; } } internal readonly struct ClockState { public bool? IsNight { get; } public bool? IsMorning { get; } public double? Hour { get; } public string Source { get; } public ClockState(bool? isNight, bool? isMorning, double? hour, string source) { IsNight = isNight; IsMorning = isMorning; Hour = hour; Source = source; } public static ClockState Unknown(string source) { return new ClockState(null, null, null, source); } public override string ToString() { string value = (Hour.HasValue ? Hour.Value.ToString("0.00") : "?"); return $"night={IsNight?.ToString() ?? "?"},morning={IsMorning?.ToString() ?? "?"},hour={value},source={Source}"; } } internal static class PlayerProbe { private static readonly string[] CharacterTypeHints = new string[4] { "SSSGame.Character", ".PlayerCharacter", ".NetworkPlayer", ".PlayerController" }; private static Type _characterType; private static MethodInfo _resourcesFindAllGeneric; private static MethodInfo _objectFindAllGeneric; public static string LastProbeSummary { get; private set; } = "not-run"; public static List FindActivePlayers(ReflectionCache cache) { List list = new List(); HashSet hashSet = new HashSet(); int num = 0; int num2 = 0; int num3 = 0; int num4 = 0; string text = string.Empty; foreach (Component item in CharacterSpawnTracker.Snapshot()) { num++; TryAddCharacter(item, cache, list, hashSet); } Type type = ResolveCharacterType(); if (type != null) { try { foreach (Component item2 in FindAllComponentsOfExactType(type)) { num2++; TryAddCharacter(item2, cache, list, hashSet); } } catch (Exception ex) { text = ex.GetType().Name; } } IEnumerable enumerable; try { enumerable = (IEnumerable)Object.FindObjectsOfType(); } catch { enumerable = Array.Empty(); } foreach (MonoBehaviour item3 in enumerable) { if (!((Object)(object)item3 == (Object)null) && !((Object)(object)((Component)item3).gameObject == (Object)null)) { Type type2 = ((object)item3).GetType(); string fullName = type2.FullName ?? type2.Name; if (CharacterTypeHints.Any((string h) => fullName.Contains(h, StringComparison.OrdinalIgnoreCase))) { num3++; TryAddCharacter((Component)(object)item3, cache, list, hashSet); } } } try { foreach (GameObject item4 in (Il2CppArrayBase)(object)GameObject.FindGameObjectsWithTag("Player")) { num4++; if (!((Object)(object)item4 == (Object)null) && hashSet.Add(((Object)item4).GetInstanceID())) { Component val = ((IEnumerable)item4.GetComponentsInChildren(true)).FirstOrDefault((Func)((Component c) => (Object)(object)c != (Object)null && (((object)c).GetType().FullName ?? ((object)c).GetType().Name).Contains("Character", StringComparison.OrdinalIgnoreCase))); if (!((Object)(object)val == (Object)null)) { list.Add(new PlayerRecord { Root = item4, CharacterComponent = val, Name = BuildPlayerName(item4, list.Count + 1) }); } } } } catch { } string value = type?.FullName ?? "missing"; LastProbeSummary = $"type={value},tracked={num},exact={num2},broad={num3},tagged={num4},accepted={list.Count}" + (string.IsNullOrEmpty(text) ? string.Empty : (",exactError=" + text)); return list; } private static void TryAddCharacter(Component character, ReflectionCache cache, List result, HashSet seen) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)character == (Object)null || (Object)(object)character.gameObject == (Object)null) { return; } try { Scene scene = character.gameObject.scene; if (!((Scene)(ref scene)).IsValid() || !((Scene)(ref scene)).isLoaded) { return; } } catch { return; } Type type = ((object)character).GetType(); string? obj2 = type.FullName ?? type.Name; string text = ((Object)character.gameObject).name ?? string.Empty; bool flag = text.Contains("CharacterAska", StringComparison.OrdinalIgnoreCase) || text.Contains("PlayerCharacter", StringComparison.OrdinalIgnoreCase); bool flag2 = obj2.Contains("Player", StringComparison.OrdinalIgnoreCase) || flag; MethodInfo method = cache.GetMethod(type, "IsPlayer", Type.EmptyTypes); if (method != null) { try { if (method.Invoke(character, null) is bool flag3) { flag2 = flag3 || flag; } } catch { } } if (!flag2 && _characterType != null && _characterType.IsInstanceOfType(character)) { _ = method == null; } else if (flag2) { GameObject val = FindLikelyPlayerRoot(character.gameObject); int instanceID = ((Object)val).GetInstanceID(); if (seen.Add(instanceID)) { result.Add(new PlayerRecord { Root = val, CharacterComponent = character, Name = BuildPlayerName(val, result.Count + 1) }); } } } internal static Type GetResolvedCharacterType() { return ResolveCharacterType(); } private static Type ResolveCharacterType() { if (_characterType != null) { return _characterType; } Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { try { Type type = assembly.GetType("SSSGame.Character", throwOnError: false, ignoreCase: false); if (type != null && typeof(Component).IsAssignableFrom(type)) { _characterType = type; return _characterType; } } catch { } } assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly2 in assemblies) { try { Type type2 = assembly2.GetTypes().FirstOrDefault((Type t) => typeof(Component).IsAssignableFrom(t) && string.Equals(t.Name, "Character", StringComparison.OrdinalIgnoreCase) && cacheHasIsPlayer(t)); if (type2 != null) { _characterType = type2; return _characterType; } } catch { } } return null; static bool cacheHasIsPlayer(Type type3) { try { return type3.GetMethod("IsPlayer", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, Type.EmptyTypes, null) != null; } catch { return false; } } } private static IEnumerable FindAllComponentsOfExactType(Type targetType) { List list = new List(); try { if ((object)_resourcesFindAllGeneric == null) { _resourcesFindAllGeneric = typeof(Resources).GetMethods(BindingFlags.Static | BindingFlags.Public).FirstOrDefault((MethodInfo m) => m.Name == "FindObjectsOfTypeAll" && m.IsGenericMethodDefinition && m.GetParameters().Length == 0); } if (_resourcesFindAllGeneric != null) { foreach (object item in Enumerate(_resourcesFindAllGeneric.MakeGenericMethod(targetType).Invoke(null, null))) { Component val = (Component)((item is Component) ? item : null); if (val != null) { list.Add(val); } } } } catch { } if (list.Count > 0) { return list; } try { if ((object)_objectFindAllGeneric == null) { _objectFindAllGeneric = typeof(Object).GetMethods(BindingFlags.Static | BindingFlags.Public).FirstOrDefault((MethodInfo m) => m.Name == "FindObjectsOfType" && m.IsGenericMethodDefinition && m.GetParameters().Length == 1 && m.GetParameters()[0].ParameterType == typeof(bool)); } if (_objectFindAllGeneric != null) { foreach (object item2 in Enumerate(_objectFindAllGeneric.MakeGenericMethod(targetType).Invoke(null, new object[1] { true }))) { Component val2 = (Component)((item2 is Component) ? item2 : null); if (val2 != null) { list.Add(val2); } } } } catch { } return list; } private static IEnumerable Enumerate(object raw) { if (!(raw is IEnumerable enumerable)) { yield break; } foreach (object item in enumerable) { yield return item; } } private static GameObject FindLikelyPlayerRoot(GameObject origin) { Transform val = origin.transform; GameObject result = origin; for (int i = 0; i < 6; i++) { if (!((Object)(object)val != (Object)null)) { break; } string text = ((Object)val).name.ToLowerInvariant(); if (text.Contains("player") || text.Contains("character")) { result = ((Component)val).gameObject; } val = val.parent; } return result; } private static string BuildPlayerName(GameObject root, int fallbackIndex) { string text = ((Object)root).name; if (string.IsNullOrWhiteSpace(text)) { text = $"Player{fallbackIndex}"; } return text; } } internal static class SleepProbe { private sealed class GraphNode { public object Value { get; } public string Path { get; } public int Depth { get; } public GraphNode(object value, string path, int depth) { Value = value; Path = path; Depth = depth; } } private static readonly string[] ExactSleepNames = new string[14] { "IsSleeping", "Sleeping", "IsAsleep", "Asleep", "InBed", "IsInBed", "_isSleeping", "_sleeping", "_isAsleep", "_asleep", "_inBed", "_isInBed", "m_IsSleeping", "m_Sleeping" }; private static readonly string[] SleepTokens = new string[7] { "sleep", "asleep", "inbed", "bedinteraction", "resting", "lying", "laying" }; private static readonly string[] StrongSleepTokens = new string[12] { "issleeping", "sleeping", "asleep", "inbed", "sleepinteraction", "bedinteraction", "restinteraction", "resting", "lyingdown", "layingdown", "wakeupinteraction", "wakeup" }; private static readonly string[] StateMemberTokens = new string[8] { "state", "status", "action", "activity", "interaction", "pose", "mode", "moveset" }; private static readonly string[] GraphContextTokens = new string[20] { "current", "active", "selected", "running", "interaction", "action", "activity", "state", "status", "pose", "mode", "moveset", "target", "task", "agent", "controller", "station", "occup", "animation", "anim" }; private static readonly string[] GraphSkipMemberTokens = new string[16] { "gameobject", "transform", "parent", "root", "children", "material", "renderer", "mesh", "texture", "shader", "sprite", "audio", "camera", "light", "collider", "rigidbody" }; private const int MaxGraphDepth = 4; private const int MaxGraphNodes = 128; public static SleepState GetSleepState(PlayerRecord player, ReflectionCache cache) { SleepState result = SleepState.Unknown("no sleep signal"); IEnumerable componentsInChildren; try { componentsInChildren = (IEnumerable)player.Root.GetComponentsInChildren(true); } catch { return result; } Component[] array = componentsInChildren.Where((Component c) => (Object)(object)c != (Object)null).ToArray(); Component[] array2 = array; foreach (Component val in array2) { Type type = ((object)val).GetType(); string text = type.FullName ?? type.Name; MemberAccessor[] readableMembers = cache.GetReadableMembers(type); foreach (MemberAccessor memberAccessor in readableMembers) { string memberName = memberAccessor.Name; int num3 = Array.FindIndex(ExactSleepNames, (string n) => string.Equals(n, memberName, StringComparison.OrdinalIgnoreCase)); if (memberAccessor.ValueType == typeof(bool) && num3 >= 0) { if (memberAccessor.TryGet(val, out var value) && value is bool flag) { float num4 = 1f - (float)num3 * 0.01f; if (flag) { return SleepState.Sleeping(num4, type.Name + "." + memberName + "=true"); } if (num4 > result.Confidence) { result = SleepState.Awake(num4, type.Name + "." + memberName + "=false"); } } continue; } bool flag2 = ContainsAny(memberName, SleepTokens); object value3; if (memberAccessor.ValueType == typeof(bool) && flag2) { if (memberAccessor.TryGet(val, out var value2) && value2 is bool flag3) { float num5 = 0.92f; if (flag3) { return SleepState.Sleeping(num5, type.Name + "." + memberName + "=true"); } if (num5 > result.Confidence) { result = SleepState.Awake(num5, type.Name + "." + memberName + "=false"); } } } else if ((ContainsAny(memberName, StateMemberTokens) || flag2) && memberAccessor.TryGet(val, out value3) && value3 != null) { string value4 = SafeDescribe(value3); if (ContainsStrongSleepSignal(value4)) { return SleepState.Sleeping(0.9f, $"{type.Name}.{memberName}={Shorten(value4, 80)}"); } } } if (ContainsStrongSleepSignal(text)) { bool activeInHierarchy = val.gameObject.activeInHierarchy; Behaviour val2 = (Behaviour)(object)((val is Behaviour) ? val : null); if (activeInHierarchy && ((Object)(object)val2 == (Object)null || val2.enabled)) { return SleepState.Sleeping(0.72f, "active component " + text); } } Animator val3 = (Animator)(object)((val is Animator) ? val : null); if (val3 != null) { SleepState sleepState = ReadAnimator(val3); if (sleepState.IsSleeping) { return sleepState; } if (sleepState.Confidence > result.Confidence) { result = sleepState; } } } string summary; SleepState sleepState2 = ReadInteractionGraph(player, array, cache, out summary); if (sleepState2.IsSleeping) { return sleepState2; } if (sleepState2.Confidence > result.Confidence) { result = sleepState2; } string text2 = BuildHierarchySignal(player.Root.transform); if (ContainsAny(text2, new string[4] { "sleeping", "inbed", "bedinteraction", "sleepinteraction" })) { return SleepState.Sleeping(0.6f, "hierarchy=" + text2); } if (result.Confidence > 0f) { return result; } return SleepState.Unknown(string.IsNullOrEmpty(summary) ? "no sleep signal" : ("no sleep signal; graph=" + summary)); } private static SleepState ReadInteractionGraph(PlayerRecord player, Component[] components, ReflectionCache cache, out string summary) { Queue queue = new Queue(); HashSet hashSet = new HashSet(); List list = new List(); EnqueueGraphRoot(queue, player.CharacterComponent, "Character", 0); foreach (Component val in components) { string value = ((object)val).GetType().FullName ?? ((object)val).GetType().Name; if (ContainsAny(value, GraphContextTokens) || ContainsAny(value, SleepTokens)) { EnqueueGraphRoot(queue, val, ((object)val).GetType().Name, 0); } } int num = 0; SleepState result = SleepState.Unknown("no interaction sleep signal"); while (queue.Count > 0 && num++ < 128) { GraphNode graphNode = queue.Dequeue(); object value2 = graphNode.Value; if (value2 == null) { continue; } int hashCode; try { hashCode = value2.GetHashCode(); } catch { hashCode = graphNode.Path.GetHashCode(); } if (!hashSet.Add(hashCode)) { continue; } Type type; try { type = value2.GetType(); } catch { continue; } string text = type.FullName ?? type.Name; bool flag = ContainsAny(graphNode.Path, GraphContextTokens); string text2 = SafeDescribe(value2); if (flag && (ContainsStrongSleepSignal(text) || ContainsStrongSleepSignal(text2))) { summary = JoinNotes(list); return SleepState.Sleeping(0.91f, "graph:" + graphNode.Path + "=" + Shorten(text + ":" + text2, 100)); } MemberAccessor[] readableMembers; try { readableMembers = cache.GetReadableMembers(type); } catch { continue; } MemberAccessor[] array = readableMembers; foreach (MemberAccessor memberAccessor in array) { string memberName = memberAccessor.Name; if (ContainsAny(memberName, GraphSkipMemberTokens) || !memberAccessor.TryGet(value2, out var value3) || value3 == null) { continue; } string text3 = graphNode.Path + "." + memberName; bool flag2 = flag || ContainsAny(memberName, GraphContextTokens); int num2 = Array.FindIndex(ExactSleepNames, (string n) => string.Equals(n, memberName, StringComparison.OrdinalIgnoreCase)); if (value3 is bool flag3) { if (num2 >= 0 || ContainsAny(memberName, SleepTokens)) { float num3 = ((num2 >= 0) ? 0.98f : 0.9f); if (flag3) { summary = JoinNotes(list); return SleepState.Sleeping(num3, "graph:" + text3 + "=true"); } if (num3 > result.Confidence) { result = SleepState.Awake(num3, "graph:" + text3 + "=false"); } } continue; } string text4; try { text4 = value3.GetType().FullName ?? value3.GetType().Name; } catch { text4 = string.Empty; } string text5 = SafeDescribe(value3); string value4 = memberName + " " + text4 + " " + text5; if (flag2 && ContainsStrongSleepSignal(value4)) { summary = JoinNotes(list); return SleepState.Sleeping(0.93f, "graph:" + text3 + "=" + Shorten(text4 + ":" + text5, 100)); } if (flag2 && list.Count < 6) { list.Add(Shorten(text3 + "=" + text4 + ":" + text5, 90)); } if (graphNode.Depth < 4 && ShouldTraverse(value3, memberName, flag2)) { queue.Enqueue(new GraphNode(value3, text3, graphNode.Depth + 1)); } } } summary = JoinNotes(list); return result; } internal static List DescribeInteractionGraph(PlayerRecord player, ReflectionCache cache, int maxLines) { List list = new List(); Queue queue = new Queue(); HashSet hashSet = new HashSet(); EnqueueGraphRoot(queue, player.CharacterComponent, "Character", 0); Component[] array; try { array = ((IEnumerable)player.Root.GetComponentsInChildren(true)).Where((Component c) => (Object)(object)c != (Object)null).ToArray(); } catch { array = Array.Empty(); } Component[] array2 = array; foreach (Component val in array2) { string value = ((object)val).GetType().FullName ?? ((object)val).GetType().Name; if (ContainsAny(value, GraphContextTokens) || ContainsAny(value, SleepTokens)) { EnqueueGraphRoot(queue, val, ((object)val).GetType().Name, 0); } } int num2 = 0; while (queue.Count > 0 && num2++ < 128 && list.Count < maxLines) { GraphNode graphNode = queue.Dequeue(); object value2 = graphNode.Value; if (value2 == null) { continue; } int hashCode; try { hashCode = value2.GetHashCode(); } catch { hashCode = graphNode.Path.GetHashCode(); } if (!hashSet.Add(hashCode)) { continue; } Type type; try { type = value2.GetType(); } catch { continue; } string value3 = type.FullName ?? type.Name; list.Add($"GRAPH depth={graphNode.Depth} path={graphNode.Path} type={value3} text={Shorten(SafeDescribe(value2), 120)}"); MemberAccessor[] readableMembers; try { readableMembers = cache.GetReadableMembers(type); } catch { continue; } MemberAccessor[] array3 = readableMembers; foreach (MemberAccessor memberAccessor in array3) { if (list.Count >= maxLines) { break; } string name = memberAccessor.Name; if (!ContainsAny(name, GraphSkipMemberTokens) && memberAccessor.TryGet(value2, out var value4) && value4 != null) { string text = graphNode.Path + "." + name; bool flag = ContainsAny(graphNode.Path, GraphContextTokens) || ContainsAny(name, GraphContextTokens) || ContainsAny(name, SleepTokens); string value5; try { value5 = value4.GetType().FullName ?? value4.GetType().Name; } catch { value5 = string.Empty; } string value6 = SafeDescribe(value4); if (flag || ContainsStrongSleepSignal(value5) || ContainsStrongSleepSignal(value6)) { list.Add($" {text} [{value5}] = {Shorten(value6, 140)}"); } if (graphNode.Depth < 4 && ShouldTraverse(value4, name, flag)) { queue.Enqueue(new GraphNode(value4, text, graphNode.Depth + 1)); } } } } if (list.Count == 0) { list.Add("GRAPH no traversable interaction/state objects were found"); } return list; } private static void EnqueueGraphRoot(Queue queue, object value, string path, int depth) { if (value != null) { queue.Enqueue(new GraphNode(value, path, depth)); } } private static bool ShouldTraverse(object raw, string memberName, bool memberContext) { if (raw == null) { return false; } Type type; try { type = raw.GetType(); } catch { return false; } if (type.IsPrimitive || type.IsEnum || type == typeof(string) || type == typeof(decimal) || type == typeof(DateTime) || type == typeof(TimeSpan) || type == typeof(Type)) { return false; } if (raw is IEnumerable) { return false; } if (raw is GameObject || raw is Transform) { return false; } if (raw is Object && !(raw is Component)) { return false; } string value = type.FullName ?? type.Name; if (memberContext) { return true; } if (ContainsAny(memberName, GraphContextTokens) || ContainsAny(value, GraphContextTokens)) { return true; } if (ContainsAny(memberName, SleepTokens) || ContainsStrongSleepSignal(value)) { return true; } string text = type.Namespace ?? string.Empty; if (text.StartsWith("SSSGame", StringComparison.Ordinal) || text.StartsWith("SandSailorStudio", StringComparison.Ordinal)) { return ContainsAny(memberName, new string[5] { "agent", "controller", "state", "interaction", "action" }); } return false; } private static SleepState ReadAnimator(Animator animator) { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Invalid comparison between Unknown and I4 //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)animator == (Object)null || (Object)(object)animator.runtimeAnimatorController == (Object)null || animator.layerCount <= 0) { return SleepState.Unknown("animator has no controller/layers"); } foreach (AnimatorControllerParameter item in (Il2CppArrayBase)(object)animator.parameters) { if ((int)item.type == 4 && ContainsAny(item.name, SleepTokens)) { return animator.GetBool(item.nameHash) ? SleepState.Sleeping(0.88f, "Animator." + item.name + "=true") : SleepState.Awake(0.7f, "Animator." + item.name + "=false"); } } for (int i = 0; i < animator.layerCount; i++) { foreach (AnimatorClipInfo item2 in (Il2CppArrayBase)(object)animator.GetCurrentAnimatorClipInfo(i)) { AnimatorClipInfo current2 = item2; if (!((Object)(object)((AnimatorClipInfo)(ref current2)).clip == (Object)null)) { string text = ((Object)((AnimatorClipInfo)(ref current2)).clip).name ?? string.Empty; if (ContainsStrongSleepSignal(text)) { return SleepState.Sleeping(0.86f, "AnimatorClip=" + text); } } } } } catch { } return SleepState.Unknown("no animator sleep parameter/clip"); } private static string BuildHierarchySignal(Transform root) { List list = new List(); Transform val = root; for (int i = 0; i < 6; i++) { if (!((Object)(object)val != (Object)null)) { break; } list.Add(((Object)val).name.Replace(" ", string.Empty).ToLowerInvariant()); val = val.parent; } return string.Join("/", list); } private static bool ContainsStrongSleepSignal(string value) { if (string.IsNullOrWhiteSpace(value)) { return false; } string normalized = value.Replace(" ", string.Empty).Replace("_", string.Empty).Replace("-", string.Empty) .ToLowerInvariant(); return StrongSleepTokens.Any((string token) => normalized.Contains(token.Replace(" ", string.Empty).Replace("_", string.Empty).Replace("-", string.Empty), StringComparison.OrdinalIgnoreCase)); } private static string SafeDescribe(object value) { if (value == null) { return "null"; } try { return value.ToString() ?? value.GetType().Name; } catch { return value.GetType().Name; } } private static string Shorten(string value, int maxLength) { if (string.IsNullOrEmpty(value) || value.Length <= maxLength) { return value ?? string.Empty; } return value.Substring(0, maxLength - 3) + "..."; } private static string JoinNotes(List notes) { if (notes == null || notes.Count == 0) { return string.Empty; } return string.Join(" | ", notes.Take(4)); } internal static bool ContainsAny(string value, IEnumerable tokens) { if (string.IsNullOrEmpty(value)) { return false; } return tokens.Any((string token) => value.Contains(token, StringComparison.OrdinalIgnoreCase)); } } internal static class AuthorityProbe { private static readonly string[] PositiveMembers = new string[3] { "IsServer", "IsSharedModeMasterClient", "IsSinglePlayer" }; private static readonly string[] NegativeMembers = new string[1] { "IsClient" }; public static AuthorityState GetAuthorityState(ReflectionCache cache) { if (Application.isBatchMode) { return AuthorityState.Authority; } IEnumerable enumerable; try { enumerable = (IEnumerable)Object.FindObjectsOfType(); } catch { return AuthorityState.Unknown; } foreach (MonoBehaviour item in enumerable) { if ((Object)(object)item == (Object)null) { continue; } Type type = ((object)item).GetType(); if (!(type.FullName ?? type.Name).Contains("NetworkRunner", StringComparison.OrdinalIgnoreCase)) { continue; } bool flag = false; string[] positiveMembers = PositiveMembers; foreach (string name in positiveMembers) { if (cache.TryReadNamedBoolean(item, name, out var value)) { flag = true; if (value) { return AuthorityState.Authority; } } } if (flag) { return AuthorityState.NotAuthority; } positiveMembers = NegativeMembers; foreach (string name2 in positiveMembers) { if (cache.TryReadNamedBoolean(item, name2, out var value2) && !value2) { return AuthorityState.Authority; } } } return AuthorityState.Unknown; } } internal sealed class WorldClockProbe { private readonly Dictionary _lastNumericValues = new Dictionary(); private ClockCandidate _cached; private float _nextDiscoveryRealtime; private bool _observedNight; private static readonly string[] TypeTokens = new string[12] { "daynight", "daycycle", "timeofday", "worldtime", "gametime", "timecontroller", "timemanager", "calendar", "worldclock", "skycontroller", "suncontroller", "seasonmanager" }; private static readonly string[] ExcludedMemberTokens = new string[15] { "delta", "duration", "timer", "cooldown", "elapsed", "speed", "scale", "tick", "update", "interval", "network", "last", "start", "end", "timeout" }; public void Reset() { _cached = null; _nextDiscoveryRealtime = 0f; _lastNumericValues.Clear(); _observedNight = false; } public ClockState Sample(ReflectionCache cache, float morningHour) { if (_cached != null && _cached.TryRead(out var state, morningHour)) { if (state.IsNight == true) { _observedNight = true; } if (_observedNight && state.IsNight == false) { state = new ClockState(state.IsNight, true, state.Hour, state.Source); } return state; } float realtimeSinceStartup = Time.realtimeSinceStartup; if (realtimeSinceStartup >= _nextDiscoveryRealtime) { _nextDiscoveryRealtime = realtimeSinceStartup + 5f; _cached = Discover(cache); if (_cached != null && _cached.TryRead(out var state2, morningHour)) { if (state2.IsNight == true) { _observedNight = true; } if (_observedNight && state2.IsNight == false) { state2 = new ClockState(state2.IsNight, true, state2.Hour, state2.Source); } return state2; } } ClockState result = ReadSunFallback(); if (result.IsNight == true) { _observedNight = true; } if (_observedNight && result.IsNight == false) { result = new ClockState(result.IsNight, true, result.Hour, result.Source); } return result; } private ClockCandidate Discover(ReflectionCache cache) { //IL_0287: Unknown result type (might be due to invalid IL or missing references) //IL_028e: Expected O, but got Unknown IEnumerable enumerable; try { enumerable = (IEnumerable)Object.FindObjectsOfType(); } catch { return null; } List list = new List(); foreach (MonoBehaviour item in enumerable) { if ((Object)(object)item == (Object)null) { continue; } Type type = ((object)item).GetType(); string typeName = type.FullName ?? type.Name; int num = TypeTokens.Sum((string token) => typeName.Contains(token, StringComparison.OrdinalIgnoreCase) ? 20 : 0); if (num == 0) { continue; } MemberAccessor[] readableMembers = cache.GetReadableMembers(type); foreach (MemberAccessor memberAccessor in readableMembers) { string name = memberAccessor.Name; if (ExcludedMemberTokens.Any((string t) => name.Contains(t, StringComparison.OrdinalIgnoreCase))) { continue; } if (memberAccessor.ValueType == typeof(bool)) { int num3 = num + ScoreBooleanMember(name); if (num3 > num) { list.Add(new ClockCandidate((Component)(object)item, memberAccessor, ClockValueKind.Boolean, num3)); } } else { if (!ReflectionCache.IsNumericType(memberAccessor.ValueType)) { continue; } int num4 = num + ScoreNumericMember(name); if (num4 <= num || !memberAccessor.TryGet(item, out var value) || !ReflectionCache.TryConvertDouble(value, out var number)) { continue; } ClockValueKind clockValueKind = InferNumericKind(name, number); if (clockValueKind != ClockValueKind.Unknown) { string key = $"{typeName}.{name}:{((Object)item).GetInstanceID()}"; if (_lastNumericValues.TryGetValue(key, out var value2) && Math.Abs(number - value2) > 1E-06) { num4 += 25; } _lastNumericValues[key] = number; list.Add(new ClockCandidate((Component)(object)item, memberAccessor, clockValueKind, num4)); } } } } ClockCandidate clockCandidate = list.OrderByDescending((ClockCandidate c) => c.Score).FirstOrDefault(); if (Plugin.DiagnosticLogging.Value && clockCandidate != null) { ManualLogSource log = Plugin.Log; bool flag = default(bool); BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(23, 1, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("World clock candidate: "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(clockCandidate); } log.LogInfo(val); } return clockCandidate; } private static int ScoreBooleanMember(string name) { if (name.Equals("IsNight", StringComparison.OrdinalIgnoreCase)) { return 100; } if (name.Equals("IsDay", StringComparison.OrdinalIgnoreCase)) { return 95; } if (name.Equals("IsDaytime", StringComparison.OrdinalIgnoreCase)) { return 95; } if (name.Equals("IsMorning", StringComparison.OrdinalIgnoreCase)) { return 90; } if (name.Contains("night", StringComparison.OrdinalIgnoreCase)) { return 70; } if (name.Contains("day", StringComparison.OrdinalIgnoreCase)) { return 50; } if (name.Contains("morning", StringComparison.OrdinalIgnoreCase)) { return 60; } return 0; } private static int ScoreNumericMember(string name) { if (name.Equals("Hour", StringComparison.OrdinalIgnoreCase)) { return 100; } if (name.Equals("CurrentHour", StringComparison.OrdinalIgnoreCase)) { return 100; } if (name.Contains("timeofday", StringComparison.OrdinalIgnoreCase)) { return 90; } if (name.Contains("daytime", StringComparison.OrdinalIgnoreCase)) { return 80; } if (name.Contains("normalized", StringComparison.OrdinalIgnoreCase) && name.Contains("time", StringComparison.OrdinalIgnoreCase)) { return 85; } if (name.Contains("dayprogress", StringComparison.OrdinalIgnoreCase)) { return 80; } if (name.Contains("clock", StringComparison.OrdinalIgnoreCase)) { return 70; } if (name.Contains("hour", StringComparison.OrdinalIgnoreCase)) { return 65; } if (name.Equals("Time", StringComparison.OrdinalIgnoreCase)) { return 30; } return 0; } private static ClockValueKind InferNumericKind(string name, double value) { if (name.Contains("hour", StringComparison.OrdinalIgnoreCase) && value >= 0.0 && value <= 24.1) { return ClockValueKind.Hour24; } if (value >= 0.0 && value <= 1.01 && (name.Contains("normalized", StringComparison.OrdinalIgnoreCase) || name.Contains("progress", StringComparison.OrdinalIgnoreCase))) { return ClockValueKind.NormalizedDay; } if (value >= 0.0 && value <= 24.1 && (name.Contains("time", StringComparison.OrdinalIgnoreCase) || name.Contains("clock", StringComparison.OrdinalIgnoreCase))) { return ClockValueKind.Hour24; } if (value >= 0.0 && value <= 1440.1 && name.Contains("minute", StringComparison.OrdinalIgnoreCase)) { return ClockValueKind.Minutes1440; } return ClockValueKind.Unknown; } private static ClockState ReadSunFallback() { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Invalid comparison between Unknown and I4 //IL_015d: Unknown result type (might be due to invalid IL or missing references) //IL_0047: 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) try { List list = new List(); try { foreach (Light item in Resources.FindObjectsOfTypeAll()) { if (!((Object)(object)item == (Object)null) && (int)item.type == 1 && !((Object)(object)((Component)item).gameObject == (Object)null)) { Scene scene = ((Component)item).gameObject.scene; if (((Scene)(ref scene)).IsValid() && ((Scene)(ref scene)).isLoaded) { list.Add(item); } } } } catch { list.AddRange(((IEnumerable)Object.FindObjectsOfType()).Where((Light l) => (Object)(object)l != (Object)null && (int)l.type == 1)); } Light val = ((IEnumerable)list).FirstOrDefault((Func)((Light l) => ((Object)l).name.Contains("sun", StringComparison.OrdinalIgnoreCase) && !((Object)l).name.Contains("moon", StringComparison.OrdinalIgnoreCase))); Light val2 = ((IEnumerable)list).FirstOrDefault((Func)((Light l) => ((Object)l).name.Contains("moon", StringComparison.OrdinalIgnoreCase))); Light val3 = val ?? RenderSettings.sun ?? val2 ?? list.OrderByDescending((Light l) => l.intensity).FirstOrDefault(); if ((Object)(object)val3 == (Object)null) { return ClockState.Unknown("no clock or directional light found"); } float num = 0f - ((Component)val3).transform.forward.y; if (((Object)val3).name.Contains("moon", StringComparison.OrdinalIgnoreCase)) { bool flag = num > 0.02f; return new ClockState(flag, !flag, null, $"moon:{((Object)val3).name}:{num:0.000}"); } bool value = num < -0.02f; bool value2 = num >= 0.03f; return new ClockState(value, value2, null, $"sun:{((Object)val3).name}:{num:0.000}"); } catch (Exception ex) { return ClockState.Unknown("sun/moon probe failed:" + ex.GetType().Name); } } } internal enum ClockValueKind { Unknown, Boolean, Hour24, NormalizedDay, Minutes1440 } internal sealed class ClockCandidate { private readonly Component _component; private readonly MemberAccessor _member; private readonly ClockValueKind _kind; public int Score { get; } public ClockCandidate(Component component, MemberAccessor member, ClockValueKind kind, int score) { _component = component; _member = member; _kind = kind; Score = score; } public bool TryRead(out ClockState state, float morningHour) { state = ClockState.Unknown(ToString()); if ((Object)(object)_component == (Object)null || !_member.TryGet(_component, out var value) || value == null) { return false; } string name = _member.Name; if (_kind == ClockValueKind.Boolean && value is bool flag) { bool? isNight = null; bool? isMorning = null; if (name.Contains("night", StringComparison.OrdinalIgnoreCase)) { isNight = flag; } else if (name.Contains("morning", StringComparison.OrdinalIgnoreCase)) { isMorning = flag; } else if (name.Contains("day", StringComparison.OrdinalIgnoreCase)) { isNight = !flag; } state = new ClockState(isNight, isMorning, null, ToString()); return true; } if (!ReflectionCache.TryConvertDouble(value, out var number)) { return false; } double num = _kind switch { ClockValueKind.Hour24 => Normalize24(number), ClockValueKind.NormalizedDay => Normalize24(number * 24.0), ClockValueKind.Minutes1440 => Normalize24(number / 60.0), _ => double.NaN, }; if (double.IsNaN(num)) { return false; } bool value2 = num >= 18.0 || num < (double)morningHour - 0.25; bool value3 = num >= (double)morningHour && num < (double)morningHour + 3.0; state = new ClockState(value2, value3, num, ToString()); return true; } private static double Normalize24(double value) { value %= 24.0; if (value < 0.0) { value += 24.0; } return value; } public override string ToString() { return $"{((object)_component).GetType().Name}.{_member.Name}/{_kind}/score={Score}"; } } internal static class DiscoveryDump { private static readonly string[] InterestingTokens = new string[17] { "sleep", "asleep", "bed", "rest", "state", "action", "activity", "interaction", "pose", "night", "day", "morning", "hour", "time", "clock", "calendar", "season" }; public static void Write(List players, AuthorityState authority, ClockState clock, ReflectionCache cache) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_001f: Expected O, but got Unknown //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Expected O, but got Unknown //IL_0194: Unknown result type (might be due to invalid IL or missing references) //IL_019a: Expected O, but got Unknown //IL_0234: Unknown result type (might be due to invalid IL or missing references) //IL_023a: Expected O, but got Unknown Plugin.Log.LogWarning((object)"=== ALL-SLEEP NIGHT SKIP DISCOVERY START ==="); ManualLogSource log = Plugin.Log; bool flag = default(bool); BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(28, 3, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Authority="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(authority); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; clock="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(clock); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("; players="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(players.Count); } log.LogWarning(val); foreach (PlayerRecord player in players) { ManualLogSource log2 = Plugin.Log; val = new BepInExWarningLogInterpolatedStringHandler(28, 3, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("--- Player "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(player.Name); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" root="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(GetPath(player.Root.transform)); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" state="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(player.SleepState); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" ---"); } log2.LogWarning(val); IEnumerable componentsInChildren; try { componentsInChildren = (IEnumerable)player.Root.GetComponentsInChildren(true); } catch { continue; } foreach (Component item in componentsInChildren) { if ((Object)(object)item == (Object)null) { continue; } Type type = ((object)item).GetType(); string text = type.FullName ?? type.Name; bool num = SleepProbe.ContainsAny(text, InterestingTokens); MemberAccessor[] array = (from m in cache.GetReadableMembers(type) where SleepProbe.ContainsAny(m.Name, InterestingTokens) select m).ToArray(); if (!num && array.Length == 0) { continue; } ManualLogSource log3 = Plugin.Log; val = new BepInExWarningLogInterpolatedStringHandler(27, 3, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral("COMPONENT "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(text); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" object="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(GetPath(item.transform)); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" enabled="); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(ReadEnabled(item)); } log3.LogWarning(val); MemberAccessor[] array2 = array; foreach (MemberAccessor memberAccessor in array2) { object value; string text2 = ((!memberAccessor.TryGet(item, out value)) ? "" : (value?.ToString() ?? "null")); ManualLogSource log4 = Plugin.Log; val = new BepInExWarningLogInterpolatedStringHandler(6, 3, ref flag); if (flag) { ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(memberAccessor.ValueType.Name); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(memberAccessor.Name); ((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" = "); ((BepInExLogInterpolatedStringHandler)val).AppendFormatted(text2); } log4.LogWarning(val); } } Plugin.Log.LogWarning((object)"--- Recursive interaction/state graph ---"); foreach (string item2 in SleepProbe.DescribeInteractionGraph(player, cache, 100)) { Plugin.Log.LogWarning((object)item2); } } Plugin.Log.LogWarning((object)"=== ALL-SLEEP NIGHT SKIP DISCOVERY END ==="); } private static string ReadEnabled(Component component) { Behaviour val = (Behaviour)(object)((component is Behaviour) ? component : null); if (val == null) { return "n/a"; } return val.enabled.ToString(); } private static string GetPath(Transform transform) { string text = ((Object)transform).name; Transform parent = transform.parent; int num = 0; while ((Object)(object)parent != (Object)null && num++ < 12) { text = ((Object)parent).name + "/" + text; parent = parent.parent; } return text; } } internal sealed class ReflectionCache { private readonly Dictionary _readableMembers = new Dictionary(); private readonly Dictionary<(Type Type, string Name), MemberAccessor> _namedMembers = new Dictionary<(Type, string), MemberAccessor>(); private readonly Dictionary<(Type Type, string Name, string Signature), MethodInfo> _methods = new Dictionary<(Type, string, string), MethodInfo>(); public MemberAccessor[] GetReadableMembers(Type type) { if (_readableMembers.TryGetValue(type, out var value)) { return value; } List list = new List(); try { PropertyInfo[] properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (PropertyInfo propertyInfo in properties) { MethodInfo getMethod = propertyInfo.GetGetMethod(nonPublic: true); if (!(getMethod == null) && !getMethod.IsStatic && propertyInfo.GetIndexParameters().Length == 0) { list.Add(new MemberAccessor(propertyInfo)); } } } catch { } try { FieldInfo[] fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); foreach (FieldInfo fieldInfo in fields) { if (!fieldInfo.IsStatic) { list.Add(new MemberAccessor(fieldInfo)); } } } catch { } value = (from g in list.GroupBy((MemberAccessor v) => v.Name, StringComparer.OrdinalIgnoreCase) select g.First()).ToArray(); _readableMembers[type] = value; return value; } public bool TryReadNamedBoolean(object instance, string name, out bool value) { value = false; Type type = instance.GetType(); (Type, string) key = (type, name); if (!_namedMembers.TryGetValue(key, out var value2)) { value2 = GetReadableMembers(type).FirstOrDefault((MemberAccessor m) => m.ValueType == typeof(bool) && string.Equals(m.Name, name, StringComparison.OrdinalIgnoreCase)); _namedMembers[key] = value2; } if (value2 == null || !value2.TryGet(instance, out var value3) || !(value3 is bool flag)) { return false; } value = flag; return true; } public MethodInfo GetMethod(Type type, string name, Type[] parameters) { string item = string.Join(",", parameters.Select((Type t) => t.FullName)); (Type, string, string) key = (type, name, item); if (_methods.TryGetValue(key, out var value)) { return value; } try { value = type.GetMethod(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, parameters, null); } catch { value = null; } _methods[key] = value; return value; } public void ResetSceneCaches() { } public static bool IsNumericType(Type type) { Type type2 = Nullable.GetUnderlyingType(type) ?? type; if (!(type2 == typeof(byte)) && !(type2 == typeof(sbyte)) && !(type2 == typeof(short)) && !(type2 == typeof(ushort)) && !(type2 == typeof(int)) && !(type2 == typeof(uint)) && !(type2 == typeof(long)) && !(type2 == typeof(ulong)) && !(type2 == typeof(float)) && !(type2 == typeof(double))) { return type2 == typeof(decimal); } return true; } public static bool TryConvertDouble(object value, out double number) { number = 0.0; if (value == null) { return false; } try { number = Convert.ToDouble(value, CultureInfo.InvariantCulture); return !double.IsNaN(number) && !double.IsInfinity(number); } catch { return false; } } } internal sealed class MemberAccessor { private readonly PropertyInfo _property; private readonly FieldInfo _field; public string Name { get; } public Type ValueType { get; } public MemberAccessor(PropertyInfo property) { _property = property; Name = property.Name; ValueType = property.PropertyType; } public MemberAccessor(FieldInfo field) { _field = field; Name = field.Name; ValueType = field.FieldType; } public bool TryGet(object instance, out object value) { value = null; try { value = ((_property != null) ? _property.GetValue(instance) : _field?.GetValue(instance)); return true; } catch { return false; } } } }