Decompiled source of JoinAnytime v0.1.0

JoinAnytime.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using Bifrost.Cooked;
using HarmonyLib;
using JoinAnytime;
using JoinAnytime.Enums;
using JoinAnytime.Helpers;
using MelonLoader;
using MelonLoader.Preferences;
using Microsoft.CodeAnalysis;
using ReluProtocol;
using ReluProtocol.C2S;
using ReluProtocol.Enum;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: MelonInfo(typeof(JoinAnytimeMod), "JoinAnytime", "0.1.0", "Shlygly", null)]
[assembly: MelonGame("ReLUGames", "MIMESIS")]
[assembly: MelonColor(255, 0, 128, 255)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("Shlygly")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("JoinAnytime")]
[assembly: AssemblyTitle("JoinAnytime")]
[assembly: NeutralResourcesLanguage("en-US")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace JoinAnytime
{
	public class JoinAnytimeMod : MelonMod
	{
		public static LateJoinRedirectState RedirectState = LateJoinRedirectState.None;

		public static string PendingSceneName = "";

		public static JoinAnytimeMod Instance { get; private set; }

		public override void OnInitializeMelon()
		{
			Instance = this;
			((MelonBase)this).LoggerInstance.Msg("Reading settings...");
			Preferences.Init();
			(bool, string)[] array = new(bool, string)[4]
			{
				(Preferences.AllowPreGame.Value, "pre-game tramway"),
				(Preferences.AllowOnPlaying.Value, "dungeon"),
				(Preferences.AllowEndGame.Value, "end game tramway"),
				(Preferences.AllowDeathMatch.Value, "final deathmatch")
			};
			for (int i = 0; i < array.Length; i++)
			{
				var (flag, text) = array[i];
				if (flag)
				{
					((MelonBase)this).LoggerInstance.Msg("Players are allowed to join during " + text + ".");
				}
				else
				{
					((MelonBase)this).LoggerInstance.Msg("Players won't be able to join during " + text + ".");
				}
			}
			((MelonBase)this).LoggerInstance.Msg("Initialized !");
		}

		public static void Log(string message)
		{
			if (Preferences.Verbose.Value)
			{
				((MelonBase)Instance).LoggerInstance.Msg(message);
			}
		}
	}
	internal static class LateJoinManager
	{
		[CompilerGenerated]
		private sealed class <<EnsureVoiceConnected>g__connectDelayed|8_0>d : IEnumerator<object>, IEnumerator, IDisposable
		{
			private int <>1__state;

			private object <>2__current;

			public VoiceManager voice;

			private PersistentData <pdata>5__1;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <<EnsureVoiceConnected>g__connectDelayed|8_0>d(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<pdata>5__1 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_003a: Unknown result type (might be due to invalid IL or missing references)
				//IL_0044: Expected O, but got Unknown
				//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
				//IL_00d4: Expected O, but got Unknown
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					voiceCheckRunning = true;
					<>2__current = (object)new WaitForSeconds(0.5f);
					<>1__state = 1;
					return true;
				case 1:
					<>1__state = -1;
					<pdata>5__1 = ReflectionTools.GetFieldOrProperty<PersistentData>(Hub.s, "pdata");
					if (<pdata>5__1 != null && (Object)(object)voice != (Object)null && !voice.IsConnected())
					{
						voice.TryRecoverMicrophone();
						voice.ConnectVoiceChat(<pdata>5__1.GameServerAddressOrSteamId, <pdata>5__1.WithRelay);
					}
					<>2__current = (object)new WaitForSeconds(2f);
					<>1__state = 2;
					return true;
				case 2:
					<>1__state = -1;
					voiceCheckRunning = false;
					return false;
				}
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		private static bool voiceCheckRunning;

		public static bool IsStateAllowed(VGameSessionState state)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0003: Invalid comparison between Unknown and I4
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Invalid comparison between Unknown and I4
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Invalid comparison between Unknown and I4
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Invalid comparison between Unknown and I4
			return ((int)state == 3 && Preferences.AllowPreGame.Value) || ((int)state == 4 && Preferences.AllowOnPlaying.Value) || ((int)state == 6 && Preferences.AllowEndGame.Value) || ((int)state == 7 && Preferences.AllowDeathMatch.Value);
		}

		public static void OnServerLogin(SessionContext context)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Invalid comparison between Unknown and I4
			PersistentData fieldOrProperty = ReflectionTools.GetFieldOrProperty<PersistentData>(Hub.s, "pdata");
			if (fieldOrProperty != null && (int)fieldOrProperty.ClientMode <= 0)
			{
				GameMainBase main = fieldOrProperty.main;
				GamePlayScene val = (GamePlayScene)(object)((main is GamePlayScene) ? main : null);
				if (val != null && IsStateAllowed((VGameSessionState)4))
				{
					JoinAnytimeMod.Log($"Login while OnPlaying: uid={context.GetPlayerUID()} dungeon={val.DungeonMasterID} seed={val.RandDungeonSeed}");
					NetworkTools.SendOnPlayingStateToClient(context);
				}
			}
		}

		public static void OnServerPlayerCreated(VPlayer player)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Invalid comparison between Unknown and I4
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Expected O, but got Unknown
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			PersistentData fieldOrProperty = ReflectionTools.GetFieldOrProperty<PersistentData>(Hub.s, "pdata");
			if (fieldOrProperty != null && (int)fieldOrProperty.ClientMode <= 0 && !player.IsHost)
			{
				JoinAnytimeMod.Log($"VPlayer ctor: UID={((VActor)player).UID} room={((object)((VActor)player).VRoom)?.GetType().Name} main={((object)fieldOrProperty.main)?.GetType().Name}");
				if (fieldOrProperty.main is InTramWaitingScene && ((VActor)player).VRoom is MaintenanceRoom && IsStateAllowed((VGameSessionState)3))
				{
					JoinAnytimeMod.Log($"Join PreGame detected, sending EnableWaitingRoomSig to UID={((VActor)player).UID}");
					((VActor)player).SendToMe((IMsg)new EnableWaitingRoomSig());
				}
				else if (fieldOrProperty.main is GamePlayScene && ((VActor)player).VRoom is MaintenanceRoom && IsStateAllowed((VGameSessionState)4))
				{
					NetworkTools.SendOnPlayingStateToClient(player);
				}
			}
		}

		public static bool OnClientPacket(IMsg msg)
		{
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Invalid comparison between Unknown and I4
			//IL_01c2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c8: Invalid comparison between Unknown and I4
			PersistentData fieldOrProperty = ReflectionTools.GetFieldOrProperty<PersistentData>(Hub.s, "pdata");
			AllMemberEnterRoomSig val = (AllMemberEnterRoomSig)(object)((msg is AllMemberEnterRoomSig) ? msg : null);
			if (val == null)
			{
				if (!(msg is EnableWaitingRoomSig))
				{
					StartGameSig val2 = (StartGameSig)(object)((msg is StartGameSig) ? msg : null);
					if (val2 == null)
					{
						CompleteMakingRoomSig val3 = (CompleteMakingRoomSig)(object)((msg is CompleteMakingRoomSig) ? msg : null);
						if (val3 != null)
						{
							fieldOrProperty.completeMakingRoomSig = val3;
							if (val3.nextRoomInfo.roomMasterID != 0)
							{
								fieldOrProperty.dungeonMasterID = val3.nextRoomInfo.roomMasterID;
							}
							string sceneNameFromDungeon = RoomTools.GetSceneNameFromDungeon(fieldOrProperty.dungeonMasterID);
							JoinAnytimeMod.Log(string.Format("CompleteMakingRoomSig: roomUID={0}, scene={1}, main={2}", val3.nextRoomInfo.roomUID, sceneNameFromDungeon, ((object)fieldOrProperty.main)?.GetType().Name ?? "null"));
							JoinAnytimeMod.PendingSceneName = sceneNameFromDungeon;
							if ((int)fieldOrProperty.ClientMode == 1)
							{
								if (fieldOrProperty.main is MaintenanceScene && !string.IsNullOrEmpty(sceneNameFromDungeon))
								{
									JoinAnytimeMod.Log("Redirect now -> " + sceneNameFromDungeon);
									JoinAnytimeMod.RedirectState = LateJoinRedirectState.EnteringDungeon;
									Hub.LoadScene(sceneNameFromDungeon);
								}
								else
								{
									JoinAnytimeMod.RedirectState = LateJoinRedirectState.PendingDungeonRedirect;
								}
							}
						}
					}
					else
					{
						fieldOrProperty.dungeonMasterID = val2.selectedDungeonMasterID;
						fieldOrProperty.randDungeonSeed = val2.randDungeonSeed;
						JoinAnytimeMod.Log($"StartGameSig: dungeon={fieldOrProperty.dungeonMasterID} seed={fieldOrProperty.randDungeonSeed}");
					}
				}
				else
				{
					if (fieldOrProperty == null || (int)fieldOrProperty.ClientMode != 1)
					{
						return true;
					}
					JoinAnytimeMod.Log("PreGame signal received. main=" + (((object)fieldOrProperty.main)?.GetType().Name ?? "null"));
					JoinAnytimeMod.RedirectState = LateJoinRedirectState.PendingWaitingRoomRedirect;
					JoinAnytimeMod.PendingSceneName = "InTramWaitingScene";
					if (fieldOrProperty.main is MaintenanceScene)
					{
						JoinAnytimeMod.RedirectState = LateJoinRedirectState.None;
						Hub.LoadScene("InTramWaitingScene");
					}
				}
			}
			else if (ShouldIgnoreEarlyGameAllMemberEnterRoomSig(fieldOrProperty, val))
			{
				return false;
			}
			return true;
		}

		private static bool ShouldIgnoreEarlyGameAllMemberEnterRoomSig(PersistentData pdata, AllMemberEnterRoomSig sig)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Invalid comparison between Unknown and I4
			if (pdata == null || (int)pdata.ClientMode != 1)
			{
				return false;
			}
			if (!(pdata.main is GamePlayScene))
			{
				return false;
			}
			if (ReflectionTools.GetFieldOrProperty<bool>(pdata.main, "EnteringCompleteAll"))
			{
				return false;
			}
			if (JoinAnytimeMod.RedirectState != LateJoinRedirectState.EnteringDungeon)
			{
				return false;
			}
			if (sig.enterCutsceneNames != null && sig.enterCutsceneNames.Contains("ArriveCutScene"))
			{
				JoinAnytimeMod.RedirectState = LateJoinRedirectState.None;
				JoinAnytimeMod.PendingSceneName = "";
				return false;
			}
			JoinAnytimeMod.Log("Ignoring early AllMemberEnterRoomSig names=" + string.Join(",", sig.enterCutsceneNames ?? new List<string>()));
			return true;
		}

		public static void OnLoadScene(string sceneName)
		{
			if (JoinAnytimeMod.RedirectState != 0 && (JoinAnytimeMod.RedirectState != LateJoinRedirectState.EnteringDungeon || !(sceneName == JoinAnytimeMod.PendingSceneName)) && sceneName != JoinAnytimeMod.PendingSceneName)
			{
				JoinAnytimeMod.RedirectState = LateJoinRedirectState.None;
				JoinAnytimeMod.PendingSceneName = "";
			}
		}

		public static void OnMaintenanceSceneStart()
		{
			PersistentData fieldOrProperty = ReflectionTools.GetFieldOrProperty<PersistentData>(Hub.s, "pdata");
			if (fieldOrProperty != null)
			{
				if (JoinAnytimeMod.RedirectState == LateJoinRedirectState.PendingWaitingRoomRedirect)
				{
					JoinAnytimeMod.Log("Pending PreGame redirect -> InTramWaitingScene");
					JoinAnytimeMod.RedirectState = LateJoinRedirectState.None;
					Hub.LoadScene("InTramWaitingScene");
				}
				else if (JoinAnytimeMod.RedirectState == LateJoinRedirectState.PendingDungeonRedirect && !string.IsNullOrEmpty(JoinAnytimeMod.PendingSceneName))
				{
					JoinAnytimeMod.Log("Pending in-game redirect -> " + JoinAnytimeMod.PendingSceneName);
					JoinAnytimeMod.RedirectState = LateJoinRedirectState.EnteringDungeon;
					Hub.LoadScene(JoinAnytimeMod.PendingSceneName);
				}
			}
		}

		public static void EnsureVoiceConnected(VoiceManager voiceManager, VoiceMode mode)
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Invalid comparison between Unknown and I4
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Invalid comparison between Unknown and I4
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Invalid comparison between Unknown and I4
			if (voiceCheckRunning)
			{
				return;
			}
			PersistentData fieldOrProperty = ReflectionTools.GetFieldOrProperty<PersistentData>(Hub.s, "pdata");
			if (fieldOrProperty == null)
			{
				return;
			}
			if (1 == 0)
			{
			}
			bool flag = (((int)mode == 1) ? (Preferences.AllowPreGame.Value || Preferences.AllowEndGame.Value) : ((int)mode == 2 && (Preferences.AllowOnPlaying.Value || Preferences.AllowDeathMatch.Value)));
			if (1 == 0)
			{
			}
			if (flag && (int)fieldOrProperty.ClientMode == 1)
			{
				GameMainBase main = fieldOrProperty.main;
				if (1 == 0)
				{
				}
				flag = ((main is InTramWaitingScene) ? (Preferences.AllowPreGame.Value || Preferences.AllowEndGame.Value) : ((main is GamePlayScene) ? Preferences.AllowOnPlaying.Value : (main is DeathMatchScene && Preferences.AllowDeathMatch.Value)));
				if (1 == 0)
				{
				}
				if (flag && !voiceManager.IsConnected())
				{
					JoinAnytimeMod.Log($"Voice connect fix: addr={fieldOrProperty.GameServerAddressOrSteamId} relay={fieldOrProperty.WithRelay}");
					((MonoBehaviour)voiceManager).StartCoroutine(connectDelayed(voiceManager));
				}
			}
			[IteratorStateMachine(typeof(<<EnsureVoiceConnected>g__connectDelayed|8_0>d))]
			static IEnumerator connectDelayed(VoiceManager voice)
			{
				//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
				return new <<EnsureVoiceConnected>g__connectDelayed|8_0>d(0)
				{
					voice = voice
				};
			}
		}

		public static void OnServerEnterWaitingRoom(SessionContext context)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Invalid comparison between Unknown and I4
			if (context != null && context.ExistPlayer())
			{
				PersistentData fieldOrProperty = ReflectionTools.GetFieldOrProperty<PersistentData>(Hub.s, "pdata");
				if ((int)context.GetVRoomType() == 3 && fieldOrProperty?.main is InTramWaitingScene)
				{
					JoinAnytimeMod.Log("Moving player snapshot Maintenance -> Waiting");
					RoomTools.MoveCurrentPlayerToSnapshot(context);
				}
			}
		}

		public static void OnServerEnterDungeon(SessionContext context, long roomUID)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Invalid comparison between Unknown and I4
			if (context != null && context.ExistPlayer())
			{
				PersistentData fieldOrProperty = ReflectionTools.GetFieldOrProperty<PersistentData>(Hub.s, "pdata");
				if ((int)context.GetVRoomType() == 3 && fieldOrProperty?.main is GamePlayScene)
				{
					JoinAnytimeMod.Log($"Moving player snapshot Maintenance -> Dungeon, roomUID={roomUID}");
					RoomTools.MoveCurrentPlayerToSnapshot(context);
				}
			}
		}
	}
	public static class Preferences
	{
		public static MelonPreferences_Entry<bool> Verbose { get; private set; }

		public static MelonPreferences_Entry<bool> AllowPreGame { get; private set; }

		public static MelonPreferences_Entry<bool> AllowOnPlaying { get; private set; }

		public static MelonPreferences_Entry<bool> AllowEndGame { get; private set; }

		public static MelonPreferences_Entry<bool> AllowDeathMatch { get; private set; }

		public static void Init()
		{
			MelonPreferences_Category val = MelonPreferences.CreateCategory("Join Anytime");
			Verbose = val.CreateEntry<bool>("Verbose logging", false, "Verbose logging", "Activate verbose logging.", false, false, (ValueValidator)null, (string)null);
			AllowPreGame = val.CreateEntry<bool>("Join during pre-game", true, "Join during pre-game", "Activate to allow players to join during pre-game.", false, false, (ValueValidator)null, (string)null);
			AllowOnPlaying = val.CreateEntry<bool>("Join when playing", true, "Join when playing", "Activate to allow players to join during a dungeon.", false, false, (ValueValidator)null, (string)null);
			AllowEndGame = val.CreateEntry<bool>("Join during end game", true, "Join during end game", "Activate to allow players to join for the end game.", false, false, (ValueValidator)null, (string)null);
			AllowDeathMatch = val.CreateEntry<bool>("Join during deathmatch", false, "Join during deathmatch", "Activate to allow players to join for the final deathmatch.", false, false, (ValueValidator)null, (string)null);
		}
	}
}
namespace JoinAnytime.Patches
{
	[HarmonyPatch(typeof(Hub), "LoadScene", new Type[] { typeof(string) })]
	internal static class Hub_LoadScene
	{
		private static void Prefix(string sceneName)
		{
			LateJoinManager.OnLoadScene(sceneName);
		}
	}
	[HarmonyPatch(typeof(SessionContext), "Login")]
	internal static class SessionContext_Login
	{
		private static void Postfix(SessionContext __instance)
		{
			LateJoinManager.OnServerLogin(__instance);
		}
	}
	[HarmonyPatch(typeof(GameSessionInfo), "CanEnterSession")]
	internal static class GameSessionInfo_CanEnterSession
	{
		private static bool Prefix(ref GameSessionInfo __instance, ref bool __result)
		{
			//IL_0003: 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)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			VGameSessionState gameSessionState = __instance.GameSessionState;
			if (LateJoinManager.IsStateAllowed(gameSessionState))
			{
				__result = true;
				return false;
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(MaintenanceScene), "Start")]
	internal static class MaintenanceScene_Start
	{
		private static void Postfix()
		{
			LateJoinManager.OnMaintenanceSceneStart();
		}
	}
	[HarmonyPatch(typeof(NetworkManagerV2), "OnRecvPacket", new Type[] { typeof(IMsg) })]
	internal static class NetworkManagerV2_OnRecvPacket
	{
		private static bool Prefix(IMsg msg)
		{
			return LateJoinManager.OnClientPacket(msg);
		}
	}
	[HarmonyPatch(typeof(VoiceManager), "SetVoiceMode")]
	internal static class VoiceManager_SetVoiceMode
	{
		private static void Postfix(VoiceManager __instance, VoiceMode voiceMode)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			LateJoinManager.EnsureVoiceConnected(__instance, voiceMode);
		}
	}
	[HarmonyPatch]
	internal static class VPlayer_Ctor
	{
		private static MethodBase TargetMethod()
		{
			return AccessTools.Constructor(typeof(VPlayer), new Type[10]
			{
				typeof(SessionContext),
				typeof(int),
				typeof(int),
				typeof(bool),
				typeof(string),
				typeof(string),
				typeof(PosWithRot),
				typeof(bool),
				typeof(IVroom),
				typeof(ReasonOfSpawn)
			}, false);
		}

