diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs index f78dd9c..6aaef64 100644 --- a/Properties/AssemblyInfo.cs +++ b/Properties/AssemblyInfo.cs @@ -1,4 +1,5 @@ -using System.Reflection; +using System.Resources; +using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -6,9 +7,9 @@ using System.Runtime.InteropServices; // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyTitle("SeamlessClientPlugin")] -[assembly: AssemblyDescription("")] +[assembly: AssemblyDescription("A seamless client plugin for Nexus compatible servers")] [assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] +[assembly: AssemblyCompany("Casimir")] [assembly: AssemblyProduct("SeamlessClientPlugin")] [assembly: AssemblyCopyright("Copyright © 2020")] [assembly: AssemblyTrademark("")] @@ -32,5 +33,6 @@ using System.Runtime.InteropServices; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: AssemblyVersion("1.3.0.1")] +[assembly: AssemblyFileVersion("1.3.0.1")] +[assembly: NeutralResourcesLanguage("en")] diff --git a/SeamlessClient.cs b/SeamlessClient.cs index c650e20..1214aec 100644 --- a/SeamlessClient.cs +++ b/SeamlessClient.cs @@ -106,7 +106,7 @@ namespace SeamlessClientPlugin - public static string Version = "1.2.20"; + public static string Version = "1.3.01"; public static bool Debug = false; private static bool Initilized = false; @@ -144,8 +144,6 @@ namespace SeamlessClientPlugin { TryShow("Initilizing Communications!"); RunInitilizations(); - - } //OnNewPlayerRequest //throw new NotImplementedException(); @@ -165,18 +163,12 @@ namespace SeamlessClientPlugin } catch (Exception ex) { - TryShow(ex.ToString()); + //TryShow(ex.ToString()); } } - - - - - - - + public static void RunInitilizations() { @@ -214,31 +206,6 @@ namespace SeamlessClientPlugin } - public static void RestartClientAfterUpdate() - { - try - { - TryShow("Restarting Client!"); - string exe = Assembly.GetEntryAssembly().Location; - Process currentProcess = Process.GetCurrentProcess(); - - string[] CommandArgs = Environment.GetCommandLineArgs(); - string NewCommandLine = ""; - for (int i = 1; i < CommandArgs.Length; i++) - { - NewCommandLine += " " + CommandArgs[i]; - } - - TryShow(NewCommandLine); - Process.Start(exe, NewCommandLine); - currentProcess.Kill(); - } - catch (Exception ex) - { - TryShow("Restarting Client error!"); - } - } - public static void TryShow(string message) { if (MySession.Static?.LocalHumanPlayer != null && Debug) diff --git a/SeamlessTransfer/ModLoader.cs b/SeamlessTransfer/ModLoader.cs index 2c495b1..069301b 100644 --- a/SeamlessTransfer/ModLoader.cs +++ b/SeamlessTransfer/ModLoader.cs @@ -10,8 +10,15 @@ namespace SeamlessClientPlugin.SeamlessTransfer { /* Mod loader should download and load missing mods for target server, and unload ones that arent needed * + * Sandbox.Game.World.MyScriptManager.LoadData() is where modded scripts get loaded and added + * Sandbox.Game.World.MySession() calls MyDefinitionManager.Static.LoadData(mods); which loads mod data files * * + * Need to be called in the following order: + * ScriptManager.Init(checkpoint.ScriptManagerData); + * MyDefinitionManager.Static.LoadData(checkpoint.Mods); + * PreloadModels(sector); + * * */ diff --git a/SeamlessTransfer/SwitchServers.cs b/SeamlessTransfer/SwitchServers.cs index eb88313..7cd7a56 100644 --- a/SeamlessTransfer/SwitchServers.cs +++ b/SeamlessTransfer/SwitchServers.cs @@ -14,10 +14,12 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; +using System.Reflection; using System.Text; using System.Threading.Tasks; using VRage; using VRage.Game; +using VRage.Game.Components; using VRage.Game.ModAPI; using VRage.GameServices; using VRage.Steam; @@ -305,8 +307,11 @@ namespace SeamlessClientPlugin.SeamlessTransfer MyPlayerCollection.RequestLocalRespawn(); } + //Request client state batch + (MyMultiplayer.Static as MyMultiplayerClientBase).RequestBatchConfirmation(); + MyMultiplayer.Static.PendingReplicablesDone += MyMultiplayer_PendingReplicablesDone; //typeof(MyGuiScreenTerminal).GetMethod("CreateTabs") - + MySession.Static.LoadDataComponents(); //MyGuiSandbox.LoadData(false); //MyGuiSandbox.AddScreen(MyGuiSandbox.CreateScreen(MyPerGameSettings.GUI.HUDScreen)); @@ -321,24 +326,50 @@ namespace SeamlessClientPlugin.SeamlessTransfer MyGuiScreenHudSpace.Static.RecreateControls(true); } - - - + private void MyMultiplayer_PendingReplicablesDone() + { + if (MySession.Static.VoxelMaps.Instances.Count > 0) + { + MySandboxGame.AreClipmapsReady = false; + } + MyMultiplayer.Static.PendingReplicablesDone -= MyMultiplayer_PendingReplicablesDone; + } private void UpdateWorldGenerator() { //This will re-init the MyProceduralWorldGenerator. (Not doing this will result in asteroids not rendering in properly) - //This shoud never be null - var Generator = MySession.Static.GetComponent(); + + //This shoud never be null + var Generator = MySession.Static.GetComponent(); //Force component to unload Patches.UnloadProceduralWorldGenerator.Invoke(Generator, null); + //Re-call the generator init + MyObjectBuilder_WorldGenerator GeneratorSettings = (MyObjectBuilder_WorldGenerator)TargetWorld.Checkpoint.SessionComponents.FirstOrDefault(x => x.GetType() == typeof(MyObjectBuilder_WorldGenerator)); + if (GeneratorSettings != null) + { + //Re-initilized this component (forces to update asteroid areas like not in planets etc) + Generator.Init(GeneratorSettings); + } + + + //Force component to reload, re-syncing settings and seeds to the destination server Generator.LoadData(); - + + //We need to go in and force planets to be empty areas in the generator. This is originially done on planet init. + FieldInfo PlanetInitArgs = typeof(MyPlanet).GetField("m_planetInitValues", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); + foreach (var Planet in MyEntities.GetEntities().OfType()) + { + MyPlanetInitArguments args = (MyPlanetInitArguments)PlanetInitArgs.GetValue(Planet); + + float MaxRadius = args.MaxRadius; + + Generator.MarkEmptyArea(Planet.PositionComp.GetPosition(), MaxRadius); + } } private void UnloadCurrentServer() diff --git a/Utilities/Patches.cs b/Utilities/Patches.cs index 3abb77b..f575607 100644 --- a/Utilities/Patches.cs +++ b/Utilities/Patches.cs @@ -1,9 +1,13 @@ using HarmonyLib; +using Sandbox.Engine.Analytics; using Sandbox.Engine.Multiplayer; using Sandbox.Engine.Networking; +using Sandbox.Game; +using Sandbox.Game.Gui; using Sandbox.Game.Multiplayer; using Sandbox.Game.World; using Sandbox.Game.World.Generator; +using Sandbox.Graphics; using Sandbox.Graphics.GUI; using System; using System.Collections.Generic; @@ -18,6 +22,7 @@ using VRage.Game; using VRage.GameServices; using VRage.Network; using VRage.Utils; +using VRageMath; namespace SeamlessClientPlugin.SeamlessTransfer { @@ -75,8 +80,8 @@ namespace SeamlessClientPlugin.SeamlessTransfer public static void GetPatches() { //Get reflected values and store them - - + + /* Get Constructors */ @@ -99,7 +104,7 @@ namespace SeamlessClientPlugin.SeamlessTransfer /* Get Methods */ MethodInfo OnJoin = GetMethod(ClientType, "OnUserJoined", BindingFlags.NonPublic | BindingFlags.Instance); - MethodInfo LoadingAction = GetMethod(typeof(MySessionLoader),"LoadMultiplayerSession", BindingFlags.Public | BindingFlags.Static); + MethodInfo LoadingAction = GetMethod(typeof(MySessionLoader), "LoadMultiplayerSession", BindingFlags.Public | BindingFlags.Static); InitVirtualClients = GetMethod(VirtualClientsType, "Init", BindingFlags.Instance | BindingFlags.Public); LoadPlayerInternal = GetMethod(typeof(MyPlayerCollection), "LoadPlayerInternal", BindingFlags.Instance | BindingFlags.NonPublic); LoadMembersFromWorld = GetMethod(typeof(MySession), "LoadMembersFromWorld", BindingFlags.NonPublic | BindingFlags.Instance); @@ -108,13 +113,15 @@ namespace SeamlessClientPlugin.SeamlessTransfer UnloadProceduralWorldGenerator = GetMethod(typeof(MyProceduralWorldGenerator), "UnloadData", BindingFlags.Instance | BindingFlags.NonPublic); + + MethodInfo ConnectToServer = GetMethod(typeof(MyGameService), "ConnectToServer", BindingFlags.Static | BindingFlags.Public); + MethodInfo LoadingScreenDraw = GetMethod(typeof(MyGuiScreenLoading), "DrawInternal", BindingFlags.Instance | BindingFlags.NonPublic); - - + Patcher.Patch(LoadingScreenDraw, prefix: new HarmonyMethod(GetPatchMethod(nameof(DrawInternal)))); Patcher.Patch(OnJoin, postfix: new HarmonyMethod(GetPatchMethod(nameof(OnUserJoined)))); Patcher.Patch(LoadingAction, prefix: new HarmonyMethod(GetPatchMethod(nameof(LoadMultiplayerSession)))); //Patcher.Patch(ConnectToServer, prefix: new HarmonyMethod(GetPatchMethod(nameof(OnConnectToServer)))); @@ -126,9 +133,16 @@ namespace SeamlessClientPlugin.SeamlessTransfer } #region LoadingScreen + /* Loading Screen Stuff */ + + private static string LoadingScreenTexture = null; + private static string ServerName; + private static bool LoadMultiplayerSession(MyObjectBuilder_World world, MyMultiplayerBase multiplayerSession) { + + MyLog.Default.WriteLine("LoadSession() - Start"); if (!MyWorkshop.CheckLocalModsAllowed(world.Checkpoint.Mods, allowLocalMods: false)) { @@ -148,15 +162,16 @@ namespace SeamlessClientPlugin.SeamlessTransfer MySession.Static = null; } - string CustomBackgroundImage = null; - GetCustomLoadingScreenPath(world.Checkpoint.Mods, out CustomBackgroundImage); + ServerName = multiplayerSession.HostName; + GetCustomLoadingScreenPath(world.Checkpoint.Mods, out LoadingScreenTexture); + MySessionLoader.StartLoading(delegate { LoadMultiplayer.Invoke(null, new object[] { world, multiplayerSession }); //MySession.LoadMultiplayer(world, multiplayerSession); - }, null, CustomBackgroundImage, null); + }, null, null, null); } else { @@ -181,10 +196,62 @@ namespace SeamlessClientPlugin.SeamlessTransfer return false; } + private static bool DrawInternal(MyGuiScreenLoading __instance) + { + + //If we dont have a custom loading screen texture, do not do the special crap below + if (string.IsNullOrEmpty(LoadingScreenTexture)) + return true; + + + float m_transitionAlpha = (float)typeof(MyGuiScreenBase).GetField("m_transitionAlpha", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(__instance); + string m_font = "LoadingScreen"; + //MyGuiControlMultilineText m_multiTextControl = (MyGuiControlMultilineText)typeof(MyGuiScreenLoading).GetField("m_multiTextControl", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(__instance); + + + Color color = new Color(255, 255, 255, 250); + color.A = (byte)((float)(int)color.A * m_transitionAlpha); + Rectangle fullscreenRectangle = MyGuiManager.GetFullscreenRectangle(); + MyGuiManager.DrawSpriteBatch("Textures\\GUI\\Blank.dds", fullscreenRectangle, Color.Black, false, true); + Rectangle outRect; + MyGuiManager.GetSafeHeightFullScreenPictureSize(MyGuiConstants.LOADING_BACKGROUND_TEXTURE_REAL_SIZE, out outRect); + MyGuiManager.DrawSpriteBatch(LoadingScreenTexture, outRect, new Color(new Vector4(1f, 1f, 1f, m_transitionAlpha)), true, true); + MyGuiManager.DrawSpriteBatch("Textures\\Gui\\Screens\\screen_background_fade.dds", outRect, new Color(new Vector4(1f, 1f, 1f, m_transitionAlpha)), true, true); + + //MyGuiSandbox.DrawGameLogoHandler(m_transitionAlpha, MyGuiManager.ComputeFullscreenGuiCoordinate(MyGuiDrawAlignEnum.HORISONTAL_LEFT_AND_VERTICAL_TOP, 44, 68)); + + string LoadScreen = $"Loading into {ServerName}! Please wait!"; + + + MyGuiManager.DrawString(m_font, LoadScreen, new Vector2(0.5f, 0.95f), MyGuiSandbox.GetDefaultTextScaleWithLanguage() * 1.1f, new Color(MyGuiConstants.LOADING_PLEASE_WAIT_COLOR * m_transitionAlpha), MyGuiDrawAlignEnum.HORISONTAL_CENTER_AND_VERTICAL_BOTTOM); + + MyGuiManager.DrawString(m_font, "Nexus & SeamlessClient Made by: Casimir", new Vector2(0.95f, 0.95f), MyGuiSandbox.GetDefaultTextScaleWithLanguage() * 1.1f, new Color(MyGuiConstants.LOADING_PLEASE_WAIT_COLOR * m_transitionAlpha), MyGuiDrawAlignEnum.HORISONTAL_CENTER_AND_VERTICAL_BOTTOM); + + /* + if (string.IsNullOrEmpty(m_customTextFromConstructor)) + { + string font = m_font; + Vector2 positionAbsoluteBottomLeft = m_multiTextControl.GetPositionAbsoluteBottomLeft(); + Vector2 textSize = m_multiTextControl.TextSize; + Vector2 normalizedCoord = positionAbsoluteBottomLeft + new Vector2((m_multiTextControl.Size.X - textSize.X) * 0.5f + 0.025f, 0.025f); + MyGuiManager.DrawString(font, m_authorWithDash.ToString(), normalizedCoord, MyGuiSandbox.GetDefaultTextScaleWithLanguage()); + } + */ + + + //m_multiTextControl.Draw(1f, 1f); + + return false; + } + + + private static bool GetCustomLoadingScreenPath(List Mods, out string File) { File = null; string WorkshopDir = MyFileSystem.ModsPath; + List backgrounds = new List(); + Random r = new Random(DateTime.Now.Millisecond); SeamlessClient.TryShow(WorkshopDir); try { @@ -196,18 +263,21 @@ namespace SeamlessClientPlugin.SeamlessTransfer if (!Directory.Exists(SearchDir)) continue; - var files = Directory.GetFiles(SearchDir, "*.dds", SearchOption.TopDirectoryOnly); + + var files = Directory.GetFiles(SearchDir, "CustomLoadingBackground*.dds", SearchOption.TopDirectoryOnly); foreach (var file in files) { - if (Path.GetFileNameWithoutExtension(file) == "CustomLoadingBackground") - { - SeamlessClient.TryShow(Mod.FriendlyName + " contains a custom loading background!"); - File = file; - return true; - } + // Adds all files containing CustomLoadingBackground to a list for later randomisation + SeamlessClient.TryShow(Mod.FriendlyName + " contains a custom loading background!"); + backgrounds.Add(file); + } } + // Randomly pick a loading screen from the available backgrounds + var rInt = r.Next(0, backgrounds.Count() - 1); + File = backgrounds[rInt]; + return true; } catch (Exception ex) { @@ -258,7 +328,7 @@ namespace SeamlessClientPlugin.SeamlessTransfer return FoundMethod; } - catch(Exception Ex) + catch (Exception Ex) { throw Ex; } diff --git a/Utilities/UpdateChecker.cs b/Utilities/UpdateChecker.cs index 7cdc29f..449f5ea 100644 --- a/Utilities/UpdateChecker.cs +++ b/Utilities/UpdateChecker.cs @@ -2,6 +2,7 @@ using Sandbox.Graphics.GUI; using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.IO.Compression; using System.Linq; @@ -21,7 +22,7 @@ namespace SeamlessClientPlugin.Utilities public string PluginFolder; public string CurrentVersion; public bool DownloadUpdate; - private string GitHubAPILink = "https://api.github.com/repos/Casimir255/SeamlessClientPlugin/releases/latest"; + private const string GitHubAPILink = "https://api.github.com/repos/Casimir255/SeamlessClientPlugin/releases/latest"; private WebClient Client; @@ -35,6 +36,31 @@ namespace SeamlessClientPlugin.Utilities DeleteOLDFiles(); } + private static void RestartClientAfterUpdate() + { + try + { + SeamlessClient.TryShow("Restarting Client!"); + string exe = Assembly.GetEntryAssembly().Location; + Process currentProcess = Process.GetCurrentProcess(); + + string[] CommandArgs = Environment.GetCommandLineArgs(); + string NewCommandLine = ""; + for (int i = 1; i < CommandArgs.Length; i++) + { + NewCommandLine += " " + CommandArgs[i]; + } + + SeamlessClient.TryShow(NewCommandLine); + Process.Start(exe, NewCommandLine); + currentProcess.Kill(); + } + catch + { + SeamlessClient.TryShow("Restarting Client error!"); + } + } + @@ -201,7 +227,7 @@ namespace SeamlessClientPlugin.Utilities //Restart client SeamlessClient.TryShow("UpdateComplete!"); - SeamlessClient.RestartClientAfterUpdate(); + RestartClientAfterUpdate(); return true; }