Decompiled source of Moderation Improvements v1.1.1
BepInEx/plugins/ModerationImprovements.dll
Decompiled 3 weeks ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Text; using System.Text.Json; using System.Text.RegularExpressions; using System.Threading; using BepInEx; using BepInEx.Configuration; using BepInEx.Core.Logging.Interpolation; using BepInEx.Logging; using BepInEx.Unity.IL2CPP; using Epic.OnlineServices; using Epic.OnlineServices.Connect; using HarmonyLib; using HouseHouse.Dream; using Il2CppInterop.Runtime.Injection; using Il2CppInterop.Runtime.InteropTypes; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppSystem; using Il2CppSystem.Collections.Generic; using Il2CppSystem.Reflection; using Microsoft.CodeAnalysis; using Mirror; using Mirror.Authenticators; using PlayEveryWare.EpicOnlineServices; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("AdamMady")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.1.0.0")] [assembly: AssemblyInformationalVersion("1.1.0")] [assembly: AssemblyProduct("ModerationImprovements")] [assembly: AssemblyTitle("ModerationImprovements")] [assembly: AssemblyVersion("1.1.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 BigOrb { internal static class Chime { private static bool _broken; private static AudioSource _source; private static AudioClip _join; private static AudioClip _leave; private static readonly Queue<bool> _pending = new Queue<bool>(); private static float _nextPlay; private const int Rate = 44100; private const float NoteLen = 0.32f; private const float NoteFade = 0.03f; private const float NoteGap = 0.115f; private const float Spacing = 0.45f; private const int MaxQueued = 3; internal static bool Enabled { get { if (!_broken) { return Plugin.ChimeEnabled.Value; } return false; } } internal static void SetEnabled(bool on) { Plugin.ChimeEnabled.Value = on; if (on) { _broken = false; } } internal static void Play(bool join) { if (Enabled && _pending.Count < 3) { _pending.Enqueue(join); } } internal static void Step(GameObject holder) { if (_pending.Count == 0 || Time.unscaledTime < _nextPlay) { return; } _nextPlay = Time.unscaledTime + 0.45f; bool flag = _pending.Dequeue(); try { if (Ensure(holder)) { AudioClip val = (flag ? _join : _leave); if ((Object)(object)val != (Object)null) { _source.PlayOneShot(val, Mathf.Clamp01(Plugin.ChimeVolume.Value)); } } } catch (Exception ex) { Plugin.Logger.LogError((object)("chime: " + ex.Message)); _pending.Clear(); _broken = true; } } private static bool Ensure(GameObject holder) { if ((Object)(object)_source != (Object)null && (Object)(object)_join != (Object)null && (Object)(object)_leave != (Object)null) { return true; } if ((Object)(object)holder == (Object)null) { return false; } if ((Object)(object)_source == (Object)null) { _source = holder.GetComponent<AudioSource>(); if ((Object)(object)_source == (Object)null) { _source = holder.AddComponent<AudioSource>(); } _source.playOnAwake = false; _source.spatialBlend = 0f; _source.bypassEffects = true; _source.bypassListenerEffects = true; _source.bypassReverbZones = true; _source.ignoreListenerPause = true; _source.ignoreListenerVolume = true; } if (_join == null) { _join = Build("orbJoin", 659.25f, 880f); } if (_leave == null) { _leave = Build("orbLeave", 880f, 587.33f); } if ((Object)(object)_source != (Object)null && (Object)(object)_join != (Object)null) { return (Object)(object)_leave != (Object)null; } return false; } private static AudioClip Build(string name, float f1, float f2) { int num = 19183; Il2CppStructArray<float> val = new Il2CppStructArray<float>((long)num); for (int i = 0; i < num; i++) { float num2 = (float)i / 44100f; ((Il2CppArrayBase<float>)(object)val)[i] = Mathf.Clamp(0.5f * (Note(num2, f1) + Note(num2 - 0.115f, f2)), -1f, 1f); } AudioClip obj = AudioClip.Create(name, num, 1, 44100, false); obj.SetData(val, 0); return obj; } private static float Note(float t, float freq) { if (t < 0f || t > 0.32f) { return 0f; } float num = Mathf.Min(1f, t / 0.006f) * Mathf.Exp((0f - t) * 14f); if (t > 0.29f) { num *= (0.32f - t) / 0.03f; } return num * (Mathf.Sin((float)Math.PI * 2f * freq * t) + 0.35f * Mathf.Sin((float)Math.PI * 4f * freq * t)); } } internal static class Guard { [HarmonyPatch(typeof(HouseAuthenticator), "OnInitialAuthRequestMessage")] internal static class AuthPatch { private static void Postfix(NetworkConnectionToClient __0, InitialialAuthRequestMessage __1) { try { Patches.MarkFired("auth"); if (__0 == null) { return; } string text = null; string text2 = null; string text3 = null; try { text = __0.address; } catch { } try { text2 = ((__1 != null) ? __1.platformUserId : null); } catch { } try { text3 = ((__1 != null) ? __1.versionNumber : null); } catch { } OrbState.AuthSeen(((NetworkConnection)__0).connectionId, text, text2, text3); string name = OrbState.RosterName(text2) ?? text2 ?? "?"; OrbState.AddEvent("auth", text2, name, $"conn {((NetworkConnection)__0).connectionId} addr {text ?? "?"} claims {text2 ?? "?"} v{text3 ?? "?"}"); if (OrbState.IsBannedAddress(text)) { OrbState.AddEvent("autokick", text2, name, "banned address " + text + " tried to join as " + text2); ((NetworkConnection)__0).Disconnect(); } else if (OrbState.IsBanned(text2)) { OrbState.AddEvent("autokick", text2, name, "banned identifier tried to join from " + (text ?? "?")); OrbState.BanAttachAddress(text2, text); ((NetworkConnection)__0).Disconnect(); } else if (!OrbState.LobbyAllows(text2, text)) { OrbState.AddEvent("lockreject", text2, name, "lobby locked; connection was not present when locked"); ((NetworkConnection)__0).Disconnect(); } else { if (string.IsNullOrEmpty(text) || string.IsNullOrEmpty(text2) || text == "localhost") { return; } if (IsSteam(text) && IsSteam(text2) && text != text2) { Act("idspoof", text2, name, "claims SteamID " + text2 + " but the connection is " + text, text, __0); return; } string text4 = Base(text2); lock (IdsByAddress) { if (!IdsByAddress.TryGetValue(text, out var value)) { value = (IdsByAddress[text] = new HashSet<string>()); } if (text4 != text) { value.Add(text4); } if (value.Count >= 2) { string detail = $"address {text} has claimed {value.Count} identifiers: {string.Join(", ", value)}"; if (value.Count >= 3) { Act("idrotate", text2, name, detail, text, __0); return; } OrbState.AddAlert("idrotate", text2, name, detail); } } Eos.Lookup(text, text2); } } catch (Exception ex) { Plugin.Logger.LogError((object)("auth gate: " + ex.Message)); } } } internal static class Eos { private struct QueryOptions { public int ApiVersion; public IntPtr LocalUserId; public IntPtr AccountIdTypeDeprecated; public IntPtr ProductUserIds; public uint Count; } private struct QueryInfo { public int ResultCode; public IntPtr ClientData; public IntPtr LocalUserId; } private struct CountOptions { public int ApiVersion; public IntPtr TargetUserId; } private struct CopyOptions { public int ApiVersion; public IntPtr TargetUserId; public uint Index; } private struct AccountInfo { public int ApiVersion; public IntPtr ProductUserId; public IntPtr DisplayName; public IntPtr AccountId; public int AccountIdType; public long LastLoginTime; } [UnmanagedFunctionPointer(CallingConvention.Cdecl)] private delegate void OnQuery(IntPtr info); internal class Mapping { public string Type; public string AccountId; public string DisplayName; public string When; public List<string> All = new List<string>(); } private const string Dll = "EOSSDK-Win64-Shipping"; private static OnQuery _cb; private static readonly string[] Types = new string[15] { "epic", "steam", "psn", "xbl", "discord", "gog", "nintendo", "uplay", "openid", "apple", "google", "oculus", "itchio", "amazon", "viveport" }; internal static readonly Dictionary<string, Mapping> Map = new Dictionary<string, Mapping>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<string, string> ClaimedBy = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); private static readonly HashSet<string> Pending = new HashSet<string>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary<long, string> Inflight = new Dictionary<long, string>(); private static long _seq; private static string File => Path.Combine(OrbState.DataDir, "eosmap.tsv"); [DllImport("EOSSDK-Win64-Shipping", CallingConvention = CallingConvention.Cdecl)] private static extern IntPtr EOS_ProductUserId_FromString(string s); [DllImport("EOSSDK-Win64-Shipping", CallingConvention = CallingConvention.Cdecl)] private static extern void EOS_Connect_QueryProductUserIdMappings(IntPtr h, ref QueryOptions o, IntPtr clientData, OnQuery cb); [DllImport("EOSSDK-Win64-Shipping", CallingConvention = CallingConvention.Cdecl)] private static extern uint EOS_Connect_GetProductUserExternalAccountCount(IntPtr h, ref CountOptions o); [DllImport("EOSSDK-Win64-Shipping", CallingConvention = CallingConvention.Cdecl)] private static extern int EOS_Connect_CopyProductUserExternalAccountByIndex(IntPtr h, ref CopyOptions o, out IntPtr info); [DllImport("EOSSDK-Win64-Shipping", CallingConvention = CallingConvention.Cdecl)] private static extern void EOS_Connect_ExternalAccountInfo_Release(IntPtr info); internal static void Load() { try { if (!System.IO.File.Exists(File)) { return; } string[] array = System.IO.File.ReadAllLines(File); for (int i = 0; i < array.Length; i++) { string[] array2 = array[i].Split('\t'); if (array2.Length >= 5) { Mapping mapping = new Mapping { Type = array2[1], AccountId = array2[2], DisplayName = array2[3], When = array2[4] }; if (array2.Length > 5) { mapping.All.AddRange(array2[5].Split('|')); } Map[array2[0]] = mapping; } } } catch { } } internal static void Lookup(string addr, string claimed) { if (!IsEosAddr(addr)) { return; } ClaimedBy[addr] = claimed; if (Map.TryGetValue(addr, out var value)) { Compare(addr, value, claimed); } else { if (Pending.Contains(addr)) { return; } try { EOSSingleton instance = EOSManager.Instance; ConnectInterface val = ((instance != null) ? instance.GetEOSConnectInterface() : null); ProductUserId val2 = ((instance != null) ? instance.GetProductUserId() : null); if ((Handle)(object)val == (Handle)null || (Handle)(object)val2 == (Handle)null) { return; } IntPtr intPtr = EOS_ProductUserId_FromString(addr); if (!(intPtr == IntPtr.Zero)) { IntPtr intPtr2 = Marshal.AllocHGlobal(IntPtr.Size); Marshal.WriteIntPtr(intPtr2, intPtr); long num = ++_seq; lock (Inflight) { Inflight[num] = addr; } if (_cb == null) { _cb = OnQueried; } QueryOptions o = new QueryOptions { ApiVersion = 2, LocalUserId = ((Handle)val2).InnerHandle, ProductUserIds = intPtr2, Count = 1u }; Pending.Add(addr); EOS_Connect_QueryProductUserIdMappings(((Handle)val).InnerHandle, ref o, (IntPtr)num, _cb); } } catch (Exception ex) { Plugin.Logger.LogError((object)("eos lookup: " + ex.Message)); Pending.Remove(addr); } } } private static void OnQueried(IntPtr infoPtr) { try { QueryInfo queryInfo = Marshal.PtrToStructure<QueryInfo>(infoPtr); string value; lock (Inflight) { Inflight.TryGetValue((long)queryInfo.ClientData, out value); Inflight.Remove((long)queryInfo.ClientData); } if (value != null) { Pending.Remove(value); if (queryInfo.ResultCode != 0) { OrbState.AddEvent("eos", null, "host", $"lookup {value} → result {queryInfo.ResultCode}"); } else { Read(value); } } } catch (Exception ex) { Plugin.Logger.LogError((object)("eos callback: " + ex.Message)); } } private static void Read(string addr) { EOSSingleton instance = EOSManager.Instance; ConnectInterface val = ((instance != null) ? instance.GetEOSConnectInterface() : null); if ((Handle)(object)val == (Handle)null) { return; } IntPtr targetUserId = EOS_ProductUserId_FromString(addr); CountOptions o = new CountOptions { ApiVersion = 1, TargetUserId = targetUserId }; uint num = EOS_Connect_GetProductUserExternalAccountCount(((Handle)val).InnerHandle, ref o); Mapping mapping = new Mapping { When = OrbState.Now() }; for (uint num2 = 0u; num2 < num; num2++) { CopyOptions o2 = new CopyOptions { ApiVersion = 1, TargetUserId = targetUserId, Index = num2 }; if (EOS_Connect_CopyProductUserExternalAccountByIndex(((Handle)val).InnerHandle, ref o2, out var info) != 0 || info == IntPtr.Zero) { continue; } try { AccountInfo accountInfo = Marshal.PtrToStructure<AccountInfo>(info); string text = ((accountInfo.AccountIdType >= 0 && accountInfo.AccountIdType < Types.Length) ? Types[accountInfo.AccountIdType] : ("type" + accountInfo.AccountIdType)); string text2 = ((accountInfo.AccountId != IntPtr.Zero) ? Marshal.PtrToStringUTF8(accountInfo.AccountId) : ""); string text3 = ((accountInfo.DisplayName != IntPtr.Zero) ? Marshal.PtrToStringUTF8(accountInfo.DisplayName) : ""); mapping.All.Add($"{text}:{text2}:{text3}"); if (mapping.Type == null || (mapping.Type == "epic" && text != "epic")) { mapping.Type = text; mapping.AccountId = text2; mapping.DisplayName = text3; } } finally { EOS_Connect_ExternalAccountInfo_Release(info); } } ClaimedBy.TryGetValue(addr, out var value); if (num == 0) { mapping.Type = "none"; } Map[addr] = mapping; Save(addr, mapping); OrbState.AddEvent("eos", value, OrbState.RosterName(value) ?? value ?? "?", (num == 0) ? (addr + " has no linked account") : $"{addr} → {mapping.Type} {mapping.AccountId} \"{mapping.DisplayName}\""); if (num == 0) { string name = OrbState.RosterName(value) ?? value ?? "?"; string text4 = addr + " has no external account on Epic: anonymous device-id login, not a real Steam/PSN/Xbox client (claimed " + (value ?? "?") + ")"; OrbState.AddAlert("eosanon", value, name, text4); if (Plugin.GuardBanAnonymous.Value && AutoBan) { BanAddress(addr, name, "eosanon: " + text4, value); } } else { Compare(addr, mapping, value); } } private static void Compare(string addr, Mapping m, string claimed) { if (string.IsNullOrEmpty(claimed) || m == null || m.Type == "none") { return; } string text = Base(claimed); bool flag = false; bool flag2 = false; foreach (string item in m.All) { string[] array = item.Split(':'); if (array.Length > 1 && array[1].Length > 0) { flag = true; if (array[1] == text) { flag2 = true; } } } if (!(!flag || flag2)) { string name = OrbState.RosterName(claimed) ?? claimed; string text2 = $"Epic says {addr} is {m.Type} account {m.AccountId} (\"{m.DisplayName}\") but the client claimed {claimed}"; OrbState.AddAlert("eosspoof", claimed, name, text2); if (AutoBan) { BanAddress(addr, name, "eosspoof: " + text2, claimed); } } } private static void Save(string addr, Mapping m) { try { List<string> list = new List<string>(); if (System.IO.File.Exists(File)) { list.AddRange(System.IO.File.ReadAllLines(File)); } list.RemoveAll((string l) => l.StartsWith(addr + "\t", StringComparison.OrdinalIgnoreCase)); list.Add($"{addr}\t{m.Type}\t{m.AccountId}\t{(m.DisplayName ?? "").Replace('\t', ' ')}\t{m.When}\t{string.Join("|", m.All).Replace('\t', ' ')}"); System.IO.File.WriteAllLines(File, list); } catch { } } internal static string Json() { StringBuilder stringBuilder = new StringBuilder("{"); foreach (KeyValuePair<string, Mapping> item in Map) { if (stringBuilder.Length > 1) { stringBuilder.Append(','); } stringBuilder.Append(OrbState.J(item.Key)).Append(":{\"type\":").Append(OrbState.J(item.Value.Type)) .Append(",\"accountId\":") .Append(OrbState.J(item.Value.AccountId)) .Append(",\"displayName\":") .Append(OrbState.J(item.Value.DisplayName)) .Append(",\"when\":") .Append(OrbState.J(item.Value.When)) .Append('}'); } return stringBuilder.Append('}').ToString(); } } internal static class Voice { private sealed class Bucket { public int Count; public int LastRate; public int Dropped; public float Window; public float OverSince = -1f; public float LastAlert = -999f; } private static readonly Dictionary<int, Bucket> B = new Dictionary<int, Bucket>(); private const float BanAfter = 5f; internal static void Patch(Harmony harmony) { //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Expected O, but got Unknown MethodInfo methodInfo = null; try { Type[] types = typeof(PlayerNetworking).Assembly.GetTypes(); foreach (Type type in types) { if (type.Name == "MirrorIgnoranceServer") { methodInfo = type.GetMethod("OnMessageReceived", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); break; } } } catch (Exception ex) { Plugin.Logger.LogWarning((object)("voice relay type scan: " + ex.Message)); } if (methodInfo == null) { Plugin.Logger.LogWarning((object)"voice relay hook not found, voice limiter off"); return; } try { harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(typeof(Voice).GetMethod("Prefix", BindingFlags.Static | BindingFlags.Public)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); } catch (Exception ex2) { Plugin.Logger.LogWarning((object)("voice relay patch: " + ex2.Message)); } } public static bool Prefix(object __0) { int value = Plugin.GuardVoiceLimit.Value; if (value <= 0) { return true; } try { NetworkConnection val = (NetworkConnection)((__0 is NetworkConnection) ? __0 : null); if (val == null) { return true; } int connectionId = val.connectionId; if (connectionId == 0) { return true; } float unscaledTime = Time.unscaledTime; lock (B) { if (!B.TryGetValue(connectionId, out var value2)) { Dictionary<int, Bucket> b = B; Bucket obj = new Bucket { Window = unscaledTime }; value2 = obj; b[connectionId] = obj; } if (unscaledTime - value2.Window >= 1f) { value2.LastRate = value2.Count; value2.Count = 0; value2.Window = unscaledTime; } value2.Count++; if (value2.Count > value) { value2.Dropped++; return false; } } } catch { } return true; } internal static void Tick() { int value = Plugin.GuardVoiceLimit.Value; if (value <= 0) { return; } float unscaledTime = Time.unscaledTime; List<(int, Bucket)> list = null; lock (B) { foreach (KeyValuePair<int, Bucket> item in B) { Bucket value2 = item.Value; if (value2.Count > value || (unscaledTime - value2.Window < 1f && value2.LastRate > value)) { if (value2.OverSince < 0f) { value2.OverSince = unscaledTime; } (list ?? (list = new List<(int, Bucket)>())).Add((item.Key, value2)); } else { value2.OverSince = -1f; } } foreach (int item2 in B.Keys.Where((int c) => !NetworkServer.connections.ContainsKey(c)).ToList()) { B.Remove(item2); } } if (list == null) { return; } NetworkConnectionToClient val = default(NetworkConnectionToClient); foreach (var (num, bucket) in list) { if (!NetworkServer.connections.TryGetValue(num, ref val) || val == null) { continue; } string text = OrbState.AuthFor(num)?.ClaimedId; string name = OrbState.RosterName(text) ?? text ?? $"conn {num}"; if (unscaledTime - bucket.LastAlert > 30f) { bucket.LastAlert = unscaledTime; OrbState.AddAlert("voiceflood", text, name, $"{Math.Max(bucket.Count, bucket.LastRate)} voice packets/s (limit {value}), {bucket.Dropped} dropped"); } if (unscaledTime - bucket.OverSince >= 5f) { string address = null; try { address = val.address; } catch { } if (AutoBan) { BanAddress(address, name, $"voiceflood: {5f:0}s sustained over {value} packets/s", text); } try { ((NetworkConnection)val).Disconnect(); } catch { } bucket.OverSince = -1f; } } } } [HarmonyPatch(typeof(PlayerNetworking), "InvokeUserCode_CmdSendTextChatMessage__String")] internal static class FakeChatPatch { private static bool Prefix(NetworkBehaviour __0, NetworkReader __1) { if (!Plugin.GuardChatFilter.Value) { return true; } try { PlayerNetworking val = ((Il2CppObjectBase)__0).TryCast<PlayerNetworking>(); if ((Object)(object)val == (Object)null || ((NetworkBehaviour)val).isLocalPlayer) { return true; } int position = __1.Position; string text = null; try { text = NetworkReaderExtensions.ReadString(__1); } catch { } __1.Position = position; if (string.IsNullOrEmpty(text) || !FakeSystem.IsMatch(text)) { return true; } Patches.MarkFired("chat/fake"); OrbState.AddAlert("fakesys", Patches.Pid(val), Patches.Display(val), "chat styled as a system message, dropped: \"" + text + "\""); return false; } catch { return true; } } } private const float KickGrace = 1f; private static readonly Dictionary<int, float> PendingDisconnect = new Dictionary<int, float>(); private static readonly Dictionary<string, HashSet<string>> IdsByAddress = new Dictionary<string, HashSet<string>>(); private static readonly Dictionary<uint, string> Judged = new Dictionary<uint, string>(); private static readonly Dictionary<string, int> Refused = new Dictionary<string, int>(); private static readonly Regex FakeSystem = new Regex("^\\s*(?:\\[?(?:server|system|host|admin)\\]?\\s*[:\\-]|(?:the\\s+)?(?:host|server|admin|you|player|[\\w\\-]+)\\s+(?:has|have)\\s+been\\s+(?:removed|kicked|banned|disconnected)|[\\w\\-]+\\s+(?:has|have)\\s+(?:joined|left)\\s+the\\s+(?:game|lobby|server)|(?:you\\s+(?:have\\s+been|were)\\s+)?(?:kicked|banned)\\s+(?:by|from)\\s+(?:the\\s+)?(?:host|server)|(?:connection|host)\\s+(?:lost|timed\\s+out)|lost\\s+connection\\s+to\\s+(?:the\\s+)?host)", RegexOptions.IgnoreCase | RegexOptions.Compiled); internal static bool AutoBan => Plugin.GuardAutoBan.Value; internal static void Kick(PlayerNetworking pn, string reason = null) { if ((Object)(object)pn == (Object)null) { return; } NetworkConnectionToClient val = null; try { val = ((NetworkBehaviour)pn).connectionToClient; } catch { } if (val == null) { return; } lock (PendingDisconnect) { if (PendingDisconnect.ContainsKey(((NetworkConnection)val).connectionId)) { return; } PendingDisconnect[((NetworkConnection)val).connectionId] = Time.unscaledTime + 1f; } try { PeckEffectTextInput.ClearTextWrittenByAuthor(pn.identifier); } catch { } try { pn.RPCKickUser(val); } catch { } } internal static void StepKicks() { List<int> list = null; lock (PendingDisconnect) { foreach (KeyValuePair<int, float> item in PendingDisconnect) { if (Time.unscaledTime >= item.Value) { (list ?? (list = new List<int>())).Add(item.Key); } } } if (list == null) { return; } NetworkConnectionToClient val = default(NetworkConnectionToClient); foreach (int item2 in list) { lock (PendingDisconnect) { PendingDisconnect.Remove(item2); } try { if (NetworkServer.connections.TryGetValue(item2, ref val) && val != null) { ((NetworkConnection)val).Disconnect(); } } catch { } } } internal static void BanAddress(string address, string name, string why, string id = null) { if (string.IsNullOrEmpty(address) || address == "localhost") { return; } if (!OrbState.IsBannedAddress(address)) { OrbState.BanAdd(id, name, 0uL, address); } OrbState.AddEvent("autoban", id, name, why); Enumerator<int, NetworkConnectionToClient> enumerator = NetworkServer.connections.GetEnumerator(); while (enumerator.MoveNext()) { NetworkConnectionToClient value = enumerator.Current.Value; if (value == null || ((NetworkConnection)value).connectionId == 0) { continue; } string text = null; try { text = value.address; } catch { } if (text == address) { try { ((NetworkConnection)value).Disconnect(); } catch { } } } } private static void Act(string kind, string id, string name, string detail, string address, NetworkConnectionToClient conn) { OrbState.AddAlert(kind, id, name, detail); if (AutoBan) { BanAddress(address, name ?? id ?? "?", kind + ": " + detail, id); return; } try { if (conn != null) { ((NetworkConnection)conn).Disconnect(); } } catch { } } private static string Base(string id) { if (id != null) { return Regex.Replace(id, "-\\d+$", ""); } return null; } private static bool IsSteam(string id) { if (id != null && id.Length == 17 && id.StartsWith("7656119")) { return id.All(char.IsDigit); } return false; } private static bool IsNumeric(string id) { if (!string.IsNullOrEmpty(id) && id.Length >= 15) { return id.All(char.IsDigit); } return false; } private static bool IsEosAddr(string a) { if (a != null && a.Length == 32) { return a.All(Uri.IsHexDigit); } return false; } internal static void Tick() { StepKicks(); Voice.Tick(); foreach (PlayerCharacter item in OrbBehaviour.Players()) { try { PlayerNetworking playerNetworking = item.playerNetworking; if ((Object)(object)playerNetworking == (Object)null || ((NetworkBehaviour)playerNetworking).isLocalPlayer) { continue; } string text = null; string text2 = null; string text3 = null; ulong num = 0uL; try { text = Patches.Pid(playerNetworking); } catch { } try { NetworkConnectionToClient connectionToClient = ((NetworkBehaviour)playerNetworking).connectionToClient; text2 = ((connectionToClient != null) ? connectionToClient.address : null); } catch { } try { text3 = playerNetworking.epicUserId; } catch { } try { num = playerNetworking.userPlatformId; } catch { } if (OrbState.IsUnsetIdentifier(text) || text2 == null) { continue; } string text4 = text3 + "|" + num; if (Judged.TryGetValue(((NetworkBehaviour)playerNetworking).netId, out var value) && value == text4) { continue; } Judged[((NetworkBehaviour)playerNetworking).netId] = text4; string name = Patches.Display(playerNetworking); if (IsEosAddr(text2) && !string.IsNullOrEmpty(text3) && !string.Equals(text3, text2, StringComparison.OrdinalIgnoreCase)) { Act("epicspoof", text, name, "connection is " + text2 + " but the client reports epic id " + text3, text2, ((NetworkBehaviour)playerNetworking).connectionToClient); } else { if (num == 0L || !IsNumeric(Base(text)) || !(num.ToString() != Base(text))) { continue; } string text5 = null; foreach (PlayerCharacter item2 in OrbBehaviour.Players()) { PlayerNetworking playerNetworking2 = item2.playerNetworking; if ((Object)(object)playerNetworking2 != (Object)null && ((Il2CppObjectBase)playerNetworking2).Pointer != ((Il2CppObjectBase)playerNetworking).Pointer && Base(Patches.Pid(playerNetworking2)) == num.ToString()) { text5 = Patches.Display(playerNetworking2); break; } } Act("platspoof", text, name, $"claims identifier {text} but platform id is {num}" + ((text5 != null) ? (" (" + text5 + "'s, who is connected)") : ""), text2, ((NetworkBehaviour)playerNetworking).connectionToClient); continue; } } catch { } } if (Judged.Count <= 64) { return; } HashSet<uint> live = new HashSet<uint>(); foreach (PlayerCharacter item3 in OrbBehaviour.Players()) { try { live.Add(((NetworkBehaviour)item3.playerNetworking).netId); } catch { } } foreach (uint item4 in Judged.Keys.Where((uint k) => !live.Contains(k)).ToList()) { Judged.Remove(item4); } } } public class Finale : IOrbScript { private ScriptHost _h; private int _tick; private readonly List<TrackedPeckState> _held = new List<TrackedPeckState>(); private float _releaseAt; private readonly List<(TrackedPeckState st, float at)> _bounces = new List<(TrackedPeckState, float)>(); private static Finale _instance; private readonly List<(string[] roots, float at)> _rechecks = new List<(string[], float)>(); private static readonly (string key, string label, string[] roots, (string suffix, string label)[] probe, string note, string anchor, float radius, string[] bounce, string[] props)[] Stages = new(string, string, string[], (string, string)[], string, string, float, string[], string[])[10] { ("dreamdoor", "Dream door (N-hold → vision → interior door)", new string[4] { "BlackTower/Positioner/NHoldFullSet", "BlackTower/Positioner/DreamDoorLogic", "BlackTower/BlackTowerDream", "BlackTower/Positioner/BasicDoor DreamBlocker" }, new(string, string)[4] { ("/DreamDoorLogic", "door"), ("/DreamStateSystem", "dream"), ("/NHoldSucess", "success"), ("DreamBlocker/BasicDoorPeckLogic", "blocker") }, "hold all N buttons (N = player count) at once; one presser gets the vision, door unlocks", null, 0f, null, null), ("bell", "Bell room (4 holds → bell crushed → exit gate)", new string[2] { "EndingGate/GatePositioner/SecondGate", "EndingGate/GatePositioner/EndingGate_Door - Exit" }, new(string, string)[3] { ("SpeechlessField/SpeechlessFieldLogic", "field"), ("EndingGate_Door - Exit/AnimatingPartsEndingGate/GateOpeningLogic", "exit"), ("NHoldPlinth0/ButtonNHoldFour/ButtonSystem", "hold0") }, "field 1 = bell up, 2 = crushing, 0 = crushed; cheat: nhold key=EndingGate/GatePositioner/SecondGate/NHolds", null, 0f, null, null), ("gauntlet0", "Gauntlet 0 — peg board (9 tiles)", new string[1] { "Positioner/Level0/" }, new(string, string)[3] { ("Level0/GauntletChamber00PegBoard/GauntletHallway/GauntletChamberNetworkObjects/ChallengeCompletedSystem", "complete"), ("Level0/GourdTower_Stairs/GourdTower_Gate Challenge Complete/GourdTowerGateNetworking", "gate"), ("Level0/GourdTower_Stairs/GourdTower_Gate All Hold/GourdTowerGateNetworking", "holdgate") }, "9 tiles back on the rack (by guid — wherever they were carried), gates closed, completion cleared, room doors bounced to re-scramble", null, 0f, new string[3] { "Level0/GauntletChamber00PegBoard/Room Always/BasicDoor Always/BasicDoorPeckLogic", "Room Teller1/BasicDoor Teller1/BasicDoorPeckLogic", "Room Teller2/BasicDoor Teller2/BasicDoorPeckLogic" }, new string[9] { "fc58b531-2bfa-46ca-9ecf-fb53bc5cf37f", "a2d05ea0-6cbd-433a-84af-a194a05d6536", "5eec232c-c20f-432f-95dc-1c8d6b32e159", "1690f6b6-2f55-404f-b578-dd7bf7ca3d47", "987b4ef6-7b9d-483f-9eba-4bcf1e0643f0", "556ba962-5281-47ac-9ecf-b7ba73b4ef15", "dceb4dd3-5f79-423c-b83c-39cca9e1bae2", "c02dde13-6886-411c-9b8c-b558473b1316", "8bde95f3-e638-4ddf-95c8-e30613cd9e42" }), Gauntlet(1, "GauntletChamber01Pointers", "Gauntlet 1 — pointers (press in order)", "tracker + lights to baseline, doors bounced to re-arm", null, 0f, new string[3] { "Level1/GauntletChamber01Pointers/DoorGroup/BasicDoor/BasicDoorPeckLogic", "DoorGroup/BasicDoor 3PlayerAndUp/BasicDoorPeckLogic", "DoorGroup/BasicDoor4Player/BasicDoorPeckLogic" }), Gauntlet(2, "GauntletKick02ToKick", "Gauntlet 2 — kick to kick (pomodoro → pedestal)", "receptacle/doors/turnstiles to baseline, pomodoro back to the dispenser (pose reset 40m)", "Level2/GauntletKick02ToKick/BasicReceptical", 40f, null), Gauntlet(3, "GauntletChamber03SimPress", "Gauntlet 3 — sim-press (4 switches)", "switches self-reset; completion + gates to baseline", null, 0f, null), Gauntlet(4, "GauntletChamberVolleyBall", "Gauntlet 4 — volleyball (press in order, red kettle)", "tracker + lights to baseline, kettle back by guid, doors bounced", null, 0f, new string[4] { "Level4/GauntletChamberVolleyBall/Doors/BasicDoor 1 (primary)/BasicDoorPeckLogic", "Doors/BasicDoor 2/BasicDoorPeckLogic", "Doors/BasicDoor 3/BasicDoorPeckLogic", "Doors/BasicDoor 4/BasicDoorPeckLogic" }, new string[1] { "33494b4b-6d5e-4ac4-bcbb-fb43ddeb6bf2" }), Gauntlet(5, "GauntletChamberInvisi", "Gauntlet 5 — invisible ink (peg board, telescope)", "9 tiles back on the rack (by guid), screen/completion/gates cleared, doors bounced to re-scramble", null, 0f, new string[4] { "Level5/GauntletChamberInvisi/Door1/BasicDoor/BasicDoorPeckLogic", "Level5/GauntletChamberInvisi/Door2/BasicDoor/BasicDoorPeckLogic", "3PlayerAndUp/Door3/", "4PlayerOnly/Door4/" }, new string[9] { "3016c28d-21d9-40fd-9875-2d3470c19efe", "abbcb967-6c81-4cae-b246-c809f364fbe0", "a3088495-97ef-4a27-bedf-98f3bedb606a", "cee0452e-f813-46b2-adbc-b2580379555c", "c7aa5a25-c784-455d-aadd-ad3b89a6b208", "e007cc19-9f5b-4d45-af25-1d722c50b74d", "7211c43f-4efd-4c8b-ae62-01fbd8a62308", "90dc6ef0-92e4-4637-821c-307a9fc97a64", "1207f056-b89d-4280-9ebf-a848b95db57e" }), Gauntlet(6, "GauntletChamber06Sculptures", "Gauntlet 6 — sculptures (press in order)", "tracker + lights to baseline, doors bounced to re-arm", null, 0f, new string[4] { "Level6/GauntletChamber06Sculptures/Doors/BasicDoor 1 (primary)/", "Doors/BasicDoor 2 /", "Doors/BasicDoor 3/", "Doors/BasicDoor 4/" }), ("gauntlet7", "Gauntlet 7 — final bell (4-hold → skylight)", new string[2] { "Positioner/Level7/", "CreditsTheatre/Positioner/CreditsScreen" }, new(string, string)[3] { ("Level7/SpeechlessField/SpeechlessFieldLogic", "field"), ("Level7/GauntletEndingLogicGroup/SkylightDoorSystem", "skylight"), ("Level7/GauntletEndingLogicGroup/GauntletIsCompletedSystem", "complete") }, "field 1 = bell up; reset puts the bell back, closes the skylight, clears GauntletComplete; cheat: nhold key=Level7/NHoldAnchor", null, 0f, null, null) }; private static float _suppressUntil; private static bool _patched; private readonly Dictionary<IntPtr, string> _trapped = new Dictionary<IntPtr, string>(); private float _nextTrapCheck; private readonly List<(TrackedPeckState st, string key)> _probeIdx = new List<(TrackedPeckState, string)>(); private float _probeIdxAt = -999f; public string Name => "finale"; public static void BounceEndingGates() { _instance?.BounceGates(); } public static string DreamStatic(PlayerCharacter pc) { if (_instance == null) { return "finale script not loaded"; } return _instance.GiveDream(pc); } public static void WakeStatic(PlayerCharacter pc) { DreamController item = Controller().dc; if ((Object)(object)item == (Object)null || (Object)(object)pc == (Object)null) { return; } try { pc.playerNetworking.ServerSetDream(item, false); } catch { } try { if ((Object)(object)item.dreamPlayer == (Object)(object)pc) { item.dreamPlayer = null; } } catch { } } private void BounceGates() { int num = 0; foreach (TrackedPeckState item in Object.FindObjectsOfType<TrackedPeckState>(true)) { if ((Object)(object)item == (Object)null) { continue; } string text; try { text = Api.TransformPath(((Component)item).transform); } catch { continue; } if (text.IndexOf("EndingGate/GatePositioner/EndingGate_Door", StringComparison.OrdinalIgnoreCase) >= 0 && text.EndsWith("/GateOpeningLogic")) { try { item.SetState(1); _bounces.Add((item, Time.unscaledTime + 3f)); num++; } catch { } } } if (num > 0) { _h?.Event("finalereset", $"ending gates: {num} bounced open→close (3s)"); } } private static (string, string, string[], (string, string)[], string, string, float, string[], string[]) Gauntlet(int n, string chamber, string label, string note, string anchor, float radius, string[] bounce, string[] props = null) { return ("gauntlet" + n, label, new string[1] { "Positioner/Level" + n + "/" }, new(string, string)[3] { ("Level" + n + "/" + chamber + "/GauntletHallway/GauntletChamberNetworkObjects/ChallengeCompletedSystem", "complete"), ("Level" + n + "/GourdTower_Stairs/GourdTower_Gate Challenge Complete/GourdTowerGateNetworking", "gate"), ("Level" + n + "/GourdTower_Stairs/GourdTower_Gate All Hold/GourdTowerGateNetworking", "holdgate") }, note, anchor, radius, bounce, props); } public void Load(ScriptHost host) { //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Expected O, but got Unknown _h = host; _instance = this; host.Command("finalereset", delegate(string id, string key, int val, string text) { //IL_007f: 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_0086: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: 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_029f: Unknown result type (might be due to invalid IL or missing references) int num = 0; if (string.Equals(key, "all", StringComparison.OrdinalIgnoreCase)) { try { HubReset.ResetStatic("BlackTower/Positioner/GourdPlinth X6"); } catch (Exception ex2) { host.Warn("monument: " + ex2.Message); } } if (key.StartsWith("gauntlet", StringComparison.OrdinalIgnoreCase) || string.Equals(key, "all", StringComparison.OrdinalIgnoreCase)) { List<string> list = new List<string>(); foreach (PlayerCharacter item2 in Api.Players()) { try { Vector3 position = ((Component)item2).transform.position; if (position.x > -800f && position.x < -680f && position.z > 640f && position.z < 740f && position.y > 40f) { list.Add($"{Api.Display(item2.playerNetworking)} (y {position.y:0})"); } } catch { } } if (list.Count > 0) { host.Event("finalereset", "⚠ players inside the orb above the entrance — the tower will re-open the gates below them: " + string.Join(", ", list)); } } (string, string, string[], (string, string)[], string, string, float, string[], string[])[] stages = Stages; for (int i = 0; i < stages.Length; i++) { (string, string, string[], (string, string)[], string, string, float, string[], string[]) tuple = stages[i]; if (string.Equals(key, "all", StringComparison.OrdinalIgnoreCase) || string.Equals(key, tuple.Item1, StringComparison.OrdinalIgnoreCase)) { List<string> list2 = new List<string>(); if (tuple.Item1 == "dreamdoor") { Release(); if (_trapped.Count > 0) { list2.Add("traps dropped: " + string.Join(", ", _trapped.Values)); _trapped.Clear(); } StopDream(list2); } if (tuple.Item1 == "bell") { BounceGates(); } if (!string.IsNullOrEmpty(tuple.Item6) && tuple.Item7 > 0f) { Vector3? val2 = Anchor(tuple.Item6); if (!val2.HasValue) { list2.Add("anchor not found: " + tuple.Item6); } else { try { (int, int, int) tuple2 = Api.ResetPropsAround(val2.Value, tuple.Item7); list2.Add($"{tuple2.Item1}/{tuple2.Item2} prop(s) re-posed{((tuple2.Item3 > 0) ? $", {tuple2.Item3} held" : "")}"); } catch (Exception ex3) { list2.Add("props: " + ex3.Message); } } } if (tuple.Rest.Item2 != null) { string[] item = tuple.Rest.Item2; foreach (string text2 in item) { try { Api.ResetProps(text2, 0); list2.Add("re-posed " + text2.Substring(0, Math.Min(8, text2.Length))); } catch (Exception ex4) { list2.Add("prop " + text2 + ": " + ex4.Message); } } } int value = HubReset.ResetStatesUnder(tuple.Item3, list2); if (tuple.Rest.Item1 != null) { foreach (TrackedPeckState item3 in Object.FindObjectsOfType<TrackedPeckState>(true)) { if (!((Object)(object)item3 == (Object)null)) { string text3; try { text3 = Api.TransformPath(((Component)item3).transform); } catch { continue; } bool flag = false; string[] item = tuple.Item3; foreach (string value2 in item) { if (text3.IndexOf(value2, StringComparison.OrdinalIgnoreCase) >= 0) { flag = true; break; } } if (flag && text3.EndsWith("/BasicDoorPeckLogic", StringComparison.OrdinalIgnoreCase)) { item = tuple.Rest.Item1; foreach (string text4 in item) { if (text3.IndexOf(text4, StringComparison.OrdinalIgnoreCase) >= 0) { try { item3.SetState(1); _bounces.Add((item3, Time.unscaledTime + 1f)); list2.Add("bounced " + text4.TrimEnd('/').Split('/')[^1]); } catch (Exception ex5) { list2.Add("bounce: " + ex5.Message); } break; } } } } } } if (tuple.Rest.Item1 != null) { _rechecks.Add((tuple.Item3, Time.unscaledTime + 2.5f)); } host.Event("finalereset", $"{tuple.Item1}: {value} state(s) → baseline{((list2.Count > 0) ? (" — " + string.Join("; ", list2)) : "")}"); num++; } } if (num == 0) { host.Event("finalereset", $"unknown stage {key}; stages: {string.Join(", ", Array.ConvertAll(Stages, ((string key, string label, string[] roots, (string suffix, string label)[] probe, string note, string anchor, float radius, string[] bounce, string[] props) s) => s.key))}, all"); } Publish(); }); host.Command("dreamhold", delegate(string id, string key, int val, string text) { Release(); int num = 4; try { num = Api.WorldVariant(); } catch { } string value = "NHoldFullSet/NHoldSet" + num + "/"; int num2 = ((val > 0) ? val : (num - 1)); foreach (TrackedPeckState item4 in Object.FindObjectsOfType<TrackedPeckState>(true)) { if (!((Object)(object)item4 == (Object)null) && _held.Count < num2) { string text2; try { text2 = Api.TransformPath(((Component)item4).transform); } catch { continue; } if (text2.IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0 && text2.EndsWith("/ButtonSystem")) { try { item4.SetState(1); _held.Add(item4); } catch (Exception ex2) { host.Warn("dreamhold: " + ex2.Message); } } } } _releaseAt = Time.unscaledTime + 30f; host.Event("dreamhold", $"holding {_held.Count} of the {num}-hold buttons for 30s — press the rest for real"); }); host.Command("gauntletopen", delegate(string id, string key, int val, string text) { GauntletPress(key, victory: true, hold: false); }); host.Command("gauntlethold", delegate(string id, string key, int val, string text) { GauntletPress(key, victory: false, hold: true); }); host.Command("dreamrelease", delegate { int count = _held.Count; Release(); host.Event("dreamhold", $"released {count}"); }); host.Command("hold", delegate(string id, string key, int val, string text) { Release(); int num = ((val <= 0) ? 1 : val); foreach (TrackedPeckState item5 in Object.FindObjectsOfType<TrackedPeckState>(true)) { if (!((Object)(object)item5 == (Object)null) && _held.Count < num) { string text2; try { text2 = Api.TransformPath(((Component)item5).transform); } catch { continue; } if (text2.IndexOf(key ?? "", StringComparison.OrdinalIgnoreCase) >= 0 && text2.EndsWith("/ButtonSystem")) { try { item5.SetState(1); _held.Add(item5); } catch (Exception ex2) { host.Warn("hold: " + ex2.Message); } } } } _releaseAt = Time.unscaledTime + 30f; host.Event("hold", $"holding {_held.Count} button(s) under {key} for 30s"); }); host.Command("dreamstop", delegate { List<string> list = new List<string>(); StopDream(list); host.Event("dream", string.Join("; ", list)); }); try { MethodInfo method = typeof(DreamController).GetMethod("OnStartDreamPeck", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); host.Harmony.Patch((MethodBase)method, new HarmonyMethod(typeof(Finale).GetMethod("StartDreamPrefix", BindingFlags.Static | BindingFlags.Public)), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null); _patched = true; } catch (Exception ex) { host.Warn("patch OnStartDreamPeck: " + ex.Message); } host.Tick(delegate { if (_held.Count > 0 && Time.unscaledTime >= _releaseAt) { int count = _held.Count; Release(); host.Event("dreamhold", $"auto-released {count}"); } TrapTick(); for (int num = _bounces.Count - 1; num >= 0; num--) { if (Time.unscaledTime >= _bounces[num].at) { try { _bounces[num].st.SetState(0); } catch { } _bounces.RemoveAt(num); } } for (int num2 = _rechecks.Count - 1; num2 >= 0; num2--) { if (Time.unscaledTime >= _rechecks[num2].at) { int num3 = HubReset.ResetStatesUnder(_rechecks[num2].roots, null); if (num3 > 0) { _h.Event("finalereset", $"settle pass: {num3} state(s) re-applied"); } _rechecks.RemoveAt(num2); } } if (++_tick % 8 == 6 && Api.DashboardLive) { Publish(); } }); Publish(); } public static bool StartDreamPrefix() { return Time.unscaledTime >= _suppressUntil; } private string GiveDream(PlayerCharacter pc) { var (val, val2) = Controller(); if ((Object)(object)val == (Object)null) { return "no DreamController (BlackTowerDream/DreamStateSystem)"; } try { _suppressUntil = Time.unscaledTime + 3f; int num = -1; try { PeckContext currentPeckContext = val2.currentPeckContext; if (currentPeckContext != null) { num = currentPeckContext.state; } } catch { } if (num != 1) { try { val2.SetState(1); } catch (Exception ex) { _h.Warn("dream state: " + ex.Message); } } val.dreamPlayer = pc; pc.playerNetworking.ServerSetDream(val, true); return null; } catch (Exception ex2) { return "failed: " + ex2.Message; } } private void TrapTick() { if (_trapped.Count == 0 || Time.unscaledTime < _nextTrapCheck) { return; } _nextTrapCheck = Time.unscaledTime + 1f; List<IntPtr> list = new List<IntPtr>(); foreach (KeyValuePair<IntPtr, string> item in _trapped) { PlayerCharacter val = null; foreach (PlayerCharacter item2 in Api.Players()) { if (((Il2CppObjectBase)item2).Pointer == item.Key) { val = item2; break; } } if ((Object)(object)val == (Object)null) { list.Add(item.Key); continue; } bool flag = true; try { flag = val.dreamer.isDreaming; } catch { } if (!flag) { string text = GiveDream(val); if (text != null) { _h.Warn("dreamtrap " + item.Value + ": " + text); } } } foreach (IntPtr item3 in list) { _h.Event("dreamtrap", _trapped[item3] + " left — trap dropped"); _trapped.Remove(item3); } } private void Release() { foreach (TrackedPeckState item in _held) { try { item.SetState(0); } catch { } } _held.Clear(); } private static PlayerCharacter Pick(string id, string text) { if (!string.IsNullOrEmpty(id)) { try { PlayerCharacter val = Api.ById(id); if ((Object)(object)val != (Object)null) { return val; } } catch { } } if (!string.IsNullOrEmpty(text)) { foreach (PlayerCharacter item in Api.Players()) { try { if (Api.Display(item.playerNetworking).IndexOf(text, StringComparison.OrdinalIgnoreCase) >= 0) { return item; } } catch { } } } return null; } private static List<PlayerCharacter> Targets(string id, string text, bool defaultHost) { List<PlayerCharacter> list = new List<PlayerCharacter>(); bool num = string.Equals(id, "all", StringComparison.OrdinalIgnoreCase); bool flag = string.Equals(id, "others", StringComparison.OrdinalIgnoreCase); if (num || flag) { PlayerCharacter val = Api.Local(); { foreach (PlayerCharacter item in Api.Players()) { if ((Object)(object)item != (Object)null && (!flag || !((Object)(object)item == (Object)(object)val))) { list.Add(item); } } return list; } } PlayerCharacter val2 = Pick(id, text) ?? ((defaultHost && string.IsNullOrEmpty(id) && string.IsNullOrEmpty(text)) ? Api.Local() : null); if ((Object)(object)val2 != (Object)null) { list.Add(val2); } return list; } private void GauntletPress(string key, bool victory, bool hold) { int result = 0; if (!int.TryParse(key, out result)) { _h.Event("gauntlet", "need key=<level>"); return; } int num = 4; try { num = Api.WorldVariant(); } catch { } int num2 = 0; foreach (TrackedPeckState item in Object.FindObjectsOfType<TrackedPeckState>(true)) { if ((Object)(object)item == (Object)null) { continue; } string text; try { text = Api.TransformPath(((Component)item).transform); } catch { continue; } if (text.IndexOf("/Level" + result + "/", StringComparison.OrdinalIgnoreCase) < 0) { continue; } bool num3 = victory && text.EndsWith("/GauntletChamberNetworkObjects/VictoryLogic", StringComparison.OrdinalIgnoreCase); bool flag = hold && text.IndexOf("GourdTower_Stairs/NHoldFullSet/NHoldSet" + num + "/", StringComparison.OrdinalIgnoreCase) >= 0 && text.EndsWith("/ButtonSystem"); if (!num3 && !flag) { continue; } try { item.SetState(1); num2++; if (flag) { _held.Add(item); } } catch (Exception ex) { _h.Warn("gauntlet: " + ex.Message); } } if (hold) { _releaseAt = Time.unscaledTime + 1.5f; } _h.Event(victory ? "gauntletopen" : "gauntlethold", $"level {result}: {num2} state(s) pressed ({(victory ? "victory" : (num + "-hold"))})"); } private static Vector3? Anchor(string suffix) { //IL_0040: Unknown result type (might be due to invalid IL or missing references) foreach (TrackedPeckState item in Object.FindObjectsOfType<TrackedPeckState>(true)) { if (!((Object)(object)item == (Object)null)) { string text; try { text = Api.TransformPath(((Component)item).transform); } catch { continue; } if (text.EndsWith(suffix, StringComparison.OrdinalIgnoreCase)) { return ((Component)item).transform.position; } } } return null; } private static (DreamController dc, TrackedPeckState st) Controller() { foreach (TrackedPeckState item in Object.FindObjectsOfType<TrackedPeckState>(true)) { if (!((Object)(object)item == (Object)null)) { string text; try { text = Api.TransformPath(((Component)item).transform); } catch { continue; } if (text.EndsWith("BlackTowerDream/DreamStateSystem", StringComparison.OrdinalIgnoreCase)) { return (dc: ((Component)item).GetComponent<DreamController>(), st: item); } } } return (dc: null, st: null); } private void StopDream(List<string> notes) { DreamController item = Controller().dc; if ((Object)(object)item == (Object)null) { notes?.Add("no DreamController"); return; } int num = 0; foreach (PlayerCharacter item2 in Api.Players()) { try { item2.playerNetworking.ServerSetDream(item, false); num++; } catch (Exception ex) { notes?.Add("stop " + Api.Display(item2.playerNetworking) + ": " + ex.Message); } } try { item.dreamPlayer = null; } catch { } notes?.Add($"dream stopped for {num} player(s)"); } private void IndexProbes() { _probeIdx.Clear(); _probeIdxAt = Time.unscaledTime; foreach (TrackedPeckState item2 in Object.FindObjectsOfType<TrackedPeckState>(true)) { if ((Object)(object)item2 == (Object)null) { continue; } string text; try { text = Api.TransformPath(((Component)item2).transform); } catch { continue; } if (text.IndexOf("BlackTower", StringComparison.OrdinalIgnoreCase) < 0 && text.IndexOf("EndingGate", StringComparison.OrdinalIgnoreCase) < 0 && text.IndexOf("Gauntlet", StringComparison.OrdinalIgnoreCase) < 0) { continue; } (string, string, string[], (string, string)[], string, string, float, string[], string[])[] stages = Stages; for (int i = 0; i < stages.Length; i++) { (string, string, string[], (string, string)[], string, string, float, string[], string[]) tuple = stages[i]; (string, string)[] item = tuple.Item4; for (int j = 0; j < item.Length; j++) { var (value, text2) = item[j]; if (text.EndsWith(value, StringComparison.OrdinalIgnoreCase)) { _probeIdx.Add((item2, tuple.Item1 + "/" + text2)); } } } } } private void Publish() { if (Time.unscaledTime - _probeIdxAt > 60f || _probeIdx.Count == 0) { IndexProbes(); } Dictionary<string, int> dictionary = new Dictionary<string, int>(); foreach (var (val, key) in _probeIdx) { if ((Object)(object)val == (Object)null) { continue; } int value = int.MinValue; try { PeckContext currentPeckContext = val.currentPeckContext; if (currentPeckContext != null) { value = currentPeckContext.state; } } catch { } dictionary[key] = value; } StringBuilder stringBuilder = new StringBuilder("{\"held\":").Append(_held.Count).Append(",\"trapped\":["); bool flag = true; foreach (string value7 in _trapped.Values) { if (!flag) { stringBuilder.Append(','); } flag = false; stringBuilder.Append(Api.Json(value7)); } stringBuilder.Append("],\"stages\":["); for (int i = 0; i < Stages.Length; i++) { (string, string, string[], (string, string)[], string, string, float, string[], string[]) tuple2 = Stages[i]; if (i > 0) { stringBuilder.Append(','); } bool flag2 = false; try { flag2 = ((!(tuple2.Item1 == "dreamdoor")) ? ((!(tuple2.Item1 == "bell")) ? ((!(tuple2.Item1 == "gauntlet7")) ? (dictionary.TryGetValue(tuple2.Item1 + "/gate", out var value2) && value2 == 1) : (dictionary.TryGetValue("gauntlet7/skylight", out var value3) && value3 == 1)) : (dictionary.TryGetValue("bell/exit", out var value4) && value4 == 1)) : (dictionary.TryGetValue("dreamdoor/door", out var value5) && value5 == 1)); } catch { } stringBuilder.Append("{\"key\":").Append(Api.Json(tuple2.Item1)).Append(",\"label\":") .Append(Api.Json(tuple2.Item2)) .Append(",\"note\":") .Append(Api.Json(tuple2.Item5)) .Append(",\"done\":") .Append(flag2 ? "true" : "false") .Append(",\"probe\":{"); bool flag3 = true; (string, string)[] item = tuple2.Item4; for (int j = 0; j < item.Length; j++) { string item2 = item[j].Item2; if (!flag3) { stringBuilder.Append(','); } flag3 = false; stringBuilder.Append(Api.Json(item2)).Append(':').Append((dictionary.TryGetValue(tuple2.Item1 + "/" + item2, out var value6) && value6 != int.MinValue) ? value6.ToString() : "null"); } stringBuilder.Append("}}"); } _h.Panel("finale", stringBuilder.Append("]}").ToString()); } public void Unload() { Release(); _trapped.Clear(); } } public class HubReset : IOrbScript { private class HubIdx { public List<PropHome> Slots = new List<PropHome>(); public Prop Key; public KeyBlank Blank; } private ScriptHost _h; private static readonly (string root, string label, string keySave, string[] roots, string note)[] Hubs = new(string, string, string, string[], string)[6] { ("IntroPavilion", "Tutorial hub (intro pavilion)", "bigKeyIntro", new string[6] { "IntroPavilion", "CuttingTrails/CuttingTrailBoardwalk", "IntroCage", "Walkway-IntroExit", "MonumentMapFlag Intro", "EndingGateIndicator" }, "4 slots → blank key → 5 cutters → drawbridge. Known: cutter plate markers + key cover meshes only refresh for connected clients on rejoin (game never re-enables them over the network); host is fixed locally"), ("Lookout_RedCone/RoofStuff", "Red tower hub (lookout roof)", "bigKeyRedZone", new string[5] { "Lookout_RedCone/RoofStuff", "CuttingTrails/CuttingTrailRedTower", "MonumentMapFlag Red", "UnlockLogics/Lookout0Red", "EndingGateIndicator (1)" }, "5 slots → red key → 5 cutters → MapRoom plinth; door/lights separate (Doors panel); verified"), ("Lookout_GreenHourglass/RoofStuff", "Green tower hub (lookout roof)", "bigKeyGreenZone", new string[5] { "Lookout_GreenHourglass/RoofStuff", "CuttingTrails/CuttingTrailGreenTower", "MonumentMapFlag Green", "UnlockLogics/Lookout1Green", "EndingGateIndicator (2)" }, "5 slots → green key → 5 cutters → chairlift plinth (ChairliftBend_green); door/lights separate (Doors panel); indicator index verified"), ("Lookout_BlueTube/RoofStuff", "Blue tower hub (lookout roof)", "bigKeyBlueZone", new string[5] { "Lookout_BlueTube/RoofStuff", "CuttingTrails/CuttingTrailBlueTower", "MonumentMapFlag Blue", "UnlockLogics/Lookout2Blue", "EndingGateIndicator (3)" }, "5 slots → blue key → 5 cutters; door/lights separate (Doors panel); indicator index verified"), ("Lookout_YellowZigzag/RoofStuff", "Yellow tower hub (lookout roof)", "bigKeyYellowZone", new string[5] { "Lookout_YellowZigzag/RoofStuff", "CuttingTrails/CuttingTrailYellowTower", "MonumentMapFlag Yellow", "UnlockLogics/Lookout3Yellow", "EndingGateIndicator (4)" }, "5 slots → yellow key → 5 cutters; door/lights separate (Doors panel); indicator index verified"), ("BlackTower/Positioner/GourdPlinth X6", "Black tower monument (6 gourds → black key)", "bigKeyBoss", new string[7] { "BlackTower/Positioner/GourdPlinth X6", "BlackTower/Positioner/BigKeyInStoneEnding", "BigKeyInBox Ending", "MonumentMapFlag Overflow", "Indicators/EndingGateIndicator - black", "OverflowMonument/Positioner/BasicDoor - to overflow", "OverflowMonument/Positioner/Lights/" }, "6 slots → black key (no cutting) → ending gate plinth; completing it un-hides the overflow monument's map flag + lights its black indicator (both re-hidden on reset); reset sends the key back to its stone") }; private const float LooseRadius = 25f; private static readonly string[] TowerShared = new string[3] { "MonumentMapFlag Black", "BlackTower/Positioner/BasicDoor/", "BlackTower/Positioner/Lighting/" }; private static HubReset _instance; private int _tick; private readonly List<string> _recheck = new List<string>(); private float _recheckAt; private static readonly (string plinth, string scope)[] PlinthScopes = new(string, string)[3] { ("BigKeyPlinth Chairlift", "ChairLift"), ("BigKeyPlinth Tunnels", "TunnelSystem"), ("BigKeyPlinth Ending", "EndingGate/GatePositioner") }; private readonly Dictionary<string, HubIdx> _idx = new Dictionary<string, HubIdx>(); private float _idxBuiltAt = -999f; public string Name => "hubreset"; public static IEnumerable<(string root, string label)> Table() { (string root, string label, string keySave, string[] roots, string note)[] hubs = Hubs; for (int i = 0; i < hubs.Length; i++) { (string, string, string, string[], string) tuple = hubs[i]; yield return (root: tuple.Item1, label: tuple.Item2); } } private static bool IsTowerHub(string root) { return !root.StartsWith("BlackTower", StringComparison.OrdinalIgnoreCase); } public static void ResetStatic(string hubRoot, bool keepGourds = false) { _instance?.Reset(hubRoot, keepGourds); } public unsafe void Load(ScriptHost host) { _h = host; _instance = this; host.Command("resethub", delegate(string id, string key, int val, string text) { Reset(string.IsNullOrEmpty(key) ? Hubs[0].root : key, val == 1); }); host.Command("hubfill", delegate(string id, string key, int val, string text) { //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Invalid comparison between Unknown and I4 //IL_01ae: Unknown result type (might be due to invalid IL or missing references) //IL_01b3: Unknown result type (might be due to invalid IL or missing references) int num = Array.FindIndex(Hubs, ((string root, string label, string keySave, string[] roots, string note) h) => string.Equals(h.root, key, StringComparison.OrdinalIgnoreCase)); if (num < 0) { host.Event("hubfill", "unknown hub " + key); } else { (string, string, string, string[], string) tuple = Hubs[num]; List<PropHome> list = new List<PropHome>(); List<PropHome> allPropHomes = PropHome.allPropHomes; if (allPropHomes != null) { for (int num2 = 0; num2 < allPropHomes.Count; num2++) { PropHome val2 = null; try { val2 = allPropHomes[num2]; } catch { } if (!((Object)(object)val2 == (Object)null)) { try { if ((int)val2.pinGroup == 19 && (Object)(object)val2.pinnedProp == (Object)null && Under(Api.TransformPath(((Component)val2).transform), tuple.Item1)) { list.Add(val2); } } catch { } } } } int num3 = ((val > 0) ? Math.Min(val, list.Count) : list.Count); List<string> list2 = new List<string>(); int num4 = 0; List<Prop> allProps = Prop.allProps; if (allProps != null) { for (int num5 = 0; num5 < allProps.Count; num5++) { if (num4 >= num3) { break; } Prop val3 = null; try { val3 = allProps[num5]; } catch { } if (!((Object)(object)val3 == (Object)null)) { try { if (val3.MatchesGroup((PropGroup)19) && !((Object)(object)val3.startHome == (Object)null) && !((Object)(object)val3.currentHome != (Object)(object)val3.startHome) && !Under(Api.TransformPath(((Component)val3.startHome).transform), tuple.Item1) && PuzzleReset.RootForGourd(val3) != null) { val3.ServerSetUnpinned(); val3.ServerSetPinned(list[num4]); list2.Add(((object)val3.saveablePropName/*cast due to .constrained prefix*/).ToString()); num4++; } } catch (Exception ex) { host.Warn("hubfill: " + ex.Message); } } } } host.Event("hubfill", $"{tuple.Item1}: {num4}/{list.Count} empty slot(s) filled — {string.Join(", ", list2)}"); } }); host.Command("hubkey", delegate(string id, string key, int val, string text) { //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_01f3: Unknown result type (might be due to invalid IL or missing references) int num = Array.FindIndex(Hubs, ((string root, string label, string keySave, string[] roots, string note) h) => string.Equals(h.root, key, StringComparison.OrdinalIgnoreCase)); if (num < 0 || string.IsNullOrEmpty(text)) { host.Event("hubkey", "need key=<hub root> text=<plinth substring>"); } else { (string, string, string, string[], string) tuple = Hubs[num]; Prop val2 = null; List<Prop> allProps = Prop.allProps; int num2 = 0; while (allProps != null && num2 < allProps.Count) { Prop val3 = null; try { val3 = allProps[num2]; } catch { } if (!((Object)(object)val3 == (Object)null)) { try { if (((object)val3.saveablePropName/*cast due to .constrained prefix*/).ToString() == tuple.Item3) { val2 = val3; break; } } catch { } } num2++; } if ((Object)(object)val2 == (Object)null) { host.Event("hubkey", "key not found: " + tuple.Item3); } else { PropHome val4 = null; List<PropHome> allPropHomes = PropHome.allPropHomes; int num3 = 0; while (allPropHomes != null && num3 < allPropHomes.Count) { PropHome val5 = null; try { val5 = allPropHomes[num3]; } catch { } if (!((Object)(object)val5 == (Object)null)) { try { if (Api.TransformPath(((Component)val5).transform).IndexOf(text, StringComparison.OrdinalIgnoreCase) >= 0) { val4 = val5; break; } } catch { } } num3++; } if ((Object)(object)val4 == (Object)null) { host.Event("hubkey", "no PropHome matching " + text); } else { List<string> list = new List<string>(); try { KeyBlank val6 = KeyBlankOf(val2); if ((Object)(object)val6 != (Object)null) { SyncList<bool> cuts = val6.cuts; int num4 = cuts?.Count ?? 0; for (int num5 = 0; num5 < num4; num5++) { cuts[num5] = true; } val6.RefreshPropGroup(); list.Add($"{num4} cuts set, group {(val2.MatchesGroup(val6.finishedPropGroup) ? "complete" : "NOT complete")}"); } else { list.Add("no KeyBlank"); } DropIfHeld(val2); val2.ServerSetUnpinned(); val2.ServerSetPinned(val4); list.Add("pinned in " + ((Object)val4).name + (((Object)(object)val2.currentHome == (Object)(object)val4) ? "" : (" (REJECTED — currentHome is " + (((Object)(object)val2.currentHome != (Object)null) ? ((Object)val2.currentHome).name : "none") + ")"))); } catch (Exception ex) { list.Add(ex.Message); } host.Event("hubkey", tuple.Item3 + ": " + string.Join("; ", list)); } } } }); host.Command("keygroups", delegate(string id, string key, int val, string text) { //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_00df: 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_0195: Unknown result type (might be due to invalid IL or missing references) //IL_019a: 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_00a6: Unknown result type (might be due to invalid IL or missing references) Prop val2 = null; List<Prop> allProps = Prop.allProps; int num = 0; while (allProps != null && num < allProps.Count) { Prop val3 = null; try { val3 = allProps[num]; } catch { } if (!((Object)(object)val3 == (Object)null)) { try { if (((object)val3.saveablePropName/*cast due to .constrained prefix*/).ToString() == key) { val2 = val3; break; } } catch { } } num++; } if ((Object)(object)val2 == (Object)null) { host.Event("keygroups", "no key " + key); } else { List<string> list = new List<string>(); try { Enumerator<PropGroup> enumerator = val2.propGroups.GetEnumerator(); while (enumerator.MoveNext()) { list.Add(((object)enumerator.Current/*cast due to .constrained prefix*/).ToString()); } } catch { } if (!string.IsNullOrEmpty(text) && Enum.TryParse<PropGroup>(text, ignoreCase: true, out PropGroup result)) { try { if (!val2.MatchesGroup(result)) { val2.propGroups.Add(result); list.Add(((object)(*(PropGroup*)(&result))/*cast due to .constrained prefix*/).ToString() + " (added)"); } } catch (Exception ex) { list.Add("add failed: " + ex.Message); } } List<string> list2 = new List<string>(); List<PropHome> allPropHomes = PropHome.allPropHomes; int num2 = 0; while (allPropHomes != null && num2 < allPropHomes.Count) { PropHome val4 = null; try { val4 = allPropHomes[num2]; } catch { } if (!((Object)(object)val4 == (Object)null)) { try { string text2 = Api.TransformPath(((Component)val4).transform); if (text2.IndexOf("BigKeyPlinth", StringComparison.OrdinalIgnoreCase) >= 0) { list2.Add(text2.Split('/')[^2] + " wants " + ((object)val4.pinGroup/*cast due to .constrained prefix*/).ToString()); } } catch { } } num2++; } host.Event("keygroups", $"{key}: [{string.Join(", ", list)}] · plinths: {string.Join("; ", list2)}"); } }); host.Command("respawnkey", delegate { //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Invalid comparison between Unknown and I4 List<Prop> allProps = Prop.allProps; List<string> list = new List<string>(); int value = 0; for (int i = 0; i < allProps.Count; i++) { Prop val2 = null; try { val2 = allProps[i]; } catch { } if (!((Object)(object)val2 == (Object)null)) { try { if ((int)val2.saveablePropName == 300) { value = RespawnForRemotes(((NetworkBehaviour)val2).netIdentity, list); break; } } catch { } } } host.Event("respawnkey", $"respawned for {value} client(s){((list.Count > 0) ? (" — " + string.Join("; ", list)) : "")}"); }); host.Tick(delegate { if (++_tick % 8 == 4 && Api.DashboardLive) { PublishStatus(); } if (_recheck.Count > 0 && Time.unscaledTime >= _recheckAt) { foreach (string item in _recheck) { try { PuzzleReset.ResetStatic(item, 0); } catch { } } _recheck.Clear(); } }); host.Endpoint("stationdebug", delegate { //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) StringBuilder stringBuilder = new StringBuilder("["); bool flag = true; foreach (UnlockTrailStation item2 in Object.FindObjectsOfType<UnlockTrailStation>(true)) { if (!((Object)(object)item2 == (Object)null)) { if (!flag) { stringBuilder.Append(','); } flag = false; stringBuilder.Append("{\"path\":").Append(Api.Json(Api.TransformPath(((Component)item2).transform))).Append(",\"index\":") .Append(item2.stationIndex); try { Transform arrowTransform = item2.arrowTransform; StringBuilder stringBuilder2 = stringBuilder.Append(",\"arrow\":"); object value; if (!((Object)(object)arrowTransform != (Object)null)) { value = "null"; } else { string[] obj = new string[7] { ((Object)arrowTransform).name, " localRot=", null, null, null, null, null }; Quaternion localRotation = arrowTransform.localRotation; Vector3 val = ((Quaternion)(ref localRotation)).eulerAngles; obj[2] = ((Vector3)(ref val)).ToString("0.#"); obj[3] = " localPos="; val = arrowTransform.localPosition; obj[4] = ((Vector3)(ref val)).ToString("0.##"); obj[5] = " active="; obj[6] = ((Component)arrowTransform).gameObject.activeSelf.ToString(); value = Api.Json(string.Concat(obj)); } stringBuilder2.Append((string?)value); } catch { } try { PeckEffectTween peckTween = item2.peckTween; stringBuilder.Append(",\"tween\":").Append(((Object)(object)peckTween != (Object)null) ? Api.Json(Api.TransformPath(((Component)peckTween).transform) + " dur=" + peckTween.duration) : "null"); } catch { } List<string> list = new List<string>(); try { foreach (Component componentsInChild in ((Component)item2).GetComponentsInChildren<Component>(true)) { try { string name = ((MemberInfo)((Object)componentsInChild).GetIl2CppType()).Name; if (name.StartsWith("PeckEffect") || name.Contains("Tween") || name.Contains("Toggle") || name == "TrackedPeckState") { list.Add(name + "@" + ((Object)componentsInChild.gameObject).name); } } catch { } } } catch { } stringBuilder.Append(",\"effects\":").Append(Api.Json(string.Join(" | ", list))).Append('}'); } } return stringBuilder.Append(']').ToString(); }); host.Endpoint("keytree", delegate { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Invalid comparison between Unknown and I4 List<Prop> allProps = Prop.allProps; Prop val = null; for (int i = 0; i < allProps.Count; i++) { Prop val2 = null; try { val2 = allProps[i]; } catch { } if (!((Object)(object)val2 == (Object)null)) { try { if ((int)val2.saveablePropName == 300) { val = val2; break; } } catch { } } } if ((Object)(object)val == (Object)null) { return "{\"err\":\"no key\"}"; } StringBuilder stringBuilder = new StringBuilder("["); bool flag = true; foreach (Transform componentsInChild2 in ((Component)val).GetComponentsInChildren<Transform>(true)) { if (!flag) { stringBuilder.Append(','); } flag = false; List<string> list = new List<string>(); foreach (Component component2 in ((Component)componentsInChild2).GetComponents<Component>()) { try { string name = ((MemberInfo)((Object)component2).GetIl2CppType()).Name; if (name != "Transform") { list.Add(name); } } catch { } } Renderer component = ((Component)componentsInChild2).GetComponent<Renderer>(); stringBuilder.Append("{\"path\":").Append(Api.Json(Api.TransformPath(componentsInChild2).Replace(Api.TransformPath(((Component)val).transform), "KEY"))).Append(",\"active\":") .Append(((Component)componentsInChild2).gameObject.activeSelf ? "true" : "false") .Append(",\"renderer\":") .Append(((Object)(object)component != (Object)null) ? (component.enabled ? "\"on\"" : "\"off\"") : "null") .Append(",\"comps\":") .Append(Api.Json(string.Join(",", list))) .Append('}'); } KeyBlank val3 = KeyBlankOf(val); if ((Object)(object)val3 != (Object)null && val3.covers != null) { foreach (KeyBlankCover item3 in (Il2CppArrayBase<KeyBlankCover>)(object)val3.covers) { if (!((Object)(object)item3 == (Object)null)) { stringBuilder.Append(",{\"cover\":").Append(Api.Json(((Object)item3).name)).Append(",\"stage\":") .Append(item3.currentStage) .Append(",\"stages\":") .Append(Api.Json(StageNames(item3))) .Append('}'); } } } return stringBuilder.Append(']').ToString(); }); host.Endpoint("keydebug", delegate { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Invalid comparison between Unknown and I4 //IL_021f: Unknown result type (might be due to invalid IL or missing references) //IL_0224: Unknown result type (might be due to invalid IL or missing references) List<Prop> allProps = Prop.allProps; StringBuilder stringBuilder = new StringBuilder("{"); for (int i = 0; i < allProps.Count; i++) { Prop val = null; try { val = allProps[i]; } catch { } if (!((Object)(object)val == (Object)null)) { try { if ((int)val.saveablePropName != 300) { continue; } } catch { continue; } stringBuilder.Append("\"prop\":").Append(Api.Json(((Object)val).name)); KeyBlank val2 = KeyBlankOf(val); stringBuilder.Append(",\"kb\":").Append(((Object)(object)val2 != (Object)null) ? "true" : "false"); if ((Object)(object)val2 != (Object)null) { object obj3 = null; string text = null; int num = -1; string text2 = ""; try { obj3 = val2.cuts; } catch (Exception ex) { text = "cuts: " + ex.Message; } try { if (val2.cuts != null) { num = val2.cuts.Count; for (int j = 0; j < num; j++) { text2 += (val2.cuts[j] ? "1" : "0"); } } } catch (Exception ex2) { text = text + " count: " + ex2.Message; } Il2CppArrayBase<Component> components = ((Component)val).GetComponents<Component>(); List<string> list = new List<string>(); foreach (Component item4 in components) { try { list.Add(((MemberInfo)((Object)item4).GetIl2CppType()).Name); } catch { } } stringBuilder.Append(",\"cutsNull\":").Append((obj3 == null) ? "true" : "false").Append(",\"count\":") .Append(num) .Append(",\"items\":") .Append(Api.Json(text2)) .Append(",\"err\":") .Append(Api.Json(text)) .Append(",\"covers\":") .Append((val2.covers != null) ? ((Il2CppArrayBase<KeyBlankCover>)(object)val2.covers).Length : (-1)) .Append(",\"finishedGroup\":") .Append(Api.Json(((object)val2.finishedPropGroup/*cast due to .constrained prefix*/).ToString())) .Append(",\"components\":") .Append(Api.Json(string.Join(",", list))); } break; } } return stringBuilder.Append('}').ToString(); }); } private static KeyBlank KeyBlankOf(Prop p) { try { KeyBlank componentInChildren = ((Component)p).GetComponentInChildren<KeyBlank>(true); if ((Object)(object)componentInChildren != (Object)null) { return componentInChildren; } } catch { } try { KeyBlank componentInParent = ((Component)p).GetComponentInParent<KeyBlank>(); if ((Object)(object)componentInParent != (Object)null) { return componentInParent; } } catch { } try { foreach (KeyBlank item in Object.FindObjectsOfType<KeyBlank>(true)) { if ((Object)(object)item != (Object)null && (Object)(object)item.prop == (Object)(object)p) { return item; } } } catch { } return null; } public static int RespawnForRemotes(NetworkIdentity id, List<string> notes) { if ((Object)(object)id == (Object)null) { return 0; } int num = 0; try { Enumerator<int, NetworkConnectionToClient> enumerator = NetworkServer.connections.GetEnumerator(); while (enumerator.MoveNext()) { KeyValuePair<int, NetworkConnectionToClient> current = enumerator.Current; NetworkConnectionToClient value = current.Value; if (value != null && (object)value != NetworkServer.localConnection) { try { NetworkServer.HideForConnection(id, (NetworkConnection)(object)value); NetworkServer.ShowForConnection(id, (NetworkConnection)(object)value); num++; } catch (Exception ex) { notes?.Add("respawn conn " + current.Key + ": " + ex.Message); } } } } catch (Exception ex2) { notes?.Add("respawn: " + ex2.Message); } return num; } private static string StageNames(KeyBlankCover cv) { Il2CppReferenceArray<Transform> stages = cv.stages; if (stages == null) { return ""; } List<string> list = new List<string>(); for (int i = 0; i < ((Il2CppArrayBase<Transform>)(object)stages).Length; i++) { Transform val = ((Il2CppArrayBase<Transform>)(object)stages)[i]; list.Add(((Object)(object)val == (Object)null) ? "null" : (((Object)val).name + (((Component)val).gameObject.activeSelf ? "*" : ""))); } return string.Join("|", list); } private static bool Under(string path, string root) { return path.IndexOf(root, StringComparison.OrdinalIgnoreCase) >= 0; } public static int ResetStatesUnder(IEnumerable<string> roots, List<string> notes) { int num = 0; Il2CppArrayBase<TrackedPeckState> val = null; try { val = Object.FindObjectsOfType<TrackedPeckState>(true); } catch (Exception ex) { notes?.Add("states: " + ex.Message); return 0; } if (val == null) { return 0; } for (int i = 0; i < 2; i++) { for (int j = 0; j < val.Length; j++) { TrackedPeckState val2 = null; try { val2 = val[j]; } catch { } if ((Object)(object)val2 == (Object)null) { continue; } string text; try { text = Api.TransformPath(((Component)val2).transform); } catch { continue; } if (text.Contains("PlayerCharacter") || text.EndsWith("/UnhideSystem") != (i == 0)) { continue; } bool flag = false; foreach (string root in roots) { if (Under(text, root)) { flag = true; break; } } if (!flag) { continue; } string label = ""; try { label = val2.label ?? ""; } catch { } if (!PuzzleProbeBaseline.TryGet(PuzzleProbeBaseline.Key(text, label), out var value)) { continue; } int num2 = int.MinValue; try { PeckContext currentPeckContext = val2.currentPeckContext; if (currentPeckContext != null) { num2 = currentPeckContext.state; } } catch { } if (num2 != value) { try { val2.SetState(value); num++; } catch (Exception ex2) { notes?.Add(text.Substring(text.LastIndexOf('/') + 1) + ": " + ex2.Message); } } } } return num; } private void Reset(string hubRoot, bool keepGourds) { //IL_017a: Unknown result type (might be due to invalid IL or missing references) //IL_017f: 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) //IL_018e: Unknown result type (might be due to invalid IL or missing references) //IL_0193: Unknown result type (might be due to invalid IL or missing references) //IL_019d: Unknown result type (might be due to invalid IL or missing references) //IL_01a2: Unknown result type (might be due to invalid IL or missing references) //IL_01a7: Unknown result type (might be due to invalid IL or missing references) //IL_01b0: Unknown result type (might be due to invalid IL or missing references) //IL_040d: Unknown result type (might be due to invalid IL or missing references) //IL_0412: Unknown result type (might be due to invalid IL or missing references) //IL_024a: Unknown result type (might be due to invalid IL or missing references) //IL_024f: 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_00d7: Invalid comparison between Unknown and I4 //IL_01cc: Unknown result type (might be due to invalid IL or missing references) //IL_01d5: Unknown result type (might be due to invalid IL or missing references) //IL_06d1: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_06e1: Unknown result type (might be due to invalid IL or missing references) //IL_025e: Unknown result type (might be due to invalid IL or missing references) //IL_0263: Unknown result type (might be due to invalid IL or missing references) //IL_0265: Unknown result type (might be due to invalid IL or missing references) //IL_0267: Unknown result type (might be due to invalid IL or missing references) //IL_0269: Unknown result type (might be due to invalid IL or missing references) //IL_026e: Unknown result type (might be due to invalid IL or missing references) //IL_0289: Unknown result type (might be due to invalid IL or missing references) //IL_0293: Unknown result type (might be due to invalid IL or missing references) //IL_0298: Unknown result type (might be due to invalid IL or missing references) //IL_0332: Unknown result type (might be due to invalid IL or missing references) //IL_0337: Unknown result type (might be due to invalid IL or missing references) //IL_0339: Unknown result type (might be due to invalid IL or missing references) //IL_033e: Unknown result type (might be due to invalid IL or missing references) int num = Array.FindIndex(Hubs, ((string root, string label, string keySave, string[] roots, string note) h) => string.Equals(h.root, hubRoot, StringComparison.OrdinalIgnoreCase)); if (num < 0) { _h.Event("resethub", "unknown hub " + hubRoot); return; } (string, string, string, string[], string) tuple = Hubs[num]; List<string> list = new List<string>(); int num2 = 0; int num3 = 0; int num4 = 0; List<PropHome> allPropHomes = PropHome.allPropHomes; HashSet<string> hashSet = new HashSet<string>(); HashSet<IntPtr> hashSet2 = new HashSet<IntPtr>(); List<Vector3> list2 = new List<Vector3>(); if (allPropHomes != null) { for (int num5 = 0; num5 < allPropHomes.Count; num5++) { PropHome val = null; try { val = allPropHomes[num5]; } catch { } if ((Object)(object)val == (Object)null) { continue; } try { if (!Under(Api.TransformPath(((Component)val).transform), tuple.Item1)) { continue; } if ((int)val.pinGroup == 19) { list2.Add(((Component)val).transform.position); } Prop pinnedProp = val.pinnedProp; if ((Object)(object)pinnedProp == (Object)null || !pinnedProp.MatchesGroup((PropGroup)19)) { continue; } hashSet2.Add(((Il2CppObjectBase)pinnedProp).Pointer); try { pinnedProp.ServerSetUnpinned(); } catch { } if (!keepGourds && (Object)(object)pinnedProp.startHome != (Object)null) { pinnedProp.ServerSetPinned(pinnedProp.startHome); AddPuzzle(pinnedProp, hashSet, list); } else { if (!keepGourds) { list.Add(((Object)pinnedProp).name + ": no startHome, dropped at hub"); } Vector3 position = ((Component)val).transform.position + Vector3.up * 0.5f + Random.insideUnitSphere * 0.5f; ((Component)pinnedProp).transform.position = position; Rigidbody component = ((Component)pinnedProp).GetComponent<Rigidbody>(); if ((Object)(object)component != (Object)null) { component.position = position; component.velocity = Vector3.zero; } try { RewardGourd val2 = PuzzleReset.RewardGourdOf(pinnedProp); if ((Object)(object)val2 != (Object)null) { val2.ServerSetGourdState((GourdState)1); } } catch { } } num2++; } catch (Exception ex) { list.Add("gourd: " + ex.Message); } } } if (!keepGourds && list2.Count > 0) { Vector3 val3 = Vector3.zero; foreach (Vector3 item in list2) { val3 += item; } val3 /= (float)list2.Count; List<Prop> allProps = Prop.allProps; if (allProps != null) { for (int num6 = 0; num6 < allProps.Count; num6++) { Prop val4 = null; try { val4 = allProps[num6]; } catch { } if ((Object)(object)val4 == (Object)null || hashSet2.Contains(((Il2CppObjectBase)val4).Pointer)) { continue; } try { bool flag; if (val4.MatchesGroup((PropGroup)19) && !((Object)(object)val4.startHome == (Object)null) && !((Object)(object)val4.currentHome == (Object)(object)val4.startHome)) { flag = (Object)(object)HeldBy(val4) != (Object)null; if (flag) { goto IL_0350; } Vector3 val5 = ((Component)val4).transform.position - val3; if (!(((Vector3)(ref val5)).magnitude > 25f)) { goto IL_0350; } } goto end_IL_02e4; IL_0350: DropIfHeld(val4); try { val4.ServerSetUnpinned(); } catch { } val4.ServerSetPinned(val4.startHome); AddPuzzle(val4, hashSet, list); list.Add(((Object)val4).name + (flag ? " taken from a player" : " picked up loose") + " → home"); num2++; end_IL_02e4:; } catch (Exception ex2) { list.Add("loose gourd: " + ex2.Message); } } } } Prop val6 = null; List<Prop> allProps2 = Prop.allProps; if (allProps2 != null) { for (int num7 = 0; num7 < allProps2.Count; num7++) { Prop val7 = null; try { val7 = allProps2[num7]; } catch { } if ((Object)(object)val7 == (Object)null) { continue; } try { if (((object)val7.saveablePropName/*cast due to .constrained prefix*/).ToString() == tuple.Item3) { val6 = val7; break; } } catch { } } } List<string> list3 = new List<string>(tuple.Item4); if (IsTowerHub(tuple.Item1)) { list3.AddRange(TowerShared); } if ((Object)(object)val6 == (Object)null) { list.Add("key not found (" + tuple.Item3 + ")"); } else { try { PropHome currentHome = val6.currentHome; if ((Object)(object)currentHome != (Object)null && (Object)(object)currentHome != (Object)(object)val6.startHome) { string text = PlinthScopeOf(Api.TransformPath(((Component)currentHome).transform)); if (text != null) { string[] array = text.Split('|'); foreach (string text2 in array) { if (!list3.Contains(text2)) { list3.Add(text2); list.Add("key was in " + ((Object)currentHome).name + " → also resetting " + text2); } } } } } catch { } try { DropIfHeld(val6); KeyBlank val8 = KeyBlankOf(val6); if ((Object)(object)val8 != (Object)null) { SyncList<bool> cuts = val8.cuts; int num9 = cuts?.Count ?? 0; int num10 = 0; for (int num11 = 0; num11 < num9; num11++) { if (cuts[num11]) { cuts[num11] = false; num10++; } } try { val8.RefreshPropGroup(); } catch (Exception ex3) { list.Add("refresh group: " + ex3.Message); } try { Il2CppReferenceArray<KeyBlankCover> covers = val8.covers; int num12 = 0; if (covers != null) { foreach (KeyBlankCover item2 in (Il2CppArrayBase<KeyBlankCover>)(object)covers) { if ((Object)(object)item2 == (Object)null) { continue; } item2.currentStage = 0; ((Component)item2).gameObject.SetActive(true); Il2CppReferenceArray<Transform> stages = item2.stages; if (stages != null) { for (int num13 = 0; num13 < ((Il2CppArrayBase<Transform>)(object)stages).Length; num13++) { if ((Object)(object)((Il2CppArrayBase<Transform>)(object)stages)[num13] != (Object)null) { ((Component)((Il2CppArrayBase<Transform>)(object)stages)[num13]).gameObject.SetActive(num13 == 0); } } } num12++; } } list.Add($"{num12} cover(s) rewound"); } catch (Exception ex4) { list.Add("covers: " + ex4.Message); } try { List<PropGroup> propGroups = val6.propGroups; if (num9 > 0 && propGroups != null && val6.MatchesGroup(val8.finishedPropGroup)) { propGroups.Remove(val8.finishedPropGroup); list.Add("finished group removed"); } else if (num9 == 0) { list.Add("uncut key, finished group kept"); } } catch (Exception ex5) { list.Add("group strip: " + ex5.Message); } list.Add($"key: {num10}/{num9} cuts cleared"); } else { list.Add("key has no KeyBlank"); } try { val6.ServerSetUnpinned(); } catch { } if ((Object)(object)val6.startHome != (Object)null) { val6.ServerSetPinned(val6.startHome); } else { list.Add("key has no startHome"); } } catch (Exception ex6) { list.Add("key: " + ex6.Message); } } num4 = ResetStatesUnder(list3, list); if (list3.Exists((string r) => r.StartsWith("EndingGate/GatePositioner", StringComparison.OrdinalIgnoreCase))) { try { Finale.BounceEndingGates(); } catch (Exception ex7) { list.Add("gates: " + ex7.Message); } } int num14 = 0; try { foreach (UnlockTrailStation item3 in Object.FindObjectsOfType<UnlockTrailStation>(true)) { if ((Object)(object)item3 == (Object)null) { continue; } string path; try { path = Api.TransformPath(((Component)item3).transform); } catch { continue; } bool flag2 = false; foreach (string item4 in list3) { if (Under(path, item4)) { flag2 = true; break; } } if (!flag2) { continue; } try { PeckEffectTween peckTween = item3.peckTween; if ((Object)(object)peckTween != (Object)null) { peckTween.UpdateTween(0f); num14++; } } catch (Exception ex8) { list.Add("arrow: " + ex8.Message); } } } catch (Exception ex9) { list.Add("arrows: " + ex9.Message); } if (!IsTowerHub(tuple.Item1)) { try { foreach (TrackedPeckState item5 in Object.FindObjectsOfType<TrackedPeckState>(true)) { if ((Object)(object)item5 == (Object)null) { continue; } string text3; try { text3 = Api.TransformPath(((Component)item5).transform); } catch { continue; } if (!text3.EndsWith("MonumentMapFlag Black/MonumentMapFlagLogic", StringComparison.OrdinalIgnoreCase)) { continue; } int num15 = int.MinValue; try { PeckContext currentPeckContext = item5.currentPeckContext; if (currentPeckContext != null) { num15 = currentPeckContext.state; } } catch { } if (num15 != 0) { item5.SetState(0); list.Add("black map flag → incomplete"); } break; } } catch (Exception ex10) { list.Add("black flag: " + ex10.Message); } } foreach (string item6 in hashSet) { try { PuzzleReset.ResetStatic(item6, 0); num3++; } catch (Exception ex11) { list.Add(item6 + ": " + ex11.Message); } } if (hashSet.Count > 0) { _recheck.AddRange(hashSet); _recheckAt = Time.unscaledTime + 2f; } _h.Event("resethub", $"{tuple.Item1}: {num2} gourd(s) {(keepGourds ? "dropped at hub" : "returned")}, {num3} puzzle(s) reset, {num4} state(s) reset, {num14} arrow(s) rewound{((list.Count > 0) ? (" — " + string.Join("; ", list)) : "")}"); } private static string PlinthScopeOf(string homePath) { (string, string)[] plinthScopes = PlinthScopes; for (int i = 0; i < plinthScopes.Length; i++) { var (value, result) = plinthScopes[i]; if (homePath.IndexOf(value, StringComparison.OrdinalIgnoreCase) >= 0) { return result; } } List<string> list = new