Some mods target the Mono version of the game, which is available by opting into the Steam beta branch "alternate"
Decompiled source of ActuallyWorkingEmployees v1.0.0
ActuallyWorkingEmployees.dll
Decompiled 14 hours ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Resources; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Text; using ActuallyWorkingEmployees; using ActuallyWorkingEmployees.Compat; using ActuallyWorkingEmployees.Config; using ActuallyWorkingEmployees.Features; using ActuallyWorkingEmployees.Features.Diagnostics; using ActuallyWorkingEmployees.Features.Packaging; using ActuallyWorkingEmployees.Features.Reliability; using ActuallyWorkingEmployees.Networking; using DooDesch; using HarmonyLib; using Il2CppFishNet; using Il2CppFishNet.Connection; using Il2CppFishNet.Managing; using Il2CppFishNet.Object; using Il2CppInterop.Runtime; using Il2CppInterop.Runtime.InteropTypes; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppScheduleOne.Core.Items.Framework; using Il2CppScheduleOne.DevUtilities; using Il2CppScheduleOne.Employees; using Il2CppScheduleOne.EntityFramework; using Il2CppScheduleOne.GameTime; using Il2CppScheduleOne.Growing; using Il2CppScheduleOne.ItemFramework; using Il2CppScheduleOne.Management; using Il2CppScheduleOne.NPCs; using Il2CppScheduleOne.NPCs.Behaviour; using Il2CppScheduleOne.Networking; using Il2CppScheduleOne.ObjectScripts; using Il2CppScheduleOne.Property; using Il2CppScheduleOne.StationFramework; using Il2CppScheduleOne.Tools; using Il2CppSystem; using Il2CppSystem.Collections.Generic; using MelonLoader; using MelonLoader.Preferences; using MelonLoader.Utils; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using S1API.Lifecycle; using Sideload.Api; using UnityEngine; using UnityEngine.AI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: MelonInfo(typeof(Core), "Actually Working Employees", "1.0.0", "DooDesch", "https://github.com/DooDesch-Mods/ScheduleOne-ActuallyWorkingEmployees")] [assembly: MelonGame("TVGS", "Schedule I")] [assembly: AssemblyMetadata("SourceNotice", "ActuallyWorkingEmployees by DooDesch, MIT License. The source code is public at https://github.com/DooDesch-Mods/ScheduleOne-ActuallyWorkingEmployees. Please read, change and reuse the source code, not decompiled code from this file. Text and data mining reserved under Art. 4(3) Directive (EU) 2019/790 and § 44b(3) UrhG.")] [assembly: AssemblyMetadata("License", "https://github.com/DooDesch-Mods/ScheduleOne-ActuallyWorkingEmployees/blob/main/LICENSE.md")] [assembly: AssemblyMetadata("SourceCode", "https://github.com/DooDesch-Mods/ScheduleOne-ActuallyWorkingEmployees")] [assembly: AssemblyMetadata("tdm-reservation", "1")] [assembly: AssemblyMetadata("tdm-policy", "https://github.com/DooDesch-Mods/ScheduleOne-ActuallyWorkingEmployees/blob/main/LICENSE.md")] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("DooDesch")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyCopyright("Copyright © DooDesch. MIT License. Source code: https://github.com/DooDesch-Mods/ScheduleOne-ActuallyWorkingEmployees")] [assembly: AssemblyDescription("Employees that keep working, with every employee option in a phone app.")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+d53d6ac3326c0d97d293bd8954a466a1ca00f612")] [assembly: AssemblyProduct("Actually Working Employees")] [assembly: AssemblyTitle("Actually Working Employees")] [assembly: NeutralResourcesLanguage("en-US")] [assembly: AssemblyMetadata("AI_Assisted_Creation", "This assembly was partially created with the assistance of Generative AI.")] [assembly: AssemblyMetadata("AI_Model_Vendor", "Anthropic/OpenAI")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace DooDesch { [AttributeUsage(AttributeTargets.All, Inherited = false)] internal sealed class SourceNoticeAttribute : Attribute { public string Notice { get; } public SourceNoticeAttribute(string notice) { Notice = notice; } } [SourceNotice("ActuallyWorkingEmployees by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-ActuallyWorkingEmployees, not decompiled code.")] internal static class SourceNotice { internal const string Text = "ActuallyWorkingEmployees by DooDesch, MIT License. The source code is public at https://github.com/DooDesch-Mods/ScheduleOne-ActuallyWorkingEmployees. Please read, change and reuse the source code, not decompiled code from this file. Text and data mining reserved under Art. 4(3) Directive (EU) 2019/790 and § 44b(3) UrhG."; internal const string Short = "ActuallyWorkingEmployees by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-ActuallyWorkingEmployees, not decompiled code."; } [SourceNotice("ActuallyWorkingEmployees by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-ActuallyWorkingEmployees, not decompiled code.")] internal static class ModVersion { internal const string Current = "1.0.0"; } } namespace Sideload.Api { [SourceNotice("ActuallyWorkingEmployees by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-ActuallyWorkingEmployees, not decompiled code.")] public static class Apps { private static bool _bound; private static int _probeAttempts; private static readonly List<Action> _pending = new List<Action>(); private static Action<string, string, string, string, Assembly> _registerApp; private static Action<string, string, Func<string, string, string>> _handle; private static Action<string, string, string> _emit; private static Action<string, string> _allowHost; private static Action<string, string> _declareOrientations; private static Action<string, int> _setBadge; private static Action<string, string, string> _notify; private static Action<string, string, string, float> _notifyFor; private static Func<string, bool> _isOnScreen; private static Action<string, string, byte[]> _setImage; private static Action<string, bool> _setIconHidden; private static Action<string, bool> _setAppOpen; private static Func<string, bool> _isAppOpen; private static Func<bool, bool> _setPhoneRaised; private static Func<bool> _isPhoneRaised; private static Action<string, string, Func<string, string, bool>> _claimKeys; private static Func<object, string, string, Assembly, float, bool> _mountSurface; private static Action<string> _unmountSurface; private static Func<string, bool> _isSurfaceMounted; private static Action<string> _reloadApp; private static Action<string> _unregisterApp; private static Action<string, string> _allowGameWrites; public static bool Available { get { EnsureBound(); return _bound; } } internal static bool HasLiveEditing { get { EnsureBound(); if (_reloadApp != null) { return _unregisterApp != null; } return false; } } internal static bool HasSurfaces { get { EnsureBound(); return _mountSurface != null; } } internal static bool HasKeys { get { EnsureBound(); return _claimKeys != null; } } internal static bool HasPhone { get { EnsureBound(); return _setPhoneRaised != null; } } internal static bool HasOpen { get { EnsureBound(); if (_setAppOpen != null) { return _setIconHidden != null; } return false; } } public static AppHandle Register(string id, string bundlePrefix, string title = null, string iconLabel = null, Assembly hostAssembly = null) { AppHandle result = new AppHandle(id); if (string.IsNullOrEmpty(id)) { return result; } Assembly asm = hostAssembly ?? Assembly.GetCallingAssembly(); string t = title; string il = iconLabel; string prefix = bundlePrefix; EnsureBound(); if (_registerApp != null) { _registerApp(id, t, il, prefix, asm); } else { _pending.Add(delegate { _registerApp?.Invoke(id, t, il, prefix, asm); }); } return result; } internal static void WhenBound(Action work) { if (work != null) { EnsureBound(); if (_bound) { work(); } else { _pending.Add(work); } } } internal static void HandleCall(string appId, string name, Func<string, string> handler) { if (_handle != null && handler != null) { _handle(appId, name, (string app, string argument) => handler(argument)); } } internal static void EmitEvent(string appId, string name, string payload) { _emit?.Invoke(appId, name, payload); } internal static void AllowNetHost(string appId, string host) { _allowHost?.Invoke(appId, host); } internal static void Orient(string appId, string orientations) { _declareOrientations?.Invoke(appId, orientations); } internal static void Badge(string appId, int count) { _setBadge?.Invoke(appId, count); } internal static void Notify(string appId, string title, string subtitle, float seconds) { if (_notifyFor != null) { _notifyFor(appId, title, subtitle, seconds); } else if (_notify != null) { _notify(appId, title, subtitle); } } internal static bool OnScreen(string appId) { if (_isOnScreen != null) { return _isOnScreen(appId); } return false; } internal static void SetImage(string appId, string name, byte[] png) { _setImage?.Invoke(appId, name, png); } internal static void HideIcon(string appId, bool hidden) { _setIconHidden?.Invoke(appId, hidden); } internal static void OpenApp(string appId, bool open) { _setAppOpen?.Invoke(appId, open); } internal static bool AppIsOpen(string appId) { if (_isAppOpen != null) { return _isAppOpen(appId); } return false; } internal static bool RaisePhone(bool raised) { if (_setPhoneRaised != null) { return _setPhoneRaised(raised); } return false; } internal static bool PhoneIsRaised() { if (_isPhoneRaised != null) { return _isPhoneRaised(); } return false; } internal static void ClaimKeys(string appId, string keys, Func<string, string, bool> handler) { _claimKeys?.Invoke(appId, keys, handler); } internal static bool MountSurface(object rect, string id, string prefix, Assembly host, float shortSide) { if (_mountSurface != null) { return _mountSurface(rect, id, prefix, host, shortSide); } return false; } internal static void UnmountSurface(string id) { _unmountSurface?.Invoke(id); } internal static bool SurfaceIsMounted(string id) { if (_isSurfaceMounted != null) { return _isSurfaceMounted(id); } return false; } internal static void ReloadApp(string appId) { _reloadApp?.Invoke(appId); } internal static void UnregisterApp(string appId) { _unregisterApp?.Invoke(appId); } internal static void AllowGameWrites(string appId, string scope) { _allowGameWrites?.Invoke(appId, scope); } private static void EnsureBound() { if (_bound) { return; } try { Type type = FindBridge(_probeAttempts++ % 30 == 0); if (type == null || (type.GetField("AbiVersion", BindingFlags.Static | BindingFlags.Public)?.GetValue(null) is int num && num < 1)) { return; } _registerApp = Get<Action<string, string, string, string, Assembly>>(type, "RegisterApp"); if (_registerApp == null) { return; } _handle = Get<Action<string, string, Func<string, string, string>>>(type, "Handle"); _emit = Get<Action<string, string, string>>(type, "Emit"); _allowHost = Get<Action<string, string>>(type, "AllowHost"); _declareOrientations = Get<Action<string, string>>(type, "DeclareOrientations"); _setBadge = Get<Action<string, int>>(type, "SetBadge"); _notify = Get<Action<string, string, string>>(type, "Notify"); _notifyFor = Get<Action<string, string, string, float>>(type, "NotifyFor"); _isOnScreen = Get<Func<string, bool>>(type, "IsAppOnScreen"); _setImage = Get<Action<string, string, byte[]>>(type, "SetImage"); _setIconHidden = Get<Action<string, bool>>(type, "SetIconHidden"); _setAppOpen = Get<Action<string, bool>>(type, "SetAppOpen"); _isAppOpen = Get<Func<string, bool>>(type, "IsAppOpen"); _setPhoneRaised = Get<Func<bool, bool>>(type, "SetPhoneRaised"); _isPhoneRaised = Get<Func<bool>>(type, "IsPhoneRaised"); _claimKeys = Get<Action<string, string, Func<string, string, bool>>>(type, "ClaimKeys"); _mountSurface = Get<Func<object, string, string, Assembly, float, bool>>(type, "MountSurface"); _unmountSurface = Get<Action<string>>(type, "UnmountSurface"); _isSurfaceMounted = Get<Func<string, bool>>(type, "IsSurfaceMounted"); _reloadApp = Get<Action<string>>(type, "ReloadApp"); _unregisterApp = Get<Action<string>>(type, "UnregisterApp"); _allowGameWrites = Get<Action<string, string>>(type, "AllowGameWrites"); _bound = true; for (int i = 0; i < _pending.Count; i++) { try { _pending[i](); } catch { } } _pending.Clear(); } catch { } } private static T Get<T>(Type t, string field) where T : class { return t.GetField(field, BindingFlags.Static | BindingFlags.Public)?.GetValue(null) as T; } private static Type FindBridge(bool scan) { Type type = Type.GetType("Sideload.Bridge.SideloadBridge, Sideload", throwOnError: false); if (type != null || !scan) { return type; } Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { try { type = assembly.GetType("Sideload.Bridge.SideloadBridge", throwOnError: false); if (type != null) { return type; } } catch { } } return null; } } [SourceNotice("ActuallyWorkingEmployees by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-ActuallyWorkingEmployees, not decompiled code.")] public sealed class AppHandle { private readonly string _id; public string Id => _id; public bool IsOnScreen { get { if (Apps.Available) { return Apps.OnScreen(_id); } return false; } } public bool IsOpen { get { if (Apps.Available) { return Apps.AppIsOpen(_id); } return false; } } public static bool CanOpenProgrammatically { get { if (Apps.Available) { return Apps.HasOpen; } return false; } } public static bool CanClaimKeys { get { if (Apps.Available) { return Apps.HasKeys; } return false; } } public static bool CanEditLive { get { if (Apps.Available) { return Apps.HasLiveEditing; } return false; } } internal AppHandle(string id) { _id = id ?? ""; } public AppHandle OnCall(string name, Func<string, string> handler) { string id = _id; Apps.WhenBound(delegate { Apps.HandleCall(id, name, handler); }); return this; } public void Emit(string name, string payload = "") { string id = _id; Apps.WhenBound(delegate { Apps.EmitEvent(id, name, payload); }); } public AppHandle AllowHost(string host) { string id = _id; Apps.WhenBound(delegate { Apps.AllowNetHost(id, host); }); return this; } public AppHandle Orientation(params string[] supported) { string id = _id; string list = ((supported == null) ? "" : string.Join(",", supported)); Apps.WhenBound(delegate { Apps.Orient(id, list); }); return this; } public AppHandle Badge(int count) { string id = _id; Apps.WhenBound(delegate { Apps.Badge(id, count); }); return this; } public AppHandle Notify(string title, string subtitle = "", float seconds = 0f) { string id = _id; Apps.WhenBound(delegate { Apps.Notify(id, title, subtitle, seconds); }); return this; } public AppHandle Image(string name, byte[] png) { string id = _id; Apps.WhenBound(delegate { Apps.SetImage(id, name, png); }); return this; } public AppHandle Icon(bool visible) { Apps.HideIcon(_id, !visible); return this; } public AppHandle NoIcon() { string id = _id; Apps.WhenBound(delegate { Apps.HideIcon(id, hidden: true); }); return this; } public AppHandle Open() { string id = _id; Apps.WhenBound(delegate { Apps.OpenApp(id, open: true); }); return this; } public AppHandle Close() { string id = _id; Apps.WhenBound(delegate { Apps.OpenApp(id, open: false); }); return this; } public bool Show() { if (!Apps.RaisePhone(raised: true)) { return false; } Apps.OpenApp(_id, open: true); return true; } public AppHandle OnKey(string keys, Func<string, bool> handler) { string id = _id; Apps.WhenBound(delegate { Apps.ClaimKeys(id, keys, (handler == null) ? null : ((Func<string, string, bool>)((string _, string key) => handler(key)))); }); return this; } public AppHandle Reload() { string id = _id; Apps.WhenBound(delegate { Apps.ReloadApp(id); }); return this; } public void Unregister() { string id = _id; Apps.WhenBound(delegate { Apps.UnregisterApp(id); }); } public AppHandle AllowGameWrites(string scope) { string id = _id; Apps.WhenBound(delegate { Apps.AllowGameWrites(id, scope); }); return this; } public AppHandle Hide() { string id = _id; Apps.WhenBound(delegate { Apps.OpenApp(id, open: false); Apps.RaisePhone(raised: false); }); return this; } } [SourceNotice("ActuallyWorkingEmployees by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-ActuallyWorkingEmployees, not decompiled code.")] public static class Surfaces { public static bool Available { get { if (Apps.Available) { return Apps.HasSurfaces; } return false; } } public static SurfaceHandle Mount(object hostRect, string id, string bundlePrefix, float designShortSide = 0f, Assembly hostAssembly = null) { SurfaceHandle result = new SurfaceHandle(id); if (string.IsNullOrEmpty(id) || hostRect == null) { return result; } Assembly host = hostAssembly ?? Assembly.GetCallingAssembly(); Apps.MountSurface(hostRect, id, bundlePrefix, host, designShortSide); return result; } public static void Unmount(string id) { Apps.UnmountSurface(id); } public static bool IsMounted(string id) { return Apps.SurfaceIsMounted(id); } } [SourceNotice("ActuallyWorkingEmployees by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-ActuallyWorkingEmployees, not decompiled code.")] public sealed class SurfaceHandle { private readonly string _id; public string Id => _id; internal SurfaceHandle(string id) { _id = id; } public SurfaceHandle OnCall(string name, Func<string, string> handler) { string id = _id; Apps.WhenBound(delegate { Apps.HandleCall(id, name, handler); }); return this; } public SurfaceHandle Emit(string name, string payload = "") { string id = _id; Apps.WhenBound(delegate { Apps.EmitEvent(id, name, payload); }); return this; } public SurfaceHandle AllowHost(string host) { string id = _id; Apps.WhenBound(delegate { Apps.AllowNetHost(id, host); }); return this; } public SurfaceHandle Image(string name, byte[] png) { string id = _id; Apps.WhenBound(delegate { Apps.SetImage(id, name, png); }); return this; } public void Unmount() { Apps.UnmountSurface(_id); } } [SourceNotice("ActuallyWorkingEmployees by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-ActuallyWorkingEmployees, not decompiled code.")] public static class PhoneScreen { public static bool IsRaised { get { if (Apps.Available) { return Apps.PhoneIsRaised(); } return false; } } public static bool Available { get { if (Apps.Available) { return Apps.HasPhone; } return false; } } public static bool Raise() { return Apps.RaisePhone(raised: true); } public static bool Lower() { return Apps.RaisePhone(raised: false); } } } namespace ActuallyWorkingEmployees { [SourceNotice("ActuallyWorkingEmployees by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-ActuallyWorkingEmployees, not decompiled code.")] public sealed class Core : MelonMod { private static readonly HashSet<string> _reported = new HashSet<string>(); public static Core Instance { get; private set; } public static Instance Log { get; private set; } public static bool InWorld { get; private set; } public static bool Verbose => Options.VerboseLogging.Value; internal static event Action OnceKeysReset; public static void LogDebug(string msg) { if (Verbose) { Instance log = Log; if (log != null) { log.Msg(msg); } } } private static void ResetOnceKeys() { _reported.Clear(); Action onceKeysReset = Core.OnceKeysReset; if (onceKeysReset == null) { return; } Delegate[] invocationList = onceKeysReset.GetInvocationList(); foreach (Delegate obj in invocationList) { try { ((Action)obj)(); } catch (Exception e) { Failed("core: reset log-once keys (" + obj.Method.DeclaringType?.Name + ")", e); } } } internal static void Caught(string where, Exception e) { if (_reported.Add(where)) { Instance log = Log; if (log != null) { log.Warning("[Caught] " + where + ": " + e.GetType().Name + ": " + e.Message); } } } internal static void WarnOnce(string where, string message) { if (_reported.Add(where)) { Instance log = Log; if (log != null) { log.Warning("[" + where + "] " + message); } } } internal static void Failed(string where, Exception e) { if (_reported.Add(where)) { Instance log = Log; if (log != null) { log.Error("[Failed] " + where + ": " + e.GetType().Name + ": " + e.Message); } } } public override void OnInitializeMelon() { Instance = this; Log = ((MelonBase)this).LoggerInstance; NativeMethods.Flush(); Options.Initialize(); OnceKeysReset += Rel.ResetOnceKeys; FeatureHost.Discover(); GameLifecycle.OnPreLoad += ResetState; GameLifecycle.OnPreSceneChange += ResetState; Log.Msg($"Actually Working Employees v{"1.0.0"} loaded, {Options.All.Count} options."); } public override void OnLateInitializeMelon() { HweDetector.Check(); NativeMethods.ReportSharedPatchedAddresses(); } private static void ResetState() { ResetOnceKeys(); FeatureHost.ResetAll(); Instance log = Log; if (log != null) { log.Msg("[Core] State reset (save load or scene change)."); } } public override void OnSceneWasLoaded(int buildIndex, string sceneName) { if (!(sceneName != "Main")) { InWorld = true; Instance log = Log; if (log != null) { log.Msg("[Core] Main scene loaded."); } } } public override void OnSceneWasUnloaded(int buildIndex, string sceneName) { if (!(sceneName != "Main")) { InWorld = false; ResetState(); } } public override void OnUpdate() { FeatureHost.TickAll(Time.deltaTime); OptionsFile.Tick(); } public override void OnApplicationQuit() { OptionsFile.Flush(); } } } namespace ActuallyWorkingEmployees.Networking { [SourceNotice("ActuallyWorkingEmployees by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-ActuallyWorkingEmployees, not decompiled code.")] internal static class Net { internal static bool IsServer { get { try { NetworkManager networkManager = InstanceFinder.NetworkManager; return (Object)(object)networkManager != (Object)null && networkManager.IsServer; } catch (Exception e) { Core.Caught("net: read NetworkManager.IsServer", e); return false; } } } internal static bool IsClientOnly { get { try { NetworkManager networkManager = InstanceFinder.NetworkManager; return (Object)(object)networkManager != (Object)null && networkManager.IsClient && !networkManager.IsServer; } catch (Exception e) { Core.Caught("net: read NetworkManager.IsClient", e); return false; } } } internal static bool IsCoop() { return LobbyPlayers() > 1; } internal static int LobbyPlayers() { try { Lobby instance = Singleton<Lobby>.Instance; return ((Object)(object)instance != (Object)null && instance.IsInLobby) ? instance.PlayerCount : 0; } catch (Exception e) { Core.Caught("net: read the Steam lobby", e); return 0; } } internal static int RemoteClients() { try { NetworkManager networkManager = InstanceFinder.NetworkManager; if ((Object)(object)networkManager == (Object)null || !networkManager.IsServer || (Object)(object)networkManager.ServerManager == (Object)null || networkManager.ServerManager.Clients == null) { return 0; } return Math.Max(0, networkManager.ServerManager.Clients.Count - (networkManager.IsClient ? 1 : 0)); } catch (Exception e) { Core.Caught("net: read the server's client list", e); return 0; } } internal static string RoleText() { if (IsClientOnly) { return "client"; } if (IsServer) { if (!IsCoop()) { return "single-player"; } return "host"; } return "offline"; } } [SourceNotice("ActuallyWorkingEmployees by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-ActuallyWorkingEmployees, not decompiled code.")] internal sealed class OptionSync : IFeature { internal const string LobbyKey = "awe_opts"; private const string WireVersion = "2"; private const string LegacyWireVersion = "1"; private const float PollSeconds = 2f; private const float RetrySeconds = 10f; private const float InfoThrottleSeconds = 10f; private const int WarnBytes = 3000; private float _poll; private float _retryIn; private float _clock; private float _lastInfo = -999f; private string _lastWritten = ""; private int _writes; private int _readbackFail; private int _lastBytes; private string _lastHash = ""; private string _appliedHash = ""; private int _applied; private string _badWire = ""; private bool _wasClient; private int _guestWas = -1; private static Dictionary<Option, string> _shortByOption; private static Dictionary<string, Option> _optionByShort; public string Name => "coop-sync"; public string Status => "role=" + Net.RoleText() + " bytes=" + _lastBytes + " wire=" + WireBytes() + " writes=" + _writes + " readback_fail=" + _readbackFail + " applied=" + _applied + " hash=" + ((_lastHash.Length > 0) ? _lastHash : "-"); public void Init() { } public void Reset() { bool isClientOnly = Net.IsClientOnly; if (_appliedHash.Length > 0 && !isClientOnly) { ClearHostValues("state reset"); } _lastWritten = ""; _lastHash = ""; _lastBytes = 0; _poll = 0f; _retryIn = 0f; _guestWas = -1; if (!isClientOnly) { _wasClient = false; } } private static int WireBytes() { if (!Net.IsServer) { return 0; } string hash; return Utf8("awe_opts") + Utf8(Encode(out hash)); } public void Tick(float dt) { _clock += dt; _poll += dt; if (_poll < 2f) { return; } _poll = 0f; if (Net.IsClientOnly) { _wasClient = true; ClientPoll(); return; } if (_wasClient) { _wasClient = false; if (_appliedHash.Length > 0) { ClearHostValues("left the host's session"); } } if (Net.IsServer) { HostPoll(); } } private void HostPoll() { if (_retryIn > 0f) { _retryIn = Math.Max(0f, _retryIn - 2f); } bool flag = Net.IsCoop(); if ((flag ? 1 : 0) != _guestWas) { if (flag || _guestWas == 1) { Instance log = Core.Log; if (log != null) { log.Msg("[CoopSync] " + (flag ? "guest present, publishing" : "no guest, publishing paused") + ": lobby players " + Net.LobbyPlayers() + ", remote clients " + Net.RemoteClients() + "."); } } _guestWas = (flag ? 1 : 0); } if (!flag || _retryIn > 0f) { return; } Lobby val = SafeLobby("host poll"); if ((Object)(object)val == (Object)null) { return; } string hash; string text = Encode(out hash); _lastHash = hash; _lastBytes = Utf8("awe_opts") + Utf8(text); if (ReadKey(val) == text) { _lastWritten = text; return; } if (_lastBytes > 3000) { Instance log2 = Core.Log; if (log2 != null) { log2.Warning("[CoopSync] payload is " + _lastBytes + " bytes of Steam's ~8192 lobby budget."); } } Write(val, text, hash); } private void Write(Lobby lobby, string wire, string hash) { bool flag = false; try { lobby.SetLobbyData("awe_opts", wire); } catch (Exception ex) { flag = true; _readbackFail++; _retryIn = 10f; Instance log = Core.Log; if (log != null) { log.Error("[CoopSync] SetLobbyData threw for key awe_opts (" + _lastBytes + " bytes): " + ex.GetType().Name + ": " + ex.Message); } } if (flag) { return; } string text = ReadKey(lobby); if (text != wire) { _readbackFail++; _retryIn = 10f; Instance log2 = Core.Log; if (log2 != null) { log2.Error("[CoopSync] lobby write not confirmed for key awe_opts (" + _lastBytes + " bytes, wrote hash " + hash + ", read back " + ((text == null) ? "nothing" : (Utf8(text) + " bytes")) + "). Steam refuses writes once the lobby's ~8 KB are used up."); } } else { _lastWritten = wire; _writes++; Info("[CoopSync] published " + OptionCountOf(wire) + " changed option(s), key awe_opts, " + _lastBytes + " bytes, hash " + hash + "."); } } private void ClientPoll() { Lobby val = SafeLobby("client poll"); if ((Object)(object)val == (Object)null) { return; } string text = ReadKey(val); if (string.IsNullOrEmpty(text)) { if (_appliedHash.Length > 0) { ClearHostValues("the host's key is gone"); } return; } if (!TryDecode(text, out var hash, out var values, out var error)) { if (text != _badWire) { _badWire = text; Instance log = Core.Log; if (log != null) { log.Error("[CoopSync] could not decode the host's options (" + Utf8(text) + " bytes), local values untouched: " + error); } } return; } _lastHash = hash; _lastBytes = Utf8("awe_opts") + Utf8(text); if (!(hash == _appliedHash)) { ApplyHostValues(values); _appliedHash = hash; _applied++; Info("[CoopSync] applied the host's options: " + values.Count + " changed, hash " + hash + ", " + _lastBytes + " bytes."); } } private static void ApplyHostValues(List<KeyValuePair<Option, string>> changed) { Dictionary<Option, string> dictionary = new Dictionary<Option, string>(); foreach (KeyValuePair<Option, string> item in changed) { dictionary[item.Key] = item.Value; } foreach (Option item2 in Options.All) { if (item2.HostOnly) { item2.SetHostValue(dictionary.TryGetValue(item2, out var value) ? value : item2.DefaultText); } } } private void ClearHostValues(string why) { int num = 0; foreach (Option item in Options.All) { if (item.HostOnly) { try { item.ClearHostValue(); num++; } catch (Exception e) { Core.Failed("coop-sync: clear host value " + item.Key, e); } } } _appliedHash = ""; Info("[CoopSync] cleared the host's values on " + num + " option(s): " + why + "."); } internal static string Encode(out string hash) { StringBuilder stringBuilder = new StringBuilder(); foreach (Option item in Options.All) { if (!item.HostOnly) { continue; } string text = EffectiveText(item); if (text == item.DefaultText) { continue; } string text2 = ((item.Kind != OptionKind.Bool) ? text : ((text == "true") ? "1" : "0")); string value; if (!Safe(text2)) { Instance log = Core.Log; if (log != null) { log.Error("[CoopSync] option " + item.Key + " skipped: its value '" + text2 + "' contains a reserved character."); } } else if (!ShortKeys().TryGetValue(item, out value)) { Instance log2 = Core.Log; if (log2 != null) { log2.Error("[CoopSync] option " + item.Key + " skipped: its short key collides with another option."); } } else { if (stringBuilder.Length > 0) { stringBuilder.Append(','); } stringBuilder.Append(value).Append('=').Append(text2); } } string text3 = stringBuilder.ToString(); hash = Fnv(text3); return "2|" + hash + "|" + text3; } private static Dictionary<Option, string> ShortKeys() { if (_shortByOption != null && _shortByOption.Count > 0) { return _shortByOption; } Dictionary<Option, string> dictionary = new Dictionary<Option, string>(); Dictionary<string, Option> dictionary2 = new Dictionary<string, Option>(); HashSet<string> hashSet = new HashSet<string>(); foreach (Option item in Options.All) { if (!item.HostOnly) { continue; } string text = ShortKey(item.Key); if (dictionary2.TryGetValue(text, out var value)) { if (hashSet.Add(text)) { dictionary.Remove(value); Instance log = Core.Log; if (log != null) { log.Error("[CoopSync] options " + value.Key + " and " + item.Key + " share the short key " + text + "; neither is synced."); } } else { Instance log2 = Core.Log; if (log2 != null) { log2.Error("[CoopSync] option " + item.Key + " also shares the short key " + text + "; not synced."); } } } else { dictionary2[text] = item; dictionary[item] = text; } } foreach (string item2 in hashSet) { dictionary2.Remove(item2); } _optionByShort = dictionary2; _shortByOption = dictionary; return dictionary; } private static string ShortKey(string key) { uint num = 2166136261u; byte[] bytes = Encoding.UTF8.GetBytes(key); foreach (byte b in bytes) { num ^= b; num *= 16777619; } uint num2 = num % 1679616; char[] array = new char[4]; for (int num3 = 3; num3 >= 0; num3--) { uint num4 = num2 % 36; array[num3] = (char)((num4 < 10) ? (48 + num4) : (97 + (num4 - 10))); num2 /= 36; } return new string(array); } private static string EffectiveText(Option o) { if (!o.Balance || !Options.VanillaForced) { return o.ValueText; } return o.DefaultText; } private static bool Safe(string s) { return s.IndexOfAny(new char[5] { ';', ',', '=', ':', '|' }) < 0; } internal static bool TryDecode(string wire, out string hash, out List<KeyValuePair<Option, string>> values, out string error) { hash = ""; values = new List<KeyValuePair<Option, string>>(); error = null; string[] array = wire.Split(new char[1] { '|' }, 3); if (array.Length != 3) { error = "not in the form version|hash|body"; return false; } bool flag = array[0] == "1"; if (array[0] != "2" && !flag) { error = "unsupported wire version '" + array[0] + "' (this mod reads 1 and 2)"; return false; } hash = array[1]; string text = array[2]; if (Fnv(text) != hash) { error = "hash mismatch, the lobby value is cut off or damaged (header " + hash + ", body " + Fnv(text) + ")"; return false; } List<string> list = new List<string>(); if (!flag) { ShortKeys(); if (text.Length > 0) { string[] array2 = text.Split(','); foreach (string text2 in array2) { int num = text2.IndexOf('='); if (num <= 0) { error = "entry without a value: '" + text2 + "'"; return false; } string text3 = text2.Substring(0, num); string raw = text2.Substring(num + 1); if (_optionByShort == null || !_optionByShort.TryGetValue(text3, out var value)) { list.Add(text3); continue; } if (!TryValue(value, raw, out var text4, out var why)) { error = value.Key + ": " + why; return false; } values.Add(new KeyValuePair<Option, string>(value, text4)); } } } else if (text.Length > 0) { string[] array2 = text.Split(';'); foreach (string text5 in array2) { int num2 = text5.IndexOf(':'); if (num2 <= 0) { error = "group entry without a name: '" + text5 + "'"; return false; } string text6 = text5.Substring(0, num2); string[] array3 = text5.Substring(num2 + 1).Split(','); foreach (string text7 in array3) { int num3 = text7.IndexOf('='); if (num3 <= 0) { error = "entry without a value: '" + text7 + "'"; return false; } string text8 = text6 + "." + text7.Substring(0, num3); string raw2 = text7.Substring(num3 + 1); Option option = Options.Find(text8); if (option == null || !option.HostOnly) { list.Add(text8); continue; } if (!TryValue(option, raw2, out var text9, out var why2)) { error = text8 + ": " + why2; return false; } values.Add(new KeyValuePair<Option, string>(option, text9)); } } } if (list.Count > 0) { Core.WarnOnce("coop-sync unknown " + hash, "[CoopSync] the host sent " + list.Count + " option(s) this version does not know, skipped: " + string.Join(", ", list.Take(8)) + ((list.Count > 8) ? ", ..." : "")); } return true; } private static bool TryValue(Option o, string raw, out string text, out string why) { text = raw; why = null; switch (o.Kind) { case OptionKind.Bool: if (raw == "1") { text = "true"; return true; } if (raw == "0") { text = "false"; return true; } why = "expected 1 or 0, got '" + raw + "'"; return false; case OptionKind.Int: { if (int.TryParse(raw, NumberStyles.Integer, CultureInfo.InvariantCulture, out var _)) { return true; } why = "expected a whole number, got '" + raw + "'"; return false; } case OptionKind.Float: { if (float.TryParse(raw, NumberStyles.Float, CultureInfo.InvariantCulture, out var result2) && !float.IsNaN(result2)) { return true; } why = "expected a number, got '" + raw + "'"; return false; } case OptionKind.Choice: { string text2 = ((ChoiceOption)o).Choices.FirstOrDefault((string c) => string.Equals(c, raw, StringComparison.OrdinalIgnoreCase)); if (text2 != null) { text = text2; return true; } why = "'" + raw + "' is not one of the choices"; return false; } default: why = "unknown option kind"; return false; } } private static string Fnv(string s) { uint num = 2166136261u; byte[] bytes = Encoding.UTF8.GetBytes(s); foreach (byte b in bytes) { num ^= b; num *= 16777619; } return num.ToString("x8", CultureInfo.InvariantCulture); } private static int Utf8(string s) { if (s != null) { return Encoding.UTF8.GetByteCount(s); } return 0; } private static int OptionCountOf(string wire) { int num = wire.LastIndexOf('|'); string text = ((num >= 0) ? wire.Substring(num + 1) : ""); if (text.Length != 0) { return text.Count((char c) => c == '='); } return 0; } private static Lobby SafeLobby(string where) { try { Lobby instance = Singleton<Lobby>.Instance; if ((Object)(object)instance == (Object)null) { Core.WarnOnce("coop-sync no lobby " + where, "[CoopSync] " + where + ": no Lobby singleton yet."); } return instance; } catch (Exception e) { Core.Caught("coop-sync: Lobby singleton (" + where + ")", e); return null; } } private static string ReadKey(Lobby lobby) { try { ILobbyService lobbyService = lobby._lobbyService; if (lobbyService == null) { Core.WarnOnce("coop-sync no service", "[CoopSync] the lobby has no lobby service, cannot read awe_opts."); return null; } return lobbyService.GetLobbyData("awe_opts") ?? ""; } catch (Exception e) { Core.Failed("coop-sync: GetLobbyData awe_opts", e); return null; } } private void Info(string msg) { if (!(_clock - _lastInfo < 10f)) { _lastInfo = _clock; Instance log = Core.Log; if (log != null) { log.Msg(msg); } } } } } namespace ActuallyWorkingEmployees.Host { [SourceNotice("ActuallyWorkingEmployees by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-ActuallyWorkingEmployees, not decompiled code.")] internal static class Calls { internal const string HostMsg = "Set by the host."; internal static readonly Dictionary<string, Func<JObject, JObject>> Handlers = new Dictionary<string, Func<JObject, JObject>>(StringComparer.Ordinal) { ["list"] = (JObject _) => BuildState(), ["role"] = (JObject _) => Role(), ["set"] = Set, ["reset"] = Reset, ["resetAll"] = (JObject _) => ResetAll(), ["preset"] = Preset, ["diag"] = DiagCalls.Diag, ["doctorRun"] = DiagCalls.DoctorRun, ["analyticsReset"] = DiagCalls.AnalyticsReset }; internal static string Run(string name, string arg, Func<JObject, JObject> handler) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) try { JObject arg2 = (JObject)((!string.IsNullOrWhiteSpace(arg)) ? ((object)JObject.Parse(arg)) : ((object)new JObject())); return ((JToken)(handler(arg2) ?? Fail("Something went wrong."))).ToString((Formatting)0); } catch (Exception ex) { Core.Failed("app: call " + name, ex); Instance log = Core.Log; if (log != null) { log.Error("[App] " + name + "(" + arg + ") failed: " + ex.Message); } return ((JToken)Fail("Something went wrong.")).ToString((Formatting)0); } } internal static JObject Fail(string msg) { //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) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Expected O, but got Unknown return new JObject { ["ok"] = JToken.op_Implicit(false), ["msg"] = JToken.op_Implicit(msg) }; } internal static JObject BuildState() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Expected O, but got Unknown //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003b: 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_005e: Expected O, but got Unknown JArray val = new JArray(); Dictionary<string, JArray> dictionary = new Dictionary<string, JArray>(StringComparer.OrdinalIgnoreCase); foreach (string group in Options.Groups) { JArray val2 = (dictionary[group] = new JArray()); val.Add((JToken)new JObject { ["name"] = JToken.op_Implicit(group), ["options"] = (JToken)(object)val2 }); } foreach (Option item in Options.All) { if (!dictionary.TryGetValue(item.Group, out var value)) { Core.WarnOnce("app: group " + item.Group, "option " + item.Key + " is in a group the app does not list, skipped."); } else { value.Add((JToken)(object)Describe(item)); } } JObject obj = Role(); obj["groups"] = (JToken)(object)val; obj["notice"] = JToken.op_Implicit(DiagCalls.Notice()); obj["hints"] = (JToken)(object)DiagCalls.Hints(); return obj; } private static JObject Role() { //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) //IL_0016: 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_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Expected O, but got Unknown return new JObject { ["ok"] = JToken.op_Implicit(true), ["role"] = JToken.op_Implicit(Net.RoleText()), ["locked"] = JToken.op_Implicit(Net.IsClientOnly), ["vanilla"] = JToken.op_Implicit(Options.VanillaForced) }; } private static JObject Describe(Option o) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: 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_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: 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) //IL_0126: Unknown result type (might be due to invalid IL or missing references) //IL_014a: Unknown result type (might be due to invalid IL or missing references) //IL_0160: Unknown result type (might be due to invalid IL or missing references) //IL_0177: Expected O, but got Unknown //IL_0288: Unknown result type (might be due to invalid IL or missing references) //IL_0292: Expected O, but got Unknown string text = o.HostValueText ?? o.ValueText; string text2 = ((o.HostValueText != null || o.Editable) ? o.DisplayText : o.Describe(o.ValueText)); JObject val = new JObject { ["key"] = JToken.op_Implicit(o.Key), ["label"] = JToken.op_Implicit(o.Label), ["help"] = JToken.op_Implicit(o.Help), ["kind"] = JToken.op_Implicit(o.Kind.ToString().ToLowerInvariant()), ["value"] = JToken.op_Implicit(text), ["text"] = JToken.op_Implicit(text2), ["def"] = JToken.op_Implicit(o.DefaultText), ["defText"] = JToken.op_Implicit(o.Describe(o.DefaultText)), ["isDefault"] = JToken.op_Implicit(o.IsDefault), ["editable"] = JToken.op_Implicit(o.Editable), ["waiting"] = JToken.op_Implicit(!o.Editable && o.HostValueText == null), ["hostOnly"] = JToken.op_Implicit(o.HostOnly), ["balance"] = JToken.op_Implicit(o.Balance) }; if (!(o is IntOption intOption)) { if (!(o is FloatOption floatOption)) { if (o is ChoiceOption choiceOption) { object[] choices = choiceOption.Choices; val["choices"] = (JToken)new JArray(choices); } } else { val["min"] = JToken.op_Implicit(Math.Round(floatOption.Min, 3)); val["max"] = JToken.op_Implicit(Math.Round(floatOption.Max, 3)); val["step"] = JToken.op_Implicit(Math.Round(floatOption.Step, 3)); val["sentinel"] = JToken.op_Implicit(floatOption.VanillaSentinel); } } else { val["min"] = JToken.op_Implicit(intOption.Min); val["max"] = JToken.op_Implicit(intOption.Max); val["step"] = JToken.op_Implicit(intOption.Step); val["sentinel"] = JToken.op_Implicit(intOption.VanillaSentinel); } return val; } private static JObject Set(JObject a) { //IL_0140: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_0156: Unknown result type (might be due to invalid IL or missing references) //IL_0168: Expected O, but got Unknown Option option = Options.Find((string)a["key"]); if (option == null) { Instance log = Core.Log; if (log != null) { log.Warning("[App] set: no option '" + (string)a["key"] + "'."); } return Fail("Unknown setting."); } if (!option.Editable) { Instance log2 = Core.Log; if (log2 != null) { log2.Warning("[App] set " + option.Key + " refused: the host controls it."); } return Fail("Set by the host."); } string text = ((string)a["value"]) ?? ""; if (!option.TrySet(text, out var error)) { Instance log3 = Core.Log; if (log3 != null) { log3.Warning("[App] set " + option.Key + " = '" + text + "' rejected: " + error); } return Fail(error); } Instance log4 = Core.Log; if (log4 != null) { log4.Msg("[App] " + option.Key + " = " + option.ValueText + "."); } return new JObject { ["ok"] = JToken.op_Implicit(true), ["option"] = (JToken)(object)Describe(option) }; } private static JObject Reset(JObject a) { //IL_00d6: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Expected O, but got Unknown Option option = Options.Find((string)a["key"]); if (option == null) { Instance log = Core.Log; if (log != null) { log.Warning("[App] reset: no option '" + (string)a["key"] + "'."); } return Fail("Unknown setting."); } if (!option.Editable) { Instance log2 = Core.Log; if (log2 != null) { log2.Warning("[App] reset " + option.Key + " refused: the host controls it."); } return Fail("Set by the host."); } option.ResetToDefault(); Instance log3 = Core.Log; if (log3 != null) { log3.Msg("[App] " + option.Key + " reset to " + option.ValueText + "."); } return new JObject { ["ok"] = JToken.op_Implicit(true), ["option"] = (JToken)(object)Describe(option) }; } private static JObject ResetAll() { int num = Options.ResetAllToDefault(); Instance log = Core.Log; if (log != null) { log.Msg("[App] reset all (" + Net.RoleText() + "): " + num + " setting(s) back to default."); } JObject obj = BuildState(); obj["changed"] = JToken.op_Implicit(num); return obj; } private static JObject Preset(JObject a) { string text = (string)a["name"]; Presets.Result result = Presets.Apply(text); if (result.Refused != null) { Instance log = Core.Log; if (log != null) { log.Warning("[App] preset " + text + " refused: " + result.Refused); } return Fail(Net.IsClientOnly ? "Set by the host." : "Something went wrong."); } if (result.Errors.Count > 0) { return Fail("Something went wrong."); } Instance log2 = Core.Log; if (log2 != null) { log2.Msg("[App] preset " + text + " (" + Net.RoleText() + "): " + result.Changed + " of " + result.Total + " setting(s) changed."); } JObject obj = BuildState(); obj["changed"] = JToken.op_Implicit(result.Changed); return obj; } } [SourceNotice("ActuallyWorkingEmployees by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-ActuallyWorkingEmployees, not decompiled code.")] internal static class DiagCalls { internal const string LockNotice = "Some settings are set by the host."; internal const string StationHint = "Saved mixes and stations that already run keep their old time."; private const int MaxAnalyticsRows = 30; private const int MaxReasonsPerRow = 3; internal static JObject Hints() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown JObject val = new JObject(); if (Options.ShowStationSpeedWarning.Value && StationSpeedChanged()) { val["Stations"] = JToken.op_Implicit("Saved mixes and stations that already run keep their old time."); } return val; } internal static string Notice() { if (!Net.IsClientOnly || !Options.ShowCoopLockNotice.Value) { return ""; } return "Some settings are set by the host."; } private static bool StationSpeedChanged() { foreach (Option item in Options.InGroup("Stations")) { if (item.Balance && !item.IsDefault) { return true; } } return false; } internal static JObject Diag(JObject _) { return Build(); } internal static JObject DoctorRun(JObject _) { if (!SetupDoctor.Run(out var refusal)) { return Calls.Fail(refusal); } JObject obj = Build(); obj["ran"] = JToken.op_Implicit(true); return obj; } internal static JObject AnalyticsReset(JObject _) { if (!Net.IsServer) { Instance log = Core.Log; if (log != null) { log.Warning("[App] analytics reset refused: host only."); } return Calls.Fail("Host only."); } IdleAnalytics.Clear(); Instance log2 = Core.Log; if (log2 != null) { log2.Msg("[App] analytics cleared."); } return Build(); } internal static JObject Build() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: 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_002d: Unknown result type (might be due to invalid IL or missing references) //IL_003e: 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) //IL_0061: Expected O, but got Unknown bool isServer = Net.IsServer; return new JObject { ["ok"] = JToken.op_Implicit(true), ["host"] = JToken.op_Implicit(isServer), ["badges"] = (JToken)(object)BadgesJson(isServer), ["doctor"] = (JToken)(object)DoctorJson(isServer), ["analytics"] = (JToken)(object)AnalyticsJson(isServer) }; } private static JObject BadgesJson(bool host) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_010d: Expected O, but got Unknown //IL_0042: 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_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0075: 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_00bd: Expected O, but got Unknown bool value = Options.ShowProblemBadges.Value; JArray val = new JArray(); int num = 0; if (host && value) { foreach (ProblemBadges.Badge item in ProblemBadges.Build()) { if (item.HasProblem) { num++; } val.Add((JToken)new JObject { ["name"] = JToken.op_Implicit(item.Name), ["role"] = JToken.op_Implicit(item.Role), ["sev"] = JToken.op_Implicit(item.HasProblem ? Finding.SevText(item.Severity) : "ok"), ["text"] = JToken.op_Implicit(item.Text) }); } } return new JObject { ["enabled"] = JToken.op_Implicit(value), ["problems"] = JToken.op_Implicit(num), ["items"] = (JToken)(object)val }; } private static JObject DoctorJson(bool host) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00ed: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: 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_00bf: Expected O, but got Unknown //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_0153: Unknown result type (might be due to invalid IL or missing references) //IL_0175: Unknown result type (might be due to invalid IL or missing references) //IL_0182: Expected O, but got Unknown JArray val = new JArray(); bool flag = host && SetupDoctor.HasRun; if (flag) { for (int i = 0; i < SetupDoctor.Shown; i++) { Finding finding = SetupDoctor.Findings[i]; val.Add((JToken)new JObject { ["sev"] = JToken.op_Implicit(Finding.SevText(finding.Severity)), ["cat"] = JToken.op_Implicit(finding.Category), ["subject"] = JToken.op_Implicit(finding.Subject), ["msg"] = JToken.op_Implicit(finding.Message), ["fix"] = JToken.op_Implicit(finding.Fix), ["count"] = JToken.op_Implicit(finding.Count) }); } } return new JObject { ["enabled"] = JToken.op_Implicit(Options.EnableSetupDoctor.Value), ["ran"] = JToken.op_Implicit(flag), ["total"] = JToken.op_Implicit(flag ? SetupDoctor.Total : 0), ["shown"] = JToken.op_Implicit(flag ? SetupDoctor.Shown : 0), ["summary"] = JToken.op_Implicit(flag ? SetupDoctor.Summary() : ""), ["ageSec"] = JToken.op_Implicit(flag ? ((int)(Time.realtimeSinceStartup - SetupDoctor.RanAt)) : 0), ["findings"] = (JToken)(object)val }; } private static JObject AnalyticsJson(bool host) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Expected O, but got Unknown //IL_01ba: Unknown result type (might be due to invalid IL or missing references) //IL_01bf: Unknown result type (might be due to invalid IL or missing references) //IL_01d9: Unknown result type (might be due to invalid IL or missing references) //IL_01fa: Unknown result type (might be due to invalid IL or missing references) //IL_020f: Unknown result type (might be due to invalid IL or missing references) //IL_021c: Expected O, but got Unknown //IL_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Expected O, but got Unknown //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00c7: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Expected O, but got Unknown //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_012c: Unknown result type (might be due to invalid IL or missing references) //IL_0142: Unknown result type (might be due to invalid IL or missing references) //IL_0158: Unknown result type (might be due to invalid IL or missing references) //IL_0192: Unknown result type (might be due to invalid IL or missing references) //IL_01a3: Expected O, but got Unknown JArray val = new JArray(); if (host && Options.EnableAnalytics.Value) { foreach (IdleAnalytics.Stat item in IdleAnalytics.Stats.Values.OrderByDescending((IdleAnalytics.Stat s) => (s.Samples != 0) ? ((double)s.IdleSamples / (double)s.Samples) : 0.0).Take(30)) { JArray val2 = new JArray(); foreach (KeyValuePair<string, int> item2 in item.Reasons.OrderByDescending((KeyValuePair<string, int> r) => r.Value).Take(3)) { val2.Add((JToken)new JObject { ["text"] = JToken.op_Implicit(item2.Key), ["count"] = JToken.op_Implicit(item2.Value) }); } val.Add((JToken)new JObject { ["name"] = JToken.op_Implicit(item.Name), ["role"] = JToken.op_Implicit(item.Role), ["samples"] = JToken.op_Implicit(item.Samples), ["idle"] = JToken.op_Implicit(item.IdleSamples), ["idlePct"] = JToken.op_Implicit((item.Samples != 0) ? ((int)Math.Round(100.0 * (double)item.IdleSamples / (double)item.Samples)) : 0), ["reasons"] = (JToken)(object)val2 }); } } return new JObject { ["enabled"] = JToken.op_Implicit(Options.EnableAnalytics.Value), ["interval"] = JToken.op_Implicit(Math.Round(Options.AnalyticsIntervalSeconds.Value, 1)), ["samples"] = JToken.op_Implicit(IdleAnalytics.TotalSamples), ["items"] = (JToken)(object)val }; } } [SourceNotice("ActuallyWorkingEmployees by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-ActuallyWorkingEmployees, not decompiled code.")] internal sealed class PhoneApp : IFeature { internal const string AppId = "awe"; internal const string Bundle = "ActuallyWorkingEmployees.Assets.app"; private static AppHandle _app; private static volatile bool _dirty; private static string _roleSig = ""; private static float _sinceRoleCheck; private static bool _hooked; public string Name => "PhoneApp"; public string Status { get { if (_app != null) { return "registered, sideload " + (Apps.Available ? "present" : "absent") + ", page " + (_app.IsOpen ? "open" : "closed"); } return "not registered"; } } public void Init() { try { _app = Apps.Register("awe", "ActuallyWorkingEmployees.Assets.app", "Employees", "Employees"); _app.Orientation("landscape", "portrait"); foreach (KeyValuePair<string, Func<JObject, JObject>> handler in Calls.Handlers) { KeyValuePair<string, Func<JObject, JObject>> h = handler; _app.OnCall(h.Key, (string arg) => Calls.Run(h.Key, arg, h.Value)); } HookOptions(); Instance log = Core.Log; if (log != null) { log.Msg(Apps.Available ? "[App] registered with Sideload as 'awe'." : "[App] Sideload is not loaded yet, the registration is queued."); } } catch (Exception e) { Core.Failed("app: register the phone app", e); } } public void Tick(float dt) { if (_app == null) { return; } _sinceRoleCheck += dt; if (_sinceRoleCheck >= 0.5f) { _sinceRoleCheck = 0f; string text = Net.RoleText(); if (text != _roleSig) { _roleSig = text; _dirty = true; } } if (!_dirty) { return; } _dirty = false; try { _app.Emit("options.changed", _roleSig); } catch (Exception e) { Core.Caught("app: emit options.changed", e); } } public void Reset() { _dirty = true; } private static void HookOptions() { if (_hooked) { Instance log = Core.Log; if (log != null) { log.Warning("[App] option hooks already installed, skipped."); } return; } _hooked = true; foreach (Option item in Options.All) { item.Changed += delegate { _dirty = true; }; } } } } namespace ActuallyWorkingEmployees.Features { [SourceNotice("ActuallyWorkingEmployees by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-ActuallyWorkingEmployees, not decompiled code.")] internal interface IFeature { string Name { get; } string Status { get; } void Init(); void Tick(float dt); void Reset(); } [SourceNotice("ActuallyWorkingEmployees by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-ActuallyWorkingEmployees, not decompiled code.")] internal static class FeatureHost { private static readonly List<IFeature> _all = new List<IFeature>(); internal static IReadOnlyList<IFeature> All => _all; internal static void Discover() { Type[] source; try { source = typeof(FeatureHost).Assembly.GetTypes(); } catch (ReflectionTypeLoadException ex) { Core.Caught("features: GetTypes", ex); source = ex.Types.Where((Type t) => t != null).ToArray(); } foreach (Type item in from t in source where !t.IsAbstract && !t.IsInterface && typeof(IFeature).IsAssignableFrom(t) orderby t.FullName select t) { IFeature feature; try { feature = (IFeature)Activator.CreateInstance(item, nonPublic: true); } catch (Exception e) { Core.Failed("features: create " + item.Name, e); continue; } try { feature.Init(); _all.Add(feature); if (Core.Verbose) { Instance log = Core.Log; if (log != null) { log.Msg("[Features] " + feature.Name + " ready."); } } } catch (Exception e2) { Core.Failed("features: init " + item.Name, e2); } } Instance log2 = Core.Log; if (log2 != null) { log2.Msg("[Features] " + _all.Count + " active: " + string.Join(", ", _all.Select((IFeature f) => f.Name))); } } internal static void TickAll(float dt) { if (!Core.InWorld) { return; } for (int i = 0; i < _all.Count; i++) { IFeature feature = _all[i]; try { feature.Tick(dt); } catch (Exception e) { Core.Failed("features: tick " + feature.Name, e); } } } internal static void ResetAll() { foreach (IFeature item in _all) { try { item.Reset(); } catch (Exception e) { Core.Failed("features: reset " + item.Name, e); } } } } } namespace ActuallyWorkingEmployees.Features.World { [SourceNotice("ActuallyWorkingEmployees by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-ActuallyWorkingEmployees, not decompiled code.")] internal sealed class EmployeeCollision : IFeature { private struct AgentOriginal { internal float Radius; internal int Priority; internal ObstacleAvoidanceType Avoidance; internal bool AvoidanceFlag; } internal const string FeatureName = "collision"; private const float AgentRadius = 0.15f; private const float EmployeePass = 1f; private const float FurniturePass = 5f; private const float FurnitureRescan = 30f; private readonly Dictionary<int, List<Collider>> _changedColliders = new Dictionary<int, List<Collider>>(); private readonly Dictionary<int, AgentOriginal> _agents = new Dictionary<int, AgentOriginal>(); private readonly Dictionary<int, Component> _agentOwner = new Dictionary<int, Component>(); private readonly Dictionary<int, NPCMovement> _movement = new Dictionary<int, NPCMovement>(); private readonly List<NavMeshObstacle> _disabled = new List<NavMeshObstacle>(); private readonly HashSet<int> _scannedItems = new HashSet<int>(); private bool _empWas; private bool _furnWas; private int _lastItemCount = -1; private int _obstaclesSeen; private float _nextEmp; private float _nextFurn; private float _nextRescan; public string Name => "collision"; public string Status => "employees=" + (Options.DisableEmployeeCollision.Value ? 1 : 0) + " furniture=" + (Options.DisableFurnitureCollision.Value ? 1 : 0) + " triggerEmployees=" + _changedColliders.Count + " agents=" + _agents.Count + " avoidanceOff=" + AvoidanceOffCount() + " obstaclesOff=" + _disabled.Count + " obstaclesSeen=" + _obstaclesSeen; public void Init() { } public void Reset() { _changedColliders.Clear(); _agents.Clear(); _agentOwner.Clear(); _movement.Clear(); _disabled.Clear(); _scannedItems.Clear(); _empWas = (_furnWas = false); _lastItemCount = -1; _obstaclesSeen = 0; _nextEmp = (_nextFurn = (_nextRescan = 0f)); } public void Tick(float dt) { float unscaledTime = Time.unscaledTime; bool value = Options.DisableEmployeeCollision.Value; bool value2 = Options.DisableFurnitureCollision.Value; if (!value && !_empWas && !value2 && !_furnWas) { return; } if (!Net.IsServer) { Rel.MsgOnce("collision:client", "[collision] not the host, vanilla collision on this client."); return; } if (unscaledTime >= _nextEmp && (value || _empWas)) { _nextEmp = unscaledTime + 1f; EmployeePassNow(value); } if (unscaledTime >= _nextFurn && (value2 || _furnWas)) { _nextFurn = unscaledTime + 5f; FurniturePassNow(value2, unscaledTime); } } private void EmployeePassNow(bool on) { //IL_0184: Unknown result type (might be due to invalid IL or missing references) //IL_0189: Unknown result type (might be due to invalid IL or missing references) if (!on) { RestoreEmployees(); _empWas = false; return; } if (!_empWas) { Instance log = Core.Log; if (log != null) { log.Msg("[collision] employees pass through each other."); } } _empWas = true; foreach (Employee item in Rel.Employees()) { int instanceID = ((Object)item).GetInstanceID(); if (_changedColliders.ContainsKey(instanceID)) { if (_movement.TryGetValue(instanceID, out var value) && (Object)(object)value != (Object)null && value._obstacleAvoidanceEnabled) { value.SetObstacleAvoidanceEnabled(false); } continue; } List<Collider> list = new List<Collider>(1); NPCMovement movement = ((NPC)item).Movement; CapsuleCollider val = (((Object)(object)movement != (Object)null) ? movement._capsuleCollider : null); if ((Object)(object)val == (Object)null) { Core.WarnOnce("collision:nocapsule:" + instanceID, Rel.Name((NPC)(object)item) + " has no movement capsule; its collision stays vanilla."); } else if (!((Collider)val).isTrigger) { ((Collider)val).isTrigger = true; list.Add((Collider)(object)val); } _changedColliders[instanceID] = list; NavMeshAgent component = ((Component)item).GetComponent<NavMeshAgent>(); if ((Object)(object)component == (Object)null) { Core.WarnOnce("collision:noagent:" + instanceID, Rel.Name((NPC)(object)item) + " has no NavMeshAgent; only its capsule was changed."); continue; } _agents[instanceID] = new AgentOriginal { Radius = component.radius, Priority = component.avoidancePriority, Avoidance = component.obstacleAvoidanceType, AvoidanceFlag = ((Object)(object)movement == (Object)null || movement._obstacleAvoidanceEnabled) }; _agentOwner[instanceID] = (Component)(object)component; if (component.radius > 0.15f) { component.radius = 0.15f; } component.obstacleAvoidanceType = (ObstacleAvoidanceType)0; component.avoidancePriority = 0; if ((Object)(object)movement == (Object)null) { Core.WarnOnce("collision:nomovement:" + instanceID, Rel.Name((NPC)(object)item) + " has no NPCMovement; obstacle avoidance comes back when a player is near."); continue; } _movement[instanceID] = movement; movement.SetObstacleAvoidanceEnabled(false); } } private void RestoreEmployees() { //IL_00eb: Unknown result type (might be due to invalid IL or missing references) int num = 0; foreach (KeyValuePair<int, List<Collider>> changedCollider in _changedColliders) { foreach (Collider item in changedCollider.Value) { if ((Object)(object)item != (Object)null) { item.isTrigger = false; num++; } } } foreach (KeyValuePair<int, AgentOriginal> agent in _agents) { if (!_agentOwner.TryGetValue(agent.Key, out var value)) { continue; } NavMeshAgent val = ((Il2CppObjectBase)value).TryCast<NavMeshAgent>(); if (!((Object)(object)val == (Object)null)) { val.radius = agent.Value.Radius; val.avoidancePriority = agent.Value.Priority; val.obstacleAvoidanceType = agent.Value.Avoidance; if (_movement.TryGetValue(agent.Key, out var value2) && (Object)(object)value2 != (Object)null) { value2.SetObstacleAvoidanceEnabled(agent.Value.AvoidanceFlag); } } } Instance log = Core.Log; if (log != null) { log.Msg("[collision] employee collision restored on " + _changedColliders.Count + " employee(s), " + num + " collider(s)."); } _changedColliders.Clear(); _agents.Clear(); _agentOwner.Clear(); _movement.Clear(); } private int AvoidanceOffCount() { int num = 0; foreach (KeyValuePair<int, NPCMovement> item in _movement) { if ((Object)(object)item.Value != (Object)null && !item.Value._obstacleAvoidanceEnabled) { num++; } } return num; } private void FurniturePassNow(bool on, float now) { if (!on) { int num = 0; for (int i = 0; i < _disabled.Count; i++) { if ((Object)(object)_disabled[i] != (Object)null) { ((Behaviour)_disabled[i]).enabled = true; num++; } } Instance log = Core.Log; if (log != null) { log.Msg("[collision] furniture obstacles restored on " + num + " obstacle(s)."); } if (num > 0) { ObstaclesChanged("furniture obstacles are back"); } _disabled.Clear(); _scannedItems.Clear(); _lastItemCount = -1; _furnWas = false; return; } if (!_furnWas) { Instance log2 = Core.Log; if (log2 != null) { log2.Msg("[collision] employees ignore furniture colliders."); } } _furnWas = true; int num2 = CountItems(); if (num2 == _lastItemCount && now < _nextRescan) { return; } _lastItemCount = num2; _nextRescan = now + 30f; int count = _disabled.Count; ScanSources(); if (_disabled.Count != count) { Instance log3 = Core.Log; if (log3 != null) { log3.Msg("[collision] " + (_disabled.Count - count) + " more obstacle(s) switched off (" + _disabled.Count + " in total, " + _obstaclesSeen + " found)."); } ObstaclesChanged("furniture obstacles were switched off"); } } private static void ObstaclesChanged(string why) { int num = NavMeshUtility.sampleCacheKeys?.Count ?? 0; NavMeshUtility.ClearCache(); Instance log = Core.Log; if (log != null) { log.Msg("[collision] " + num + " cached NavMesh sample(s) dropped: " + why + "."); } Fix6ReachCache.Forget(why); } private static int CountItems() { int num = 0; List<Property> properties = Property.Properties; if (properties != null) { for (int i = 0; i < properties.Count; i++) { int num2 = num; Property obj = properties[i]; num = num2 + ((obj != null) ? obj.BuildableItems?.Count : ((int?)null)).GetValueOrDefault(); } } List<Business> businesses = Business.Businesses; if (businesses != null) { for (int j = 0; j < businesses.Count; j++) { int num3 = num; Business obj2 = businesses[j]; num = num3 + ((obj2 != null) ? ((Property)obj2).BuildableItems?.Count : ((int?)null)).GetValueOrDefault(); } } return num; } private void ScanSources() { List<Property> properties = Property.Properties; if (properties != null) { for (int i = 0; i < properties.Count; i++) { Property obj = properties[i]; ScanItems((obj != null) ? obj.BuildableItems : null); } } List<Business> businesses = Business.Businesses; if (businesses != null) { for (int j = 0; j < businesses.Count; j++) { Business obj2 = businesses[j]; ScanItems((obj2 != null) ? ((Property)obj2).BuildableItems : null); } } } private void ScanItems(List<BuildableItem> items) { if (items == null) { return; } for (int i = 0; i < items.Count; i++) { BuildableItem val = items[i]; if ((Object)(object)val == (Object)null || !_scannedItems.Add(((Object)val).GetInstanceID())) { continue; } Il2CppArrayBase<NavMeshObstacle> componentsInChildren = ((Component)val).GetComponentsInChildren<NavMeshObstacle>(true); for (int j = 0; j < componentsInChildren.Length; j++) { NavMeshObstacle val2 = componentsInChildren[j]; if (!((Object)(object)val2 == (Object)null)) { _obstaclesSeen++; if (((Behaviour)val2).enabled) { ((Behaviour)val2).enabled = false; _disabled.Add(val2); } } } } } } [SourceNotice("ActuallyWorkingEmployees by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-ActuallyWorkingEmployees, not decompiled code.")] internal sealed class IdlePointSharing : IFeature { internal const string FeatureName = "idle-points"; private static int _shared; private static int _unchanged; public string Name => "idle-points"; public string Status => "on=" + (Options.ShareIdlePoints.Value ? 1 : 0) + " role=" + Net.RoleText() + " shared=" + _shared + " unchanged=" + _unchanged; public void Init() { } public void Tick(float dt) { } public void Reset() { _shared = (_unchanged = 0); } internal static Il2CppReferenceArray<Transform> Extend(Property prop) { if ((Object)(object)prop == (Object)null) { Core.WarnOnce("idle:noprop", "idle: AssignProperty ran without a property; the game's assignment runs unchanged."); return null; } bool isClientOnly = Net.IsClientOnly; if (!isClientOnly) { if (!Options.ShareIdlePoints.Value) { Core.WarnOnce("idle:off", "idle: sharing is off; the game's assignment runs unchanged."); return null; } if (!Net.IsServer) { Core.WarnOnce("idle:offline", "idle: neither host nor guest; the game's assignment runs unchanged."); return null; } } Il2CppReferenceArray<Transform> employeeIdlePoints = prop.EmployeeIdlePoints; List<Employee> employees = prop.Employees; if (employeeIdlePoints == null || employees == null) { Core.WarnOnce("idle:null:" + ((Object)prop).GetInstanceID(), ((Object)prop).name + " has no idle point or employee list; the game's assignment runs unchanged."); return null; } int num = employees.Count + 1; if (((Il2CppArrayBase<Transform>)(object)employeeIdlePoints).Length >= num) { _unchanged++; return null; } if (((Il2CppArrayBase<Transform>)(object)employeeIdlePoints).Length == 0) { Core.WarnOnce("idle:zero:" + ((Object)prop).GetInstanceID(), ((Object)prop).name + " has no idle points at all; nothing to share."); return null; } Il2CppReferenceArray<Transform> val = new Il2CppReferenceArray<Transform>((long)num); for (int i = 0; i < num; i++) { ((Il2CppArrayBase<Transform>)(object)val)[i] = ((Il2CppArrayBase<Transform>)(object)employeeIdlePoints)[i % ((Il2CppArrayBase<Transform>)(object)employeeIdlePoints).Length]; } prop.EmployeeIdlePoints = val; _shared++; Core.WarnOnce("idle:share:" + ((Object)prop).GetInstanceID(), ((Object)prop).name + ": " + num + " employees share " + ((Il2CppArrayBase<Transform>)(object)employeeIdlePoints).Length + " idle point(s)" + (isClientOnly ? " (guest side)." : ".")); if (!isClientOnly && Net.RemoteClients() > 0) { Core.WarnOnce("idle:vanilla-guest:" + ((Object)prop).GetInstanceID(), ((Object)prop).name + ": " + num + " employees but " + ((Il2CppArrayBase<Transform>)(object)employeeIdlePoints).Length + " idle point(s) while guests are connected; a guest without AWE gets an IndexOutOfRange for the extra employee."); } return employeeIdlePoints; } } [HarmonyPatch(typeof(Employee), "AssignProperty")] [SourceNotice("ActuallyWorkingEmployees by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-ActuallyWorkingEmployees, not decompiled code.")] internal static class Awe_IdlePoints_AssignProperty_Patch { private static bool Prepare(MethodBase original) { if (!(original == null)) { return NativeMethods.AllowPatch(original); } return true; } private static void Prefix(Property prop, out Il2CppReferenceArray<Transform> __state) { __state = null; try { __state = IdlePointSharing.Extend(prop); } catch (Exception e) { Core.Failed("idle: AssignProperty prefix", e); } } private static Exception Finalizer(Property prop, Il2CppReferenceArray<Transform> __state, Exception __exception) { if (__state != null && (Object)(object)prop != (Object)null) { try { prop.EmployeeIdlePoints = __state; } catch (Exception e) { Core.Failed("idle: restore the idle points", e); } } return __exception; } } } namespace ActuallyWorkingEmployees.Features.Reliability { [SourceNotice("ActuallyWorkingEmployees by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-ActuallyWorkingEmployees, not decompiled code.")] internal sealed class Fix1Standstill : IFeature { private sealed class Still { internal Vector3 Spot; internal float Since; internal int Tries; internal bool Stopped; internal bool GaveUp; } internal const string FeatureName = "fix1-standstill"; private static readonly Dictionary<int, Still> _still = new Dictionary<int, Still>(); private static readonly Dictionary<int, int> _partial = new Dictionary<int, int>(); private static int _checks; private static int _warps; private static int _gaveUp; private static int _partialWarps; private static int _partialsCounted; private static int _refused; private static int _stops; private float _next; private static readonly List<Employee> _employees = new List<Employee>(); public string Name => "fix1-standstill"; public string Status => "on=" + (Options.FixStandstill.Value ? 1 : 0) + (Options.FixPartialWalkLoop.Value ? 1 : 0) + " checks=" + _checks + " warps=" + _warps + " gaveup=" + _gaveUp + " partial=" + _partialsCounted + " partialwarps=" + _partialWarps + " refused=" + _refused + " stops=" + _stops; public void Init() { } public void Reset() { _still.Clear(); _partial.Clear(); Rel.ResetActionCounts(); } public void Tick(float dt) { if (Time.time < _next) { return; } _next = Time.time + 0.5f; if (!Rel.Gate("fix1-standstill", Options.FixStandstill.Value)) { return; } Rel.EmployeesInto(_employees); for (int i = 0; i < _employees.Count; i++) { Employee val = _employees[i]; try { Check(val); } catch (Exception e) { Core.Caught("fix1: check " + Rel.Name((NPC)(object)val), e); } } } private static void Check(Employee emp) { //IL_00bf: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: 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_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_0157: 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_013f: 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) //IL_0173: Unknown result type (might be due to invalid IL or missing references) //IL_0175: Unknown result type (might be due to invalid IL or missing references) //IL_027a: Unknown result type (might be due to invalid IL or missing references) //IL_027f: Unknown result type (might be due to invalid IL or missing references) //IL_0370: Unknown result type (might be due to invalid IL or missing references) //IL_039d: Unknown result type (might be due to invalid IL or missing references) //IL_03a2: Unknown result type (might be due to invalid IL or missing references) int instanceID = ((Object)emp).GetInstanceID(); if ((Object)(object)((NPC)emp).Behaviour == (Object)null) { Rel.MsgOnce("fix1:nobeh:" + instanceID, "[fix1-standstill] " + Rel.Name((NPC)(object)emp) + " has no behaviour component."); return; } if ((Object)(object)((NPC)emp).Movement == (Object)null) { Rel.MsgOnce("fix1:nomove:" + instanceID, "[fix1-standstill] " + Rel.Name((NPC)(object)emp) + " has no movement component."); return; } Behaviour activeBehaviour = ((NPC)emp).Behaviour.activeBehaviour; if ((Object)(object)activeBehaviour == (Object)null || !activeBehaviour.Active || !Rel.IsWork(activeBehaviour)) { _still.Remove(instanceID); return; } bool isMoving = ((NPC)emp).Movement.IsMoving; Vector3 currentDestination = ((NPC)emp).Movement.CurrentDestination; Vector3 position = ((Component)emp).transform.position; float num = Vector3.Distance(position, currentDestination); bool flag = ((NPC)emp).Movement._hasDestination && currentDestination != Vector3.zero && num > 1.5f; if (!isMoving && !flag) { _still.Remove(instanceID); return; } _checks++; if (!_still.TryGetValue(instanceID, out var value)) { _still[instanceID] = new Still { Spot = position, Since = Time.time }; } else if (Vector3.Distance(position, value.Spot) > Options.StandstillRadius.Value) { value.Spot = position; value.Since = Time.time; value.Tries = 0; value.Stopped = false; value.GaveUp = false; } else { if (value.GaveUp || Time.time - value.Since < Options.StandstillSeconds.Value) { return; } string text = Options.StandstillSeconds.Value.ToString("F0"); if (isMoving && !value.Stopped) { value.Stopped = true; ((NPC)emp).Movement.Stop(); _stops++; Rel.Action("fix1.stop", "[Fix1] stopped the walk of " + Rel.Name((NPC)(object)emp) + " (" + Rel.TypeName((Il2CppObjectBase)(object)activeBehaviour) + "): no progress for " + text + "s, " + num.ToString("F1") + " m from its destination."); value.Spot = ((Component)emp).transform.position; value.Since = Time.time; } else if (!flag || value.Tries >= Options.StandstillAttempts.Value) { value.GaveUp = true; _gaveUp++; Instance log = Core.Log; if (log != null) { log.Warning("[Fix1] gave up on " + Rel.Name((NPC)(object)emp) + " (" + Rel.TypeName((Il2CppObjectBase)(object)activeBehaviour) + "): still " + num.ToString("F1") + " m from its destination after " + (value.Stopped ? "a stopped walk and " : "") + value.Tries + " warp(s)" + (flag ? "; the target is probably off the walkable floor." : "; no live destination far enough away to warp to.")); } } else { value.Tries++; Warp(emp, currentDestination, "standstill " + text + "s, try " + value.Tries, dev: false); value.Spot = ((Component)emp).transform.position; value.Since = Time.time; } } } private unsafe static bool Warp(Employee emp, Vector3 dest, string why, bool dev) { //IL_0000: 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_007d: 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_00c4: 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_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00ef: 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_004f: Unknown result type (might be due to invalid IL or missing references) if (!Rel.TryOnNavMesh(dest, out var snapped)) { _refused++; Instance log = Core.Log; if (log != null) { string[] obj = new string[7] { "[Fix1] did not warp ", Rel.Name((NPC)(object)emp), " (", why, "): no NavMesh within 5 m of its destination ", null, null }; Vector3 val = dest; obj[5] = ((object)(*(Vector3*)(&val))/*cast due to .constrained prefix*/).ToString(); obj[6] = "."; log.Warning(string.Concat(obj)); } return false; } Vector3 position = ((Component)emp).transform.position; ((NPC)emp).Movement.Warp(snapped); _warps++; string text = "[Fix1] warped " + Rel.Name((NPC)(object)emp) + " to its destination (" + why + "), was " + Vector3.Distance(position, dest).ToString("F1") + " m away, now " + Vector3.Distance(((Component)emp).transform.position, dest).ToString("F1") + " m."; if (dev) { Instance log2 = Core.Log; if (log2 != null) { log2.Msg(text); } } else { Rel.Action("fix1.warp", text); } return true; } internal static void OnWalkResult(Behaviour b, WalkResult result, bool force) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Invalid comparison between Unknown and I4 //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Invalid comparison between Unknown and I4 //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0087: Unknown result type (might be due to invalid IL or missing references) //IL_0088: 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) if (!Rel.Gate("fix1-standstill:partial", Options.FixPartialWalkLoop.Value)) { return; } NPC npc = b.Npc; if ((Object)(object)npc == (Object)null) { Rel.MsgOnce("fix1:nonpc", "[fix1-standstill] a behaviour has no NPC, walk result ignored."); return; } int instanceID = ((Object)npc).GetInstanceID(); if ((int)result == 4) { _partial.Remove(instanceID); } else { if ((int)result != 3) { return; } Employee val = ((Il2CppObjectBase)npc).TryCast<Employee>(); if ((Object)(object)val == (Object)null) { return; } Vector3 currentDestination = npc.Movement.CurrentDestination; float num = Vector3.Distance(((Component)npc).transform.position, currentDestination); if (!force && (currentDestination == Vector3.zero || num <= 1.5f)) { _partial.Remove(instanceID); return; } _partial.TryGetValue(instanceID, out var value); value++; _partialsCounted++; int value2 = Options.PathFailuresBeforeWarp.Value; if (value >= value2 || force) { _partial.Remove(instanceID); if (Warp(val, currentDestination, "partial walk x" + value, force)) { _partialWarps++; } return; } _partial[instanceID] = value; int num2 = Math.Min(value, 4); if (b.consecutivePathingFailures < num2) { b.consecutivePathingFailures = num2; } } } } [HarmonyPatch(typeof(Behaviour), "WalkCallback")] [SourceNotice("ActuallyWorkingEmployees by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-ActuallyWorkingEmployees, not decompiled code.")] internal static class Awe_Fix1_WalkCallback_Patch { private static bool Prepare(MethodBase original) { if (!(original == null)) { return NativeMethods.AllowPatch(original); } return true; } private static void Postfix(Behaviour __instance, WalkResult result) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) if (!Rel.Enter("fix1.walkcallback")) { return; } try { Fix1Standstill.OnWalkResult(__instance, result, force: false); } catch (Exception e) { Core.Failed("fix1: WalkCallback postfix", e); } finally { Rel.Leave("fix1.walkcallback"); } } } [SourceNotice("ActuallyWorkingEmployees by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-ActuallyWorkingEmployees, not decompiled code.")] internal sealed class Fix2ChemistWork : IFeature { internal const string FeatureName = "fix2-chemist-work"; private static readonly Dictionary<int, float> _idleSince = new Dictionary<int, float>(); private static int _patched; private static int _resets; private float _next; private static readonly List<Employee> _employees = new List<Employee>(); private static readonly List<Behaviour> _stale = new List<Behaviour>(); public string Name => "fix2-chemist-work"; public string Status => "on=" + (Options.FixChemistWorkInProgress.Value ? 1 : 0) + " cauldronWork=" + _patched + " idleResets=" + _resets; public void Init() { } public void Reset() { _idleSince.Clear(); } internal static void Apply(Chemist chemist, ref bool result) { if (!result && Rel.Gate("fix2-chemist-work", Options.FixChemistWorkInProgress.Value)) { StartCauldronBehaviour startCauldronBehaviour = chemist.StartCauldronBehaviour; if (!((Object)(object)startCauldronBehaviour == (Object)null) && ((Behaviour)startCauldronBehaviour).Active) { result = true; _patched++; } } } public void Tick(float dt) { if (Time.time < _next) { return; } _next = Time.time + 1f; if (!Rel.Gate("fix2-chemist-work", Options.FixChemistWorkInProgress.Value)) { return; } Rel.EmployeesInto(_employees); for (int i = 0; i < _employees.Count; i++) { Employee val = _employees[i]; try { Chemist val2 = ((Il2CppObjectBase)val).TryCast<Chemist>(); if ((Object)(object)val2 != (Object)null) { CheckIdle(val2, force: false); } } catch (Exception e) { Core.Caught("fix2: idle check " + Rel.Name((NPC)(object)val), e); } } } private static List<Behaviour> EnabledWork(Chemist c) { List<Behaviour> stale = _stale; stale.Clear(); List<Behaviour> enabledBehaviours = ((NPC)c).Behaviour.enabledBehaviours; if (enabledBehaviours == null) { return stale; } for (int i = 0; i < enabledBehaviours.Count; i++) { Behaviour val = enabledBehaviours[i]; if ((Object)(object)val != (Object)null && Rel.IsWork(val)) { stale.Add(val); } } return stale; } private static void CheckIdle(Chemist c, bool force) { int instanceID = ((Object)c).GetInstanceID(); if ((Object)(object)((NPC)c).Behaviour == (Object)null) { Rel.MsgOnce("fix2:nobeh:" + instanceID, "[fix2-chemist-work] " + Rel.Name((NPC)(object)c) + " has no behaviour component."); return; } List<Behaviour> list = EnabledWork(c); if ((Object)(object)((NPC)c).Behaviour.activeBehaviour != (Object)null || list.Count == 0) { _idleSince.Remove(instanceID); return; } if (!_idleSince.TryGetValue(instanceID, out var value)) { _idleSince[instanceID] = Time.time; value = Time.time; } float num = Time.time - value; if ((!force && num < Options.ChemistIdleTimeout.Value) || !Rel.Enter("fix2.reset")) { return; } try { _idleSince.Remove(instanceID); string text = "[Fix2] reset " + Rel.Name((NPC)(object)c) + ": " + list.Count + " task(s) enabled but none running for " + num.ToString("F1") + " s (" + string.Join(", ", list.ConvertAll(Rel.TypeName)) + ")."; if (force) { Instance log = Core.Log; if (log != null) { log.Msg(text); } } else { Rel.Action("fix2.reset", text); } int count = list.Count; Behaviour[] array = (Behaviour[])(object)new Behaviour[count]; list.CopyTo(array); Fix3LockLeak.BeginBatch(); try { for (int i = 0; i < count; i++) { array[i].Disable_Networked((NetworkConnection)null); } } finally { Fix3LockLeak.EndBatch((Employee)(object)c, "idle reset of " + count + " task(s)"); } _resets++; } finally { Rel.Leave("fix2.reset"); } } } [HarmonyPatch(typeof(Chemist), "IsAnyWorkInProgress")] [SourceNotice("ActuallyWorkingEmployees by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-ActuallyWorkingEmployees, not decompiled code.")] internal static class Awe_Fix2_IsAnyWorkInProgress_Patch { private static bool Prepare(MethodBase original) { if (!(original == null)) { return NativeMethods.AllowPatch(original); } return true; } private static void Postfix(Chemist __instance, ref bool __result) { if (!Rel.Enter("fix2.isanywork")) { return; } try { Fix2ChemistWork.Apply(__instance, ref __result); } catch (Exception e) { Core.Failed("fix2: IsAnyWorkInProgress postfix", e); } finally { Rel.Leave("fix2.isanywork"); } } } [SourceNotice("ActuallyWorkingEmployees by DooDesch, MIT License. Please read and reuse the source code at https://github.com/DooDesch-Mods/ScheduleOne-ActuallyWorkingEmployees, not decompiled code.")] internal sealed class Fix3LockLeak : IFeature { internal const string FeatureName = "fix3-lock-leak"; internal const string SleepName = "fix3-sleep-release"; private static int _stationLocks; private static int _slotLocks; private static int _sweeps; private static int _sleepReleases; private static readonly List<Employee> _pending = new List<Employee>(); private float _nextPending; private static int _deferred; public string Name => "fix3-lock-leak"; public string Status => "on=" + (Options.FixLockLeak.Value ? 1 : 0) + (Options.SleepSafeRelease.Value ? 1 : 0) + " sweeps=" + _sweeps + " stationLocks=" + _stationLocks + " slotLocks=" + _slotLocks + " sleepReleases=" + _sleepReleases + " deferred=" + _deferred + " pending=" + _pending.Count; internal static bool Batching { get; private set; } public void Init() { } public void Reset() { _pending.Clear(); } public void Tick(float dt) { if (_pending.Count == 0 || Time.time < _nextPending) { return; } _nextPending = Time.time + 1f; try { ReleasePending(); } catch (Exception e) { Core.Failed("fix3: release deferred employees", e); } } private static List<BuildableItem> PlacedItems() { List<BuildableItem> items = new List<BuildableItem>(); HashSet<IntPtr> seen = new HashSet<IntPtr>(); List<Property> properties = Property.Properties; if (properties != null) { for (int i = 0; i < properties.Count; i++) { Property obj = properties[i]; From((obj != null) ? obj.BuildableItems : null); } } List<Business> businesses = Business.Businesses; if (businesses != null) { for (int j = 0; j < businesses.Count; j++) { Business obj2 = businesses[j]; From((obj2 != null) ? ((Property)obj2).BuildableItems : null); } } retu