		private static void Postfix(VPlayer __instance)
		{
			LateJoinManager.OnServerPlayerCreated(__instance);
		}
	}
	[HarmonyPatch(typeof(VRoomManager), "EnterWaitingRoom")]
	internal static class VRoomManager_EnterWaitingRoom
	{
		private static void Prefix(SessionContext context, int hashCode)
		{
			LateJoinManager.OnServerEnterWaitingRoom(context);
		}
	}
	[HarmonyPatch(typeof(VRoomManager), "EnterDungeon")]
	internal static class VRoomManager_EnterDungeon
	{
		private static void Prefix(SessionContext context, int hashCode, long roomUID)
		{
			LateJoinManager.OnServerEnterDungeon(context, roomUID);
		}
	}
}
namespace JoinAnytime.Helpers
{
	internal static class NetworkTools
	{
		public static void SendOnPlayingStateToClient(VPlayer player)
		{
			SendOnPlayingState(((VActor)player).UID, delegate(IMsg msg)
			{
				//IL_0007: Unknown result type (might be due to invalid IL or missing references)
				((VActor)player).SendToMe(msg);
			});
		}

		public static void SendOnPlayingStateToClient(SessionContext context)
		{
			SendOnPlayingState(context.GetPlayerUID(), delegate(IMsg msg)
			{
				//IL_0007: Unknown result type (might be due to invalid IL or missing references)
				context.Send(msg);
			});
		}

