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
}
}