diff --git a/Components/LoadingScreenComponent.cs b/Components/LoadingScreenComponent.cs index 98652ad..47961f3 100644 --- a/Components/LoadingScreenComponent.cs +++ b/Components/LoadingScreenComponent.cs @@ -34,12 +34,12 @@ namespace SeamlessClient.Components private bool ScannedMods = false; public static string JoiningServerName { get; private set; } - public static List CustomLoadingTextures { get; private set; } = new List(); + public static List CustomLoadingTextures { get; private set; } = new List(); static Random random = new Random(Guid.NewGuid().GetHashCode()); delegate void MyDelegate(string text); - private static List mods; + private static List mods; public override void Patch(Harmony patcher) { @@ -97,7 +97,7 @@ namespace SeamlessClient.Components //Use custom loading screen GUILoadingScreen myGuiScreenLoading = new GUILoadingScreen(newGameplayScreen, MyGuiScreenGamePlay.Static, customLoadingBackground, customLoadingtext); - + myGuiScreenLoading.OnScreenLoadingFinished += delegate { if (MySession.Static != null) diff --git a/Components/ModAPI.cs b/Components/ModAPI.cs new file mode 100644 index 0000000..b22ee92 --- /dev/null +++ b/Components/ModAPI.cs @@ -0,0 +1,119 @@ +using HarmonyLib; +using Sandbox.Game.World; +using SeamlessClient.Utilities; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading.Tasks; +using VRage.Collections; +using VRage.Game; +using VRage.Game.Components; +using VRage.Utils; + +namespace SeamlessClient.Components +{ + /// + /// ModAPI so that mods can register seamless events + /// + public class ModAPI : ComponentBase + { + private static FieldInfo SessionComponents; + private static List LoadedMods = new List(); + + public class LoadedMod + { + public MethodInfo SeamlessServerUnload; + public MethodInfo SeamlessServerLoad; + public MySessionComponentBase ModSession; + } + + + public override void Patch(Harmony patcher) + { + + var AddModAssembly = PatchUtils.GetMethod(typeof(MySession), "TryRegisterSessionComponent"); + patcher.Patch(AddModAssembly, postfix: new HarmonyMethod(Get(typeof(ModAPI), nameof(AddModAssembly)))); + + SessionComponents = PatchUtils.GetField(typeof(MySession), "m_sessionComponents"); + + + base.Patch(patcher); + } + + public static void ClearCache() + { + LoadedMods.Clear(); + } + + public static void StartModSwitching() + { + + Seamless.TryShow($"Invoking SeamlessUnload API on {LoadedMods.Count} mods!"); + foreach (var mod in LoadedMods) + { + try + { + mod.SeamlessServerUnload?.Invoke(mod.ModSession, null); + } + catch (Exception ex) + { + Seamless.TryShow(ex, $"Error during modAPI unloading! {mod.SeamlessServerUnload.Name}"); + } + } + } + + public static void ServerSwitched() + { + Seamless.TryShow($"Invoking SeamlessServerLoad API on {LoadedMods.Count} mods!"); + foreach (var mod in LoadedMods) + { + try + { + mod.SeamlessServerLoad?.Invoke(mod.ModSession, null); + } + catch (Exception ex) + { + Seamless.TryShow(ex, $"Error during modAPI loading! {mod.SeamlessServerLoad.Name}"); + } + } + } + + + public static void AddModAssembly(Type type, bool modAssembly, MyModContext context) + { + + if (!modAssembly || context == null) + return; + + CachingDictionary dict = (CachingDictionary)SessionComponents.GetValue(MySession.Static); + dict.TryGetValue(type, out MySessionComponentBase component); + Seamless.TryShow($"Loading Mod Assembly: {component.ComponentType.FullName}"); + + MethodInfo Load = AccessTools.Method(component.ComponentType, "SeamlessServerLoaded"); + MethodInfo Unload = AccessTools.Method(component.ComponentType, "SeamlessServerUnloaded"); + + if(Load != null || Unload != null) + { + + LoadedMod newMod = new LoadedMod(); + newMod.SeamlessServerLoad = Load; + newMod.SeamlessServerUnload = Unload; + newMod.ModSession = component; + + Seamless.TryShow($"Mod Assembly: {component.ComponentType.FullName} has SeamlessServerLoaded/SeamlessServerUnloaded methods!"); + + LoadedMods.Add(newMod); + return; + } + + + + + + } + + + } +} diff --git a/Components/MyGUIScreenMedicalsPatch.cs b/Components/MyGUIScreenMedicalsPatch.cs index 977e0f1..e656271 100644 --- a/Components/MyGUIScreenMedicalsPatch.cs +++ b/Components/MyGUIScreenMedicalsPatch.cs @@ -55,8 +55,8 @@ namespace SeamlessClient.Components item.GetCell(0).Text.Append("Nexus Lobby"); } } - - + + diff --git a/Components/MyHudTimeTracker.cs b/Components/MyHudTimeTracker.cs index 2eac119..7c543fe 100644 --- a/Components/MyHudTimeTracker.cs +++ b/Components/MyHudTimeTracker.cs @@ -51,11 +51,11 @@ namespace SeamlessClient.Components double t = Math.Round(CalculateTimeToTarget(v0, distance), 0); - - if(t <= 0) + + if (t <= 0) return; stringBuilder.AppendLine($" [T-{FormatDuration(t)}]"); diff --git a/Components/PlayersWindowComponent.cs b/Components/PlayersWindowComponent.cs index aae1cfb..8a1bfe2 100644 --- a/Components/PlayersWindowComponent.cs +++ b/Components/PlayersWindowComponent.cs @@ -47,7 +47,7 @@ namespace SeamlessClient.OnlinePlayersWindow foreach (OnlineClientServer server in servers) { - if(server.ServerID == CurrentServer) + if (server.ServerID == CurrentServer) { onlineServer = server; continue; diff --git a/Components/ServerSwitcherComponent.cs b/Components/ServerSwitcherComponent.cs index 2e0a88f..803f011 100644 --- a/Components/ServerSwitcherComponent.cs +++ b/Components/ServerSwitcherComponent.cs @@ -91,7 +91,7 @@ namespace SeamlessClient.ServerSwitching private void PauseResetTimer_Elapsed(object sender, ElapsedEventArgs e) { - if(MySandboxGame.IsPaused) + if (MySandboxGame.IsPaused) { Seamless.TryShow("Game is still paused... Attempting to unpause!"); MySandboxGame.PausePop(); @@ -164,7 +164,7 @@ namespace SeamlessClient.ServerSwitching - + //SendPlayerData @@ -179,6 +179,7 @@ namespace SeamlessClient.ServerSwitching MySandboxGame.PausePop(); } + ModAPI.ServerSwitched(); SendPlayerData.Invoke(MyMultiplayer.Static, new object[] { MyGameService.OnlineName }); isSwitch = false; } @@ -186,19 +187,19 @@ namespace SeamlessClient.ServerSwitching public static bool LoadClientsFromWorld(ref List clients) { - if(!isSwitch || clients == null || clients.Count == 0) + if (!isSwitch || clients == null || clients.Count == 0) return true; - + //Dictionary IDictionary m_memberData = (IDictionary)PatchUtils.GetField(PatchUtils.ClientType, "m_memberData").GetValue(MyMultiplayer.Static); - + Seamless.TryShow($"{m_memberData.Count} members from clients"); var keys = m_memberData.Keys.Cast(); - for(int i = clients.Count - 1; i >= 0; i-- ) + for (int i = clients.Count - 1; i >= 0; i--) { Seamless.TryShow($"Client {clients[i].SteamId}"); if (keys.Contains(clients[i].SteamId)) @@ -206,8 +207,8 @@ namespace SeamlessClient.ServerSwitching Seamless.TryShow($"Remove {clients[i].SteamId}"); clients.RemoveAt(i); } - - + + } return false; @@ -217,7 +218,7 @@ namespace SeamlessClient.ServerSwitching private static bool ProcessAllMembersData(ref AllMembersDataMsg msg) { - if(!isSwitch) + if (!isSwitch) return true; @@ -245,7 +246,7 @@ namespace SeamlessClient.ServerSwitching private static bool RemovePlayerFromDict(MyPlayer.PlayerId playerId) { //Seamless.TryShow($"Removing player {playerId.SteamId} from dictionariy! \n {Environment.StackTrace.ToString()} - Sender: {MyEventContext.Current.Sender}"); - + return true; @@ -272,7 +273,7 @@ namespace SeamlessClient.ServerSwitching { if (steamId != Sync.MyId) return; - + //Seamless.TryShow(Environment.StackTrace.ToString()); } @@ -307,13 +308,13 @@ namespace SeamlessClient.ServerSwitching originalLocalCharacter = MySession.Static.LocalCharacter; //originalControlledEntity = MySession.Static.ControlledEntity; - + player = MySession.Static.LocalHumanPlayer.GetObjectBuilder(); player.Connected = false; - AsyncInvoke.InvokeAsync(() => + AsyncInvoke.InvokeAsync(() => { Seamless.TryShow($"Needs entity Unload: {needsEntityUnload}"); @@ -326,7 +327,8 @@ namespace SeamlessClient.ServerSwitching UnloadServer(); SetNewMultiplayerClient(); - }catch(Exception ex) + } + catch (Exception ex) { Seamless.TryShow(ex.ToString()); } @@ -390,7 +392,7 @@ namespace SeamlessClient.ServerSwitching //client.Disconnect(); - + MyGameService.Peer2Peer.CloseSession(Sync.ServerId); @@ -422,7 +424,7 @@ namespace SeamlessClient.ServerSwitching Seamless.TryShow($"2 NexusMajor: {Seamless.NexusVersion.Major} - ConrolledEntity {MySession.Static.ControlledEntity == null} - HumanPlayer {MySession.Static.LocalHumanPlayer == null} - Character {MySession.Static.LocalCharacter == null}"); - + ModAPI.StartModSwitching(); //MyMultiplayer.Static.ReplicationLayer.Disconnect(); //MyMultiplayer.Static.ReplicationLayer.Dispose(); @@ -464,7 +466,7 @@ namespace SeamlessClient.ServerSwitching Seamless.TryShow($"3 Streaming: {clienta.HasPendingStreamingReplicables} - LastMessage: {clienta.LastMessageFromServer}"); Seamless.TryShow($"3 NexusMajor: {Seamless.NexusVersion.Major} - ConrolledEntity {MySession.Static.ControlledEntity == null} - HumanPlayer {MySession.Static.LocalHumanPlayer == null} - Character {MySession.Static.LocalCharacter == null}"); - + MySandboxGame.Static.SessionCompatHelper.FixSessionComponentObjectBuilders(TargetWorld.Checkpoint, TargetWorld.Sector); @@ -473,7 +475,7 @@ namespace SeamlessClient.ServerSwitching PatchUtils.ClientType.GetProperty("Server", BindingFlags.Public | BindingFlags.Instance).SetValue(MyMultiplayer.Static, TargetServer); typeof(MyMultiplayerBase).GetProperty("ServerId", BindingFlags.Public | BindingFlags.Instance).SetValue(MyMultiplayer.Static, TargetServer.SteamID); - + MyGameService.ConnectToServer(TargetServer, delegate (JoinResult joinResult) { @@ -503,7 +505,7 @@ namespace SeamlessClient.ServerSwitching MyMultiplayer.Static = UtilExtensions.CastToReflected(instance, PatchUtils.ClientType); MyMultiplayer.Static.ExperimentalMode = true; - + // Set the new SyncLayer to the MySession.Static.SyncLayer MySessionLayer.SetValue(MySession.Static, MyMultiplayer.Static.SyncLayer); @@ -521,7 +523,7 @@ namespace SeamlessClient.ServerSwitching private void StartSwitch() { - + MyReplicationClient clienta = (MyReplicationClient)MyMultiplayer.Static.ReplicationLayer; Seamless.TryShow($"5 Streaming: {clienta.HasPendingStreamingReplicables} - LastMessage: {clienta.LastMessageFromServer}"); Seamless.TryShow($"5 NexusMajor: {Seamless.NexusVersion.Major} - ConrolledEntity {MySession.Static.ControlledEntity == null} - HumanPlayer {MySession.Static.LocalHumanPlayer == null} - Character {MySession.Static.LocalCharacter == null}"); @@ -534,14 +536,14 @@ namespace SeamlessClient.ServerSwitching - List clients = new List(); - foreach(var client in Sync.Clients.GetClients()) + List clients = new List(); + foreach (var client in Sync.Clients.GetClients()) { clients.Add(client.SteamUserId); Seamless.TryShow($"ADDING {client.SteamUserId} - {Sync.MyId}"); } - foreach(var client in clients) + foreach (var client in clients) { if (client == TargetServer.SteamID || client == Sync.MyId) continue; @@ -573,7 +575,7 @@ namespace SeamlessClient.ServerSwitching typeof(MySandboxGame).GetField("m_pauseStackCount", BindingFlags.Static | BindingFlags.NonPublic).SetValue(null, 0); - + MyHud.Chat.RegisterChat(MyMultiplayer.Static); //GpsRegisterChat.Invoke(MySession.Static.Gpss, new object[] { MyMultiplayer.Static }); @@ -582,12 +584,12 @@ namespace SeamlessClient.ServerSwitching //Recreate all controls... Will fix weird gui/paint/crap //MyGuiScreenHudSpace.Static?.RecreateControls(true); - + Seamless.TryShow($"6 NexusMajor: {Seamless.NexusVersion.Major} - ConrolledEntity {MySession.Static.ControlledEntity == null} - HumanPlayer {MySession.Static.LocalHumanPlayer == null} - Character {MySession.Static.LocalCharacter == null}"); Seamless.TryShow($"6 Streaming: {clienta.HasPendingStreamingReplicables} - LastMessage: {clienta.LastMessageFromServer}"); - + originalLocalCharacter?.Close(); ResetReplicationTime(true); @@ -764,7 +766,7 @@ namespace SeamlessClient.ServerSwitching } } - private void StartEntitySync() + private void StartEntitySync() { Seamless.TryShow("Requesting Player From Server"); @@ -772,7 +774,7 @@ namespace SeamlessClient.ServerSwitching if (!Sandbox.Engine.Platform.Game.IsDedicated && MySession.Static.LocalHumanPlayer == null) { Seamless.TryShow("RequestNewPlayer"); - + } else if (MySession.Static.ControlledEntity == null && Sync.IsServer && !Sandbox.Engine.Platform.Game.IsDedicated) @@ -782,7 +784,7 @@ namespace SeamlessClient.ServerSwitching MyPlayerCollection.RequestLocalRespawn(); } - + //Request client state batch (MyMultiplayer.Static as MyMultiplayerClientBase).RequestBatchConfirmation(); @@ -832,7 +834,7 @@ namespace SeamlessClient.ServerSwitching player.IsWildlifeAgent = true; CreateNewPlayerInternal.Invoke(MySession.Static.Players, new object[] { Sync.Clients.LocalClient, savingPlayerId.Value, player }); - typeof(MyPlayerCollection).GetMethod("LoadPlayerInternal", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(MySession.Static.Players, new object[] { savingPlayerId.Value, player, false }); + typeof(MyPlayerCollection).GetMethod("LoadPlayerInternal", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(MySession.Static.Players, new object[] { savingPlayerId.Value, player, false }); Seamless.TryShow("Saving PlayerID: " + savingPlayerId.ToString()); diff --git a/Components/ServerSwitcherComponentOLD.cs b/Components/ServerSwitcherComponentOLD.cs index f47514c..f378b04 100644 --- a/Components/ServerSwitcherComponentOLD.cs +++ b/Components/ServerSwitcherComponentOLD.cs @@ -69,17 +69,17 @@ namespace SeamlessClient.Components if (WaitingForClientCheck == false && isSeamlessSwitching) WaitingForClientCheck = true; - if(WaitingForClientCheck && MySession.Static.LocalHumanPlayer != null) + if (WaitingForClientCheck && MySession.Static.LocalHumanPlayer != null) WaitingForClientCheck = false; if (isSeamlessSwitching || WaitingForClientCheck) { //SeamlessClient.TryShow("Switching Servers!"); - MyRenderProxy.DebugDrawText2D(new VRageMath.Vector2(MySandboxGame.ScreenViewport.Width/2, MySandboxGame.ScreenViewport.Height - 150), SwitchingText, VRageMath.Color.AliceBlue, 1f, MyGuiDrawAlignEnum.HORISONTAL_CENTER_AND_VERTICAL_CENTER); + MyRenderProxy.DebugDrawText2D(new VRageMath.Vector2(MySandboxGame.ScreenViewport.Width / 2, MySandboxGame.ScreenViewport.Height - 150), SwitchingText, VRageMath.Color.AliceBlue, 1f, MyGuiDrawAlignEnum.HORISONTAL_CENTER_AND_VERTICAL_CENTER); MyRenderProxy.DebugDrawText2D(new VRageMath.Vector2(MySandboxGame.ScreenViewport.Width / 2, MySandboxGame.ScreenViewport.Height - 200), $"Transferring to {TargetServer.Name}", VRageMath.Color.Yellow, 1.5f, MyGuiDrawAlignEnum.HORISONTAL_CENTER_AND_VERTICAL_CENTER); - MyRenderProxy.DebugDrawLine2D(new VRageMath.Vector2((MySandboxGame.ScreenViewport.Width / 2) - 250, MySandboxGame.ScreenViewport.Height - 170), new VRageMath.Vector2((MySandboxGame.ScreenViewport.Width / 2)+250, MySandboxGame.ScreenViewport.Height - 170), VRageMath.Color.Blue, VRageMath.Color.Green); + MyRenderProxy.DebugDrawLine2D(new VRageMath.Vector2((MySandboxGame.ScreenViewport.Width / 2) - 250, MySandboxGame.ScreenViewport.Height - 170), new VRageMath.Vector2((MySandboxGame.ScreenViewport.Width / 2) + 250, MySandboxGame.ScreenViewport.Height - 170), VRageMath.Color.Blue, VRageMath.Color.Green); } } @@ -87,7 +87,7 @@ namespace SeamlessClient.Components public override void Patch(Harmony patcher) { TransportLayerConstructor = PatchUtils.GetConstructor(PatchUtils.MyTransportLayerType, new[] { typeof(int) }); - SyncLayerConstructor = PatchUtils.GetConstructor(PatchUtils.SyncLayerType, new[] { PatchUtils.MyTransportLayerType }); + SyncLayerConstructor = PatchUtils.GetConstructor(PatchUtils.SyncLayerType, new[] { PatchUtils.MyTransportLayerType }); ClientConstructor = PatchUtils.GetConstructor(PatchUtils.ClientType, new[] { typeof(MyGameServerItem), PatchUtils.SyncLayerType }); MySessionLayer = PatchUtils.GetProperty(typeof(MySession), "SyncLayer"); @@ -100,8 +100,51 @@ namespace SeamlessClient.Components InitVirtualClients = PatchUtils.GetMethod(PatchUtils.VirtualClientsType, "Init"); VirtualClients = PatchUtils.GetField(typeof(MySession), "VirtualClients"); - patcher.Patch(onJoin, postfix: new HarmonyMethod(Get(typeof(ServerSwitcherComponentOLD), nameof(OnUserJoined)))); - base.Patch(patcher); + patcher.Patch(onJoin, postfix: new HarmonyMethod(Get(typeof(ServerSwitcherComponentOLD), nameof(OnUserJoined)))); + + + } + + public override void Initilized() + { + MyAPIGateway.Utilities.MessageEntered += Utilities_MessageEntered; + } + + private void Utilities_MessageEntered(string messageText, ref bool sendToOthers) + { + if (!messageText.StartsWith("/nexus")) + return; + + string[] cmd = messageText.ToLowerInvariant().Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); + + if (cmd[1] == "refreshcharacter") + { + if (MySession.Static.LocalHumanPlayer == null) + { + MyAPIGateway.Utilities?.ShowMessage("Seamless", "LocalHumanPlayer Null!"); + return; + } + + if (MySession.Static.LocalHumanPlayer.Character == null) + { + MyAPIGateway.Utilities?.ShowMessage("Seamless", "LocalHumanPlayerCharacter Null!"); + return; + } + + + //None of this shit works.... 5/3/2025 + MySession.Static.LocalHumanPlayer.SpawnIntoCharacter(MySession.Static.LocalHumanPlayer.Character); + MySession.Static.LocalHumanPlayer.Controller.TakeControl(MySession.Static.LocalHumanPlayer.Character); + + MySession.Static.LocalHumanPlayer.Character.GetOffLadder(); + MySession.Static.LocalHumanPlayer.Character.Stand(); + + MySession.Static.LocalHumanPlayer.Character.ResetControls(); + MySession.Static.LocalHumanPlayer.Character.UpdateCharacterPhysics(true); + + MyAPIGateway.Utilities?.ShowMessage("Seamless", "Character Controls Reset!"); + + } } private static void OnUserJoined(ref JoinResultMsg msg) @@ -111,14 +154,16 @@ namespace SeamlessClient.Components //SeamlessClient.TryShow("User Joined! Result: " + msg.JoinResult.ToString()); //Invoke the switch event + SwitchingText = "Server Responded! Removing Old Entities and forcing client connection!"; RemoveOldEntities(); ForceClientConnection(); + ModAPI.ServerSwitched(); - //Fix any character issues - if (MySession.Static.LocalCharacter != null) - MySession.Static.LocalHumanPlayer.SpawnIntoCharacter(MySession.Static.LocalCharacter); + + + MySession.Static.LocalHumanPlayer?.Character?.Stand(); isSeamlessSwitching = false; } } @@ -126,12 +171,12 @@ namespace SeamlessClient.Components public void StartBackendSwitch(MyGameServerItem _TargetServer, MyObjectBuilder_World _TargetWorld) { - if(MySession.Static.LocalCharacter != null) + if (MySession.Static.LocalCharacter != null) { var viewMatrix = MySession.Static.LocalCharacter.GetViewMatrix(); MySpectator.Static.SetViewMatrix(viewMatrix); } - + SwitchingText = "Starting Seamless Switch... Please wait!"; isSeamlessSwitching = true; OldArmorSkin = MySession.Static.LocalHumanPlayer.BuildArmorSkin; @@ -140,10 +185,15 @@ namespace SeamlessClient.Components MySandboxGame.Static.Invoke(delegate { + //Pause the game/update thread while we load + MySandboxGame.IsPaused = true; + //Set camera controller to fixed spectator MySession.Static.SetCameraController(MyCameraControllerEnum.SpectatorFixed); UnloadCurrentServer(); SetNewMultiplayerClient(); + ModAPI.StartModSwitching(); + //SeamlessClient.IsSwitching = false; SwitchingText = "Waiting for server response..."; @@ -172,7 +222,7 @@ namespace SeamlessClient.Components SwitchingText = "New Multiplayer Session Set"; Seamless.TryShow("Successfully set MyMultiplayer.Static"); - + MySandboxGame.IsPaused = true; Sync.Clients.SetLocalSteamId(Sync.MyId, false, MyGameService.UserName); Sync.Players.RegisterEvents(); @@ -184,6 +234,8 @@ namespace SeamlessClient.Components private static void ForceClientConnection() { + + //Set World Settings SetWorldSettings(); @@ -217,6 +269,8 @@ namespace SeamlessClient.Components StartEntitySync(); + //Resume the game/update thread + MySandboxGame.IsPaused = false; MyHud.Chat.RegisterChat(MyMultiplayer.Static); GpsRegisterChat.Invoke(MySession.Static.Gpss, new object[] { MyMultiplayer.Static }); @@ -228,6 +282,12 @@ namespace SeamlessClient.Components //Recreate all controls... Will fix weird gui/paint/crap MyGuiScreenHudSpace.Static.RecreateControls(true); SwitchingText = "Client Registered. Waiting for entities from server..."; + + + Seamless.TryShow($"LocalHumanPlayer = {MySession.Static.LocalHumanPlayer == null}"); + + + //MySession.Static.LocalHumanPlayer.BuildArmorSkin = OldArmorSkin; } @@ -392,6 +452,8 @@ namespace SeamlessClient.Components private static void LoadConnectedClients() { + //TargetWorld.Checkpoint.AllPlayers.Count + Seamless.TryShow($"Loading members from world... {TargetWorld.Checkpoint.AllPlayers.Count}"); LoadMembersFromWorld.Invoke(MySession.Static, new object[] { TargetWorld, MyMultiplayer.Static }); @@ -511,7 +573,8 @@ namespace SeamlessClient.Components //Close any respawn screens that are open MyGuiScreenMedicals.Close(); - //MySession.Static.UnloadDataComponents(); + //Unload any lingering updates queued + MyEntities.Orchestrator.Unload(); } @@ -520,7 +583,11 @@ namespace SeamlessClient.Components foreach (var ent in MyEntities.GetEntities()) { if (ent is MyPlanet) + { + //Re-Add planet updates + MyEntities.RegisterForUpdate(ent); continue; + } ent.Close(); } @@ -530,7 +597,7 @@ namespace SeamlessClient.Components { AccessTools.Field(typeof(MyCoordinateSystem), "m_lastCoordSysId").SetValue(MyCoordinateSystem.Static, 1L); AccessTools.Field(typeof(MyCoordinateSystem), "m_localCoordSystems").SetValue(MyCoordinateSystem.Static, new Dictionary()); - } + } diff --git a/GUI/Screens/GUILoadingScreen.cs b/GUI/Screens/GUILoadingScreen.cs index c076632..8947cc5 100644 --- a/GUI/Screens/GUILoadingScreen.cs +++ b/GUI/Screens/GUILoadingScreen.cs @@ -113,7 +113,7 @@ namespace SeamlessClient.GUI.Screens m_isTopMostScreen = true; object instance = PatchUtils.GetProperty(PatchUtils.MyLoadingPerformance, "Instance").GetValue(null); - if(instance != null) + if (instance != null) PatchUtils.GetMethod(PatchUtils.MyLoadingPerformance, "StartTiming").Invoke(instance, null); //MyLoadingPerformance.Instance.StartTiming(); @@ -160,7 +160,7 @@ namespace SeamlessClient.GUI.Screens MySession.LoadingLocalTotalSet = (Action)Delegate.Combine(MySession.LoadingLocalTotalSet, new Action(SetLocalTotal)); - + @@ -200,8 +200,8 @@ namespace SeamlessClient.GUI.Screens m_multiTextControl.OriginAlign = MyGuiDrawAlignEnum.HORISONTAL_CENTER_AND_VERTICAL_BOTTOM; m_multiTextControl.TextBoxAlign = MyGuiDrawAlignEnum.HORISONTAL_CENTER_AND_VERTICAL_BOTTOM; Controls.Add(m_wheel); - - + + RefreshText(); } @@ -235,12 +235,12 @@ namespace SeamlessClient.GUI.Screens MySandboxGame.Log.DecreaseIndent(); MySandboxGame.Log.WriteLine("MyGuiScreenLoading.LoadContent - END"); - + } public static string GetRandomBackgroundTexture() { - + string text = MyUtils.GetRandomInt(MyPerGameSettings.GUI.LoadingScreenIndexRange.X, MyPerGameSettings.GUI.LoadingScreenIndexRange.Y + 1).ToString().PadLeft(3, '0'); return "Textures\\GUI\\Screens\\loading_background_" + text + ".dds"; @@ -531,9 +531,9 @@ namespace SeamlessClient.GUI.Screens MyGuiManager.GetSafeHeightFullScreenPictureSize(MyGuiConstants.LOADING_BACKGROUND_TEXTURE_REAL_SIZE, out outRect); bool isCustom = false; - if(LoadingScreenComponent.CustomLoadingTextures.Count != 0) + if (LoadingScreenComponent.CustomLoadingTextures.Count != 0) { - if(LoadingScreenComponent.CustomLoadingTextures.Count > 1) + if (LoadingScreenComponent.CustomLoadingTextures.Count > 1) { if (!backgroundTimer.Enabled) { @@ -567,9 +567,9 @@ namespace SeamlessClient.GUI.Screens MyGuiManager.DrawString(m_font, loading, MyGuiConstants.LOADING_PLEASE_WAIT_POSITION, MyGuiSandbox.GetDefaultTextScaleWithLanguage() * 1.1f, new Color(MyGuiConstants.LOADING_PLEASE_WAIT_COLOR * m_transitionAlpha), MyGuiDrawAlignEnum.HORISONTAL_CENTER_AND_VERTICAL_BOTTOM); MyGuiManager.DrawString(m_font, string.Format("{0}%", m_displayProgress), MyGuiConstants.LOADING_PERCENTAGE_POSITION, MyGuiSandbox.GetDefaultTextScaleWithLanguage() * 1.1f, new Color(MyGuiConstants.LOADING_PLEASE_WAIT_COLOR * m_transitionAlpha), MyGuiDrawAlignEnum.HORISONTAL_CENTER_AND_VERTICAL_BOTTOM); - - - + + + if (!isCustom && string.IsNullOrEmpty(m_customTextFromConstructor)) { string font = m_font; @@ -590,7 +590,7 @@ namespace SeamlessClient.GUI.Screens } - + private void BackgroundTimer_Elapsed(object sender, ElapsedEventArgs e) { diff --git a/Messages/WorldRequestData.cs b/Messages/WorldRequestData.cs index 106b450..40b8cef 100644 --- a/Messages/WorldRequestData.cs +++ b/Messages/WorldRequestData.cs @@ -37,7 +37,7 @@ namespace SeamlessClient.Messages this.IdentityID = playerIdentity; } - + public void SerializeWorldData(MyObjectBuilder_World WorldData) { diff --git a/OnlinePlayersWindow/OnlineNexusPlayersWindow.cs b/OnlinePlayersWindow/OnlineNexusPlayersWindow.cs index 4f304dc..304ca05 100644 --- a/OnlinePlayersWindow/OnlineNexusPlayersWindow.cs +++ b/OnlinePlayersWindow/OnlineNexusPlayersWindow.cs @@ -385,8 +385,6 @@ namespace SeamlessClient.OnlinePlayersWindow Controls.Add(m_playersTable); - string servername = PlayersWindowComponent.onlineServer?.ServerName ?? "thisServer"; - foreach (MyPlayer onlinePlayer in Sync.Players.GetOnlinePlayers()) { if (onlinePlayer.Id.SerialId != 0) @@ -403,16 +401,14 @@ namespace SeamlessClient.OnlinePlayersWindow } } - - - AddPlayer(onlinePlayer.Id.SteamId, servername); + AddPlayer(onlinePlayer.Id.SteamId, $"{MyMultiplayer.Static.HostName ?? "thisServer"}*"); } - foreach(var server in PlayersWindowComponent.allServers) + foreach (var server in PlayersWindowComponent.allServers) { - foreach(var player in server.Players) + foreach (var player in server.Players) AddPlayerFromOtherServer(player, server.ServerName); } @@ -796,7 +792,7 @@ namespace SeamlessClient.OnlinePlayersWindow protected void Multiplayer_PlayerJoined(ulong userId, string userName) { - AddPlayer(userId, PlayersWindowComponent.onlineServer.ServerName); + AddPlayer(userId, PlayersWindowComponent.onlineServer?.ServerName ?? "Unknown"); } protected void Multiplayer_PlayerLeft(ulong userId, MyChatMemberStateChangeEnum arg2) @@ -962,7 +958,7 @@ namespace SeamlessClient.OnlinePlayersWindow { Seamless.TryShow("Requesting Refresh Pings!"); MyMultiplayer.RaiseStaticEvent((IMyEventOwner s) => MyGuiScreenPlayers.RequestPingsAndRefresh); - + } diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs index 6ef9a1b..2c242b5 100644 --- a/Properties/AssemblyInfo.cs +++ b/Properties/AssemblyInfo.cs @@ -33,5 +33,5 @@ using System.Runtime.InteropServices; // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("3.0.0.6")] //Set these both to the same -[assembly: AssemblyFileVersion("3.0.0.6")] +[assembly: AssemblyVersion("3.0.0.10")] //Set these both to the same +[assembly: AssemblyFileVersion("3.0.0.10")] diff --git a/README.md b/README.md index 0483d28..cf288ac 100644 --- a/README.md +++ b/README.md @@ -13,13 +13,21 @@ With Nexus servers, all data is shared between servers. (Factions, Identities, P ## How to install -Simply install the plguin loader, and check this plugins box to be added to the plugin loaders' active plugin list. (SE will need to be restarted afterwards) - +Simply install the plugin loader, and check this plugin's box to be added to the plugin loaders' active plugin list. (SE will need to be restarted afterwards) ## Known issues -Obviously this is not an issue free-system. Currently since im doing no mod unloading or loading there could be issues if your servers dont have the exact same mods, or the mods dont properly work right. Please do not swarm mod authors with faults if seamless doesnt play nice with it. ***Its not their fault*** its ***mine***. I will be trying to implement mod unloading and loading switching between servers, just no ETA. +Obviously this is not an issue-free system. Currently, since im doing no mod unloading or loading, there could be issues if your servers don't have the same mods, or the mods don't work right. Please do not swarm mod authors with faults if seamless doesn't play nice with it. ***Its not their fault*** its ***mine***. I will be trying to implement mod unloading and loading switching between servers, just no ETA. + +## ModAPI +I attempted to avoid implementing any modAPI in seamless, but unfortunately, Space Engineers doesn't handle unloading and reloading the same mod without compiling easily. Either I compile the mod every time you switch servers, eventually running into memory issues, or I attempt to unload the mod manually and restart it. +In both scenarios, unloading static variables are often up to the mod author and sometimes are set to null. On mod load, these variables are not re-instantiated with the default values resulting in many issues. It is way easier for mod authors when needed implement seamless unload and load logic appropriately. + +There are two methods you can add to your mods (In the main mod session component class) + +private void SeamlessServerLoaded(){} +private void SeamlessServerUnloaded(){} + +Unloaded happens when seamless starts switching, Loaded when seamless is done switching. Seamless patches these methods on mod compilation. -## Fork Changes -This fork changes how components are loaded to be compatible with [CringeLauncher](https://git.zznty.ru/PvE/se-launcher). \ No newline at end of file diff --git a/Seamless.cs b/Seamless.cs index f38070a..469d91a 100644 --- a/Seamless.cs +++ b/Seamless.cs @@ -2,6 +2,7 @@ using HarmonyLib; using NLog.Fluent; using Sandbox; +using Sandbox.Engine.Multiplayer; using Sandbox.Game.Localization; using Sandbox.Game.World; using Sandbox.ModAPI; @@ -39,7 +40,7 @@ namespace SeamlessClient public static bool isSeamlessServer { get; private set; } = false; public static bool isDebug = false; public static bool UseNewVersion = false; - + public void Init(object gameInstance) @@ -49,10 +50,15 @@ namespace SeamlessClient GetComponents(); PatchComponents(SeamlessPatcher); + MySession.LoadingStep += SessionLoaded; } - + private void SessionLoaded(LoadingProgress progress) + { + if (progress >= LoadingProgress.PROGRESS_STEP8) + SendSeamlessVersion(); + } private void GetComponents() { @@ -93,16 +99,19 @@ namespace SeamlessClient } } + + private void InitilizeComponents() { - foreach(ComponentBase component in allComps) + foreach (ComponentBase component in allComps) { try { component.Initilized(); TryShow($"Initilized {component.GetType()}"); - }catch(Exception ex) + } + catch (Exception ex) { TryShow(ex, $"Failed to initialize {component.GetType()}"); } @@ -126,24 +135,18 @@ namespace SeamlessClient isSeamlessServer = true; switch (msg.MessageType) { - case ClientMessageType.FirstJoin: - Seamless.TryShow("Sending First Join!"); - SendSeamlessVersion(); - break; - case ClientMessageType.TransferServer: StartSwitch(msg.GetTransferData()); break; case ClientMessageType.OnlinePlayers: - //Not implemented yet var playerData = msg.GetOnlinePlayers(); PlayersWindowComponent.ApplyRecievedPlayers(playerData.OnlineServers, playerData.currentServerID); break; } } - + public static void SendSeamlessVersion() @@ -151,13 +154,15 @@ namespace SeamlessClient ClientMessage response = new ClientMessage(SeamlessVersion.ToString()); MyAPIGateway.Multiplayer?.SendMessageToServer(SeamlessClientNetId, MessageUtils.Serialize(response)); Seamless.TryShow("Sending Seamless request..."); + + } public void Dispose() { - + } public void Update() { diff --git a/SeamlessClient.xml b/SeamlessClient.xml index f16e4d6..3d0c250 100644 --- a/SeamlessClient.xml +++ b/SeamlessClient.xml @@ -1,23 +1,23 @@  - - Casimir255/SeamlessClient + + Casimir255/SeamlessClient - - NexusSeamless + + NexusSeamless - - SeamlessClient + + SeamlessClient - - Casimir + + Casimir - - Allows transferring between Nexus enabled servers seamlessly + + Allows transferring between Nexus enabled servers seamlessly - - This plugin allows seamless transfers between Nexus enabled servers. Some mods or plugins may not play nice with switching between servers. Be cautious when using! + + This plugin allows seamless transfers between Nexus enabled servers. Some mods or plugins may not play nice with switching between servers. Be cautious when using! - - 7a5ff795fba108309810a4cde7f414c1ffb1ba73 + + 7a5ff795fba108309810a4cde7f414c1ffb1ba73 \ No newline at end of file diff --git a/Utilities/PatchUtils.cs b/Utilities/PatchUtils.cs index e7386a7..39ea9dc 100644 --- a/Utilities/PatchUtils.cs +++ b/Utilities/PatchUtils.cs @@ -70,7 +70,7 @@ Type.GetType("Sandbox.Game.Screens.Helpers.MyLoadingScreenText, Sandbox.Game"); /* Static MethodInfos */ public static MethodInfo LoadPlayerInternal { get; private set; } - + @@ -84,7 +84,7 @@ Type.GetType("Sandbox.Game.Screens.Helpers.MyLoadingScreenText, Sandbox.Game"); public override void Patch(Harmony patcher) { - + MySessionConstructor = GetConstructor(MySessionType, new[] { typeof(MySyncLayer), typeof(bool) }); MyMultiplayerClientBaseConstructor = GetConstructor(MyMultiplayerClientBase, new[] { typeof(MySyncLayer) }); @@ -92,12 +92,12 @@ Type.GetType("Sandbox.Game.Screens.Helpers.MyLoadingScreenText, Sandbox.Game"); /* Get Methods */ LoadPlayerInternal = GetMethod(typeof(MyPlayerCollection), "LoadPlayerInternal"); - - - - //MethodInfo ConnectToServer = GetMethod(typeof(MyGameService), "ConnectToServer", BindingFlags.Static | BindingFlags.Public); + + + + base.Patch(patcher); }