		private static void SendOnPlayingState(long uid, Action<IMsg> send)
		{
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c2: Expected O, but got Unknown
			//IL_00c4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: 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_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Expected O, but got Unknown
			//IL_00fc: Expected O, but got Unknown
			GameMainBase obj = ReflectionTools.GetFieldOrProperty<PersistentData>(Hub.s, "pdata")?.main;
			GamePlayScene val = (GamePlayScene)(object)((obj is GamePlayScene) ? obj : null);
			if (val != null)
			{
				IVroom activeDungeonRoom = RoomTools.GetActiveDungeonRoom();
				if (activeDungeonRoom == null)
				{
					JoinAnytimeMod.Log("SendOnPlayingStateToClient failed: no active DungeonRoom.");
					return;
				}
				JoinAnytimeMod.Log($"Sending OnPlaying state to UID={uid}: dungeon={val.DungeonMasterID}, seed={val.RandDungeonSeed}, roomUID={activeDungeonRoom.RoomID}");
				send((IMsg)new StartGameSig
				{
					selectedDungeonMasterID = val.DungeonMasterID,
					randDungeonSeed = val.RandDungeonSeed
				});
				send((IMsg)new CompleteMakingRoomSig
				{
					nextRoomInfo = new RoomInfo
					{
						roomType = (VRoomType)2,
						roomMasterID = val.DungeonMasterID,
						roomUID = activeDungeonRoom.RoomID
					}
				});
			}
		}
	}
	internal static class RoomTools
	{
		public static void MoveCurrentPlayerToSnapshot(SessionContext context)
		{
			VPlayer fieldOrProperty = ReflectionTools.GetFieldOrProperty<VPlayer>(context, "_vPlayer");
			if (fieldOrProperty != null)
			{
				IVroom vRoom = ((VActor)fieldOrProperty).VRoom;
				int objectID = ((VActor)fieldOrProperty).ObjectID;
				JoinAnytimeMod.Log($"Removing old player actor={objectID} room={((object)vRoom)?.GetType().Name}");
				if (vRoom != null)
				{
					vRoom.PendRemovePlayer(objectID, false, false);
				}
				context.CreatePlayerSnapshot(true);
			}
		}

