From 8df2868708254bcf782bb57c3be2acefc978f19f Mon Sep 17 00:00:00 2001
From: Bob Da Ross <52760019+BobDaRoss@users.noreply.github.com>
Date: Sun, 21 Mar 2021 22:00:21 -0500
Subject: [PATCH] Added AutoUpdater
---
Messages/ClientMessages.cs | 2 +-
SeamlessClient.cs | 59 ++++++-
SeamlessClientPlugin.csproj | 13 ++
SeamlessTransfer/LoadServer.cs | 146 ++++++++++++----
SeamlessTransfer/Transfer.cs | 5 +
Updater/UpdateChecker.cs | 300 +++++++++++++++++++++++++++++++++
packages.config | 2 +
7 files changed, 488 insertions(+), 39 deletions(-)
create mode 100644 Updater/UpdateChecker.cs
diff --git a/Messages/ClientMessages.cs b/Messages/ClientMessages.cs
index 6eaa343..c375cc4 100644
--- a/Messages/ClientMessages.cs
+++ b/Messages/ClientMessages.cs
@@ -37,7 +37,7 @@ namespace SeamlessClientPlugin.ClientMessages
{
MessageType = Type;
- if (!MyAPIGateway.Multiplayer.IsServer)
+ if (MyAPIGateway.Multiplayer != null && !MyAPIGateway.Multiplayer.IsServer)
{
if(MyAPIGateway.Session.LocalHumanPlayer == null)
{
diff --git a/SeamlessClient.cs b/SeamlessClient.cs
index bafc6eb..58fa6ae 100644
--- a/SeamlessClient.cs
+++ b/SeamlessClient.cs
@@ -9,13 +9,16 @@ using Sandbox.Graphics.GUI;
using Sandbox.ModAPI;
using SeamlessClientPlugin.ClientMessages;
using SeamlessClientPlugin.SeamlessTransfer;
+using SeamlessClientPlugin.Updater;
using System;
using System.Collections.Generic;
+using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Timers;
+using System.Windows.Forms;
using VRage.Input;
using VRage.Plugins;
using VRage.Utils;
@@ -103,11 +106,11 @@ namespace SeamlessClientPlugin
- public static string Version = "1.0.0";
+ public static string Version = "1.1.0";
private bool Initilized = false;
private bool SentPingResponse = false;
public const ushort SeamlessClientNetID = 2936;
- private Timer PingTimer = new Timer(2000);
+ private System.Timers.Timer PingTimer = new System.Timers.Timer(3000);
public static LoadServer Server = new LoadServer();
@@ -132,7 +135,20 @@ namespace SeamlessClientPlugin
public void Init(object gameInstance)
{
TryShow("Running Seamless Client Plugin v[" + Version + "]");
- // Reload = new ReloadPatch();
+
+ UpdateChecker Checker = new UpdateChecker(Version, false);
+
+ Task UpdateChecker = new Task(() => Checker.PingUpdateServer());
+ UpdateChecker.Start();
+
+
+
+
+
+
+
+
+ // Reload = new ReloadPatch();
//Patching goes here
@@ -141,6 +157,8 @@ namespace SeamlessClientPlugin
//throw new NotImplementedException();
}
+
+
private void PingTimer_Elapsed(object sender, ElapsedEventArgs e)
{
//TryShow("Sending PluginVersion to Server!");
@@ -148,7 +166,7 @@ namespace SeamlessClientPlugin
{
ClientMessage PingServer = new ClientMessage(ClientMessageType.FirstJoin);
- MyAPIGateway.Multiplayer.SendMessageToServer(SeamlessClientNetID, Utilities.Utility.Serialize(PingServer));
+ MyAPIGateway.Multiplayer?.SendMessageToServer(SeamlessClientNetID, Utilities.Utility.Serialize(PingServer));
}
catch (Exception ex)
{
@@ -222,5 +240,38 @@ namespace SeamlessClientPlugin
MyLog.Default?.WriteLineAndConsole($"SeamlessClient: {message}");
}
+
+
+ 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!");
+ }
+ }
}
}
diff --git a/SeamlessClientPlugin.csproj b/SeamlessClientPlugin.csproj
index b7ae61d..981e56a 100644
--- a/SeamlessClientPlugin.csproj
+++ b/SeamlessClientPlugin.csproj
@@ -84,9 +84,21 @@
False
+
+ ..\Nexus\packages\System.IO.Compression.4.3.0\lib\net46\System.IO.Compression.dll
+ True
+ True
+
+
+
+ ..\Nexus\packages\System.IO.Compression.ZipFile.4.3.0\lib\net46\System.IO.Compression.ZipFile.dll
+ True
+ True
+
False
+
False
@@ -149,6 +161,7 @@
+
diff --git a/SeamlessTransfer/LoadServer.cs b/SeamlessTransfer/LoadServer.cs
index 8fed05d..d5a74a4 100644
--- a/SeamlessTransfer/LoadServer.cs
+++ b/SeamlessTransfer/LoadServer.cs
@@ -2,6 +2,7 @@
using Sandbox;
using Sandbox.Definitions;
using Sandbox.Engine;
+using Sandbox.Engine.Analytics;
using Sandbox.Engine.Multiplayer;
using Sandbox.Engine.Networking;
using Sandbox.Engine.Physics;
@@ -21,6 +22,7 @@ using SeamlessClientPlugin.Utilities;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
+using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
@@ -28,6 +30,7 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
using VRage;
+using VRage.FileSystem;
using VRage.Game;
using VRage.Game.ModAPI;
using VRage.Game.SessionComponents;
@@ -68,6 +71,7 @@ namespace SeamlessClientPlugin.SeamlessTransfer
//Reflected Methods
public static FieldInfo VirtualClients;
public static FieldInfo AdminSettings;
+
public static FieldInfo RemoteAdminSettings;
public static FieldInfo MPlayerGPSCollection;
public static MethodInfo RemovePlayerFromDictionary;
@@ -75,6 +79,8 @@ namespace SeamlessClientPlugin.SeamlessTransfer
public static MethodInfo LoadPlayerInternal;
public static MethodInfo LoadMembersFromWorld;
+ public static MethodInfo LoadMultiplayer;
+
public LoadServer()
{
@@ -131,6 +137,12 @@ namespace SeamlessClientPlugin.SeamlessTransfer
AdminSettings = typeof(MySession).GetField("m_adminSettings", BindingFlags.Instance | BindingFlags.NonPublic);
RemoteAdminSettings = typeof(MySession).GetField("m_remoteAdminSettings", BindingFlags.Instance | BindingFlags.NonPublic);
MPlayerGPSCollection = typeof(MyPlayerCollection).GetField("m_players", BindingFlags.Instance | BindingFlags.NonPublic);
+ LoadMultiplayer = typeof(MySession).GetMethod("LoadMultiplayer", BindingFlags.Static | BindingFlags.NonPublic);
+
+
+ MethodInfo LoadingAction = typeof(MySessionLoader).GetMethod("LoadMultiplayerSession", BindingFlags.Public | BindingFlags.Static);
+ Patcher.Patch(LoadingAction, prefix: new HarmonyMethod(GetPatchMethod(nameof(LoadMultiplayerSession))));
+
}
@@ -140,6 +152,101 @@ namespace SeamlessClientPlugin.SeamlessTransfer
}
+
+ private static bool GetCustomLoadingScreenPath(List Mods, out string File)
+ {
+
+
+ string WorkshopDir = MyFileSystem.ModsPath;
+
+ SeamlessClient.TryShow(WorkshopDir);
+
+
+ File = null;
+ SeamlessClient.TryShow("Installed Mods: " + Mods);
+ foreach(var Mod in Mods)
+ {
+ string SearchDir = Mod.GetPath();
+ var files = Directory.EnumerateFiles(SearchDir, "*.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;
+ }
+
+
+ }
+
+
+ }
+
+ SeamlessClient.TryShow("No installed custom loading screen!");
+ return false;
+ }
+
+
+
+ private static bool LoadMultiplayerSession(MyObjectBuilder_World world, MyMultiplayerBase multiplayerSession)
+ {
+ MyLog.Default.WriteLine("LoadSession() - Start");
+ if (!MyWorkshop.CheckLocalModsAllowed(world.Checkpoint.Mods, allowLocalMods: false))
+ {
+ MyGuiSandbox.AddScreen(MyGuiSandbox.CreateMessageBox(MyMessageBoxStyleEnum.Error, MyMessageBoxButtonsType.OK, messageCaption: MyTexts.Get(MyCommonTexts.MessageBoxCaptionError), messageText: MyTexts.Get(MyCommonTexts.DialogTextLocalModsDisabledInMultiplayer)));
+ MyLog.Default.WriteLine("LoadSession() - End");
+ return false;
+ }
+ MyWorkshop.DownloadModsAsync(world.Checkpoint.Mods, delegate (bool success)
+ {
+ if (success)
+ {
+ MyScreenManager.CloseAllScreensNowExcept(null);
+ MyGuiSandbox.Update(16);
+ if (MySession.Static != null)
+ {
+ MySession.Static.Unload();
+ MySession.Static = null;
+ }
+
+ string CustomBackgroundImage = null;
+ GetCustomLoadingScreenPath(world.Checkpoint.Mods, out CustomBackgroundImage);
+
+ MySessionLoader.StartLoading(delegate
+ {
+
+ LoadMultiplayer.Invoke(null, new object[] { world, multiplayerSession });
+ //MySession.LoadMultiplayer(world, multiplayerSession);
+ }, null, CustomBackgroundImage, null);
+ }
+ else
+ {
+ multiplayerSession.Dispose();
+ MySessionLoader.UnloadAndExitToMenu();
+ if (MyGameService.IsOnline)
+ {
+ MyGuiSandbox.AddScreen(MyGuiSandbox.CreateMessageBox(MyMessageBoxStyleEnum.Error, MyMessageBoxButtonsType.OK, messageCaption: MyTexts.Get(MyCommonTexts.MessageBoxCaptionError), messageText: MyTexts.Get(MyCommonTexts.DialogTextDownloadModsFailed)));
+ }
+ else
+ {
+ MyGuiSandbox.AddScreen(MyGuiSandbox.CreateMessageBox(MyMessageBoxStyleEnum.Error, MyMessageBoxButtonsType.OK, messageCaption: MyTexts.Get(MyCommonTexts.MessageBoxCaptionError), messageText: new StringBuilder(string.Format(MyTexts.GetString(MyCommonTexts.DialogTextDownloadModsFailedSteamOffline), MySession.GameServiceName))));
+ }
+ }
+ MyLog.Default.WriteLine("LoadSession() - End");
+ }, delegate
+ {
+ multiplayerSession.Dispose();
+ MySessionLoader.UnloadAndExitToMenu();
+ });
+
+ return false;
+ }
+
+
+
+
+
private static void OnUserJoined(ref JoinResultMsg msg)
{
if (SeamlessClient.IsSwitching && msg.JoinResult == JoinResult.OK)
@@ -317,48 +424,19 @@ namespace SeamlessClientPlugin.SeamlessTransfer
- foreach(var GPS in checkpoint.Gps.Dictionary)
- {
- if (GPS.Key != MySession.Static.LocalPlayerId)
- continue;
-
- SeamlessClient.TryShow(GPS.Key + ":" + GPS.Value.Entries.Count);
-
-
- }
-
SeamlessClient.TryShow("LocalPlayerID: " + MySession.Static.LocalPlayerId);
- checkpoint.Gps.Dictionary.TryGetValue(MySession.Static.LocalPlayerId, out MyObjectBuilder_Gps GPSCollection);
- SeamlessClient.TryShow("You have " + GPSCollection.Entries.Count + " gps points!");
-
-
- Dictionary> m_playerGpss = (Dictionary>)typeof(MyGpsCollection).GetField("m_playerGpss", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(MySession.Static.Gpss);
- m_playerGpss.Clear();
-
-
- foreach (var GPS in GPSCollection.Entries)
- {
- MyGps myGps = new MyGps(GPS);
-
- if(MySession.Static.Gpss.AddPlayerGps(MySession.Static.LocalPlayerId, ref myGps))
- {
- MyHud.GpsMarkers.RegisterMarker(myGps);
- }
- else
- {
- SeamlessClient.TryShow("Failed to registered Marker! It already exsists!");
- }
- }
+ //checkpoint.Gps.Dictionary.TryGetValue(MySession.Static.LocalPlayerId, out MyObjectBuilder_Gps GPSCollection);
+ //SeamlessClient.TryShow("You have " + GPSCollection.Entries.Count + " gps points!");
+ MySession.Static.Gpss = new MyGpsCollection();
MySession.Static.Gpss.LoadGpss(checkpoint);
- MySession.Static.Toolbars.LoadToolbars(checkpoint);
+ MyRenderProxy.RebuildCullingStructure();
+ //MySession.Static.Toolbars.LoadToolbars(checkpoint);
Sync.Players.RespawnComponent.InitFromCheckpoint(checkpoint);
-
-
}
diff --git a/SeamlessTransfer/Transfer.cs b/SeamlessTransfer/Transfer.cs
index 7038cd0..388bde7 100644
--- a/SeamlessTransfer/Transfer.cs
+++ b/SeamlessTransfer/Transfer.cs
@@ -124,7 +124,12 @@ namespace SeamlessClientPlugin.SeamlessTransfer
//Sync.Clients.Clear();
+ var PCollection = MySession.Static.Gpss[MySession.Static.LocalPlayerId];
+ PCollection.Clear();
+ MyHud.GpsMarkers.Clear();
+
+ SeamlessClient.TryShow(PCollection.Count + "registered GPS points");
// MyGuiSandbox.UnloadContent();
}
}
diff --git a/Updater/UpdateChecker.cs b/Updater/UpdateChecker.cs
new file mode 100644
index 0000000..38ac0b6
--- /dev/null
+++ b/Updater/UpdateChecker.cs
@@ -0,0 +1,300 @@
+using ProtoBuf;
+using Sandbox.Graphics.GUI;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.IO.Compression;
+using System.Linq;
+using System.Net.Sockets;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+using System.Xml.Serialization;
+
+namespace SeamlessClientPlugin.Updater
+{
+ public class UpdateChecker
+ {
+ public string PluginFolder;
+ public string CurrentVersion;
+ public bool DownloadUpdate;
+
+ const int PORT_NO = 27010;
+ //AWS Website server
+ const string SERVER_IP = "3.80.137.183";
+
+ private byte[] RecievedZipMememory;
+
+ public UpdateChecker(string Version, bool AutoUpdate)
+ {
+ this.DownloadUpdate = AutoUpdate;
+ PluginFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
+ CurrentVersion = Version;
+
+ SeamlessClient.TryShow("You are running @" + PluginFolder);
+ DeleteOLDFiles();
+ }
+
+
+ public void PingUpdateServer()
+ {
+ try
+ {
+
+ TcpClient client = new TcpClient(SERVER_IP, PORT_NO);
+ client.ReceiveBufferSize = 1000000;
+
+ NetworkStream nwStream = client.GetStream();
+
+ UpdateMessage Message = new UpdateMessage();
+ Message.ClientVersion = CurrentVersion;
+ Message.DownloadNewUpdate = DownloadUpdate;
+
+ //Send current version to server
+ //string XMLData = Utility.Serialize(Message);
+ byte[] bytesToSend = Utility.Serialize(Message);
+ nwStream.Write(bytesToSend, 0, bytesToSend.Length);
+
+ //Get server reply
+ byte[] bytesToRead = new byte[client.ReceiveBufferSize];
+ int bytesRead = nwStream.Read(bytesToRead, 0, client.ReceiveBufferSize);
+
+
+ byte[] ReMessage = bytesToRead.Take(bytesRead).ToArray();
+ //string StringServerReply = Encoding.ASCII.GetString(bytesToRead, 0, bytesRead);
+ // SeamlessClient.TryShow(StringServerReply);
+
+
+ Message = Utility.Deserialize(ReMessage);
+ UpdateServerReply(Message);
+
+ SeamlessClient.TryShow("Received! Latest Version: " + Message.ServerVersion);
+ }catch(SocketException)
+ {
+ //Cant connect to server.
+ SeamlessClient.TryShow("Cant Connect to UpdateServer!");
+ return;
+ }catch(Exception ex)
+ {
+ SeamlessClient.TryShow("Update Error! \n"+ex.ToString());
+ return;
+ }
+
+ }
+
+ private void UpdateServerReply(UpdateMessage Message)
+ {
+ if (!NeedsUpdate(Message.ClientVersion, Message.ServerVersion))
+ return;
+
+ SeamlessClient.TryShow("An Update is required! ClientVersion: [" + Message.ClientVersion + "] Server Version: [" + Message.ServerVersion + "]");
+ RecievedZipMememory = Message.XmlCharactersAsBytes;
+
+
+ if (!DownloadUpdate)
+ {
+ //Create Update question and UI
+ StringBuilder Title = new StringBuilder();
+ Title.Append($"Would you like to download Seamless Client Plugin {Message.ServerVersion} Update?");
+ StringBuilder Caption = new StringBuilder();
+ Caption.AppendLine(" - Patch Notes - ");
+ Caption.AppendLine();
+ Caption.AppendLine(Message.UpdateNotes);
+ Caption.AppendLine();
+ Caption.AppendLine("Your client will restart after install");
+
+
+ MyScreenManager.AddScreen(MyGuiSandbox.CreateMessageBox(MyMessageBoxStyleEnum.Info, MyMessageBoxButtonsType.YES_NO_TIMEOUT, Caption, Title, null, null, null, null, UpdateMessageBoxCallback, 60000, MyGuiScreenMessageBox.ResultEnum.YES, canHideOthers: true, new VRageMath.Vector2(.5f,.4f), useOpacity: false));
+
+
+ return;
+ }
+ else
+ {
+ ExtractAndReplace();
+ }
+
+
+
+
+ }
+
+
+ private void UpdateMessageBoxCallback(MyGuiScreenMessageBox.ResultEnum Result)
+ {
+ //Update chcker
+ if (Result == MyGuiScreenMessageBox.ResultEnum.YES)
+ {
+ SeamlessClient.TryShow("Clicked Yes");
+ //If they clicked yes, ping the update server again and request new file
+ DownloadUpdate = true;
+ PingUpdateServer();
+ return;
+ }
+
+ if (Result == MyGuiScreenMessageBox.ResultEnum.NO || Result == MyGuiScreenMessageBox.ResultEnum.CANCEL)
+ {
+ //Return. (Do not update)
+ SeamlessClient.TryShow("Clicked No or Cancel");
+ return;
+ }
+
+ }
+
+
+ private void DeleteOLDFiles()
+ {
+ foreach(var OLDFile in Directory.GetFiles(PluginFolder, "*.old"))
+ {
+ File.Delete(OLDFile);
+ }
+
+ SeamlessClient.TryShow("Deleted all OLD update files");
+
+ }
+
+
+ private void ExtractAndReplace()
+ {
+ //save latest zip in dir
+ string ZipPath = Path.Combine(PluginFolder, "Latest.zip");
+ File.WriteAllBytes(ZipPath, RecievedZipMememory);
+
+
+ //Start extractor
+ using (ZipArchive archive = ZipFile.OpenRead(ZipPath))
+ {
+ foreach (ZipArchiveEntry entry in archive.Entries)
+ {
+ string ExsistingFilePath = Path.Combine(PluginFolder, entry.Name);
+ string OldFilePath = Path.Combine(PluginFolder, entry.Name+".old");
+ SeamlessClient.TryShow(ExsistingFilePath + "=>" + OldFilePath);
+
+
+ if (File.Exists(OldFilePath))
+ File.Delete(OldFilePath);
+
+ File.Move(ExsistingFilePath, OldFilePath);
+ entry.ExtractToFile(ExsistingFilePath, true);
+ //File.Delete(OldFilePath);
+ }
+ }
+
+ //Delete latest zip
+ File.Delete(ZipPath);
+
+ //Restart client
+ SeamlessClient.TryShow("UpdateComplete!");
+ SeamlessClient.RestartClientAfterUpdate();
+ }
+
+ private bool NeedsUpdate(string ClientVersion, string ServerVersion)
+ {
+
+
+ Version Client = new Version(ClientVersion);
+ Version Latest = new Version(ServerVersion);
+
+ var result = Client.CompareTo(Latest);
+ if (result > 0)
+ {
+ //Console.WriteLine("Client is greater");
+ return false;
+ }
+ else if (result < 0)
+ {
+ //Console.WriteLine("Latest is greater");
+ return true;
+ }
+ else
+ {
+ //Console.WriteLine("versions are equal");
+ return false;
+ }
+ }
+
+ }
+
+
+ [ProtoContract]
+ public class UpdateMessage
+ {
+ [ProtoMember(10)]
+ public string ClientVersion = "";
+
+ [ProtoMember(20)]
+ public bool UpToDate = false;
+
+ [ProtoMember(30)]
+ public bool DownloadNewUpdate = false;
+
+ [ProtoMember(40)]
+ public string ServerVersion = "";
+
+ [ProtoMember(50)]
+ public string UpdateNotes = "";
+
+
+
+ /* Misc Stuff incase I need it */
+ [ProtoMember(60)]
+ public byte[] XmlCharactersAsBytes;
+
+
+ public UpdateMessage() { }
+
+
+ }
+
+ public static class Utility
+ {
+ public static string SerializeObject(this T toSerialize)
+ {
+ XmlSerializer xmlSerializer = new XmlSerializer(toSerialize.GetType());
+
+ using (StringWriter textWriter = new StringWriter())
+ {
+ xmlSerializer.Serialize(textWriter, toSerialize);
+ return textWriter.ToString();
+ }
+ }
+
+
+ public static T XmlDeserialize(this string toDeserialize)
+ {
+ XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
+ using (StringReader textReader = new StringReader(toDeserialize))
+ {
+ return (T)xmlSerializer.Deserialize(textReader);
+ }
+ }
+
+
+ public static byte[] Serialize(T instance)
+ {
+ if (instance == null)
+ return null;
+
+
+ using (var m = new MemoryStream())
+ {
+ // m.Seek(0, SeekOrigin.Begin);
+ Serializer.Serialize(m, instance);
+
+ return m.ToArray();
+ }
+ }
+
+ public static T Deserialize(byte[] data)
+ {
+ if (data == null)
+ return default;
+
+ using (var m = new MemoryStream(data))
+ {
+ return Serializer.Deserialize(m);
+ }
+ }
+ }
+
+}
diff --git a/packages.config b/packages.config
index f0f60a9..8b50108 100644
--- a/packages.config
+++ b/packages.config
@@ -1,4 +1,6 @@
+
+
\ No newline at end of file