Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of LateJoin v2.8.0
RepoLateJoin.dll
Decompiled a month ago
The result has been truncated due to the large size, download it to view full contents!
using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text.RegularExpressions; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using ExitGames.Client.Photon; using HarmonyLib; using Microsoft.CodeAnalysis; using Photon.Pun; using Photon.Realtime; using Photon.Voice; using Photon.Voice.PUN; using Photon.Voice.Unity; using RepoLateJoin.Client; using RepoLateJoin.Core; using RepoLateJoin.Guard; using RepoLateJoin.Join; using RepoLateJoin.Lobby; using RepoLateJoin.Menu; using Steamworks; using Steamworks.Data; using TMPro; using UnityEngine; using UnityEngine.Events; using UnityEngine.SceneManagement; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("RepoLateJoin")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Unified late-join mod for R.E.P.O.")] [assembly: AssemblyFileVersion("2.8.0.0")] [assembly: AssemblyInformationalVersion("2.8.0")] [assembly: AssemblyProduct("RepoLateJoin")] [assembly: AssemblyTitle("RepoLateJoin")] [assembly: AssemblyVersion("2.8.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 RepoLateJoin { [BepInPlugin("repolatejoin.unified", "RepoLateJoin", "2.8.0")] public sealed class Plugin : BaseUnityPlugin { private sealed class Runner : MonoBehaviour { private void Update() { if (PunGate.PhotonInitialised) { Handshake.Tick(); VoiceWatchdog.Tick(); CartRescue.Tick(); if (PunGate.Ready) { LobbyGate.Tick(); ModuleEstimate.Tick(); LocalCatchUp.Tick(); LevelSnapshot.Tick(); TruckScreen.Tick(); } } } private void OnGUI() { StatusOverlay.Draw(); } } internal const string Guid = "repolatejoin.unified"; internal const string Version = "2.8.0"; private static readonly Dictionary<string, string> Superseded = new Dictionary<string, string> { { "ozoka.latejoin", "LateJoin (Ozoka Gaming)" }, { "chaos.holz.laterepo", "LateRepo (Chaosholz)" }, { "laterepo.fix.runtime", "LateRepo Fix (AST Studios)" }, { "jp.laterepo.introfix", "LateRepo Intro Fix (CapackMods)" }, { "knightmike.latejoin", "Late Join (knightmike)" }, { "zichen.latejoinnow", "LateJoinNow (Zichen)" }, { "com.repo.latejoinfixed", "LateJoinFixed (Scorpio)" }, { "com.sunwu.anti_Item_teleport", "AntiItemTeleport (sunwu)" } }; private Harmony _harmony; private void Awake() { //IL_0072: 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_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Expected O, but got Unknown //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Expected O, but got Unknown Log.Attach(((BaseUnityPlugin)this).Logger); ModConfig.Bind(((BaseUnityPlugin)this).Config); WarnAboutSupersededMods(); Application.SetStackTraceLogType((LogType)3, (StackTraceLogType)0); Application.SetStackTraceLogType((LogType)2, (StackTraceLogType)0); TeleportGuard.Active = !Chainloader.PluginInfos.ContainsKey("com.sunwu.anti_Item_teleport"); PlayerLimit.Watch(); if (ModConfig.MaxPlayersEnabled.Value && Chainloader.PluginInfos.ContainsKey("Linkoid.Repo.RoboUnion")) { Log.Warn("RoboUnion also sets max players. Use either its MaxPlayers or ours (MorePlayer), not both - they overwrite each other. Turn MorePlayer / Enabled off to let RoboUnion decide."); } GameObject val = new GameObject("RepoLateJoin") { hideFlags = (HideFlags)61 }; Object.DontDestroyOnLoad((Object)val); JoinQueue.Attach((MonoBehaviour)(object)val.AddComponent<Runner>()); _harmony = new Harmony("repolatejoin.unified"); int num = ApplyPatches("RepoLateJoin.Patches"); PunGate.Initialised += delegate { ApplyPatches("RepoLateJoin.Patches.Late"); Handshake.Listen(); LevelSnapshot.Listen(); HostReports.Listen(); }; Log.Info("RepoLateJoin 2.8.0 loaded. Late join " + (ModConfig.On ? "on" : "off") + ", AntiCheat " + (TeleportGuard.Active ? "on" : "off (standalone AntiItemTeleport loaded)") + ", debug logging " + (Log.DebugOn ? "on" : "off") + "."); Log.Debug(num + " patch group(s) applied before Photon start."); } private int ApplyPatches(string ns) { Type[] array; try { array = (from t in typeof(Plugin).Assembly.GetTypes() where t.Namespace == ns && t.GetCustomAttributes(typeof(HarmonyPatch), inherit: true).Length != 0 select t).ToArray(); } catch (Exception ex) { Log.Error("Could not enumerate patch classes: " + ex); return 0; } int num = 0; Type[] array2 = array; foreach (Type type in array2) { try { _harmony.CreateClassProcessor(type).Patch(); num++; Log.Debug("Applied patch group " + type.Name + "."); } catch (Exception ex2) { Log.Warn("Patch group " + type.Name + " could not be applied (" + ex2.GetType().Name + ": " + ex2.Message + "). That feature is disabled; everything else still works."); } } return num; } private void WarnAboutSupersededMods() { List<string> list = new List<string>(); try { foreach (KeyValuePair<string, string> item in Superseded) { if (Chainloader.PluginInfos.ContainsKey(item.Key)) { list.Add(item.Value); } } } catch (Exception ex) { Log.Debug("Could not inspect the loaded plugin list: " + ex); return; } if (list.Count != 0) { Log.Warn("RepoLateJoin already contains the features of: " + string.Join(", ", list) + ". Remove " + ((list.Count == 1) ? "it" : "them") + " - running both leaves two mods fighting over the same lobby state."); } } } } namespace RepoLateJoin.Patches { [HarmonyPatch(typeof(PlayerDeathHead), "UpdateParticleColors")] internal static class DeathHeadColorPatch { private static Exception Finalizer(Exception __exception) { if (__exception == null) { return null; } if (!ModConfig.On) { return __exception; } Log.Debug("Suppressed a death head colour update that ran before the head finished arriving over the network (" + __exception.GetType().Name + "). Particle colours stay default until the next cosmetics change."); return null; } } [HarmonyPatch(typeof(RunManager), "ChangeLevel")] internal static class RunManagerChangeLevelPatch { private static readonly FieldInfo RestartingField = Reflect.Field(typeof(RunManager), "restarting"); private static void Prefix() { if (!PunGate.Ready) { return; } try { if ((!SemiFunc.MenuLevel() && !SemiFunc.IsMasterClientOrSingleplayer()) || ((Object)(object)RunManager.instance != (Object)null && Reflect.Get(RestartingField, RunManager.instance, fallback: false))) { return; } } catch (Exception ex) { Log.Debug("Could not evaluate the change-level guard: " + ex); return; } try { LobbyGate.BeginTransition(); PhotonCacheCleaner.ClearSceneCache(); PhotonCacheCleaner.ClearStaleLevelUpdateRpc(); LevelStateSync.Clear(); LevelEndWait.Begin(JoinQueue.StillLoading()); JoinQueue.Reset(); } catch (Exception ex2) { Log.Warn("Level-change housekeeping failed; the level change continues. " + ex2); } } } [HarmonyPatch(typeof(RunManager), "RestartScene")] internal static class RunManagerRestartScenePatch { private static void Prefix(RunManager __instance) { try { LevelEndWait.Check(__instance); } catch (Exception ex) { Log.Debug("Level-end wait check failed: " + ex); } } } [HarmonyPatch(typeof(ItemEquippable), "RPC_RequestEquip")] internal static class ItemRequestEquipPatch { private static bool Prefix(int physGrabberPhotonViewID) { try { return !InventoryGuard.HoldBack(physGrabberPhotonViewID); } catch (Exception ex) { Log.Debug("Equip request check failed: " + ex); return true; } } } [HarmonyPatch(typeof(ItemEquippable), "RPC_UpdateItemState")] internal static class ItemUpdateStatePatch { private static bool Prefix(ItemEquippable __instance, int state) { try { if (state == 2) { InventorySlotPatch.Register(); } return InventoryGuard.AllowEquipState(__instance, state); } catch (Exception ex) { Log.Debug("Item state check failed: " + ex); return true; } } } [HarmonyPatch(typeof(MenuPageServerListCreateNew), "ButtonConfirm")] internal static class ServerNameTypedPatch { [HarmonyPriority(800)] private static void Prefix(MenuPageServerListCreateNew __instance) { try { RoomName.Typed(__instance); } catch (Exception ex) { Log.Debug("Could not read the typed server name: " + ex); } } } [HarmonyPatch(typeof(TruckScreenText), "MessageSendCustomRPC")] internal static class TruckScreenMessagePatch { private static bool Prefix(string message, PhotonMessageInfo _info) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) try { return TruckScreen.AllowMessage(message, _info); } catch (Exception ex) { Log.Debug("Truck screen message check failed: " + ex); return true; } } } [HarmonyPatch(typeof(StaticGrabObject), "GrabStartedRPC")] internal static class StaticGrabStartedPatch { private static bool Prefix(int playerPhotonID) { try { return TruckScreen.AllowGrabReport(playerPhotonID); } catch (Exception ex) { Log.Debug("Grab report check failed: " + ex); return true; } } } [HarmonyPatch(typeof(StaticGrabObject), "GrabEndedRPC")] internal static class StaticGrabEndedPatch { private static bool Prefix(int playerPhotonID) { try { return TruckScreen.AllowGrabReport(playerPhotonID); } catch (Exception ex) { Log.Debug("Grab report check failed: " + ex); return true; } } } [HarmonyPatch(typeof(LevelGenerator), "Start")] internal static class LevelGeneratorStartPatch { private static void Prefix(LevelGenerator __instance) { PunGate.Mark(); IntroSkip.Reset(); ModuleEstimate.Reset(); LocalCatchUp.Reset(); JoinStatus.Clear(); EnemyAwareness.ForgetDestroyed(); if (!ModConfig.On) { return; } try { PhotonCacheCleaner.ClearStaleGenerationRpcs(__instance); } catch (Exception ex) { Log.Debug("Could not clear stale generation RPCs: " + ex); } } private static Exception Finalizer(Exception __exception) { if (__exception == null) { return null; } if (!IsSingleplayerPoolMiss(__exception)) { return __exception; } Log.Debug("Suppressed an outdated RunManager.singleplayerPool access during level generation."); return null; } private static bool IsSingleplayerPoolMiss(Exception e) { while (e != null) { if (e is MissingFieldException && e.ToString().IndexOf("singleplayerPool", StringComparison.OrdinalIgnoreCase) >= 0) { return true; } e = e.InnerException; } return false; } } [HarmonyPatch(typeof(Module), "ModuleConnectionSetRPC")] internal static class ModuleConnectionSetRpcPatch { private static bool Prefix(Module __instance, bool _top, bool _bottom, bool _right, bool _left, bool _first, PhotonMessageInfo _info) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) try { return ModuleEstimate.OnHostData(__instance, new bool[5] { _top, _bottom, _right, _left, _first }, _info); } catch (Exception ex) { Log.Debug("Room connection comparison failed: " + ex); return true; } } } [HarmonyPatch(typeof(LoadingUI), "LevelAnimationStart")] internal static class LoadingUiAnimationStartPatch { private static bool Prefix(LoadingUI __instance) { return IntroSkip.Prefix(__instance); } } [HarmonyPatch(typeof(PlayerAvatar), "Spawn")] internal static class PlayerAvatarSpawnPatch { private static readonly FieldInfo SpawnedField = Reflect.Field(typeof(PlayerAvatar), "spawned"); private static bool Prefix(PlayerAvatar __instance, ref Vector3 position, ref Quaternion rotation) { if (!ModConfig.On || (Object)(object)__instance == (Object)null) { return true; } if (!Reflect.Get(SpawnedField, __instance, fallback: false)) { JoinSpawn.Choose(__instance, ref position, ref rotation); return true; } Log.Debug("Blocked a re-spawn of an already-spawned player so late joins do not teleport the party."); return false; } } [HarmonyPatch(typeof(PlayerAvatar), "Awake")] internal static class PlayerAvatarAwakePatch { private static void Postfix(PlayerAvatar __instance) { try { if (ModConfig.On && (Object)(object)__instance != (Object)null) { EnemyAwareness.RegisterAllPlayers(); } } catch (Exception ex) { Log.Debug("Enemy registration at Awake failed: " + ex); } } } [HarmonyPatch(typeof(PlayerAvatar), "Start")] internal static class PlayerAvatarStartPatch { private static void Postfix(PlayerAvatar __instance) { if (PunGate.Ready && !((Object)(object)__instance == (Object)null) && !PhotonNetwork.IsMasterClient && !((Object)(object)__instance.photonView == (Object)null) && __instance.photonView.IsMine) { IntroSkip.OnLocalAvatarStart(__instance); } } } [HarmonyPatch(typeof(SteamManager), "LockLobby")] internal static class SteamManagerLockLobbyPatch { private static bool Prefix() { if (!PunGate.Ready || LobbyGate.SelfLocking) { return true; } if (!LobbyGate.JoinAllowedNow()) { return true; } Log.Debug("Kept the lobby unlocked because late joining is currently allowed."); LobbyGate.Apply("lock suppressed", force: true); return false; } } [HarmonyPatch(typeof(SteamManager), "OnLobbyEntered")] internal static class SteamLobbyEnteredPatch { private static void Prefix(Lobby _lobby, ref Lobby ___currentLobby) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) SteamLobbyJoin.StayIn(_lobby, ref ___currentLobby); } } [HarmonyPatch(typeof(RunManagerPUN), "SendJoinSteamLobby")] internal static class SteamLobbySendPatch { private static bool Prefix() { try { return !SteamLobbyJoin.SendJoin(); } catch (Exception ex) { Log.Debug("Steam lobby send failed, using the game's own: " + ex); return true; } } } [HarmonyPatch(typeof(NetworkManager), "OnEnable")] internal static class NetworkManagerOnEnablePatch { private static void Postfix() { PunGate.Mark(); } } [HarmonyPatch(typeof(NetworkManager), "OnPlayerEnteredRoom")] internal static class NetworkManagerPlayerEnteredPatch { private static void Postfix(Player newPlayer) { if (PunGate.PhotonInitialised) { SteamLobbyJoin.OnPlayerEntered(newPlayer); } if (PunGate.Ready) { JoinQueue.OnPlayerEntered(newPlayer); } } } [HarmonyPatch(typeof(NetworkManager), "OnPlayerLeftRoom")] internal static class NetworkManagerPlayerLeftPatch { private static void Postfix(Player otherPlayer) { if (PunGate.Ready) { JoinQueue.OnPlayerLeft(otherPlayer); } } } [HarmonyPatch(typeof(EnemyOnScreen), "OnEnable")] internal static class EnemyOnScreenEnablePatch { private static void Prefix(EnemyOnScreen __instance) { EnemyAwareness.EnsureFor(__instance); } } [HarmonyPatch(typeof(EnemyVision), "OnEnable")] internal static class EnemyVisionEnablePatch { private static void Prefix(EnemyVision __instance) { EnemyAwareness.EnsureFor(__instance); } } [HarmonyPatch(typeof(EnemyOnScreen), "GetOnScreen")] internal static class EnemyGetOnScreenPatch { private static void Prefix(EnemyOnScreen __instance, PlayerAvatar _playerAvatar) { if ((Object)(object)_playerAvatar != (Object)null && (Object)(object)_playerAvatar.photonView != (Object)null && _playerAvatar.photonView.ViewID != 0) { __instance.PlayerAdded(_playerAvatar.photonView.ViewID); } } } [HarmonyPatch] internal static class GrabObjectDestroyPatch { private static IEnumerable<MethodBase> TargetMethods() { string[] array = new string[2] { "PhysGrabObject", "StaticGrabObject" }; for (int i = 0; i < array.Length; i++) { MethodInfo methodInfo = Reflect.Method(Reflect.TypeByName(array[i]), "DestroyPhysGrabObjectRPC"); if (methodInfo != null) { yield return methodInfo; } } } private static void Prefix(object __instance) { if (PunGate.Ready && PhotonNetwork.IsMasterClient) { Component val = (Component)((__instance is Component) ? __instance : null); if (val != null && (Object)(object)val != (Object)null) { LevelStateSync.RememberDestroyed(val.GetComponent<PhotonView>()); } } } } [HarmonyPatch(typeof(GameManager), "Awake")] internal static class GameManagerAwakePatch { private static void Postfix() { PlayerLimit.Apply(); } } [HarmonyPatch(typeof(MenuPageEsc), "Start")] internal static class MenuPageEscStartPatch { private static void Postfix(MenuPageEsc __instance) { InviteButton.TryAdd(__instance); } } [HarmonyPatch(typeof(PhysGrabObject), "Teleport", new Type[] { typeof(Vector3), typeof(Quaternion) })] internal static class TeleportDepthPatch { private static void Prefix() { TeleportGuard.TeleportEnter(); } private static Exception Finalizer(Exception __exception) { TeleportGuard.TeleportLeave(); return __exception; } } [HarmonyPatch(typeof(NetworkManager), "OnEventReceivedCustom")] internal static class FakeKickPatch { private static bool Prefix(EventData photonEvent) { if (photonEvent == null || photonEvent.Code != 199 || photonEvent.Sender <= 0) { return true; } Player masterClient = PhotonNetwork.MasterClient; if (masterClient != null && photonEvent.Sender == masterClient.ActorNumber) { return true; } Room currentRoom = PhotonNetwork.CurrentRoom; Player sender = ((currentRoom != null) ? currentRoom.GetPlayer(photonEvent.Sender, false) : null); HostReports.Warn("fake-kick:" + photonEvent.Sender, () => "[Blocked] a fake kick from " + ((sender != null) ? TeleportGuard.Describe(sender) : ("#" + photonEvent.Sender)) + " - only the host can kick."); return false; } } [HarmonyPatch(typeof(PhysGrabObject), "SetPositionRPC")] internal static class SetPositionRpcPatch { private static bool Prefix(PhysGrabObject __instance) { try { return TeleportGuard.AllowSetPosition(__instance); } catch (Exception ex) { Log.Debug("SetPositionRPC check failed: " + ex); return true; } } } [HarmonyPatch(typeof(PhysGrabObject), "Update")] internal static class VoidRescuePatch { private const float VoidY = -50f; private static void Prefix(PhysGrabObject __instance) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) if (!(((Component)__instance).transform.position.y >= -50f) && TeleportGuard.Active && TeleportGuard.CanMoveObjects && !__instance.dead && __instance.spawned && !((Object)(object)((Component)__instance).GetComponent<ValuableObject>() == (Object)null) && TeleportGuard.MoveToTruck(__instance)) { HostReports.Share("rescue", "[Rescued] " + HostReports.NameOf((Object)(object)__instance) + " -> truck"); } } } [HarmonyPatch(typeof(PhysGrabObjectImpactDetector), "DestroyObject")] internal static class CartRescuePatch { private static bool Prefix(PhysGrabObjectImpactDetector __instance) { if (CartRescue.On && CartRescue.Protects(__instance)) { return !CartRescue.Rescue(((Component)__instance).GetComponent<PhysGrabObject>()); } return true; } } [HarmonyPatch(typeof(GumballValuable), "UpdateHypnosisLinesList")] internal static class GumballValuableHypnosisPatch { private static readonly FieldInfo LinesField = Reflect.Field(typeof(GumballValuable), "hypnosisLines"); private static readonly FieldInfo InSightField = Reflect.Field(typeof(GumballValuable), "playerInSight"); private static readonly FieldInfo SeenField = Reflect.Field(typeof(GumballValuable), "previouslySeenList"); private static void Prefix(GumballValuable __instance) { if (!ModConfig.On || (Object)(object)__instance == (Object)null) { return; } try { List<PlayerAvatar> list = SemiFunc.PlayerGetList(); if (list != null) { GrowFlags(InSightField, __instance, list.Count); GrowFlags(SeenField, __instance, list.Count); GrowLines(__instance, list); } } catch (Exception ex) { Log.Debug("Could not grow the gumball hypnosis lists: " + ex); } } private static void GrowFlags(FieldInfo field, GumballValuable instance, int target) { if (field?.GetValue(instance) is List<bool> list) { while (list.Count < target) { list.Add(item: false); } } } private static void GrowLines(GumballValuable instance, List<PlayerAvatar> players) { //IL_00c6: Unknown result type (might be due to invalid IL or missing references) //IL_00d7: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Unknown result type (might be due to invalid IL or missing references) if (!(LinesField?.GetValue(instance) is List<GameObject> list) || list.Count >= players.Count) { return; } GameObject hypnosisLine = instance.hypnosisLine; Transform hypnosisLinesParenTransform = instance.HypnosisLinesParenTransform; if ((Object)(object)hypnosisLine == (Object)null) { return; } int num = 0; for (int i = list.Count; i < players.Count; i++) { PlayerAvatar val = players[i]; if ((Object)(object)val == (Object)null) { list.Add(null); continue; } GameObject val2 = Object.Instantiate<GameObject>(hypnosisLine, ((Component)instance).transform.position, Quaternion.identity); Transform val3 = null; try { PlayerLocalCamera localCamera = val.localCamera; val3 = ((localCamera != null) ? localCamera.GetOverrideTransform() : null); } catch { } if ((Object)(object)val3 != (Object)null) { val2.transform.SetParent(val3); val2.transform.localPosition = Vector3.zero; val2.transform.localRotation = Quaternion.identity; } EyeLines component = val2.GetComponent<EyeLines>(); if ((Object)(object)component != (Object)null) { component.InitializeLine(val); } if ((Object)(object)hypnosisLinesParenTransform != (Object)null) { val2.transform.SetParent(hypnosisLinesParenTransform); } val2.SetActive(false); list.Add(val2); num++; } if (num > 0) { Log.Debug("Grew a gumball valuable's hypnosis line list by " + num + " so it stops throwing once somebody joins mid-level."); } } } [HarmonyPatch(typeof(RunManager), "UpdateLevel")] internal static class InventorySlotPatch { private static void Postfix() { Register(); } internal static void Register() { if (!ModConfig.On) { return; } try { Inventory instance = Inventory.instance; if ((Object)(object)instance == (Object)null || !instance.GetAllSpots().Exists((InventorySpot s) => (Object)(object)s == (Object)null) || SemiFunc.MenuLevel() || SemiFunc.RunIsArena()) { return; } int num = 0; InventorySpot[] array = Object.FindObjectsOfType<InventorySpot>(true); foreach (InventorySpot val in array) { if (!((Object)(object)instance.GetSpotByIndex(val.inventorySpotIndex) != (Object)null)) { instance.InventorySpotAddAtIndex(val, val.inventorySpotIndex); num++; } } if (num > 0) { Log.Debug("Registered " + num + " inventory slot(s) the join skipped."); } } catch (Exception ex) { Log.Debug("Could not register inventory slots: " + ex); } } } [HarmonyPatch(typeof(PlayerVoiceChat), "ToggleMixer")] internal static class VoiceMixerGuardPatch { private static bool Prefix(AudioSource ___audioSource) { return (Object)(object)___audioSource != (Object)null; } } [HarmonyPatch(typeof(PlayerVoiceChat), "Start")] internal static class VoiceStartPatch { private static void Postfix(PlayerVoiceChat __instance, PlayerAvatar ___playerAvatar) { try { if (!((Object)(object)___playerAvatar == (Object)null) && !SemiFunc.MenuLevel()) { __instance.ToggleMixer(false, false); Log.Debug("Linked " + ((Object)___playerAvatar).name + "'s voice that was claimed before it started."); } } catch (Exception ex) { Log.Debug("Could not link an early voice: " + ex); } } } [HarmonyPatch] internal static class SteamIdWaitPatch { private static readonly FieldInfo GrabberAvatarField = Reflect.Field(typeof(PhysGrabber), "playerAvatar"); private static IEnumerable<MethodBase> TargetMethods() { MethodInfo[] array = new MethodInfo[4] { Reflect.Method(typeof(PlayerAvatar), "LateStart"), Reflect.Method(typeof(PhysGrabber), "LateStart"), Reflect.Method(typeof(PlayerTumble), "Setup"), Reflect.Method(typeof(PlayerDeathHead), "Setup") }; foreach (MethodInfo methodInfo in array) { if (methodInfo != null) { yield return methodInfo; } } } private static void Postfix(object __instance, ref IEnumerator __result) { PlayerAvatar val = (PlayerAvatar)((__instance is PlayerAvatar) ? __instance : null); Func<PlayerAvatar> owner; if (val == null) { PhysGrabber val2 = (PhysGrabber)((__instance is PhysGrabber) ? __instance : null); if (val2 == null) { PlayerTumble val3 = (PlayerTumble)((__instance is PlayerTumble) ? __instance : null); if (val3 == null) { PlayerDeathHead val4 = (PlayerDeathHead)((__instance is PlayerDeathHead) ? __instance : null); if (val4 == null || !SemiFunc.IsMasterClient()) { return; } owner = () => val4.playerAvatar; } else { if (!SemiFunc.IsMasterClient()) { return; } owner = () => val3.playerAvatar; } } else { owner = () => Reflect.Get<PlayerAvatar>(GrabberAvatarField, val2); } } else { owner = () => val; } __result = AfterSteamId(owner, __result); } private static IEnumerator AfterSteamId(Func<PlayerAvatar> owner, IEnumerator original) { float until = Time.time + 10f; while (Time.time < until && string.IsNullOrEmpty(Read(owner))) { yield return (object)new WaitForSeconds(0.2f); } while (original.MoveNext()) { yield return original.Current; } } private static string Read(Func<PlayerAvatar> owner) { try { PlayerAvatar val = owner(); return ((Object)(object)val != (Object)null) ? SemiFunc.PlayerGetSteamID(val) : null; } catch { return null; } } } [HarmonyPatch(typeof(TruckScreenText), "NextLineRPC")] internal static class TruckScreenLinePatch { private static readonly FieldInfo PageField = Reflect.Field(typeof(TruckScreenText), "currentPageIndex"); private static bool Prefix(TruckScreenText __instance, int index, int _currentLineIndex) { int num = Reflect.Get(PageField, __instance, -1); if (num >= 0 && num < __instance.pages.Count) { List<TextLine> textLines = __instance.pages[num].textLines; if (_currentLineIndex >= 0 && _currentLineIndex < textLines.Count && index >= 0 && index < textLines[_currentLineIndex].textLines.Count) { return true; } } Log.Debug("Skipped truck screen line " + _currentLineIndex + "/" + index + " not on our page " + num + "."); return false; } } [HarmonyPatch(typeof(EnemyBirthdayBoy), "RemoveBalloonRPC")] internal static class BalloonRemovePatch { private static bool Prefix(EnemyBirthdayBoy __instance, Vector3 _position) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) return __instance.balloons.ContainsKey(_position); } } [HarmonyPatch(typeof(EnemyBirthdayBoy), "SpawnBalloonRPC")] internal static class BalloonSpawnPatch { private static bool Prefix(EnemyBirthdayBoy __instance, Vector3 _keyPos) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) if (__instance.balloons.TryGetValue(_keyPos, out var value)) { return (Object)(object)value == (Object)null; } return true; } } [HarmonyPatch(typeof(ExtractionPoint), "ActivateTheFirstExtractionPointAutomaticallyWhenAPlayerLeaveTruck")] internal static class ExtractionAutoPressPatch { private static bool Prefix() { if (!IntroSkip.LateJoined || PhotonNetwork.IsMasterClient) { return true; } Log.Debug("Left the first extraction point alone: we joined this level late."); return false; } } [HarmonyPatch(typeof(LevelPoint), "Start")] internal static class LevelPointLinkPatch { private static void Postfix(LevelPoint __instance) { if (ModConfig.On && __instance.ModuleConnect && !((Object)(object)LevelGenerator.Instance == (Object)null) && LevelGenerator.Instance.Generated) { ((MonoBehaviour)__instance).StartCoroutine(Relink(__instance)); } } private static IEnumerator Relink(LevelPoint point) { yield return null; if ((Object)(object)point == (Object)null || (Object)(object)LevelGenerator.Instance == (Object)null) { yield break; } Transform transform = ((Component)point).transform; foreach (LevelPoint levelPathPoint in LevelGenerator.Instance.LevelPathPoints) { if ((Object)(object)levelPathPoint == (Object)null || (Object)(object)levelPathPoint == (Object)(object)point || !levelPathPoint.ModuleConnect || point.ConnectedPoints.Contains(levelPathPoint)) { continue; } Transform transform2 = ((Component)levelPathPoint).transform; if (Vector3.Distance(transform.position, transform2.position) < 15f && Vector3.Dot(transform2.forward, transform.forward) <= -0.8f) { Vector3 forward = transform2.forward; Vector3 val = transform.position - transform2.position; if (Vector3.Dot(forward, ((Vector3)(ref val)).normalized) > 0.8f) { point.ConnectedPoints.Add(levelPathPoint); } } } } } [HarmonyPatch] internal static class LightManagerActivationPatch { private static IEnumerable<MethodBase> TargetMethods() { Type type = Reflect.TypeByName("LightManager"); string[] array = new string[2] { "HandleLightActivation", "HandleEmissionActivation" }; foreach (string name in array) { MethodInfo methodInfo = Reflect.Method(type, name); if (methodInfo != null) { yield return methodInfo; } } } private static Exception Finalizer(Exception __exception, MethodBase __originalMethod) { if (__exception == null) { return null; } if (!ModConfig.On) { return __exception; } Log.Debug("Suppressed a " + __exception.GetType().Name + " in LightManager." + (__originalMethod?.Name ?? "?") + " so the rest of the level's lights still turn on."); return null; } } [HarmonyPatch] internal static class ReviveItemTargetPatch { private static IEnumerable<MethodBase> TargetMethods() { yield return AccessTools.Method(typeof(ItemReviveItem), "Update", (Type[])null, (Type[])null); yield return AccessTools.Method(typeof(ItemReviveItem), "FixedUpdate", (Type[])null, (Type[])null); } private static void Prefix(ItemReviveItem __instance, ref PlayerAvatar ___playerTarget, ref bool ___hasTarget) { if (!___hasTarget || (Object)(object)___playerTarget != (Object)null) { return; } ___playerTarget = null; ___hasTarget = false; if (SemiFunc.IsMultiplayer() && PhotonNetwork.IsMasterClient) { PhotonView component = ((Component)__instance).GetComponent<PhotonView>(); if (component != null) { component.RPC("UpdatePlayerTargetRPC", (RpcTarget)0, new object[1] { -1 }); } } } } [HarmonyPatch(typeof(SpectateCamera), "StopSpectate")] internal static class SpectateStopPatch { private static Exception Finalizer(SpectateCamera __instance, Exception __exception) { if (__exception == null) { return null; } if ((Object)(object)__instance != (Object)null) { if ((Object)(object)__instance.normalTransformPivot != (Object)null) { Object.Destroy((Object)(object)((Component)__instance.normalTransformPivot).gameObject); } Object.Destroy((Object)(object)((Component)__instance).gameObject); } Log.Debug("Spectate camera cleanup threw (" + __exception.Message + "); finished it so the revive completes."); return null; } } [HarmonyPatch(typeof(StaticGrabObject), "Update")] internal static class StaticGrabLeaverPatch { private static void Prefix(StaticGrabObject __instance) { if (__instance.playerGrabbing.Count > 0) { __instance.playerGrabbing.RemoveAll((PhysGrabber p) => (Object)(object)p == (Object)null || (Object)(object)p.physGrabPoint == (Object)null); } } } [HarmonyPatch] internal static class TornDownPlayerPatch { private static IEnumerable<MethodBase> TargetMethods() { (Type, string)[] array = new(Type, string)[7] { (typeof(PlayerEyes), "LookAtTransform"), (typeof(PlayerEyes), "ClosestPhysGrabPoint"), (typeof(PhysGrabBeam), "DrawCurve"), (typeof(PhysGrabber), "ObjectTurning"), (typeof(PhysGrabber), "OverrideDisableGrabLookAt"), (typeof(ItemUpgradePlayerTumbleWingsLogic), "FixedUpdate"), (typeof(SlowWalkerAttack), "SuckInListPlayerUpdate") }; for (int i = 0; i < array.Length; i++) { (Type, string) tuple = array[i]; Type item = tuple.Item1; string item2 = tuple.Item2; MethodInfo methodInfo = Reflect.Method(item, item2); if (methodInfo != null) { yield return methodInfo; } } } private static Exception Finalizer(Exception __exception) { if (!(__exception is NullReferenceException) && !(__exception is MissingReferenceException)) { return __exception; } return null; } } [HarmonyPatch(typeof(PlayerHealth), "Update")] internal static class PlayerHealthUpdatePatch { private static bool Prefix(PlayerHealth __instance, ref PlayerAvatar ___playerAvatar) { if ((Object)(object)___playerAvatar != (Object)null) { return true; } if (!__instance.isMenuAvatar || (Object)(object)PlayerAvatar.instance == (Object)null) { return false; } ___playerAvatar = PlayerAvatar.instance; return true; } } [HarmonyPatch] internal static class AmbientLightAfterLeavePatch { private static IEnumerable<MethodBase> TargetMethods() { string[] array = new string[2] { "AmbientLightLogic", "FogLogic" }; foreach (string name in array) { MethodInfo methodInfo = Reflect.Method(typeof(EnvironmentDirector), name); if (methodInfo != null) { yield return methodInfo; } } } private static bool Prefix() { if ((Object)(object)PlayerController.instance != (Object)null) { return (Object)(object)PlayerController.instance.playerAvatarScript != (Object)null; } return false; } } [HarmonyPatch(typeof(PhysGrabObject), "PhysicsGrabbingManipulation")] internal static class GrabberLeaverPatch { private static void Prefix(PhysGrabObject __instance) { if (__instance.playerGrabbing.Count > 0) { __instance.playerGrabbing.RemoveAll((PhysGrabber g) => (Object)(object)g == (Object)null || (Object)(object)g.playerAvatar == (Object)null); } } } [HarmonyPatch(typeof(PlayerAvatar), "Awake")] internal static class GrabberViewPatch { private static void Postfix(PlayerAvatar __instance) { PhysGrabber component = ((Component)__instance).GetComponent<PhysGrabber>(); if (!((Object)(object)component == (Object)null)) { if ((Object)(object)component.photonView == (Object)null) { component.photonView = __instance.photonView; } if ((Object)(object)component.playerAvatar == (Object)null) { component.playerAvatar = __instance; } } } } [HarmonyPatch(typeof(PlayerDeathEffects), "GetColors")] internal static class DeathColorsPatch { private static Exception Finalizer(Exception __exception, ref GradientColorKey[] __result) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) if (__exception == null) { return null; } __result = (GradientColorKey[])(object)new GradientColorKey[2] { new GradientColorKey(Color.white, 0f), new GradientColorKey(Color.gray, 1f) }; Log.Debug("Death effect colours were not there yet (" + __exception.GetType().Name + "); used plain ones."); return null; } } [HarmonyPatch(typeof(EnemySlowMouthAttached), "Update")] internal static class SpewerMouthPatch { private static readonly FieldInfo EnemyField = Reflect.Field(typeof(EnemySlowMouthAttached), "enemySlowMouth"); private static Exception Finalizer(EnemySlowMouthAttached __instance, Exception __exception) { if (!(__exception is NullReferenceException) && !(__exception is MissingReferenceException)) { return __exception; } if ((Object)(object)__instance == (Object)null || (Object)(object)Reflect.Get<EnemySlowMouth>(EnemyField, __instance) != (Object)null) { return null; } Transform[] array = (Transform[])(object)new Transform[3] { __instance.otherTopJawTransform, __instance.otherBotJawTransform, __instance.localParentTransform }; foreach (Transform val in array) { if ((Object)(object)val != (Object)null) { Object.Destroy((Object)(object)((Component)val).gameObject); } } Object.Destroy((Object)(object)((Component)__instance).gameObject); Log.Debug("Removed a Spewer mouth left on a player after its enemy was gone."); return null; } } [HarmonyPatch] internal static class ExtractionActivationPatch { private static readonly FieldInfo StateField = Reflect.Field(typeof(ExtractionPoint), "currentState"); private static IEnumerable<MethodBase> TargetMethods() { yield return AccessTools.Method(typeof(RoundDirector), "RequestExtractionPointActivationRPC", (Type[])null, (Type[])null); yield return AccessTools.Method(typeof(RoundDirector), "ExtractionPointActivate", (Type[])null, (Type[])null); } private static bool Prefix(int photonViewID) { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Invalid comparison between Unknown and I4 if (!SemiFunc.IsMultiplayer() || !PhotonNetwork.IsMasterClient) { return true; } PhotonView val = PhotonView.Find(photonViewID); ExtractionPoint val2 = (((Object)(object)val != (Object)null) ? ((Component)val).GetComponent<ExtractionPoint>() : null); if ((Object)(object)val2 != (Object)null && !val2.isLocked && (int)Reflect.Get<State>(StateField, val2, (State)1) == 1) { return true; } Log.Debug("Ignored a request to start extraction point " + photonViewID + ": " + (((Object)(object)val2 == (Object)null) ? "no such point" : (val2.isLocked ? "locked" : "not ready")) + "."); return false; } } [HarmonyPatch(typeof(UnityLogSource), "OnUnityLogMessageReceived")] internal static class KinematicWarningPatch { private static bool Prefix(string message) { if (message != null) { if (message.IndexOf("velocity of a kinematic body is not supported", StringComparison.Ordinal) < 0) { return message.IndexOf("BoxCollider does not support negative scale or size", StringComparison.Ordinal) < 0; } return false; } return true; } } [HarmonyPatch(typeof(PlayerNameChecker), "Update")] internal static class PlayerNameTagPatch { private const float Grace = 5f; private static readonly FieldInfo TagField = Reflect.Field(typeof(PlayerAvatar), "worldSpaceUIPlayerName"); private static readonly FieldInfo LocalField = Reflect.Field(typeof(PlayerAvatar), "isLocal"); private static readonly Dictionary<PlayerAvatar, float> MissingSince = new Dictionary<PlayerAvatar, float>(); private static float _next; private static bool Prefix() { if ((Object)(object)Camera.main == (Object)null) { return false; } if (Time.unscaledTime < _next) { return true; } _next = Time.unscaledTime + 1f; try { GiveMissingTags(); } catch (Exception ex) { Log.Debug("Could not give a player their name tag: " + ex); } return true; } private static void GiveMissingTags() { if ((Object)(object)WorldSpaceUIParent.instance == (Object)null || (Object)(object)GameDirector.instance == (Object)null || SemiFunc.MenuLevel()) { return; } HashSet<PlayerAvatar> hashSet = new HashSet<PlayerAvatar>(); foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if (!((Object)(object)player == (Object)null) && !Reflect.Get(LocalField, player, fallback: true) && !((Object)(object)Reflect.Get<WorldSpaceUIPlayerName>(TagField, player) != (Object)null) && !string.IsNullOrEmpty(SemiFunc.PlayerGetName(player))) { hashSet.Add(player); if (!MissingSince.TryGetValue(player, out var value)) { value = (MissingSince[player] = Time.unscaledTime); } if (!(Time.unscaledTime - value < 5f)) { WorldSpaceUIParent.instance.PlayerName(player); Log.Debug("Gave " + SemiFunc.PlayerGetName(player) + " the name tag their start never made."); } } } foreach (PlayerAvatar item in new List<PlayerAvatar>(MissingSince.Keys)) { if (!hashSet.Contains(item)) { MissingSince.Remove(item); } } } private static Exception Finalizer(Exception __exception) { if (!(__exception is NullReferenceException) && !(__exception is MissingReferenceException)) { return __exception; } return null; } } [HarmonyPatch(typeof(EnemyOogly), "StateCeilingRoam")] internal static class OoglyCeilingRoamPatch { private static readonly FieldInfo PointField = Reflect.Field(typeof(EnemyOogly), "currentLevelPoint"); private static readonly FieldInfo StartRoomField = Reflect.Field(typeof(LevelPoint), "inStartRoom"); private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions) { List<CodeInstruction> list = new List<CodeInstruction>(instructions); if (PointField == null || StartRoomField == null) { return list; } for (int i = 0; i + 1 < list.Count; i++) { if (CodeInstructionExtensions.LoadsField(list[i], PointField, false) && CodeInstructionExtensions.LoadsField(list[i + 1], StartRoomField, false)) { list[i].opcode = OpCodes.Call; list[i].operand = AccessTools.Method(typeof(OoglyCeilingRoamPatch), "InStartRoom", (Type[])null, (Type[])null); list.RemoveAt(i + 1); } } return list; } public static bool InStartRoom(EnemyOogly oogly) { LevelPoint val = Reflect.Get<LevelPoint>(PointField, oogly); if ((Object)(object)val != (Object)null) { return Reflect.Get(StartRoomField, val, fallback: false); } return false; } } } namespace RepoLateJoin.Patches.Late { [HarmonyPatch(typeof(NetworkConnect), "OnJoinedRoom")] internal static class NetworkConnectJoinedRoomPatch { private static void Postfix() { JoinRetry.Joined(); TeleportGuard.NewRoom(); Handshake.Greet(); } } [HarmonyPatch(typeof(NetworkConnect), "OnJoinRoomFailed")] internal static class JoinRetryPatch { private static bool Prefix(NetworkConnect __instance, short returnCode, string _cause) { try { return JoinRetry.OnJoinFailed(__instance, returnCode, _cause); } catch (Exception ex) { Log.Debug("Join retry check failed: " + ex); return true; } } } [HarmonyPatch(typeof(NetworkConnect), "OnConnectedToMaster")] internal static class ConnectionTimeoutPatch { private static void Prefix() { try { LoadBalancingClient networkingClient = PhotonNetwork.NetworkingClient; LoadBalancingPeer val = ((networkingClient != null) ? networkingClient.LoadBalancingPeer : null); if (val != null) { ((PhotonPeer)val).DisconnectTimeout = Math.Max(((PhotonPeer)val).DisconnectTimeout, 30000); ((PhotonPeer)val).SentCountAllowance = Math.Max(((PhotonPeer)val).SentCountAllowance, 10); } } catch (Exception ex) { Log.Debug("Could not raise the connection timeout: " + ex); } } } [HarmonyPatch(typeof(LoadBalancingClient), "OpCreateRoom")] internal static class RoomNameCreatePatch { [HarmonyPriority(0)] private static void Prefix(EnterRoomParams enterRoomParams) { try { RoomName.Create(enterRoomParams?.RoomOptions?.CustomRoomProperties); } catch (Exception ex) { Log.Debug("Could not set the server name: " + ex); } } } [HarmonyPatch(typeof(Room), "SetCustomProperties")] internal static class RoomNameKeepPatch { [HarmonyPriority(0)] private static void Prefix(Hashtable propertiesToSet) { try { RoomName.Keep(propertiesToSet); } catch (Exception ex) { Log.Debug("Could not keep the server name: " + ex); } } } [HarmonyPatch(typeof(PhotonNetwork), "ExecuteRpc")] internal static class RpcSenderPatch { [HarmonyPriority(800)] private static bool Prefix(Hashtable rpcData, Player sender) { RpcContext.Enter(sender); try { return !TeleportGuard.ShouldDrop(rpcData, sender) && !ForeignRpc.Drop(rpcData); } catch (Exception ex) { Log.Debug("RPC check failed: " + ex); return true; } } private static Exception Finalizer(Exception __exception) { RpcContext.Leave(); return __exception; } } [HarmonyPatch] internal static class PublishUserIdPatch { private static IEnumerable<MethodBase> TargetMethods() { yield return AccessTools.Method(typeof(LoadBalancingClient), "OpCreateRoom", (Type[])null, (Type[])null); yield return AccessTools.Method(typeof(LoadBalancingClient), "OpJoinOrCreateRoom", (Type[])null, (Type[])null); } private static void Prefix(EnterRoomParams enterRoomParams) { TeleportGuard.PublishUserId(enterRoomParams); } } [HarmonyPatch(typeof(PhotonView), "OnDestroy")] internal static class DestroyedViewCachePatch { private static readonly FieldInfo RemovedField = Reflect.Field(typeof(PhotonView), "removedFromLocalViewList"); private static void Prefix(PhotonView __instance) { //IL_0064: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)__instance == (Object)null || !PunGate.Ready || !PhotonNetwork.IsMasterClient || !PhotonNetwork.InRoom || !PhotonNetwork.IsMessageQueueRunning || __instance.InstantiationId < 1 || __instance.ViewID != __instance.InstantiationId || !__instance.IsMine || Reflect.Get(RemovedField, __instance, fallback: true)) { return; } Scene scene = ((Component)__instance).gameObject.scene; if (((Scene)(ref scene)).buildIndex != -1) { if (PhotonCacheCleaner.ClearOne(__instance)) { Log.Debug("Removed destroyed " + ((Object)((Component)__instance).gameObject).name + " (view " + __instance.ViewID + ") from the room cache."); } PhotonCacheCleaner.ClearBufferedRpcs(__instance); } } catch { } } } [HarmonyPatch(typeof(VoiceConnection), "OnRemoteVoiceInfo")] internal static class VoiceArrivingPatch { private static void Prefix() { VoiceWatchdog.NewStreamArriving = true; } private static Exception Finalizer(Exception __exception) { VoiceWatchdog.NewStreamArriving = false; return __exception; } } [HarmonyPatch(typeof(Speaker), "Link")] internal static class VoiceRelinkPatch { private static void Prefix(Speaker __instance, RemoteVoiceLink stream) { try { VoiceWatchdog.BeforeLink(__instance, stream); } catch (Exception ex) { Log.Debug("Could not replace a voice stream: " + ex); } } } } namespace RepoLateJoin.Menu { internal static class InviteButton { [CompilerGenerated] private static class <>O { public static UnityAction <0>__OpenInviteOverlay; } private const string CloneName = "RepoLateJoin_InviteButton"; private static readonly FieldInfo OriginalPositionField = Reflect.Field(typeof(MenuButton), "originalButtonPosition"); internal static void TryAdd(MenuPageEsc page) { //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Expected O, but got Unknown if (!PunGate.Ready || !ModConfig.On || (Object)(object)page == (Object)null) { return; } if (!PhotonNetwork.InRoom || !LobbyGate.SceneAllowsJoin()) { Log.Debug("Skipping the invite button: this scene does not accept joins."); return; } try { MenuButton val = FindQuitButton(page); if ((Object)(object)val == (Object)null) { Log.Debug("Could not find the quit button to anchor the invite button to."); return; } Transform parent = ((Component)val).transform.parent; if ((Object)(object)parent == (Object)null || (Object)(object)parent.Find("RepoLateJoin_InviteButton") != (Object)null) { return; } GameObject val2 = Object.Instantiate<GameObject>(((Component)val).gameObject, parent); ((Object)val2).name = "RepoLateJoin_InviteButton"; MenuButtonPopUp component = val2.GetComponent<MenuButtonPopUp>(); if ((Object)(object)component != (Object)null) { Object.Destroy((Object)(object)component); } Relabel(val2, "INVITE"); Button component2 = val2.GetComponent<Button>(); if ((Object)(object)component2 != (Object)null) { ((Behaviour)component2).enabled = true; ((Selectable)component2).interactable = true; ((UnityEventBase)component2.onClick).RemoveAllListeners(); ButtonClickedEvent onClick = component2.onClick; object obj = <>O.<0>__OpenInviteOverlay; if (obj == null) { UnityAction val3 = OpenInviteOverlay; <>O.<0>__OpenInviteOverlay = val3; obj = (object)val3; } ((UnityEvent)onClick).AddListener((UnityAction)obj); } val2.transform.SetSiblingIndex(((Component)val).transform.GetSiblingIndex()); ((MonoBehaviour)page).StartCoroutine(PlaceNextFrame(val2, ((Component)val).gameObject)); Log.Debug("Added an INVITE button to the escape menu."); } catch (Exception ex) { Log.Debug("Could not add the invite button: " + ex); } } private static void OpenInviteOverlay() { try { SteamManager instance = SteamManager.instance; if (instance != null) { instance.OpenSteamOverlayToInvite(); } } catch (Exception ex) { Log.Debug("Could not open the Steam invite overlay: " + ex); } } private static IEnumerator PlaceNextFrame(GameObject clone, GameObject anchor) { yield return null; if ((Object)(object)clone == (Object)null || (Object)(object)anchor == (Object)null) { yield break; } RectTransform component = clone.GetComponent<RectTransform>(); RectTransform component2 = anchor.GetComponent<RectTransform>(); if ((Object)(object)component == (Object)null || (Object)(object)component2 == (Object)null) { yield break; } component.anchorMin = component2.anchorMin; component.anchorMax = component2.anchorMax; component.pivot = component2.pivot; component.sizeDelta = component2.sizeDelta; ((Transform)component).localScale = ((Transform)component2).localScale; ((Transform)component).localPosition = FreeSlotBelow(clone, component2); Relabel(clone, "INVITE"); MenuButton component3 = clone.GetComponent<MenuButton>(); if (!((Object)(object)component3 != (Object)null)) { yield break; } Reflect.Set(OriginalPositionField, component3, Vector2.op_Implicit(((Transform)component).localPosition)); try { component3.ResizeToText(); } catch (Exception ex) { Log.Debug("Invite button resize failed: " + ex); } } private static Vector3 FreeSlotBelow(GameObject clone, RectTransform anchor) { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected O, but got Unknown //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00ff: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) List<float> list = new List<float>(); foreach (Transform item in ((Transform)anchor).parent) { Transform val = item; if ((Object)(object)((Component)val).gameObject != (Object)(object)clone && ((Component)val).gameObject.activeSelf && (Object)(object)((Component)val).GetComponent<MenuButton>() != (Object)null) { list.Add(val.localPosition.y); } } list.Sort(); float num = float.MaxValue; for (int i = 1; i < list.Count; i++) { float num2 = list[i] - list[i - 1]; if (num2 > 1f && num2 < num) { num = num2; } } if (num == float.MaxValue) { num = 32f; } float num3 = ((list.Count > 0) ? list[0] : ((Transform)anchor).localPosition.y); return new Vector3(((Transform)anchor).localPosition.x, num3 - num, ((Transform)anchor).localPosition.z); } private static void Relabel(GameObject buttonObject, string label) { if ((Object)(object)buttonObject == (Object)null) { return; } MenuButton component = buttonObject.GetComponent<MenuButton>(); if ((Object)(object)component != (Object)null) { component.buttonTextString = label; } TextMeshProUGUI[] componentsInChildren = buttonObject.GetComponentsInChildren<TextMeshProUGUI>(true); foreach (TextMeshProUGUI val in componentsInChildren) { if (!((Object)(object)val == (Object)null)) { ((TMP_Text)val).SetText(label, true); ((TMP_Text)val).ForceMeshUpdate(false, false); } } } private static MenuButton FindQuitButton(MenuPageEsc page) { MenuButton[] componentsInChildren = ((Component)page).GetComponentsInChildren<MenuButton>(true); MenuButton val = null; MenuButton[] array = componentsInChildren; foreach (MenuButton val2 in array) { if (!((Object)(object)val2 == (Object)null)) { string text = (val2.buttonTextString ?? string.Empty).ToUpperInvariant().Replace(" ", string.Empty); if (text == "QUITGAME") { return val2; } if ((Object)(object)val == (Object)null && text.Contains("QUIT")) { val = val2; } } } return val; } } internal static class StatusOverlay { private const float LineHeight = 22f; private const float Margin = 12f; private const float Edge = 10f; private const float Width = 700f; private static readonly Color Green = new Color(0.65f, 0.95f, 0.65f); private static readonly Color Red = new Color(1f, 0.66f, 0.66f); private static readonly Color Yellow = new Color(1f, 0.91f, 0.63f); private static readonly Regex ColorTags = new Regex("</?color[^>]*>"); private static readonly List<string> Lines = new List<string>(); private static GUIStyle _style; private static GUIStyle _shadow; internal static void Draw() { //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Invalid comparison between Unknown and I4 //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Expected O, but got Unknown //IL_0097: 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_00b0: Expected O, but got Unknown //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_02b9: Unknown result type (might be due to invalid IL or missing references) //IL_02c0: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Unknown result type (might be due to invalid IL or missing references) //IL_0347: Unknown result type (might be due to invalid IL or missing references) //IL_0375: Unknown result type (might be due to invalid IL or missing references) //IL_0245: Unknown result type (might be due to invalid IL or missing references) //IL_0234: Unknown result type (might be due to invalid IL or missing references) bool flag = ModConfig.ShowStatus != null && ModConfig.ShowStatus.Value; bool flag2 = ModConfig.ShowJoinStatus != null && ModConfig.ShowJoinStatus.Value && ModConfig.On; if ((!flag && !flag2) || Event.current == null || (int)Event.current.type != 7 || !PunGate.PhotonInitialised || !PhotonNetwork.InRoom || PhotonNetwork.CurrentRoom == null) { return; } if (_style == null) { _style = new GUIStyle(GUI.skin.label) { fontSize = 16, richText = true }; _style.normal.textColor = Color.white; _shadow = new GUIStyle(_style); _shadow.normal.textColor = new Color(0f, 0f, 0f, 0.7f); } ModConfig.Corner corner = ((ModConfig.InfoPosition != null) ? ModConfig.InfoPosition.Value : ModConfig.Corner.TopLeft); bool flag3 = corner == ModConfig.Corner.TopRight || corner == ModConfig.Corner.BottomRight; bool flag4 = corner == ModConfig.Corner.BottomLeft || corner == ModConfig.Corner.BottomRight; Lines.Clear(); if (flag2) { foreach (var (text, phase) in JoinStatus.Current()) { Lines.Add(text + ": " + Paint(phase.ToString(), PhaseColor(phase))); } } if (flag) { Room currentRoom = PhotonNetwork.CurrentRoom; bool flag5 = currentRoom.IsOpen && (currentRoom.MaxPlayers == 0 || currentRoom.PlayerCount < currentRoom.MaxPlayers); bool flag6 = LobbyGate.IsPrivateLobby(); string text2 = (flag6 ? "Private" : (LobbyGate.IsMatchmaking() ? "Matchmaking" : "Public")); int index = (flag4 ? Lines.Count : 0); Lines.Insert(index, "Name: " + (flag6 ? "Private Game" : RoomName(currentRoom))); Lines.Insert(index, "Room: " + text2 + " - " + (flag5 ? Paint("Open", Green) : Paint("Closed", Red))); Lines.Insert(index, "Players: " + currentRoom.PlayerCount + "/" + ((currentRoom.MaxPlayers > 0) ? currentRoom.MaxPlayers : currentRoom.PlayerCount)); } GUIStyle style = _style; GUIStyle shadow = _shadow; int num = (flag3 ? 2 : 0); TextAnchor alignment = (TextAnchor)num; shadow.alignment = (TextAnchor)num; style.alignment = alignment; float num2 = (flag3 ? ((float)Screen.width - 12f - 700f) : 12f); float num3 = (flag4 ? ((float)Screen.height - 10f - 4f - (float)Lines.Count * 22f) : 10f); foreach (string line in Lines) { GUI.Label(new Rect(num2 + 1f, num3 + 1f, 700f, 26f), ColorTags.Replace(line, ""), _shadow); GUI.Label(new Rect(num2, num3, 700f, 26f), line, _style); num3 += 22f; } } private static Color PhaseColor(JoinStatus.Phase phase) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) return (Color)(phase switch { JoinStatus.Phase.Left => Red, JoinStatus.Phase.Joined => Green, _ => Yellow, }); } private static string Paint(string text, Color color) { //IL_0010: Unknown result type (might be due to invalid IL or missing references) return "<color=#" + ColorUtility.ToHtmlStringRGB(color) + ">" + text + "</color>"; } private static string RoomName(Room room) { try { if (((Dictionary<object, object>)(object)((RoomInfo)room).CustomProperties).TryGetValue((object)"server_name", out object value) && value is string text && !string.IsNullOrEmpty(text)) { return text; } } catch (Exception) { } return room.Name; } } } namespace RepoLateJoin.Lobby { internal static class JoinRetry { private const float Every = 5f; private const int Tries = 6; private static readonly FieldInfo ConnectRandomField = Reflect.Field(typeof(GameManager), "connectRandom"); private static readonly FieldInfo JoinNameField = Reflect.Field(typeof(DataDirector), "networkJoinServerName"); private static readonly MethodInfo JoinPrivate = Reflect.Method(typeof(NetworkConnect), "TryJoiningRoom"); private static int _tries; private static bool _givingUp; internal static bool OnJoinFailed(NetworkConnect connect, short code, string cause) { if (_givingUp || (Object)(object)connect == (Object)null || (Object)(object)JoinQueue.Runner == (Object)null) { return true; } if ((code != 32764 && code != 32765) || !CanRetry()) { return true; } if (_tries >= 6) { Log.Info("Still could not join the room (" + cause + ") after " + 6 + " tries."); _tries = 0; return true; } _tries++; Log.Info("Could not join the room (" + cause + "); trying again in " + 5f + " s (" + _tries + "/" + 6 + ")."); JoinQueue.Runner.StartCoroutine(Retry(connect, code, cause)); return false; } internal static void Joined() { if (_tries > 0) { Log.Info("Joined the room after " + _tries + " retr" + ((_tries == 1) ? "y." : "ies.")); } _tries = 0; } private static IEnumerator Retry(NetworkConnect connect, short code, string cause) { yield return (object)new WaitForSecondsRealtime(5f); if ((Object)(object)connect == (Object)null || PhotonNetwork.InRoom) { yield break; } bool flag = false; try { ClientState networkClientState = PhotonNetwork.NetworkClientState; if (PhotonNetwork.IsConnectedAndReady && ((int)networkClientState == 15 || (int)networkClientState == 4)) { flag = Send(connect); } } catch (Exception ex) { Log.Debug("Could not retry the join: " + ex); } if (flag) { yield break; } _tries = 0; _givingUp = true; try { ((MonoBehaviourPunCallbacks)connect).OnJoinRoomFailed(code, cause); } finally { _givingUp = false; } } private static bool Send(NetworkConnect connect) { string text = ServerName(); if (text != null) { if (text.Length > 0) { return PhotonNetwork.JoinRoom(text, (string[])null); } return false; } if (JoinPrivate == null) { return false; } JoinPrivate.Invoke(connect, null); return true; } private static bool CanRetry() { string text = ServerName(); if (text == null) { return JoinPrivate != null; } return text.Length > 0; } private static string ServerName() { if ((Object)(object)GameManager.instance == (Object)null || !Reflect.Get(ConnectRandomField, GameManager.instance, fallback: false)) { return null; } return (((Object)(object)DataDirector.instance != (Object)null) ? Reflect.Get<string>(JoinNameField, DataDirector.instance) : null) ?? ""; } } internal static class LobbyGate { private static readonly FieldInfo PrivateLobbyField = Reflect.Field(typeof(SteamManager), "privateLobby"); private static readonly FieldInfo LobbyTypeField = Reflect.Field(typeof(GameManager), "lobbyType"); private const float TransitionCooldown = 4f; private static float _cooldownUntil; private static LevelGenerator _staleGenerator; private static float _nextTick; private static bool? _appliedState; internal static bool SelfLocking; private static string _seenRoom; private static LobbyTypes _seenType; internal static bool InTransition => Time.unscaledTime < _cooldownUntil; internal static bool SceneAllowsJoin() { try { if (SemiFunc.RunIsLobbyMenu()) { return true; } if (SemiFunc.RunIsLobby()) { return ModConfig.JoinInLobby.Value; } if (SemiFunc.RunIsShop()) { return ModConfig.JoinInShop.Value; } if (SemiFunc.RunIsLevel()) { return ModConfig.JoinInLevel.Value; } return false; } catch (Exception ex) { Log.Debug("Scene gate could not be evaluated: " + ex); return false; } } internal static bool ReadyForJoiners() { //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Invalid comparison between Unknown and I4 //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Invalid comparison between Unknown and I4 try { if ((Object)(object)LevelGenerator.Instance == (Object)null || !LevelGenerator.Instance.Generated) { return false; } if ((Object)(object)LevelGenerator.Instance == (Object)(object)_staleGenerator) { return false; } if ((Object)(object)GameDirector.instance == (Object)null) { return false; } gameState currentState = GameDirector.instance.currentState; return (int)currentState == 2 || (int)currentState == 7; } catch (Exception ex) { Log.Debug("Ready gate could not be evaluated: " + ex); return false; } } internal static bool JoinAllowedNow() { if (PunGate.Ready && PhotonNetwork.IsMasterClient && PhotonNetwork.InRoom && !InTransition && SceneAllowsJoin()) { return ReadyForJoiners(); } return false; } internal static void BeginTransition() { _appliedState = null; _staleGenerator = LevelGenerator.Instance; _cooldownUntil = Time.unscaledTime + 4f; Apply("level transition started", force: true); } internal static void Tick() { if (PunGate.Ready && !(Time.unscaledTime < _nextTick)) { _nextTick = Time.unscaledTime + 1f; Apply("periodic refresh"); if (_appliedState == true && PhotonNetwork.IsMasterClient && PhotonNetwork.CurrentRoom != null) { EnsureSceneProperty(PhotonNetwork.CurrentRoom); } } } private static void EnsureSceneProperty(Room room, bool force = false) { //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_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Expected O, but got Unknown //IL_005b: Expected O, but got Unknown try { Scene activeScene = SceneManager.GetActiveScene(); string name = ((Scene)(ref activeScene)).name; ((Dictionary<object, object>)(object)((RoomInfo)room).CustomProperties).TryGetValue((object)"curScn", out object value); bool flag = value is string text && text == name; if (!flag || force) { Hashtable val = new Hashtable(); ((Dictionary<object, object>)val).Add((object)"curScn", (object)name); room.SetCustomProperties(val, (Hashtable)null, (WebFlags)null); if (flag) { Log.Debug("Re-wrote the room scene property '" + name + "' before opening."); return; } Log.Info("Room scene property was '" + (value ?? "unset")?.ToString() + "' while we are in '" + name + "'; corrected so late joiners load the right scene."); } } catch (Exception ex) { Log.Debug("Could not check the room scene property: " + ex); } } internal static void Apply(string reason, bool force = false) { if (!PunGate.Ready || !PhotonNetwork.IsMasterClient || !PhotonNetwork.InRoom) { return; } bool flag = JoinAllowedNow(); if (force || !_appliedState.HasValue || _appliedState.Value != flag) { _appliedState = flag; if (flag) { OpenRoom(reason); } else { CloseRoom(reason); } } } private static void OpenRoom(string reason) { bool flag = !IsPrivateLobby(); try { Room currentRoom = PhotonNetwork.CurrentRoom; if (currentRoom != null) { EnsureSceneProperty(currentRoom, force: true); currentRoom.IsOpen = true; currentRoom.IsVisible = flag; } if ((Object)(object)SteamManager.instance != (Object)null) { SteamManager.instance.UnlockLobby(flag); } if (flag && IsMatchmaking() && (Object)(object)GameManager.instance != (Object)null) { GameManager.instance.SetConnectRandom(true); } Log.Debug("Lobby open for late joins (" + reason + "), advertised=" + flag + "."); } catch (Exception ex) { Log.Debug("Could not open the lobby after " + reason + ": " + ex); } } private static void CloseRoom(string reason) { try { Room currentRoom = PhotonNetwork.CurrentRoom; if (currentRoom != null) { currentRoom.IsOpen = false; currentRoom.IsVisible = false; } if (IsMatchmaking() && (Object)(object)GameManager.instance != (Object)null) { GameManager.instance.SetConnectRandom(false); } if ((Object)(object)SteamManager.instance != (Object)null) { SelfLocking = true; try { SteamManager.instance.LockLobby(); } finally { SelfLocking = false; } } Log.Debug("Lobby closed to new joins (" + reason + ")."); } catch (Exception ex) { Log.Debug("Could not close the lobby after " + reason + ": " + ex); } } internal static bool IsPrivateLobby() { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Invalid comparison between Unknown and I4 LobbyTypes? val = LobbyType(); if (val.HasValue) { return (int)val.Value == 0; } if ((Object)(object)SteamManager.instance != (Object)null && PrivateLobbyField != null) { return Reflect.Get(PrivateLobbyField, SteamManager.instance, fallback: true); } return true; } internal static bool IsMatchmaking() { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Invalid comparison between Unknown and I4 LobbyTypes? val = LobbyType(); if (val.HasValue) { return (int)val.Value == 2; } return false; } private static LobbyTypes? LobbyType() { //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) //IL_00e8: Unknown result type (might be due to invalid IL or missing references) //IL_0076: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) Room val = (PhotonNetwork.InRoom ? PhotonNetwork.CurrentRoom : null); if (val != null) { object value; LobbyTypes? result = ((((Dictionary<object, object>)(object)((RoomInfo)val).CustomProperties).TryGetValue((object)"server_name", out value) && value is string { Length: >0 }) ? new LobbyTypes?((LobbyTypes)1) : (val.IsVisible ? new LobbyTypes?((LobbyTypes)2) : ((LobbyTypes?)null))); if (result.HasValue) { _seenRoom = val.Name; _seenType = result.Value; return result; } if (val.Name == _seenRoom) { return _seenType; } } if ((Object)(object)GameManager.instance == (Object)null || LobbyTypeField == null) { return null; } try { object value2 = LobbyTypeField.GetValue(GameManager.instance); if (value2 is LobbyTypes) { return (LobbyTypes)value2; } } catch { } return null; } } internal static class PhotonCacheCleaner { private static readonly FieldInfo RemoveFilterField = Reflect.Field(typeof(PhotonNetwork), "removeFilter"); private static readonly FieldInfo KeyByteSevenField = Reflect.Field(typeof(PhotonNetwork), "keyByteSeven"); private static readonly FieldInfo ServerCleanOptionsField = Reflect.Field(typeof(PhotonNetwork), "ServerCleanOptions"); private static readonly MethodInfo RaiseEventInternalMethod = Reflect.Method(typeof(PhotonNetwork), "RaiseEventInternal"); internal static bool Available { get { if (RemoveFilterField != null && KeyByteSevenField != null && ServerCleanOptionsField != null) { return RaiseEventInternalMethod != null; } return false; } } internal static void ClearSceneCache() { //IL_007a: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) if (!PunGate.Ready || !PhotonNetwork.IsMasterClient) { return; } if (!Available) { Log.Debug("Photon cache clearing is unavailable on this Photon build; skipping."); return; } PhotonView[] array; try { array = Object.FindObjectsOfType<PhotonView>(true); } catch (Exception ex) { Log.Debug("Could not enumerate PhotonViews: " + ex); return; } int num = 0; int num2 = 0; PhotonView val = RunManagerView(); PhotonView[] array2 = array; foreach (PhotonView val2 in array2) { if ((Object)(object)val2 == (Object)null) { continue; } Scene scene = ((Component)val2).gameObject.scene; if (((Scene)(ref scene)).buildIndex == -1) { num2++; continue; } if (ClearOne(val2)) { num++; } if ((Object)(object)val2 != (Object)(object)val) { ClearBufferedRpcs(val2); } } if (num > 0) { Log.Debug("Cleared " + num + " buffered instantiation(s) from the finished level, left " + num2 + " persistent object(s) alone."); } else { Log.Debug("No buffered Photon instantiations needed clearing."); } } internal static bool ClearOne(PhotonView view) { //IL_0075: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)view == (Object)null || !Available) { return false; } if (!view.isRuntimeInstantiated || view.InstantiationId < 1) { return false; } try { object? value = RemoveFilterField.GetValue(null); Hashtable val = (Hashtable)((value is Hashtable) ? value : null); object value2 = KeyByteSevenField.GetValue(null); object? value3 = ServerCleanOptionsField.GetValue(null); RaiseEventOptions val2 = (RaiseEventOptions)((value3 is RaiseEventOptions) ? value3 : null); if (val == null || value2 == null || val2 == null) { return false; } val[value2] = view.InstantiationId; val2.CachingOption = (EventCaching)6; return RaiseInternal(202, val, val2); } catch (Exception ex) { Log.Debug("Could not clear Photon cache for view " + view.ViewID + ": " + ex); return false; } } internal static bool RaiseInternal(byte code, object data, RaiseEventOptions options) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) if (RaiseEventInternalMethod == null) { return false; } try { object obj = RaiseEventInternalMethod.Invoke(null, new object[4] { code, data, options, SendOptions.SendReliable }); bool flag = default(bool); int num; if (obj is bool) { flag = (bool)obj; num = 1; } else { num = 0; } return (byte)((uint)num & (flag ? 1u : 0u)) != 0; } catch (Exception ex) { Log.Debug("Photon internal event " + code + " failed: " + ex); return false; } } internal static PhotonView RunManagerView() { try { object obj = Reflect.Field(typeof(RunManager), "runManagerPUN")?.GetValue(RunManager.instance); object result; if (obj != null) { object? obj2 = Reflect.Field(obj.GetType(), "photonView")?.GetValue(obj); PhotonView val = (PhotonView)((obj2 is PhotonView) ? obj2 : null); if (val != null) { result = val; goto IL_0053; } } result = null; goto IL_0053; IL_0053: return (PhotonView)result; } catch { return null; } } internal static void ClearBufferedRpcs(PhotonView view) { if ((Object)(object)view == (Object)null || view.ViewID == 0) { return; } try { PhotonNetwork.RemoveBufferedRPCs(view.ViewID, (string)null, (int[])null); } catch (Exception ex) { Log.Debug("Could not clear buffered RPCs for view " + view.ViewID + ": " + ex); } } internal static void ClearStaleLevelUpdateRpc() { if (!PunGate.Ready || !PhotonNetwork.IsMasterClient || (Object)(object)RunManager.instance == (Object)null) { return; } try { PhotonView val = RunManagerView(); if (!((Object)(object)val == (Object)null)) { PhotonNetwork.RemoveBufferedRPCs(val.ViewID, "UpdateLevelRPC", (int[])null); PhotonNetwork.RemoveBufferedRPCs(val.ViewID, "AllPlayersDeadRPC", (int[])null); Log.Debug("Removed the previous level's buffered UpdateLevelRPC and AllPlayersDeadRPC."); } } catch (Exception ex) { Log.Debug("Could not remove the stale level update RPC: " + ex); } } internal static void ClearStaleGenerationRpcs(LevelGenerator generator) { if (!PunGate.Ready || !PhotonNetwork.IsMasterClient || (Object)(object)generator == (Object)null) { return; } if (generator.Generated) { Log.Debug("Generation already reported done; leaving its buffered RPCs alone."); return; } try { PhotonView photonView = generator.PhotonView; if (!((Object)(object)photonView == (Object)null) && photonView.ViewID != 0) { PhotonNetwork.RemoveBufferedRPCs(photonView.ViewID, (string)null, (int[])null); Log.Debug("Removed the previous level's buffered generation RPCs."); } } catch (Exception ex) { Log.Debug("Could not remove stale generation RPCs: " + ex); } } } internal static class PlayerLimit { internal static void Watch() { ModConfig.MaxPlayers.SettingChanged += delegate { Apply(); }; ModConfig.MaxPlayersEnabled.SettingChanged += delegate { Apply(); }; } internal static void Apply() { if (!ModConfig.MaxPlayersEnabled.Value || (Object)(object)GameManager.instance == (Object)null) { return; } int value = ModConfig.MaxPlayers.Value; try { GameManager.instance.SetMaxPlayers(value); Log.Debug("Max players set to " + GameManager.instance.maxPlayers + "."); } catch (Exception ex) { Log.Debug("Could not set max players: " + ex); } } } internal static class RoomName { private const string Key = "server_name"; private static readonly FieldInfo TextField = Reflect.Field(typeof(MenuTextInput), "textCurrent"); private static string _typed; private static string _created; internal static void Typed(MenuPageServerListCreateNew page) { string text = (((Object)(object)page != (Object)null && (Object)(object)page.menuTextInput != (Object)null) ? Reflect.Get<string>(TextField, page.menuTextInput) : null); _typed = (string.IsNullOrWhiteSpace(text) ? null : text.Trim()); } internal static void Create(Hashtable properties) { _created = null; if (ModConfig.On && properties != null && ((Dictionary<object, object>)(object)properties).ContainsKey((object)"server_name") && _typed != null) { _created = ModConfig.RoomPrefix() + " " + _typed; if (properties[(object)"server_name"] as string != _created) { Log.Debug("Server name '" + properties[(object)"server_name"]?.ToString() + "' set to '" + _created + "'."); } properties[(object)"server_name"] = _created; } } internal static void Keep(Hashtable properties) { if (_created != null && properties != null && ((Dictionary<object, object>)(object)properties).ContainsKey((object)"server_name") && PhotonNetwork.IsMasterClient && !(properties[(object)"server_name"] as string == _created)) { Log.Debug("Kept the server name '" + _created + "' instead of '" + properties[(object)"server_name"]?.ToString() + "'."); properties[(object)"server_name"] = _created; } } } internal static class SteamLobbyJoin { private const string Rpc = "JoinSteamLobbyRPC"; private const float WaitForId = 60f; private const float Recheck = 15f; private static readonly FieldInfo ConnectRandomField = Reflect.Field(typeof(GameManager), "connectRandom"); private static FieldRef<SteamManager, Lobby> _currentLobby; internal static Lobby? Current() { //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_0049: 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_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0066: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)SteamManager.instance == (Object)null || !SteamClient.IsValid) { return null; } try { if (_currentLobby == null) { _currentLobby = AccessTools.FieldRefAccess<SteamManager, Lobby>("currentLobby"); } Lobby value = _currentLobby.Invoke(SteamManager.instance); SteamId id = ((Lobby)(ref value)).Id; return ((SteamId)(ref id)).IsValid ? new Lobby?(value) : ((Lobby?)null); } catch { return null; } } internal static void StayIn(Lobby entered, ref Lobby current) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_001d: 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) SteamId id = ((Lobby)(ref current)).Id; if (((SteamId)(ref id)).IsValid && ((Lobby)(ref current)).Id.Value == ((Lobby)(ref entered)).Id.Value) { current = default(Lobby); Log.Debug("Asked to join the Steam lobby we are already in; stayed in it."); } } internal static bool SendJoin() { //IL_006b: 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) if (!PhotonNetwork.IsMasterClient || !PhotonNetwork.InRoom) { return false; } Lobby? val = Current(); PhotonView val2 = PhotonCacheCleaner.RunManagerView(); if (!val.HasValue || (Object)(object)val2 == (Object)null || val2.ViewID < 1) { return false; } SteamManager.instance.UnlockLobby(true); PhotonNetwork.RemoveBufferedRPCs(val2.ViewID, "JoinSteamLobbyRPC", (int[])null); int num = 0; Player[] playerListOthers = PhotonNetwork.PlayerListOthers; foreach (Player player in playerListOthers) { if (!InLobby(val.Value, player) && Send(val2, val.Value, player)) { num++; } } Log.Debug("Steam lobby ready; sent it to " + num + " player(s) not in it yet."); return true; } internal static void OnPlayerEntered(Player player) { if (player != null && !player.IsLocal && PhotonNetwork.IsMasterClient && !((Object)(object)JoinQueue.Runner == (Object)null) && !((Object)(object)GameManager.instance == (Object)null) && Reflect.Get(ConnectRandomField, GameManager.instance, fallback: false)) { JoinQueue.Runner.StartCoroutine(Welcome(player)); } } private static IEnumerator Welcome(Player player) { float until = Time.unscaledTime + 60f; while (SteamIdOf(player) == 0L) { if (Time.unscaledTime > until || !Here(player)) { yield break; } yield return (object)new WaitForSecondsRealtime(2f); } for (int attempt = 0; attempt < 2; attempt++) { if (!Here(player)) { break; } Lobby? val = Current(); PhotonView val2 = PhotonCacheCleaner.RunManagerView(); if (!val.HasValue || (Object)(object)val2 == (Object)null || val2.ViewID < 1 || InLobby(val.Value, player)) { break; } if (Send(val2, val.Value, player)) { Log.Debug(JoinQueue.Name(player) + " was not in our Steam lobby; sent it to them."); } yield return (object)new WaitForSecondsRealtime(15f); } } private static bool Here(Player player) { if (PhotonNetwork.InRoom && PhotonNetwork.IsMasterClient) { return PhotonNetwork.CurrentRoom.GetPlayer(player.ActorNumber, false) != null; } return false; } private static bool Send(PhotonView view, Lobby lobby, Player player) { //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) try { view.RPC("JoinSteamLobbyRPC", player, new object[1] { ((object)((Lobby)(ref lobby)).Id/*cast due to .constrained prefix*/).ToString() }); return true; } catch (Exception ex) { Log.Debug("Could not send the Steam lobby to " + JoinQueue.Name(player) + ": " + ex); return false; } } private static bool InLobby(Lobby lobby, Player player) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) ulong num = SteamIdOf(player); if (num == 0L) { return true; } try { foreach (Friend member in ((Lobby)(ref lobby)).Members) { if (member.Id.Value == num) { return true; } } } catch { return true; } return false; } private static ulong SteamIdOf(Player player) { string text = player.UserId; if (string.IsNullOrEmpty(text)) { PlayerAvatar val = null; try { val = SemiFunc.PlayerAvatarGetFromPhotonPlayer(player); } catch { } text = (((Object)(object)val != (Object)null) ? SemiFunc.PlayerGetSteamID(val) : null); } if (!ulong.TryParse(text, out var result)) { return 0uL; } return result; } } } namespace RepoLateJoin.Join { internal static class EnemyAwareness { private static readonly List<int> Scratch = new List<int>(); private static readonly HashSet<EnemyOnScreen> OnScreens = new HashSet<EnemyOnScreen>(); private static readonly HashSet<EnemyVision> Visions = new HashSet<EnemyVision>(); private static bool Wanted => ModConfig.On; private static List<int> PlayerViewIds() { Scratch.Clear(); if (GameDirector.instance?.PlayerList == null) { return Scratch; } foreach (PlayerAvatar player in GameDirector.instance.PlayerList) { if (!((Object)(object)player == (Object)null) && !((Object)(object)player.photonView == (Object)null) && player.photonView.ViewID != 0) { Scratch.Add(player.photonView.ViewID); } } return Scratch; } internal static void ForgetDestroyed() { OnScreens.RemoveWhere((EnemyOnScreen t) => (Object)(object)t == (Object)null); Visions.RemoveWhere((EnemyVision t) => (Object)(object)t == (Object)null); } internal static void EnsureFor(EnemyOnScreen onScreen) { if ((Object)(object)onScreen == (Object)null) { return; } OnScreens.Add(onScreen); if (!Wanted) { return; } try { List<int> list = PlayerViewIds(); for (int i = 0; i < list.Count; i++) { onScreen.PlayerAdded(list[i]); } } catch (Exception ex) { Log.Debug("EnemyOnScreen registration failed: " + ex); } } internal static void EnsureFor(EnemyVision vision) { if ((Object)(object)vision == (Object)null) { return; } Visions.Add(vision); if (!Wanted) { return; } try { List<int> list = PlayerViewIds(); for (int i = 0; i < list.Count; i++) { vision.PlayerAdded(list[i]); } } catch (Exception ex) { Log.Debug("EnemyVision registration failed: " + ex); } } internal static void RegisterAllPlayers() { if (!Wanted) { return; } List<int> list = PlayerViewIds(); if (list.Count == 0) { return; } int num = 0; try { OnScreens.RemoveWhere((EnemyOnScreen t) => (Object)(object)t == (Object)null); Visions.RemoveWhere((EnemyVision t) => (Object)(object)t == (Object)null); foreach (EnemyOnScreen onScreen in OnScreens) { for (int num2 = 0; num2 < list.Count; num2++) { onScreen.PlayerAdded(list[num2]); } num++; } foreach (EnemyVision vision in Visions) { for (int num3 = 0; num3 < list.Count; num3++) { vision.PlayerAdded(list[num3]); } num++; } } catch (Exception ex) { Log.Debug("Could not register players with the enemies: " + ex); return; } if (num > 0) { Log.Debug("Registered " + list.Count + " player(s) with " + num + " enemy tracker(s) so enemies can see late joiners."); } } } internal static class ExtractionSync { private static readonly FieldInfo RoundHaulGoalField = Reflect.Field(typeof(RoundDirector), "haulGoal"); private static readonly FieldInfo PointListField = Reflect.Field(typeof(RoundDirector), "extractionPointList"); private static readonly FieldInfo PointStateField = Reflect.Field(typeof(ExtractionPoint), "currentState"); private static readonly FieldInfo PointViewField = Reflect.Field(typeof(ExtractionPoint), "photonView"); private static readonly FieldInfo PointHaulFetchedField = Reflect.Field(typeof(ExtractionPoint), "haulGoalFetched"); private static readonly FieldInfo AllCompletedField = Reflect.Field(typeof(RoundDirector), "allExtractionPointsCompleted"); private static readonly Dictionary<int, Dictionary<int, State>> AtEntry = new Dictionary<int, Dictionary<int, State>>(); private static readonly HashSet<int> AllDoneAtEntry = new HashSet<int>(); internal static void Clear() { AtEntry.Clear(); AllDoneAtEntry.Clear(); } internal static void RememberAtEntry(Player player) { //IL_0053: Unknown result type (might be due to invalid IL or missing references) if (player == null || !PhotonNetwork.IsMasterClient || (Object)(object)RoundDirector.instance == (Object)null) { return; } try { Dictionary<int, State> dictionary = new Dictionary<int, State>(); foreach (ExtractionPoint item in Points()) { PhotonView val = ViewOf(item); if ((Object)(object)val != (Object)null && val.ViewID != 0) { dictionary[val.ViewID] = StateOf(item); } } AtEntry[player.ActorNumber] = dictionary; if (Reflect.Get(AllCompletedField, RoundDirector.instance, fallback: false)) { AllDoneAtEntry.Add(player.ActorNumber); } else { AllDoneAtEntry.Remove(player.ActorNumber); } } catch (Exception ex) { Log.Debug("Could not record the extraction points for a joiner: " + ex); } } internal static int SendTo(Player target) { if (!PunGate.Ready) { return 0; } if (target == null || !PhotonNetwork.IsMasterClient) { return 0; } if ((Object)(object)RoundDirector.instance == (Object)null) { return 0; } int num = 0 + SendHaulGoal(target) + SendActivation(target) + SendPointStates(target) + SendAllCompleted(target); if (num > 0) { Log.Debug("Sent extraction point state to late joiners."); } return num; } private static int SendAllCompleted(Player target) { if (!Reflect.Get(AllCompletedField, RoundDirector.instance, fallback: false)) { return 0; } if (Known(target) && !AllDoneAtEntry.Contains(target.ActorNumber)) { return 0; } PhotonView val = ViewOf(RoundDirector.instance); if ((Object)(object)val == (Object)null) { return 0; } try { val.RPC("ExtractionCompletedAllRPC", target, Array.Empty<object>()); return 1; } catch (Exception ex) { Log.Debug("Could not send that every extraction point is done: " + ex); return 0; } } private static int SendHaulGoal(Player target) { int num = Reflect.Get(RoundHaulGoalField, RoundDirector.instance, 0); if (num <= 0) { return 0; } PhotonView val = ViewOf(RoundDirector.instance); if ((Object)(object)val == (Object)null) { return 0; } try { val.RPC("StartRoundRPC", target, new object[1] { num }); return 1; } catch (Exception ex) { Log.Debug("Could not send the haul goal: " + ex); return 0; } } private static int SendActivation(Player target) { //IL_0030: 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_0175: Unknown result type (might be due to invalid IL or missing references) //IL_017a: Unknown result type (might be due to invalid IL or missing references) PhotonView val = ViewOf(RoundDirector.instance); if ((Object)(object)val == (Object)null) { return 0; } try { ExtractionPoint val2 = null; foreach (ExtractionPoint item in Points()) { if (InProgress(StateOf(item))) { val2 = item; break; } } if ((Object)(object)val2 == (Object)null) { val.RPC("ExtractionPointsUnlockRPC", target, Array.Empty<object>()); return 1; } PhotonView val3 = ViewOf(val2); if ((Object)(object)val3 == (Object)null || val3.ViewID == 0) { return 0; } val.RPC("ExtractionPointActivateRPC", target, new object[1] { val3.ViewID }); if (Known(target) && !InProgress(EntryState(target, val3))) { Log.Debug("Extraction point " + val3.ViewID + " started after the joiner entered; they have it."); return 1; } if (val2.haulGoal > 0 && Reflect.Get(PointHaulFetchedField, val2, fallback: false)) { val3.RPC("HaulGoalSetRPC", target, new object[1] { val2.haulGoal }); } val3.RPC("StateSetRPC", target, new object[1] { (object)(State)2 }); Log.Debug("Showed extraction point " + val3.ViewID + " (" + ((object)StateOf(val2)/*cast due to .constrained prefix*/).ToString() + ") to the joiner as active."); return 2; } catch (Exception ex) { Log.Debug("Could not send the extraction activation state: " + ex); return 0; } } private static int SendPointStates(Player target) { //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_007a: Invalid comparison between Unknown and I4 //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Invalid comparison between Unknown and I4 int num = 0; foreach (ExtractionPoint item in Points()) { PhotonView val = ViewOf(item); if ((Object)(object)val == (Object)null || val.ViewID == 0) { continue; } try { if (item.haulGoal > 0 && Reflect.Get(PointHaulFetchedField, item, fallback: false)) { val.RPC("HaulGoalSetRPC", target, new object[1] { item.haulGoal }); num++; } if ((int)StateOf(item) == 7) { if (Known(target) && (int)EntryState(target, val) != 7) { Log.Debug("Extraction point " + val.ViewID + " finished after the joiner entered; they have it."); continue; } val.RPC("StateSetRPC", target, new object[1] { (object)(State)7 }); num++; Log.Debug("Sent extraction point " + val.ViewID + " as complete."); } } catch (Exception ex) { Log.Debug("Could not send state for extraction point " + val.ViewID + ": " + ex); } } return num; } private static bool InProgress(State state) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Invalid comparison between Unknown and I4 //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Invalid comparison between Unknown and I4 if ((int)state != 0 && (int)state != 1) { return (int)state != 7; } return false; } private static State StateOf(ExtractionPoint point) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) return Reflect.Get<State>(PointStateField, point, (State)0); } private static bool Known(Player target) { return AtEntry.ContainsKey(target.ActorNumber); } private static State EntryState(Player target, PhotonView view) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) if (!AtEntry.TryGetValue(target.ActorNumber, out var value) || !value.TryGetValue(view.ViewID, out var value2)) { return (State)0; } return value2; } private static IEnumerable<ExtractionPoint> Points() { if (PointListField?.GetValue(RoundDirector.instance) is List<GameObject> list) { foreach (GameObject item in list) { if (!((Object)(object)item == (Object)null)) { ExtractionPoint component = item.GetComponent<ExtractionPoint>(); if ((Object)(object)component != (Object)null) { yield return component; } } } yield break; } ExtractionPoint[] array; try { array = Object.FindObjectsOfType<ExtractionPoint>(true); } catch { yield break; } ExtractionPoint[] array2 = array; foreach (ExtractionPoint val in array2) { if ((Object)(object)val != (Object)null) { yield return val; } } } private static PhotonView ViewOf(RoundDirector director) { if ((Object)(object)director == (Object)null) { return null; } return ((Component)director).GetComponent<PhotonView>(); } private static PhotonView ViewOf(ExtractionPoint point) { if ((Object)(object)point == (Object)null) { return null; } object? obj = PointViewField?.GetValue(point); PhotonView val = (PhotonView)((obj is PhotonView) ? obj : null); if (val != null && (Object)(object)val != (Object)null) { return val; } return ((Component)point).GetComponent<PhotonView>(); } } internal static class Handshake { private const byte Event = 173; private const string Tag = "RepoLateJoin"; private const float ReplyTimeout = 15f; private static readonly HashSet<int> ModdedActors = new HashSet<int>(); private static float _greetedAt; internal static bool HostHasMod { get { if (PhotonNetwork.MasterClient != null) { return HasMod(PhotonNetwork.MasterClient.ActorNumber); } return false; } } internal static bool HasMod(int actor) { return ModdedActors.Contains(actor); } internal static void Listen() { PhotonNetwork.NetworkingClient.EventReceived += OnEvent; } internal static void Greet() { //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Expected O, but got Unknown ModdedActors.Clear(); _greetedAt = 0f; if (!PhotonNetwork.IsMasterClient && Send(new RaiseEventOptions { Receivers = (ReceiverGroup)0 })) { _greetedAt = Time.unscaledTime; Log.Debug("Asked the host whether it runs RepoLateJoin."); } } internal static void Tick() { if (_greetedAt == 0f) { return; } if (!PhotonNetwork.IsMessageQueueRunning) { _greetedAt = Time.unscaledTime; } if (!(Time.unscaledTime - _greetedAt < 15f)) { _greetedAt = 0f; if (PhotonNetwork.InRoom && !PhotonNetwork.IsMasterClient) { Log.Info("The host did not answer within " + 15f + " s: the host is not running RepoLateJoin 2.8.0 or newer, so joining mid-run relies on the game alone."); } } } private static bool Send(RaiseEventOptions options) { //IL_0029: Unknown result type (might be due to invalid IL or missing references) try { return PhotonNetwork.RaiseEvent((byte)173, (object)new object[3] { "RepoLateJoin", "2.8.0", ModConfig.On }, options, SendOptions.SendReliable); } catch (Exception ex) { Log.Debug("Could not send the RepoLateJoin handshake: " + ex); return false; } } private static void OnEvent(Even