		public static string GetSceneNameFromDungeon(int dungeonMasterID)
		{
			DataManager fieldOrProperty = ReflectionTools.GetFieldOrProperty<DataManager>(Hub.s, "dataman");
			DungeonMasterInfo dungeonInfo = fieldOrProperty.ExcelDataManager.GetDungeonInfo(dungeonMasterID);
			if (dungeonInfo == null)
			{
				return "";
			}
			MapMasterInfo mapInfo = fieldOrProperty.ExcelDataManager.GetMapInfo(dungeonInfo.MapID);
			return ((mapInfo != null) ? mapInfo.SceneName : null) ?? "";
		}

		public static IVroom GetActiveDungeonRoom()
		{
			VRoomManager vRoomManager = ReflectionTools.GetFieldOrProperty<VWorld>(Hub.s, "vworld").VRoomManager;
			return ((IEnumerable<IVroom>)ReflectionTools.GetFieldOrProperty<Dictionary<long, IVroom>>(vRoomManager, "_vrooms").Values).FirstOrDefault((Func<IVroom, bool>)((IVroom room) => room is DungeonRoom));
		}
	}
	internal static class ReflectionTools
	{
		public static object GetFieldOrProperty(object instance, string name)
		{
			Type type = instance.GetType();
			FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (field != null)
			{
				return field.GetValue(instance);
			}
			PropertyInfo property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (property != null)
			{
				return property.GetValue(instance);
			}
			throw new Exception("Field/property '" + name + "' not found on " + type.FullName);
		}

		public static T GetFieldOrProperty<T>(object instance, string name)
		{
			return (T)GetFieldOrProperty(instance, name);
		}

		public static void SetFieldOrProperty(object instance, string name, object value)
		{
			Type type = instance.GetType();
			FieldInfo field = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (field != null)
			{
				field.SetValue(instance, value);
				return;
			}
			PropertyInfo property = type.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (property != null)
			{
				property.SetValue(instance, value);
				return;
			}
			throw new Exception("Field/property '" + name + "' not found on " + type.FullName);
		}
	}
}
namespace JoinAnytime.Enums
{
	public enum LateJoinRedirectState
	{
		None,
		PendingWaitingRoomRedirect,
		PendingDungeonRedirect,
		EnteringDungeon
	}
}