From dda5928149341ba433784eb89e402b06758a67b7 Mon Sep 17 00:00:00 2001 From: crowbarmaster Date: Tue, 16 Nov 2021 13:23:20 -0500 Subject: [PATCH 01/62] Updates to .NET 6 --- .../BedrockService.Shared.csproj | 80 +++ .../Classes/ClientSideServiceConfiguration.cs | 28 + .../Classes/CommsKeyContainer.cs | 95 +++ .../Classes/NetworkEnums.cs | 59 ++ .../BedrockService.Shared/Classes/Player.cs | 124 ++++ .../BedrockService.Shared/Classes/Property.cs | 29 + .../Classes/ServerInfo.cs | 223 ++++++ .../Classes/ServerLogger.cs | 102 +++ .../Classes/ServiceInfo.cs | 156 +++++ .../Classes/ServiceProcessInfo.cs | 38 + .../Classes/StartCmdEntry.cs | 12 + .../IClientSideServiceConfiguration.cs | 10 + .../Interfaces/IConfiguration.cs | 0 .../Interfaces/ILogger.cs | 0 .../Interfaces/IPlayer.cs | 19 + .../Interfaces/IProcessInfo.cs | 17 + .../Interfaces/IServerConfiguration.cs | 30 + .../Interfaces/IServiceConfiguration.cs | 27 + .../PackParser/MinecraftKnownPacksClass.cs | 67 ++ .../PackParser/MinecraftPackParser.cs | 174 +++++ .../Properties/AssemblyInfo.cs | 35 + .../Utilities/FileUtils.cs | 62 ++ .../BedrockService.Shared/packages.config | 0 .../BedrockService.Shared/upgrade.backup | 1 + BedrockService.backup/Client/App.config | 6 + .../Client/BedrockService.Client.csproj | 186 +++++ .../Client/Configs/Config.conf | 2 + .../Client/Forms/AddNewServerForm.Designer.cs | 158 +++++ .../Client/Forms/AddNewServerForm.cs | 69 ++ .../Client/Forms/AddNewServerForm.resx | 120 ++++ .../Client/Forms/ClientConfigForm.Designer.cs | 133 ++++ .../Client/Forms/ClientConfigForm.cs | 73 ++ .../Client/Forms/ClientConfigForm.resx | 138 ++++ .../Client/Forms/MainWindow.Designer.cs | 424 ++++++++++++ .../Client/Forms/MainWindow.cs | 654 ++++++++++++++++++ .../Client/Forms/MainWindow.resx | 198 ++++++ .../Client/Forms/ManagePacksForms.Designer.cs | 215 ++++++ .../Client/Forms/ManagePacksForms.cs | 135 ++++ .../Client/Forms/ManagePacksForms.resx | 120 ++++ .../NewPlayerRegistrationForm.Designer.cs | 182 +++++ .../Client/Forms/NewPlayerRegistrationForm.cs | 26 + .../Forms/NewPlayerRegistrationForm.resx | 120 ++++ .../Forms/PlayerManagerForm.Designer.cs | 153 ++++ .../Client/Forms/PlayerManagerForm.cs | 169 +++++ .../Client/Forms/PlayerManagerForm.resx | 120 ++++ .../Client/Forms/PropEditorForm.Designer.cs | 132 ++++ .../Client/Forms/PropEditorForm.cs | 133 ++++ .../Client/Forms/PropEditorForm.resx | 132 ++++ .../Client/Management/ClientLogger.cs | 57 ++ .../Client/Management/ConfigManager.cs | 89 +++ .../Client/Management/FormManager.cs | 41 ++ .../Client/Management/LogManager.cs | 123 ++++ .../Client/Networking/TCPClient.cs | 346 +++++++++ .../Client/Properties/AssemblyInfo.cs | 35 + .../Client/Properties/Resources.Designer.cs | 63 ++ .../Client/Properties/Resources.resx | 117 ++++ .../Client/Properties/Settings.Designer.cs | 26 + .../Client/Properties/Settings.settings | 7 + .../Client/packages.config | 0 BedrockService.backup/Client/upgrade.backup | 1 + .../Service/BedrockService.Service.csproj | 197 ++++++ .../Service/Configs/Default.conf | 28 + .../Service/Configs/Default.players | 5 + .../Service/Configs/Globals.conf | 16 + .../Service/Core/BedrockService.cs | 310 +++++++++ .../Core/Interfaces/IBedrockService.cs | 22 + .../Service/Core/Interfaces/IService.cs | 10 + .../Service/Core/Interfaces/IServiceThread.cs | 8 + BedrockService.backup/Service/Core/Service.cs | 74 ++ .../Core/Threads/ClientServiceThread.cs | 38 + .../Service/Core/Threads/HeartbeatThread.cs | 31 + .../Core/Threads/ServerProcessThread.cs | 35 + .../Service/Core/Threads/TCPThread.cs | 39 ++ .../Service/Core/Threads/WatchdogThread.cs | 33 + .../Service/Logging/ServiceLogger.cs | 102 +++ .../Service/Management/ConfigManager.cs | 540 +++++++++++++++ .../Service/Management/IConfigurator.cs | 26 + .../Service/Networking/ITCPListener.cs | 17 + .../Service/Networking/IUpdater.cs | 14 + .../IFlaggedMessageParser.cs | 9 + .../MessageInterfaces/IMessageParser.cs | 7 + .../MessageInterfaces/IMessageSender.cs | 19 + .../Networking/NetworkStrategyLookup.cs | 654 ++++++++++++++++++ .../Service/Networking/TCPListener.cs | 296 ++++++++ .../Service/Networking/Updater.cs | 195 ++++++ BedrockService.backup/Service/Program.cs | 80 +++ .../Service/Properties/AssemblyInfo.cs | 35 + .../Service/Server/BedrockServer.cs | 465 +++++++++++++ .../Service/Server/IBedrockServer.cs | 22 + .../Server/Management/IPlayerManager.cs | 15 + .../Server/Management/PlayerManager.cs | 71 ++ .../Service/packages.config | 0 BedrockService.backup/Service/upgrade.backup | 1 + .../BedrockService.Shared.csproj | 77 +-- .../Classes/CommsKeyContainer.cs | 95 +++ .../Classes/ServerLogger.cs | 2 +- .../Interfaces/IBedrockConfiguration.cs | 25 + .../Interfaces/IBedrockLogger.cs | 11 + .../Interfaces/IServerConfiguration.cs | 2 +- .../Interfaces/IServiceConfiguration.cs | 2 +- .../PackParser/MinecraftPackParser.cs | 8 +- .../Client/BedrockService.Client.csproj | 152 +--- BedrockService/Client/Forms/MainWindow.cs | 4 +- .../Client/Forms/ManagePacksForms.cs | 4 +- .../Client/Management/ClientLogger.cs | 2 +- .../Client/Management/ConfigManager.cs | 4 +- .../Client/Management/FormManager.cs | 2 +- .../Client/Management/LogManager.cs | 4 +- BedrockService/Client/Networking/TCPClient.cs | 4 +- .../Service/BedrockService.Service.csproj | 154 +---- BedrockService/Service/Core/BedrockService.cs | 9 +- .../Service/Core/Interfaces/IService.cs | 6 +- BedrockService/Service/Core/Service.cs | 18 +- .../Service/Logging/ServiceLogger.cs | 2 +- .../Service/Management/ConfigManager.cs | 61 +- .../Service/Management/IConfigurator.cs | 5 +- .../Service/Networking/ITCPListener.cs | 2 + .../Networking/NetworkStrategyLookup.cs | 26 +- .../Service/Networking/TCPListener.cs | 33 +- BedrockService/Service/Networking/Updater.cs | 4 +- BedrockService/Service/Program.cs | 92 +-- .../Service/Properties/AssemblyInfo.cs | 4 +- .../Service/Server/BedrockServer.cs | 8 +- .../Service/Server/IBedrockServer.cs | 2 +- .../Server/Management/PlayerManager.cs | 4 +- BedrockService/Service/Startup.cs | 88 +++ .../Service/appsettings.Development.json | 9 + BedrockService/Service/appsettings.json | 10 + 128 files changed, 9991 insertions(+), 449 deletions(-) create mode 100644 BedrockService.backup/BedrockService.Shared/BedrockService.Shared.csproj create mode 100644 BedrockService.backup/BedrockService.Shared/Classes/ClientSideServiceConfiguration.cs create mode 100644 BedrockService.backup/BedrockService.Shared/Classes/CommsKeyContainer.cs create mode 100644 BedrockService.backup/BedrockService.Shared/Classes/NetworkEnums.cs create mode 100644 BedrockService.backup/BedrockService.Shared/Classes/Player.cs create mode 100644 BedrockService.backup/BedrockService.Shared/Classes/Property.cs create mode 100644 BedrockService.backup/BedrockService.Shared/Classes/ServerInfo.cs create mode 100644 BedrockService.backup/BedrockService.Shared/Classes/ServerLogger.cs create mode 100644 BedrockService.backup/BedrockService.Shared/Classes/ServiceInfo.cs create mode 100644 BedrockService.backup/BedrockService.Shared/Classes/ServiceProcessInfo.cs create mode 100644 BedrockService.backup/BedrockService.Shared/Classes/StartCmdEntry.cs create mode 100644 BedrockService.backup/BedrockService.Shared/Interfaces/IClientSideServiceConfiguration.cs rename {BedrockService => BedrockService.backup}/BedrockService.Shared/Interfaces/IConfiguration.cs (100%) rename {BedrockService => BedrockService.backup}/BedrockService.Shared/Interfaces/ILogger.cs (100%) create mode 100644 BedrockService.backup/BedrockService.Shared/Interfaces/IPlayer.cs create mode 100644 BedrockService.backup/BedrockService.Shared/Interfaces/IProcessInfo.cs create mode 100644 BedrockService.backup/BedrockService.Shared/Interfaces/IServerConfiguration.cs create mode 100644 BedrockService.backup/BedrockService.Shared/Interfaces/IServiceConfiguration.cs create mode 100644 BedrockService.backup/BedrockService.Shared/PackParser/MinecraftKnownPacksClass.cs create mode 100644 BedrockService.backup/BedrockService.Shared/PackParser/MinecraftPackParser.cs create mode 100644 BedrockService.backup/BedrockService.Shared/Properties/AssemblyInfo.cs create mode 100644 BedrockService.backup/BedrockService.Shared/Utilities/FileUtils.cs rename {BedrockService => BedrockService.backup}/BedrockService.Shared/packages.config (100%) create mode 100644 BedrockService.backup/BedrockService.Shared/upgrade.backup create mode 100644 BedrockService.backup/Client/App.config create mode 100644 BedrockService.backup/Client/BedrockService.Client.csproj create mode 100644 BedrockService.backup/Client/Configs/Config.conf create mode 100644 BedrockService.backup/Client/Forms/AddNewServerForm.Designer.cs create mode 100644 BedrockService.backup/Client/Forms/AddNewServerForm.cs create mode 100644 BedrockService.backup/Client/Forms/AddNewServerForm.resx create mode 100644 BedrockService.backup/Client/Forms/ClientConfigForm.Designer.cs create mode 100644 BedrockService.backup/Client/Forms/ClientConfigForm.cs create mode 100644 BedrockService.backup/Client/Forms/ClientConfigForm.resx create mode 100644 BedrockService.backup/Client/Forms/MainWindow.Designer.cs create mode 100644 BedrockService.backup/Client/Forms/MainWindow.cs create mode 100644 BedrockService.backup/Client/Forms/MainWindow.resx create mode 100644 BedrockService.backup/Client/Forms/ManagePacksForms.Designer.cs create mode 100644 BedrockService.backup/Client/Forms/ManagePacksForms.cs create mode 100644 BedrockService.backup/Client/Forms/ManagePacksForms.resx create mode 100644 BedrockService.backup/Client/Forms/NewPlayerRegistrationForm.Designer.cs create mode 100644 BedrockService.backup/Client/Forms/NewPlayerRegistrationForm.cs create mode 100644 BedrockService.backup/Client/Forms/NewPlayerRegistrationForm.resx create mode 100644 BedrockService.backup/Client/Forms/PlayerManagerForm.Designer.cs create mode 100644 BedrockService.backup/Client/Forms/PlayerManagerForm.cs create mode 100644 BedrockService.backup/Client/Forms/PlayerManagerForm.resx create mode 100644 BedrockService.backup/Client/Forms/PropEditorForm.Designer.cs create mode 100644 BedrockService.backup/Client/Forms/PropEditorForm.cs create mode 100644 BedrockService.backup/Client/Forms/PropEditorForm.resx create mode 100644 BedrockService.backup/Client/Management/ClientLogger.cs create mode 100644 BedrockService.backup/Client/Management/ConfigManager.cs create mode 100644 BedrockService.backup/Client/Management/FormManager.cs create mode 100644 BedrockService.backup/Client/Management/LogManager.cs create mode 100644 BedrockService.backup/Client/Networking/TCPClient.cs create mode 100644 BedrockService.backup/Client/Properties/AssemblyInfo.cs create mode 100644 BedrockService.backup/Client/Properties/Resources.Designer.cs create mode 100644 BedrockService.backup/Client/Properties/Resources.resx create mode 100644 BedrockService.backup/Client/Properties/Settings.Designer.cs create mode 100644 BedrockService.backup/Client/Properties/Settings.settings rename {BedrockService => BedrockService.backup}/Client/packages.config (100%) create mode 100644 BedrockService.backup/Client/upgrade.backup create mode 100644 BedrockService.backup/Service/BedrockService.Service.csproj create mode 100644 BedrockService.backup/Service/Configs/Default.conf create mode 100644 BedrockService.backup/Service/Configs/Default.players create mode 100644 BedrockService.backup/Service/Configs/Globals.conf create mode 100644 BedrockService.backup/Service/Core/BedrockService.cs create mode 100644 BedrockService.backup/Service/Core/Interfaces/IBedrockService.cs create mode 100644 BedrockService.backup/Service/Core/Interfaces/IService.cs create mode 100644 BedrockService.backup/Service/Core/Interfaces/IServiceThread.cs create mode 100644 BedrockService.backup/Service/Core/Service.cs create mode 100644 BedrockService.backup/Service/Core/Threads/ClientServiceThread.cs create mode 100644 BedrockService.backup/Service/Core/Threads/HeartbeatThread.cs create mode 100644 BedrockService.backup/Service/Core/Threads/ServerProcessThread.cs create mode 100644 BedrockService.backup/Service/Core/Threads/TCPThread.cs create mode 100644 BedrockService.backup/Service/Core/Threads/WatchdogThread.cs create mode 100644 BedrockService.backup/Service/Logging/ServiceLogger.cs create mode 100644 BedrockService.backup/Service/Management/ConfigManager.cs create mode 100644 BedrockService.backup/Service/Management/IConfigurator.cs create mode 100644 BedrockService.backup/Service/Networking/ITCPListener.cs create mode 100644 BedrockService.backup/Service/Networking/IUpdater.cs create mode 100644 BedrockService.backup/Service/Networking/MessageInterfaces/IFlaggedMessageParser.cs create mode 100644 BedrockService.backup/Service/Networking/MessageInterfaces/IMessageParser.cs create mode 100644 BedrockService.backup/Service/Networking/MessageInterfaces/IMessageSender.cs create mode 100644 BedrockService.backup/Service/Networking/NetworkStrategyLookup.cs create mode 100644 BedrockService.backup/Service/Networking/TCPListener.cs create mode 100644 BedrockService.backup/Service/Networking/Updater.cs create mode 100644 BedrockService.backup/Service/Program.cs create mode 100644 BedrockService.backup/Service/Properties/AssemblyInfo.cs create mode 100644 BedrockService.backup/Service/Server/BedrockServer.cs create mode 100644 BedrockService.backup/Service/Server/IBedrockServer.cs create mode 100644 BedrockService.backup/Service/Server/Management/IPlayerManager.cs create mode 100644 BedrockService.backup/Service/Server/Management/PlayerManager.cs rename {BedrockService => BedrockService.backup}/Service/packages.config (100%) create mode 100644 BedrockService.backup/Service/upgrade.backup create mode 100644 BedrockService/BedrockService.Shared/Classes/CommsKeyContainer.cs create mode 100644 BedrockService/BedrockService.Shared/Interfaces/IBedrockConfiguration.cs create mode 100644 BedrockService/BedrockService.Shared/Interfaces/IBedrockLogger.cs create mode 100644 BedrockService/Service/Startup.cs create mode 100644 BedrockService/Service/appsettings.Development.json create mode 100644 BedrockService/Service/appsettings.json diff --git a/BedrockService.backup/BedrockService.Shared/BedrockService.Shared.csproj b/BedrockService.backup/BedrockService.Shared/BedrockService.Shared.csproj new file mode 100644 index 00000000..470a5013 --- /dev/null +++ b/BedrockService.backup/BedrockService.Shared/BedrockService.Shared.csproj @@ -0,0 +1,80 @@ + + + + + Debug + AnyCPU + {F146D5E8-EF1F-4785-9150-182631F059B7} + Library + Properties + BedrockService.Shared + BedrockService.Shared + v4.7.2 + 512 + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 2 + + + + ..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll + + + + + + + ..\packages\System.IO.Compression.ZipFile.4.3.0\lib\net46\System.IO.Compression.ZipFile.dll + True + True + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/BedrockService.backup/BedrockService.Shared/Classes/ClientSideServiceConfiguration.cs b/BedrockService.backup/BedrockService.Shared/Classes/ClientSideServiceConfiguration.cs new file mode 100644 index 00000000..f1936fa2 --- /dev/null +++ b/BedrockService.backup/BedrockService.Shared/Classes/ClientSideServiceConfiguration.cs @@ -0,0 +1,28 @@ +using BedrockService.Shared.Interfaces; + +namespace BedrockService.Shared.Classes +{ + public class ClientSideServiceConfiguration : IClientSideServiceConfiguration + { + private readonly string _hostName; + private readonly string _address; + private readonly string _port; + private readonly string _displayName; + + public ClientSideServiceConfiguration(string hostName, string address, string port) + { + this._hostName = hostName; + this._address = address; + this._port = port; + _displayName = $@"{hostName}@{address}:{port}"; + } + + public string GetHostName() => _hostName; + + public string GetAddress() => _address; + + public string GetPort() => _port; + + public string GetDisplayName() => _displayName; + } +} diff --git a/BedrockService.backup/BedrockService.Shared/Classes/CommsKeyContainer.cs b/BedrockService.backup/BedrockService.Shared/Classes/CommsKeyContainer.cs new file mode 100644 index 00000000..2df98f77 --- /dev/null +++ b/BedrockService.backup/BedrockService.Shared/Classes/CommsKeyContainer.cs @@ -0,0 +1,95 @@ +using System; +using System.Collections.Generic; +using System.Security.Cryptography; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BedrockService.Shared.Classes +{ + [Serializable] + public class RSAContainer + { + public byte[] D; + public byte[] DP; + public byte[] DQ; + public byte[] Exponent; + public byte[] InverseQ; + public byte[] Modulus; + public byte[] P; + public byte[] Q; + + public RSAContainer(RSAParameters input) + { + D = input.D; + P = input.DP; + DP = input.DP; + Q = input.DQ; + DQ = input.DQ; + InverseQ = input.InverseQ; + Modulus = input.Modulus; + Exponent = input.Exponent; + } + + public RSAParameters GetPrivateKey() + { + return new RSAParameters() + { + D = this.D, + P = this.P, + DP = this.DP, + Q = this.Q, + DQ = this.DQ, + InverseQ = this.InverseQ, + Exponent = this.Exponent, + Modulus = this.Modulus + }; + } + + public RSAParameters GetPublicKey() + { + return new RSAParameters() + { + Modulus = this.Modulus, + Exponent = this.Exponent + }; + } + + public void SetPrivateKey(RSAParameters privateKey) + { + this.D = privateKey.D; + this.DP = privateKey.DP; + this.P = privateKey.P; + this.DQ = privateKey.DQ; + this.Q = privateKey.Q; + this.InverseQ = privateKey.InverseQ; + this.Modulus = privateKey.Modulus; + this.Exponent = privateKey.Exponent; + } + + public void SetPublicKey(RSAParameters publicKey) + { + this.Exponent = publicKey.Exponent; + this.Modulus = publicKey.Modulus; + } + } + + [Serializable] + public class CommsKeyContainer + { + public RSAContainer LocalPrivateKey = new RSAContainer(new RSAParameters()); + public RSAContainer RemotePublicKey = new RSAContainer(new RSAParameters()); + public byte[] AesKey; + public byte[] AesIV; + + public CommsKeyContainer() { } + + public CommsKeyContainer (RSAParameters priv, RSAParameters pub, byte[] key, byte[] IV) + { + LocalPrivateKey = new RSAContainer(priv); + RemotePublicKey = new RSAContainer(pub); + AesKey = key; + AesIV = IV; + } + } +} diff --git a/BedrockService.backup/BedrockService.Shared/Classes/NetworkEnums.cs b/BedrockService.backup/BedrockService.Shared/Classes/NetworkEnums.cs new file mode 100644 index 00000000..3cc62590 --- /dev/null +++ b/BedrockService.backup/BedrockService.Shared/Classes/NetworkEnums.cs @@ -0,0 +1,59 @@ +namespace BedrockService.Shared.Classes +{ + public enum NetworkMessageSource + { + Client, + Server, + Service + } + + + public enum NetworkMessageDestination + { + Client, + Server, + Service + } + + public enum NetworkMessageTypes + { + Connect, + AddNewServer, + RemoveServer, + ConsoleLogUpdate, + PropUpdate, + PlayersRequest, + PlayersUpdate, + StartCmdUpdate, + CheckUpdates, + PackFile, + PackList, + LevelEditRequest, + LevelEditFile, + RemovePack, + Command, + Backup, + BackupRollback, + BackupAll, + DelBackups, + EnumBackups, + Restart, + Heartbeat, + Disconnect, + UICallback + } + + public enum NetworkMessageFlags + { + Failed, + Passed, + RemoveBackups, + RemoveSrv, + RemovePlayers, + RemoveBckSrv, + RemoveBckPly, + RemovePlySrv, + RemoveAll, + None + } +} diff --git a/BedrockService.backup/BedrockService.Shared/Classes/Player.cs b/BedrockService.backup/BedrockService.Shared/Classes/Player.cs new file mode 100644 index 00000000..93b318b3 --- /dev/null +++ b/BedrockService.backup/BedrockService.Shared/Classes/Player.cs @@ -0,0 +1,124 @@ +using BedrockService.Shared.Interfaces; +using Newtonsoft.Json; +using System; + +namespace BedrockService.Shared.Classes +{ + public class Player : IPlayer + { + [JsonProperty] + private string Username { get; set; } + [JsonProperty] + private string XUID { get; set; } + [JsonProperty] + private string PermissionLevel; + [JsonProperty] + private string FirstConnectedTime { get; set; } + [JsonProperty] + private string LastConnectedTime { get; set; } + [JsonProperty] + private string LastDisconnectTime { get; set; } + [JsonProperty] + private string ServerDefaultPerm { get; set; } + [JsonProperty] + private bool Whitelisted { get; set; } + [JsonProperty] + private bool IgnorePlayerLimits { get; set; } + [JsonProperty] + private bool FromConfig { get; set; } + + [JsonConstructor] + public Player(string xuid, string username, string firstConn, string lastConn, string lastDiscon, bool whtlist, string perm, bool ignoreLimit) + { + Username = username; + XUID = xuid; + FirstConnectedTime = firstConn; + LastConnectedTime = lastConn; + LastDisconnectTime = lastDiscon; + Whitelisted = whtlist; + PermissionLevel = perm; + IgnorePlayerLimits = ignoreLimit; + } + + public Player(string serverDefaultPermission) + { + ServerDefaultPerm = serverDefaultPermission; + PermissionLevel = serverDefaultPermission; + } + + public void Initialize(string xuid, string username) + { + XUID = xuid; + Username = username; + } + + public string GetUsername() => Username; + + public string SearchForProperty(string input) + { + if (input == "name" || input == "username" || input == "un") + return Username; + if (input == "xuid" || input == "id") + return XUID; + if (input == "perm" || input == "permission" || input == "pl") + return PermissionLevel; + if (input == "whitelist" || input == "white" || input == "wl") + return Whitelisted.ToString(); + if (input == "ignoreslimit" || input == "il") + return IgnorePlayerLimits.ToString(); + return null; + } + + public string GetXUID() => XUID; + + public void UpdateTimes(string lastConn, string lastDiscon) + { + if (FirstConnectedTime == "") + FirstConnectedTime = DateTime.Now.Ticks.ToString(); + LastConnectedTime = lastConn; + LastDisconnectTime = lastDiscon; + } + + public void UpdateRegistration(string whtlist, string perm, string ignoreLimit) + { + Whitelisted = bool.Parse(whtlist); + PermissionLevel = perm; + IgnorePlayerLimits = bool.Parse(ignoreLimit); + } + + public string[] GetTimes() + { + return new string[] { FirstConnectedTime, LastConnectedTime, LastDisconnectTime }; + } + + public string[] GetRegistration() + { + return new string[] { Whitelisted.ToString(), PermissionLevel, IgnorePlayerLimits.ToString() }; + } + + public bool IsDefaultRegistration() + { + return Whitelisted == false && IgnorePlayerLimits == false && PermissionLevel == ServerDefaultPerm; + } + + public string ToString(string format) + { + if (format == "Known") + { + return $"{XUID},{Username},{FirstConnectedTime},{LastConnectedTime},{LastDisconnectTime}"; + } + if (format == "Registered") + { + return $"{XUID},{Username},{PermissionLevel},{Whitelisted},{IgnorePlayerLimits}"; + } + return null; + } + + public bool IsPlayerWhitelisted() => Whitelisted; + + public bool PlayerIgnoresLimit() => IgnorePlayerLimits; + + public string GetPermissionLevel() => PermissionLevel; + } + +} \ No newline at end of file diff --git a/BedrockService.backup/BedrockService.Shared/Classes/Property.cs b/BedrockService.backup/BedrockService.Shared/Classes/Property.cs new file mode 100644 index 00000000..ffc64a7a --- /dev/null +++ b/BedrockService.backup/BedrockService.Shared/Classes/Property.cs @@ -0,0 +1,29 @@ +using Newtonsoft.Json; + +namespace BedrockService.Shared.Classes +{ + public class Property + { + public string KeyName { get; set; } + public string Value { get; set; } + public string DefaultValue { get; set; } + + [JsonConstructor] + public Property(string key, string defaultValue) + { + KeyName = key; + Value = defaultValue; + DefaultValue = defaultValue; + } + + public override string ToString() + { + return Value; + } + + public void SetValue(string newValue) + { + Value = newValue; + } + } +} diff --git a/BedrockService.backup/BedrockService.Shared/Classes/ServerInfo.cs b/BedrockService.backup/BedrockService.Shared/Classes/ServerInfo.cs new file mode 100644 index 00000000..969d0a0a --- /dev/null +++ b/BedrockService.backup/BedrockService.Shared/Classes/ServerInfo.cs @@ -0,0 +1,223 @@ +using BedrockService.Shared.Interfaces; +using System.Collections.Generic; +using System.Linq; + +namespace BedrockService.Shared.Classes +{ + public class ServerInfo : IServerConfiguration + { + public string serversPath; + public string ServerName { get; set; } + public string FileName { get; set; } + public List ConsoleBuffer = new List(); + public Property ServerPath { get; set; } + public Property ServerExeName { get; set; } + public List PlayersList = new List(); + public List ServerPropList = new List(); + public List StartCmds = new List(); + + public ServerInfo(string[] configEntries, string coreServersPath) + { + serversPath = coreServersPath; + InitializeDefaults(); + ProcessConfiguration(configEntries); + } + + public void InitializeDefaults() + { + ServerName = "Default"; + FileName = "Default.conf"; + ServerPath = new Property("ServerPath", $@"{serversPath}\{ServerName}"); + ServerExeName = new Property("ServerExeName", $"BedrockService.{ServerName}.exe"); + + ServerPropList.Clear(); + ServerPropList.Add(new Property("server-name", "Default")); + ServerPropList.Add(new Property("gamemode", "creative")); + ServerPropList.Add(new Property("difficulty", "easy")); + ServerPropList.Add(new Property("allow-cheats", "false")); + ServerPropList.Add(new Property("max-players", "10")); + ServerPropList.Add(new Property("online-mode", "true")); + ServerPropList.Add(new Property("white-list", "false")); + ServerPropList.Add(new Property("server-port", "19132")); + ServerPropList.Add(new Property("server-portv6", "19133")); + ServerPropList.Add(new Property("view-distance", "32")); + ServerPropList.Add(new Property("tick-distance", "4")); + ServerPropList.Add(new Property("player-idle-timeout", "30")); + ServerPropList.Add(new Property("max-threads", "8")); + ServerPropList.Add(new Property("level-name", "Bedrock Level")); + ServerPropList.Add(new Property("level-seed", "")); + ServerPropList.Add(new Property("default-player-permission-level", "member")); + ServerPropList.Add(new Property("texturepack-required", "false")); + ServerPropList.Add(new Property("content-log-file-enabled", "false")); + ServerPropList.Add(new Property("compression-threshold", "1")); + ServerPropList.Add(new Property("server-authoritative-movement", "server-auth")); + ServerPropList.Add(new Property("player-movement-score-threshold", "20")); + ServerPropList.Add(new Property("player-movement-distance-threshold", "0.3")); + ServerPropList.Add(new Property("player-movement-duration-threshold-in-ms", "500")); + ServerPropList.Add(new Property("correct-player-movement", "false")); + } + + + public void SetStartCommands(List newEntries) => StartCmds = newEntries; + + public void ProcessConfiguration(string[] fileEntries) + { + if (fileEntries == null) + return; + foreach (string line in fileEntries) + { + if (!line.StartsWith("#") && !string.IsNullOrEmpty(line)) + { + string[] split = line.Split('='); + if (split.Length == 1) + { + split = new string[] { split[0], "" }; + } + Property SrvProp = ServerPropList.FirstOrDefault(prop => prop.KeyName == split[0]); + if (SrvProp != null) + { + SetProp(split[0], split[1]); + } + switch (split[0]) + { + case "server-name": + ServerName = split[1]; + ServerPath.SetValue($@"{serversPath}\{ServerName}"); + ServerExeName.SetValue($"BedrockService.{ServerName}.exe"); + FileName = $@"{ServerName}.conf"; + break; + + case "AddStartCmd": + StartCmds.Add(new StartCmdEntry(split[1])); + break; + } + } + } + } + + public bool SetProp(string name, string newValue) + { + try + { + Property serverProp = ServerPropList.First(prop => prop.KeyName == name); + ServerPropList[ServerPropList.IndexOf(serverProp)].SetValue(newValue); + return true; + } + catch + { + + } + return false; + } + + public bool SetProp(Property propToSet) + { + try + { + Property serverProp = ServerPropList.First(prop => prop.KeyName == propToSet.KeyName); + ServerPropList[ServerPropList.IndexOf(serverProp)] = propToSet; + return true; + } + catch + { + + } + return false; + } + + public Property GetProp(string name) + { + Property foundProp = ServerPropList.FirstOrDefault(prop => prop.KeyName == name); + if (foundProp == null) + { + switch (name) + { + case "ServerPath": + return ServerPath; + case "ServerExeName": + return ServerExeName; + } + } + return foundProp; + } + + public void SetAllInfos() => throw new System.NotImplementedException(); + + public void AddStartCommand(string command) + { + StartCmds.Add(new StartCmdEntry(command)); + } + + public bool DeleteStartCommand(string command) + { + StartCmdEntry entry = StartCmds.FirstOrDefault(prop => prop.Command == command); + return StartCmds.Remove(entry); + } + + public List GetStartCommands() => StartCmds; + + public override string ToString() + { + return ServerName; + } + + public override int GetHashCode() + { + int hashCode = -298215838; + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(ServerName); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(FileName); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(ServerPath); + return hashCode; + } + + public override bool Equals(object obj) + { + return obj is ServerInfo info && + ServerName == info.ServerName && + EqualityComparer.Default.Equals(ServerPath, info.ServerPath); + } + + public string GetServerName() => ServerName; + + public List GetAllProps() => ServerPropList; + + public void SetAllProps(List newPropList) => ServerPropList = newPropList; + + public void AddUpdatePlayer(IPlayer player) + { + IPlayer foundPlayer = PlayersList.FirstOrDefault(p => p.GetXUID() == player.GetXUID()); + if (foundPlayer == null) + { + PlayersList.Add(player); + return; + } + PlayersList[PlayersList.IndexOf(foundPlayer)] = player; + } + + public IPlayer GetPlayerByXuid(string xuid) + { + IPlayer foundPlayer = PlayersList.FirstOrDefault(p => p.GetXUID() == xuid); + if (foundPlayer == null) + { + return null; + } + return foundPlayer; + } + + public string GetFileName() + { + return FileName; + } + + public List GetLog() => ConsoleBuffer = ConsoleBuffer ?? new List(); + + public void SetLog(List newLog) => ConsoleBuffer = newLog; + + public List GetPlayerList() => PlayersList ?? new List(); + + public void SetPlayerList(List newList) => PlayersList = newList; + + public IServerConfiguration GetServerInfo() => this; + } + +} \ No newline at end of file diff --git a/BedrockService.backup/BedrockService.Shared/Classes/ServerLogger.cs b/BedrockService.backup/BedrockService.Shared/Classes/ServerLogger.cs new file mode 100644 index 00000000..83fa2495 --- /dev/null +++ b/BedrockService.backup/BedrockService.Shared/Classes/ServerLogger.cs @@ -0,0 +1,102 @@ +using BedrockService.Shared.Interfaces; +using Newtonsoft.Json; +using System; +using System.IO; +using System.Text; + +namespace BedrockService.Shared.Classes +{ + public class ServerLogger : ILogger + { + private StringBuilder OutString = new StringBuilder(); + [NonSerialized] + private readonly StreamWriter _logWriter; + private readonly bool _logToFile = false; + private readonly bool _logToConsole = false; + private readonly string _parent; + private readonly string _logDir; + private readonly IServerConfiguration _serverConfiguration; + + public ServerLogger(IProcessInfo processInfo, IServiceConfiguration serviceConfiguration, IServerConfiguration serverConfiguration, string parent) + { + _serverConfiguration = serverConfiguration; + _logDir = $@"{processInfo.GetDirectory()}\Server\Logs"; + _logToFile = bool.Parse(serviceConfiguration.GetProp("LogServersToFile").ToString()); + _parent = parent; + _logToConsole = true; + if (_logToFile) + { + if (!Directory.Exists(_logDir)) + Directory.CreateDirectory(_logDir); + _logWriter = new StreamWriter($@"{_logDir}\ServerLog_{parent}_{DateTime.Now:yyyymmddhhmmss}.log", true); + } + } + + [JsonConstructor] + public ServerLogger(string serverName) + { + _parent = serverName; + _logToFile = false; + _logToConsole = false; + } + + public void AppendLine(string text) + { + try + { + _serverConfiguration.GetLog().Add(text); + if (_logToFile && _logWriter != null) + { + _logWriter.WriteLine(text); + _logWriter.Flush(); + } + if (_logToConsole) + Console.WriteLine(text); + } + catch + { + } + } + + public void AppendText(string text) + { + try + { + _serverConfiguration.GetLog().Add(text); + if (_logToFile && _logWriter != null) + { + _logWriter.Write(text); + _logWriter.Flush(); + } + if (_logToConsole) + { + Console.Write(text); + Console.Out.Flush(); + } + } + catch + { + } + } + + public int Count() + { + return _serverConfiguration.GetLog().Count; + } + + public string FromIndex(int index) + { + return _serverConfiguration.GetLog()[index]; + } + + public override string ToString() + { + OutString = new StringBuilder(); + foreach (string s in _serverConfiguration.GetLog()) + { + OutString.Append(s); + } + return OutString.ToString(); + } + } +} diff --git a/BedrockService.backup/BedrockService.Shared/Classes/ServiceInfo.cs b/BedrockService.backup/BedrockService.Shared/Classes/ServiceInfo.cs new file mode 100644 index 00000000..b7fea589 --- /dev/null +++ b/BedrockService.backup/BedrockService.Shared/Classes/ServiceInfo.cs @@ -0,0 +1,156 @@ +using BedrockService.Shared.Interfaces; +using Newtonsoft.Json; +using System.Collections.Generic; +using System.Linq; + +namespace BedrockService.Shared.Classes +{ + public class ServiceInfo : IServiceConfiguration + { + private string hostName { get; set; } + private string address { get; set; } + private string hostDisplayName { get; set; } + [JsonProperty] + private string serverVersion { get; set; } + [JsonProperty] + private List serviceLog = new List(); + [JsonProperty] + private List ServerList = new List(); + [JsonProperty] + private List globals = new List(); + private readonly IProcessInfo _processInfo; + + public ServiceInfo(IProcessInfo processInfo) + { + _processInfo = processInfo; + InitializeDefaults(); + } + + public void InitializeDefaults() + { + globals.Clear(); + globals.Add(new Property("ServersPath", @"C:\MCBedrockService")); + globals.Add(new Property("AcceptedMojangLic", "false")); + globals.Add(new Property("ClientPort", "19134")); + globals.Add(new Property("BackupEnabled", "false")); + globals.Add(new Property("BackupPath", "Default")); + globals.Add(new Property("BackupCron", "0 1 * * *")); + globals.Add(new Property("MaxBackupCount", "25")); + globals.Add(new Property("EntireBackups", "false")); + globals.Add(new Property("CheckUpdates", "false")); + globals.Add(new Property("UpdateCron", "0 2 * * *")); + globals.Add(new Property("LogServersToFile", "true")); + globals.Add(new Property("LogServiceToFile", "true")); + } + + public void ProcessConfiguration(string[] fileEntries) + { + foreach (string line in fileEntries) + { + if (!line.StartsWith("#") && !string.IsNullOrEmpty(line)) + { + string[] split = line.Split('='); + if (split.Length == 1) + { + split[1] = ""; + } + if (split[0] == "BackupPath") + { + if (split[1] == "Default") + split[1] = $@"{_processInfo.GetDirectory()}\Server\Backups"; + } + if (!SetProp(split[0], split[1])) + { + //Logger.AppendLine($"Error! Key \"{split[0]}\" was not found! Check configs!"); + } + } + } + } + + public bool SetProp(string name, string newValue) + { + try + { + Property GlobalToEdit = globals.First(glob => glob.KeyName == name); + globals[globals.IndexOf(GlobalToEdit)].SetValue(newValue); + return true; + } + catch + { + // handle soon. + return false; + } + } + + public bool SetProp(Property propToSet) + { + try + { + Property GlobalToEdit = globals.First(glob => glob.KeyName == propToSet.KeyName); + globals[globals.IndexOf(GlobalToEdit)] = propToSet; + } + catch + { + // handle soon. + return false; + } + return true; + } + + public Property GetProp(string name) + { + return globals.FirstOrDefault(prop => prop.KeyName == name); + } + + public List GetAllProps() => globals; + + public void SetAllProps(List props) => globals = props; + + public IServerConfiguration GetServerInfoByName(string serverName) + { + return ServerList.FirstOrDefault(info => info.GetServerName() == serverName); + } + + public IServerConfiguration GetServerInfoByIndex(int index) + { + return ServerList[index]; + } + + public void RemoveServerInfo(IServerConfiguration serverConfiguration) + { + ServerList.Remove(serverConfiguration.GetServerInfo()); + } + + public void SetServerInfo(IServerConfiguration newInfo) + { + ServerList[ServerList.IndexOf(newInfo.GetServerInfo())] = newInfo.GetServerInfo(); + } + + public List GetServerList() => ServerList; + + public void SetAllServerInfos(List newInfos) + { + ServerList = newInfos; + } + + public void AddNewServerInfo(IServerConfiguration serverConfiguration) + { + ServerList.Add(serverConfiguration); + } + + public void RemoveServerInfoByIndex(int serverIndex) + { + ServerList.RemoveAt(serverIndex); + } + + public void SetServerVersion(string newVersion) => serverVersion = newVersion; + + public string GetServerVersion() => serverVersion; + + public List GetLog() => serviceLog ?? new List(); + + public void SetLog(List newLog) => serviceLog = newLog; + + public byte GetServerIndex(IServerConfiguration server) => (byte)ServerList.IndexOf(server); + } +} \ No newline at end of file diff --git a/BedrockService.backup/BedrockService.Shared/Classes/ServiceProcessInfo.cs b/BedrockService.backup/BedrockService.Shared/Classes/ServiceProcessInfo.cs new file mode 100644 index 00000000..9f9e0645 --- /dev/null +++ b/BedrockService.backup/BedrockService.Shared/Classes/ServiceProcessInfo.cs @@ -0,0 +1,38 @@ +using BedrockService.Shared.Interfaces; + +namespace BedrockService.Shared.Classes +{ + public class ServiceProcessInfo : IProcessInfo + { + private readonly string _serviceDirectory; + private readonly string _serviceExeName; + private readonly int _processPid; + private bool _debugEnabled; + private bool _isConsoleMode; + + public ServiceProcessInfo(string serviceDirectory, string serviceExeName, int processPid, bool debugEnabled, bool isConsoleMode) + { + _serviceDirectory = serviceDirectory; + _serviceExeName = serviceExeName; + _processPid = processPid; + _debugEnabled = debugEnabled; + _isConsoleMode = isConsoleMode; + } + + public string GetDirectory() => _serviceDirectory; + + public string GetExecutableName() => _serviceExeName; + + public int GetProcessPID() => _processPid; + + public bool IsDebugEnabled() => _debugEnabled; + + public bool IsConsoleMode() => _isConsoleMode; + + public void SetArguments(bool isDebug, bool isConsole) + { + _debugEnabled = isDebug; + _isConsoleMode = isConsole; + } + } +} diff --git a/BedrockService.backup/BedrockService.Shared/Classes/StartCmdEntry.cs b/BedrockService.backup/BedrockService.Shared/Classes/StartCmdEntry.cs new file mode 100644 index 00000000..5200e2d8 --- /dev/null +++ b/BedrockService.backup/BedrockService.Shared/Classes/StartCmdEntry.cs @@ -0,0 +1,12 @@ +namespace BedrockService.Shared.Classes +{ + public class StartCmdEntry + { + public string Command = "help 1"; + + public StartCmdEntry(string entry) + { + Command = entry; + } + } +} \ No newline at end of file diff --git a/BedrockService.backup/BedrockService.Shared/Interfaces/IClientSideServiceConfiguration.cs b/BedrockService.backup/BedrockService.Shared/Interfaces/IClientSideServiceConfiguration.cs new file mode 100644 index 00000000..4d4c8f7a --- /dev/null +++ b/BedrockService.backup/BedrockService.Shared/Interfaces/IClientSideServiceConfiguration.cs @@ -0,0 +1,10 @@ +namespace BedrockService.Shared.Interfaces +{ + public interface IClientSideServiceConfiguration + { + string GetAddress(); + string GetDisplayName(); + string GetHostName(); + string GetPort(); + } +} \ No newline at end of file diff --git a/BedrockService/BedrockService.Shared/Interfaces/IConfiguration.cs b/BedrockService.backup/BedrockService.Shared/Interfaces/IConfiguration.cs similarity index 100% rename from BedrockService/BedrockService.Shared/Interfaces/IConfiguration.cs rename to BedrockService.backup/BedrockService.Shared/Interfaces/IConfiguration.cs diff --git a/BedrockService/BedrockService.Shared/Interfaces/ILogger.cs b/BedrockService.backup/BedrockService.Shared/Interfaces/ILogger.cs similarity index 100% rename from BedrockService/BedrockService.Shared/Interfaces/ILogger.cs rename to BedrockService.backup/BedrockService.Shared/Interfaces/ILogger.cs diff --git a/BedrockService.backup/BedrockService.Shared/Interfaces/IPlayer.cs b/BedrockService.backup/BedrockService.Shared/Interfaces/IPlayer.cs new file mode 100644 index 00000000..f4a4876e --- /dev/null +++ b/BedrockService.backup/BedrockService.Shared/Interfaces/IPlayer.cs @@ -0,0 +1,19 @@ +namespace BedrockService.Shared.Interfaces +{ + public interface IPlayer + { + void Initialize(string xuid, string username); + void UpdateTimes(string lastConn, string lastDiscon); + void UpdateRegistration(string permission, string whitelisted, string ignoreMaxPlayerLimit); + string SearchForProperty(string input); + string GetUsername(); + string GetXUID(); + string[] GetTimes(); + string[] GetRegistration(); + bool IsPlayerWhitelisted(); + bool PlayerIgnoresLimit(); + string GetPermissionLevel(); + bool IsDefaultRegistration(); + string ToString(string format); + } +} diff --git a/BedrockService.backup/BedrockService.Shared/Interfaces/IProcessInfo.cs b/BedrockService.backup/BedrockService.Shared/Interfaces/IProcessInfo.cs new file mode 100644 index 00000000..b6af2a12 --- /dev/null +++ b/BedrockService.backup/BedrockService.Shared/Interfaces/IProcessInfo.cs @@ -0,0 +1,17 @@ +namespace BedrockService.Shared.Interfaces +{ + public interface IProcessInfo + { + string GetDirectory(); + + string GetExecutableName(); + + int GetProcessPID(); + + bool IsDebugEnabled(); + + bool IsConsoleMode(); + + void SetArguments(bool isDebug, bool isConsole); + } +} diff --git a/BedrockService.backup/BedrockService.Shared/Interfaces/IServerConfiguration.cs b/BedrockService.backup/BedrockService.Shared/Interfaces/IServerConfiguration.cs new file mode 100644 index 00000000..ed5c41bb --- /dev/null +++ b/BedrockService.backup/BedrockService.Shared/Interfaces/IServerConfiguration.cs @@ -0,0 +1,30 @@ +using BedrockService.Shared.Classes; +using System.Collections.Generic; + +namespace BedrockService.Shared.Interfaces +{ + public interface IServerConfiguration : IConfiguration + { + string GetServerName(); + + string GetFileName(); + + void AddStartCommand(string command); + + bool DeleteStartCommand(string command); + + List GetStartCommands(); + + void SetStartCommands(List newEntries); + + void AddUpdatePlayer(IPlayer player); + + IPlayer GetPlayerByXuid(string xuid); + + List GetPlayerList(); + + void SetPlayerList(List newList); + + IServerConfiguration GetServerInfo(); + } +} diff --git a/BedrockService.backup/BedrockService.Shared/Interfaces/IServiceConfiguration.cs b/BedrockService.backup/BedrockService.Shared/Interfaces/IServiceConfiguration.cs new file mode 100644 index 00000000..836d530b --- /dev/null +++ b/BedrockService.backup/BedrockService.Shared/Interfaces/IServiceConfiguration.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; + +namespace BedrockService.Shared.Interfaces +{ + public interface IServiceConfiguration : IConfiguration + { + IServerConfiguration GetServerInfoByName(string serverName); + + IServerConfiguration GetServerInfoByIndex(int index); + + byte GetServerIndex(IServerConfiguration server); + + List GetServerList(); + + void SetAllServerInfos(List newInfos); + + void AddNewServerInfo(IServerConfiguration serverConfiguration); + + void RemoveServerInfoByIndex(int serverIndex); + + void RemoveServerInfo(IServerConfiguration serverConfiguration); + + void SetServerVersion(string newVersion); + + string GetServerVersion(); + } +} diff --git a/BedrockService.backup/BedrockService.Shared/PackParser/MinecraftKnownPacksClass.cs b/BedrockService.backup/BedrockService.Shared/PackParser/MinecraftKnownPacksClass.cs new file mode 100644 index 00000000..68f1dfc8 --- /dev/null +++ b/BedrockService.backup/BedrockService.Shared/PackParser/MinecraftKnownPacksClass.cs @@ -0,0 +1,67 @@ +using Newtonsoft.Json.Linq; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace BedrockService.Shared.PackParser +{ + public class MinecraftKnownPacksClass + { + public List KnownPacks = new List(); + private readonly List _stockPacks = new List(); + public class KnownPack + { + public int file_version { get; set; } + public string file_system { get; set; } + public bool? from_disk { get; set; } + public List hashes { get; set; } + public string path { get; set; } + public string uuid { get; set; } + public string version { get; set; } + + public override string ToString() + { + return path; + } + } + + public MinecraftKnownPacksClass(string serverFile, string stockFile) + { + KnownPacks = ParseJsonArray(serverFile); + _stockPacks = ParseJsonArray(stockFile); + KnownPacks.RemoveAt(0); // Strip file version entry. + foreach (KnownPack pack in _stockPacks) + { + KnownPack packToRemove = new KnownPack(); + if (pack.uuid == null) + continue; + packToRemove = KnownPacks.First(p => p.uuid != null && p.uuid == pack.uuid); + KnownPacks.Remove(packToRemove); + } + } + + public void RemovePackFromServer(string serverPath, MinecraftPackContainer pack) + { + if (pack.ManifestType == "WorldPack") + Directory.Delete($@"{serverPath}\worlds\{pack.FolderName}", true); + if (pack.ManifestType == "data") + Directory.Delete($@"{serverPath}\behavior_packs\{pack.FolderName}", true); + if (pack.ManifestType == "resources") + Directory.Delete($@"{serverPath}\resource_packs\{ pack.FolderName}", true); + KnownPacks.Remove(KnownPacks.First(p => p.uuid != null || p.uuid == pack.JsonManifest.header.uuid)); + List packsToWrite = new List(_stockPacks); + if (KnownPacks.Count > 0) + packsToWrite.AddRange(KnownPacks); + File.WriteAllText($@"{serverPath}\valid_known_packs.json", JArray.FromObject(packsToWrite).ToString()); + } + + private List ParseJsonArray(string jsonFile) + { + JArray packList = JArray.Parse(File.ReadAllText(jsonFile)); + List parsedPackInfos = new List(); + foreach (JToken token in packList) + parsedPackInfos.Add(token.ToObject()); + return parsedPackInfos; + } + } +} diff --git a/BedrockService.backup/BedrockService.Shared/PackParser/MinecraftPackParser.cs b/BedrockService.backup/BedrockService.Shared/PackParser/MinecraftPackParser.cs new file mode 100644 index 00000000..c6c4127e --- /dev/null +++ b/BedrockService.backup/BedrockService.Shared/PackParser/MinecraftPackParser.cs @@ -0,0 +1,174 @@ +using BedrockService.Shared.Interfaces; +using BedrockService.Shared.Utilities; +using Newtonsoft.Json; +using System.Collections.Generic; +using System.IO; +using System.IO.Compression; + +namespace BedrockService.Shared.PackParser +{ + public class Header + { + public string description { get; set; } + public string name { get; set; } + public string uuid { get; set; } + public List version { get; set; } + } + + public class Module + { + public string description { get; set; } + public string type { get; set; } + public string uuid { get; set; } + public List version { get; set; } + } + + public class Dependency + { + public string uuid { get; set; } + public List version { get; set; } + } + + public class Manifest + { + public int format_version { get; set; } + public Header header { get; set; } + public List modules { get; set; } + public List dependencies { get; set; } + } + + public class MinecraftPackContainer + { + public Manifest JsonManifest; + public DirectoryInfo PackContentLocation; + public string ManifestType; + public string FolderName; + public byte[] IconBytes; + + public override string ToString() + { + return JsonManifest != null ? JsonManifest.header.name : "WorldPack"; + } + } + + public class MinecraftPackParser + { + private readonly IProcessInfo _processInfo; + private readonly ILogger _logger; + public DirectoryInfo PackExtractDirectory; + public List FoundPacks = new List(); + + [JsonConstructor] + public MinecraftPackParser(ILogger logger, IProcessInfo processInfo) + { + _processInfo = processInfo; + _logger = logger; + } + + public MinecraftPackParser(byte[] fileContents, ILogger logger, IProcessInfo processInfo) + { + _logger = logger; + PackExtractDirectory = new DirectoryInfo($@"{processInfo.GetDirectory()}\Temp"); + _processInfo = processInfo; + new FileUtils(processInfo.GetDirectory()).ClearTempDir(); + using (MemoryStream fileStream = new MemoryStream(fileContents, 5, fileContents.Length - 5)) + { + using (ZipArchive zipArchive = new ZipArchive(fileStream, ZipArchiveMode.Read)) + { + zipArchive.ExtractToDirectory(PackExtractDirectory.FullName); + } + } + ParseDirectory(PackExtractDirectory); + } + + public MinecraftPackParser(string[] files, DirectoryInfo extractDir, ILogger logger, IProcessInfo processInfo) + { + PackExtractDirectory = extractDir; + _logger = logger; + _processInfo = processInfo; + new FileUtils(_processInfo.GetDirectory()).ClearTempDir(); + if (Directory.Exists($@"{PackExtractDirectory.FullName}\ZipTemp")) + { + Directory.CreateDirectory($@"{PackExtractDirectory.FullName}\ZipTemp"); + } + foreach (string file in files) + { + FileInfo fInfo = new FileInfo(file); + string zipFilePath = $@"{PackExtractDirectory.FullName}\{fInfo.Name.Replace(fInfo.Extension, "")}"; + ZipFile.ExtractToDirectory(file, zipFilePath); + foreach (FileInfo extractedFile in new DirectoryInfo(zipFilePath).GetFiles()) + { + if (extractedFile.Extension == ".mcpack") + { + Directory.CreateDirectory($@"{zipFilePath}\{extractedFile.Name.Replace(extractedFile.Extension, "")}"); + ZipFile.ExtractToDirectory(extractedFile.FullName, $@"{zipFilePath}\{fInfo.Name.Replace(fInfo.Extension, "")}_{extractedFile.Name.Replace(extractedFile.Extension, "")}"); + } + } + } + foreach (DirectoryInfo directory in PackExtractDirectory.GetDirectories()) + ParseDirectory(directory); + } + + public void ParseDirectory(DirectoryInfo directoryToParse) + { + _logger.AppendLine($"Parsing directory {directoryToParse.Name}"); + if (directoryToParse.Exists) + { + foreach (FileInfo file in directoryToParse.GetFiles("*", SearchOption.AllDirectories)) + { + if (file.Name == "levelname.txt") + { + byte[] iconBytes; + try + { + if (File.Exists($@"{file.Directory.FullName}\world_icon.jpeg")) + iconBytes = File.ReadAllBytes($@"{file.Directory.FullName}\world_icon.jpeg"); + else + iconBytes = File.ReadAllBytes($@"{file.Directory.FullName}\world_icon.png"); + } + catch + { + iconBytes = null; + } + + FoundPacks.Add(new MinecraftPackContainer + { + JsonManifest = null, + PackContentLocation = file.Directory, + ManifestType = "WorldPack", + FolderName = file.Directory.Name, + IconBytes = iconBytes + }); + _logger.AppendLine("Pack was detected as MCWorld"); + return; + } + if (file.Name == "manifest.json") + { + byte[] iconBytes; + if (File.Exists($@"{file.Directory.FullName}\pack_icon.jpeg")) + iconBytes = File.ReadAllBytes($@"{file.Directory.FullName}\pack_icon.jpeg"); + else + iconBytes = File.ReadAllBytes($@"{file.Directory.FullName}\pack_icon.png"); + + MinecraftPackContainer container = new MinecraftPackContainer + { + JsonManifest = new Manifest(), + PackContentLocation = file.Directory, + ManifestType = "", + FolderName = file.Directory.Name, + IconBytes = iconBytes + }; + JsonSerializerSettings settings = new JsonSerializerSettings() + { + TypeNameHandling = TypeNameHandling.All + }; + container.JsonManifest = JsonConvert.DeserializeObject(File.ReadAllText(file.FullName), settings); + container.ManifestType = container.JsonManifest.modules[0].type; + _logger.AppendLine($"{container.ManifestType} pack found, name: {container.JsonManifest.header.name}, path: {container.PackContentLocation.Name}"); + FoundPacks.Add(container); + } + } + } + } + } +} diff --git a/BedrockService.backup/BedrockService.Shared/Properties/AssemblyInfo.cs b/BedrockService.backup/BedrockService.Shared/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..0b64fbcf --- /dev/null +++ b/BedrockService.backup/BedrockService.Shared/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("BedrockService.Shared")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("BedrockService.Shared")] +[assembly: AssemblyCopyright("Copyright © 2021")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("f146d5e8-ef1f-4785-9150-182631f059b7")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// 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")] diff --git a/BedrockService.backup/BedrockService.Shared/Utilities/FileUtils.cs b/BedrockService.backup/BedrockService.Shared/Utilities/FileUtils.cs new file mode 100644 index 00000000..6ef8cebb --- /dev/null +++ b/BedrockService.backup/BedrockService.Shared/Utilities/FileUtils.cs @@ -0,0 +1,62 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace BedrockService.Shared.Utilities +{ + public class FileUtils + { + readonly string _servicePath; + public FileUtils(string servicePath) + { + this._servicePath = servicePath; + } + public void CopyFilesRecursively(DirectoryInfo source, DirectoryInfo target) + { + foreach (DirectoryInfo dir in source.GetDirectories()) + CopyFilesRecursively(dir, target.CreateSubdirectory(dir.Name)); + foreach (FileInfo file in source.GetFiles()) + file.CopyTo(Path.Combine(target.FullName, file.Name), true); + } + + public void DeleteFilesRecursively(DirectoryInfo source, bool removeSourceFolder) + { + foreach (DirectoryInfo dir in source.GetDirectories()) + DeleteFilesRecursively(dir, removeSourceFolder); + foreach (FileInfo file in source.GetFiles()) + file.Delete(); + foreach (DirectoryInfo emptyDir in source.GetDirectories()) + emptyDir.Delete(true); + if (removeSourceFolder) + source.Delete(true); + } + + public void ClearTempDir() + { + DirectoryInfo tempDirectory = new DirectoryInfo($@"{_servicePath}\Temp"); + if (!tempDirectory.Exists) + tempDirectory.Create(); + foreach (FileInfo file in tempDirectory.GetFiles("*", SearchOption.AllDirectories)) + file.Delete(); + foreach (DirectoryInfo directory in tempDirectory.GetDirectories()) + directory.Delete(true); + } + + public void DeleteFilelist(string[] fileList, string serverPath) + { + foreach (string file in fileList) + try + { + File.Delete($@"{serverPath}\{file}"); + } + catch { } + List exesInPath = Directory.EnumerateFiles(serverPath, "*.exe", SearchOption.AllDirectories).ToList(); + foreach (string exe in exesInPath) + File.Delete(exe); + foreach (string dir in Directory.GetDirectories(serverPath)) + if (Directory.EnumerateFiles(dir, "*", SearchOption.AllDirectories).Count() == 0) + Directory.Delete(dir, true); + } + + } +} diff --git a/BedrockService/BedrockService.Shared/packages.config b/BedrockService.backup/BedrockService.Shared/packages.config similarity index 100% rename from BedrockService/BedrockService.Shared/packages.config rename to BedrockService.backup/BedrockService.Shared/packages.config diff --git a/BedrockService.backup/BedrockService.Shared/upgrade.backup b/BedrockService.backup/BedrockService.Shared/upgrade.backup new file mode 100644 index 00000000..a39f2887 --- /dev/null +++ b/BedrockService.backup/BedrockService.Shared/upgrade.backup @@ -0,0 +1 @@ +Backup created at 1636641895 (11/11/2021 2:44:55 PM +00:00) \ No newline at end of file diff --git a/BedrockService.backup/Client/App.config b/BedrockService.backup/Client/App.config new file mode 100644 index 00000000..56efbc7b --- /dev/null +++ b/BedrockService.backup/Client/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/BedrockService.backup/Client/BedrockService.Client.csproj b/BedrockService.backup/Client/BedrockService.Client.csproj new file mode 100644 index 00000000..3627b532 --- /dev/null +++ b/BedrockService.backup/Client/BedrockService.Client.csproj @@ -0,0 +1,186 @@ + + + + + Debug + AnyCPU + {A2DAF70A-925A-4F4B-B7EE-CAACE75D6740} + WinExe + BedrockService.Client + BedrockService.Client + v4.7.2 + 512 + true + true + false + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + true + + + x86 + true + full + false + ..\bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + x86 + pdbonly + true + ..\bin\Release\ + TRACE + prompt + 4 + + + BedrockService.Client.Forms.MainWindow + + + + ..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll + + + + + + + + + + + + + + + + + + Form + + + AddNewServerForm.cs + + + Form + + + ClientConfigForm.cs + + + Form + + + ManagePacksForms.cs + + + Form + + + NewPlayerRegistrationForm.cs + + + Form + + + PlayerManagerForm.cs + + + + + + Form + + + PropEditorForm.cs + + + Form + + + MainWindow.cs + + + + + + AddNewServerForm.cs + + + ClientConfigForm.cs + + + ManagePacksForms.cs + + + NewPlayerRegistrationForm.cs + + + PlayerManagerForm.cs + + + PropEditorForm.cs + + + MainWindow.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + True + + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + + + + False + Microsoft .NET Framework 4.7.2 %28x86 and x64%29 + true + + + False + .NET Framework 3.5 SP1 + false + + + + + + {f146d5e8-ef1f-4785-9150-182631f059b7} + BedrockService.Shared + + + + \ No newline at end of file diff --git a/BedrockService.backup/Client/Configs/Config.conf b/BedrockService.backup/Client/Configs/Config.conf new file mode 100644 index 00000000..86398eeb --- /dev/null +++ b/BedrockService.backup/Client/Configs/Config.conf @@ -0,0 +1,2 @@ +[Hosts] +host1=127.0.0.1:19134 diff --git a/BedrockService.backup/Client/Forms/AddNewServerForm.Designer.cs b/BedrockService.backup/Client/Forms/AddNewServerForm.Designer.cs new file mode 100644 index 00000000..1291703f --- /dev/null +++ b/BedrockService.backup/Client/Forms/AddNewServerForm.Designer.cs @@ -0,0 +1,158 @@ + +namespace BedrockService.Client.Forms +{ + partial class AddNewServerForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.srvNameBox = new System.Windows.Forms.TextBox(); + this.label1 = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.ipV4Box = new System.Windows.Forms.TextBox(); + this.label3 = new System.Windows.Forms.Label(); + this.ipV6Box = new System.Windows.Forms.TextBox(); + this.editPropsBtn = new System.Windows.Forms.Button(); + this.saveBtn = new System.Windows.Forms.Button(); + this.label4 = new System.Windows.Forms.Label(); + this.SuspendLayout(); + // + // srvNameBox + // + this.srvNameBox.Location = new System.Drawing.Point(106, 56); + this.srvNameBox.Name = "srvNameBox"; + this.srvNameBox.Size = new System.Drawing.Size(100, 20); + this.srvNameBox.TabIndex = 0; + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(30, 59); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(70, 13); + this.label1.TabIndex = 1; + this.label1.Text = "Server name:"; + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(30, 85); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(56, 13); + this.label2.TabIndex = 3; + this.label2.Text = "IP v4 port:"; + // + // ipV4Box + // + this.ipV4Box.Location = new System.Drawing.Point(106, 82); + this.ipV4Box.Name = "ipV4Box"; + this.ipV4Box.Size = new System.Drawing.Size(100, 20); + this.ipV4Box.TabIndex = 2; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(30, 111); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(56, 13); + this.label3.TabIndex = 5; + this.label3.Text = "IP v6 port:"; + // + // ipV6Box + // + this.ipV6Box.Location = new System.Drawing.Point(106, 108); + this.ipV6Box.Name = "ipV6Box"; + this.ipV6Box.Size = new System.Drawing.Size(100, 20); + this.ipV6Box.TabIndex = 4; + // + // editPropsBtn + // + this.editPropsBtn.Location = new System.Drawing.Point(33, 149); + this.editPropsBtn.Name = "editPropsBtn"; + this.editPropsBtn.Size = new System.Drawing.Size(173, 23); + this.editPropsBtn.TabIndex = 6; + this.editPropsBtn.Text = "Edit server settings..."; + this.editPropsBtn.UseVisualStyleBackColor = true; + this.editPropsBtn.Click += new System.EventHandler(this.editPropsBtn_Click); + // + // saveBtn + // + this.saveBtn.Location = new System.Drawing.Point(106, 191); + this.saveBtn.Name = "saveBtn"; + this.saveBtn.Size = new System.Drawing.Size(100, 23); + this.saveBtn.TabIndex = 7; + this.saveBtn.Text = "Save Server"; + this.saveBtn.UseVisualStyleBackColor = true; + this.saveBtn.Click += new System.EventHandler(this.saveBtn_Click); + // + // label4 + // + this.label4.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.label4.Location = new System.Drawing.Point(30, 9); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(176, 44); + this.label4.TabIndex = 8; + this.label4.Text = "Add a new server to the service. Note: Requires a Global restart to run new serve" + + "r!"; + // + // AddNewServerForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(238, 226); + this.Controls.Add(this.label4); + this.Controls.Add(this.saveBtn); + this.Controls.Add(this.editPropsBtn); + this.Controls.Add(this.label3); + this.Controls.Add(this.ipV6Box); + this.Controls.Add(this.label2); + this.Controls.Add(this.ipV4Box); + this.Controls.Add(this.label1); + this.Controls.Add(this.srvNameBox); + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "AddNewServerForm"; + this.ShowInTaskbar = false; + this.Text = "AddNewServerForm"; + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.TextBox srvNameBox; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.TextBox ipV4Box; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.TextBox ipV6Box; + private System.Windows.Forms.Button editPropsBtn; + private System.Windows.Forms.Button saveBtn; + private System.Windows.Forms.Label label4; + } +} \ No newline at end of file diff --git a/BedrockService.backup/Client/Forms/AddNewServerForm.cs b/BedrockService.backup/Client/Forms/AddNewServerForm.cs new file mode 100644 index 00000000..9b328bac --- /dev/null +++ b/BedrockService.backup/Client/Forms/AddNewServerForm.cs @@ -0,0 +1,69 @@ +using BedrockService.Client.Management; +using BedrockService.Shared.Classes; +using BedrockService.Shared.Interfaces; +using System.Collections.Generic; +using System.Linq; +using System.Windows.Forms; + +namespace BedrockService.Client.Forms +{ + public partial class AddNewServerForm : Form + { + public List DefaultProps = new List(); + private readonly List serverConfigurations; + private readonly IClientSideServiceConfiguration serviceConfiguration; + public AddNewServerForm(IClientSideServiceConfiguration serviceConfiguration, List serverConfigurations) + { + this.serviceConfiguration = serviceConfiguration; + this.serverConfigurations = serverConfigurations; + InitializeComponent(); + IServerConfiguration server = new ServerInfo(null, FormManager.MainWindow.connectedHost.GetProp("ServersPath").ToString()); + server.InitializeDefaults(); + DefaultProps = server.GetAllProps(); + } + + private void editPropsBtn_Click(object sender, System.EventArgs e) + { + PropEditorForm editSrvDialog = new PropEditorForm(); + if (srvNameBox.TextLength > 0) + DefaultProps.First(prop => prop.KeyName == "server-name").Value = srvNameBox.Text; + if (ipV4Box.TextLength > 0) + DefaultProps.First(prop => prop.KeyName == "server-port").Value = ipV4Box.Text; + if (ipV6Box.TextLength > 0) + DefaultProps.First(prop => prop.KeyName == "server-portv6").Value = ipV6Box.Text; + editSrvDialog.PopulateBoxes(DefaultProps); + if (editSrvDialog.ShowDialog() == DialogResult.OK) + { + DefaultProps = editSrvDialog.workingProps; + editSrvDialog.Close(); + editSrvDialog.Dispose(); + } + } + + private void saveBtn_Click(object sender, System.EventArgs e) + { + List usedPorts = new List(); + usedPorts.Add(serviceConfiguration.GetPort()); + foreach (IServerConfiguration serverConfiguration in serverConfigurations) + { + usedPorts.Add(serverConfiguration.GetProp("server-port").ToString()); + usedPorts.Add(serverConfiguration.GetProp("server-portv6").ToString()); + } + foreach(string port in usedPorts) + { + if(ipV4Box.Text == port || ipV6Box.Text == port) + { + MessageBox.Show($"You have selected port {port} to use, but this port is already used. Please select another port!"); + return; + } + } + if (srvNameBox.TextLength > 0) + DefaultProps.First(prop => prop.KeyName == "server-name").Value = srvNameBox.Text; + if (ipV4Box.TextLength > 0) + DefaultProps.First(prop => prop.KeyName == "server-port").Value = ipV4Box.Text; + if (ipV6Box.TextLength > 0) + DefaultProps.First(prop => prop.KeyName == "server-portv6").Value = ipV6Box.Text; + DialogResult = DialogResult.OK; + } + } +} diff --git a/BedrockService.backup/Client/Forms/AddNewServerForm.resx b/BedrockService.backup/Client/Forms/AddNewServerForm.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/BedrockService.backup/Client/Forms/AddNewServerForm.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/BedrockService.backup/Client/Forms/ClientConfigForm.Designer.cs b/BedrockService.backup/Client/Forms/ClientConfigForm.Designer.cs new file mode 100644 index 00000000..bf9ac45d --- /dev/null +++ b/BedrockService.backup/Client/Forms/ClientConfigForm.Designer.cs @@ -0,0 +1,133 @@ + +namespace BedrockService.Client.Forms +{ + partial class ClientConfigForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.serverGridView = new System.Windows.Forms.DataGridView(); + this.HostName = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.HostAddress = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.Port = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.nbtPathLabel = new System.Windows.Forms.Label(); + this.nbtButton = new System.Windows.Forms.Button(); + this.saveBtn = new System.Windows.Forms.Button(); + ((System.ComponentModel.ISupportInitialize)(this.serverGridView)).BeginInit(); + this.SuspendLayout(); + // + // serverGridView + // + this.serverGridView.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; + this.serverGridView.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] { + this.HostName, + this.HostAddress, + this.Port}); + this.serverGridView.Location = new System.Drawing.Point(12, 30); + this.serverGridView.Name = "serverGridView"; + this.serverGridView.RowHeadersWidth = 62; + this.serverGridView.RowTemplate.Height = 28; + this.serverGridView.Size = new System.Drawing.Size(1154, 381); + this.serverGridView.TabIndex = 0; + // + // HostName + // + this.HostName.HeaderText = "Host Name"; + this.HostName.MinimumWidth = 25; + this.HostName.Name = "HostName"; + this.HostName.Width = 250; + // + // HostAddress + // + this.HostAddress.HeaderText = "IP Address"; + this.HostAddress.MinimumWidth = 8; + this.HostAddress.Name = "HostAddress"; + this.HostAddress.Width = 250; + // + // Port + // + this.Port.HeaderText = "IP Port"; + this.Port.MinimumWidth = 8; + this.Port.Name = "Port"; + this.Port.Width = 200; + // + // nbtPathLabel + // + this.nbtPathLabel.AutoSize = true; + this.nbtPathLabel.Location = new System.Drawing.Point(12, 437); + this.nbtPathLabel.Name = "nbtPathLabel"; + this.nbtPathLabel.Size = new System.Drawing.Size(130, 20); + this.nbtPathLabel.TabIndex = 1; + this.nbtPathLabel.Text = "NBT Studio path:"; + // + // nbtButton + // + this.nbtButton.Location = new System.Drawing.Point(16, 473); + this.nbtButton.Name = "nbtButton"; + this.nbtButton.Size = new System.Drawing.Size(209, 37); + this.nbtButton.TabIndex = 2; + this.nbtButton.Text = "Set NBT Studio path"; + this.nbtButton.UseVisualStyleBackColor = true; + this.nbtButton.Click += new System.EventHandler(this.nbtButton_Click); + // + // saveBtn + // + this.saveBtn.Location = new System.Drawing.Point(957, 473); + this.saveBtn.Name = "saveBtn"; + this.saveBtn.Size = new System.Drawing.Size(209, 37); + this.saveBtn.TabIndex = 3; + this.saveBtn.Text = "Save settings"; + this.saveBtn.UseVisualStyleBackColor = true; + this.saveBtn.Click += new System.EventHandler(this.saveBtn_Click); + // + // ClientConfigForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 20F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(1178, 544); + this.Controls.Add(this.saveBtn); + this.Controls.Add(this.nbtButton); + this.Controls.Add(this.nbtPathLabel); + this.Controls.Add(this.serverGridView); + this.Name = "ClientConfigForm"; + this.Text = "ClientConfigForm"; + ((System.ComponentModel.ISupportInitialize)(this.serverGridView)).EndInit(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.DataGridView serverGridView; + private System.Windows.Forms.DataGridViewTextBoxColumn HostName; + private System.Windows.Forms.DataGridViewTextBoxColumn HostAddress; + private System.Windows.Forms.DataGridViewTextBoxColumn Port; + private System.Windows.Forms.Label nbtPathLabel; + private System.Windows.Forms.Button nbtButton; + private System.Windows.Forms.Button saveBtn; + } +} \ No newline at end of file diff --git a/BedrockService.backup/Client/Forms/ClientConfigForm.cs b/BedrockService.backup/Client/Forms/ClientConfigForm.cs new file mode 100644 index 00000000..4d4c88b2 --- /dev/null +++ b/BedrockService.backup/Client/Forms/ClientConfigForm.cs @@ -0,0 +1,73 @@ +using BedrockService.Client.Management; +using BedrockService.Shared.Classes; +using BedrockService.Shared.Interfaces; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace BedrockService.Client.Forms +{ + public partial class ClientConfigForm : Form + { + private readonly List _clientConfigs; + private readonly ConfigManager _configManager; + public ClientConfigForm(ConfigManager configManager) + { + InitializeComponent(); + _configManager = configManager; + _clientConfigs = _configManager.HostConnectList; + if (!string.IsNullOrEmpty(_configManager.NBTStudioPath)) + { + nbtPathLabel.Text = $"NBT Studio path: {_configManager.NBTStudioPath}"; + } + foreach (IClientSideServiceConfiguration config in _clientConfigs) + { + serverGridView.Rows.Add(new string[3] { config.GetHostName(), config.GetAddress(), config.GetPort() }); + } + } + + public void SimulateTests() + { + nbtButton.PerformClick(); + } + + private void nbtButton_Click(object sender, EventArgs e) + { + using(OpenFileDialog fileDialog = new OpenFileDialog()) + { + fileDialog.Filter = "EXE Files|*.exe"; + fileDialog.FileName = "NbtStudio.exe"; + fileDialog.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Desktop); + if(fileDialog.ShowDialog() == DialogResult.OK) + { + _configManager.NBTStudioPath = fileDialog.FileName; + nbtPathLabel.Text = $"NBT Studio path: {_configManager.NBTStudioPath}"; + } + } + } + + private void saveBtn_Click(object sender, EventArgs e) + { + List newConfigs = new List(); + foreach(DataGridViewRow row in serverGridView.Rows) + { + if (!string.IsNullOrEmpty((string)row.Cells[0].Value)) + { + newConfigs.Add(new ClientSideServiceConfiguration((string)row.Cells[0].Value, (string)row.Cells[1].Value, (string)row.Cells[2].Value)); + } + } + _configManager.HostConnectList = newConfigs; + + _configManager.SaveConfigFile(); + DialogResult = DialogResult.OK; + Close(); + } + } +} diff --git a/BedrockService.backup/Client/Forms/ClientConfigForm.resx b/BedrockService.backup/Client/Forms/ClientConfigForm.resx new file mode 100644 index 00000000..2d8f1f02 --- /dev/null +++ b/BedrockService.backup/Client/Forms/ClientConfigForm.resx @@ -0,0 +1,138 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + True + + + True + + + True + + + True + + + True + + \ No newline at end of file diff --git a/BedrockService.backup/Client/Forms/MainWindow.Designer.cs b/BedrockService.backup/Client/Forms/MainWindow.Designer.cs new file mode 100644 index 00000000..7b72ed20 --- /dev/null +++ b/BedrockService.backup/Client/Forms/MainWindow.Designer.cs @@ -0,0 +1,424 @@ +namespace BedrockService.Client.Forms +{ + partial class MainWindow + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.Connect = new System.Windows.Forms.Button(); + this.HostInfoLabel = new System.Windows.Forms.Label(); + this.HostListBox = new System.Windows.Forms.ComboBox(); + this.LogBox = new System.Windows.Forms.TextBox(); + this.ServerSelectBox = new System.Windows.Forms.ListBox(); + this.Disconn = new System.Windows.Forms.Button(); + this.EditGlobals = new System.Windows.Forms.Button(); + this.removeSrvBtn = new System.Windows.Forms.Button(); + this.ChkUpdates = new System.Windows.Forms.Button(); + this.GlobBackup = new System.Windows.Forms.Button(); + this.cmdTextBox = new System.Windows.Forms.TextBox(); + this.SendCmd = new System.Windows.Forms.Button(); + this.EditCfg = new System.Windows.Forms.Button(); + this.PlayerManagerBtn = new System.Windows.Forms.Button(); + this.EditStCmd = new System.Windows.Forms.Button(); + this.ManPacks = new System.Windows.Forms.Button(); + this.SingBackup = new System.Windows.Forms.Button(); + this.RestartSrv = new System.Windows.Forms.Button(); + this.BackupManagerBtn = new System.Windows.Forms.Button(); + this.SvcLog = new System.Windows.Forms.CheckBox(); + this.ServerInfoBox = new System.Windows.Forms.TextBox(); + this.newSrvBtn = new System.Windows.Forms.Button(); + this.scrollLockChkBox = new System.Windows.Forms.CheckBox(); + this.nbtStudioBtn = new System.Windows.Forms.Button(); + this.clientConfigBtn = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // Connect + // + this.Connect.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.Connect.Location = new System.Drawing.Point(622, 57); + this.Connect.Name = "Connect"; + this.Connect.Size = new System.Drawing.Size(170, 25); + this.Connect.TabIndex = 0; + this.Connect.Text = "Connect"; + this.Connect.UseVisualStyleBackColor = true; + this.Connect.Click += new System.EventHandler(this.Connect_Click); + // + // HostInfoLabel + // + this.HostInfoLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.HostInfoLabel.AutoSize = true; + this.HostInfoLabel.Location = new System.Drawing.Point(620, 12); + this.HostInfoLabel.Name = "HostInfoLabel"; + this.HostInfoLabel.Size = new System.Drawing.Size(87, 13); + this.HostInfoLabel.TabIndex = 1; + this.HostInfoLabel.Text = "HostConnectInfo"; + // + // HostListBox + // + this.HostListBox.AllowDrop = true; + this.HostListBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.HostListBox.FormattingEnabled = true; + this.HostListBox.Location = new System.Drawing.Point(622, 28); + this.HostListBox.Name = "HostListBox"; + this.HostListBox.Size = new System.Drawing.Size(355, 21); + this.HostListBox.TabIndex = 2; + this.HostListBox.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.HostListBox_KeyPress); + // + // LogBox + // + this.LogBox.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.LogBox.Location = new System.Drawing.Point(12, 28); + this.LogBox.Multiline = true; + this.LogBox.Name = "LogBox"; + this.LogBox.ReadOnly = true; + this.LogBox.ScrollBars = System.Windows.Forms.ScrollBars.Both; + this.LogBox.Size = new System.Drawing.Size(580, 361); + this.LogBox.TabIndex = 3; + this.LogBox.WordWrap = false; + // + // ServerSelectBox + // + this.ServerSelectBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Right))); + this.ServerSelectBox.FormattingEnabled = true; + this.ServerSelectBox.Location = new System.Drawing.Point(808, 96); + this.ServerSelectBox.Name = "ServerSelectBox"; + this.ServerSelectBox.Size = new System.Drawing.Size(170, 95); + this.ServerSelectBox.TabIndex = 4; + this.ServerSelectBox.SelectedIndexChanged += new System.EventHandler(this.ServerSelectBox_SelectedIndexChanged); + // + // Disconn + // + this.Disconn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.Disconn.Enabled = false; + this.Disconn.Location = new System.Drawing.Point(808, 57); + this.Disconn.Name = "Disconn"; + this.Disconn.Size = new System.Drawing.Size(170, 25); + this.Disconn.TabIndex = 5; + this.Disconn.Text = "Disconnect"; + this.Disconn.UseVisualStyleBackColor = true; + this.Disconn.Click += new System.EventHandler(this.Disconn_Click); + // + // EditGlobals + // + this.EditGlobals.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.EditGlobals.Enabled = false; + this.EditGlobals.Location = new System.Drawing.Point(808, 266); + this.EditGlobals.Name = "EditGlobals"; + this.EditGlobals.Size = new System.Drawing.Size(170, 23); + this.EditGlobals.TabIndex = 8; + this.EditGlobals.Text = "Edit global service settings"; + this.EditGlobals.UseVisualStyleBackColor = true; + this.EditGlobals.Click += new System.EventHandler(this.EditGlobals_Click); + // + // removeSrvBtn + // + this.removeSrvBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.removeSrvBtn.Enabled = false; + this.removeSrvBtn.Location = new System.Drawing.Point(808, 353); + this.removeSrvBtn.Name = "removeSrvBtn"; + this.removeSrvBtn.Size = new System.Drawing.Size(170, 23); + this.removeSrvBtn.TabIndex = 9; + this.removeSrvBtn.Text = "Remove selected server"; + this.removeSrvBtn.UseVisualStyleBackColor = true; + this.removeSrvBtn.Click += new System.EventHandler(this.RemoveSrvBtn_Click); + // + // ChkUpdates + // + this.ChkUpdates.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.ChkUpdates.Enabled = false; + this.ChkUpdates.Location = new System.Drawing.Point(808, 294); + this.ChkUpdates.Name = "ChkUpdates"; + this.ChkUpdates.Size = new System.Drawing.Size(170, 23); + this.ChkUpdates.TabIndex = 10; + this.ChkUpdates.Text = "Check for updates"; + this.ChkUpdates.UseVisualStyleBackColor = true; + this.ChkUpdates.Click += new System.EventHandler(this.ChkUpdates_Click); + // + // GlobBackup + // + this.GlobBackup.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.GlobBackup.Enabled = false; + this.GlobBackup.Location = new System.Drawing.Point(808, 237); + this.GlobBackup.Name = "GlobBackup"; + this.GlobBackup.Size = new System.Drawing.Size(170, 23); + this.GlobBackup.TabIndex = 11; + this.GlobBackup.Text = "Backup all servers"; + this.GlobBackup.UseVisualStyleBackColor = true; + this.GlobBackup.Click += new System.EventHandler(this.GlobBackup_Click); + // + // cmdTextBox + // + this.cmdTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.cmdTextBox.Enabled = false; + this.cmdTextBox.Location = new System.Drawing.Point(446, 429); + this.cmdTextBox.Name = "cmdTextBox"; + this.cmdTextBox.Size = new System.Drawing.Size(355, 20); + this.cmdTextBox.TabIndex = 12; + this.cmdTextBox.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.cmdTextBox_KeyPress); + // + // SendCmd + // + this.SendCmd.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.SendCmd.Enabled = false; + this.SendCmd.Location = new System.Drawing.Point(808, 426); + this.SendCmd.Name = "SendCmd"; + this.SendCmd.Size = new System.Drawing.Size(170, 23); + this.SendCmd.TabIndex = 13; + this.SendCmd.Text = "Send command to server"; + this.SendCmd.UseVisualStyleBackColor = true; + this.SendCmd.Click += new System.EventHandler(this.SendCmd_Click); + // + // EditCfg + // + this.EditCfg.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.EditCfg.Enabled = false; + this.EditCfg.Location = new System.Drawing.Point(622, 208); + this.EditCfg.Name = "EditCfg"; + this.EditCfg.Size = new System.Drawing.Size(170, 23); + this.EditCfg.TabIndex = 15; + this.EditCfg.Text = "Edit server config"; + this.EditCfg.UseVisualStyleBackColor = true; + this.EditCfg.Click += new System.EventHandler(this.EditCfg_Click); + // + // PlayerManagerBtn + // + this.PlayerManagerBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.PlayerManagerBtn.Enabled = false; + this.PlayerManagerBtn.Location = new System.Drawing.Point(622, 382); + this.PlayerManagerBtn.Name = "PlayerManagerBtn"; + this.PlayerManagerBtn.Size = new System.Drawing.Size(170, 23); + this.PlayerManagerBtn.TabIndex = 16; + this.PlayerManagerBtn.Text = "Player Manager"; + this.PlayerManagerBtn.UseVisualStyleBackColor = true; + this.PlayerManagerBtn.Click += new System.EventHandler(this.PlayerManager_Click); + // + // EditStCmd + // + this.EditStCmd.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.EditStCmd.Enabled = false; + this.EditStCmd.Location = new System.Drawing.Point(622, 237); + this.EditStCmd.Name = "EditStCmd"; + this.EditStCmd.Size = new System.Drawing.Size(170, 23); + this.EditStCmd.TabIndex = 18; + this.EditStCmd.Text = "Edit start commands"; + this.EditStCmd.UseVisualStyleBackColor = true; + this.EditStCmd.Click += new System.EventHandler(this.EditStCmd_Click); + // + // ManPacks + // + this.ManPacks.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.ManPacks.Enabled = false; + this.ManPacks.Location = new System.Drawing.Point(622, 353); + this.ManPacks.Name = "ManPacks"; + this.ManPacks.Size = new System.Drawing.Size(170, 23); + this.ManPacks.TabIndex = 19; + this.ManPacks.Text = "R/B Pack Manager"; + this.ManPacks.UseVisualStyleBackColor = true; + this.ManPacks.Click += new System.EventHandler(this.ManPacks_Click); + // + // SingBackup + // + this.SingBackup.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.SingBackup.Enabled = false; + this.SingBackup.Location = new System.Drawing.Point(622, 266); + this.SingBackup.Name = "SingBackup"; + this.SingBackup.Size = new System.Drawing.Size(170, 23); + this.SingBackup.TabIndex = 20; + this.SingBackup.Text = "Backup selected server"; + this.SingBackup.UseVisualStyleBackColor = true; + this.SingBackup.Click += new System.EventHandler(this.SingBackup_Click); + // + // RestartSrv + // + this.RestartSrv.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.RestartSrv.Enabled = false; + this.RestartSrv.Location = new System.Drawing.Point(622, 294); + this.RestartSrv.Name = "RestartSrv"; + this.RestartSrv.Size = new System.Drawing.Size(170, 23); + this.RestartSrv.TabIndex = 21; + this.RestartSrv.Text = "Restart selected server"; + this.RestartSrv.UseVisualStyleBackColor = true; + this.RestartSrv.Click += new System.EventHandler(this.RestartSrv_Click); + // + // BackupManagerBtn + // + this.BackupManagerBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.BackupManagerBtn.Enabled = false; + this.BackupManagerBtn.Location = new System.Drawing.Point(622, 324); + this.BackupManagerBtn.Name = "BackupManagerBtn"; + this.BackupManagerBtn.Size = new System.Drawing.Size(170, 23); + this.BackupManagerBtn.TabIndex = 22; + this.BackupManagerBtn.Text = "Backup Manager"; + this.BackupManagerBtn.UseVisualStyleBackColor = true; + this.BackupManagerBtn.Click += new System.EventHandler(this.BackupManager_Click); + // + // SvcLog + // + this.SvcLog.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.SvcLog.Enabled = false; + this.SvcLog.Location = new System.Drawing.Point(17, 395); + this.SvcLog.Name = "SvcLog"; + this.SvcLog.RightToLeft = System.Windows.Forms.RightToLeft.No; + this.SvcLog.Size = new System.Drawing.Size(131, 26); + this.SvcLog.TabIndex = 24; + this.SvcLog.Text = "Switch to service logs"; + this.SvcLog.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + this.SvcLog.UseVisualStyleBackColor = true; + // + // ServerInfoBox + // + this.ServerInfoBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Right))); + this.ServerInfoBox.Location = new System.Drawing.Point(622, 96); + this.ServerInfoBox.Multiline = true; + this.ServerInfoBox.Name = "ServerInfoBox"; + this.ServerInfoBox.ReadOnly = true; + this.ServerInfoBox.Size = new System.Drawing.Size(170, 95); + this.ServerInfoBox.TabIndex = 25; + // + // newSrvBtn + // + this.newSrvBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.newSrvBtn.Enabled = false; + this.newSrvBtn.Location = new System.Drawing.Point(808, 208); + this.newSrvBtn.Name = "newSrvBtn"; + this.newSrvBtn.Size = new System.Drawing.Size(170, 23); + this.newSrvBtn.TabIndex = 26; + this.newSrvBtn.Text = "Deploy new server"; + this.newSrvBtn.UseVisualStyleBackColor = true; + this.newSrvBtn.Click += new System.EventHandler(this.newSrvBtn_Click); + // + // scrollLockChkBox + // + this.scrollLockChkBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.scrollLockChkBox.Enabled = false; + this.scrollLockChkBox.Location = new System.Drawing.Point(464, 395); + this.scrollLockChkBox.Name = "scrollLockChkBox"; + this.scrollLockChkBox.RightToLeft = System.Windows.Forms.RightToLeft.No; + this.scrollLockChkBox.Size = new System.Drawing.Size(131, 26); + this.scrollLockChkBox.TabIndex = 27; + this.scrollLockChkBox.Text = "Lock scrollbar to end"; + this.scrollLockChkBox.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + this.scrollLockChkBox.UseVisualStyleBackColor = true; + this.scrollLockChkBox.CheckedChanged += new System.EventHandler(this.scrollLockChkBox_CheckedChanged); + // + // nbtStudioBtn + // + this.nbtStudioBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.nbtStudioBtn.Enabled = false; + this.nbtStudioBtn.Location = new System.Drawing.Point(808, 324); + this.nbtStudioBtn.Name = "nbtStudioBtn"; + this.nbtStudioBtn.Size = new System.Drawing.Size(170, 23); + this.nbtStudioBtn.TabIndex = 28; + this.nbtStudioBtn.Text = "Edit world via NBTStudio"; + this.nbtStudioBtn.UseVisualStyleBackColor = true; + this.nbtStudioBtn.Click += new System.EventHandler(this.nbtStudioBtn_Click); + // + // clientConfigBtn + // + this.clientConfigBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.clientConfigBtn.Location = new System.Drawing.Point(808, 382); + this.clientConfigBtn.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); + this.clientConfigBtn.Name = "clientConfigBtn"; + this.clientConfigBtn.Size = new System.Drawing.Size(170, 23); + this.clientConfigBtn.TabIndex = 29; + this.clientConfigBtn.Text = "Edit client config"; + this.clientConfigBtn.UseVisualStyleBackColor = true; + this.clientConfigBtn.Click += new System.EventHandler(this.clientConfigBtn_Click); + // + // MainWindow + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(989, 467); + this.Controls.Add(this.clientConfigBtn); + this.Controls.Add(this.nbtStudioBtn); + this.Controls.Add(this.scrollLockChkBox); + this.Controls.Add(this.newSrvBtn); + this.Controls.Add(this.ServerInfoBox); + this.Controls.Add(this.SvcLog); + this.Controls.Add(this.BackupManagerBtn); + this.Controls.Add(this.RestartSrv); + this.Controls.Add(this.SingBackup); + this.Controls.Add(this.ManPacks); + this.Controls.Add(this.EditStCmd); + this.Controls.Add(this.PlayerManagerBtn); + this.Controls.Add(this.EditCfg); + this.Controls.Add(this.SendCmd); + this.Controls.Add(this.cmdTextBox); + this.Controls.Add(this.GlobBackup); + this.Controls.Add(this.ChkUpdates); + this.Controls.Add(this.removeSrvBtn); + this.Controls.Add(this.EditGlobals); + this.Controls.Add(this.Disconn); + this.Controls.Add(this.ServerSelectBox); + this.Controls.Add(this.LogBox); + this.Controls.Add(this.HostListBox); + this.Controls.Add(this.HostInfoLabel); + this.Controls.Add(this.Connect); + this.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); + this.Name = "MainWindow"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + this.Text = "Bedrock Service Management"; + this.Load += new System.EventHandler(this.MainWindow_Load); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Button Connect; + private System.Windows.Forms.Label HostInfoLabel; + public System.Windows.Forms.TextBox LogBox; + private System.Windows.Forms.ListBox ServerSelectBox; + private System.Windows.Forms.Button Disconn; + private System.Windows.Forms.Button EditGlobals; + private System.Windows.Forms.Button removeSrvBtn; + private System.Windows.Forms.Button ChkUpdates; + private System.Windows.Forms.Button GlobBackup; + private System.Windows.Forms.TextBox cmdTextBox; + private System.Windows.Forms.Button SendCmd; + private System.Windows.Forms.Button EditCfg; + private System.Windows.Forms.Button PlayerManagerBtn; + private System.Windows.Forms.Button EditStCmd; + private System.Windows.Forms.Button ManPacks; + private System.Windows.Forms.Button SingBackup; + private System.Windows.Forms.Button RestartSrv; + private System.Windows.Forms.Button BackupManagerBtn; + private System.Windows.Forms.CheckBox SvcLog; + private System.Windows.Forms.TextBox ServerInfoBox; + public System.Windows.Forms.ComboBox HostListBox; + private System.Windows.Forms.Button newSrvBtn; + private System.Windows.Forms.CheckBox scrollLockChkBox; + private System.Windows.Forms.Button nbtStudioBtn; + private System.Windows.Forms.Button clientConfigBtn; + } +} + diff --git a/BedrockService.backup/Client/Forms/MainWindow.cs b/BedrockService.backup/Client/Forms/MainWindow.cs new file mode 100644 index 00000000..2420abe2 --- /dev/null +++ b/BedrockService.backup/Client/Forms/MainWindow.cs @@ -0,0 +1,654 @@ +using BedrockService.Client.Management; +using BedrockService.Shared.Classes; +using BedrockService.Shared.Interfaces; +using Newtonsoft.Json; +using System; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Text; +using System.Timers; +using System.Threading; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace BedrockService.Client.Forms +{ + public partial class MainWindow : Form + { + public IServiceConfiguration connectedHost; + public IServerConfiguration selectedServer; + public IClientSideServiceConfiguration clientSideServiceConfiguration; + public bool ShowsSvcLog = false; + public bool ServerBusy = false; + private PropEditorForm _editDialog; + private int _connectTimeout; + private bool _followTail = false; + private const int _connectTimeoutLimit = 3; + private readonly ILogger _logger; + private readonly IProcessInfo _processInfo; + private readonly System.Timers.Timer _connectTimer = new System.Timers.Timer(100.0); + private readonly LogManager _logManager; + private readonly ConfigManager _configManager; + public MainWindow(IProcessInfo processInfo, ILogger logger) + { + _processInfo = processInfo; + _logger = logger; + _logManager = new LogManager(_logger); + _configManager = new ConfigManager(_logger); + InitializeComponent(); + InitForm(); + SvcLog.CheckedChanged += SvcLog_CheckedChanged; + _connectTimer.Elapsed += ConnectTimer_Elapsed; + } + + private void ConnectTimer_Elapsed(object sender, ElapsedEventArgs e) + { + if (_connectTimer.Enabled && !FormManager.TCPClient.Connected) + { + if (_connectTimer.Interval == 100.0) + _connectTimer.Interval = 5000.0; + Invoke((MethodInvoker)delegate { FormManager.TCPClient.ConnectHost(_configManager.HostConnectList.FirstOrDefault(host => host.GetHostName() == (string)HostListBox.SelectedItem)); }); + if (connectedHost != null && FormManager.TCPClient.Connected) + { + ServerBusy = false; + Invoke((MethodInvoker)delegate { ComponentEnableManager(); }); + _connectTimer.Enabled = false; + _connectTimer.Stop(); + _connectTimer.Close(); + return; + } + _connectTimeout++; + if (_connectTimeout >= _connectTimeoutLimit) + { + Invoke((MethodInvoker)delegate + { + RefreshServerContents(); + HostInfoLabel.Text = $"Failed to connect to host!"; + Connect.Enabled = true; + ComponentEnableManager(); + _connectTimer.Enabled = false; + _connectTimer.Stop(); + _connectTimer.Close(); + return; + }); + } + } + } + + #region Win32 API +#pragma warning disable 649 +#pragma warning disable 169 + + [DllImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + static extern bool GetScrollInfo(IntPtr hWnd, int scrollDirection, ref ScrollInfo si); //Thanks goes out to stever@GitHub for this! + + [DllImport("user32.dll")] + static extern int SetScrollInfo(IntPtr hWnd, int scrollDirection, [In] ref ScrollInfo si, bool redraw); + + [DllImport("user32.dll")] + static extern IntPtr SendMessage(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam); + + struct ScrollInfo + { + public uint Size; + public uint Mask; + public int Min; + public int Max; + public uint Page; + public int Pos; + public int TrackPos; + } + + enum ScrollInfoMask + { + Range = 0x1, + Page = 0x2, + Pos = 0x4, + DisableEndScroll = 0x8, + TrackPos = 0x10, + All = Range + Page + Pos + TrackPos + } + + enum ScrollBarDirection + { + Horizontal = 0, + Vertical = 1, + Ctl = 2, + Both = 3 + } + + new const int VerticalScroll = 277; + const int LineUp = 0; + const int LineDown = 1; + const int ThumbPosition = 4; + const int Thumbtrack = 5; + const int ScrollTop = 6; + const int ScrolBottom = 7; + const int EndScroll = 8; + + const int SetRedraw = 0x000B; + const int User = 0x400; + const int GetEventMask = (User + 59); + const int SetEventMask = (User + 69); + +#pragma warning restore 649 +#pragma warning restore 169 + #endregion + + + [STAThread] + public static void Main() + { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.ApplicationExit += OnExit; + Application.Run(FormManager.MainWindow); + } + + public void RefreshServerContents() + { + Invoke((MethodInvoker)delegate + { + Refresh(); + return; + }); + } + + public override void Refresh() + { + HostInfoLabel.Text = $"Connected to host:"; + ServerSelectBox.Items.Clear(); + if(connectedHost != null) + { + _logManager.InitLogThread(connectedHost); + foreach (ServerInfo server in connectedHost.GetServerList()) + ServerSelectBox.Items.Add(server.ServerName); + if(ServerSelectBox.Items.Count > 0) + { + ServerSelectBox.SelectedIndex = 0; + selectedServer = connectedHost.GetServerInfoByName((string)ServerSelectBox.SelectedItem); + } + } + ServerSelectBox.Refresh(); + base.Refresh(); + } + + public void InitForm() + { + _configManager.LoadConfigs(); + HostListBox.Items.Clear(); + foreach (IClientSideServiceConfiguration host in _configManager.HostConnectList) + { + HostListBox.Items.Add(host.GetHostName()); + } + if(HostListBox.Items.Count > 0) + { + HostListBox.SelectedIndex = 0; + } + HostListBox.Refresh(); + FormClosing += MainWindow_FormClosing; + } + + public void HeartbeatFailDisconnect() + { + Disconn_Click(null, null); + try + { + HostInfoLabel.Invoke((MethodInvoker)delegate { HostInfoLabel.Text = "Lost connection to host!"; }); + ServerInfoBox.Invoke((MethodInvoker)delegate { ServerInfoBox.Text = "Lost connection to host!"; }); + ServerBusy = false; + ComponentEnableManager(); + } + catch (InvalidOperationException) { } + + selectedServer = null; + connectedHost = null; + } + + public void UpdateLogBox(string contents) + { + int curPos = HorizontalScrollPosition; + LogBox.Text = contents; + HorizontalScrollPosition = curPos; + if (_followTail) + ScrollToEnd(); + } + + private static void OnExit(object sender, EventArgs e) + { + FormManager.TCPClient.Dispose(); + } + + private void SvcLog_CheckedChanged(object sender, EventArgs e) + { + ShowsSvcLog = SvcLog.Checked; + } + + private void MainWindow_FormClosing(object sender, FormClosingEventArgs e) + { + _logger.AppendLine("Stopping log thread..."); + if (_logManager.StopLogThread()) + { + _logger.AppendLine("Sending disconnect msg..."); + FormManager.TCPClient.SendData(NetworkMessageSource.Client, NetworkMessageDestination.Service, NetworkMessageTypes.Disconnect); + _logger.AppendLine("Closing connection..."); + FormManager.TCPClient.CloseConnection(); + selectedServer = null; + connectedHost = null; + } + } + + private void Connect_Click(object sender, EventArgs e) + { + HostInfoLabel.Text = $"Connecting to host {(string)HostListBox.SelectedItem}..."; + Connect.Enabled = false; + _connectTimeout = 0; + _connectTimer.Interval = 100.0; + _connectTimer.Start(); + } + + private void ServerSelectBox_SelectedIndexChanged(object sender, EventArgs e) + { + if (connectedHost != null) + { + foreach (ServerInfo server in connectedHost.GetServerList()) + { + if (ServerSelectBox.SelectedItem != null && ServerSelectBox.SelectedItem.ToString() == server.GetServerName()) + { + selectedServer = server; + ServerInfoBox.Text = server.GetServerName(); + ComponentEnableManager(); + } + } + } + } + + private void SingBackup_Click(object sender, EventArgs e) + { + FormManager.TCPClient.SendData(NetworkMessageSource.Client, NetworkMessageDestination.Server, connectedHost.GetServerIndex(selectedServer), NetworkMessageTypes.Backup); + DisableUI(); + } + + private void EditCfg_Click(object sender, EventArgs e) + { + _editDialog = new PropEditorForm(); + _editDialog.PopulateBoxes(selectedServer.GetAllProps()); + if (_editDialog.ShowDialog() == DialogResult.OK) + { + JsonSerializerSettings settings = new JsonSerializerSettings() + { + TypeNameHandling = TypeNameHandling.All + }; + byte[] serializeToBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(_editDialog.workingProps, Formatting.Indented, settings)); + FormManager.TCPClient.SendData(serializeToBytes, NetworkMessageSource.Client, NetworkMessageDestination.Server, connectedHost.GetServerIndex(selectedServer), NetworkMessageTypes.PropUpdate); + selectedServer.SetAllProps(_editDialog.workingProps); + _editDialog.Close(); + _editDialog.Dispose(); + RestartSrv_Click(null, null); + } + } + + private void RestartSrv_Click(object sender, EventArgs e) + { + FormManager.TCPClient.SendData(NetworkMessageSource.Client, NetworkMessageDestination.Server, connectedHost.GetServerIndex(selectedServer), NetworkMessageTypes.Restart); + DisableUI(); + } + + private void EditGlobals_Click(object sender, EventArgs e) + { + _editDialog = new PropEditorForm(); + _editDialog.PopulateBoxes(connectedHost.GetAllProps()); + if (_editDialog.ShowDialog() == DialogResult.OK) + { + JsonSerializerSettings settings = new JsonSerializerSettings() + { + TypeNameHandling = TypeNameHandling.All + }; + byte[] serializeToBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(_editDialog.workingProps, Formatting.Indented, settings)); + FormManager.TCPClient.SendData(serializeToBytes, NetworkMessageSource.Client, NetworkMessageDestination.Service, NetworkMessageTypes.PropUpdate); + connectedHost.SetAllProps(_editDialog.workingProps); + _editDialog.Close(); + _editDialog.Dispose(); + RestartSrv_Click(null, null); + } + } + + private void GlobBackup_Click(object sender, EventArgs e) + { + FormManager.TCPClient.SendData(NetworkMessageSource.Client, NetworkMessageDestination.Service, NetworkMessageTypes.BackupAll); + DisableUI(); + } + + private void newSrvBtn_Click(object sender, EventArgs e) + { + if(clientSideServiceConfiguration == null) + { + clientSideServiceConfiguration = _configManager.HostConnectList.First(host => host.GetHostName() == HostListBox.Text); + } + AddNewServerForm newServerForm = new AddNewServerForm(clientSideServiceConfiguration, connectedHost.GetServerList()); + if (newServerForm.ShowDialog() == DialogResult.OK) + { + JsonSerializerSettings settings = new JsonSerializerSettings() + { + TypeNameHandling = TypeNameHandling.All + }; + byte[] serializeToBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(newServerForm.DefaultProps, Formatting.Indented, settings)); + FormManager.TCPClient.SendData(serializeToBytes, NetworkMessageSource.Client, NetworkMessageDestination.Service, NetworkMessageTypes.AddNewServer); + newServerForm.Close(); + newServerForm.Dispose(); + } + } + + private void RemoveSrvBtn_Click(object sender, EventArgs e) + { + FormManager.TCPClient.SendData(NetworkMessageSource.Client, NetworkMessageDestination.Service, connectedHost.GetServerIndex(selectedServer), NetworkMessageTypes.RemoveServer, NetworkMessageFlags.RemoveAll); + DisableUI(); + } + + private void PlayerManager_Click(object sender, EventArgs e) + { + FormManager.TCPClient.SendData(NetworkMessageSource.Client, NetworkMessageDestination.Server, connectedHost.GetServerIndex(selectedServer), NetworkMessageTypes.PlayersRequest); + WaitForServerData().Wait(); + FormManager.TCPClient.PlayerInfoArrived = false; + PlayerManagerForm form = new PlayerManagerForm(selectedServer); + form.Show(); + } + + private void Disconn_Click(object sender, EventArgs e) + { + if (_logManager.StopLogThread()) + { + try + { + if (FormManager.TCPClient.Connected) + { + FormManager.TCPClient.SendData(NetworkMessageSource.Client, NetworkMessageDestination.Service, NetworkMessageTypes.Disconnect); + Thread.Sleep(500); + FormManager.TCPClient.CloseConnection(); + } + selectedServer = null; + connectedHost = null; + LogBox.Invoke((MethodInvoker)delegate { LogBox.Text = ""; }); + FormManager.MainWindow.Invoke((MethodInvoker)delegate + { + ComponentEnableManager(); + ServerSelectBox.Items.Clear(); + ServerSelectBox.SelectedIndex = -1; + ServerInfoBox.Text = ""; + HostInfoLabel.Text = $"Select a host below:"; + }); + } + catch (Exception) { } + + } + } + + private void SendCmd_Click(object sender, EventArgs e) + { + if (cmdTextBox.Text.Length > 0) + { + byte[] msg = Encoding.UTF8.GetBytes(cmdTextBox.Text); + FormManager.TCPClient.SendData(msg, NetworkMessageSource.Client, NetworkMessageDestination.Server, connectedHost.GetServerIndex(selectedServer), NetworkMessageTypes.Command); + } + cmdTextBox.Text = ""; + } + + private void EditStCmd_Click(object sender, EventArgs e) + { + PropEditorForm editSrvDialog = new PropEditorForm(); + editSrvDialog.PopulateStartCmds(selectedServer.GetStartCommands()); + JsonSerializerSettings settings = new JsonSerializerSettings() + { + TypeNameHandling = TypeNameHandling.All + }; + byte[] serializeToBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(editSrvDialog.startCmds, Formatting.Indented, settings)); + if (editSrvDialog.ShowDialog() == DialogResult.OK) + { + DisableUI(); + FormManager.TCPClient.SendData(serializeToBytes, NetworkMessageSource.Client, NetworkMessageDestination.Service, connectedHost.GetServerIndex(selectedServer), NetworkMessageTypes.StartCmdUpdate); + selectedServer.SetStartCommands(editSrvDialog.startCmds); + editSrvDialog.Close(); + editSrvDialog.Dispose(); + RestartSrv_Click(null, null); + } + } + + private void ChkUpdates_Click(object sender, EventArgs e) + { + FormManager.TCPClient.SendData(NetworkMessageSource.Client, NetworkMessageDestination.Service, NetworkMessageTypes.CheckUpdates); + DisableUI(); + } + + private int HorizontalScrollPosition + { + get + { + ScrollInfo si = new ScrollInfo(); + si.Size = (uint)Marshal.SizeOf(si); + si.Mask = (uint)ScrollInfoMask.All; + GetScrollInfo(LogBox.Handle, (int)ScrollBarDirection.Vertical, ref si); + return si.Pos; + } + set + { + ScrollInfo si = new ScrollInfo(); + si.Size = (uint)Marshal.SizeOf(si); + si.Mask = (uint)ScrollInfoMask.All; + GetScrollInfo(LogBox.Handle, (int)ScrollBarDirection.Vertical, ref si); + si.Pos = value; + SetScrollInfo(LogBox.Handle, (int)ScrollBarDirection.Vertical, ref si, true); + SendMessage(LogBox.Handle, VerticalScroll, new IntPtr(Thumbtrack + 0x10000 * si.Pos), new IntPtr(0)); + } + } + + public void ScrollToEnd() + { + + // Get the current scroll info. + ScrollInfo si = new ScrollInfo(); + si.Size = (uint)Marshal.SizeOf(si); + si.Mask = (uint)ScrollInfoMask.All; + GetScrollInfo(LogBox.Handle, (int)ScrollBarDirection.Vertical, ref si); + + // Set the scroll position to maximum. + si.Pos = si.Max - (int)si.Page; + SetScrollInfo(LogBox.Handle, (int)ScrollBarDirection.Vertical, ref si, true); + SendMessage(LogBox.Handle, VerticalScroll, new IntPtr(Thumbtrack + 0x10000 * si.Pos), new IntPtr(0)); + + _followTail = true; + } + + public void PerformBackupTests() + { + HostListBox.SelectedIndex = 0; + Connect_Click(null, null); + GlobBackup_Click(null, null); + DisableUI(); + ServerSelectBox.SelectedIndex = 0; + FormManager.TCPClient.SendData(NetworkMessageSource.Client, NetworkMessageDestination.Service, connectedHost.GetServerIndex(selectedServer), NetworkMessageTypes.EnumBackups); + DisableUI().Wait(); + FormManager.TCPClient.EnumBackupsArrived = false; + JsonSerializerSettings settings = new JsonSerializerSettings() + { + TypeNameHandling = TypeNameHandling.All + }; + byte[] serializeToBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(new string[] { FormManager.TCPClient.BackupList[0].ToString() }, Formatting.Indented, settings)); + FormManager.TCPClient.SendData(serializeToBytes, NetworkMessageSource.Client, NetworkMessageDestination.Service, FormManager.MainWindow.connectedHost.GetServerIndex(FormManager.MainWindow.selectedServer), NetworkMessageTypes.DelBackups); + } + + private void ComponentEnableManager() + { + Connect.Enabled = connectedHost == null; + Disconn.Enabled = connectedHost != null; + newSrvBtn.Enabled = connectedHost != null && !ServerBusy; + ChkUpdates.Enabled = connectedHost != null && !ServerBusy; + GlobBackup.Enabled = connectedHost != null && !ServerBusy; + EditGlobals.Enabled = connectedHost != null && !ServerBusy; + BackupManagerBtn.Enabled = (connectedHost != null && selectedServer != null && !ServerBusy); + nbtStudioBtn.Enabled = (connectedHost != null && selectedServer != null && !ServerBusy); + ManPacks.Enabled = (connectedHost != null && selectedServer != null && !ServerBusy); + scrollLockChkBox.Enabled = (connectedHost != null && selectedServer != null && !ServerBusy); + removeSrvBtn.Enabled = (connectedHost != null && selectedServer != null && !ServerBusy); + EditCfg.Enabled = (connectedHost != null && selectedServer != null && !ServerBusy); + PlayerManagerBtn.Enabled = (connectedHost != null && selectedServer != null && !ServerBusy); + EditStCmd.Enabled = (connectedHost != null && selectedServer != null && !ServerBusy); + SingBackup.Enabled = (connectedHost != null && selectedServer != null && !ServerBusy); + RestartSrv.Enabled = (connectedHost != null && selectedServer != null && !ServerBusy); + ServerInfoBox.Enabled = (connectedHost != null && selectedServer != null && !ServerBusy); + SendCmd.Enabled = (connectedHost != null && selectedServer != null && !ServerBusy); + cmdTextBox.Enabled = (connectedHost != null && selectedServer != null && !ServerBusy); + SvcLog.Enabled = (connectedHost != null && selectedServer != null && !ServerBusy); + } + + public Task DisableUI() + { + ServerBusy = true; + return Task.Run(() => + { + + Invoke((MethodInvoker)delegate { ComponentEnableManager(); }); + while (ServerBusy) + { + Task.Delay(250); + } + Invoke((MethodInvoker)delegate { ComponentEnableManager(); }); + }); + } + + public Task WaitForServerData() + { + return Task.Run(() => + { + while (!FormManager.TCPClient.EnumBackupsArrived && !FormManager.TCPClient.PlayerInfoArrived && FormManager.TCPClient.RecievedPacks == null) + { + Task.Delay(250); + } + }); + } + + public class ServerConnectException : Exception + { + public ServerConnectException() { } + + public ServerConnectException(string message) + : base(message) + { + + } + + public ServerConnectException(string message, Exception inner) + : base(message, inner) + { + + } + } + + private void scrollLockChkBox_CheckedChanged(object sender, EventArgs e) => _followTail = scrollLockChkBox.Checked; + + private void cmdTextBox_KeyPress(object sender, KeyPressEventArgs e) + { + if (e.KeyChar == (char)Keys.Enter) + { + SendCmd_Click(null, null); + } + } + + private void HostListBox_KeyPress(object sender, KeyPressEventArgs e) + { + if (e.KeyChar == (char)Keys.Enter) + { + Connect_Click(null, null); + } + } + + private void BackupManager_Click(object sender, EventArgs e) + { + using (PropEditorForm editDialog = new PropEditorForm()) + { + editDialog.EnableBackupManager(); + FormManager.TCPClient.SendData(NetworkMessageSource.Client, NetworkMessageDestination.Service, connectedHost.GetServerIndex(selectedServer), NetworkMessageTypes.EnumBackups); + DisableUI(); + WaitForServerData().Wait(); + FormManager.TCPClient.EnumBackupsArrived = false; + editDialog.PopulateBoxes(FormManager.TCPClient.BackupList); + if (editDialog.ShowDialog() == DialogResult.OK) + { + FormManager.TCPClient.SendData(Encoding.UTF8.GetBytes(editDialog.RollbackFolderName), NetworkMessageSource.Client, NetworkMessageDestination.Service, connectedHost.GetServerIndex(selectedServer), NetworkMessageTypes.BackupRollback); + ServerBusy = true; + } + editDialog.Close(); + } + } + + private void ManPacks_Click(object sender, EventArgs e) + { + FormManager.TCPClient.SendData(NetworkMessageSource.Client, NetworkMessageDestination.Server, connectedHost.GetServerIndex(selectedServer), NetworkMessageTypes.PackList); + DisableUI(); + WaitForServerData().Wait(); + using (ManagePacksForms form = new ManagePacksForms(connectedHost.GetServerIndex(selectedServer), _logger, _processInfo)) + { + form.PopulateServerPacks(FormManager.TCPClient.RecievedPacks); + if (form.ShowDialog() == DialogResult.OK) + form.Close(); + } + FormManager.TCPClient.RecievedPacks = null; + } + + private void nbtStudioBtn_Click(object sender, EventArgs e) + { + if (string.IsNullOrEmpty(_configManager.NBTStudioPath)) + using (OpenFileDialog openFile = new OpenFileDialog()) + { + openFile.FileName = "NBTStudio.exe"; + openFile.Title = "Please locate NBT Studio executable..."; + openFile.Filter = "NBTStudio.exe|NBTStudio.exe"; + if (openFile.ShowDialog() == DialogResult.OK) + { + _configManager.NBTStudioPath = openFile.FileName; + _configManager.SaveConfigFile(); + } + } + ServerBusy = true; + FormManager.TCPClient.SendData(NetworkMessageSource.Client, NetworkMessageDestination.Server, connectedHost.GetServerIndex(selectedServer), NetworkMessageTypes.LevelEditRequest); + DisableUI(); + using (Process nbtStudioProcess = new Process()) + { + string tempPath = $@"{Path.GetTempPath()}level.dat"; + nbtStudioProcess.StartInfo = new ProcessStartInfo(_configManager.NBTStudioPath, tempPath); + nbtStudioProcess.Start(); + nbtStudioProcess.WaitForExit(); + FormManager.TCPClient.SendData(File.ReadAllBytes(tempPath), NetworkMessageSource.Client, NetworkMessageDestination.Server, connectedHost.GetServerIndex(selectedServer), NetworkMessageTypes.LevelEditFile); + } + ServerBusy = false; + } + + private void HostListBox_SelectedIndexChanged(object sender, EventArgs e) + { + if(HostListBox.SelectedIndex != -1) + { + clientSideServiceConfiguration = _configManager.HostConnectList.FirstOrDefault(host => host.GetHostName() == (string)HostListBox.SelectedItem); + } + } + + private void MainWindow_Load(object sender, EventArgs e) + { + + } + + private void clientConfigBtn_Click(object sender, EventArgs e) + { + using (ClientConfigForm form = new ClientConfigForm(_configManager)) + { + if(form.ShowDialog() == DialogResult.OK) + { + form.Close(); + InitForm(); + } + } + } + } +} diff --git a/BedrockService.backup/Client/Forms/MainWindow.resx b/BedrockService.backup/Client/Forms/MainWindow.resx new file mode 100644 index 00000000..26b2a12a --- /dev/null +++ b/BedrockService.backup/Client/Forms/MainWindow.resx @@ -0,0 +1,198 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + \ No newline at end of file diff --git a/BedrockService.backup/Client/Forms/ManagePacksForms.Designer.cs b/BedrockService.backup/Client/Forms/ManagePacksForms.Designer.cs new file mode 100644 index 00000000..3e09ec3a --- /dev/null +++ b/BedrockService.backup/Client/Forms/ManagePacksForms.Designer.cs @@ -0,0 +1,215 @@ + +namespace BedrockService.Client.Forms +{ + partial class ManagePacksForms + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.serverListBox = new System.Windows.Forms.ListBox(); + this.parsedPacksListBox = new System.Windows.Forms.ListBox(); + this.sendPacksBtn = new System.Windows.Forms.Button(); + this.sendAllBtn = new System.Windows.Forms.Button(); + this.removePackBtn = new System.Windows.Forms.Button(); + this.removeAllPacksBtn = new System.Windows.Forms.Button(); + this.textBox1 = new System.Windows.Forms.TextBox(); + this.selectedPackIcon = new System.Windows.Forms.PictureBox(); + this.openFileBtn = new System.Windows.Forms.Button(); + this.label1 = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.label3 = new System.Windows.Forms.Label(); + ((System.ComponentModel.ISupportInitialize)(this.selectedPackIcon)).BeginInit(); + this.SuspendLayout(); + // + // serverListBox + // + this.serverListBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left))); + this.serverListBox.FormattingEnabled = true; + this.serverListBox.Location = new System.Drawing.Point(12, 82); + this.serverListBox.Name = "serverListBox"; + this.serverListBox.Size = new System.Drawing.Size(189, 251); + this.serverListBox.TabIndex = 0; + this.serverListBox.Click += new System.EventHandler(this.ListBox_SelectedIndexChanged); + // + // parsedPacksListBox + // + this.parsedPacksListBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Right))); + this.parsedPacksListBox.FormattingEnabled = true; + this.parsedPacksListBox.Location = new System.Drawing.Point(599, 82); + this.parsedPacksListBox.Name = "parsedPacksListBox"; + this.parsedPacksListBox.SelectionMode = System.Windows.Forms.SelectionMode.MultiSimple; + this.parsedPacksListBox.Size = new System.Drawing.Size(189, 251); + this.parsedPacksListBox.TabIndex = 1; + this.parsedPacksListBox.Click += new System.EventHandler(this.ListBox_SelectedIndexChanged); + // + // sendPacksBtn + // + this.sendPacksBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.sendPacksBtn.Location = new System.Drawing.Point(468, 82); + this.sendPacksBtn.Name = "sendPacksBtn"; + this.sendPacksBtn.Size = new System.Drawing.Size(125, 23); + this.sendPacksBtn.TabIndex = 2; + this.sendPacksBtn.Text = "Send selected packs"; + this.sendPacksBtn.UseVisualStyleBackColor = true; + this.sendPacksBtn.Click += new System.EventHandler(this.sendPacksBtn_Click); + // + // sendAllBtn + // + this.sendAllBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.sendAllBtn.Location = new System.Drawing.Point(468, 111); + this.sendAllBtn.Name = "sendAllBtn"; + this.sendAllBtn.Size = new System.Drawing.Size(125, 23); + this.sendAllBtn.TabIndex = 3; + this.sendAllBtn.Text = "Send all packs"; + this.sendAllBtn.UseVisualStyleBackColor = true; + this.sendAllBtn.Click += new System.EventHandler(this.sendAllBtn_Click); + // + // removePackBtn + // + this.removePackBtn.Location = new System.Drawing.Point(207, 82); + this.removePackBtn.Name = "removePackBtn"; + this.removePackBtn.Size = new System.Drawing.Size(119, 23); + this.removePackBtn.TabIndex = 4; + this.removePackBtn.Text = "Remove pack"; + this.removePackBtn.UseVisualStyleBackColor = true; + this.removePackBtn.Click += new System.EventHandler(this.removePackBtn_Click); + // + // removeAllPacksBtn + // + this.removeAllPacksBtn.Location = new System.Drawing.Point(207, 111); + this.removeAllPacksBtn.Name = "removeAllPacksBtn"; + this.removeAllPacksBtn.Size = new System.Drawing.Size(119, 23); + this.removeAllPacksBtn.TabIndex = 5; + this.removeAllPacksBtn.Text = "Remove all packs"; + this.removeAllPacksBtn.UseVisualStyleBackColor = true; + this.removeAllPacksBtn.Click += new System.EventHandler(this.removeAllPacksBtn_Click); + // + // textBox1 + // + this.textBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.textBox1.Location = new System.Drawing.Point(288, 211); + this.textBox1.Multiline = true; + this.textBox1.Name = "textBox1"; + this.textBox1.ReadOnly = true; + this.textBox1.Size = new System.Drawing.Size(219, 122); + this.textBox1.TabIndex = 6; + // + // selectedPackIcon + // + this.selectedPackIcon.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.selectedPackIcon.Location = new System.Drawing.Point(332, 75); + this.selectedPackIcon.Name = "selectedPackIcon"; + this.selectedPackIcon.Size = new System.Drawing.Size(130, 130); + this.selectedPackIcon.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage; + this.selectedPackIcon.TabIndex = 7; + this.selectedPackIcon.TabStop = false; + // + // openFileBtn + // + this.openFileBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.openFileBtn.Location = new System.Drawing.Point(599, 29); + this.openFileBtn.Name = "openFileBtn"; + this.openFileBtn.Size = new System.Drawing.Size(189, 23); + this.openFileBtn.TabIndex = 8; + this.openFileBtn.Text = "Open pack file(s)"; + this.openFileBtn.UseVisualStyleBackColor = true; + this.openFileBtn.Click += new System.EventHandler(this.openFileBtn_Click); + // + // label1 + // + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(12, 66); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(153, 13); + this.label1.TabIndex = 9; + this.label1.Text = "Current packs found on server:"; + // + // label2 + // + this.label2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(596, 66); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(146, 13); + this.label2.TabIndex = 10; + this.label2.Text = "Packs found in archive file(s):"; + // + // label3 + // + this.label3.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(204, 9); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(418, 13); + this.label3.TabIndex = 11; + this.label3.Text = "!! NOTICE !! Send map pack alone, do not send all! Not all packs will parse serve" + + "r-side!"; + // + // ManagePacksForms + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(800, 450); + this.Controls.Add(this.label3); + this.Controls.Add(this.label2); + this.Controls.Add(this.label1); + this.Controls.Add(this.openFileBtn); + this.Controls.Add(this.selectedPackIcon); + this.Controls.Add(this.textBox1); + this.Controls.Add(this.removeAllPacksBtn); + this.Controls.Add(this.removePackBtn); + this.Controls.Add(this.sendAllBtn); + this.Controls.Add(this.sendPacksBtn); + this.Controls.Add(this.parsedPacksListBox); + this.Controls.Add(this.serverListBox); + this.Name = "ManagePacksForms"; + this.Text = "Form1"; + ((System.ComponentModel.ISupportInitialize)(this.selectedPackIcon)).EndInit(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.ListBox serverListBox; + private System.Windows.Forms.ListBox parsedPacksListBox; + private System.Windows.Forms.Button sendPacksBtn; + private System.Windows.Forms.Button sendAllBtn; + private System.Windows.Forms.Button removePackBtn; + private System.Windows.Forms.Button removeAllPacksBtn; + private System.Windows.Forms.TextBox textBox1; + private System.Windows.Forms.PictureBox selectedPackIcon; + private System.Windows.Forms.Button openFileBtn; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Label label3; + } +} \ No newline at end of file diff --git a/BedrockService.backup/Client/Forms/ManagePacksForms.cs b/BedrockService.backup/Client/Forms/ManagePacksForms.cs new file mode 100644 index 00000000..29fbfe9f --- /dev/null +++ b/BedrockService.backup/Client/Forms/ManagePacksForms.cs @@ -0,0 +1,135 @@ +using BedrockService.Client.Management; +using BedrockService.Shared.Classes; +using BedrockService.Shared.Interfaces; +using BedrockService.Shared.PackParser; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Drawing; +using System.IO; +using System.IO.Compression; +using System.Text; +using System.Windows.Forms; + +namespace BedrockService.Client.Forms +{ + public partial class ManagePacksForms : Form + { + private readonly byte ServerIndex = 0x00; + private readonly ILogger Logger; + private readonly IProcessInfo ProcessInfo; + private readonly DirectoryInfo PackExtractDir; + public ManagePacksForms(byte serverIndex, ILogger logger, IProcessInfo processInfo) + { + Logger = logger; + PackExtractDir = new DirectoryInfo($@"{processInfo.GetDirectory()}\Temp"); + ProcessInfo = processInfo; + ServerIndex = serverIndex; + InitializeComponent(); + } + + public void PopulateServerPacks(List packList) + { + foreach (MinecraftPackParser pack in packList) + foreach (MinecraftPackContainer container in pack.FoundPacks) + serverListBox.Items.Add(container); + } + + private void ListBox_SelectedIndexChanged(object sender, EventArgs e) + { + ListBox thisBox = (ListBox)sender; + if (thisBox == serverListBox) + parsedPacksListBox.SelectedIndex = -1; + if (thisBox == parsedPacksListBox) + serverListBox.SelectedIndex = -1; + if (thisBox.SelectedIndex != -1) + { + MinecraftPackContainer selectedPack = (MinecraftPackContainer)thisBox.SelectedItem; + if (selectedPack.IconBytes != null) + using (MemoryStream ms = new MemoryStream(selectedPack.IconBytes)) + { + selectedPackIcon.Image = Image.FromStream(ms); + } + if (selectedPack.JsonManifest != null) + textBox1.Text = $"{selectedPack.JsonManifest.header.name}\r\n{selectedPack.JsonManifest.header.description}\r\n{selectedPack.JsonManifest.header.uuid}\r\n{selectedPack.JsonManifest.header.version[0]}"; + else + textBox1.Text = selectedPack.PackContentLocation.Name; + } + } + + private void removePackBtn_Click(object sender, EventArgs e) + { + if (serverListBox.SelectedIndex != -1) + { + List temp = new List(); + temp.Add((MinecraftPackContainer)serverListBox.SelectedItem); + JsonSerializerSettings settings = new JsonSerializerSettings() + { + TypeNameHandling = TypeNameHandling.All + }; + FormManager.TCPClient.SendData(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(temp, Formatting.Indented, settings)), NetworkMessageSource.Client, NetworkMessageDestination.Server, ServerIndex, NetworkMessageTypes.RemovePack); + serverListBox.Items.Remove(temp[0]); + } + + } + + private void removeAllPacksBtn_Click(object sender, EventArgs e) + { + List temp = new List(); + foreach (object item in serverListBox.Items) + temp.Add((MinecraftPackContainer)item); + JsonSerializerSettings settings = new JsonSerializerSettings() + { + TypeNameHandling = TypeNameHandling.All + }; + FormManager.TCPClient.SendData(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(temp, Formatting.Indented, settings)), NetworkMessageSource.Client, NetworkMessageDestination.Server, ServerIndex, NetworkMessageTypes.RemovePack); + serverListBox.Items.Clear(); + } + + private void sendPacksBtn_Click(object sender, EventArgs e) + { + if (parsedPacksListBox.SelectedIndex != -1) + { + object[] items = new object[parsedPacksListBox.SelectedItems.Count]; + parsedPacksListBox.SelectedItems.CopyTo(items, 0); + SendPacks(items); + } + } + + private void sendAllBtn_Click(object sender, EventArgs e) + { + object[] items = new object[parsedPacksListBox.Items.Count]; + parsedPacksListBox.Items.CopyTo(items, 0); + SendPacks(items); + } + + private void openFileBtn_Click(object sender, EventArgs e) + { + OpenFileDialog openFileDialog = new OpenFileDialog(); + openFileDialog.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); + openFileDialog.Filter = "MC pack file (.MCWORLD, .MCPACK, .MCADDON, .Zip)|*.mcworld;*.mcpack;*.mcaddon;*.zip"; + openFileDialog.Title = "Select pack file(s)"; + openFileDialog.Multiselect = true; + if (openFileDialog.ShowDialog() == DialogResult.OK) + { + MinecraftPackParser parser = new MinecraftPackParser(openFileDialog.FileNames, PackExtractDir, Logger, ProcessInfo); + parsedPacksListBox.Items.Clear(); + foreach (MinecraftPackContainer container in parser.FoundPacks) + parsedPacksListBox.Items.Add(container); + } + } + + private void SendPacks(object[] packList) + { + foreach (MinecraftPackContainer container in packList) + { + Directory.CreateDirectory($@"{PackExtractDir.FullName}\ZipTemp"); + container.PackContentLocation.MoveTo($@"{PackExtractDir.FullName}\ZipTemp\{container.PackContentLocation.Name}"); + container.PackContentLocation = new DirectoryInfo($@"{PackExtractDir.FullName}\ZipTemp\{container.PackContentLocation.Name}"); + } + ZipFile.CreateFromDirectory($@"{PackExtractDir.FullName}\ZipTemp", $@"{PackExtractDir.FullName}\SendZip.zip"); + FormManager.TCPClient.SendData(File.ReadAllBytes($@"{PackExtractDir.FullName}\SendZip.zip"), NetworkMessageSource.Client, NetworkMessageDestination.Server, ServerIndex, NetworkMessageTypes.PackFile); + parsedPacksListBox.Items.Clear(); + } + } +} diff --git a/BedrockService.backup/Client/Forms/ManagePacksForms.resx b/BedrockService.backup/Client/Forms/ManagePacksForms.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/BedrockService.backup/Client/Forms/ManagePacksForms.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/BedrockService.backup/Client/Forms/NewPlayerRegistrationForm.Designer.cs b/BedrockService.backup/Client/Forms/NewPlayerRegistrationForm.Designer.cs new file mode 100644 index 00000000..66d091df --- /dev/null +++ b/BedrockService.backup/Client/Forms/NewPlayerRegistrationForm.Designer.cs @@ -0,0 +1,182 @@ + +namespace BedrockService.Client.Forms +{ + partial class NewPlayerRegistrationForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.label2 = new System.Windows.Forms.Label(); + this.xuidTextBox = new System.Windows.Forms.TextBox(); + this.usernameTextBox = new System.Windows.Forms.TextBox(); + this.label3 = new System.Windows.Forms.Label(); + this.label4 = new System.Windows.Forms.Label(); + this.label5 = new System.Windows.Forms.Label(); + this.permissionComboBox = new System.Windows.Forms.ComboBox(); + this.whitelistedChkBox = new System.Windows.Forms.CheckBox(); + this.ignoreLimitChkBox = new System.Windows.Forms.CheckBox(); + this.label6 = new System.Windows.Forms.Label(); + this.button1 = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // label2 + // + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(70, 33); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(36, 13); + this.label2.TabIndex = 1; + this.label2.Text = "XUID:"; + // + // xuidTextBox + // + this.xuidTextBox.Location = new System.Drawing.Point(111, 30); + this.xuidTextBox.Name = "xuidTextBox"; + this.xuidTextBox.Size = new System.Drawing.Size(185, 20); + this.xuidTextBox.TabIndex = 2; + // + // usernameTextBox + // + this.usernameTextBox.Location = new System.Drawing.Point(111, 56); + this.usernameTextBox.Name = "usernameTextBox"; + this.usernameTextBox.Size = new System.Drawing.Size(185, 20); + this.usernameTextBox.TabIndex = 4; + // + // label3 + // + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(48, 59); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(58, 13); + this.label3.TabIndex = 3; + this.label3.Text = "Username:"; + // + // label4 + // + this.label4.AutoSize = true; + this.label4.Location = new System.Drawing.Point(46, 85); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(60, 13); + this.label4.TabIndex = 5; + this.label4.Text = "Permission:"; + // + // label5 + // + this.label5.AutoSize = true; + this.label5.Location = new System.Drawing.Point(40, 109); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(65, 13); + this.label5.TabIndex = 6; + this.label5.Text = "Whitelisted?"; + // + // permissionComboBox + // + this.permissionComboBox.FormattingEnabled = true; + this.permissionComboBox.Items.AddRange(new object[] { + "visitor", + "member", + "operator"}); + this.permissionComboBox.Location = new System.Drawing.Point(111, 82); + this.permissionComboBox.Name = "permissionComboBox"; + this.permissionComboBox.Size = new System.Drawing.Size(185, 21); + this.permissionComboBox.TabIndex = 7; + // + // whitelistedChkBox + // + this.whitelistedChkBox.AutoSize = true; + this.whitelistedChkBox.Location = new System.Drawing.Point(111, 109); + this.whitelistedChkBox.Name = "whitelistedChkBox"; + this.whitelistedChkBox.Size = new System.Drawing.Size(15, 14); + this.whitelistedChkBox.TabIndex = 8; + this.whitelistedChkBox.UseVisualStyleBackColor = true; + // + // ignoreLimitChkBox + // + this.ignoreLimitChkBox.AutoSize = true; + this.ignoreLimitChkBox.Location = new System.Drawing.Point(111, 132); + this.ignoreLimitChkBox.Name = "ignoreLimitChkBox"; + this.ignoreLimitChkBox.Size = new System.Drawing.Size(15, 14); + this.ignoreLimitChkBox.TabIndex = 9; + this.ignoreLimitChkBox.UseVisualStyleBackColor = true; + // + // label6 + // + this.label6.AutoSize = true; + this.label6.Location = new System.Drawing.Point(5, 132); + this.label6.Name = "label6"; + this.label6.Size = new System.Drawing.Size(101, 13); + this.label6.TabIndex = 10; + this.label6.Text = "Ignore max players?"; + // + // button1 + // + this.button1.Location = new System.Drawing.Point(221, 123); + this.button1.Name = "button1"; + this.button1.Size = new System.Drawing.Size(75, 25); + this.button1.TabIndex = 11; + this.button1.Text = "Save"; + this.button1.UseVisualStyleBackColor = true; + this.button1.Click += new System.EventHandler(this.saveClick); + // + // NewPlayerRegistrationForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(323, 171); + this.Controls.Add(this.button1); + this.Controls.Add(this.label6); + this.Controls.Add(this.ignoreLimitChkBox); + this.Controls.Add(this.whitelistedChkBox); + this.Controls.Add(this.permissionComboBox); + this.Controls.Add(this.label5); + this.Controls.Add(this.label4); + this.Controls.Add(this.usernameTextBox); + this.Controls.Add(this.label3); + this.Controls.Add(this.xuidTextBox); + this.Controls.Add(this.label2); + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "NewPlayerRegistrationForm"; + this.Text = "New Player Registration Form"; + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + private System.Windows.Forms.Label label2; + private System.Windows.Forms.TextBox xuidTextBox; + private System.Windows.Forms.TextBox usernameTextBox; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.Label label5; + private System.Windows.Forms.ComboBox permissionComboBox; + private System.Windows.Forms.CheckBox whitelistedChkBox; + private System.Windows.Forms.CheckBox ignoreLimitChkBox; + private System.Windows.Forms.Label label6; + private System.Windows.Forms.Button button1; + } +} \ No newline at end of file diff --git a/BedrockService.backup/Client/Forms/NewPlayerRegistrationForm.cs b/BedrockService.backup/Client/Forms/NewPlayerRegistrationForm.cs new file mode 100644 index 00000000..2de19efc --- /dev/null +++ b/BedrockService.backup/Client/Forms/NewPlayerRegistrationForm.cs @@ -0,0 +1,26 @@ +using BedrockService.Shared.Classes; +using BedrockService.Shared.Interfaces; +using System; +using System.Windows.Forms; + +namespace BedrockService.Client.Forms +{ + public partial class NewPlayerRegistrationForm : Form + { + public IPlayer PlayerToAdd; + public NewPlayerRegistrationForm() + { + InitializeComponent(); + } + + private void saveClick(object sender, EventArgs e) + { + if (usernameTextBox.TextLength > 0 && xuidTextBox.TextLength == 16) + { + PlayerToAdd = new Player(xuidTextBox.Text, usernameTextBox.Text, DateTime.Now.Ticks.ToString(), "0", "0", whitelistedChkBox.Checked, permissionComboBox.SelectedItem.ToString(), ignoreLimitChkBox.Checked); + DialogResult = DialogResult.OK; + Close(); + } + } + } +} diff --git a/BedrockService.backup/Client/Forms/NewPlayerRegistrationForm.resx b/BedrockService.backup/Client/Forms/NewPlayerRegistrationForm.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/BedrockService.backup/Client/Forms/NewPlayerRegistrationForm.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/BedrockService.backup/Client/Forms/PlayerManagerForm.Designer.cs b/BedrockService.backup/Client/Forms/PlayerManagerForm.Designer.cs new file mode 100644 index 00000000..d548d659 --- /dev/null +++ b/BedrockService.backup/Client/Forms/PlayerManagerForm.Designer.cs @@ -0,0 +1,153 @@ + +namespace BedrockService.Client.Forms +{ + partial class PlayerManagerForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle7 = new System.Windows.Forms.DataGridViewCellStyle(); + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle8 = new System.Windows.Forms.DataGridViewCellStyle(); + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle9 = new System.Windows.Forms.DataGridViewCellStyle(); + this.searchEntryBox = new System.Windows.Forms.TextBox(); + this.saveBtn = new System.Windows.Forms.Button(); + this.label2 = new System.Windows.Forms.Label(); + this.gridView = new System.Windows.Forms.DataGridView(); + this.registerPlayerBtn = new System.Windows.Forms.Button(); + ((System.ComponentModel.ISupportInitialize)(this.gridView)).BeginInit(); + this.SuspendLayout(); + // + // searchEntryBox + // + this.searchEntryBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.searchEntryBox.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.searchEntryBox.Location = new System.Drawing.Point(177, 390); + this.searchEntryBox.Name = "searchEntryBox"; + this.searchEntryBox.Size = new System.Drawing.Size(729, 26); + this.searchEntryBox.TabIndex = 4; + this.searchEntryBox.TextChanged += new System.EventHandler(this.searchEntryBox_TextChanged); + // + // saveBtn + // + this.saveBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.saveBtn.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.saveBtn.Location = new System.Drawing.Point(912, 390); + this.saveBtn.Name = "saveBtn"; + this.saveBtn.Size = new System.Drawing.Size(87, 26); + this.saveBtn.TabIndex = 5; + this.saveBtn.Text = "Save"; + this.saveBtn.UseVisualStyleBackColor = true; + this.saveBtn.Click += new System.EventHandler(this.saveBtn_Click); + // + // label2 + // + this.label2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.label2.AutoSize = true; + this.label2.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.label2.Location = new System.Drawing.Point(127, 393); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(44, 20); + this.label2.TabIndex = 6; + this.label2.Text = "Find:"; + // + // gridView + // + this.gridView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.gridView.AutoSizeColumnsMode = System.Windows.Forms.DataGridViewAutoSizeColumnsMode.AllCells; + this.gridView.AutoSizeRowsMode = System.Windows.Forms.DataGridViewAutoSizeRowsMode.AllCells; + dataGridViewCellStyle7.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft; + dataGridViewCellStyle7.BackColor = System.Drawing.SystemColors.Control; + dataGridViewCellStyle7.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + dataGridViewCellStyle7.ForeColor = System.Drawing.SystemColors.WindowText; + dataGridViewCellStyle7.SelectionBackColor = System.Drawing.SystemColors.Highlight; + dataGridViewCellStyle7.SelectionForeColor = System.Drawing.SystemColors.HighlightText; + dataGridViewCellStyle7.WrapMode = System.Windows.Forms.DataGridViewTriState.True; + this.gridView.ColumnHeadersDefaultCellStyle = dataGridViewCellStyle7; + this.gridView.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; + dataGridViewCellStyle8.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft; + dataGridViewCellStyle8.BackColor = System.Drawing.SystemColors.Window; + dataGridViewCellStyle8.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + dataGridViewCellStyle8.ForeColor = System.Drawing.SystemColors.ControlText; + dataGridViewCellStyle8.SelectionBackColor = System.Drawing.SystemColors.Highlight; + dataGridViewCellStyle8.SelectionForeColor = System.Drawing.SystemColors.HighlightText; + dataGridViewCellStyle8.WrapMode = System.Windows.Forms.DataGridViewTriState.False; + this.gridView.DefaultCellStyle = dataGridViewCellStyle8; + this.gridView.Location = new System.Drawing.Point(16, 27); + this.gridView.Name = "gridView"; + dataGridViewCellStyle9.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft; + dataGridViewCellStyle9.BackColor = System.Drawing.SystemColors.Control; + dataGridViewCellStyle9.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + dataGridViewCellStyle9.ForeColor = System.Drawing.SystemColors.WindowText; + dataGridViewCellStyle9.SelectionBackColor = System.Drawing.SystemColors.Highlight; + dataGridViewCellStyle9.SelectionForeColor = System.Drawing.SystemColors.HighlightText; + dataGridViewCellStyle9.WrapMode = System.Windows.Forms.DataGridViewTriState.True; + this.gridView.RowHeadersDefaultCellStyle = dataGridViewCellStyle9; + this.gridView.Size = new System.Drawing.Size(983, 338); + this.gridView.TabIndex = 3; + this.gridView.CellBeginEdit += new System.Windows.Forms.DataGridViewCellCancelEventHandler(this.gridView_CellBeginEdit); + this.gridView.CellEndEdit += new System.Windows.Forms.DataGridViewCellEventHandler(this.gridView_CellEndEdit); + // + // registerPlayerBtn + // + this.registerPlayerBtn.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.registerPlayerBtn.Location = new System.Drawing.Point(16, 390); + this.registerPlayerBtn.Name = "registerPlayerBtn"; + this.registerPlayerBtn.Size = new System.Drawing.Size(105, 26); + this.registerPlayerBtn.TabIndex = 7; + this.registerPlayerBtn.Text = "Register new..."; + this.registerPlayerBtn.UseVisualStyleBackColor = true; + this.registerPlayerBtn.Click += new System.EventHandler(this.registerPlayerBtn_Click); + // + // PlayerManagerForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(1016, 435); + this.Controls.Add(this.registerPlayerBtn); + this.Controls.Add(this.gridView); + this.Controls.Add(this.label2); + this.Controls.Add(this.saveBtn); + this.Controls.Add(this.searchEntryBox); + this.Margin = new System.Windows.Forms.Padding(2); + this.Name = "PlayerManagerForm"; + this.Text = "Player Manager"; + ((System.ComponentModel.ISupportInitialize)(this.gridView)).EndInit(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + private System.Windows.Forms.TextBox searchEntryBox; + private System.Windows.Forms.Button saveBtn; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.DataGridView gridView; + private System.Windows.Forms.Button registerPlayerBtn; + } +} \ No newline at end of file diff --git a/BedrockService.backup/Client/Forms/PlayerManagerForm.cs b/BedrockService.backup/Client/Forms/PlayerManagerForm.cs new file mode 100644 index 00000000..ea819e69 --- /dev/null +++ b/BedrockService.backup/Client/Forms/PlayerManagerForm.cs @@ -0,0 +1,169 @@ +using BedrockService.Client.Management; +using BedrockService.Shared.Classes; +using BedrockService.Shared.Interfaces; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Text; +using System.Windows.Forms; + +namespace BedrockService.Client.Forms +{ + public partial class PlayerManagerForm : Form + { + private readonly IServerConfiguration _server; + private readonly string[] RegisteredPlayerColumnArray = new string[8] { "XUID:", "Username:", "Permission:", "Whitelisted:", "Ignores max players:", "First connected on:", "Last connected on:", "Time spent in game:" }; + private List playersFound = new List(); + private readonly List modifiedPlayers = new List(); + private IPlayer playerToEdit; + + public PlayerManagerForm(IServerConfiguration server) + { + InitializeComponent(); + _server = server; + playersFound = _server.GetPlayerList(); + + gridView.Columns.Clear(); + gridView.Rows.Clear(); + foreach (string s in RegisteredPlayerColumnArray) + { + gridView.Columns.Add(s.Replace(" ", "").Replace(":", ""), s); + } + gridView.Columns[5].ReadOnly = true; + gridView.Columns[6].ReadOnly = true; + gridView.Columns[7].ReadOnly = true; + gridView.Columns[5].CellTemplate.Style.BackColor = System.Drawing.Color.FromArgb(225, 225, 225); + gridView.Columns[6].CellTemplate.Style.BackColor = System.Drawing.Color.FromArgb(225, 225, 225); + gridView.Columns[7].CellTemplate.Style.BackColor = System.Drawing.Color.FromArgb(225, 225, 225); + gridView.AllowUserToAddRows = false; + RefreshGridContents(); + } + + private void RefreshGridContents() + { + gridView.Rows.Clear(); + foreach (IPlayer player in playersFound) + { + string[] playerReg = player.GetRegistration(); + string[] playerTimes = player.GetTimes(); + string playerFirstConnect = playerTimes[0]; + string playerConnectTime = playerTimes[1]; + string playerDisconnectTime = playerTimes[2]; + string playerWhitelist = playerReg[0]; + string playerPermission = player.GetPermissionLevel(); + string playerIgnoreLimit = playerReg[2]; + TimeSpan timeSpent = TimeSpan.FromTicks(long.Parse(playerConnectTime) - long.Parse(playerDisconnectTime)); + string[] list = new string[] { player.GetXUID(), player.GetUsername(), playerPermission, playerWhitelist, playerIgnoreLimit, playerFirstConnect, playerConnectTime, timeSpent.ToString("hhmmss") }; + gridView.Rows.Add(list); + } + gridView.Refresh(); + } + + private void saveBtn_Click(object sender, EventArgs e) + { + if (modifiedPlayers.Count > 0) + { + foreach (IPlayer player in modifiedPlayers) + { + _server.AddUpdatePlayer(player); + } + } + JsonSerializerSettings settings = new JsonSerializerSettings() + { + TypeNameHandling = TypeNameHandling.All + }; + byte[] sendBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(modifiedPlayers, Formatting.Indented, settings)); + FormManager.TCPClient.SendData(sendBytes, NetworkMessageSource.Client, NetworkMessageDestination.Server, FormManager.MainWindow.connectedHost.GetServerIndex(_server), NetworkMessageTypes.PlayersUpdate); + FormManager.MainWindow.DisableUI(); + Close(); + Dispose(); + } + + private void searchEntryBox_TextChanged(object sender, EventArgs e) + { + playersFound = _server.GetPlayerList(); + string curText = searchEntryBox.Text; + List tempList = new List(); + string[] splitCommands; + string cmd; + string value; + + if (curText.Contains(":")) + { + splitCommands = curText.Split(','); + if (splitCommands.Length > 1) + { + foreach (string s in splitCommands) + { + if (s.Contains(":")) + { + string[] finalSplit = s.Split(':'); + cmd = finalSplit[0]; + value = finalSplit[1]; + tempList = new List(); + foreach (IPlayer player in playersFound) + { + if (player.SearchForProperty(cmd).Contains(value)) + { + tempList.Add(player); + } + } + playersFound = tempList; + gridView.Refresh(); + } + } + } + splitCommands = curText.Split(':'); + cmd = splitCommands[0]; + value = splitCommands[1]; + foreach (IPlayer player in playersFound) + { + if (player.SearchForProperty(cmd).Contains(value)) + { + tempList.Add(player); + } + } + playersFound = tempList; + gridView.Refresh(); + RefreshGridContents(); + } + } + + private void gridView_CellBeginEdit(object sender, DataGridViewCellCancelEventArgs e) + { + DataGridViewRow focusedRow = gridView.Rows[e.RowIndex]; + playerToEdit = _server.GetPlayerByXuid((string)focusedRow.Cells[0].Value); + } + + private void gridView_CellEndEdit(object sender, DataGridViewCellEventArgs e) + { + DataGridViewRow focusedRow = gridView.Rows[e.RowIndex]; + string[] playerReg = playerToEdit.GetRegistration(); + string[] playerTimes = playerToEdit.GetTimes(); + string playerFirstConnect = playerTimes[0]; + string playerConnectTime = playerTimes[1]; + string playerDisconnectTime = playerTimes[2]; + string playerWhitelist = playerReg[0]; + string playerPermission = playerReg[1]; + string playerIgnoreLimit = playerReg[2]; + if ((string)focusedRow.Cells[0].Value != playerToEdit.GetXUID() || (string)focusedRow.Cells[1].Value != playerToEdit.GetUsername() || (string)focusedRow.Cells[2].Value != playerPermission || (string)focusedRow.Cells[3].Value != playerWhitelist || (string)focusedRow.Cells[4].Value != playerIgnoreLimit) + { + playerToEdit = new Player((string)focusedRow.Cells[0].Value, (string)focusedRow.Cells[1].Value, playerFirstConnect, playerConnectTime, playerDisconnectTime, bool.Parse((string)focusedRow.Cells[3].Value), (string)focusedRow.Cells[2].Value, bool.Parse((string)focusedRow.Cells[4].Value)); + modifiedPlayers.Add(playerToEdit); + } + } + + private void registerPlayerBtn_Click(object sender, EventArgs e) + { + using (NewPlayerRegistrationForm form = new NewPlayerRegistrationForm()) + { + if (form.ShowDialog() == DialogResult.OK) + { + _server.GetPlayerList().Add(form.PlayerToAdd); + modifiedPlayers.Add(form.PlayerToAdd); + RefreshGridContents(); + } + } + } + } +} diff --git a/BedrockService.backup/Client/Forms/PlayerManagerForm.resx b/BedrockService.backup/Client/Forms/PlayerManagerForm.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/BedrockService.backup/Client/Forms/PlayerManagerForm.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/BedrockService.backup/Client/Forms/PropEditorForm.Designer.cs b/BedrockService.backup/Client/Forms/PropEditorForm.Designer.cs new file mode 100644 index 00000000..c27c8bd2 --- /dev/null +++ b/BedrockService.backup/Client/Forms/PropEditorForm.Designer.cs @@ -0,0 +1,132 @@ + +namespace BedrockService.Client.Forms +{ + partial class PropEditorForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.CancelBtn = new System.Windows.Forms.Button(); + this.SaveBtn = new System.Windows.Forms.Button(); + this.gridView = new System.Windows.Forms.DataGridView(); + this.EntryKey = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.EntryData = new System.Windows.Forms.DataGridViewTextBoxColumn(); + this.DelBackupBtn = new System.Windows.Forms.Button(); + ((System.ComponentModel.ISupportInitialize)(this.gridView)).BeginInit(); + this.SuspendLayout(); + // + // CancelBtn + // + this.CancelBtn.Anchor = System.Windows.Forms.AnchorStyles.Bottom; + this.CancelBtn.Location = new System.Drawing.Point(13, 362); + this.CancelBtn.Name = "CancelBtn"; + this.CancelBtn.Size = new System.Drawing.Size(200, 23); + this.CancelBtn.TabIndex = 48; + this.CancelBtn.Text = "Cancel"; + this.CancelBtn.UseVisualStyleBackColor = true; + this.CancelBtn.Click += new System.EventHandler(this.CancelBtn_Click); + // + // SaveBtn + // + this.SaveBtn.Anchor = System.Windows.Forms.AnchorStyles.Bottom; + this.SaveBtn.Location = new System.Drawing.Point(588, 362); + this.SaveBtn.Name = "SaveBtn"; + this.SaveBtn.Size = new System.Drawing.Size(200, 23); + this.SaveBtn.TabIndex = 49; + this.SaveBtn.Text = "Save"; + this.SaveBtn.UseVisualStyleBackColor = true; + this.SaveBtn.Click += new System.EventHandler(this.SaveBtn_Click); + // + // gridView + // + this.gridView.AllowUserToDeleteRows = false; + this.gridView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.gridView.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; + this.gridView.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] { + this.EntryKey, + this.EntryData}); + this.gridView.Location = new System.Drawing.Point(13, 13); + this.gridView.MultiSelect = false; + this.gridView.Name = "gridView"; + this.gridView.Size = new System.Drawing.Size(775, 343); + this.gridView.TabIndex = 1; + this.gridView.NewRowNeeded += new System.Windows.Forms.DataGridViewRowEventHandler(this.gridView_NewRowNeeded); + // + // EntryKey + // + this.EntryKey.HeaderText = "Entry:"; + this.EntryKey.MinimumWidth = 20; + this.EntryKey.Name = "EntryKey"; + this.EntryKey.ReadOnly = true; + this.EntryKey.Width = 300; + // + // EntryData + // + this.EntryData.HeaderText = "Value:"; + this.EntryData.Name = "EntryData"; + this.EntryData.Width = 600; + // + // DelBackupBtn + // + this.DelBackupBtn.Anchor = System.Windows.Forms.AnchorStyles.Bottom; + this.DelBackupBtn.Enabled = false; + this.DelBackupBtn.Location = new System.Drawing.Point(301, 362); + this.DelBackupBtn.Name = "DelBackupBtn"; + this.DelBackupBtn.Size = new System.Drawing.Size(200, 23); + this.DelBackupBtn.TabIndex = 50; + this.DelBackupBtn.Text = "Delete Backup"; + this.DelBackupBtn.UseVisualStyleBackColor = true; + this.DelBackupBtn.Visible = false; + this.DelBackupBtn.Click += new System.EventHandler(this.DelBackupBtn_Click); + // + // EditSrv + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(800, 397); + this.Controls.Add(this.DelBackupBtn); + this.Controls.Add(this.gridView); + this.Controls.Add(this.SaveBtn); + this.Controls.Add(this.CancelBtn); + this.Name = "EditSrv"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; + this.Text = "Form1"; + ((System.ComponentModel.ISupportInitialize)(this.gridView)).EndInit(); + this.ResumeLayout(false); + + } + + #endregion + private System.Windows.Forms.Button CancelBtn; + private System.Windows.Forms.Button SaveBtn; + private System.Windows.Forms.DataGridView gridView; + private System.Windows.Forms.DataGridViewTextBoxColumn EntryKey; + private System.Windows.Forms.DataGridViewTextBoxColumn EntryData; + private System.Windows.Forms.Button DelBackupBtn; + } +} \ No newline at end of file diff --git a/BedrockService.backup/Client/Forms/PropEditorForm.cs b/BedrockService.backup/Client/Forms/PropEditorForm.cs new file mode 100644 index 00000000..2fc849e8 --- /dev/null +++ b/BedrockService.backup/Client/Forms/PropEditorForm.cs @@ -0,0 +1,133 @@ +using BedrockService.Client.Management; +using BedrockService.Shared.Classes; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Text; +using System.Windows.Forms; + +namespace BedrockService.Client.Forms +{ + public partial class PropEditorForm : Form + { + readonly DataGridView dataGrid; + public List workingProps; + public List startCmds; + public string RollbackFolderName = ""; + + public PropEditorForm() + { + InitializeComponent(); + dataGrid = gridView; + } + + public void PopulateBoxes(List propList) + { + int index = 0; + workingProps = propList; + foreach (Property prop in workingProps) + { + dataGrid.Rows.Add(new string[2] { prop.KeyName, prop.Value }); + if (prop.KeyName == "server-name" || prop.KeyName == "server-port" || prop.KeyName == "server-portv6") + { + dataGrid.Rows[index].ReadOnly = true; + dataGrid.Rows[index].DefaultCellStyle.BackColor = System.Drawing.Color.FromArgb(225, 225, 225); + } + index++; + } + gridView.AllowUserToAddRows = false; + } + + public void PopulateStartCmds(List list) + { + startCmds = list; + gridView.Columns.RemoveAt(0); + foreach (StartCmdEntry entry in startCmds) + { + dataGrid.Rows.Add(new string[1] { entry.Command }); + } + } + + public void EnableBackupManager() + { + gridView.MultiSelect = true; + DelBackupBtn.Enabled = true; + DelBackupBtn.Visible = true; + SaveBtn.Text = "Rollback this date"; + } + + private void CancelBtn_Click(object sender, EventArgs e) + { + Close(); + Dispose(); + } + + private void SaveBtn_Click(object sender, EventArgs e) + { + if (DelBackupBtn.Enabled) + { + if (dataGrid.SelectedRows.Count == 0) + { + if (dataGrid.SelectedCells.Count == 1) + { + dataGrid.SelectedCells[0].OwningRow.Selected = true; + } + } + if (dataGrid.SelectedRows.Count < 2) + RollbackFolderName = (string)dataGrid.CurrentRow.Cells[0].Value; + DialogResult = DialogResult.OK; + Close(); + } + startCmds = new List(); + foreach (DataGridViewRow row in dataGrid.Rows) + { + if (workingProps != null) + { + foreach (Property prop in workingProps) + { + if ((string)row.Cells[0].Value == prop.KeyName) + { + prop.Value = (string)row.Cells[1].Value; + } + } + } + else + { + if ((string)row.Cells[0].Value != null) + startCmds.Add(new StartCmdEntry((string)row.Cells[0].Value)); + } + + } + DialogResult = DialogResult.OK; + Close(); + } + + private void gridView_NewRowNeeded(object sender, DataGridViewRowEventArgs e) + { + DataGridViewRow focusedRow = gridView.CurrentRow; + focusedRow.Cells[0].Value = "Cmd:"; + gridView.Refresh(); + } + + private void DelBackupBtn_Click(object sender, EventArgs e) + { + if (dataGrid.SelectedRows.Count == 0) + { + if (dataGrid.SelectedCells.Count == 1) + { + dataGrid.SelectedCells[0].OwningRow.Selected = true; + } + } + List removeBackups = new List(); + if (dataGrid.SelectedRows.Count > 0) + foreach (DataGridViewRow viewRow in dataGrid.SelectedRows) + removeBackups.Add((string)viewRow.Cells[0].Value); + JsonSerializerSettings settings = new JsonSerializerSettings() + { + TypeNameHandling = TypeNameHandling.All + }; + byte[] serializeToBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(removeBackups, Formatting.Indented, settings)); + FormManager.TCPClient.SendData(serializeToBytes, NetworkMessageSource.Client, NetworkMessageDestination.Service, FormManager.MainWindow.connectedHost.GetServerIndex(FormManager.MainWindow.selectedServer), NetworkMessageTypes.DelBackups); + } + } +} diff --git a/BedrockService.backup/Client/Forms/PropEditorForm.resx b/BedrockService.backup/Client/Forms/PropEditorForm.resx new file mode 100644 index 00000000..6f35f1cd --- /dev/null +++ b/BedrockService.backup/Client/Forms/PropEditorForm.resx @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + True + + + True + + + True + + + True + + \ No newline at end of file diff --git a/BedrockService.backup/Client/Management/ClientLogger.cs b/BedrockService.backup/Client/Management/ClientLogger.cs new file mode 100644 index 00000000..8e9a737e --- /dev/null +++ b/BedrockService.backup/Client/Management/ClientLogger.cs @@ -0,0 +1,57 @@ +using BedrockService.Shared.Interfaces; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace BedrockService.Client.Management +{ + public class ClientLogger : ILogger + { + public List Log = new List(); + public StringBuilder OutString = new StringBuilder(); + public StreamWriter LogWriter; + private readonly string LogDir; + + public ClientLogger(IProcessInfo processInfo) + { + LogDir = $@"{processInfo.GetDirectory()}\Client\ClientLogs"; + if (!Directory.Exists(LogDir)) + { + Directory.CreateDirectory(LogDir); + } + LogWriter = new StreamWriter($@"{LogDir}\ClientLog_{DateTime.Now:yyyymmddhhmmss}.log", true); + } + + public void AppendLine(string text) + { + string addText = $"Client: {text}\r\n"; + Log.Add(addText); + LogWriter.WriteLine(addText); + LogWriter.Flush(); + Console.WriteLine(text); + } + + public void AppendText(string text) => AppendLine(text); + + public int Count() + { + return Log.Count; + } + + public string FromIndex(int index) + { + return Log[index]; + } + + public override string ToString() + { + OutString = new StringBuilder(); + foreach (string s in Log) + { + OutString.Append(s); + } + return OutString.ToString(); + } + } +} diff --git a/BedrockService.backup/Client/Management/ConfigManager.cs b/BedrockService.backup/Client/Management/ConfigManager.cs new file mode 100644 index 00000000..837c0fa8 --- /dev/null +++ b/BedrockService.backup/Client/Management/ConfigManager.cs @@ -0,0 +1,89 @@ +using BedrockService.Shared.Classes; +using BedrockService.Shared.Interfaces; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; + +namespace BedrockService.Client.Management +{ + public class ConfigManager + { + public string ConfigDir = $@"{Directory.GetCurrentDirectory()}\Client\Configs"; + public string ConfigFile; + public List HostConnectList = new List(); + public string NBTStudioPath; + private readonly ILogger Logger; + + public ConfigManager(ILogger logger) + { + Logger = logger; + ConfigFile = $@"{ConfigDir}\Config.conf"; + } + + public void LoadConfigs() + { + HostConnectList.Clear(); + if (!Directory.Exists(ConfigDir)) + { + Directory.CreateDirectory(ConfigDir); + } + if (!File.Exists(ConfigFile)) + { + Logger.AppendLine("Config file missing! Regenerating default file..."); + CreateDefaultConfig(); + LoadConfigs(); + return; + } + string[] lines = File.ReadAllLines(ConfigFile); + foreach (string line in lines) + { + string[] entrySplit = line.Split('='); + if (!string.IsNullOrEmpty(line) && !line.StartsWith("#")) + { + if (entrySplit[0] == "HostEntry") + { + string[] hostSplit = entrySplit[1].Split(';'); + string[] addressSplit = hostSplit[1].Split(':'); + IClientSideServiceConfiguration hostToList = new ClientSideServiceConfiguration(hostSplit[0], addressSplit[0], addressSplit[1]); + HostConnectList.Add(hostToList); + } + if (entrySplit[0] == "NBTStudioPath") + { + NBTStudioPath = entrySplit[1]; + } + } + } + } + + public void CreateDefaultConfig() + { + string[] Config = new string[] + { + "HostEntry=host1;127.0.0.1:19134" + }; + StringBuilder builder = new StringBuilder(); + builder.Append("# Hosts\n"); + foreach (string entry in Config) + { + builder.Append($"{entry}\n"); + } + builder.Append("\n# Settings\n"); + builder.Append("NBTStudioPath=\n"); + File.WriteAllText(ConfigFile, builder.ToString()); + } + + public void SaveConfigFile() + { + StringBuilder fileContent = new StringBuilder(); + fileContent.Append("# hosts\n\n"); + foreach (ClientSideServiceConfiguration host in HostConnectList) + { + fileContent.Append($"HostEntry={host.GetHostName()};{host.GetAddress()}:{host.GetPort()}\n"); + } + fileContent.Append("\n# Settings\n"); + fileContent.Append($"NBTStudioPath={NBTStudioPath}"); + File.WriteAllText(ConfigFile, fileContent.ToString()); + } + } +} diff --git a/BedrockService.backup/Client/Management/FormManager.cs b/BedrockService.backup/Client/Management/FormManager.cs new file mode 100644 index 00000000..37a2ca80 --- /dev/null +++ b/BedrockService.backup/Client/Management/FormManager.cs @@ -0,0 +1,41 @@ +using BedrockService.Client.Forms; +using BedrockService.Client.Networking; +using BedrockService.Shared.Classes; +using BedrockService.Shared.Interfaces; +using System.Diagnostics; +using System.IO; +using System.Reflection; + +namespace BedrockService.Client.Management +{ + public sealed class FormManager + { + private static readonly IProcessInfo processInfo = new ServiceProcessInfo(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), Path.GetFileName(Assembly.GetExecutingAssembly().Location), Process.GetCurrentProcess().Id, false, true); + private static readonly ILogger Logger = new ClientLogger(processInfo); + private static MainWindow main; + private static TCPClient client; + public static MainWindow MainWindow + { + get + { + if (main == null || main.IsDisposed) + { + main = new MainWindow(processInfo, Logger); + } + return main; + } + } + + public static TCPClient TCPClient + { + get + { + if (client == null) + { + client = new TCPClient(Logger); + } + return client; + } + } + } +} diff --git a/BedrockService.backup/Client/Management/LogManager.cs b/BedrockService.backup/Client/Management/LogManager.cs new file mode 100644 index 00000000..7e8a1d05 --- /dev/null +++ b/BedrockService.backup/Client/Management/LogManager.cs @@ -0,0 +1,123 @@ +using BedrockService.Shared.Classes; +using BedrockService.Shared.Interfaces; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; +using System.Windows.Forms; + +namespace BedrockService.Client.Management +{ + class LogManager + { + public Thread LogThread; + public bool EnableFlag; + public bool Working = false; + public List ServiceLogs = new List(); + private IServiceConfiguration connectedHost; + private readonly ILogger Logger; + + public LogManager(ILogger logger) + { + Logger = logger; + } + + private void LogManagerTask() + { + while (FormManager.TCPClient.Connected) + { + try + { + Working = true; + StringBuilder sendString = new StringBuilder(); + foreach (ServerInfo server in connectedHost.GetServerList()) + { + server.ConsoleBuffer = server.ConsoleBuffer ?? new List(); + sendString.Append($"{server.ServerName};{server.ConsoleBuffer.Count}|"); + } + sendString.Append($"Service;{connectedHost.GetLog().Count}"); + byte[] stringsToBytes = Encoding.UTF8.GetBytes(sendString.ToString()); + FormManager.TCPClient.SendData(stringsToBytes, NetworkMessageSource.Client, NetworkMessageDestination.Service, NetworkMessageTypes.ConsoleLogUpdate); + Thread.Sleep(200); + int currentLogBoxLength = 0; + + if (FormManager.MainWindow.selectedServer == null) + { + UpdateLogBoxInvoked(""); + } + FormManager.MainWindow.LogBox.Invoke((MethodInvoker)delegate + { + currentLogBoxLength = FormManager.MainWindow.LogBox.TextLength; + }); + if (FormManager.MainWindow.ShowsSvcLog && connectedHost.GetLog().Count != currentLogBoxLength) + { + UpdateLogBoxInvoked(string.Join("\r\n", connectedHost.GetLog())); + } + else if (!FormManager.MainWindow.ShowsSvcLog && FormManager.MainWindow.selectedServer.GetLog() != null && FormManager.MainWindow.selectedServer.GetLog().Count != currentLogBoxLength) + { + UpdateLogBoxInvoked(string.Join("", FormManager.MainWindow.selectedServer.GetLog())); + } + + } + catch (Exception e) + { + Logger.AppendLine($"LogManager Error! Stacetrace: {e.StackTrace}"); + } + } + } + + public bool InitLogThread(IServiceConfiguration host) + { + connectedHost = host; + return StartLogThread(); + } + + public bool StartLogThread() + { + try + { + if (LogThread != null && LogThread.IsAlive) + LogThread.Abort(); + LogThread = new Thread(new ThreadStart(LogManagerTask)); + LogThread.Name = "LogThread"; + LogThread.IsBackground = true; + EnableFlag = true; + LogThread.Start(); + Logger.AppendLine("LogThread started"); + return true; + } + catch (Exception e) + { + Logger.AppendLine($"Error starting LogThread: {e.StackTrace}"); + } + return false; + } + + public bool StopLogThread() + { + if (LogThread == null) + { + return true; + } + try + { + LogThread.Abort(); + } + catch (ThreadAbortException e) + { + Logger.AppendLine(e.StackTrace); + } + Logger.AppendLine("LogThread stopped"); + LogThread = null; + return true; + } + + private static void UpdateLogBoxInvoked(string contents) + { + FormManager.MainWindow.LogBox.Invoke((MethodInvoker)delegate + { + FormManager.MainWindow.UpdateLogBox(contents); + }); + } + } +} diff --git a/BedrockService.backup/Client/Networking/TCPClient.cs b/BedrockService.backup/Client/Networking/TCPClient.cs new file mode 100644 index 00000000..15eb7cab --- /dev/null +++ b/BedrockService.backup/Client/Networking/TCPClient.cs @@ -0,0 +1,346 @@ +using BedrockService.Client.Management; +using BedrockService.Shared.Classes; +using BedrockService.Shared.Interfaces; +using BedrockService.Shared.PackParser; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.IO; +using System.Net.Sockets; +using System.Text; +using System.Threading; + +namespace BedrockService.Client.Networking +{ + public class TCPClient + { + public TcpClient OpenedTcpClient; + public string ClientName; + public NetworkStream stream; + public bool Connected; + public bool EnableRead; + public bool PlayerInfoArrived; + public bool EnumBackupsArrived; + public List BackupList; + public List RecievedPacks; + public Thread ClientReciever; + public Thread HeartbeatThread; + private int _heartbeatFailTimeout; + private const int _heartbeatFailTimeoutLimit = 250; + private bool _heartbeatRecieved; + private bool _keepAlive; + private readonly ILogger _logger; + + public TCPClient (ILogger logger) + { + _logger = logger; + } + + public void ConnectHost(IClientSideServiceConfiguration host) + { + if (EstablishConnection(host.GetAddress(), int.Parse(host.GetPort()))) + { + SendData(NetworkMessageSource.Client, NetworkMessageDestination.Service, NetworkMessageTypes.Connect); + return; + } + } + + public bool EstablishConnection(string addr, int port) + { + _logger.AppendLine("Connecting to Server"); + try + { + EnableRead = false; + OpenedTcpClient = new TcpClient(addr, port); + stream = OpenedTcpClient.GetStream(); + _keepAlive = true; + ClientReciever = new Thread(new ThreadStart(ReceiveListener)); + ClientReciever.Name = "ClientPacketReviever"; + ClientReciever.IsBackground = true; + ClientReciever.Start(); + } + catch + { + _logger.AppendLine("Could not connect to Server"); + if(ClientReciever != null) + ClientReciever.Abort(); + ClientReciever = null; + return false; + } + return _keepAlive; + } + + public void CloseConnection() + { + try + { + if (stream != null) + stream.Dispose(); + stream = null; + Connected = false; + _keepAlive = false; + } + catch (NullReferenceException) + { + Connected = false; + _keepAlive = false; + } + catch (Exception e) + { + _logger.AppendLine($"Error closing connection: {e.StackTrace}"); + } + } + + public void ReceiveListener() + { + List byteBlocks = new List(); + while (_keepAlive) + { + try + { + byte[] buffer = new byte[4]; + while (OpenedTcpClient.Client.Available > 0) + { + int byteCount = stream.Read(buffer, 0, 4); + int expectedLen = BitConverter.ToInt32(buffer, 0); + buffer = new byte[expectedLen]; + byteCount = stream.Read(buffer, 0, expectedLen); + NetworkMessageSource source = (NetworkMessageSource)buffer[0]; + NetworkMessageDestination destination = (NetworkMessageDestination)buffer[1]; + byte serverIndex = buffer[2]; + NetworkMessageTypes msgType = (NetworkMessageTypes)buffer[3]; + NetworkMessageFlags msgStatus = (NetworkMessageFlags)buffer[4]; + string data = ""; + if (msgType != NetworkMessageTypes.PackFile || msgType != NetworkMessageTypes.LevelEditFile) + data = GetOffsetString(buffer); + if (destination != NetworkMessageDestination.Client) + continue; + int srvCurLen = 0; + JsonSerializerSettings settings = new JsonSerializerSettings() + { + TypeNameHandling = TypeNameHandling.All + }; + switch (source) + { + case NetworkMessageSource.Service: + switch (msgType) + { + case NetworkMessageTypes.Connect: + try + { + _logger.AppendLine("Connection to Host successful!"); + FormManager.MainWindow.connectedHost = null; + FormManager.MainWindow.connectedHost = JsonConvert.DeserializeObject(data, settings); + Connected = true; + FormManager.MainWindow.RefreshServerContents(); + _heartbeatFailTimeout = 0; + if (HeartbeatThread == null || !HeartbeatThread.IsAlive) + HeartbeatThread = new Thread(new ThreadStart(SendHeatbeatSignal)) + { + IsBackground = true, + Name = "HeartBeatThread" + }; + HeartbeatThread.Start(); + + } + catch (Exception e) + { + _logger.AppendLine($"Error: ConnectMan reported error: {e.Message}\n{e.StackTrace}"); + } + break; + case NetworkMessageTypes.Heartbeat: + _heartbeatRecieved = true; + if (!HeartbeatThread.IsAlive) + { + HeartbeatThread = new Thread(new ThreadStart(SendHeatbeatSignal)); + HeartbeatThread.IsBackground = true; + HeartbeatThread.Name = "HeartBeatThread"; + HeartbeatThread.Start(); + } + break; + + case NetworkMessageTypes.EnumBackups: + + BackupList = JsonConvert.DeserializeObject>(data, settings); + EnumBackupsArrived = true; + + break; + case NetworkMessageTypes.CheckUpdates: + + //TODO: Ask user if update now or perform later. + UnlockUI(); + + break; + case NetworkMessageTypes.UICallback: + + UnlockUI(); + + break; + } + break; + case NetworkMessageSource.Server: + switch (msgType) + { + case NetworkMessageTypes.ConsoleLogUpdate: + string[] strings = data.Split('|'); + for (int i = 0; i < strings.Length; i++) + { + string[] srvSplit = strings[i].Split(';'); + string srvName = srvSplit[0]; + string srvText = srvSplit[1]; + srvCurLen = int.Parse(srvSplit[2]); + if (srvName != "Service") + { + IServerConfiguration bedrockServer = FormManager.MainWindow.connectedHost.GetServerInfoByName(srvName); + int curCount = bedrockServer.GetLog().Count; + if (curCount == srvCurLen) + { + bedrockServer.GetLog().Add(srvText); + } + } + else + { + int curCount = FormManager.MainWindow.connectedHost.GetLog().Count; + if (curCount == srvCurLen) + { + FormManager.MainWindow.connectedHost.GetLog().Add(srvText); + } + } + } + break; + case NetworkMessageTypes.Backup: + + _logger.AppendLine(msgStatus.ToString()); + + break; + case NetworkMessageTypes.UICallback: + + UnlockUI(); + + break; + case NetworkMessageTypes.PackList: + + List temp = new List(); + JArray jArray = JArray.Parse(data); + foreach (JToken token in jArray) + temp.Add(token.ToObject()); + RecievedPacks = temp; + + break; + case NetworkMessageTypes.PlayersRequest: + + List fetchedPlayers = JsonConvert.DeserializeObject>(data, settings); + FormManager.MainWindow.connectedHost.GetServerInfoByIndex(serverIndex).SetPlayerList(fetchedPlayers); + PlayerInfoArrived = true; + + break; + case NetworkMessageTypes.LevelEditFile: + + byte[] stripHeaderFromBuffer = new byte[buffer.Length - 5]; + Buffer.BlockCopy(buffer, 5, stripHeaderFromBuffer, 0, stripHeaderFromBuffer.Length); + string pathToLevelDat = $@"{Path.GetTempPath()}\level.dat"; + File.WriteAllBytes(pathToLevelDat, stripHeaderFromBuffer); + UnlockUI(); + + break; + } + break; + } + } + } + catch (Exception e) + { + _logger.AppendLine($"TCPClient error! Stacktrace: {e.Message}\n{e.StackTrace}"); + } + Thread.Sleep(200); + } + } + + public void SendHeatbeatSignal() + { + _logger.AppendLine("HeartbeatThread started."); + while (_keepAlive) + { + _heartbeatRecieved = false; + SendData(NetworkMessageSource.Client, NetworkMessageDestination.Service, NetworkMessageTypes.Heartbeat); + while (!_heartbeatRecieved && _keepAlive) + { + Thread.Sleep(100); + _heartbeatFailTimeout++; + if (_heartbeatFailTimeout > _heartbeatFailTimeoutLimit) + { + FormManager.MainWindow.HeartbeatFailDisconnect(); + HeartbeatThread.Abort(); + _heartbeatFailTimeout = 0; + } + } + // Logger.AppendLine("ThumpThump"); + _heartbeatRecieved = false; + _heartbeatFailTimeout = 0; + Thread.Sleep(3000); + } + } + + public bool SendData(byte[] bytes, NetworkMessageSource source, NetworkMessageDestination destination, byte serverIndex, NetworkMessageTypes type, NetworkMessageFlags status) + { + byte[] compiled = new byte[9 + bytes.Length]; + byte[] len = BitConverter.GetBytes(5 + bytes.Length); + Buffer.BlockCopy(len, 0, compiled, 0, 4); + compiled[4] = (byte)source; + compiled[5] = (byte)destination; + compiled[6] = serverIndex; + compiled[7] = (byte)type; + compiled[8] = (byte)status; + Buffer.BlockCopy(bytes, 0, compiled, 9, bytes.Length); + if (_keepAlive) + { + try + { + stream.Write(compiled, 0, compiled.Length); + stream.Flush(); + return true; + + } + catch + { + _logger.AppendLine("Error writing to network stream!"); + return false; + } + } + return false; + } + + public void SendData(NetworkMessageSource source, NetworkMessageDestination destination, NetworkMessageTypes type) => SendData(new byte[0], source, destination, 0xFF, type, NetworkMessageFlags.None); + + public void SendData(NetworkMessageSource source, NetworkMessageDestination destination, byte serverIndex, NetworkMessageTypes type) => SendData(new byte[0], source, destination, serverIndex, type, NetworkMessageFlags.None); + + public void SendData(NetworkMessageSource source, NetworkMessageDestination destination, byte serverIndex, NetworkMessageTypes type, NetworkMessageFlags flag) => SendData(new byte[0], source, destination, serverIndex, type, flag); + + public void SendData(byte[] bytes, NetworkMessageSource source, NetworkMessageDestination destination, byte serverIndex, NetworkMessageTypes type) => SendData(bytes, source, destination, serverIndex, type, NetworkMessageFlags.None); + + public void SendData(byte[] bytes, NetworkMessageSource source, NetworkMessageDestination destination, NetworkMessageTypes type) => SendData(bytes, source, destination, 0xFF, type, NetworkMessageFlags.None); + + public void SendData(NetworkMessageSource source, NetworkMessageDestination destination, NetworkMessageTypes type, NetworkMessageFlags status) => SendData(new byte[0], source, destination, 0xFF, type, status); + + public void Dispose() + { + if (HeartbeatThread != null && HeartbeatThread.IsAlive) + HeartbeatThread.Abort(); + if (ClientReciever != null && ClientReciever.IsAlive) + ClientReciever.Abort(); + HeartbeatThread = null; + ClientReciever = null; + if (OpenedTcpClient != null) + { + OpenedTcpClient.Close(); + OpenedTcpClient.Dispose(); + } + + } + + private string GetOffsetString(byte[] array) => Encoding.UTF8.GetString(array, 5, array.Length - 5); + + private void UnlockUI() => FormManager.MainWindow.ServerBusy = false; + } +} diff --git a/BedrockService.backup/Client/Properties/AssemblyInfo.cs b/BedrockService.backup/Client/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..83c81492 --- /dev/null +++ b/BedrockService.backup/Client/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("BedrockClientGUI")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("BedrockClientGUI")] +[assembly: AssemblyCopyright("Copyright © 2021")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("a2daf70a-925a-4f4b-b7ee-caace75d6740")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// 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")] diff --git a/BedrockService.backup/Client/Properties/Resources.Designer.cs b/BedrockService.backup/Client/Properties/Resources.Designer.cs new file mode 100644 index 00000000..2764b906 --- /dev/null +++ b/BedrockService.backup/Client/Properties/Resources.Designer.cs @@ -0,0 +1,63 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace BedrockService.Client.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("BedrockService.Client.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + } +} diff --git a/BedrockService.backup/Client/Properties/Resources.resx b/BedrockService.backup/Client/Properties/Resources.resx new file mode 100644 index 00000000..af7dbebb --- /dev/null +++ b/BedrockService.backup/Client/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/BedrockService.backup/Client/Properties/Settings.Designer.cs b/BedrockService.backup/Client/Properties/Settings.Designer.cs new file mode 100644 index 00000000..609bb426 --- /dev/null +++ b/BedrockService.backup/Client/Properties/Settings.Designer.cs @@ -0,0 +1,26 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace BedrockService.Client.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.10.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + } +} diff --git a/BedrockService.backup/Client/Properties/Settings.settings b/BedrockService.backup/Client/Properties/Settings.settings new file mode 100644 index 00000000..39645652 --- /dev/null +++ b/BedrockService.backup/Client/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/BedrockService/Client/packages.config b/BedrockService.backup/Client/packages.config similarity index 100% rename from BedrockService/Client/packages.config rename to BedrockService.backup/Client/packages.config diff --git a/BedrockService.backup/Client/upgrade.backup b/BedrockService.backup/Client/upgrade.backup new file mode 100644 index 00000000..7f14ee63 --- /dev/null +++ b/BedrockService.backup/Client/upgrade.backup @@ -0,0 +1 @@ +Backup created at 1636645213 (11/11/2021 3:40:13 PM +00:00) \ No newline at end of file diff --git a/BedrockService.backup/Service/BedrockService.Service.csproj b/BedrockService.backup/Service/BedrockService.Service.csproj new file mode 100644 index 00000000..f0053119 --- /dev/null +++ b/BedrockService.backup/Service/BedrockService.Service.csproj @@ -0,0 +1,197 @@ + + + + + Debug + AnyCPU + {13B2B5A8-71E9-49F4-9BFB-3C3B3C0A054C} + Exe + BedrockService.Service + BedrockService.Service + v4.7.2 + 512 + true + true + true + C:\Users\crowbar\source\repos\BedrockManagementService\Releases\ + true + Web + true + Foreground + 7 + Days + false + false + true + http://Coming.soon/ + true + publish.htm + 4 + 1.0.0.%2a + false + true + true + + + x86 + true + full + false + ..\bin\Debug\ + DEBUG;TRACE + prompt + 2 + + + AnyCPU + pdbonly + true + ..\bin\Release\ + TRACE + prompt + 4 + + + EE2403F7477A35F08B98B0A8FB2404C95BE04FEB + + + BedrockService_TemporaryKey.pfx + + + true + + + false + + + false + + + + + + + + ..\packages\Microsoft.Bcl.AsyncInterfaces.5.0.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll + False + True + + + ..\packages\Microsoft.Extensions.DependencyInjection.5.0.2\lib\net461\Microsoft.Extensions.DependencyInjection.dll + False + True + + + ..\packages\Microsoft.Extensions.DependencyInjection.Abstractions.5.0.0\lib\net461\Microsoft.Extensions.DependencyInjection.Abstractions.dll + False + True + + + ..\packages\NCrontab.Signed.3.3.2\lib\net35\NCrontab.Signed.dll + False + True + + + ..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll + False + True + + + + False + + + + + False + + + + ..\packages\System.IO.Compression.ZipFile.4.3.0\lib\net46\System.IO.Compression.ZipFile.dll + True + True + + + ..\packages\System.Runtime.InteropServices.RuntimeInformation.4.3.0\lib\net45\System.Runtime.InteropServices.RuntimeInformation.dll + True + False + + + + + ..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll + True + + + + + + False + + + + + + ..\packages\Topshelf.4.3.0\lib\net452\Topshelf.dll + False + True + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + False + Microsoft .NET Framework 4.7.2 %28x86 and x64%29 + true + + + False + .NET Framework 3.5 SP1 + false + + + + + + + + + + {f146d5e8-ef1f-4785-9150-182631f059b7} + BedrockService.Shared + + + + \ No newline at end of file diff --git a/BedrockService.backup/Service/Configs/Default.conf b/BedrockService.backup/Service/Configs/Default.conf new file mode 100644 index 00000000..0379cdeb --- /dev/null +++ b/BedrockService.backup/Service/Configs/Default.conf @@ -0,0 +1,28 @@ +# Server +server-name=Default +gamemode=creative +difficulty=easy +allow-cheats=false +max-players=10 +online-mode=true +white-list=false +server-port=19132 +server-portv6=19133 +view-distance=32 +tick-distance=4 +player-idle-timeout=30 +max-threads=8 +level-name=Bedrock level +level-seed= +default-player-permission-level=member +texturepack-required=false +content-log-file-enabled=false +compression-threshold=1 +server-authoritative-movement=server-auth +player-movement-score-threshold=20 +player-movement-distance-threshold=0.3 +player-movement-duration-threshold-in-ms=500 +correct-player-movement=false + +# StartCmds +AddStartCmd=help 1 \ No newline at end of file diff --git a/BedrockService.backup/Service/Configs/Default.players b/BedrockService.backup/Service/Configs/Default.players new file mode 100644 index 00000000..b2ddaf2f --- /dev/null +++ b/BedrockService.backup/Service/Configs/Default.players @@ -0,0 +1,5 @@ +# Registered player list +# Register player entries: xuid,username,permission,isWhitelisted,ignoreMaxPlayers +# Example: TestUser,555111222333444,visitor,false,false + +1234111222333444,TestUser,visitor,false,false \ No newline at end of file diff --git a/BedrockService.backup/Service/Configs/Globals.conf b/BedrockService.backup/Service/Configs/Globals.conf new file mode 100644 index 00000000..3d3c6083 --- /dev/null +++ b/BedrockService.backup/Service/Configs/Globals.conf @@ -0,0 +1,16 @@ +#Globals +ServersPath=C:\MCBedrockService +AcceptedMojangLic=false +ClientPort=19134 +#Backups +BackupEnabled=false +BackupPath=Default +BackupCron=0 1 * * * +MaxBackupCount=25 +EntireBackups=false +#Updates +CheckUpdates=true +UpdateCron=38 19 * * * +#Logging +LogServersToFile=false +LogServiceToFile=false diff --git a/BedrockService.backup/Service/Core/BedrockService.cs b/BedrockService.backup/Service/Core/BedrockService.cs new file mode 100644 index 00000000..4aa17913 --- /dev/null +++ b/BedrockService.backup/Service/Core/BedrockService.cs @@ -0,0 +1,310 @@ +using BedrockService.Service.Core.Interfaces; +using BedrockService.Service.Management; +using BedrockService.Service.Networking; +using BedrockService.Service.Server; +using BedrockService.Shared.Interfaces; +using BedrockService.Service.Core.Threads; +using NCrontab; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using System.Timers; +using Topshelf; + +namespace BedrockService.Service.Core +{ + public class BedrockService : ServiceControl, IBedrockService + { + private enum ServiceStatus + { + Stopped, + Starting, + Started, + Stopping + } + private readonly IServiceConfiguration _serviceConfiguration; + private readonly ILogger _logger; + private readonly IProcessInfo _processInfo; + private readonly IConfigurator _configurator; + private readonly IUpdater _updater; + private readonly ITCPListener _tCPListener; + private readonly CrontabSchedule _shed; + private readonly CrontabSchedule _updaterCron; + private HostControl _hostControl; + private readonly List _bedrockServers = new List(); + private System.Timers.Timer _updaterTimer; + private System.Timers.Timer _cronTimer; + + public BedrockService(IConfigurator configurator, IUpdater updater, ILogger logger, IServiceConfiguration serviceConfiguration, IProcessInfo serviceProcessInfo, ITCPListener tCPListener) + { + _tCPListener = tCPListener; + _configurator = configurator; + _serviceConfiguration = serviceConfiguration; + _processInfo = serviceProcessInfo; + _updater = updater; + _logger = logger; + _shed = CrontabSchedule.TryParse(serviceConfiguration.GetProp("BackupCron").ToString()); + _updaterCron = CrontabSchedule.TryParse(serviceConfiguration.GetProp("UpdateCron").ToString()); + Initialize(); + _tCPListener.SetKeyContainer(_configurator.GetKeyContainer()); + } + + public bool Start(HostControl hostControl) + { + _hostControl = hostControl; + try + { + ValidSettingsCheck(); + + foreach (var brs in _bedrockServers) + { + if(hostControl != null) + _hostControl.RequestAdditionalTime(TimeSpan.FromSeconds(30)); + brs.SetServerStatus(BedrockServer.ServerStatus.Starting); + brs.StartWatchdog(_hostControl); + } + return true; + } + catch (Exception e) + { + _logger.AppendLine($"Error Starting BedrockServiceWrapper {e.StackTrace}"); + return false; + } + } + + public bool Stop(HostControl hostControl) + { + _hostControl = hostControl; + try + { + foreach (var brs in _bedrockServers) + { + brs.SetServerStatus(BedrockServer.ServerStatus.Stopping); + while (brs.GetServerStatus() == BedrockServer.ServerStatus.Stopping && !Program.IsExiting) + Thread.Sleep(100); + } + return true; + } + catch (Exception e) + { + _logger.AppendLine($"Error Stopping BedrockServiceWrapper {e.StackTrace}"); + return false; + } + } + + public void RestartService() + { + try + { + foreach (IBedrockServer brs in _bedrockServers) + { + brs.SetServerStatus(BedrockServer.ServerStatus.Stopping); + while (brs.GetServerStatus() == BedrockServer.ServerStatus.Stopping && !Program.IsExiting) + Thread.Sleep(100); + } + foreach (IBedrockServer brs in _bedrockServers) + { + brs.StopWatchdog(); + } + try + { + _tCPListener.ResetListener(); + } + catch (ThreadAbortException) { } + _configurator.LoadAllConfigurations().Wait(); + Initialize(); + foreach (var brs in _bedrockServers) + { + brs.SetServerStatus(BedrockServer.ServerStatus.Starting); + } + Start(_hostControl); + } + catch (Exception e) + { + _logger.AppendLine($"Error Stopping BedrockServiceWrapper {e.StackTrace}"); + } + } + + public IBedrockServer GetBedrockServerByIndex(int serverIndex) + { + return _bedrockServers[serverIndex]; + } + + public IBedrockServer GetBedrockServerByName(string name) + { + return _bedrockServers.FirstOrDefault(brs => brs.GetServerName() == name); + } + + public List GetAllServers() => _bedrockServers; + + public void InitializeNewServer(IServerConfiguration server) + { + IBedrockServer bedrockServer = new BedrockServer(server, _configurator, _logger, _serviceConfiguration, _processInfo); + _bedrockServers.Add(bedrockServer); + _serviceConfiguration.AddNewServerInfo(server); + if (ValidSettingsCheck()) + { + bedrockServer.SetServerStatus(BedrockServer.ServerStatus.Starting); + bedrockServer.StartWatchdog(_hostControl); + } + } + + private void Initialize() + { + _bedrockServers.Clear(); + if (_serviceConfiguration.GetProp("BackupEnabled").ToString() == "true" && _shed != null) + { + _cronTimer = new System.Timers.Timer((_shed.GetNextOccurrence(DateTime.Now) - DateTime.Now).TotalMilliseconds); + _cronTimer.Elapsed += CronTimer_Elapsed; + _cronTimer.Start(); + } + if (_serviceConfiguration.GetProp("CheckUpdates").ToString() == "true" && _updaterCron != null) + { + _updaterTimer = new System.Timers.Timer((_updaterCron.GetNextOccurrence(DateTime.Now) - DateTime.Now).TotalMilliseconds); + _updaterTimer.Elapsed += UpdateTimer_Elapsed; + _logger.AppendLine($"Updates Enabled, will be checked in: {((float)_updaterTimer.Interval / 1000)} seconds."); + _updaterTimer.Start(); + } + try + { + List temp = _serviceConfiguration.GetServerList(); + foreach (IServerConfiguration server in temp) + { + IBedrockServer bedrockServer = new BedrockServer(server, _configurator, _logger, _serviceConfiguration, _processInfo); + _bedrockServers.Add(bedrockServer); + } + } + catch (Exception e) + { + _logger.AppendLine($"Error Instantiating BedrockServiceWrapper: {e.StackTrace}"); + } + } + + private void CronTimer_Elapsed(object sender, ElapsedEventArgs e) + { + try + { + if (_cronTimer != null) + { + _cronTimer.Stop(); + _cronTimer = null; + } + if (_serviceConfiguration.GetProp("BackupEnabled").ToString() == "true" && _shed != null) + { + Backup(); + + _cronTimer = new System.Timers.Timer((_shed.GetNextOccurrence(DateTime.Now) - DateTime.Now).TotalMilliseconds); + _cronTimer.Elapsed += CronTimer_Elapsed; + _cronTimer.Start(); + } + } + catch (Exception ex) + { + _logger.AppendLine($"Error in BackupTimer_Elapsed {ex}"); + } + } + + private void UpdateTimer_Elapsed(object sender, ElapsedEventArgs e) + { + try + { + if (_updaterTimer != null) + { + _updaterTimer.Stop(); + _updaterTimer = null; + } + _updater.CheckUpdates().Wait(); + if (_serviceConfiguration.GetProp("CheckUpdates").ToString() == "true" && _updater != null) + { + if (_updater.CheckVersionChanged()) + { + _logger.AppendLine("Version change detected! Restarting server(s) to apply update..."); + foreach(IBedrockServer server in _bedrockServers) + { + server.RestartServer(false); + } + } + + _updaterTimer = new System.Timers.Timer((_updaterCron.GetNextOccurrence(DateTime.Now) - DateTime.Now).TotalMilliseconds); + _updaterTimer.Elapsed += UpdateTimer_Elapsed; + _updaterTimer.Start(); + } + } + catch (Exception ex) + { + _logger.AppendLine($"Error in UpdateTimer_Elapsed {ex}"); + } + } + + private void Backup() + { + _logger.AppendLine("Service started backup manager."); + foreach (var brs in _bedrockServers) + { + brs.RestartServer(true); + } + _logger.AppendLine("Backups have been completed."); + } + + private bool ValidSettingsCheck() + { + bool validating = true; + bool dupedSettingsFound = false; + while (validating) + { + if (_serviceConfiguration.GetServerList().Count() < 1) + { + throw new Exception("No Servers Configured"); + } + else + { + foreach (IServerConfiguration server in _serviceConfiguration.GetServerList()) + { + foreach (IServerConfiguration compareServer in _serviceConfiguration.GetServerList()) + { + if (server != compareServer) + { + if (server.GetProp("server-port").Equals(compareServer.GetProp("server-port")) || + server.GetProp("server-portv6").Equals(compareServer.GetProp("server-portv6")) || + server.GetProp("server-name").Equals(compareServer.GetProp("server-name"))) + { + _logger.AppendLine($"Duplicate server settings between servers {server.GetFileName()} and {compareServer.GetFileName()}."); + dupedSettingsFound = true; + } + } + } + } + if (dupedSettingsFound) + { + throw new Exception("Duplicate settings found! Check logs."); + } + foreach (var server in _serviceConfiguration.GetServerList()) + { + if (_updater.CheckVersionChanged() || !File.Exists(server.GetProp("ServerPath") + "\\bedrock_server.exe")) + { + _configurator.ReplaceServerBuild(server).Wait(); + } + if (server.GetProp("ServerExeName").ToString() != "bedrock_server.exe" && File.Exists(server.GetProp("ServerPath") + "\\bedrock_server.exe") && !File.Exists(server.GetProp("ServerPath") + "\\" + server.GetProp("ServerExeName"))) + { + File.Copy(server.GetProp("ServerPath") + "\\bedrock_server.exe", server.GetProp("ServerPath") + "\\" + server.GetProp("ServerExeName")); + } + } + if (_updater.CheckVersionChanged()) + _updater.MarkUpToDate(); + else + { + validating = false; + } + } + } + return true; + } + + public void RemoveBedrockServerByIndex(int serverIndex) + { + _bedrockServers.RemoveAt(serverIndex); + } + } +} diff --git a/BedrockService.backup/Service/Core/Interfaces/IBedrockService.cs b/BedrockService.backup/Service/Core/Interfaces/IBedrockService.cs new file mode 100644 index 00000000..4fadcf8e --- /dev/null +++ b/BedrockService.backup/Service/Core/Interfaces/IBedrockService.cs @@ -0,0 +1,22 @@ +using BedrockService.Service.Server; +using BedrockService.Shared.Interfaces; +using System.Collections.Generic; +using Topshelf; + +namespace BedrockService.Service.Core +{ + public interface IBedrockService : ServiceControl + { + IBedrockServer GetBedrockServerByIndex(int index); + + void RemoveBedrockServerByIndex(int serverIndex); + + IBedrockServer GetBedrockServerByName(string name); + + List GetAllServers(); + + void InitializeNewServer(IServerConfiguration serverConfiguration); + + void RestartService(); + } +} diff --git a/BedrockService.backup/Service/Core/Interfaces/IService.cs b/BedrockService.backup/Service/Core/Interfaces/IService.cs new file mode 100644 index 00000000..b4ef9e09 --- /dev/null +++ b/BedrockService.backup/Service/Core/Interfaces/IService.cs @@ -0,0 +1,10 @@ +using System.Threading.Tasks; + +namespace BedrockService.Service.Core +{ + public interface IService + { + Task InitializeHost(); + Topshelf.TopshelfExitCode Run(); + } +} diff --git a/BedrockService.backup/Service/Core/Interfaces/IServiceThread.cs b/BedrockService.backup/Service/Core/Interfaces/IServiceThread.cs new file mode 100644 index 00000000..7ce99394 --- /dev/null +++ b/BedrockService.backup/Service/Core/Interfaces/IServiceThread.cs @@ -0,0 +1,8 @@ +namespace BedrockService.Service.Core.Interfaces +{ + public interface IServiceThread + { + bool IsAlive(); + void CloseThread(); + } +} diff --git a/BedrockService.backup/Service/Core/Service.cs b/BedrockService.backup/Service/Core/Service.cs new file mode 100644 index 00000000..87decbd5 --- /dev/null +++ b/BedrockService.backup/Service/Core/Service.cs @@ -0,0 +1,74 @@ +using BedrockService.Service.Core.Interfaces; +using BedrockService.Service.Core.Threads; +using BedrockService.Service.Networking; +using BedrockService.Service.Server; +using BedrockService.Shared.Interfaces; +using System; +using System.Threading; +using System.Threading.Tasks; +using Topshelf; +using Topshelf.Runtime; + +namespace BedrockService.Service.Core +{ + public class Service : IService + { + private readonly IBedrockService _bedrockService; + private Host _host; + private readonly ILogger _logger; + + public Service(ILogger logger, IBedrockService bedrockService) + { + _logger = logger; + _bedrockService = bedrockService; + } + + public async Task InitializeHost() + { + await Task.Run(() => + { + _host = HostFactory.New(hostConfig => + { + hostConfig.SetStartTimeout(TimeSpan.FromSeconds(10)); + hostConfig.SetStopTimeout(TimeSpan.FromSeconds(10)); + hostConfig.UseAssemblyInfoForServiceInfo(); + hostConfig.Service(settings => _bedrockService, s => + { + s.BeforeStartingService(_ => _logger.AppendLine("Starting service...")); + s.BeforeStoppingService(_ => + { + _logger.AppendLine("Stopping service..."); + foreach (IBedrockServer server in _bedrockService.GetAllServers()) + { + server.SetServerStatus(BedrockServer.ServerStatus.Stopping); + while (server.GetServerStatus() != BedrockServer.ServerStatus.Stopped) + Thread.Sleep(100); + } + }); + }); + + hostConfig.RunAsLocalSystem(); + hostConfig.SetDescription("Windows Service Wrapper for Windows Bedrock Server"); + hostConfig.SetDisplayName("BedrockService"); + hostConfig.SetServiceName("BedrockService"); + hostConfig.UnhandledExceptionPolicy = UnhandledExceptionPolicyCode.LogErrorOnly; + + hostConfig.EnableServiceRecovery(src => + { + src.RestartService(delayInMinutes: 0); + src.RestartService(delayInMinutes: 1); + src.SetResetPeriod(days: 1); + }); + + hostConfig.OnException((ex) => + { + _logger.AppendLine("Exception occured Main : " + ex.Message); + }); + }); + + }); + } + + public TopshelfExitCode Run() => _host.Run(); + } +} diff --git a/BedrockService.backup/Service/Core/Threads/ClientServiceThread.cs b/BedrockService.backup/Service/Core/Threads/ClientServiceThread.cs new file mode 100644 index 00000000..4266082f --- /dev/null +++ b/BedrockService.backup/Service/Core/Threads/ClientServiceThread.cs @@ -0,0 +1,38 @@ +using BedrockService.Service.Core.Interfaces; +using System.Threading; + +namespace BedrockService.Service.Core.Threads +{ + class ClientServiceThread : IServiceThread + { + private Thread _clientService; + + public ClientServiceThread(ThreadStart methodToRun) + { + _clientService = new Thread(methodToRun) + { + Name = "ClientService", + IsBackground = true + }; + _clientService.Start(); + } + + public void CloseThread() + { + try + { + if (IsAlive()) + { + _clientService.Abort(); + } + _clientService = null; + } + catch (ThreadAbortException) { } + } + + public bool IsAlive() + { + return _clientService != null && _clientService.IsAlive; + } + } +} diff --git a/BedrockService.backup/Service/Core/Threads/HeartbeatThread.cs b/BedrockService.backup/Service/Core/Threads/HeartbeatThread.cs new file mode 100644 index 00000000..0f4fbe13 --- /dev/null +++ b/BedrockService.backup/Service/Core/Threads/HeartbeatThread.cs @@ -0,0 +1,31 @@ +using BedrockService.Service.Core.Interfaces; +using System.Threading; + +namespace BedrockService.Service.Core.Threads +{ + class HeartbeatThread : IServiceThread + { + private Thread _heartbeatThread; + + public HeartbeatThread(ThreadStart parameterizedThreadStart) + { + _heartbeatThread = new Thread(parameterizedThreadStart) + { + Name = "HeartbeatThread", + IsBackground = true + }; + _heartbeatThread.Start(); + } + + public void CloseThread() + { + _heartbeatThread.Abort(); + _heartbeatThread = null; + } + + public bool IsAlive() + { + return _heartbeatThread != null && _heartbeatThread.IsAlive; + } + } +} diff --git a/BedrockService.backup/Service/Core/Threads/ServerProcessThread.cs b/BedrockService.backup/Service/Core/Threads/ServerProcessThread.cs new file mode 100644 index 00000000..658d3fa9 --- /dev/null +++ b/BedrockService.backup/Service/Core/Threads/ServerProcessThread.cs @@ -0,0 +1,35 @@ +using BedrockService.Service.Core.Interfaces; +using System.Threading; + +namespace BedrockService.Service.Core.Threads +{ + class ServerProcessThread : IServiceThread + { + private Thread _networkListenerThread; + + public ServerProcessThread(ThreadStart methodToRun) + { + _networkListenerThread = new Thread(methodToRun) + { + Name = "ServerThread", + IsBackground = true + }; + _networkListenerThread.Start(); + } + + public void CloseThread() + { + if (IsAlive()) + { + _networkListenerThread.Abort(); + } + _networkListenerThread = null; + } + + + public bool IsAlive() + { + return _networkListenerThread != null && _networkListenerThread.IsAlive; + } + } +} diff --git a/BedrockService.backup/Service/Core/Threads/TCPThread.cs b/BedrockService.backup/Service/Core/Threads/TCPThread.cs new file mode 100644 index 00000000..6fd7e482 --- /dev/null +++ b/BedrockService.backup/Service/Core/Threads/TCPThread.cs @@ -0,0 +1,39 @@ +using BedrockService.Service.Core.Interfaces; +using System.Threading; +using System.Threading.Tasks; + +namespace BedrockService.Service.Core.Threads +{ + class TCPThread : IServiceThread + { + private Thread _clientService; + + public TCPThread(ThreadStart threadStart) + { + _clientService = null; + _clientService = new Thread(threadStart) + { + Name = "TCPListener", + IsBackground = true + }; + _clientService.Start(); + } + + public void CloseThread() + { + Task.Run(() => + { + if (IsAlive()) + { + _clientService.Abort(); + } + }); + _clientService = null; + } + + public bool IsAlive() + { + return _clientService != null && _clientService.IsAlive; + } + } +} diff --git a/BedrockService.backup/Service/Core/Threads/WatchdogThread.cs b/BedrockService.backup/Service/Core/Threads/WatchdogThread.cs new file mode 100644 index 00000000..ee08f4d4 --- /dev/null +++ b/BedrockService.backup/Service/Core/Threads/WatchdogThread.cs @@ -0,0 +1,33 @@ +using BedrockService.Service.Core.Interfaces; +using System.Threading; + +namespace BedrockService.Service.Core.Threads +{ + class WatchdogThread : IServiceThread + { + private Thread _thread; + + public WatchdogThread(ThreadStart parameterizedThreadStart) + { + _thread = new Thread(parameterizedThreadStart) + { + Name = "WatchdogThread", + IsBackground = true + }; + _thread.Start(); + } + + public void CloseThread() + { + _thread.Abort(); + _thread = null; + } + + public bool IsAlive() + { + return _thread != null && _thread.IsAlive; + } + } + +} + diff --git a/BedrockService.backup/Service/Logging/ServiceLogger.cs b/BedrockService.backup/Service/Logging/ServiceLogger.cs new file mode 100644 index 00000000..7d3237fe --- /dev/null +++ b/BedrockService.backup/Service/Logging/ServiceLogger.cs @@ -0,0 +1,102 @@ +using BedrockService.Shared.Interfaces; +using Newtonsoft.Json; +using System; +using System.IO; +using System.Text; + +namespace BedrockService.Service.Logging +{ + public class ServiceLogger : ILogger + { + private readonly IServiceConfiguration _serviceConfiguration; + private StringBuilder _outputString = new StringBuilder(); + [NonSerialized] + private readonly StreamWriter _logWriter; + private readonly bool _logToFile = false; + private readonly bool _logToConsole = false; + private readonly string _parent = "Service"; + private readonly string _logPath; + + public ServiceLogger(IProcessInfo processInfo, IServiceConfiguration serviceConfiguration) + { + _serviceConfiguration = serviceConfiguration; + _logPath = $@"{processInfo.GetDirectory()}\Service\Logs"; + _logToFile = bool.Parse(serviceConfiguration.GetProp("LogServiceToFile").ToString()); + _logToConsole = true; + if (_logToFile) + { + if (!Directory.Exists(_logPath)) + Directory.CreateDirectory(_logPath); + _logWriter = new StreamWriter($@"{_logPath}\ServiceLog_{_parent}_{DateTime.Now:yyyymmddhhmmss}.log", true); + } + } + + [JsonConstructor] + public ServiceLogger(IServiceConfiguration serviceConfiguration, string serverName) + { + _serviceConfiguration = serviceConfiguration; + _parent = serverName; + _logToFile = false; + _logToConsole = false; + } + + public void AppendLine(string text) + { + try + { + _serviceConfiguration.GetLog().Add(text); + if (_logToFile && _logWriter != null) + { + _logWriter.WriteLine(text); + _logWriter.Flush(); + } + if (_logToConsole) + Console.WriteLine(text); + } + catch + { + } + } + + public void AppendText(string text) + { + try + { + _serviceConfiguration.GetLog().Add(text); + if (_logToFile && _logWriter != null) + { + _logWriter.Write(text); + _logWriter.Flush(); + } + if (_logToConsole) + { + Console.Write(text); + Console.Out.Flush(); + } + } + catch + { + } + } + + public int Count() + { + return _serviceConfiguration.GetLog().Count; + } + + public string FromIndex(int index) + { + return _serviceConfiguration.GetLog()[index]; + } + + public override string ToString() + { + _outputString = new StringBuilder(); + foreach (string s in _serviceConfiguration.GetLog()) + { + _outputString.Append(s); + } + return _outputString.ToString(); + } + } +} diff --git a/BedrockService.backup/Service/Management/ConfigManager.cs b/BedrockService.backup/Service/Management/ConfigManager.cs new file mode 100644 index 00000000..9a8900e5 --- /dev/null +++ b/BedrockService.backup/Service/Management/ConfigManager.cs @@ -0,0 +1,540 @@ +using BedrockService.Service.Server; +using BedrockService.Shared.Classes; +using BedrockService.Shared.Interfaces; +using BedrockService.Shared.Utilities; +using System; +using System.Collections.Generic; +using System.IO; +using System.IO.Compression; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; +using System.Runtime.Serialization.Formatters.Binary; + +namespace BedrockService.Service.Management +{ + public class ConfigManager : IConfigurator + { + private readonly string _configDir; + private readonly string _globalFile; + private readonly string _clientKeyPath; + private readonly string _commsKeyPath; + private string _loadedVersion; + private static readonly object _fileLock = new object(); + private readonly IServiceConfiguration _serviceConfiguration; + private readonly IProcessInfo _processInfo; + private readonly ILogger _logger; + private CommsKeyContainer _keyContainer; + private RSAParameters _serviceKey; + private RSAParameters _clientKey; + + public ConfigManager(IProcessInfo processInfo, IServiceConfiguration serviceConfiguration, ILogger logger) + { + _processInfo = processInfo; + _serviceConfiguration = serviceConfiguration; + _logger = logger; + _configDir = $@"{_processInfo.GetDirectory()}\Server\Configs"; + _globalFile = $@"{_processInfo.GetDirectory()}\Service\Globals.conf"; + _clientKeyPath = $@"{_processInfo.GetDirectory()}\Client\ClientKey.dat"; + _commsKeyPath = $@"{_processInfo.GetDirectory()}\Service\CommsKey.dat"; + } + + public async Task LoadAllConfigurations() + { + await Task.Run(() => + { + BinaryFormatter formatter = new BinaryFormatter(); + if (!Directory.Exists(_configDir)) + Directory.CreateDirectory(_configDir); + if (!Directory.Exists($@"{_configDir}\KnownPlayers\Backups")) + Directory.CreateDirectory($@"{_configDir}\KnownPlayers\Backups"); + if (!Directory.Exists($@"{_configDir}\RegisteredPlayers\Backups")) + Directory.CreateDirectory($@"{_configDir}\RegisteredPlayers\Backups"); + if (!Directory.Exists($@"{_configDir}\Backups")) + Directory.CreateDirectory($@"{_configDir}\Backups"); + if (File.Exists($@"{_configDir}\..\bedrock_ver.ini")) + _loadedVersion = File.ReadAllText($@"{_configDir}\..\bedrock_ver.ini"); + if(!File.Exists(_commsKeyPath)) + { + RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(2048); + using (AesCryptoServiceProvider aes = new AesCryptoServiceProvider()) + { + CommsKeyContainer serviceKeys = new CommsKeyContainer(); + CommsKeyContainer clientKeys = new CommsKeyContainer(); + serviceKeys.LocalPrivateKey.SetPrivateKey(rsa.ExportParameters(true)); + clientKeys.RemotePublicKey.SetPublicKey(rsa.ExportParameters(false)); + rsa = new RSACryptoServiceProvider(2048); + clientKeys.LocalPrivateKey.SetPrivateKey(rsa.ExportParameters(true)); + serviceKeys.RemotePublicKey.SetPublicKey(rsa.ExportParameters(false)); + aes.GenerateKey(); + aes.GenerateIV(); + serviceKeys.AesKey = aes.Key; + clientKeys.AesKey = aes.Key; + serviceKeys.AesIV = aes.IV; + clientKeys.AesIV = aes.IV; + formatter.Serialize(File.Create(_clientKeyPath), clientKeys); + formatter.Serialize(File.Create(_commsKeyPath), serviceKeys); + } + rsa.Clear(); + rsa.Dispose(); + } + else + { + try + { + _keyContainer = (CommsKeyContainer)formatter.Deserialize(File.Open(_commsKeyPath, FileMode.Open)); + _serviceKey = _keyContainer.LocalPrivateKey.GetPrivateKey(); + _clientKey = _keyContainer.RemotePublicKey.GetPrivateKey(); + } + catch + { + _logger.AppendLine("Error loading Encryption keys!"); + } + } + + ServerInfo serverInfo; + LoadGlobals(); + _serviceConfiguration.GetServerList().Clear(); + string[] files = Directory.GetFiles(_configDir, "*.conf"); + foreach (string file in files) + { + FileInfo FInfo = new FileInfo(file); + string[] fileEntries = File.ReadAllLines(file); + serverInfo = new ServerInfo(fileEntries, _serviceConfiguration.GetProp("ServersPath").ToString()); + LoadPlayerDatabase(serverInfo); + LoadRegisteredPlayers(serverInfo); + _serviceConfiguration.AddNewServerInfo(serverInfo); + } + if (_serviceConfiguration.GetServerList().Count == 0) + { + serverInfo = new ServerInfo(null, _serviceConfiguration.GetProp("ServersPath").ToString()); + serverInfo.InitializeDefaults(); + SaveServerProps(serverInfo, true); + _serviceConfiguration.AddNewServerInfo(serverInfo); + } + }); + } + + public void SaveGlobalFile() + { + string[] output = new string[_serviceConfiguration.GetAllProps().Count + 3]; + int index = 0; + output[index++] = "#Globals"; + output[index++] = string.Empty; + foreach (Property prop in _serviceConfiguration.GetAllProps()) + { + output[index++] = $"{prop.KeyName}={prop}"; + } + output[index++] = string.Empty; + + File.WriteAllLines(_globalFile, output); + } + + public void LoadRegisteredPlayers(IServerConfiguration server) + { + string serverName = server.GetServerName(); + string filePath = $@"{_configDir}\RegisteredPlayers\{serverName}.preg"; + if (!File.Exists(filePath)) + { + File.Create(filePath).Close(); + return; + } + foreach (string entry in File.ReadLines(filePath)) + { + if (entry.StartsWith("#") || string.IsNullOrWhiteSpace(entry)) + continue; + string[] split = entry.Split(','); + _logger.AppendLine($"Server \"{server.GetServerName()}\" Loaded registered player: {split[1]}"); + IPlayer playerFound = server.GetPlayerByXuid(split[0]); + if (playerFound == null) + { + server.AddUpdatePlayer(new Player(split[0], split[1], DateTime.Now.Ticks.ToString(), "0", "0", split[3].ToLower() == "true", split[2], split[4].ToLower() == "true")); + continue; + } + string[] playerTimes = playerFound.GetTimes(); + server.AddUpdatePlayer(new Player(split[0], split[1], playerTimes[0], playerTimes[1], playerTimes[2], split[3].ToLower() == "true", split[2], split[4].ToLower() == "true")); + } + } + + private void LoadGlobals() + { + if (File.Exists(_globalFile)) + { + _logger.AppendLine("Loading Globals..."); + _serviceConfiguration.ProcessConfiguration(File.ReadAllLines(_globalFile)); + _serviceConfiguration.SetServerVersion(_loadedVersion); + return; + } + _serviceConfiguration.InitializeDefaults(); + _logger.AppendLine("Globals.conf was not found. Loaded defaults and saved to file."); + SaveGlobalFile(); + } + + private int RoundOff(int i) + { + return ((int)Math.Round(i / 10.0)) * 10; + } + + public async Task ReplaceServerBuild(IServerConfiguration server) + { + await Task.Run(() => + { + try + { + if (!Directory.Exists(server.GetProp("ServerPath").ToString())) + Directory.CreateDirectory(server.GetProp("ServerPath").ToString()); + while (_serviceConfiguration.GetServerVersion() == null || _serviceConfiguration.GetServerVersion() == "None") + { + Thread.Sleep(150); + } + using (ZipArchive archive = ZipFile.OpenRead($@"{_processInfo.GetDirectory()}\Server\MCSFiles\Update_{ _serviceConfiguration.GetServerVersion()}.zip")) + { + int fileCount = archive.Entries.Count; + for(int i=0; i 2) + { + sb.Remove(sb.Length - 2, 2); + } + sb.Append("\n]"); + File.WriteAllText($@"{server.GetProp("ServerPath")}\whitelist.json", sb.ToString()); + sb = new StringBuilder(); + sb.Append("[\n"); + + foreach (Player player in server.GetPlayerList()) + { + string[] playerReg = player.GetRegistration(); + if (!player.IsDefaultRegistration() && playerReg[0] == "False") + { + sb.Append("\t{\n"); + sb.Append($"\t\t\"permission\": \"{playerReg[1]}\",\n"); + sb.Append($"\t\t\"xuid\": \"{player.GetXUID()}\"\n"); + sb.Append("\t},\n"); + } + } + if (sb.Length > 2) + { + sb.Remove(sb.Length - 2, 2); + } + sb.Append("\n]"); + File.WriteAllText($@"{server.GetProp("ServerPath")}\permissions.json", sb.ToString()); + } + + public void SaveServerProps(IServerConfiguration server, bool SaveServerInfo) + { + int index = 0; + string[] output = new string[5 + server.GetAllProps().Count + server.GetStartCommands().Count]; + output[index++] = "#Server"; + foreach (Property prop in server.GetAllProps()) + { + output[index++] = $"{prop.KeyName}={prop}"; + } + if (!SaveServerInfo) + { + if (!Directory.Exists(server.GetProp("ServerPath").ToString())) + { + Directory.CreateDirectory(server.GetProp("ServerPath").ToString()); + } + File.WriteAllLines($@"{server.GetProp("ServerPath")}\server.properties", output); + } + else + { + output[index++] = string.Empty; + output[index++] = "#StartCmds"; + + foreach (StartCmdEntry startCmd in server.GetStartCommands()) + { + output[index++] = $"AddStartCmd={startCmd.Command}"; + } + output[index++] = string.Empty; + + File.WriteAllLines($@"{_configDir}\{server.GetFileName()}", output); + if (server.GetProp("ServerPath").ToString() == null) + server.GetProp("ServerPath").SetValue(server.GetProp("ServerPath").DefaultValue); + if (!Directory.Exists(server.GetProp("ServerPath").ToString())) + { + Directory.CreateDirectory(server.GetProp("ServerPath").ToString()); + } + File.WriteAllLines($@"{server.GetProp("ServerPath")}\server.properties", output); + } + } + + public void RemoveServerConfigs(IServerConfiguration serverInfo, NetworkMessageFlags flag) + { + try + { + File.Delete($@"{_configDir}\{serverInfo.GetFileName()}"); + switch (flag) + { + case NetworkMessageFlags.RemoveBckPly: + if (DeleteBackups(serverInfo)) + _logger.AppendLine($"Deleted Backups for server {serverInfo.GetServerName()}"); + if (DeletePlayerFiles(serverInfo)) + _logger.AppendLine($"Deleted Player files for server {serverInfo.GetServerName()}"); + break; + case NetworkMessageFlags.RemoveBckSrv: + if (DeleteBackups(serverInfo)) + _logger.AppendLine($"Deleted Backups for server {serverInfo.GetServerName()}"); + if (DeleteServerFiles(serverInfo)) + _logger.AppendLine($"Deleted server directory for server {serverInfo.GetServerName()}"); + break; + case NetworkMessageFlags.RemovePlySrv: + if (DeletePlayerFiles(serverInfo)) + _logger.AppendLine($"Deleted Player files for server {serverInfo.GetServerName()}"); + if (DeleteServerFiles(serverInfo)) + _logger.AppendLine($"Deleted server directory for server {serverInfo.GetServerName()}"); + break; + case NetworkMessageFlags.RemoveSrv: + if (DeleteServerFiles(serverInfo)) + _logger.AppendLine($"Deleted server directory for server {serverInfo.GetServerName()}"); + break; + case NetworkMessageFlags.RemovePlayers: + if (DeletePlayerFiles(serverInfo)) + _logger.AppendLine($"Deleted Player files for server {serverInfo.GetServerName()}"); + break; + case NetworkMessageFlags.RemoveBackups: + if (DeleteBackups(serverInfo)) + _logger.AppendLine($"Deleted Backups for server {serverInfo.GetServerName()}"); + break; + case NetworkMessageFlags.RemoveAll: + if (DeleteBackups(serverInfo)) + _logger.AppendLine($"Deleted Backups for server {serverInfo.GetServerName()}"); + if (DeletePlayerFiles(serverInfo)) + _logger.AppendLine($"Deleted Player files for server {serverInfo.GetServerName()}"); + if (DeleteServerFiles(serverInfo)) + _logger.AppendLine($"Deleted server directory for server {serverInfo.GetServerName()}"); + break; + case NetworkMessageFlags.None: + break; + } + _serviceConfiguration.RemoveServerInfo(serverInfo); + + } + catch { } + } + + public List EnumerateBackupsForServer(byte serverIndex) + { + string serverName = _serviceConfiguration.GetServerInfoByIndex(serverIndex).GetServerName(); + List newList = new List(); + try + { + foreach (DirectoryInfo dir in new DirectoryInfo($@"{_serviceConfiguration.GetProp("BackupPath")}\{serverName}").GetDirectories()) + { + string[] splitName = dir.Name.Split('_'); + newList.Add(new Property(dir.Name, new DateTime(long.Parse(splitName[1])).ToString("G"))); + } + } + catch (IOException) + { + return newList; + } + return newList; + } + + public void DeleteBackupsForServer(byte serverIndex, List list) + { + string serverName = _serviceConfiguration.GetServerInfoByIndex(serverIndex).GetServerName(); + try + { + foreach (string deleteDir in list) + foreach (DirectoryInfo dir in new DirectoryInfo($@"{_serviceConfiguration.GetProp("BackupPath")}\{serverName}").GetDirectories()) + if (dir.Name == deleteDir) + { + new FileUtils(_processInfo.GetDirectory()).DeleteFilesRecursively(new DirectoryInfo($@"{_serviceConfiguration.GetProp("BackupPath")}\{serverName}\{deleteDir}"), true); + _logger.AppendLine($"Deleted backup {deleteDir}."); + } + } + catch (IOException e) + { + _logger.AppendLine($"Error deleting selected backups! {e.Message}"); + } + } + + private bool DeleteBackups(IServerConfiguration serverInfo) + { + try + { + string configBackupPath = ""; + DirectoryInfo backupDirInfo = new DirectoryInfo($@"{configBackupPath}\{serverInfo.GetServerName()}"); + DirectoryInfo configBackupDirInfo = new DirectoryInfo($@"{_configDir}\Backups"); + foreach (DirectoryInfo dir in backupDirInfo.GetDirectories()) + { + if (dir.Name.Contains($"{serverInfo.GetServerName()}")) + { + dir.Delete(true); + } + } + foreach (FileInfo file in configBackupDirInfo.GetFiles()) + { + if (file.Name.Contains($"{serverInfo.GetServerName()}_")) + { + file.Delete(); + } + } + return true; + } + catch { return false; } + } + + private bool DeleteServerFiles(IServerConfiguration serverInfo) + { + try + { + new FileUtils(_processInfo.GetDirectory()).DeleteFilesRecursively(new DirectoryInfo(serverInfo.GetProp("ServerPath").ToString()), false); + return true; + } + catch { return false; } + } + + private bool DeletePlayerFiles(IServerConfiguration serverInfo) + { + try + { + DirectoryInfo configDirInfo = new DirectoryInfo(_configDir); + foreach (DirectoryInfo dir in configDirInfo.GetDirectories()) + { + if (dir.Name == "KnownPlayers" || dir.Name == "RegisteredPlayers") + { + foreach (FileInfo file in dir.GetFiles()) + { + if (file.Name.Contains($"{serverInfo.GetServerName()}")) + { + file.Delete(); + } + } + } + } + return true; + } + catch { return false; } + } + + public CommsKeyContainer GetKeyContainer() => _keyContainer; + + public Task LoadConfiguration(IConfiguration configuration) + { + throw new NotImplementedException(); + } + + public Task SaveConfiguration(IConfiguration configuration) + { + throw new NotImplementedException(); + } + } +} + diff --git a/BedrockService.backup/Service/Management/IConfigurator.cs b/BedrockService.backup/Service/Management/IConfigurator.cs new file mode 100644 index 00000000..f783eb53 --- /dev/null +++ b/BedrockService.backup/Service/Management/IConfigurator.cs @@ -0,0 +1,26 @@ +using BedrockService.Service.Server; +using BedrockService.Shared.Classes; +using BedrockService.Shared.Interfaces; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace BedrockService.Service.Management +{ + public interface IConfigurator + { + void DeleteBackupsForServer(byte serverIndex, List list); + List EnumerateBackupsForServer(byte serverIndex); + Task LoadAllConfigurations(); + Task LoadConfiguration(IConfiguration configuration); + void LoadPlayerDatabase(IServerConfiguration server); + void LoadRegisteredPlayers(IServerConfiguration server); + void RemoveServerConfigs(IServerConfiguration serverInfo, NetworkMessageFlags flag); + Task ReplaceServerBuild(IServerConfiguration server); + Task SaveConfiguration(IConfiguration configuration); + void SaveGlobalFile(); + void SaveKnownPlayerDatabase(IServerConfiguration server); + void SaveServerProps(IServerConfiguration server, bool SaveServerInfo); + void WriteJSONFiles(IServerConfiguration server); + CommsKeyContainer GetKeyContainer(); + } +} \ No newline at end of file diff --git a/BedrockService.backup/Service/Networking/ITCPListener.cs b/BedrockService.backup/Service/Networking/ITCPListener.cs new file mode 100644 index 00000000..f726dc8f --- /dev/null +++ b/BedrockService.backup/Service/Networking/ITCPListener.cs @@ -0,0 +1,17 @@ +using BedrockService.Service.Networking.NetworkMessageClasses; +using BedrockService.Shared.Classes; +using System.Collections.Generic; + +namespace BedrockService.Service.Networking +{ + public interface ITCPListener : IMessageSender + { + void StartListening(); + + void ResetListener(); + + void SetStrategyDictionaries(Dictionary standard, Dictionary flagged); + + void SetKeyContainer(CommsKeyContainer keyContainer); + } +} diff --git a/BedrockService.backup/Service/Networking/IUpdater.cs b/BedrockService.backup/Service/Networking/IUpdater.cs new file mode 100644 index 00000000..e436d0d4 --- /dev/null +++ b/BedrockService.backup/Service/Networking/IUpdater.cs @@ -0,0 +1,14 @@ +using BedrockService.Shared.Interfaces; +using System.Threading.Tasks; + +namespace BedrockService.Service.Networking +{ + public interface IUpdater + { + Task CheckUpdates(); + Task FetchBuild(string path, string version); + bool CheckVersionChanged(); + void MarkUpToDate(); + Task ReplaceBuild(IServerConfiguration serverConfiguration); + } +} \ No newline at end of file diff --git a/BedrockService.backup/Service/Networking/MessageInterfaces/IFlaggedMessageParser.cs b/BedrockService.backup/Service/Networking/MessageInterfaces/IFlaggedMessageParser.cs new file mode 100644 index 00000000..83833ec2 --- /dev/null +++ b/BedrockService.backup/Service/Networking/MessageInterfaces/IFlaggedMessageParser.cs @@ -0,0 +1,9 @@ +using BedrockService.Shared.Classes; + +namespace BedrockService.Service.Networking.NetworkMessageClasses +{ + public interface IFlaggedMessageParser + { + void ParseMessage(byte[] data, byte serverIndex, NetworkMessageFlags flag); + } +} diff --git a/BedrockService.backup/Service/Networking/MessageInterfaces/IMessageParser.cs b/BedrockService.backup/Service/Networking/MessageInterfaces/IMessageParser.cs new file mode 100644 index 00000000..22b39d28 --- /dev/null +++ b/BedrockService.backup/Service/Networking/MessageInterfaces/IMessageParser.cs @@ -0,0 +1,7 @@ +namespace BedrockService.Service.Networking.NetworkMessageClasses +{ + public interface IMessageParser + { + void ParseMessage(byte[] data, byte serverIndex); + } +} diff --git a/BedrockService.backup/Service/Networking/MessageInterfaces/IMessageSender.cs b/BedrockService.backup/Service/Networking/MessageInterfaces/IMessageSender.cs new file mode 100644 index 00000000..1d7aaf13 --- /dev/null +++ b/BedrockService.backup/Service/Networking/MessageInterfaces/IMessageSender.cs @@ -0,0 +1,19 @@ +using BedrockService.Shared.Classes; + +namespace BedrockService.Service.Networking.NetworkMessageClasses +{ + public interface IMessageSender + { + void SendData(byte[] bytes, NetworkMessageSource source, NetworkMessageDestination destination, byte serverIndex, NetworkMessageTypes type, NetworkMessageFlags status); + + void SendData(NetworkMessageSource source, NetworkMessageDestination destination, NetworkMessageTypes type); + + void SendData(NetworkMessageSource source, NetworkMessageDestination destination, byte serverIndex, NetworkMessageTypes type); + + void SendData(byte[] bytes, NetworkMessageSource source, NetworkMessageDestination destination, byte serverIndex, NetworkMessageTypes type); + + void SendData(byte[] bytes, NetworkMessageSource source, NetworkMessageDestination destination, NetworkMessageTypes type); + + void SendData(NetworkMessageSource source, NetworkMessageDestination destination, NetworkMessageTypes type, NetworkMessageFlags status); + } +} \ No newline at end of file diff --git a/BedrockService.backup/Service/Networking/NetworkStrategyLookup.cs b/BedrockService.backup/Service/Networking/NetworkStrategyLookup.cs new file mode 100644 index 00000000..4f5ce7a6 --- /dev/null +++ b/BedrockService.backup/Service/Networking/NetworkStrategyLookup.cs @@ -0,0 +1,654 @@ +using BedrockService.Service.Core; +using BedrockService.Service.Management; +using BedrockService.Service.Networking.NetworkMessageClasses; +using BedrockService.Service.Server; +using BedrockService.Shared.Classes; +using BedrockService.Shared.Interfaces; +using BedrockService.Shared.PackParser; +using BedrockService.Shared.Utilities; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; + +namespace BedrockService.Service.Networking +{ + public class NetworkStrategyLookup + { + private readonly Dictionary _standardMessageLookup; + private readonly Dictionary _flaggedMessageLookup; + + public NetworkStrategyLookup(ITCPListener messageSender, IBedrockService service, ILogger logger, IConfigurator configurator, IServiceConfiguration serviceConfiguration, IProcessInfo processInfo, IUpdater updater) + { + _standardMessageLookup = new Dictionary() + { + {NetworkMessageTypes.DelBackups, new DeleteBackups(configurator) }, + {NetworkMessageTypes.BackupAll, new ServerBackupAll(messageSender, service) }, + {NetworkMessageTypes.EnumBackups, new EnumBackups(configurator, messageSender) }, + {NetworkMessageTypes.Backup, new ServerBackup(messageSender, service) }, + {NetworkMessageTypes.PropUpdate, new ServerPropUpdate(configurator, serviceConfiguration, messageSender, service) }, + {NetworkMessageTypes.Restart, new ServerRestart(messageSender, service) }, + {NetworkMessageTypes.Command, new ServerCommand(messageSender, service, logger) }, + {NetworkMessageTypes.PackList, new PackList(messageSender, processInfo, serviceConfiguration, logger) }, + {NetworkMessageTypes.RemovePack, new RemovePack(messageSender, processInfo, serviceConfiguration) }, + {NetworkMessageTypes.PackFile, new PackFile(serviceConfiguration, processInfo, logger) }, + {NetworkMessageTypes.Connect, new Connect(messageSender, serviceConfiguration) }, + {NetworkMessageTypes.StartCmdUpdate, new StartCmdUpdate(configurator, serviceConfiguration) }, + {NetworkMessageTypes.CheckUpdates, new CheckUpdates(messageSender, updater) }, + {NetworkMessageTypes.BackupRollback, new BackupRollback(messageSender, service) }, + {NetworkMessageTypes.AddNewServer, new AddNewServer(configurator, messageSender, serviceConfiguration, service) }, + {NetworkMessageTypes.ConsoleLogUpdate, new ConsoleLogUpdate(messageSender, serviceConfiguration, service) }, + {NetworkMessageTypes.PlayersRequest, new PlayerRequest(messageSender, serviceConfiguration) }, + {NetworkMessageTypes.PlayersUpdate, new PlayersUpdate(configurator, messageSender, serviceConfiguration, service) }, + {NetworkMessageTypes.LevelEditRequest, new LevelEditRequest(messageSender, serviceConfiguration) }, + {NetworkMessageTypes.LevelEditFile, new LevelEditFile(serviceConfiguration, service) } + }; + _flaggedMessageLookup = new Dictionary() + { + {NetworkMessageTypes.RemoveServer, new RemoveServer(configurator, messageSender, serviceConfiguration, service) }, + }; + messageSender.SetStrategyDictionaries(_standardMessageLookup, _flaggedMessageLookup); + } + + class DeleteBackups : IMessageParser + { + private readonly IConfigurator _configurator; + + public DeleteBackups(IConfigurator configurator) + { + _configurator = configurator; + } + + public void ParseMessage(byte[] data, byte serverIndex) + { + JsonSerializerSettings settings = new JsonSerializerSettings() + { + TypeNameHandling = TypeNameHandling.All + }; + string stringData = Encoding.UTF8.GetString(data, 5, data.Length - 5); + List backupFileNames = JsonConvert.DeserializeObject>(stringData, settings); + _configurator.DeleteBackupsForServer(serverIndex, backupFileNames); + } + } + + class ServerBackupAll : IMessageParser + { + private readonly IMessageSender _messageSender; + private readonly IBedrockService _service; + + public ServerBackupAll(IMessageSender messageSender, IBedrockService service) + { + _messageSender = messageSender; + _service = service; + } + + public void ParseMessage(byte[] data, byte serverIndex) + { + foreach (IBedrockServer server in _service.GetAllServers()) + server.RestartServer(true); + _messageSender.SendData(NetworkMessageSource.Service, NetworkMessageDestination.Client, NetworkMessageTypes.UICallback); + } + } + + class ServerBackup : IMessageParser + { + private readonly IMessageSender _messageSender; + private readonly IBedrockService _service; + public ServerBackup(IMessageSender messageSender, IBedrockService service) + { + _messageSender = messageSender; + _service = service; + } + + public void ParseMessage(byte[] data, byte serverIndex) + { + _service.GetBedrockServerByIndex(serverIndex).RestartServer(true); + _messageSender.SendData(NetworkMessageSource.Service, NetworkMessageDestination.Client, NetworkMessageTypes.UICallback); + } + } + + class EnumBackups : IMessageParser + { + private readonly IConfigurator _configurator; + private readonly IMessageSender _messageSender; + public EnumBackups(IConfigurator configurator, IMessageSender messageSender) + { + _configurator = configurator; + _messageSender = messageSender; + } + + public void ParseMessage(byte[] data, byte serverIndex) + { + Formatting indented = Formatting.Indented; + JsonSerializerSettings settings = new JsonSerializerSettings() + { + TypeNameHandling = TypeNameHandling.All + }; + byte[] serializeToBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(_configurator.EnumerateBackupsForServer(serverIndex), indented, settings)); + _messageSender.SendData(serializeToBytes, NetworkMessageSource.Service, NetworkMessageDestination.Client, NetworkMessageTypes.EnumBackups); + + } + } + + class ServerPropUpdate : IMessageParser + { + private readonly IServiceConfiguration _serviceConfiguration; + private readonly IMessageSender _messageSender; + private readonly IBedrockService _bedrockService; + private readonly IConfigurator _configurator; + + public ServerPropUpdate(IConfigurator configurator, IServiceConfiguration serviceConfiguration, IMessageSender messageSender, IBedrockService bedrockService) + { + _configurator = configurator; + _serviceConfiguration = serviceConfiguration; + _messageSender = messageSender; + _bedrockService = bedrockService; + } + + public void ParseMessage(byte[] data, byte serverIndex) + { + JsonSerializerSettings settings = new JsonSerializerSettings() + { + TypeNameHandling = TypeNameHandling.All + }; + string stringData = Encoding.UTF8.GetString(data, 5, data.Length - 5); + List propList = JsonConvert.DeserializeObject>(stringData, settings); + Property prop = propList.FirstOrDefault(p => p.KeyName == "server-name"); + if(prop == null) + { + _serviceConfiguration.SetAllProps(propList); + _configurator.SaveGlobalFile(); + _bedrockService.RestartService(); + _messageSender.SendData(NetworkMessageSource.Service, NetworkMessageDestination.Client, NetworkMessageTypes.UICallback); + return; + } + _serviceConfiguration.GetServerInfoByIndex(serverIndex).SetAllProps(propList); + _bedrockService.GetBedrockServerByIndex(serverIndex).SetServerStatus(BedrockServer.ServerStatus.Stopping); + while (_bedrockService.GetBedrockServerByIndex(serverIndex).GetServerStatus() == BedrockServer.ServerStatus.Stopping) + { + Thread.Sleep(100); + } + _bedrockService.GetBedrockServerByIndex(serverIndex).SetServerStatus(BedrockServer.ServerStatus.Starting); + _messageSender.SendData(NetworkMessageSource.Service, NetworkMessageDestination.Client, NetworkMessageTypes.UICallback); + } + } + + class ServerRestart : IMessageParser + { + private readonly IMessageSender _messageSender; + private readonly IBedrockService _service; + public ServerRestart(IMessageSender messageSender, IBedrockService service) + { + _messageSender = messageSender; + _service = service; + } + + public void ParseMessage(byte[] data, byte serverIndex) + { + _service.GetBedrockServerByIndex(serverIndex).RestartServer(false); + _messageSender.SendData(NetworkMessageSource.Service, NetworkMessageDestination.Client, NetworkMessageTypes.UICallback); + } + } + + class ServerCommand : IMessageParser + { + private readonly IBedrockService _service; + private readonly ILogger _logger; + private readonly IMessageSender _messageSender; + + public ServerCommand(IMessageSender messageSender, IBedrockService service, ILogger logger) + { + _messageSender = messageSender; + _service = service; + _logger = logger; + } + + public void ParseMessage(byte[] data, byte serverIndex) + { + string stringData = Encoding.UTF8.GetString(data, 5, data.Length - 5); + _service.GetBedrockServerByIndex(serverIndex).WriteToStandardIn(stringData); + _logger.AppendLine($"Sent command {stringData} to stdInput stream"); + _messageSender.SendData(NetworkMessageSource.Service, NetworkMessageDestination.Client, NetworkMessageTypes.UICallback); + } + } + + class PackList : IMessageParser + { + private readonly IMessageSender _messageSender; + private readonly IServiceConfiguration _serviceConfiguration; + private readonly IProcessInfo _processInfo; + private readonly ILogger _logger; + + public PackList(IMessageSender messageSender, IProcessInfo processInfo, IServiceConfiguration serviceConfiguration, ILogger logger) + { + _logger = logger; + _messageSender = messageSender; + _serviceConfiguration = serviceConfiguration; + _processInfo = processInfo; + } + + public void ParseMessage(byte[] data, byte serverIndex) + { + if (!File.Exists($@"{_processInfo.GetDirectory()}\Server\stock_packs.json")) + File.Copy($@"{_serviceConfiguration.GetServerInfoByIndex(serverIndex).GetProp("ServerPath")}\valid_known_packs.json", $@"{_processInfo.GetDirectory()}\Server\stock_packs.json"); + MinecraftKnownPacksClass knownPacks = new MinecraftKnownPacksClass($@"{_serviceConfiguration.GetServerInfoByIndex(serverIndex).GetProp("ServerPath")}\valid_known_packs.json", $@"{_processInfo.GetDirectory()}\Server\stock_packs.json"); + List list = new List(); + foreach (MinecraftKnownPacksClass.KnownPack pack in knownPacks.KnownPacks) + { + MinecraftPackParser currentParser = new MinecraftPackParser(_logger, _processInfo); + currentParser.ParseDirectory(new DirectoryInfo($@"{_serviceConfiguration.GetServerInfoByIndex(serverIndex).GetProp("ServerPath")}\{pack.path.Replace(@"/", @"\")}")); + list.Add(currentParser); + } + string arrayString = JArray.FromObject(list).ToString(); + _messageSender.SendData(Encoding.UTF8.GetBytes(arrayString), NetworkMessageSource.Server, NetworkMessageDestination.Client, NetworkMessageTypes.PackList); + } + } + + class RemovePack : IMessageParser + { + private readonly IMessageSender _messageSender; + private readonly IServiceConfiguration _serviceConfiguration; + private readonly IProcessInfo _processInfo; + + public RemovePack(IMessageSender messageSender, IProcessInfo processInfo, IServiceConfiguration serviceConfiguration) + { + _messageSender = messageSender; + _serviceConfiguration = serviceConfiguration; + _processInfo = processInfo; + } + + public void ParseMessage(byte[] data, byte serverIndex) + { + string stringData = Encoding.UTF8.GetString(data, 5, data.Length - 5); + MinecraftKnownPacksClass knownPacks = new MinecraftKnownPacksClass($@"{_serviceConfiguration.GetServerInfoByIndex(serverIndex).GetProp("ServerPath")}\valid_known_packs.json", $@"{_processInfo.GetDirectory()}\Server\stock_packs.json"); + JsonSerializerSettings settings = new JsonSerializerSettings() + { + TypeNameHandling = TypeNameHandling.All + }; + List MCContainer = JsonConvert.DeserializeObject>(stringData, settings); + foreach (MinecraftPackContainer cont in MCContainer) + knownPacks.RemovePackFromServer(_serviceConfiguration.GetServerInfoByIndex(serverIndex).GetProp("ServerPath").ToString(), cont); + _messageSender.SendData(NetworkMessageSource.Service, NetworkMessageDestination.Client, NetworkMessageTypes.UICallback); + } + } + + class LevelEditRequest : IMessageParser + { + private readonly IMessageSender _messageSender; + private readonly IServiceConfiguration _serviceConfiguration; + + public LevelEditRequest(IMessageSender messageSender, IServiceConfiguration serviceConfiguration) + { + _messageSender = messageSender; + _serviceConfiguration = serviceConfiguration; + } + + public void ParseMessage(byte[] data, byte serverIndex) + { + IServerConfiguration server = _serviceConfiguration.GetServerInfoByIndex(serverIndex); + string pathToLevelDat = $@"{_serviceConfiguration.GetServerInfoByIndex(serverIndex).GetProp("ServerPath")}\worlds\{server.GetProp("level-name")}\level.dat"; + byte[] levelDatToBytes = File.ReadAllBytes(pathToLevelDat); + _messageSender.SendData(levelDatToBytes, NetworkMessageSource.Server, NetworkMessageDestination.Client, NetworkMessageTypes.LevelEditFile); + } + } + + class PlayersUpdate : IMessageParser + { + private readonly IMessageSender _messageSender; + private readonly IServiceConfiguration _serviceConfiguration; + private readonly IConfigurator _configurator; + private readonly IBedrockService _service; + + public PlayersUpdate(IConfigurator configurator, IMessageSender messageSender, IServiceConfiguration serviceConfiguration, IBedrockService service) + { + _service = service; + _configurator = configurator; + _messageSender = messageSender; + _serviceConfiguration = serviceConfiguration; + } + + public void ParseMessage(byte[] data, byte serverIndex) + { + string stringData = Encoding.UTF8.GetString(data, 5, data.Length - 5); + JsonSerializerSettings settings = new JsonSerializerSettings() + { + TypeNameHandling = TypeNameHandling.All + }; + List fetchedPlayers = JsonConvert.DeserializeObject>(stringData, settings); + foreach (IPlayer player in fetchedPlayers) + { + try + { + _serviceConfiguration.GetServerInfoByIndex(serverIndex).AddUpdatePlayer(player); + } + catch (Exception) + { + } + } + _configurator.SaveKnownPlayerDatabase(_serviceConfiguration.GetServerInfoByIndex(serverIndex)); + _configurator.WriteJSONFiles(_serviceConfiguration.GetServerInfoByIndex(serverIndex)); + Task.Delay(500).Wait(); + _service.GetBedrockServerByIndex(serverIndex).WriteToStandardIn("ops reload"); + _service.GetBedrockServerByIndex(serverIndex).WriteToStandardIn("whitelist reload"); + _messageSender.SendData(NetworkMessageSource.Service, NetworkMessageDestination.Client, NetworkMessageTypes.UICallback); + + } + } + + class PackFile : IMessageParser + { + private readonly IServiceConfiguration _serviceConfiguration; + private readonly IProcessInfo _serviceProcessInfo; + private readonly ILogger _logger; + + public PackFile(IServiceConfiguration serviceConfiguration, IProcessInfo serviceProcessInfo, ILogger logger) + { + _logger = logger; + _serviceProcessInfo = serviceProcessInfo; + _serviceConfiguration = serviceConfiguration; + } + + public void ParseMessage(byte[] data, byte serverIndex) + { + MinecraftPackParser archiveParser = new MinecraftPackParser(data, _logger, _serviceProcessInfo); + foreach (MinecraftPackContainer container in archiveParser.FoundPacks) + { + string serverPath = _serviceConfiguration.GetServerInfoByIndex(serverIndex).GetProp("ServerPath").ToString(); + if (container.ManifestType == "WorldPack") + new FileUtils(_serviceProcessInfo.GetDirectory()).CopyFilesRecursively(container.PackContentLocation, new DirectoryInfo($@"{serverPath}\worlds\{container.FolderName}")); + if (container.ManifestType == "data") + new FileUtils(_serviceProcessInfo.GetDirectory()).CopyFilesRecursively(container.PackContentLocation, new DirectoryInfo($@"{serverPath}\behavior_packs\{container.FolderName}")); + if (container.ManifestType == "resources") + new FileUtils(_serviceProcessInfo.GetDirectory()).CopyFilesRecursively(container.PackContentLocation, new DirectoryInfo($@"{serverPath}\resource_packs\{container.FolderName}")); + } + } + } + + class LevelEditFile : IMessageParser + { + private readonly IServiceConfiguration _serviceConfiguration; + private readonly IBedrockService _bedrockService; + + public LevelEditFile(IServiceConfiguration serviceConfiguration, IBedrockService bedrockService) + { + _bedrockService = bedrockService; + _serviceConfiguration = serviceConfiguration; + } + + public void ParseMessage(byte[] data, byte serverIndex) + { + byte[] stripHeaderFromBuffer = new byte[data.Length - 5]; + Buffer.BlockCopy(data, 5, stripHeaderFromBuffer, 0, stripHeaderFromBuffer.Length); + IServerConfiguration server = _serviceConfiguration.GetServerInfoByIndex(serverIndex); + string pathToLevelDat = $@"{_serviceConfiguration.GetProp("ServersPath")}\{server.GetProp("server-name")}\worlds\{server.GetProp("level-name")}\level.dat"; + _bedrockService.GetBedrockServerByIndex(serverIndex).StopServer().Wait(); + File.WriteAllBytes(pathToLevelDat, stripHeaderFromBuffer); + _bedrockService.GetBedrockServerByIndex(serverIndex).SetServerStatus(BedrockServer.ServerStatus.Starting); + } + } + + class Connect : IMessageParser + { + private readonly IMessageSender _iTCPListener; + private readonly IServiceConfiguration _serviceConfiguration; + + public Connect(ITCPListener iTCPListener, IServiceConfiguration serviceConfiguration) + { + _iTCPListener = iTCPListener; + _serviceConfiguration = serviceConfiguration; + } + + public void ParseMessage(byte[] data, byte serverIndex) + { + Formatting indented = Formatting.Indented; + JsonSerializerSettings settings = new JsonSerializerSettings() + { + TypeNameHandling = TypeNameHandling.All + }; + string jsonString = JsonConvert.SerializeObject(_serviceConfiguration, indented, settings); + byte[] serializeToBytes = Encoding.UTF8.GetBytes(jsonString); + _iTCPListener.SendData(serializeToBytes, NetworkMessageSource.Service, NetworkMessageDestination.Client, NetworkMessageTypes.Connect); + } + } + + class StartCmdUpdate : IMessageParser + { + private readonly IServiceConfiguration _serviceConfiguration; + private readonly IConfigurator _configurator; + + public StartCmdUpdate(IConfigurator configurator, IServiceConfiguration serviceConfiguration) + { + _configurator = configurator; + _serviceConfiguration = serviceConfiguration; + } + + public void ParseMessage(byte[] data, byte serverIndex) + { + JsonSerializerSettings settings = new JsonSerializerSettings() + { + TypeNameHandling = TypeNameHandling.All + }; + _serviceConfiguration.GetServerInfoByIndex(serverIndex).SetStartCommands(JsonConvert.DeserializeObject>(Encoding.UTF8.GetString(data, 5, data.Length - 5), settings)); + _configurator.SaveServerProps(_serviceConfiguration.GetServerInfoByIndex(serverIndex), true); + } + } + + class CheckUpdates : IMessageParser + { + private readonly IMessageSender _messageSender; + private readonly IUpdater _updater; + + public CheckUpdates(IMessageSender messageSender, IUpdater updater) + { + _updater = updater; + _messageSender = messageSender; + } + + public void ParseMessage(byte[] data, byte serverIndex) + { + _updater.CheckUpdates().Wait(); + if (_updater.CheckVersionChanged()) + { + _messageSender.SendData(NetworkMessageSource.Service, NetworkMessageDestination.Client, NetworkMessageTypes.CheckUpdates); + } + + _messageSender.SendData(NetworkMessageSource.Service, NetworkMessageDestination.Client, NetworkMessageTypes.UICallback); + } + } + + class BackupRollback : IMessageParser + { + private readonly IMessageSender _messageSender; + private readonly IBedrockService _service; + + public BackupRollback(IMessageSender messageSender, IBedrockService service) + { + _service = service; + _messageSender = messageSender; + } + + public void ParseMessage(byte[] data, byte serverIndex) + { + string stringData = Encoding.UTF8.GetString(data, 5, data.Length - 5); + _service.GetBedrockServerByIndex(serverIndex).RollbackToBackup(serverIndex, stringData); + _messageSender.SendData(NetworkMessageSource.Service, NetworkMessageDestination.Client, NetworkMessageTypes.UICallback); + } + } + + class AddNewServer : IMessageParser + { + private readonly IMessageSender _messageSender; + private readonly IServiceConfiguration _serviceConfiguration; + private readonly IConfigurator _configurator; + private readonly IBedrockService _bedrockService; + + public AddNewServer(IConfigurator configurator, IMessageSender messageSender, IServiceConfiguration serviceConfiguration, IBedrockService bedrockService) + { + _bedrockService = bedrockService; + _configurator = configurator; + _messageSender = messageSender; + _serviceConfiguration = serviceConfiguration; + } + + public void ParseMessage(byte[] data, byte serverIndex) + { + string stringData = Encoding.UTF8.GetString(data, 5, data.Length - 5); + JsonSerializerSettings settings = new JsonSerializerSettings() + { + TypeNameHandling = TypeNameHandling.All + }; + List propList = JsonConvert.DeserializeObject>(stringData, settings); + Property serverNameProp = propList.First(p => p.KeyName == "server-name"); + ServerInfo newServer = new ServerInfo(null, _serviceConfiguration.GetProp("ServersPath").ToString()) + { + ServerName = serverNameProp.ToString(), + ServerPropList = propList, + ServerPath = new Property("ServerPath", "") + { + Value = $@"{_serviceConfiguration.GetProp("ServersPath")}\{serverNameProp}" + }, + ServerExeName = new Property("ServerExeName", "") + { + Value = $"BedrockService.{serverNameProp}.exe" + }, + FileName = $@"{serverNameProp}.conf" + }; + _configurator.SaveServerProps(newServer, true); + _bedrockService.InitializeNewServer(newServer); + + byte[] serializeToBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(_serviceConfiguration, Formatting.Indented, settings)); + _messageSender.SendData(serializeToBytes, NetworkMessageSource.Service, NetworkMessageDestination.Client, NetworkMessageTypes.Connect); + _messageSender.SendData(NetworkMessageSource.Service, NetworkMessageDestination.Client, NetworkMessageTypes.UICallback); + + } + } + + class RemoveServer : IFlaggedMessageParser + { + private readonly IMessageSender _messageSender; + private readonly IServiceConfiguration _serviceConfiguration; + private readonly IConfigurator _configurator; + private readonly IBedrockService _bedrockService; + + public RemoveServer(IConfigurator configurator, IMessageSender messageSender, IServiceConfiguration serviceConfiguration, IBedrockService bedrockService) + { + this._bedrockService = bedrockService; + _configurator = configurator; + _messageSender = messageSender; + _serviceConfiguration = serviceConfiguration; + } + + public void ParseMessage(byte[] data, byte serverIndex, NetworkMessageFlags flag) + { + _bedrockService.GetBedrockServerByIndex(serverIndex).StopServer().Wait(); + _configurator.RemoveServerConfigs(_serviceConfiguration.GetServerInfoByIndex(serverIndex), flag); + _bedrockService.RemoveBedrockServerByIndex(serverIndex); + JsonSerializerSettings settings = new JsonSerializerSettings() + { + TypeNameHandling = TypeNameHandling.All + }; + byte[] serializeToBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(_serviceConfiguration, Formatting.Indented, settings)); + _messageSender.SendData(serializeToBytes, NetworkMessageSource.Service, NetworkMessageDestination.Client, NetworkMessageTypes.Connect); + _messageSender.SendData(NetworkMessageSource.Service, NetworkMessageDestination.Client, NetworkMessageTypes.UICallback); + + } + } + + class ConsoleLogUpdate : IMessageParser + { + private readonly IMessageSender _messageSender; + private readonly IBedrockService _service; + private readonly IServiceConfiguration _serviceConfiguration; + + public ConsoleLogUpdate(IMessageSender messageSender, IServiceConfiguration serviceConfiguration, IBedrockService service) + { + _service = service; + _messageSender = messageSender; + _serviceConfiguration = serviceConfiguration; + } + + public void ParseMessage(byte[] data, byte serverIndex) + { + string stringData = Encoding.UTF8.GetString(data, 5, data.Length - 5); + StringBuilder srvString = new StringBuilder(); + string[] split = stringData.Split('|'); + for (int i = 0; i < split.Length; i++) + { + string[] dataSplit = split[i].Split(';'); + string srvName = dataSplit[0]; + int srvTextLen; + int clientCurLen; + int loop; + ILogger srvText; + if (srvName != "Service") + { + try + { + srvText = _service.GetBedrockServerByName(srvName).GetLogger(); + } + catch(NullReferenceException) + { + break; + } + srvTextLen = srvText.Count(); + clientCurLen = int.Parse(dataSplit[1]); + loop = clientCurLen; + while (loop < srvTextLen) + { + srvString.Append($"{srvName};{srvText.FromIndex(loop)};{loop}|"); + loop++; + } + + } + else + { + srvTextLen = _serviceConfiguration.GetLog().Count; + clientCurLen = int.Parse(dataSplit[1]); + loop = clientCurLen; + while (loop < srvTextLen) + { + srvString.Append($"{srvName};{_serviceConfiguration.GetLog()[loop]};{loop}|"); + loop++; + } + } + } + if (srvString.Length > 1) + { + srvString.Remove(srvString.Length - 1, 1); + _messageSender.SendData(Encoding.UTF8.GetBytes(srvString.ToString()), NetworkMessageSource.Server, NetworkMessageDestination.Client, NetworkMessageTypes.ConsoleLogUpdate); + } + } + } + + class PlayerRequest : IMessageParser + { + private readonly IMessageSender _messageSender; + private readonly IServiceConfiguration _serviceConfiguration; + + public PlayerRequest(IMessageSender messageSender, IServiceConfiguration serviceConfiguration) + { + _messageSender = messageSender; + _serviceConfiguration = serviceConfiguration; + } + + public void ParseMessage(byte[] data, byte serverIndex) + { + IServerConfiguration server = _serviceConfiguration.GetServerInfoByIndex(serverIndex); + JsonSerializerSettings settings = new JsonSerializerSettings() + { + TypeNameHandling = TypeNameHandling.All + }; + byte[] serializeToBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(server.GetPlayerList(), Formatting.Indented, settings)); + _messageSender.SendData(serializeToBytes, NetworkMessageSource.Server, NetworkMessageDestination.Client, serverIndex, NetworkMessageTypes.PlayersRequest); + } + } + + } +} diff --git a/BedrockService.backup/Service/Networking/TCPListener.cs b/BedrockService.backup/Service/Networking/TCPListener.cs new file mode 100644 index 00000000..ded3ced8 --- /dev/null +++ b/BedrockService.backup/Service/Networking/TCPListener.cs @@ -0,0 +1,296 @@ +using BedrockService.Service.Core.Interfaces; +using BedrockService.Service.Core.Threads; +using BedrockService.Service.Networking.NetworkMessageClasses; +using BedrockService.Shared.Classes; +using BedrockService.Shared.Interfaces; +using System; +using System.Collections.Generic; +using System.Net; +using System.Net.Sockets; +using System.Threading; +using System.IO; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; + +namespace BedrockService.Service.Networking +{ + public class TCPListener : ITCPListener, IMessageSender + { + private TcpClient _client; + private TcpListener _inListener; + private NetworkStream _stream; + private readonly IServiceConfiguration _serviceConfiguration; + private readonly ILogger _logger; + private IServiceThread _tcpThread; + private IServiceThread _clientThread; + private IServiceThread _heartbeatThread; + private bool _heartbeatRecieved = false; + private bool _firstHeartbeatRecieved = false; + private bool _keepAlive = false; + private int _heartbeatFailTimeout; + private readonly int _heartbeatFailTimeoutLimit = 200; + private Dictionary _standardMessageLookup; + private Dictionary _flaggedMessageLookup; + private readonly IPAddress _ipAddress = IPAddress.Parse("0.0.0.0"); + private readonly System.Timers.Timer _reconnectTimer = new System.Timers.Timer(500.0); + private CommsKeyContainer _keyContainer; + + public TCPListener(IServiceConfiguration serviceConfiguration, ILogger logger) + { + _logger = logger; + _serviceConfiguration = serviceConfiguration; + _reconnectTimer.Elapsed += ReconnectTimer_Elapsed; + _tcpThread = new TCPThread(new ThreadStart(StartListening)); + } + + private void ReconnectTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) + { + _reconnectTimer.Stop(); + _tcpThread = new TCPThread(new ThreadStart(StartListening)); + } + + public void SetStrategyDictionaries(Dictionary standard, Dictionary flagged) + { + _standardMessageLookup = standard; + _flaggedMessageLookup = flagged; + } + + public void StartListening() + { + _inListener = new TcpListener(_ipAddress, int.Parse(_serviceConfiguration.GetProp("ClientPort").ToString())); + try + { + _inListener.Start(); + _keepAlive = true; + } + catch(SocketException e) + { + _logger.AppendLine($"Error! {e.Message}"); + Thread.Sleep(2000); + //Environment.Exit(1); + } + + while (true) + { + try + { + _client = _inListener.AcceptTcpClient(); + _stream = _client.GetStream(); + _clientThread = new ClientServiceThread(new ThreadStart(IncomingListener)); + _heartbeatThread = new HeartbeatThread(new ThreadStart(SendBackHeatbeatSignal)); + } + catch (ThreadStateException) { } + catch (NullReferenceException) { } + catch (InvalidOperationException) { } + catch (SocketException) { } + catch (Exception e) + { + _logger.AppendLine(e.ToString()); + } + } + } + + public void ResetListener() + { + _keepAlive = false; + while (_heartbeatThread.IsAlive()) + { + Thread.Sleep(300); + } + _stream.Close(); + _stream.Dispose(); + _client.Client.Blocking = false; + _inListener.Stop(); + _tcpThread.CloseThread(); + _tcpThread = null; + StartListening(); + } + + public void SendData(byte[] bytesToSend, NetworkMessageSource source, NetworkMessageDestination destination, byte serverIndex, NetworkMessageTypes type, NetworkMessageFlags status) + { + byte[] byteHeader = new byte[9 + bytesToSend.Length]; + byte[] len = BitConverter.GetBytes(5 + bytesToSend.Length); + Buffer.BlockCopy(len, 0, byteHeader, 0, 4); + byteHeader[4] = (byte)source; + byteHeader[5] = (byte)destination; + byteHeader[6] = serverIndex; + byteHeader[7] = (byte)type; + byteHeader[8] = (byte)status; + Buffer.BlockCopy(bytesToSend, 0, byteHeader, 9, bytesToSend.Length); + + if (_clientThread.IsAlive()) + { + try + { + _stream.Write(byteHeader, 0, byteHeader.Length); + _stream.Flush(); + } + catch + { + _logger.AppendLine("Error writing to network stream!"); + } + } + } + + public void SendData(byte[] bytes, NetworkMessageSource source, NetworkMessageDestination destination, byte serverIndex, NetworkMessageTypes type) => SendData(bytes, source, destination, serverIndex, type, NetworkMessageFlags.None); + + public void SendData(byte[] bytes, NetworkMessageSource source, NetworkMessageDestination destination, NetworkMessageTypes type) => SendData(bytes, source, destination, 0xFF, type, NetworkMessageFlags.None); + + public void SendData(NetworkMessageSource source, NetworkMessageDestination destination, NetworkMessageTypes type) => SendData(new byte[0], source, destination, 0xFF, type, NetworkMessageFlags.None); + + public void SendData(NetworkMessageSource source, NetworkMessageDestination destination, byte serverIndex, NetworkMessageTypes type) => SendData(new byte[0], source, destination, serverIndex, type, NetworkMessageFlags.None); + + public void SendData(NetworkMessageSource source, NetworkMessageDestination destination, NetworkMessageTypes type, NetworkMessageFlags status) => SendData(new byte[0], source, destination, 0xFF, type, status); + + private void IncomingListener() + { + _keepAlive = true; + _logger.AppendLine("Packet listener thread started."); + int AvailBytes = 0; + int byteCount = 0; + NetworkMessageSource msgSource = 0; + NetworkMessageDestination msgDest = 0; + byte serverIndex = 0xFF; + NetworkMessageTypes msgType = 0; + NetworkMessageFlags msgFlag = 0; + while (_keepAlive) + { + try + { + byte[] buffer = new byte[4]; + while (_client.Client.Available != 0) // Recieve data from client. + { + byteCount = _stream.Read(buffer, 0, 4); + int expectedLen = BitConverter.ToInt32(buffer, 0); + buffer = new byte[expectedLen]; + byteCount = _stream.Read(buffer, 0, expectedLen); + msgSource = (NetworkMessageSource)buffer[0]; + msgDest = (NetworkMessageDestination)buffer[1]; + serverIndex = buffer[2]; + msgType = (NetworkMessageTypes)buffer[3]; + msgFlag = (NetworkMessageFlags)buffer[4]; + if (msgType == NetworkMessageTypes.Heartbeat) + { + if (!_firstHeartbeatRecieved) + _firstHeartbeatRecieved = true; + _heartbeatRecieved = true; + } + if (msgType == NetworkMessageTypes.Disconnect) + { + ResetListener(); + } + if (msgType < NetworkMessageTypes.Heartbeat) + { + if (_standardMessageLookup.ContainsKey(msgType)) + _standardMessageLookup[msgType].ParseMessage(buffer, serverIndex); + else + _flaggedMessageLookup[msgType].ParseMessage(buffer, serverIndex, msgFlag); + } + } + Thread.Sleep(200); + } + catch (OutOfMemoryException) + { + _logger.AppendLine("Out of memory exception thrown."); + } + catch (ObjectDisposedException) + { + _logger.AppendLine("Client was disposed! Killing thread..."); + break; + } + catch (InvalidOperationException e) + { + if (msgType != NetworkMessageTypes.ConsoleLogUpdate) + { + _logger.AppendLine(e.Message); + _logger.AppendLine(e.StackTrace); + } + } + catch (ThreadAbortException) + { + _logger.AppendLine("ListenerThread aborted!"); + } + catch (Exception e) + { + _logger.AppendLine($"Error: {e.Message} {e.StackTrace}"); + } + try + { + AvailBytes = _client.Client.Available; + } + catch { } + if (!_clientThread.IsAlive()) + _clientThread.CloseThread(); + } + } + + private void SendBackHeatbeatSignal() + { + _logger.AppendLine("HeartBeatSender started."); + while (_keepAlive) + { + _heartbeatRecieved = false; + while (!_heartbeatRecieved && _keepAlive) + { + Thread.Sleep(100); + _heartbeatFailTimeout++; + if (_heartbeatFailTimeout > _heartbeatFailTimeoutLimit) + { + if (!_firstHeartbeatRecieved) + { + try + { + SendData(NetworkMessageSource.Service, NetworkMessageDestination.Client, NetworkMessageTypes.Heartbeat); + _heartbeatFailTimeout = 0; + } + catch (Exception e) + { + _logger.AppendLine($"HeartBeatSender exited with error: {e.Message}"); + return; + } + } + } + } + _heartbeatRecieved = false; + _heartbeatFailTimeout = 0; + SendData(NetworkMessageSource.Service, NetworkMessageDestination.Client, NetworkMessageTypes.Heartbeat); + int timeWaited = 0; + while (timeWaited < 3000) + { + Thread.Sleep(100); + timeWaited += 100; + if (!_keepAlive) + { + _logger.AppendLine("HeartBeatSender exited."); + return; + } + } + } + _logger.AppendLine("HeartBeatSender exited."); + } + + public void SetKeyContainer(CommsKeyContainer keyContainer) + { + _keyContainer = keyContainer; + } + + public bool VerifyClientData(byte[] certificate) + { + if(certificate != null) + { + byte[] decrypted; + using(RSACryptoServiceProvider rsa = new RSACryptoServiceProvider()) + { + rsa.ImportParameters(_keyContainer.LocalPrivateKey.GetPrivateKey()); + + } + using(RSACryptoServiceProvider rsa = new RSACryptoServiceProvider()) + { + rsa.ImportParameters(_keyContainer.RemotePublicKey.GetPrivateKey()); + + } + } + return true; + } + } +} \ No newline at end of file diff --git a/BedrockService.backup/Service/Networking/Updater.cs b/BedrockService.backup/Service/Networking/Updater.cs new file mode 100644 index 00000000..8be6efb1 --- /dev/null +++ b/BedrockService.backup/Service/Networking/Updater.cs @@ -0,0 +1,195 @@ +using BedrockService.Shared.Interfaces; +using BedrockService.Shared.Utilities; +using System; +using System.IO; +using System.IO.Compression; +using System.Net.Http; +using System.Text.RegularExpressions; +using System.Threading; +using System.Threading.Tasks; + +namespace BedrockService.Service.Networking +{ + public class Updater : IUpdater + { + private bool _versionChanged = false; + private readonly ILogger _logger; + private readonly IServiceConfiguration _serviceConfiguration; + private readonly IProcessInfo _processInfo; + private readonly string _version; + + public Updater(IProcessInfo processInfo, ILogger logger, IServiceConfiguration serviceConfiguration) + { + _serviceConfiguration = serviceConfiguration; + _processInfo = processInfo; + _logger = logger; + _version = "None"; + if (!File.Exists($@"{processInfo.GetDirectory()}\Server\bedrock_ver.ini")) + { + logger.AppendLine("Version ini file missing, creating and fetching build..."); + File.Create($@"{processInfo.GetDirectory()}\Server\bedrock_ver.ini").Close(); + } + _version = File.ReadAllText($@"{processInfo.GetDirectory()}\Server\bedrock_ver.ini"); + } + + public async Task CheckUpdates() + { + await Task.Run(async () => + { + _logger.AppendLine("Checking MCS Version and fetching update if needed..."); + HttpClient client = new HttpClient(); + client.DefaultRequestHeaders.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/apng,*/*;q=0.8"); + client.DefaultRequestHeaders.Add("Accept-Language", "en-GB,en;q=0.9,en-US;q=0.8"); + client.DefaultRequestHeaders.Add("Connection", "keep-alive"); + client.DefaultRequestHeaders.Add("Cache-Control", "no-cache"); + client.DefaultRequestHeaders.Add("Pragma", "no-cache"); + client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko; Google Page Speed Insights) Chrome/27.0.1453 Safari/537.36"); + client.Timeout = new TimeSpan(0, 0, 3); + + string content = FetchHTTPContent(client).Result; + if (content == null) // This really doesn't fail often. Give it one more try or fail. + { + Thread.Sleep(500); + content = await FetchHTTPContent(client); + } + if (content == null) + return false; + Regex regex = new Regex(@"(https://minecraft.azureedge.net/bin-win/bedrock-server-)(.*)(\.zip)", RegexOptions.IgnoreCase); + Match m = regex.Match(content); + if (!m.Success) + { + _logger.AppendLine("Checking for updates failed. Check website functionality!"); + return false; + } + string downloadPath = m.Groups[0].Value; + string fetchedVersion = m.Groups[2].Value; + client.Dispose(); + + if (_version == fetchedVersion) + { + _logger.AppendLine($"Current version \"{fetchedVersion}\" is up to date!"); + return true; + } + _logger.AppendLine($"New version detected! Now fetching from {downloadPath}..."); + if (!FetchBuild(downloadPath, fetchedVersion).Wait(60000)) + { + _logger.AppendLine("Fetching build timed out. If this is a new service instance, please restart service!"); + return false; + } + _versionChanged = true; + File.WriteAllText($@"{_processInfo.GetDirectory()}\Server\bedrock_ver.ini", fetchedVersion); + _serviceConfiguration.SetServerVersion(fetchedVersion); + GenerateFileList(fetchedVersion); + return true; + + + }); + } + + public async Task FetchBuild(string path, string version) + { + string ZipDir = $@"{_processInfo.GetDirectory()}\Server\MCSFiles\Update_{version}.zip"; + if (!Directory.Exists($@"{_processInfo.GetDirectory()}\Server\MCSFiles")) + { + Directory.CreateDirectory($@"{_processInfo.GetDirectory()}\Server\MCSFiles"); + } + if (File.Exists(ZipDir)) + { + return; + } + //if (InstanceProvider.HostInfo.GetGlobalValue("AcceptedMojangLic") == "false") + //{ + // logger.AppendLine("You have not accepted the license. Please visit the readme for more info!"); + // return; + //} + _logger.AppendLine("Now downloading latest build of Minecraft Bedrock Server. Please wait..."); + using (var httpClient = new HttpClient()) + { + using (var request = new HttpRequestMessage(HttpMethod.Get, path)) + { + using (Stream contentStream = await (await httpClient.SendAsync(request)).Content.ReadAsStreamAsync(), stream = new FileStream(ZipDir, FileMode.Create, FileAccess.Write, FileShare.None, 256000, true)) + { + try + { + await contentStream.CopyToAsync(stream); + } + catch (Exception e) + { + _logger.AppendLine($"Download zip resulted in error: {e.StackTrace}"); + } + httpClient.Dispose(); + request.Dispose(); + contentStream.Dispose(); + return; + } + } + } + + } + + public bool CheckVersionChanged() => _versionChanged; + + public void MarkUpToDate() => _versionChanged = false; + + public async Task ReplaceBuild(IServerConfiguration server) + { + await Task.Run(() => + { + try + { + if (!Directory.Exists(server.GetProp("ServerPath").ToString())) + Directory.CreateDirectory(server.GetProp("ServerPath").ToString()); + else if (File.Exists($@"{_processInfo.GetDirectory()}\Server\MCSFiles\stock_filelist.ini")) + new FileUtils(_processInfo.GetDirectory()).DeleteFilelist(File.ReadAllLines($@"{_processInfo.GetDirectory()}\Server\MCSFiles\stock_filelist.ini"), server.GetProp("ServerPath").ToString()); + else + new FileUtils(_processInfo.GetDirectory()).DeleteFilesRecursively(new DirectoryInfo(server.GetProp("ServerPath").ToString()), false); + + ZipFile.ExtractToDirectory($@"{_processInfo.GetDirectory()}\Server\MCSFiles\Update_{_version}.zip", server.GetProp("ServerPath").ToString()); + File.Copy(server.GetProp("ServerPath") + "\\bedrock_server.exe", server.GetProp("ServerPath") + "\\" + server.GetProp("ServerExeName"), true); + } + catch (Exception e) + { + _logger.AppendLine($"ERROR: Got an exception deleting entire directory! {e.Message}"); + } + + + }); + } + + + private async Task FetchHTTPContent(HttpClient client) + { + try + { + return await client.GetStringAsync("https://www.minecraft.net/en-us/download/server/bedrock"); + } + catch (HttpRequestException) + { + _logger.AppendLine($"Error! Updater timed out, could not fetch current build!"); + } + catch (TaskCanceledException) + { + Thread.Sleep(200); + return await FetchHTTPContent(client); + } + catch (Exception e) + { + _logger.AppendLine($"Updater resulted in error: {e.Message}\n{e.InnerException}\n{e.StackTrace}"); + } + return null; + } + + private void GenerateFileList(string version) + { + using (ZipArchive zip = ZipFile.OpenRead($@"{_processInfo.GetDirectory()}\Server\MCSFiles\Update_{version}.zip")) + { + string[] fileList = new string[zip.Entries.Count]; + for (int i = 0; i < zip.Entries.Count; i++) + { + fileList[i] = zip.Entries[i].FullName.Replace('/', '\\'); + } + File.WriteAllLines($@"{_processInfo.GetDirectory()}\Server\MCSFiles\stock_filelist.ini", fileList); + } + } + } +} diff --git a/BedrockService.backup/Service/Program.cs b/BedrockService.backup/Service/Program.cs new file mode 100644 index 00000000..d3a6fc02 --- /dev/null +++ b/BedrockService.backup/Service/Program.cs @@ -0,0 +1,80 @@ +using BedrockService.Service.Core; +using BedrockService.Service.Logging; +using BedrockService.Service.Management; +using BedrockService.Service.Networking; +using BedrockService.Shared.Classes; +using BedrockService.Shared.Interfaces; +using Microsoft.Extensions.DependencyInjection; +using System; +using System.Diagnostics; +using System.IO; +using System.Reflection; +using Topshelf; + +namespace BedrockService.Service +{ + class Program + { + public static bool IsExiting = false; + private static bool _isDebugEnabled = false; + private static bool _isConsoleMode = false; + private static readonly IServiceCollection _services = new ServiceCollection(); + + static void Main(string[] args) + { + if (args.Length > 0) + { + _isDebugEnabled = args[0].ToLower() == "-debug"; + } + ConfigureServices(_services); + IServiceProvider serviceProvider = _services.BuildServiceProvider(); + serviceProvider.GetRequiredService().LoadAllConfigurations().Wait(); + serviceProvider.GetRequiredService().CheckUpdates().Wait(); + IService service = serviceProvider.GetRequiredService(); + ILogger Logger = serviceProvider.GetRequiredService(); + IProcessInfo ProcessInfo = serviceProvider.GetRequiredService(); + serviceProvider.GetRequiredService(); + if (args.Length == 0 || Environment.UserInteractive) + { + _isConsoleMode = true; + Logger.AppendLine("BedrockService startup detected in Console mode."); + } + else + { + Logger.AppendLine("BedrockService startup detected in Service mode."); + foreach (Process process in Process.GetProcesses()) + { + if (process.Id != ProcessInfo.GetProcessPID() && process.ProcessName.StartsWith("BedrockService.") && process.ProcessName != "BedrockService.Client") + { + Logger.AppendLine($"Found additional running instance of {process.ProcessName} with ID {process.Id}"); + Logger.AppendLine($"Killing process with id {process.Id}"); + process.Kill(); + } + } + } + service.InitializeHost().Wait(); + TopshelfExitCode rc = service.Run(); + var exitCode = (int)Convert.ChangeType(rc, rc.GetTypeCode()); + if (_isDebugEnabled) + { + Console.Write("Program is force-quitting. Press any key to exit."); + Console.Out.Flush(); + Console.ReadLine(); + } + Environment.ExitCode = exitCode; + } + private static void ConfigureServices(IServiceCollection services) + { + IProcessInfo processInfo = new ServiceProcessInfo(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), Path.GetFileName(Assembly.GetExecutingAssembly().Location), Process.GetCurrentProcess().Id, _isDebugEnabled, _isConsoleMode); + services.AddSingleton(processInfo); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + } + } +} diff --git a/BedrockService.backup/Service/Properties/AssemblyInfo.cs b/BedrockService.backup/Service/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..a875f734 --- /dev/null +++ b/BedrockService.backup/Service/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("BedrockService")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("BedrockService")] +[assembly: AssemblyCopyright("Copyright © 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("f4b1f910-30b0-4f94-a49f-94142e790c9d")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// 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("2.5.0.0")] +[assembly: AssemblyFileVersion("2.5.0.0")] diff --git a/BedrockService.backup/Service/Server/BedrockServer.cs b/BedrockService.backup/Service/Server/BedrockServer.cs new file mode 100644 index 00000000..87e5bf22 --- /dev/null +++ b/BedrockService.backup/Service/Server/BedrockServer.cs @@ -0,0 +1,465 @@ +using BedrockService.Service.Core.Interfaces; +using BedrockService.Service.Core.Threads; +using BedrockService.Service.Management; +using BedrockService.Service.Server.Management; +using BedrockService.Shared.Classes; +using BedrockService.Shared.Interfaces; +using BedrockService.Shared.Utilities; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Text.RegularExpressions; +using System.Threading; +using System.Threading.Tasks; +using Topshelf; + +namespace BedrockService.Service.Server +{ + public class BedrockServer : IBedrockServer + { + private IServiceThread _serverThread; + private IServiceThread _watchdogThread; + private StreamWriter _stdInStream; + private Process _serverProcess; + private HostControl _hostController; + private ServerStatus _currentServerStatus; + private readonly IServerConfiguration _serverConfiguration; + private readonly IServiceConfiguration _serviceConfiguration; + private readonly IPlayerManager _playerManager; + private readonly IConfigurator _configurator; + private readonly ILogger _logger; + private readonly ILogger _serverLogger; + private readonly string _servicePath; + private const string _startupMessage = "[INFO] Server started."; + public enum ServerStatus + { + Stopped, + Starting, + Stopping, + Started + } + + public BedrockServer(IServerConfiguration serverConfiguration, IConfigurator configurator, ILogger logger, IServiceConfiguration serviceConfiguration, IProcessInfo processInfo) + { + _serverConfiguration = serverConfiguration; + _serviceConfiguration = serviceConfiguration; + _configurator = configurator; + _logger = logger; + _servicePath = processInfo.GetDirectory(); + _serverLogger = new ServerLogger(processInfo, serviceConfiguration, serverConfiguration, serverConfiguration.GetServerName()); + _playerManager = new PlayerManager(serverConfiguration, logger); + } + + public void WriteToStandardIn(string command) + { + _stdInStream.WriteLine(command); + } + + public void StartControl() + { + _serverThread = new ServerProcessThread(new ThreadStart(RunServer)); + } + + public void StopControl() + { + if (_serverProcess != null) + { + _logger.AppendLine("Sending Stop to Bedrock. Process.HasExited = " + _serverProcess.HasExited.ToString()); + + _serverProcess.CancelOutputRead(); + + _stdInStream.WriteLine("stop"); + while (!_serverProcess.HasExited) { } + } + _serverThread.CloseThread(); + _serverProcess = null; + _currentServerStatus = ServerStatus.Stopped; + } + + public void StartWatchdog(HostControl hostControl) + { + _hostController = hostControl; + if (_watchdogThread == null) + { + _watchdogThread = new WatchdogThread(new ThreadStart(ApplicationWatchdogMonitor)); + } + } + + private bool Backup() + { + try + { + FileInfo exe = new FileInfo($@"{_serverConfiguration.GetProp("ServerPath")}\{_serverConfiguration.GetProp("ServerExeName")}"); + string configBackupPath = _serviceConfiguration.GetProp("BackupPath").ToString(); + DirectoryInfo backupDir = new DirectoryInfo($@"{configBackupPath}\{_serverConfiguration.GetServerName()}"); + DirectoryInfo serverDir = new DirectoryInfo(_serverConfiguration.GetProp("ServerPath").ToString()); + DirectoryInfo worldsDir = new DirectoryInfo($@"{_serverConfiguration.GetProp("ServerPath")}\worlds"); + if (!backupDir.Exists) + { + backupDir.Create(); + } + int dirCount = backupDir.GetDirectories().Length; + try + { + if (dirCount >= int.Parse(_serviceConfiguration.GetProp("MaxBackupCount").ToString())) + { + Regex reg = new Regex(@"Backup_(.*)$"); + + List Dates = new List(); + foreach (DirectoryInfo dir in backupDir.GetDirectories()) + { + if (reg.IsMatch(dir.Name)) + { + Match match = reg.Match(dir.Name); + Dates.Add(Convert.ToInt64(match.Groups[1].Value)); + } + } + long OldestDate = 0; + foreach (long date in Dates) + { + if (OldestDate == 0) + { + OldestDate = date; + } + else if (date < OldestDate) + { + OldestDate = date; + } + } + Directory.Delete($@"{backupDir}\Backup_{OldestDate}", true); + } + } + catch (Exception e) + { + if (e.GetType() == typeof(FormatException)) + { + _logger.AppendLine("Error in Config! MaxBackupCount must be nothing but a number!"); + } + } + + DirectoryInfo targetDirectory = backupDir.CreateSubdirectory($"Backup_{DateTime.Now.Ticks}"); + _logger.AppendLine($"Backing up files for server {_serverConfiguration.GetServerName()}. Please wait!"); + if (_serviceConfiguration.GetProp("EntireBackups").ToString() == "false") + { + new FileUtils(_servicePath).CopyFilesRecursively(worldsDir, targetDirectory); + return true; + } + new FileUtils(_servicePath).CopyFilesRecursively(serverDir, targetDirectory); + return true; + } + catch (Exception e) + { + _logger.AppendLine($"Error with Backup: {e.StackTrace}"); + return false; + } + } + + public void StopWatchdog() + { + if(_watchdogThread != null) + _watchdogThread.CloseThread(); + _watchdogThread = null; + } + + public ServerStatus GetServerStatus() => _currentServerStatus; + + public void SetServerStatus(ServerStatus newStatus) => _currentServerStatus = newStatus; + + private void ApplicationWatchdogMonitor() + { + while (_watchdogThread.IsAlive()) + { + string exeName = _serverConfiguration.GetProp("ServerExeName").ToString(); + string appName = exeName.Substring(0, exeName.Length - 4); + if (!MonitoredAppExists(appName) && _currentServerStatus == ServerStatus.Starting) + { + StartControl(); + _logger.AppendLine($"Recieved start signal for server {_serverConfiguration.GetServerName()}."); + Thread.Sleep(15000); + } + while (MonitoredAppExists(appName) && _currentServerStatus == ServerStatus.Started) + { + Thread.Sleep(5000); + } + if (MonitoredAppExists(appName) && _currentServerStatus == ServerStatus.Stopping) + { + _logger.AppendLine($"BedrockService signaled stop to application {appName}."); + _logger.AppendLine("Stopping..."); + StopControl(); + while (_currentServerStatus == ServerStatus.Stopping) + { + Thread.Sleep(250); + } + } + if (!MonitoredAppExists(appName) && _currentServerStatus == ServerStatus.Started) + { + StopControl(); + _logger.AppendLine($"Started application {appName} was not found in running processes... Resarting {appName}."); + StartControl(); + Thread.Sleep(1500); + } + if (!MonitoredAppExists(appName) && _currentServerStatus == ServerStatus.Stopped) + { + _logger.AppendLine("Server stopped successfully."); + } + if (!MonitoredAppExists(appName) && _currentServerStatus == ServerStatus.Stopping) + { + _logger.AppendLine("Server stopped unexpectedly. Setting server status to stopped."); + _currentServerStatus = ServerStatus.Stopped; + } + while (!MonitoredAppExists(appName) && _currentServerStatus == ServerStatus.Stopped && !Program.IsExiting) + { + Thread.Sleep(1000); + } + if (!MonitoredAppExists(appName) && _currentServerStatus == ServerStatus.Stopped && Program.IsExiting) + { + return; + } + + } + } + + public bool RestartServer(bool ShouldPerformBackup) + { + if (_currentServerStatus == ServerStatus.Started) + { + _currentServerStatus = ServerStatus.Stopping; + while (_currentServerStatus == ServerStatus.Stopping) + { + Thread.Sleep(100); + } + if (ShouldPerformBackup) + { + if (!Backup()) + return false; + } + _currentServerStatus = ServerStatus.Starting; + } + return false; + } + + public string GetServerName() => _serverConfiguration.GetServerName(); + + private void RunServer() + { + string exeName = _serverConfiguration.GetProp("ServerExeName").ToString(); + string appName = exeName.Substring(0, exeName.Length - 4); + _configurator.WriteJSONFiles(_serverConfiguration); + _configurator.SaveServerProps(_serverConfiguration, false); + + try + { + if (File.Exists($@"{_serverConfiguration.GetProp("ServerPath")}\{_serverConfiguration.GetProp("ServerExeName")}")) + { + if (MonitoredAppExists(appName)) + { + Process[] processList = Process.GetProcessesByName(appName); + if (processList.Length != 0) + { + _logger.AppendLine($@"Application {appName} was found running! Killing to proceed."); + KillProcess(processList); + } + } + // Fires up a new process to run inside this one + CreateProcess(); + } + else + { + _logger.AppendLine($"The Bedrock Server is not accessible at {$@"{_serverConfiguration.GetProp("ServerPath")}\{_serverConfiguration.GetProp("ServerExeName")}"}\r\nCheck if the file is at that location and that permissions are correct."); + _hostController.Stop(); + } + } + catch (Exception e) + { + _logger.AppendLine($"Error Running Bedrock Server: {e.Message}\n{e.StackTrace}"); + _hostController.Stop(); + + } + + } + + private void CreateProcess() + { + string fileName = $@"{_serverConfiguration.GetProp("ServerPath")}\{_serverConfiguration.GetProp("ServerExeName")}"; + _serverProcess = Process.Start(new ProcessStartInfo + { + UseShellExecute = false, + RedirectStandardError = true, + RedirectStandardInput = true, + RedirectStandardOutput = true, + WindowStyle = ProcessWindowStyle.Hidden, + FileName = fileName + }); + _serverProcess.PriorityClass = ProcessPriorityClass.High; + _serverProcess.OutputDataReceived += StdOutToLog; + + _serverProcess.BeginOutputReadLine(); + _stdInStream = _serverProcess.StandardInput; + } + + private void KillProcess(Process[] processList) + { + foreach (Process process in processList) + { + try + { + process.Kill(); + Thread.Sleep(1000); + _logger.AppendLine($@"App {_serverConfiguration.GetProp("ServerExeName")} killed!"); + } + catch (Exception e) + { + _logger.AppendLine($"Killing proccess resulted in error: {e.StackTrace}"); + } + } + } + + private bool MonitoredAppExists(string monitoredAppName) + { + try + { + Process[] processList = Process.GetProcessesByName(monitoredAppName); + if (processList.Length == 0) + { + return false; + } + else + { + return true; + } + } + catch (Exception ex) + { + _logger.AppendLine("ApplicationWatcher MonitoredAppExists Exception: " + ex.StackTrace); + return true; + } + } + + private void StdOutToLog(object sender, DataReceivedEventArgs e) + { + if (e.Data != null && !e.Data.Contains("[INFO] Running AutoCompaction...")) + { + string dataMsg = e.Data; + string logFileText = "NO LOG FILE! - "; + if (dataMsg.StartsWith(logFileText)) + dataMsg = dataMsg.Substring(logFileText.Length, dataMsg.Length - logFileText.Length); + _serverLogger.AppendText($"{_serverConfiguration.GetServerName()}: {dataMsg}\r\n"); + if (e.Data != null) + { + + if (dataMsg.Contains(_startupMessage)) + { + _currentServerStatus = ServerStatus.Started; + Thread.Sleep(1000); + + if (_serverConfiguration.GetStartCommands().Count > 0) + { + RunStartupCommands(); + } + } + if (dataMsg.StartsWith("[INFO] Player connected")) + { + int usernameStart = dataMsg.IndexOf(':') + 2; + int usernameEnd = dataMsg.IndexOf(','); + int usernameLength = usernameEnd - usernameStart; + int xuidStart = dataMsg.IndexOf(':', usernameEnd) + 2; + string username = dataMsg.Substring(usernameStart, usernameLength); + string xuid = dataMsg.Substring(xuidStart, dataMsg.Length - xuidStart); + Console.WriteLine($"Player {username} connected with XUID: {xuid}"); + _playerManager.PlayerConnected(username, xuid); + _configurator.SaveKnownPlayerDatabase(_serverConfiguration); + } + if (dataMsg.StartsWith("[INFO] Player disconnected")) + { + int usernameStart = dataMsg.IndexOf(':') + 2; + int usernameEnd = dataMsg.IndexOf(','); + int usernameLength = usernameEnd - usernameStart; + int xuidStart = dataMsg.IndexOf(':', usernameEnd) + 2; + string username = dataMsg.Substring(usernameStart, usernameLength); + string xuid = dataMsg.Substring(xuidStart, dataMsg.Length - xuidStart); + Console.WriteLine($"Player {username} disconnected with XUID: {xuid}"); + _playerManager.PlayerDisconnected(xuid); + _configurator.SaveKnownPlayerDatabase(_serverConfiguration); + } + if (dataMsg.Contains("Failed to load Vanilla")) + { + _currentServerStatus = ServerStatus.Stopping; + while (_currentServerStatus != ServerStatus.Stopped) + Thread.Sleep(200); + if (_configurator.ReplaceServerBuild(_serverConfiguration).Wait(30000)) + _currentServerStatus = ServerStatus.Starting; + } + if(dataMsg.Contains("Version ")) + { + int msgStartIndex = dataMsg.IndexOf(']') + 2; + string focusedMsg = dataMsg.Substring(msgStartIndex, dataMsg.Length - msgStartIndex); + int versionIndex = focusedMsg.IndexOf(' ') + 1; + string versionString = focusedMsg.Substring(versionIndex, focusedMsg.Length - versionIndex); + string currentVersion = _serviceConfiguration.GetServerVersion(); + if (currentVersion != versionString) + { + _logger.AppendLine($"Server {GetServerName()} version found out-of-date! Now updating!"); + StopServer().Wait(); + _configurator.ReplaceServerBuild(_serverConfiguration).Wait(); + StartControl(); + } + } + } + } + } + + private void RunStartupCommands() + { + foreach (StartCmdEntry cmd in _serverConfiguration.GetStartCommands()) + { + _stdInStream.WriteLine(cmd.Command.Trim()); + Thread.Sleep(1000); + } + } + + public bool RollbackToBackup(byte serverIndex, string folderName) + { + IServerConfiguration server = _serviceConfiguration.GetServerInfoByIndex(serverIndex); + StopServer().Wait(); + try + { + foreach (DirectoryInfo dir in new DirectoryInfo($@"{_serviceConfiguration.GetProp("BackupPath")}\{server.GetServerName()}").GetDirectories()) + if (dir.Name == folderName) + { + new FileUtils(_servicePath).DeleteFilesRecursively(new DirectoryInfo($@"{server.GetProp("ServerPath")}\worlds"), false); + _logger.AppendLine($"Deleted world folder contents."); + foreach (DirectoryInfo worldDir in new DirectoryInfo($@"{_serviceConfiguration.GetProp("BackupPath")}\{server.GetServerName()}\{folderName}").GetDirectories()) + { + new FileUtils(_servicePath).CopyFilesRecursively(worldDir, new DirectoryInfo($@"{server.GetProp("ServerPath")}\worlds\{worldDir.Name}")); + _logger.AppendLine($@"Copied {worldDir.Name} to path {server.GetProp("ServerPath")}\worlds"); + } + SetServerStatus(ServerStatus.Starting); + return true; + } + } + catch (IOException e) + { + _logger.AppendLine($"Error deleting selected backups! {e.Message}"); + } + return false; + } + + public ILogger GetLogger() => _serverLogger; + + public IPlayerManager GetPlayerManager() => _playerManager; + + public Task StopServer() + { + return Task.Run(() => + { + _currentServerStatus = ServerStatus.Stopping; + while (_currentServerStatus != ServerStatus.Stopped) + { + Thread.Sleep(100); + } + + }); + } + } +} diff --git a/BedrockService.backup/Service/Server/IBedrockServer.cs b/BedrockService.backup/Service/Server/IBedrockServer.cs new file mode 100644 index 00000000..f9967d3b --- /dev/null +++ b/BedrockService.backup/Service/Server/IBedrockServer.cs @@ -0,0 +1,22 @@ +using BedrockService.Service.Server.Management; +using BedrockService.Shared.Interfaces; +using System.Threading.Tasks; +using Topshelf; + +namespace BedrockService.Service.Server +{ + public interface IBedrockServer + { + void StartWatchdog(HostControl hostControl); + void StopWatchdog(); + string GetServerName(); + void WriteToStandardIn(string command); + bool RestartServer(bool shouldPerformBackup); + bool RollbackToBackup(byte serverIndex, string folderName); + Task StopServer(); + BedrockServer.ServerStatus GetServerStatus(); + void SetServerStatus(BedrockServer.ServerStatus newStatus); + IPlayerManager GetPlayerManager(); + ILogger GetLogger(); + } +} diff --git a/BedrockService.backup/Service/Server/Management/IPlayerManager.cs b/BedrockService.backup/Service/Server/Management/IPlayerManager.cs new file mode 100644 index 00000000..c3c0017b --- /dev/null +++ b/BedrockService.backup/Service/Server/Management/IPlayerManager.cs @@ -0,0 +1,15 @@ +using BedrockService.Shared.Interfaces; +using System.Collections.Generic; + +namespace BedrockService.Service.Server.Management +{ + public interface IPlayerManager + { + IPlayer GetPlayerByXUID(string xuid); + void SetPlayer(IPlayer player); + List GetPlayers(); + void PlayerConnected(string username, string xuid); + void PlayerDisconnected(string xuid); + void UpdatePlayerFromCfg(string xuid, string username, string permission, string whitelisted, string ignoreMaxPlayerLimit); + } +} \ No newline at end of file diff --git a/BedrockService.backup/Service/Server/Management/PlayerManager.cs b/BedrockService.backup/Service/Server/Management/PlayerManager.cs new file mode 100644 index 00000000..c2238c39 --- /dev/null +++ b/BedrockService.backup/Service/Server/Management/PlayerManager.cs @@ -0,0 +1,71 @@ +using BedrockService.Shared.Classes; +using BedrockService.Shared.Interfaces; +using System; +using System.Collections.Generic; + +namespace BedrockService.Service.Server.Management +{ + public class PlayerManager : IPlayerManager + { + readonly IServerConfiguration _serverConfiguration; + readonly ILogger _logger; + + public PlayerManager(IServerConfiguration serverConfiguration, ILogger logger) + { + this._serverConfiguration = serverConfiguration; + this._logger = logger; + } + + public void PlayerConnected(string username, string xuid) + { + IPlayer playerFound = _serverConfiguration.GetPlayerByXuid(xuid); + if (playerFound == null) + { + _serverConfiguration.AddUpdatePlayer(new Player(xuid, username, DateTime.Now.Ticks.ToString(), "0", "0", false, _serverConfiguration.GetProp("default-player-permission-level").ToString(), false)); + return; + } + playerFound.UpdateTimes(DateTime.Now.Ticks.ToString(), playerFound.GetTimes()[2]); + _serverConfiguration.AddUpdatePlayer(playerFound); + } + + public void PlayerDisconnected(string xuid) + { + IPlayer playerFound = _serverConfiguration.GetPlayerByXuid(xuid); + string[] oldTimes = playerFound.GetTimes(); + playerFound.UpdateTimes(oldTimes[1], DateTime.Now.Ticks.ToString()); + _serverConfiguration.AddUpdatePlayer(playerFound); + } + + public void UpdatePlayerFromCfg(string xuid, string username, string permission, string whitelisted, string ignoreMaxPlayerLimit) + { + IPlayer playerFound = _serverConfiguration.GetPlayerByXuid(xuid); + if (playerFound == null) + { + playerFound = new Player(_serverConfiguration.GetProp("default-player-permission-level").ToString()); + playerFound.Initialize(xuid, username); + } + playerFound.UpdateRegistration(permission, whitelisted, ignoreMaxPlayerLimit); + } + + public IPlayer GetPlayerByXUID(string xuid) + { + if (GetPlayers().Count > 0) + return _serverConfiguration.GetPlayerByXuid(xuid); + return null; + } + + public void SetPlayer(IPlayer player) + { + try + { + _serverConfiguration.GetPlayerList()[_serverConfiguration.GetPlayerList().IndexOf(player)] = player; + } + catch + { + _serverConfiguration.GetPlayerList().Add(player); + } + } + + public List GetPlayers() => _serverConfiguration.GetPlayerList(); + } +} diff --git a/BedrockService/Service/packages.config b/BedrockService.backup/Service/packages.config similarity index 100% rename from BedrockService/Service/packages.config rename to BedrockService.backup/Service/packages.config diff --git a/BedrockService.backup/Service/upgrade.backup b/BedrockService.backup/Service/upgrade.backup new file mode 100644 index 00000000..4d52c68e --- /dev/null +++ b/BedrockService.backup/Service/upgrade.backup @@ -0,0 +1 @@ +Backup created at 1636641950 (11/11/2021 2:45:50 PM +00:00) \ No newline at end of file diff --git a/BedrockService/BedrockService.Shared/BedrockService.Shared.csproj b/BedrockService/BedrockService.Shared/BedrockService.Shared.csproj index 2eded606..36708052 100644 --- a/BedrockService/BedrockService.Shared/BedrockService.Shared.csproj +++ b/BedrockService/BedrockService.Shared/BedrockService.Shared.csproj @@ -1,79 +1,24 @@ - - - + - Debug - AnyCPU - {F146D5E8-EF1F-4785-9150-182631F059B7} + netstandard2.0 Library - Properties - BedrockService.Shared - BedrockService.Shared - v4.7.2 - 512 - true - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 + false - pdbonly - true - bin\Release\ - TRACE - prompt - 4 + 2 - - ..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll - - - - - - ..\packages\System.IO.Compression.ZipFile.4.3.0\lib\net46\System.IO.Compression.ZipFile.dll - True - True - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + all + - + + - \ No newline at end of file diff --git a/BedrockService/BedrockService.Shared/Classes/CommsKeyContainer.cs b/BedrockService/BedrockService.Shared/Classes/CommsKeyContainer.cs new file mode 100644 index 00000000..2df98f77 --- /dev/null +++ b/BedrockService/BedrockService.Shared/Classes/CommsKeyContainer.cs @@ -0,0 +1,95 @@ +using System; +using System.Collections.Generic; +using System.Security.Cryptography; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BedrockService.Shared.Classes +{ + [Serializable] + public class RSAContainer + { + public byte[] D; + public byte[] DP; + public byte[] DQ; + public byte[] Exponent; + public byte[] InverseQ; + public byte[] Modulus; + public byte[] P; + public byte[] Q; + + public RSAContainer(RSAParameters input) + { + D = input.D; + P = input.DP; + DP = input.DP; + Q = input.DQ; + DQ = input.DQ; + InverseQ = input.InverseQ; + Modulus = input.Modulus; + Exponent = input.Exponent; + } + + public RSAParameters GetPrivateKey() + { + return new RSAParameters() + { + D = this.D, + P = this.P, + DP = this.DP, + Q = this.Q, + DQ = this.DQ, + InverseQ = this.InverseQ, + Exponent = this.Exponent, + Modulus = this.Modulus + }; + } + + public RSAParameters GetPublicKey() + { + return new RSAParameters() + { + Modulus = this.Modulus, + Exponent = this.Exponent + }; + } + + public void SetPrivateKey(RSAParameters privateKey) + { + this.D = privateKey.D; + this.DP = privateKey.DP; + this.P = privateKey.P; + this.DQ = privateKey.DQ; + this.Q = privateKey.Q; + this.InverseQ = privateKey.InverseQ; + this.Modulus = privateKey.Modulus; + this.Exponent = privateKey.Exponent; + } + + public void SetPublicKey(RSAParameters publicKey) + { + this.Exponent = publicKey.Exponent; + this.Modulus = publicKey.Modulus; + } + } + + [Serializable] + public class CommsKeyContainer + { + public RSAContainer LocalPrivateKey = new RSAContainer(new RSAParameters()); + public RSAContainer RemotePublicKey = new RSAContainer(new RSAParameters()); + public byte[] AesKey; + public byte[] AesIV; + + public CommsKeyContainer() { } + + public CommsKeyContainer (RSAParameters priv, RSAParameters pub, byte[] key, byte[] IV) + { + LocalPrivateKey = new RSAContainer(priv); + RemotePublicKey = new RSAContainer(pub); + AesKey = key; + AesIV = IV; + } + } +} diff --git a/BedrockService/BedrockService.Shared/Classes/ServerLogger.cs b/BedrockService/BedrockService.Shared/Classes/ServerLogger.cs index 83fa2495..c7a43f5d 100644 --- a/BedrockService/BedrockService.Shared/Classes/ServerLogger.cs +++ b/BedrockService/BedrockService.Shared/Classes/ServerLogger.cs @@ -6,7 +6,7 @@ namespace BedrockService.Shared.Classes { - public class ServerLogger : ILogger + public class ServerLogger : IBedrockLogger { private StringBuilder OutString = new StringBuilder(); [NonSerialized] diff --git a/BedrockService/BedrockService.Shared/Interfaces/IBedrockConfiguration.cs b/BedrockService/BedrockService.Shared/Interfaces/IBedrockConfiguration.cs new file mode 100644 index 00000000..98c64609 --- /dev/null +++ b/BedrockService/BedrockService.Shared/Interfaces/IBedrockConfiguration.cs @@ -0,0 +1,25 @@ +using BedrockService.Shared.Classes; +using System.Collections.Generic; + +namespace BedrockService.Shared.Interfaces +{ + public interface IBedrockConfiguration + { + + void InitializeDefaults(); + + void ProcessConfiguration(string[] configEntries); + + bool SetProp(Property propToSet); + + Property GetProp(string keyName); + + List GetAllProps(); + + void SetAllProps(List newPropList); + + List GetLog(); + + void SetLog(List newLog); + } +} diff --git a/BedrockService/BedrockService.Shared/Interfaces/IBedrockLogger.cs b/BedrockService/BedrockService.Shared/Interfaces/IBedrockLogger.cs new file mode 100644 index 00000000..2d3706f6 --- /dev/null +++ b/BedrockService/BedrockService.Shared/Interfaces/IBedrockLogger.cs @@ -0,0 +1,11 @@ +namespace BedrockService.Shared.Interfaces +{ + public interface IBedrockLogger + { + void AppendLine(string incomingText); + void AppendText(string incomingText); + int Count(); + string FromIndex(int index); + string ToString(); + } +} diff --git a/BedrockService/BedrockService.Shared/Interfaces/IServerConfiguration.cs b/BedrockService/BedrockService.Shared/Interfaces/IServerConfiguration.cs index ed5c41bb..5ddf71dd 100644 --- a/BedrockService/BedrockService.Shared/Interfaces/IServerConfiguration.cs +++ b/BedrockService/BedrockService.Shared/Interfaces/IServerConfiguration.cs @@ -3,7 +3,7 @@ namespace BedrockService.Shared.Interfaces { - public interface IServerConfiguration : IConfiguration + public interface IServerConfiguration : IBedrockConfiguration { string GetServerName(); diff --git a/BedrockService/BedrockService.Shared/Interfaces/IServiceConfiguration.cs b/BedrockService/BedrockService.Shared/Interfaces/IServiceConfiguration.cs index 836d530b..794f9e10 100644 --- a/BedrockService/BedrockService.Shared/Interfaces/IServiceConfiguration.cs +++ b/BedrockService/BedrockService.Shared/Interfaces/IServiceConfiguration.cs @@ -2,7 +2,7 @@ namespace BedrockService.Shared.Interfaces { - public interface IServiceConfiguration : IConfiguration + public interface IServiceConfiguration : IBedrockConfiguration { IServerConfiguration GetServerInfoByName(string serverName); diff --git a/BedrockService/BedrockService.Shared/PackParser/MinecraftPackParser.cs b/BedrockService/BedrockService.Shared/PackParser/MinecraftPackParser.cs index c6c4127e..420bfe18 100644 --- a/BedrockService/BedrockService.Shared/PackParser/MinecraftPackParser.cs +++ b/BedrockService/BedrockService.Shared/PackParser/MinecraftPackParser.cs @@ -54,18 +54,18 @@ public override string ToString() public class MinecraftPackParser { private readonly IProcessInfo _processInfo; - private readonly ILogger _logger; + private readonly IBedrockLogger _logger; public DirectoryInfo PackExtractDirectory; public List FoundPacks = new List(); [JsonConstructor] - public MinecraftPackParser(ILogger logger, IProcessInfo processInfo) + public MinecraftPackParser(IBedrockLogger logger, IProcessInfo processInfo) { _processInfo = processInfo; _logger = logger; } - public MinecraftPackParser(byte[] fileContents, ILogger logger, IProcessInfo processInfo) + public MinecraftPackParser(byte[] fileContents, IBedrockLogger logger, IProcessInfo processInfo) { _logger = logger; PackExtractDirectory = new DirectoryInfo($@"{processInfo.GetDirectory()}\Temp"); @@ -81,7 +81,7 @@ public MinecraftPackParser(byte[] fileContents, ILogger logger, IProcessInfo pro ParseDirectory(PackExtractDirectory); } - public MinecraftPackParser(string[] files, DirectoryInfo extractDir, ILogger logger, IProcessInfo processInfo) + public MinecraftPackParser(string[] files, DirectoryInfo extractDir, IBedrockLogger logger, IProcessInfo processInfo) { PackExtractDirectory = extractDir; _logger = logger; diff --git a/BedrockService/Client/BedrockService.Client.csproj b/BedrockService/Client/BedrockService.Client.csproj index 70028f03..ac5f5d7c 100644 --- a/BedrockService/Client/BedrockService.Client.csproj +++ b/BedrockService/Client/BedrockService.Client.csproj @@ -1,17 +1,8 @@ - - - + - Debug - AnyCPU - {A2DAF70A-925A-4F4B-B7EE-CAACE75D6740} + net6.0-windows WinExe - BedrockService.Client - BedrockService.Client - v4.7.2 - 512 - true - true + false publish\ true Disk @@ -24,144 +15,26 @@ true 0 1.0.0.%2a - false false true + false + true + true - x86 - true - full - false ..\bin\Debug\ - DEBUG;TRACE - prompt - 4 - AnyCPU - pdbonly - true ..\bin\Release\ - TRACE - prompt - 4 BedrockService.Client.Forms.MainWindow - - ..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll - - - - - - - - - - - - - - - Form - - - AddNewServerForm.cs - - - Form - - - ClientConfigForm.cs - - - Form - - - ManagePacksForms.cs - - - Form - - - NewPlayerRegistrationForm.cs - - - Form - - - PlayerManagerForm.cs - - - - - - Form - - - PropEditorForm.cs - - - Form - - - MainWindow.cs - - - - - - AddNewServerForm.cs - - - ClientConfigForm.cs - - - ManagePacksForms.cs - - - NewPlayerRegistrationForm.cs - - - PlayerManagerForm.cs - - - PropEditorForm.cs - - - MainWindow.cs - - - ResXFileCodeGenerator - Resources.Designer.cs - Designer - - - True - Resources.resx - True - - - - SettingsSingleFileGenerator - Settings.Designer.cs - - - True - Settings.settings - True - - - - @@ -175,12 +48,13 @@ false - - - {f146d5e8-ef1f-4785-9150-182631f059b7} - BedrockService.Shared - + + + + + all + + - \ No newline at end of file diff --git a/BedrockService/Client/Forms/MainWindow.cs b/BedrockService/Client/Forms/MainWindow.cs index 2420abe2..3ea67965 100644 --- a/BedrockService/Client/Forms/MainWindow.cs +++ b/BedrockService/Client/Forms/MainWindow.cs @@ -27,12 +27,12 @@ public partial class MainWindow : Form private int _connectTimeout; private bool _followTail = false; private const int _connectTimeoutLimit = 3; - private readonly ILogger _logger; + private readonly IBedrockLogger _logger; private readonly IProcessInfo _processInfo; private readonly System.Timers.Timer _connectTimer = new System.Timers.Timer(100.0); private readonly LogManager _logManager; private readonly ConfigManager _configManager; - public MainWindow(IProcessInfo processInfo, ILogger logger) + public MainWindow(IProcessInfo processInfo, IBedrockLogger logger) { _processInfo = processInfo; _logger = logger; diff --git a/BedrockService/Client/Forms/ManagePacksForms.cs b/BedrockService/Client/Forms/ManagePacksForms.cs index 29fbfe9f..1345208d 100644 --- a/BedrockService/Client/Forms/ManagePacksForms.cs +++ b/BedrockService/Client/Forms/ManagePacksForms.cs @@ -16,10 +16,10 @@ namespace BedrockService.Client.Forms public partial class ManagePacksForms : Form { private readonly byte ServerIndex = 0x00; - private readonly ILogger Logger; + private readonly IBedrockLogger Logger; private readonly IProcessInfo ProcessInfo; private readonly DirectoryInfo PackExtractDir; - public ManagePacksForms(byte serverIndex, ILogger logger, IProcessInfo processInfo) + public ManagePacksForms(byte serverIndex, IBedrockLogger logger, IProcessInfo processInfo) { Logger = logger; PackExtractDir = new DirectoryInfo($@"{processInfo.GetDirectory()}\Temp"); diff --git a/BedrockService/Client/Management/ClientLogger.cs b/BedrockService/Client/Management/ClientLogger.cs index 8e9a737e..b666ccb9 100644 --- a/BedrockService/Client/Management/ClientLogger.cs +++ b/BedrockService/Client/Management/ClientLogger.cs @@ -6,7 +6,7 @@ namespace BedrockService.Client.Management { - public class ClientLogger : ILogger + public class ClientLogger : IBedrockLogger { public List Log = new List(); public StringBuilder OutString = new StringBuilder(); diff --git a/BedrockService/Client/Management/ConfigManager.cs b/BedrockService/Client/Management/ConfigManager.cs index 837c0fa8..1491e514 100644 --- a/BedrockService/Client/Management/ConfigManager.cs +++ b/BedrockService/Client/Management/ConfigManager.cs @@ -13,9 +13,9 @@ public class ConfigManager public string ConfigFile; public List HostConnectList = new List(); public string NBTStudioPath; - private readonly ILogger Logger; + private readonly IBedrockLogger Logger; - public ConfigManager(ILogger logger) + public ConfigManager(IBedrockLogger logger) { Logger = logger; ConfigFile = $@"{ConfigDir}\Config.conf"; diff --git a/BedrockService/Client/Management/FormManager.cs b/BedrockService/Client/Management/FormManager.cs index 37a2ca80..917961b5 100644 --- a/BedrockService/Client/Management/FormManager.cs +++ b/BedrockService/Client/Management/FormManager.cs @@ -11,7 +11,7 @@ namespace BedrockService.Client.Management public sealed class FormManager { private static readonly IProcessInfo processInfo = new ServiceProcessInfo(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), Path.GetFileName(Assembly.GetExecutingAssembly().Location), Process.GetCurrentProcess().Id, false, true); - private static readonly ILogger Logger = new ClientLogger(processInfo); + private static readonly IBedrockLogger Logger = new ClientLogger(processInfo); private static MainWindow main; private static TCPClient client; public static MainWindow MainWindow diff --git a/BedrockService/Client/Management/LogManager.cs b/BedrockService/Client/Management/LogManager.cs index 7e8a1d05..e5542b9b 100644 --- a/BedrockService/Client/Management/LogManager.cs +++ b/BedrockService/Client/Management/LogManager.cs @@ -15,9 +15,9 @@ class LogManager public bool Working = false; public List ServiceLogs = new List(); private IServiceConfiguration connectedHost; - private readonly ILogger Logger; + private readonly IBedrockLogger Logger; - public LogManager(ILogger logger) + public LogManager(IBedrockLogger logger) { Logger = logger; } diff --git a/BedrockService/Client/Networking/TCPClient.cs b/BedrockService/Client/Networking/TCPClient.cs index 15eb7cab..c3ad9b17 100644 --- a/BedrockService/Client/Networking/TCPClient.cs +++ b/BedrockService/Client/Networking/TCPClient.cs @@ -30,9 +30,9 @@ public class TCPClient private const int _heartbeatFailTimeoutLimit = 250; private bool _heartbeatRecieved; private bool _keepAlive; - private readonly ILogger _logger; + private readonly IBedrockLogger _logger; - public TCPClient (ILogger logger) + public TCPClient (IBedrockLogger logger) { _logger = logger; } diff --git a/BedrockService/Service/BedrockService.Service.csproj b/BedrockService/Service/BedrockService.Service.csproj index f0053119..e179c8b1 100644 --- a/BedrockService/Service/BedrockService.Service.csproj +++ b/BedrockService/Service/BedrockService.Service.csproj @@ -1,17 +1,6 @@ - - - + - Debug - AnyCPU - {13B2B5A8-71E9-49F4-9BFB-3C3B3C0A054C} Exe - BedrockService.Service - BedrockService.Service - v4.7.2 - 512 - true - true true C:\Users\crowbar\source\repos\BedrockManagementService\Releases\ true @@ -33,23 +22,11 @@ true - x86 - true - full - false ..\bin\Debug\ - DEBUG;TRACE - prompt 2 - AnyCPU - pdbonly - true ..\bin\Release\ - TRACE - prompt - 4 EE2403F7477A35F08B98B0A8FB2404C95BE04FEB @@ -63,112 +40,20 @@ false - - false - + + net6.0 + false + - - ..\packages\Microsoft.Bcl.AsyncInterfaces.5.0.0\lib\net461\Microsoft.Bcl.AsyncInterfaces.dll - False - True - - - ..\packages\Microsoft.Extensions.DependencyInjection.5.0.2\lib\net461\Microsoft.Extensions.DependencyInjection.dll - False - True - - - ..\packages\Microsoft.Extensions.DependencyInjection.Abstractions.5.0.0\lib\net461\Microsoft.Extensions.DependencyInjection.Abstractions.dll - False - True - - - ..\packages\NCrontab.Signed.3.3.2\lib\net35\NCrontab.Signed.dll - False - True - - - ..\packages\Newtonsoft.Json.13.0.1\lib\net45\Newtonsoft.Json.dll - False - True - - - - False - - False - - - ..\packages\System.IO.Compression.ZipFile.4.3.0\lib\net46\System.IO.Compression.ZipFile.dll - True - True - - - ..\packages\System.Runtime.InteropServices.RuntimeInformation.4.3.0\lib\net45\System.Runtime.InteropServices.RuntimeInformation.dll - True - False - - - - ..\packages\System.Threading.Tasks.Extensions.4.5.4\lib\net461\System.Threading.Tasks.Extensions.dll - True - - - - - - False - - - - - - ..\packages\Topshelf.4.3.0\lib\net452\Topshelf.dll - False - True - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -183,15 +68,28 @@ - - + + + + + + + + + + + + + + all + + + + + - - - {f146d5e8-ef1f-4785-9150-182631f059b7} - BedrockService.Shared - + + - \ No newline at end of file diff --git a/BedrockService/Service/Core/BedrockService.cs b/BedrockService/Service/Core/BedrockService.cs index 103be71c..05c82166 100644 --- a/BedrockService/Service/Core/BedrockService.cs +++ b/BedrockService/Service/Core/BedrockService.cs @@ -25,12 +25,11 @@ private enum ServiceStatus Stopping } private readonly IServiceConfiguration _serviceConfiguration; - private readonly ILogger _logger; + private readonly IBedrockLogger _logger; private readonly IProcessInfo _processInfo; private readonly IConfigurator _configurator; private readonly IUpdater _updater; private readonly ITCPListener _tCPListener; - private readonly IServiceThread _tcpThread; private readonly CrontabSchedule _shed; private readonly CrontabSchedule _updaterCron; private HostControl _hostControl; @@ -38,17 +37,20 @@ private enum ServiceStatus private System.Timers.Timer _updaterTimer; private System.Timers.Timer _cronTimer; - public BedrockService(IConfigurator configurator, IUpdater updater, ILogger logger, IServiceConfiguration serviceConfiguration, IProcessInfo serviceProcessInfo, ITCPListener tCPListener) + public BedrockService(IConfigurator configurator, IUpdater updater, IBedrockLogger logger, IServiceConfiguration serviceConfiguration, IProcessInfo serviceProcessInfo, ITCPListener tCPListener) { _tCPListener = tCPListener; _configurator = configurator; + _configurator.LoadAllConfigurations().Wait(); _serviceConfiguration = serviceConfiguration; _processInfo = serviceProcessInfo; _updater = updater; + _updater.CheckUpdates().Wait(); _logger = logger; _shed = CrontabSchedule.TryParse(serviceConfiguration.GetProp("BackupCron").ToString()); _updaterCron = CrontabSchedule.TryParse(serviceConfiguration.GetProp("UpdateCron").ToString()); Initialize(); + _tCPListener.SetKeyContainer(_configurator.GetKeyContainer()); } public bool Start(HostControl hostControl) @@ -85,7 +87,6 @@ public bool Stop(HostControl hostControl) while (brs.GetServerStatus() == BedrockServer.ServerStatus.Stopping && !Program.IsExiting) Thread.Sleep(100); } - _tcpThread.CloseThread(); return true; } catch (Exception e) diff --git a/BedrockService/Service/Core/Interfaces/IService.cs b/BedrockService/Service/Core/Interfaces/IService.cs index b4ef9e09..578a6c4d 100644 --- a/BedrockService/Service/Core/Interfaces/IService.cs +++ b/BedrockService/Service/Core/Interfaces/IService.cs @@ -1,10 +1,10 @@ -using System.Threading.Tasks; +using Microsoft.Extensions.Hosting; +using System.Threading.Tasks; namespace BedrockService.Service.Core { - public interface IService + public interface IService : IHostedService { Task InitializeHost(); - Topshelf.TopshelfExitCode Run(); } } diff --git a/BedrockService/Service/Core/Service.cs b/BedrockService/Service/Core/Service.cs index 87decbd5..f3a84c60 100644 --- a/BedrockService/Service/Core/Service.cs +++ b/BedrockService/Service/Core/Service.cs @@ -15,9 +15,9 @@ public class Service : IService { private readonly IBedrockService _bedrockService; private Host _host; - private readonly ILogger _logger; + private readonly IBedrockLogger _logger; - public Service(ILogger logger, IBedrockService bedrockService) + public Service(IBedrockLogger logger, IBedrockService bedrockService, NetworkStrategyLookup lookup) { _logger = logger; _bedrockService = bedrockService; @@ -69,6 +69,18 @@ await Task.Run(() => }); } - public TopshelfExitCode Run() => _host.Run(); + public Task StartAsync(CancellationToken cancellationToken) + { + return Task.Run(() => + { + InitializeHost().Wait(); + _host.Run(); + }); + } + + public Task StopAsync(CancellationToken cancellationToken) + { + return Task.Delay(100); + } } } diff --git a/BedrockService/Service/Logging/ServiceLogger.cs b/BedrockService/Service/Logging/ServiceLogger.cs index 7d3237fe..e4ee0a8c 100644 --- a/BedrockService/Service/Logging/ServiceLogger.cs +++ b/BedrockService/Service/Logging/ServiceLogger.cs @@ -6,7 +6,7 @@ namespace BedrockService.Service.Logging { - public class ServiceLogger : ILogger + public class ServiceLogger : IBedrockLogger { private readonly IServiceConfiguration _serviceConfiguration; private StringBuilder _outputString = new StringBuilder(); diff --git a/BedrockService/Service/Management/ConfigManager.cs b/BedrockService/Service/Management/ConfigManager.cs index 4a9b49ac..5c1b13b3 100644 --- a/BedrockService/Service/Management/ConfigManager.cs +++ b/BedrockService/Service/Management/ConfigManager.cs @@ -9,6 +9,9 @@ using System.Text; using System.Threading; using System.Threading.Tasks; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; +using System.Runtime.Serialization.Formatters.Binary; namespace BedrockService.Service.Management { @@ -16,27 +19,37 @@ public class ConfigManager : IConfigurator { private readonly string _configDir; private readonly string _globalFile; + private readonly string _clientKeyPath; + private readonly string _commsKeyPath; private string _loadedVersion; private static readonly object _fileLock = new object(); private readonly IServiceConfiguration _serviceConfiguration; private readonly IProcessInfo _processInfo; - private readonly ILogger _logger; + private readonly IBedrockLogger _logger; + private CommsKeyContainer _keyContainer; + private RSAParameters _serviceKey; + private RSAParameters _clientKey; - public ConfigManager(IProcessInfo processInfo, IServiceConfiguration serviceConfiguration, ILogger logger) + public ConfigManager(IProcessInfo processInfo, IServiceConfiguration serviceConfiguration, IBedrockLogger logger) { _processInfo = processInfo; _serviceConfiguration = serviceConfiguration; _logger = logger; _configDir = $@"{_processInfo.GetDirectory()}\Server\Configs"; _globalFile = $@"{_processInfo.GetDirectory()}\Service\Globals.conf"; + _clientKeyPath = $@"{_processInfo.GetDirectory()}\Client\ClientKey.dat"; + _commsKeyPath = $@"{_processInfo.GetDirectory()}\Service\CommsKey.dat"; } public async Task LoadAllConfigurations() { await Task.Run(() => { + BinaryFormatter formatter = new BinaryFormatter(); if (!Directory.Exists(_configDir)) Directory.CreateDirectory(_configDir); + if (!Directory.Exists($@"{_processInfo.GetDirectory()}\Client")) + Directory.CreateDirectory($@"{_processInfo.GetDirectory()}\Client"); if (!Directory.Exists($@"{_configDir}\KnownPlayers\Backups")) Directory.CreateDirectory($@"{_configDir}\KnownPlayers\Backups"); if (!Directory.Exists($@"{_configDir}\RegisteredPlayers\Backups")) @@ -45,6 +58,44 @@ await Task.Run(() => Directory.CreateDirectory($@"{_configDir}\Backups"); if (File.Exists($@"{_configDir}\..\bedrock_ver.ini")) _loadedVersion = File.ReadAllText($@"{_configDir}\..\bedrock_ver.ini"); + if(!File.Exists(_commsKeyPath)) + { + RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(2048); + using (AesCryptoServiceProvider aes = new AesCryptoServiceProvider()) + { + CommsKeyContainer serviceKeys = new CommsKeyContainer(); + CommsKeyContainer clientKeys = new CommsKeyContainer(); + serviceKeys.LocalPrivateKey.SetPrivateKey(rsa.ExportParameters(true)); + clientKeys.RemotePublicKey.SetPublicKey(rsa.ExportParameters(false)); + rsa = new RSACryptoServiceProvider(2048); + clientKeys.LocalPrivateKey.SetPrivateKey(rsa.ExportParameters(true)); + serviceKeys.RemotePublicKey.SetPublicKey(rsa.ExportParameters(false)); + aes.GenerateKey(); + aes.GenerateIV(); + serviceKeys.AesKey = aes.Key; + clientKeys.AesKey = aes.Key; + serviceKeys.AesIV = aes.IV; + clientKeys.AesIV = aes.IV; + formatter.Serialize(File.Create(_clientKeyPath), clientKeys); + formatter.Serialize(File.Create(_commsKeyPath), serviceKeys); + } + rsa.Clear(); + rsa.Dispose(); + } + else + { + try + { + _keyContainer = (CommsKeyContainer)formatter.Deserialize(File.Open(_commsKeyPath, FileMode.Open)); + _serviceKey = _keyContainer.LocalPrivateKey.GetPrivateKey(); + _clientKey = _keyContainer.RemotePublicKey.GetPrivateKey(); + } + catch + { + _logger.AppendLine("Error loading Encryption keys!"); + } + } + ServerInfo serverInfo; LoadGlobals(); _serviceConfiguration.GetServerList().Clear(); @@ -475,12 +526,14 @@ private bool DeletePlayerFiles(IServerConfiguration serverInfo) catch { return false; } } - public Task LoadConfiguration(IConfiguration configuration) + public CommsKeyContainer GetKeyContainer() => _keyContainer; + + public Task LoadConfiguration(IBedrockConfiguration configuration) { throw new NotImplementedException(); } - public Task SaveConfiguration(IConfiguration configuration) + public Task SaveConfiguration(IBedrockConfiguration configuration) { throw new NotImplementedException(); } diff --git a/BedrockService/Service/Management/IConfigurator.cs b/BedrockService/Service/Management/IConfigurator.cs index a547140f..ce38925c 100644 --- a/BedrockService/Service/Management/IConfigurator.cs +++ b/BedrockService/Service/Management/IConfigurator.cs @@ -11,15 +11,16 @@ public interface IConfigurator void DeleteBackupsForServer(byte serverIndex, List list); List EnumerateBackupsForServer(byte serverIndex); Task LoadAllConfigurations(); - Task LoadConfiguration(IConfiguration configuration); + Task LoadConfiguration(IBedrockConfiguration configuration); void LoadPlayerDatabase(IServerConfiguration server); void LoadRegisteredPlayers(IServerConfiguration server); void RemoveServerConfigs(IServerConfiguration serverInfo, NetworkMessageFlags flag); Task ReplaceServerBuild(IServerConfiguration server); - Task SaveConfiguration(IConfiguration configuration); + Task SaveConfiguration(IBedrockConfiguration configuration); void SaveGlobalFile(); void SaveKnownPlayerDatabase(IServerConfiguration server); void SaveServerProps(IServerConfiguration server, bool SaveServerInfo); void WriteJSONFiles(IServerConfiguration server); + CommsKeyContainer GetKeyContainer(); } } \ No newline at end of file diff --git a/BedrockService/Service/Networking/ITCPListener.cs b/BedrockService/Service/Networking/ITCPListener.cs index b2af7c6c..f726dc8f 100644 --- a/BedrockService/Service/Networking/ITCPListener.cs +++ b/BedrockService/Service/Networking/ITCPListener.cs @@ -11,5 +11,7 @@ public interface ITCPListener : IMessageSender void ResetListener(); void SetStrategyDictionaries(Dictionary standard, Dictionary flagged); + + void SetKeyContainer(CommsKeyContainer keyContainer); } } diff --git a/BedrockService/Service/Networking/NetworkStrategyLookup.cs b/BedrockService/Service/Networking/NetworkStrategyLookup.cs index 75bb3577..10d1de25 100644 --- a/BedrockService/Service/Networking/NetworkStrategyLookup.cs +++ b/BedrockService/Service/Networking/NetworkStrategyLookup.cs @@ -15,6 +15,8 @@ using System.Text; using System.Threading; using System.Threading.Tasks; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; namespace BedrockService.Service.Networking { @@ -23,7 +25,7 @@ public class NetworkStrategyLookup private readonly Dictionary _standardMessageLookup; private readonly Dictionary _flaggedMessageLookup; - public NetworkStrategyLookup(ITCPListener messageSender, IBedrockService service, ILogger logger, IConfigurator configurator, IServiceConfiguration serviceConfiguration, IProcessInfo processInfo, IUpdater updater) + public NetworkStrategyLookup(ITCPListener messageSender, IBedrockService service, IBedrockLogger logger, IConfigurator configurator, IServiceConfiguration serviceConfiguration, IProcessInfo processInfo, IUpdater updater) { _standardMessageLookup = new Dictionary() { @@ -198,10 +200,10 @@ public void ParseMessage(byte[] data, byte serverIndex) class ServerCommand : IMessageParser { private readonly IBedrockService _service; - private readonly ILogger _logger; + private readonly IBedrockLogger _logger; private readonly IMessageSender _messageSender; - public ServerCommand(IMessageSender messageSender, IBedrockService service, ILogger logger) + public ServerCommand(IMessageSender messageSender, IBedrockService service, IBedrockLogger logger) { _messageSender = messageSender; _service = service; @@ -222,9 +224,9 @@ class PackList : IMessageParser private readonly IMessageSender _messageSender; private readonly IServiceConfiguration _serviceConfiguration; private readonly IProcessInfo _processInfo; - private readonly ILogger _logger; + private readonly IBedrockLogger _logger; - public PackList(IMessageSender messageSender, IProcessInfo processInfo, IServiceConfiguration serviceConfiguration, ILogger logger) + public PackList(IMessageSender messageSender, IProcessInfo processInfo, IServiceConfiguration serviceConfiguration, IBedrockLogger logger) { _logger = logger; _messageSender = messageSender; @@ -344,9 +346,9 @@ class PackFile : IMessageParser { private readonly IServiceConfiguration _serviceConfiguration; private readonly IProcessInfo _serviceProcessInfo; - private readonly ILogger _logger; + private readonly IBedrockLogger _logger; - public PackFile(IServiceConfiguration serviceConfiguration, IProcessInfo serviceProcessInfo, ILogger logger) + public PackFile(IServiceConfiguration serviceConfiguration, IProcessInfo serviceProcessInfo, IBedrockLogger logger) { _logger = logger; _serviceProcessInfo = serviceProcessInfo; @@ -394,12 +396,12 @@ public void ParseMessage(byte[] data, byte serverIndex) class Connect : IMessageParser { - private readonly IMessageSender _messageSender; + private readonly IMessageSender _iTCPListener; private readonly IServiceConfiguration _serviceConfiguration; - public Connect(IMessageSender messageSender, IServiceConfiguration serviceConfiguration) + public Connect(ITCPListener iTCPListener, IServiceConfiguration serviceConfiguration) { - _messageSender = messageSender; + _iTCPListener = iTCPListener; _serviceConfiguration = serviceConfiguration; } @@ -412,7 +414,7 @@ public void ParseMessage(byte[] data, byte serverIndex) }; string jsonString = JsonConvert.SerializeObject(_serviceConfiguration, indented, settings); byte[] serializeToBytes = Encoding.UTF8.GetBytes(jsonString); - _messageSender.SendData(serializeToBytes, NetworkMessageSource.Service, NetworkMessageDestination.Client, NetworkMessageTypes.Connect); + _iTCPListener.SendData(serializeToBytes, NetworkMessageSource.Service, NetworkMessageDestination.Client, NetworkMessageTypes.Connect); } } @@ -584,7 +586,7 @@ public void ParseMessage(byte[] data, byte serverIndex) int srvTextLen; int clientCurLen; int loop; - ILogger srvText; + IBedrockLogger srvText; if (srvName != "Service") { try diff --git a/BedrockService/Service/Networking/TCPListener.cs b/BedrockService/Service/Networking/TCPListener.cs index 29cdd0b1..77a581c3 100644 --- a/BedrockService/Service/Networking/TCPListener.cs +++ b/BedrockService/Service/Networking/TCPListener.cs @@ -8,6 +8,9 @@ using System.Net; using System.Net.Sockets; using System.Threading; +using System.IO; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; namespace BedrockService.Service.Networking { @@ -17,7 +20,7 @@ public class TCPListener : ITCPListener, IMessageSender private TcpListener _inListener; private NetworkStream _stream; private readonly IServiceConfiguration _serviceConfiguration; - private readonly ILogger _logger; + private readonly IBedrockLogger _logger; private IServiceThread _tcpThread; private IServiceThread _clientThread; private IServiceThread _heartbeatThread; @@ -30,8 +33,9 @@ public class TCPListener : ITCPListener, IMessageSender private Dictionary _flaggedMessageLookup; private readonly IPAddress _ipAddress = IPAddress.Parse("0.0.0.0"); private readonly System.Timers.Timer _reconnectTimer = new System.Timers.Timer(500.0); + private CommsKeyContainer _keyContainer; - public TCPListener(IServiceConfiguration serviceConfiguration, ILogger logger) + public TCPListener(IServiceConfiguration serviceConfiguration, IBedrockLogger logger) { _logger = logger; _serviceConfiguration = serviceConfiguration; @@ -113,6 +117,7 @@ public void SendData(byte[] bytesToSend, NetworkMessageSource source, NetworkMes byteHeader[7] = (byte)type; byteHeader[8] = (byte)status; Buffer.BlockCopy(bytesToSend, 0, byteHeader, 9, bytesToSend.Length); + if (_clientThread.IsAlive()) { try @@ -263,5 +268,29 @@ private void SendBackHeatbeatSignal() } _logger.AppendLine("HeartBeatSender exited."); } + + public void SetKeyContainer(CommsKeyContainer keyContainer) + { + _keyContainer = keyContainer; + } + + public bool VerifyClientData(byte[] certificate) + { + if(certificate != null) + { + byte[] decrypted; + using(RSACryptoServiceProvider rsa = new RSACryptoServiceProvider()) + { + rsa.ImportParameters(_keyContainer.LocalPrivateKey.GetPrivateKey()); + + } + using(RSACryptoServiceProvider rsa = new RSACryptoServiceProvider()) + { + rsa.ImportParameters(_keyContainer.RemotePublicKey.GetPrivateKey()); + + } + } + return true; + } } } \ No newline at end of file diff --git a/BedrockService/Service/Networking/Updater.cs b/BedrockService/Service/Networking/Updater.cs index 8be6efb1..84ef3e91 100644 --- a/BedrockService/Service/Networking/Updater.cs +++ b/BedrockService/Service/Networking/Updater.cs @@ -13,12 +13,12 @@ namespace BedrockService.Service.Networking public class Updater : IUpdater { private bool _versionChanged = false; - private readonly ILogger _logger; + private readonly IBedrockLogger _logger; private readonly IServiceConfiguration _serviceConfiguration; private readonly IProcessInfo _processInfo; private readonly string _version; - public Updater(IProcessInfo processInfo, ILogger logger, IServiceConfiguration serviceConfiguration) + public Updater(IProcessInfo processInfo, IBedrockLogger logger, IServiceConfiguration serviceConfiguration) { _serviceConfiguration = serviceConfiguration; _processInfo = processInfo; diff --git a/BedrockService/Service/Program.cs b/BedrockService/Service/Program.cs index d3a6fc02..b461fb2f 100644 --- a/BedrockService/Service/Program.cs +++ b/BedrockService/Service/Program.cs @@ -1,80 +1,62 @@ -using BedrockService.Service.Core; +// This application entry point is based on ASP.NET Core new project templates and is included +// as a starting point for app host configuration. +// This file may need updated according to the specific scenario of the application being upgraded. +// For more information on ASP.NET Core hosting, see https://docs.microsoft.com/aspnet/core/fundamentals/host/web-host + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Threading.Tasks; +using BedrockService.Service.Core; using BedrockService.Service.Logging; using BedrockService.Service.Management; using BedrockService.Service.Networking; using BedrockService.Shared.Classes; using BedrockService.Shared.Interfaces; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; -using System; -using System.Diagnostics; -using System.IO; -using System.Reflection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; using Topshelf; namespace BedrockService.Service { - class Program + public class Program { public static bool IsExiting = false; - private static bool _isDebugEnabled = false; + private static bool _isDebugEnabled; private static bool _isConsoleMode = false; - private static readonly IServiceCollection _services = new ServiceCollection(); - - static void Main(string[] args) + public static void Main(string[] args) { if (args.Length > 0) { _isDebugEnabled = args[0].ToLower() == "-debug"; } - ConfigureServices(_services); - IServiceProvider serviceProvider = _services.BuildServiceProvider(); - serviceProvider.GetRequiredService().LoadAllConfigurations().Wait(); - serviceProvider.GetRequiredService().CheckUpdates().Wait(); - IService service = serviceProvider.GetRequiredService(); - ILogger Logger = serviceProvider.GetRequiredService(); - IProcessInfo ProcessInfo = serviceProvider.GetRequiredService(); - serviceProvider.GetRequiredService(); if (args.Length == 0 || Environment.UserInteractive) { _isConsoleMode = true; - Logger.AppendLine("BedrockService startup detected in Console mode."); } - else - { - Logger.AppendLine("BedrockService startup detected in Service mode."); - foreach (Process process in Process.GetProcesses()) - { - if (process.Id != ProcessInfo.GetProcessPID() && process.ProcessName.StartsWith("BedrockService.") && process.ProcessName != "BedrockService.Client") - { - Logger.AppendLine($"Found additional running instance of {process.ProcessName} with ID {process.Id}"); - Logger.AppendLine($"Killing process with id {process.Id}"); - process.Kill(); - } - } - } - service.InitializeHost().Wait(); - TopshelfExitCode rc = service.Run(); - var exitCode = (int)Convert.ChangeType(rc, rc.GetTypeCode()); - if (_isDebugEnabled) - { - Console.Write("Program is force-quitting. Press any key to exit."); - Console.Out.Flush(); - Console.ReadLine(); - } - Environment.ExitCode = exitCode; - } - private static void ConfigureServices(IServiceCollection services) - { - IProcessInfo processInfo = new ServiceProcessInfo(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), Path.GetFileName(Assembly.GetExecutingAssembly().Location), Process.GetCurrentProcess().Id, _isDebugEnabled, _isConsoleMode); - services.AddSingleton(processInfo); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); + CreateHostBuilder(args).Build().Run(); } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Microsoft.Extensions.Hosting.Host.CreateDefaultBuilder(args) + .ConfigureServices((hostContext, services) => + { + IProcessInfo processInfo = new ServiceProcessInfo(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), Path.GetFileName(Assembly.GetExecutingAssembly().Location), Process.GetCurrentProcess().Id, _isDebugEnabled, _isConsoleMode); + services.AddHostedService() + .AddSingleton(processInfo) + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton(); + }); } } diff --git a/BedrockService/Service/Properties/AssemblyInfo.cs b/BedrockService/Service/Properties/AssemblyInfo.cs index 37ca46b4..a875f734 100644 --- a/BedrockService/Service/Properties/AssemblyInfo.cs +++ b/BedrockService/Service/Properties/AssemblyInfo.cs @@ -31,5 +31,5 @@ // 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("2.5.0.0")] +[assembly: AssemblyFileVersion("2.5.0.0")] diff --git a/BedrockService/Service/Server/BedrockServer.cs b/BedrockService/Service/Server/BedrockServer.cs index 87e5bf22..8056ac61 100644 --- a/BedrockService/Service/Server/BedrockServer.cs +++ b/BedrockService/Service/Server/BedrockServer.cs @@ -28,8 +28,8 @@ public class BedrockServer : IBedrockServer private readonly IServiceConfiguration _serviceConfiguration; private readonly IPlayerManager _playerManager; private readonly IConfigurator _configurator; - private readonly ILogger _logger; - private readonly ILogger _serverLogger; + private readonly IBedrockLogger _logger; + private readonly IBedrockLogger _serverLogger; private readonly string _servicePath; private const string _startupMessage = "[INFO] Server started."; public enum ServerStatus @@ -40,7 +40,7 @@ public enum ServerStatus Started } - public BedrockServer(IServerConfiguration serverConfiguration, IConfigurator configurator, ILogger logger, IServiceConfiguration serviceConfiguration, IProcessInfo processInfo) + public BedrockServer(IServerConfiguration serverConfiguration, IConfigurator configurator, IBedrockLogger logger, IServiceConfiguration serviceConfiguration, IProcessInfo processInfo) { _serverConfiguration = serverConfiguration; _serviceConfiguration = serviceConfiguration; @@ -445,7 +445,7 @@ public bool RollbackToBackup(byte serverIndex, string folderName) return false; } - public ILogger GetLogger() => _serverLogger; + public IBedrockLogger GetLogger() => _serverLogger; public IPlayerManager GetPlayerManager() => _playerManager; diff --git a/BedrockService/Service/Server/IBedrockServer.cs b/BedrockService/Service/Server/IBedrockServer.cs index f9967d3b..4f61eed1 100644 --- a/BedrockService/Service/Server/IBedrockServer.cs +++ b/BedrockService/Service/Server/IBedrockServer.cs @@ -17,6 +17,6 @@ public interface IBedrockServer BedrockServer.ServerStatus GetServerStatus(); void SetServerStatus(BedrockServer.ServerStatus newStatus); IPlayerManager GetPlayerManager(); - ILogger GetLogger(); + IBedrockLogger GetLogger(); } } diff --git a/BedrockService/Service/Server/Management/PlayerManager.cs b/BedrockService/Service/Server/Management/PlayerManager.cs index c2238c39..473041cd 100644 --- a/BedrockService/Service/Server/Management/PlayerManager.cs +++ b/BedrockService/Service/Server/Management/PlayerManager.cs @@ -8,9 +8,9 @@ namespace BedrockService.Service.Server.Management public class PlayerManager : IPlayerManager { readonly IServerConfiguration _serverConfiguration; - readonly ILogger _logger; + readonly IBedrockLogger _logger; - public PlayerManager(IServerConfiguration serverConfiguration, ILogger logger) + public PlayerManager(IServerConfiguration serverConfiguration, IBedrockLogger logger) { this._serverConfiguration = serverConfiguration; this._logger = logger; diff --git a/BedrockService/Service/Startup.cs b/BedrockService/Service/Startup.cs new file mode 100644 index 00000000..489c64b8 --- /dev/null +++ b/BedrockService/Service/Startup.cs @@ -0,0 +1,88 @@ +// This Startup file is based on ASP.NET Core new project templates and is included +// as a starting point for DI registration and HTTP request processing pipeline configuration. +// This file will need updated according to the specific scenario of the application being upgraded. +// For more information on ASP.NET Core startup files, see https://docs.microsoft.com/aspnet/core/fundamentals/startup + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Threading.Tasks; +using BedrockService.Service.Core; +using BedrockService.Service.Logging; +using BedrockService.Service.Management; +using BedrockService.Service.Networking; +using BedrockService.Shared.Classes; +using BedrockService.Shared.Interfaces; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.NewtonsoftJson; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace BedrockService.Service +{ + public class Startup + { + private readonly bool _isDebug; + private readonly bool _isConsole; + public Startup(bool isDebug, bool isConsole) + { + _isDebug = isDebug; + _isConsole = isConsole; + } + + public IConfiguration Configuration { get; } + + // This method gets called by the runtime. Use this method to add services to the container. + public void ConfigureServices(IServiceCollection services) + { + services.AddControllersWithViews(ConfigureMvcOptions) + // Newtonsoft.Json is added for compatibility reasons + // The recommended approach is to use System.Text.Json for serialization + // Visit the following link for more guidance about moving away from Newtonsoft.Json to System.Text.Json + // https://docs.microsoft.com/dotnet/standard/serialization/system-text-json-migrate-from-newtonsoft-how-to + .AddNewtonsoftJson(options => + { + options.UseMemberCasing(); + }); + IProcessInfo processInfo = new ServiceProcessInfo(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), Path.GetFileName(Assembly.GetExecutingAssembly().Location), Process.GetCurrentProcess().Id, _isDebug, _isConsole); + services.AddSingleton(processInfo); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseStaticFiles(); + app.UseRouting(); + app.UseAuthorization(); + app.UseEndpoints(endpoints => + { + endpoints.MapControllerRoute( + name: "default", + pattern: "{controller=Home}/{action=Index}/{id?}"); + }); + } + + private void ConfigureMvcOptions(MvcOptions mvcOptions) + { + } + } +} diff --git a/BedrockService/Service/appsettings.Development.json b/BedrockService/Service/appsettings.Development.json new file mode 100644 index 00000000..c9294ca4 --- /dev/null +++ b/BedrockService/Service/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/BedrockService/Service/appsettings.json b/BedrockService/Service/appsettings.json new file mode 100644 index 00000000..93b64d37 --- /dev/null +++ b/BedrockService/Service/appsettings.json @@ -0,0 +1,10 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + }, + "AllowedHosts": "*" +} From fa60bab47db8639141b56486f1fbb103e38b962e Mon Sep 17 00:00:00 2001 From: crowbarmaster Date: Mon, 6 Dec 2021 12:09:44 -0500 Subject: [PATCH 02/62] ASP.NET template for GUI. --- .../BedrockManagementServiceASP.csproj | 13 ++++++++++++ .../BedrockManagementServiceASP/Program.cs | 10 +++++++++ .../Properties/launchSettings.json | 11 ++++++++++ .../BedrockManagementServiceASP/Worker.cs | 21 +++++++++++++++++++ .../appsettings.Development.json | 8 +++++++ .../appsettings.json | 8 +++++++ BedrockService/BedrockService.sln | 17 ++++++++++----- 7 files changed, 83 insertions(+), 5 deletions(-) create mode 100644 BedrockService/BedrockManagementServiceASP/BedrockManagementServiceASP.csproj create mode 100644 BedrockService/BedrockManagementServiceASP/Program.cs create mode 100644 BedrockService/BedrockManagementServiceASP/Properties/launchSettings.json create mode 100644 BedrockService/BedrockManagementServiceASP/Worker.cs create mode 100644 BedrockService/BedrockManagementServiceASP/appsettings.Development.json create mode 100644 BedrockService/BedrockManagementServiceASP/appsettings.json diff --git a/BedrockService/BedrockManagementServiceASP/BedrockManagementServiceASP.csproj b/BedrockService/BedrockManagementServiceASP/BedrockManagementServiceASP.csproj new file mode 100644 index 00000000..e86a23dd --- /dev/null +++ b/BedrockService/BedrockManagementServiceASP/BedrockManagementServiceASP.csproj @@ -0,0 +1,13 @@ + + + + net6.0 + enable + enable + dotnet-BedrockManagementServiceASP-4ABD78ED-14F3-43D6-821D-3F1C9B453A6F + + + + + + diff --git a/BedrockService/BedrockManagementServiceASP/Program.cs b/BedrockService/BedrockManagementServiceASP/Program.cs new file mode 100644 index 00000000..3a33c382 --- /dev/null +++ b/BedrockService/BedrockManagementServiceASP/Program.cs @@ -0,0 +1,10 @@ +using BedrockManagementServiceASP; + +IHost host = Host.CreateDefaultBuilder(args) + .ConfigureServices(services => + { + services.AddHostedService(); + }) + .Build(); + +await host.RunAsync(); diff --git a/BedrockService/BedrockManagementServiceASP/Properties/launchSettings.json b/BedrockService/BedrockManagementServiceASP/Properties/launchSettings.json new file mode 100644 index 00000000..28900b50 --- /dev/null +++ b/BedrockService/BedrockManagementServiceASP/Properties/launchSettings.json @@ -0,0 +1,11 @@ +{ + "profiles": { + "BedrockManagementServiceASP": { + "commandName": "Project", + "dotnetRunMessages": true, + "environmentVariables": { + "DOTNET_ENVIRONMENT": "Development" + } + } + } +} diff --git a/BedrockService/BedrockManagementServiceASP/Worker.cs b/BedrockService/BedrockManagementServiceASP/Worker.cs new file mode 100644 index 00000000..0bc53b32 --- /dev/null +++ b/BedrockService/BedrockManagementServiceASP/Worker.cs @@ -0,0 +1,21 @@ +namespace BedrockManagementServiceASP +{ + public class Worker : BackgroundService + { + private readonly ILogger _logger; + + public Worker(ILogger logger) + { + _logger = logger; + } + + protected override async Task ExecuteAsync(CancellationToken stoppingToken) + { + while (!stoppingToken.IsCancellationRequested) + { + _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now); + await Task.Delay(1000, stoppingToken); + } + } + } +} \ No newline at end of file diff --git a/BedrockService/BedrockManagementServiceASP/appsettings.Development.json b/BedrockService/BedrockManagementServiceASP/appsettings.Development.json new file mode 100644 index 00000000..b2dcdb67 --- /dev/null +++ b/BedrockService/BedrockManagementServiceASP/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/BedrockService/BedrockManagementServiceASP/appsettings.json b/BedrockService/BedrockManagementServiceASP/appsettings.json new file mode 100644 index 00000000..b2dcdb67 --- /dev/null +++ b/BedrockService/BedrockManagementServiceASP/appsettings.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/BedrockService/BedrockService.sln b/BedrockService/BedrockService.sln index 29c749fe..c5b22238 100644 --- a/BedrockService/BedrockService.sln +++ b/BedrockService/BedrockService.sln @@ -1,20 +1,22 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.29709.97 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BedrockService.Service", "Service\BedrockService.Service.csproj", "{13B2B5A8-71E9-49F4-9BFB-3C3B3C0A054C}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BedrockService.Service", "Service\BedrockService.Service.csproj", "{13B2B5A8-71E9-49F4-9BFB-3C3B3C0A054C}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{83D2040C-760E-4475-ACBE-587536C90911}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BedrockService.Client", "Client\BedrockService.Client.csproj", "{A2DAF70A-925A-4F4B-B7EE-CAACE75D6740}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BedrockService.Client", "Client\BedrockService.Client.csproj", "{A2DAF70A-925A-4F4B-B7EE-CAACE75D6740}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BedrockServiceUnitTests", "BedrockServiceUnitTests\BedrockServiceUnitTests.csproj", "{216E8E7A-77A9-4077-A5B5-2C63424E7B83}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BedrockService.Shared", "BedrockService.Shared\BedrockService.Shared.csproj", "{F146D5E8-EF1F-4785-9150-182631F059B7}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BedrockService.Shared", "BedrockService.Shared\BedrockService.Shared.csproj", "{F146D5E8-EF1F-4785-9150-182631F059B7}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClientUnitTests", "ClientUnitTests\ClientUnitTests.csproj", "{550B6A98-60C3-4765-BFED-2CAA0AFFAEBB}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BedrockManagementServiceASP", "BedrockManagementServiceASP\BedrockManagementServiceASP.csproj", "{4AA65E9F-E3BE-4173-BE3B-BD4DFCD21C40}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -41,6 +43,10 @@ Global {550B6A98-60C3-4765-BFED-2CAA0AFFAEBB}.Debug|Any CPU.Build.0 = Debug|Any CPU {550B6A98-60C3-4765-BFED-2CAA0AFFAEBB}.Release|Any CPU.ActiveCfg = Release|Any CPU {550B6A98-60C3-4765-BFED-2CAA0AFFAEBB}.Release|Any CPU.Build.0 = Release|Any CPU + {4AA65E9F-E3BE-4173-BE3B-BD4DFCD21C40}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4AA65E9F-E3BE-4173-BE3B-BD4DFCD21C40}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4AA65E9F-E3BE-4173-BE3B-BD4DFCD21C40}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4AA65E9F-E3BE-4173-BE3B-BD4DFCD21C40}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -49,6 +55,7 @@ Global {13B2B5A8-71E9-49F4-9BFB-3C3B3C0A054C} = {83D2040C-760E-4475-ACBE-587536C90911} {A2DAF70A-925A-4F4B-B7EE-CAACE75D6740} = {83D2040C-760E-4475-ACBE-587536C90911} {F146D5E8-EF1F-4785-9150-182631F059B7} = {83D2040C-760E-4475-ACBE-587536C90911} + {4AA65E9F-E3BE-4173-BE3B-BD4DFCD21C40} = {83D2040C-760E-4475-ACBE-587536C90911} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {7A40EC18-E15E-44CE-87EE-61F71CBC3FB6} From a14d362408d1cb18c6b1dc79db947b8a2afea434 Mon Sep 17 00:00:00 2001 From: crowbarmaster Date: Mon, 6 Dec 2021 12:10:32 -0500 Subject: [PATCH 03/62] Update new keys in MC server.properites. --- BedrockService/BedrockService.Shared/Classes/ServerInfo.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/BedrockService/BedrockService.Shared/Classes/ServerInfo.cs b/BedrockService/BedrockService.Shared/Classes/ServerInfo.cs index 969d0a0a..67c07f19 100644 --- a/BedrockService/BedrockService.Shared/Classes/ServerInfo.cs +++ b/BedrockService/BedrockService.Shared/Classes/ServerInfo.cs @@ -52,9 +52,11 @@ public void InitializeDefaults() ServerPropList.Add(new Property("compression-threshold", "1")); ServerPropList.Add(new Property("server-authoritative-movement", "server-auth")); ServerPropList.Add(new Property("player-movement-score-threshold", "20")); + ServerPropList.Add(new Property("player-movement-action-direction-threshold", "0.85")); ServerPropList.Add(new Property("player-movement-distance-threshold", "0.3")); ServerPropList.Add(new Property("player-movement-duration-threshold-in-ms", "500")); ServerPropList.Add(new Property("correct-player-movement", "false")); + ServerPropList.Add(new Property("server-authoritative-block-breaking", "false")); } From 8a96bb740e1ac14fd415bb792838d7a7c184e9e6 Mon Sep 17 00:00:00 2001 From: crowbarmaster Date: Mon, 6 Dec 2021 12:12:35 -0500 Subject: [PATCH 04/62] Fix some GUI hangs and behavior. --- BedrockService/Client/Forms/MainWindow.cs | 36 +++++++++++-------- .../Client/Forms/PlayerManagerForm.cs | 1 + 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/BedrockService/Client/Forms/MainWindow.cs b/BedrockService/Client/Forms/MainWindow.cs index 3ea67965..9dd8783c 100644 --- a/BedrockService/Client/Forms/MainWindow.cs +++ b/BedrockService/Client/Forms/MainWindow.cs @@ -6,12 +6,11 @@ using System.Diagnostics; using System.IO; using System.Linq; -using System.Reflection; using System.Runtime.InteropServices; using System.Text; -using System.Timers; using System.Threading; using System.Threading.Tasks; +using System.Timers; using System.Windows.Forms; namespace BedrockService.Client.Forms @@ -159,15 +158,15 @@ public void RefreshServerContents() } public override void Refresh() - { + { HostInfoLabel.Text = $"Connected to host:"; ServerSelectBox.Items.Clear(); - if(connectedHost != null) + if (connectedHost != null) { _logManager.InitLogThread(connectedHost); foreach (ServerInfo server in connectedHost.GetServerList()) ServerSelectBox.Items.Add(server.ServerName); - if(ServerSelectBox.Items.Count > 0) + if (ServerSelectBox.Items.Count > 0) { ServerSelectBox.SelectedIndex = 0; selectedServer = connectedHost.GetServerInfoByName((string)ServerSelectBox.SelectedItem); @@ -185,7 +184,7 @@ public void InitForm() { HostListBox.Items.Add(host.GetHostName()); } - if(HostListBox.Items.Count > 0) + if (HostListBox.Items.Count > 0) { HostListBox.SelectedIndex = 0; } @@ -325,7 +324,7 @@ private void GlobBackup_Click(object sender, EventArgs e) private void newSrvBtn_Click(object sender, EventArgs e) { - if(clientSideServiceConfiguration == null) + if (clientSideServiceConfiguration == null) { clientSideServiceConfiguration = _configManager.HostConnectList.First(host => host.GetHostName() == HostListBox.Text); } @@ -352,10 +351,14 @@ private void RemoveSrvBtn_Click(object sender, EventArgs e) private void PlayerManager_Click(object sender, EventArgs e) { FormManager.TCPClient.SendData(NetworkMessageSource.Client, NetworkMessageDestination.Server, connectedHost.GetServerIndex(selectedServer), NetworkMessageTypes.PlayersRequest); + DisableUI(); WaitForServerData().Wait(); FormManager.TCPClient.PlayerInfoArrived = false; PlayerManagerForm form = new PlayerManagerForm(selectedServer); - form.Show(); + if(form.ShowDialog() != DialogResult.OK) + { + ServerBusy = false; + } } private void Disconn_Click(object sender, EventArgs e) @@ -470,7 +473,6 @@ public void PerformBackupTests() DisableUI(); ServerSelectBox.SelectedIndex = 0; FormManager.TCPClient.SendData(NetworkMessageSource.Client, NetworkMessageDestination.Service, connectedHost.GetServerIndex(selectedServer), NetworkMessageTypes.EnumBackups); - DisableUI().Wait(); FormManager.TCPClient.EnumBackupsArrived = false; JsonSerializerSettings settings = new JsonSerializerSettings() { @@ -509,7 +511,6 @@ public Task DisableUI() ServerBusy = true; return Task.Run(() => { - Invoke((MethodInvoker)delegate { ComponentEnableManager(); }); while (ServerBusy) { @@ -580,6 +581,10 @@ private void BackupManager_Click(object sender, EventArgs e) FormManager.TCPClient.SendData(Encoding.UTF8.GetBytes(editDialog.RollbackFolderName), NetworkMessageSource.Client, NetworkMessageDestination.Service, connectedHost.GetServerIndex(selectedServer), NetworkMessageTypes.BackupRollback); ServerBusy = true; } + else + { + ServerBusy = false; + } editDialog.Close(); } } @@ -592,8 +597,11 @@ private void ManPacks_Click(object sender, EventArgs e) using (ManagePacksForms form = new ManagePacksForms(connectedHost.GetServerIndex(selectedServer), _logger, _processInfo)) { form.PopulateServerPacks(FormManager.TCPClient.RecievedPacks); - if (form.ShowDialog() == DialogResult.OK) - form.Close(); + if (form.ShowDialog() != DialogResult.OK) + { + ServerBusy = false; + } + form.Close(); } FormManager.TCPClient.RecievedPacks = null; } @@ -628,7 +636,7 @@ private void nbtStudioBtn_Click(object sender, EventArgs e) private void HostListBox_SelectedIndexChanged(object sender, EventArgs e) { - if(HostListBox.SelectedIndex != -1) + if (HostListBox.SelectedIndex != -1) { clientSideServiceConfiguration = _configManager.HostConnectList.FirstOrDefault(host => host.GetHostName() == (string)HostListBox.SelectedItem); } @@ -643,7 +651,7 @@ private void clientConfigBtn_Click(object sender, EventArgs e) { using (ClientConfigForm form = new ClientConfigForm(_configManager)) { - if(form.ShowDialog() == DialogResult.OK) + if (form.ShowDialog() == DialogResult.OK) { form.Close(); InitForm(); diff --git a/BedrockService/Client/Forms/PlayerManagerForm.cs b/BedrockService/Client/Forms/PlayerManagerForm.cs index ea819e69..416e9ea9 100644 --- a/BedrockService/Client/Forms/PlayerManagerForm.cs +++ b/BedrockService/Client/Forms/PlayerManagerForm.cs @@ -75,6 +75,7 @@ private void saveBtn_Click(object sender, EventArgs e) byte[] sendBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(modifiedPlayers, Formatting.Indented, settings)); FormManager.TCPClient.SendData(sendBytes, NetworkMessageSource.Client, NetworkMessageDestination.Server, FormManager.MainWindow.connectedHost.GetServerIndex(_server), NetworkMessageTypes.PlayersUpdate); FormManager.MainWindow.DisableUI(); + DialogResult = DialogResult.OK; Close(); Dispose(); } From 752147cb2b9cf25d7d55b956e25f2468420ef625 Mon Sep 17 00:00:00 2001 From: crowbarmaster Date: Mon, 6 Dec 2021 12:13:52 -0500 Subject: [PATCH 05/62] misc changes and cleanup. --- .../Classes/CommsKeyContainer.cs | 6 +-- .../BedrockServiceUnitTests.csproj | 8 ---- .../Properties/AssemblyInfo.cs | 2 - .../BedrockServiceUnitTests/app.config | 15 ++++++ .../Client/BedrockService.Client.csproj | 13 +++++ .../Client/Forms/AddNewServerForm.cs | 4 +- .../Client/Forms/ClientConfigForm.cs | 13 ++--- .../Client/Management/ConfigManager.cs | 1 - .../Client/Management/LogManager.cs | 17 +++---- BedrockService/Client/Networking/TCPClient.cs | 47 +++++++------------ .../Client/Properties/Settings.Designer.cs | 2 +- .../ClientUnitTests/ClientUnitTests.csproj | 4 -- BedrockService/ClientUnitTests/FormTests.cs | 13 ++--- .../Properties/AssemblyInfo.cs | 1 - 14 files changed, 65 insertions(+), 81 deletions(-) create mode 100644 BedrockService/BedrockServiceUnitTests/app.config diff --git a/BedrockService/BedrockService.Shared/Classes/CommsKeyContainer.cs b/BedrockService/BedrockService.Shared/Classes/CommsKeyContainer.cs index 2df98f77..cf54fe2d 100644 --- a/BedrockService/BedrockService.Shared/Classes/CommsKeyContainer.cs +++ b/BedrockService/BedrockService.Shared/Classes/CommsKeyContainer.cs @@ -1,9 +1,5 @@ using System; -using System.Collections.Generic; using System.Security.Cryptography; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace BedrockService.Shared.Classes { @@ -84,7 +80,7 @@ public class CommsKeyContainer public CommsKeyContainer() { } - public CommsKeyContainer (RSAParameters priv, RSAParameters pub, byte[] key, byte[] IV) + public CommsKeyContainer(RSAParameters priv, RSAParameters pub, byte[] key, byte[] IV) { LocalPrivateKey = new RSAContainer(priv); RemotePublicKey = new RSAContainer(pub); diff --git a/BedrockService/BedrockServiceUnitTests/BedrockServiceUnitTests.csproj b/BedrockService/BedrockServiceUnitTests/BedrockServiceUnitTests.csproj index bb449d2b..4cabbb43 100644 --- a/BedrockService/BedrockServiceUnitTests/BedrockServiceUnitTests.csproj +++ b/BedrockService/BedrockServiceUnitTests/BedrockServiceUnitTests.csproj @@ -86,14 +86,6 @@ {F146D5E8-EF1F-4785-9150-182631F059B7} BedrockService.Shared - - {a2daf70a-925a-4f4b-b7ee-caace75d6740} - BedrockService.Client - - - {13b2b5a8-71e9-49f4-9bfb-3c3b3c0a054c} - BedrockService.Service - diff --git a/BedrockService/BedrockServiceUnitTests/Properties/AssemblyInfo.cs b/BedrockService/BedrockServiceUnitTests/Properties/AssemblyInfo.cs index dbcdf908..b3183c09 100644 --- a/BedrockService/BedrockServiceUnitTests/Properties/AssemblyInfo.cs +++ b/BedrockService/BedrockServiceUnitTests/Properties/AssemblyInfo.cs @@ -1,5 +1,3 @@ -using System.Reflection; -using System.Runtime.InteropServices; [assembly: AssemblyTitle("BedrockServiceUnitTests")] [assembly: AssemblyDescription("")] diff --git a/BedrockService/BedrockServiceUnitTests/app.config b/BedrockService/BedrockServiceUnitTests/app.config new file mode 100644 index 00000000..28b67d9c --- /dev/null +++ b/BedrockService/BedrockServiceUnitTests/app.config @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/BedrockService/Client/BedrockService.Client.csproj b/BedrockService/Client/BedrockService.Client.csproj index ac5f5d7c..763a70bd 100644 --- a/BedrockService/Client/BedrockService.Client.csproj +++ b/BedrockService/Client/BedrockService.Client.csproj @@ -57,4 +57,17 @@ + + + True + True + Settings.settings + + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + \ No newline at end of file diff --git a/BedrockService/Client/Forms/AddNewServerForm.cs b/BedrockService/Client/Forms/AddNewServerForm.cs index 9b328bac..cd9b4fe0 100644 --- a/BedrockService/Client/Forms/AddNewServerForm.cs +++ b/BedrockService/Client/Forms/AddNewServerForm.cs @@ -49,9 +49,9 @@ private void saveBtn_Click(object sender, System.EventArgs e) usedPorts.Add(serverConfiguration.GetProp("server-port").ToString()); usedPorts.Add(serverConfiguration.GetProp("server-portv6").ToString()); } - foreach(string port in usedPorts) + foreach (string port in usedPorts) { - if(ipV4Box.Text == port || ipV6Box.Text == port) + if (ipV4Box.Text == port || ipV6Box.Text == port) { MessageBox.Show($"You have selected port {port} to use, but this port is already used. Please select another port!"); return; diff --git a/BedrockService/Client/Forms/ClientConfigForm.cs b/BedrockService/Client/Forms/ClientConfigForm.cs index 4d4c88b2..57b7d641 100644 --- a/BedrockService/Client/Forms/ClientConfigForm.cs +++ b/BedrockService/Client/Forms/ClientConfigForm.cs @@ -3,13 +3,6 @@ using BedrockService.Shared.Interfaces; using System; using System.Collections.Generic; -using System.ComponentModel; -using System.Data; -using System.Drawing; -using System.IO; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using System.Windows.Forms; namespace BedrockService.Client.Forms @@ -40,12 +33,12 @@ public void SimulateTests() private void nbtButton_Click(object sender, EventArgs e) { - using(OpenFileDialog fileDialog = new OpenFileDialog()) + using (OpenFileDialog fileDialog = new OpenFileDialog()) { fileDialog.Filter = "EXE Files|*.exe"; fileDialog.FileName = "NbtStudio.exe"; fileDialog.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Desktop); - if(fileDialog.ShowDialog() == DialogResult.OK) + if (fileDialog.ShowDialog() == DialogResult.OK) { _configManager.NBTStudioPath = fileDialog.FileName; nbtPathLabel.Text = $"NBT Studio path: {_configManager.NBTStudioPath}"; @@ -56,7 +49,7 @@ private void nbtButton_Click(object sender, EventArgs e) private void saveBtn_Click(object sender, EventArgs e) { List newConfigs = new List(); - foreach(DataGridViewRow row in serverGridView.Rows) + foreach (DataGridViewRow row in serverGridView.Rows) { if (!string.IsNullOrEmpty((string)row.Cells[0].Value)) { diff --git a/BedrockService/Client/Management/ConfigManager.cs b/BedrockService/Client/Management/ConfigManager.cs index 1491e514..c695e631 100644 --- a/BedrockService/Client/Management/ConfigManager.cs +++ b/BedrockService/Client/Management/ConfigManager.cs @@ -1,6 +1,5 @@ using BedrockService.Shared.Classes; using BedrockService.Shared.Interfaces; -using System; using System.Collections.Generic; using System.IO; using System.Text; diff --git a/BedrockService/Client/Management/LogManager.cs b/BedrockService/Client/Management/LogManager.cs index e5542b9b..17f170d4 100644 --- a/BedrockService/Client/Management/LogManager.cs +++ b/BedrockService/Client/Management/LogManager.cs @@ -4,16 +4,18 @@ using System.Collections.Generic; using System.Text; using System.Threading; +using System.Threading.Tasks; using System.Windows.Forms; namespace BedrockService.Client.Management { class LogManager { - public Thread LogThread; + public Task LogThread; public bool EnableFlag; public bool Working = false; public List ServiceLogs = new List(); + private CancellationTokenSource LogTaskCancelSource; private IServiceConfiguration connectedHost; private readonly IBedrockLogger Logger; @@ -24,7 +26,7 @@ public LogManager(IBedrockLogger logger) private void LogManagerTask() { - while (FormManager.TCPClient.Connected) + while (!LogTaskCancelSource.Token.IsCancellationRequested) { try { @@ -69,6 +71,7 @@ private void LogManagerTask() public bool InitLogThread(IServiceConfiguration host) { connectedHost = host; + LogTaskCancelSource = new CancellationTokenSource(); return StartLogThread(); } @@ -76,11 +79,9 @@ public bool StartLogThread() { try { - if (LogThread != null && LogThread.IsAlive) - LogThread.Abort(); - LogThread = new Thread(new ThreadStart(LogManagerTask)); - LogThread.Name = "LogThread"; - LogThread.IsBackground = true; + if (LogThread != null && !LogTaskCancelSource.IsCancellationRequested) + LogTaskCancelSource.Cancel(); + LogThread = new Task(new Action(LogManagerTask), LogTaskCancelSource.Token); EnableFlag = true; LogThread.Start(); Logger.AppendLine("LogThread started"); @@ -101,7 +102,7 @@ public bool StopLogThread() } try { - LogThread.Abort(); + LogTaskCancelSource.Cancel(); } catch (ThreadAbortException e) { diff --git a/BedrockService/Client/Networking/TCPClient.cs b/BedrockService/Client/Networking/TCPClient.cs index c3ad9b17..d0298ce5 100644 --- a/BedrockService/Client/Networking/TCPClient.cs +++ b/BedrockService/Client/Networking/TCPClient.cs @@ -10,6 +10,7 @@ using System.Net.Sockets; using System.Text; using System.Threading; +using System.Threading.Tasks; namespace BedrockService.Client.Networking { @@ -24,21 +25,23 @@ public class TCPClient public bool EnumBackupsArrived; public List BackupList; public List RecievedPacks; - public Thread ClientReciever; - public Thread HeartbeatThread; + public Task ClientReciever; + public Task HeartbeatTask; + private CancellationTokenSource _netCancelSource; private int _heartbeatFailTimeout; private const int _heartbeatFailTimeoutLimit = 250; private bool _heartbeatRecieved; private bool _keepAlive; private readonly IBedrockLogger _logger; - public TCPClient (IBedrockLogger logger) + public TCPClient(IBedrockLogger logger) { _logger = logger; } public void ConnectHost(IClientSideServiceConfiguration host) { + _netCancelSource = new CancellationTokenSource(); if (EstablishConnection(host.GetAddress(), int.Parse(host.GetPort()))) { SendData(NetworkMessageSource.Client, NetworkMessageDestination.Service, NetworkMessageTypes.Connect); @@ -55,16 +58,13 @@ public bool EstablishConnection(string addr, int port) OpenedTcpClient = new TcpClient(addr, port); stream = OpenedTcpClient.GetStream(); _keepAlive = true; - ClientReciever = new Thread(new ThreadStart(ReceiveListener)); - ClientReciever.Name = "ClientPacketReviever"; - ClientReciever.IsBackground = true; - ClientReciever.Start(); + ClientReciever = Task.Factory.StartNew(new Action(ReceiveListener), _netCancelSource.Token); } catch { _logger.AppendLine("Could not connect to Server"); - if(ClientReciever != null) - ClientReciever.Abort(); + if (ClientReciever != null) + _netCancelSource.Cancel(); ClientReciever = null; return false; } @@ -95,7 +95,7 @@ public void CloseConnection() public void ReceiveListener() { List byteBlocks = new List(); - while (_keepAlive) + while (!_netCancelSource.IsCancellationRequested) { try { @@ -135,14 +135,7 @@ public void ReceiveListener() Connected = true; FormManager.MainWindow.RefreshServerContents(); _heartbeatFailTimeout = 0; - if (HeartbeatThread == null || !HeartbeatThread.IsAlive) - HeartbeatThread = new Thread(new ThreadStart(SendHeatbeatSignal)) - { - IsBackground = true, - Name = "HeartBeatThread" - }; - HeartbeatThread.Start(); - + HeartbeatTask = Task.Factory.StartNew(new Action(SendHeatbeatSignal), _netCancelSource.Token); } catch (Exception e) { @@ -151,13 +144,6 @@ public void ReceiveListener() break; case NetworkMessageTypes.Heartbeat: _heartbeatRecieved = true; - if (!HeartbeatThread.IsAlive) - { - HeartbeatThread = new Thread(new ThreadStart(SendHeatbeatSignal)); - HeartbeatThread.IsBackground = true; - HeartbeatThread.Name = "HeartBeatThread"; - HeartbeatThread.Start(); - } break; case NetworkMessageTypes.EnumBackups: @@ -271,7 +257,7 @@ public void SendHeatbeatSignal() if (_heartbeatFailTimeout > _heartbeatFailTimeoutLimit) { FormManager.MainWindow.HeartbeatFailDisconnect(); - HeartbeatThread.Abort(); + _netCancelSource.Cancel(); _heartbeatFailTimeout = 0; } } @@ -325,11 +311,10 @@ public bool SendData(byte[] bytes, NetworkMessageSource source, NetworkMessageDe public void Dispose() { - if (HeartbeatThread != null && HeartbeatThread.IsAlive) - HeartbeatThread.Abort(); - if (ClientReciever != null && ClientReciever.IsAlive) - ClientReciever.Abort(); - HeartbeatThread = null; + _netCancelSource.Cancel(); + _netCancelSource.Dispose(); + _netCancelSource = null; + HeartbeatTask = null; ClientReciever = null; if (OpenedTcpClient != null) { diff --git a/BedrockService/Client/Properties/Settings.Designer.cs b/BedrockService/Client/Properties/Settings.Designer.cs index 609bb426..20a0d7cd 100644 --- a/BedrockService/Client/Properties/Settings.Designer.cs +++ b/BedrockService/Client/Properties/Settings.Designer.cs @@ -12,7 +12,7 @@ namespace BedrockService.Client.Properties { [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.10.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "17.0.3.0")] internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); diff --git a/BedrockService/ClientUnitTests/ClientUnitTests.csproj b/BedrockService/ClientUnitTests/ClientUnitTests.csproj index 1bbbb29c..e977ee03 100644 --- a/BedrockService/ClientUnitTests/ClientUnitTests.csproj +++ b/BedrockService/ClientUnitTests/ClientUnitTests.csproj @@ -61,10 +61,6 @@ {f146d5e8-ef1f-4785-9150-182631f059b7} BedrockService.Shared - - {a2daf70a-925a-4f4b-b7ee-caace75d6740} - BedrockService.Client - diff --git a/BedrockService/ClientUnitTests/FormTests.cs b/BedrockService/ClientUnitTests/FormTests.cs index 128eac3b..85bcb5cc 100644 --- a/BedrockService/ClientUnitTests/FormTests.cs +++ b/BedrockService/ClientUnitTests/FormTests.cs @@ -1,16 +1,13 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using BedrockService.Client.Forms; +using BedrockService.Client.Forms; +using BedrockService.Client.Management; using BedrockService.Shared.Classes; using BedrockService.Shared.Interfaces; -using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using BedrockService.Client.Management; using System.Diagnostics; -using System.Reflection; using System.IO; -using System.Windows.Forms; +using System.Reflection; +using System.Threading.Tasks; namespace ClientUnitTests { diff --git a/BedrockService/ClientUnitTests/Properties/AssemblyInfo.cs b/BedrockService/ClientUnitTests/Properties/AssemblyInfo.cs index 66261c92..c7de3b86 100644 --- a/BedrockService/ClientUnitTests/Properties/AssemblyInfo.cs +++ b/BedrockService/ClientUnitTests/Properties/AssemblyInfo.cs @@ -1,5 +1,4 @@ using System.Reflection; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; [assembly: AssemblyTitle("ClientUnitTests")] From 4e7d0013280c14bcc86a795e9c6828f015b7249f Mon Sep 17 00:00:00 2001 From: crowbarmaster Date: Mon, 6 Dec 2021 12:19:17 -0500 Subject: [PATCH 06/62] All threads have been migrated to tasks. --- .../Service/Core/Tasks/ClientServiceTask.cs | 14 +++++ .../Service/Core/Tasks/HeartbeatTask.cs | 14 +++++ .../Service/Core/Tasks/ServerProcessTask.cs | 14 +++++ .../Service/Core/Tasks/TCPListenerTask.cs | 14 +++++ .../Service/Core/Tasks/WatchdogTask.cs | 14 +++++ .../Core/Threads/ClientServiceThread.cs | 38 ------------- .../Service/Core/Threads/HeartbeatThread.cs | 31 ----------- .../Core/Threads/ServerProcessThread.cs | 35 ------------ .../Service/Core/Threads/TCPThread.cs | 39 -------------- .../Service/Core/Threads/WatchdogThread.cs | 33 ------------ .../Service/Networking/TCPListener.cs | 50 ++++++++--------- .../Service/Server/BedrockServer.cs | 53 ++++++++----------- 12 files changed, 114 insertions(+), 235 deletions(-) create mode 100644 BedrockService/Service/Core/Tasks/ClientServiceTask.cs create mode 100644 BedrockService/Service/Core/Tasks/HeartbeatTask.cs create mode 100644 BedrockService/Service/Core/Tasks/ServerProcessTask.cs create mode 100644 BedrockService/Service/Core/Tasks/TCPListenerTask.cs create mode 100644 BedrockService/Service/Core/Tasks/WatchdogTask.cs delete mode 100644 BedrockService/Service/Core/Threads/ClientServiceThread.cs delete mode 100644 BedrockService/Service/Core/Threads/HeartbeatThread.cs delete mode 100644 BedrockService/Service/Core/Threads/ServerProcessThread.cs delete mode 100644 BedrockService/Service/Core/Threads/TCPThread.cs delete mode 100644 BedrockService/Service/Core/Threads/WatchdogThread.cs diff --git a/BedrockService/Service/Core/Tasks/ClientServiceTask.cs b/BedrockService/Service/Core/Tasks/ClientServiceTask.cs new file mode 100644 index 00000000..da8263fc --- /dev/null +++ b/BedrockService/Service/Core/Tasks/ClientServiceTask.cs @@ -0,0 +1,14 @@ +using BedrockService.Service.Core.Interfaces; +using System.Threading; + +namespace BedrockService.Service.Core.Tasks +{ + class ClientServiceTask : IServiceTask + { + private Task _thread; + private CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource(); + public ClientServiceTask(Action method) => _thread = Task.Factory.StartNew((CancellationToken) => method(_cancellationTokenSource.Token), _cancellationTokenSource.Token); + public void CancelTask() => _cancellationTokenSource.Cancel(); + public bool IsAlive() => _thread != null && _thread.Status == TaskStatus.Running && !_cancellationTokenSource.IsCancellationRequested; + } +} \ No newline at end of file diff --git a/BedrockService/Service/Core/Tasks/HeartbeatTask.cs b/BedrockService/Service/Core/Tasks/HeartbeatTask.cs new file mode 100644 index 00000000..a0173873 --- /dev/null +++ b/BedrockService/Service/Core/Tasks/HeartbeatTask.cs @@ -0,0 +1,14 @@ +using BedrockService.Service.Core.Interfaces; +using System.Threading; + +namespace BedrockService.Service.Core.Tasks +{ + class HeartbeatTask : IServiceTask + { + private Task _thread; + private CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource(); + public HeartbeatTask(Action method) => _thread = Task.Factory.StartNew((CancellationToken) => method(_cancellationTokenSource.Token), _cancellationTokenSource.Token); + public void CancelTask() => _cancellationTokenSource.Cancel(); + public bool IsAlive() => _thread != null && _thread.Status == TaskStatus.Running && !_cancellationTokenSource.IsCancellationRequested; + } +} \ No newline at end of file diff --git a/BedrockService/Service/Core/Tasks/ServerProcessTask.cs b/BedrockService/Service/Core/Tasks/ServerProcessTask.cs new file mode 100644 index 00000000..371f0b7b --- /dev/null +++ b/BedrockService/Service/Core/Tasks/ServerProcessTask.cs @@ -0,0 +1,14 @@ +using BedrockService.Service.Core.Interfaces; +using System.Threading; + +namespace BedrockService.Service.Core.Tasks +{ + class ServerProcessTask : IServiceTask + { + private Task _thread; + private CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource(); + public ServerProcessTask(Action method) => _thread = Task.Factory.StartNew((CancellationToken) => method(_cancellationTokenSource.Token), _cancellationTokenSource.Token); + public void CancelTask() => _cancellationTokenSource.Cancel(); + public bool IsAlive() => _thread != null && _thread.Status == TaskStatus.Running && !_cancellationTokenSource.IsCancellationRequested; + } +} \ No newline at end of file diff --git a/BedrockService/Service/Core/Tasks/TCPListenerTask.cs b/BedrockService/Service/Core/Tasks/TCPListenerTask.cs new file mode 100644 index 00000000..ede4d25a --- /dev/null +++ b/BedrockService/Service/Core/Tasks/TCPListenerTask.cs @@ -0,0 +1,14 @@ +using BedrockService.Service.Core.Interfaces; +using System.Threading; + +namespace BedrockService.Service.Core.Tasks +{ + class TCPListenerTask : IServiceTask + { + private Task _thread; + private CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource(); + public TCPListenerTask(Action method) => _thread = Task.Factory.StartNew((CancellationToken) => method(_cancellationTokenSource.Token), _cancellationTokenSource.Token); + public void CancelTask() => _cancellationTokenSource.Cancel(); + public bool IsAlive() => _thread != null && _thread.Status == TaskStatus.Running && !_cancellationTokenSource.IsCancellationRequested; + } +} \ No newline at end of file diff --git a/BedrockService/Service/Core/Tasks/WatchdogTask.cs b/BedrockService/Service/Core/Tasks/WatchdogTask.cs new file mode 100644 index 00000000..1b370288 --- /dev/null +++ b/BedrockService/Service/Core/Tasks/WatchdogTask.cs @@ -0,0 +1,14 @@ +using BedrockService.Service.Core.Interfaces; +using System.Threading; + +namespace BedrockService.Service.Core.Tasks +{ + class WatchdogTask : IServiceTask + { + private Task _thread; + private CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource(); + public WatchdogTask(Action method) => _thread = Task.Factory.StartNew((CancellationToken) => method(_cancellationTokenSource.Token), _cancellationTokenSource.Token); + public void CancelTask() => _cancellationTokenSource.Cancel(); + public bool IsAlive() => _thread != null && _thread.Status == TaskStatus.Running && !_cancellationTokenSource.IsCancellationRequested; + } +} \ No newline at end of file diff --git a/BedrockService/Service/Core/Threads/ClientServiceThread.cs b/BedrockService/Service/Core/Threads/ClientServiceThread.cs deleted file mode 100644 index 4266082f..00000000 --- a/BedrockService/Service/Core/Threads/ClientServiceThread.cs +++ /dev/null @@ -1,38 +0,0 @@ -using BedrockService.Service.Core.Interfaces; -using System.Threading; - -namespace BedrockService.Service.Core.Threads -{ - class ClientServiceThread : IServiceThread - { - private Thread _clientService; - - public ClientServiceThread(ThreadStart methodToRun) - { - _clientService = new Thread(methodToRun) - { - Name = "ClientService", - IsBackground = true - }; - _clientService.Start(); - } - - public void CloseThread() - { - try - { - if (IsAlive()) - { - _clientService.Abort(); - } - _clientService = null; - } - catch (ThreadAbortException) { } - } - - public bool IsAlive() - { - return _clientService != null && _clientService.IsAlive; - } - } -} diff --git a/BedrockService/Service/Core/Threads/HeartbeatThread.cs b/BedrockService/Service/Core/Threads/HeartbeatThread.cs deleted file mode 100644 index 0f4fbe13..00000000 --- a/BedrockService/Service/Core/Threads/HeartbeatThread.cs +++ /dev/null @@ -1,31 +0,0 @@ -using BedrockService.Service.Core.Interfaces; -using System.Threading; - -namespace BedrockService.Service.Core.Threads -{ - class HeartbeatThread : IServiceThread - { - private Thread _heartbeatThread; - - public HeartbeatThread(ThreadStart parameterizedThreadStart) - { - _heartbeatThread = new Thread(parameterizedThreadStart) - { - Name = "HeartbeatThread", - IsBackground = true - }; - _heartbeatThread.Start(); - } - - public void CloseThread() - { - _heartbeatThread.Abort(); - _heartbeatThread = null; - } - - public bool IsAlive() - { - return _heartbeatThread != null && _heartbeatThread.IsAlive; - } - } -} diff --git a/BedrockService/Service/Core/Threads/ServerProcessThread.cs b/BedrockService/Service/Core/Threads/ServerProcessThread.cs deleted file mode 100644 index 658d3fa9..00000000 --- a/BedrockService/Service/Core/Threads/ServerProcessThread.cs +++ /dev/null @@ -1,35 +0,0 @@ -using BedrockService.Service.Core.Interfaces; -using System.Threading; - -namespace BedrockService.Service.Core.Threads -{ - class ServerProcessThread : IServiceThread - { - private Thread _networkListenerThread; - - public ServerProcessThread(ThreadStart methodToRun) - { - _networkListenerThread = new Thread(methodToRun) - { - Name = "ServerThread", - IsBackground = true - }; - _networkListenerThread.Start(); - } - - public void CloseThread() - { - if (IsAlive()) - { - _networkListenerThread.Abort(); - } - _networkListenerThread = null; - } - - - public bool IsAlive() - { - return _networkListenerThread != null && _networkListenerThread.IsAlive; - } - } -} diff --git a/BedrockService/Service/Core/Threads/TCPThread.cs b/BedrockService/Service/Core/Threads/TCPThread.cs deleted file mode 100644 index 6fd7e482..00000000 --- a/BedrockService/Service/Core/Threads/TCPThread.cs +++ /dev/null @@ -1,39 +0,0 @@ -using BedrockService.Service.Core.Interfaces; -using System.Threading; -using System.Threading.Tasks; - -namespace BedrockService.Service.Core.Threads -{ - class TCPThread : IServiceThread - { - private Thread _clientService; - - public TCPThread(ThreadStart threadStart) - { - _clientService = null; - _clientService = new Thread(threadStart) - { - Name = "TCPListener", - IsBackground = true - }; - _clientService.Start(); - } - - public void CloseThread() - { - Task.Run(() => - { - if (IsAlive()) - { - _clientService.Abort(); - } - }); - _clientService = null; - } - - public bool IsAlive() - { - return _clientService != null && _clientService.IsAlive; - } - } -} diff --git a/BedrockService/Service/Core/Threads/WatchdogThread.cs b/BedrockService/Service/Core/Threads/WatchdogThread.cs deleted file mode 100644 index ee08f4d4..00000000 --- a/BedrockService/Service/Core/Threads/WatchdogThread.cs +++ /dev/null @@ -1,33 +0,0 @@ -using BedrockService.Service.Core.Interfaces; -using System.Threading; - -namespace BedrockService.Service.Core.Threads -{ - class WatchdogThread : IServiceThread - { - private Thread _thread; - - public WatchdogThread(ThreadStart parameterizedThreadStart) - { - _thread = new Thread(parameterizedThreadStart) - { - Name = "WatchdogThread", - IsBackground = true - }; - _thread.Start(); - } - - public void CloseThread() - { - _thread.Abort(); - _thread = null; - } - - public bool IsAlive() - { - return _thread != null && _thread.IsAlive; - } - } - -} - diff --git a/BedrockService/Service/Networking/TCPListener.cs b/BedrockService/Service/Networking/TCPListener.cs index 77a581c3..cf7db9af 100644 --- a/BedrockService/Service/Networking/TCPListener.cs +++ b/BedrockService/Service/Networking/TCPListener.cs @@ -1,16 +1,10 @@ using BedrockService.Service.Core.Interfaces; -using BedrockService.Service.Core.Threads; -using BedrockService.Service.Networking.NetworkMessageClasses; -using BedrockService.Shared.Classes; -using BedrockService.Shared.Interfaces; -using System; -using System.Collections.Generic; +using BedrockService.Service.Core.Tasks; +using BedrockService.Service.Networking.MessageInterfaces; using System.Net; using System.Net.Sockets; -using System.Threading; -using System.IO; using System.Security.Cryptography; -using System.Security.Cryptography.X509Certificates; +using System.Threading; namespace BedrockService.Service.Networking { @@ -21,9 +15,9 @@ public class TCPListener : ITCPListener, IMessageSender private NetworkStream _stream; private readonly IServiceConfiguration _serviceConfiguration; private readonly IBedrockLogger _logger; - private IServiceThread _tcpThread; - private IServiceThread _clientThread; - private IServiceThread _heartbeatThread; + private IServiceTask _tcpThread; + private IServiceTask _clientThread; + private IServiceTask _heartbeatThread; private bool _heartbeatRecieved = false; private bool _firstHeartbeatRecieved = false; private bool _keepAlive = false; @@ -40,13 +34,13 @@ public TCPListener(IServiceConfiguration serviceConfiguration, IBedrockLogger lo _logger = logger; _serviceConfiguration = serviceConfiguration; _reconnectTimer.Elapsed += ReconnectTimer_Elapsed; - _tcpThread = new TCPThread(new ThreadStart(StartListening)); + _tcpThread = new TCPListenerTask(new Action(StartListening)); } private void ReconnectTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) { _reconnectTimer.Stop(); - _tcpThread = new TCPThread(new ThreadStart(StartListening)); + _tcpThread = new TCPListenerTask(new Action(StartListening)); } public void SetStrategyDictionaries(Dictionary standard, Dictionary flagged) @@ -55,7 +49,7 @@ public void SetStrategyDictionaries(Dictionary(IncomingListener)); + _heartbeatThread = new HeartbeatTask(new Action(SendBackHeatbeatSignal)); } catch (ThreadStateException) { } catch (NullReferenceException) { } @@ -101,9 +95,9 @@ public void ResetListener() _stream.Dispose(); _client.Client.Blocking = false; _inListener.Stop(); - _tcpThread.CloseThread(); + _tcpThread.CancelTask(); _tcpThread = null; - StartListening(); + _tcpThread = new TCPListenerTask(new Action(StartListening)); } public void SendData(byte[] bytesToSend, NetworkMessageSource source, NetworkMessageDestination destination, byte serverIndex, NetworkMessageTypes type, NetworkMessageFlags status) @@ -117,7 +111,7 @@ public void SendData(byte[] bytesToSend, NetworkMessageSource source, NetworkMes byteHeader[7] = (byte)type; byteHeader[8] = (byte)status; Buffer.BlockCopy(bytesToSend, 0, byteHeader, 9, bytesToSend.Length); - + if (_clientThread.IsAlive()) { try @@ -142,7 +136,7 @@ public void SendData(byte[] bytesToSend, NetworkMessageSource source, NetworkMes public void SendData(NetworkMessageSource source, NetworkMessageDestination destination, NetworkMessageTypes type, NetworkMessageFlags status) => SendData(new byte[0], source, destination, 0xFF, type, status); - private void IncomingListener() + private void IncomingListener(CancellationToken token) { _keepAlive = true; _logger.AppendLine("Packet listener thread started."); @@ -220,11 +214,11 @@ private void IncomingListener() } catch { } if (!_clientThread.IsAlive()) - _clientThread.CloseThread(); + _clientThread.CancelTask(); } } - private void SendBackHeatbeatSignal() + private void SendBackHeatbeatSignal(CancellationToken token) { _logger.AppendLine("HeartBeatSender started."); while (_keepAlive) @@ -276,15 +270,15 @@ public void SetKeyContainer(CommsKeyContainer keyContainer) public bool VerifyClientData(byte[] certificate) { - if(certificate != null) + if (certificate != null) { byte[] decrypted; - using(RSACryptoServiceProvider rsa = new RSACryptoServiceProvider()) + using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider()) { rsa.ImportParameters(_keyContainer.LocalPrivateKey.GetPrivateKey()); - + } - using(RSACryptoServiceProvider rsa = new RSACryptoServiceProvider()) + using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider()) { rsa.ImportParameters(_keyContainer.RemotePublicKey.GetPrivateKey()); diff --git a/BedrockService/Service/Server/BedrockServer.cs b/BedrockService/Service/Server/BedrockServer.cs index 8056ac61..4882e957 100644 --- a/BedrockService/Service/Server/BedrockServer.cs +++ b/BedrockService/Service/Server/BedrockServer.cs @@ -1,25 +1,16 @@ using BedrockService.Service.Core.Interfaces; -using BedrockService.Service.Core.Threads; -using BedrockService.Service.Management; +using BedrockService.Service.Core.Tasks; using BedrockService.Service.Server.Management; -using BedrockService.Shared.Classes; -using BedrockService.Shared.Interfaces; using BedrockService.Shared.Utilities; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; using System.Text.RegularExpressions; using System.Threading; -using System.Threading.Tasks; -using Topshelf; namespace BedrockService.Service.Server { public class BedrockServer : IBedrockServer { - private IServiceThread _serverThread; - private IServiceThread _watchdogThread; + private IServiceTask _serverThread; + private IServiceTask _watchdogThread; private StreamWriter _stdInStream; private Process _serverProcess; private HostControl _hostController; @@ -31,7 +22,7 @@ public class BedrockServer : IBedrockServer private readonly IBedrockLogger _logger; private readonly IBedrockLogger _serverLogger; private readonly string _servicePath; - private const string _startupMessage = "[INFO] Server started."; + private const string _startupMessage = "INFO] Server started."; public enum ServerStatus { Stopped, @@ -58,7 +49,7 @@ public void WriteToStandardIn(string command) public void StartControl() { - _serverThread = new ServerProcessThread(new ThreadStart(RunServer)); + _serverThread = new ServerProcessTask(new Action(RunServer)); } public void StopControl() @@ -72,7 +63,7 @@ public void StopControl() _stdInStream.WriteLine("stop"); while (!_serverProcess.HasExited) { } } - _serverThread.CloseThread(); + _serverThread.CancelTask(); _serverProcess = null; _currentServerStatus = ServerStatus.Stopped; } @@ -82,7 +73,7 @@ public void StartWatchdog(HostControl hostControl) _hostController = hostControl; if (_watchdogThread == null) { - _watchdogThread = new WatchdogThread(new ThreadStart(ApplicationWatchdogMonitor)); + _watchdogThread = new WatchdogTask(new Action(ApplicationWatchdogMonitor)); } } @@ -157,8 +148,8 @@ private bool Backup() public void StopWatchdog() { - if(_watchdogThread != null) - _watchdogThread.CloseThread(); + if (_watchdogThread != null) + _watchdogThread.CancelTask(); _watchdogThread = null; } @@ -166,7 +157,7 @@ public void StopWatchdog() public void SetServerStatus(ServerStatus newStatus) => _currentServerStatus = newStatus; - private void ApplicationWatchdogMonitor() + private void ApplicationWatchdogMonitor(CancellationToken token) { while (_watchdogThread.IsAlive()) { @@ -241,7 +232,7 @@ public bool RestartServer(bool ShouldPerformBackup) public string GetServerName() => _serverConfiguration.GetServerName(); - private void RunServer() + private void RunServer(CancellationToken token) { string exeName = _serverConfiguration.GetProp("ServerExeName").ToString(); string appName = exeName.Substring(0, exeName.Length - 4); @@ -338,7 +329,7 @@ private bool MonitoredAppExists(string monitoredAppName) private void StdOutToLog(object sender, DataReceivedEventArgs e) { - if (e.Data != null && !e.Data.Contains("[INFO] Running AutoCompaction...")) + if (e.Data != null && !e.Data.Contains("INFO] Running AutoCompaction...")) { string dataMsg = e.Data; string logFileText = "NO LOG FILE! - "; @@ -390,7 +381,7 @@ private void StdOutToLog(object sender, DataReceivedEventArgs e) if (_configurator.ReplaceServerBuild(_serverConfiguration).Wait(30000)) _currentServerStatus = ServerStatus.Starting; } - if(dataMsg.Contains("Version ")) + if (dataMsg.Contains("Version ")) { int msgStartIndex = dataMsg.IndexOf(']') + 2; string focusedMsg = dataMsg.Substring(msgStartIndex, dataMsg.Length - msgStartIndex); @@ -451,15 +442,15 @@ public bool RollbackToBackup(byte serverIndex, string folderName) public Task StopServer() { - return Task.Run(() => - { - _currentServerStatus = ServerStatus.Stopping; - while (_currentServerStatus != ServerStatus.Stopped) - { - Thread.Sleep(100); - } - - }); + return Task.Run(() => + { + _currentServerStatus = ServerStatus.Stopping; + while (_currentServerStatus != ServerStatus.Stopped) + { + Thread.Sleep(100); + } + + }); } } } From fec57cab53a903e09f7a3cd128478ee584039857 Mon Sep 17 00:00:00 2001 From: crowbarmaster Date: Mon, 6 Dec 2021 12:19:48 -0500 Subject: [PATCH 07/62] misc fixes and cleanup. --- BedrockService/BedrockService.sln | 14 +------ BedrockService/Service/Core/BedrockService.cs | 13 +------ .../Core/Interfaces/IBedrockService.cs | 5 +-- .../Service/Core/Interfaces/IService.cs | 5 +-- .../{IServiceThread.cs => IServiceTask.cs} | 4 +- BedrockService/Service/Core/Service.cs | 8 +--- .../Service/Logging/ServiceLogger.cs | 5 +-- .../Service/Management/ConfigManager.cs | 20 +++------- .../Service/Management/IConfigurator.cs | 8 +--- .../Service/Networking/ITCPListener.cs | 7 ++-- BedrockService/Service/Networking/IUpdater.cs | 5 +-- .../IFlaggedMessageParser.cs | 4 +- .../MessageInterfaces/IMessageParser.cs | 2 +- .../MessageInterfaces/IMessageSender.cs | 4 +- .../Networking/NetworkStrategyLookup.cs | 22 +++-------- BedrockService/Service/Networking/Updater.cs | 7 +--- BedrockService/Service/Program.cs | 38 +++++++++---------- .../Service/Properties/AssemblyInfo.cs | 3 +- .../Service/Server/IBedrockServer.cs | 3 -- .../Server/Management/IPlayerManager.cs | 5 +-- .../Server/Management/PlayerManager.cs | 7 +--- BedrockService/Service/Startup.cs | 21 +--------- BedrockService/Service/app.config | 3 ++ 23 files changed, 58 insertions(+), 155 deletions(-) rename BedrockService/Service/Core/Interfaces/{IServiceThread.cs => IServiceTask.cs} (58%) create mode 100644 BedrockService/Service/app.config diff --git a/BedrockService/BedrockService.sln b/BedrockService/BedrockService.sln index c5b22238..f94d9163 100644 --- a/BedrockService/BedrockService.sln +++ b/BedrockService/BedrockService.sln @@ -9,13 +9,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BedrockService.Client", "Client\BedrockService.Client.csproj", "{A2DAF70A-925A-4F4B-B7EE-CAACE75D6740}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BedrockServiceUnitTests", "BedrockServiceUnitTests\BedrockServiceUnitTests.csproj", "{216E8E7A-77A9-4077-A5B5-2C63424E7B83}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BedrockService.Shared", "BedrockService.Shared\BedrockService.Shared.csproj", "{F146D5E8-EF1F-4785-9150-182631F059B7}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClientUnitTests", "ClientUnitTests\ClientUnitTests.csproj", "{550B6A98-60C3-4765-BFED-2CAA0AFFAEBB}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BedrockManagementServiceASP", "BedrockManagementServiceASP\BedrockManagementServiceASP.csproj", "{4AA65E9F-E3BE-4173-BE3B-BD4DFCD21C40}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BedrockManagementServiceASP", "BedrockManagementServiceASP\BedrockManagementServiceASP.csproj", "{4AA65E9F-E3BE-4173-BE3B-BD4DFCD21C40}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -31,18 +27,10 @@ Global {A2DAF70A-925A-4F4B-B7EE-CAACE75D6740}.Debug|Any CPU.Build.0 = Debug|Any CPU {A2DAF70A-925A-4F4B-B7EE-CAACE75D6740}.Release|Any CPU.ActiveCfg = Release|Any CPU {A2DAF70A-925A-4F4B-B7EE-CAACE75D6740}.Release|Any CPU.Build.0 = Release|Any CPU - {216E8E7A-77A9-4077-A5B5-2C63424E7B83}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {216E8E7A-77A9-4077-A5B5-2C63424E7B83}.Debug|Any CPU.Build.0 = Debug|Any CPU - {216E8E7A-77A9-4077-A5B5-2C63424E7B83}.Release|Any CPU.ActiveCfg = Release|Any CPU - {216E8E7A-77A9-4077-A5B5-2C63424E7B83}.Release|Any CPU.Build.0 = Release|Any CPU {F146D5E8-EF1F-4785-9150-182631F059B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F146D5E8-EF1F-4785-9150-182631F059B7}.Debug|Any CPU.Build.0 = Debug|Any CPU {F146D5E8-EF1F-4785-9150-182631F059B7}.Release|Any CPU.ActiveCfg = Release|Any CPU {F146D5E8-EF1F-4785-9150-182631F059B7}.Release|Any CPU.Build.0 = Release|Any CPU - {550B6A98-60C3-4765-BFED-2CAA0AFFAEBB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {550B6A98-60C3-4765-BFED-2CAA0AFFAEBB}.Debug|Any CPU.Build.0 = Debug|Any CPU - {550B6A98-60C3-4765-BFED-2CAA0AFFAEBB}.Release|Any CPU.ActiveCfg = Release|Any CPU - {550B6A98-60C3-4765-BFED-2CAA0AFFAEBB}.Release|Any CPU.Build.0 = Release|Any CPU {4AA65E9F-E3BE-4173-BE3B-BD4DFCD21C40}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4AA65E9F-E3BE-4173-BE3B-BD4DFCD21C40}.Debug|Any CPU.Build.0 = Debug|Any CPU {4AA65E9F-E3BE-4173-BE3B-BD4DFCD21C40}.Release|Any CPU.ActiveCfg = Release|Any CPU diff --git a/BedrockService/Service/Core/BedrockService.cs b/BedrockService/Service/Core/BedrockService.cs index 05c82166..587f9f00 100644 --- a/BedrockService/Service/Core/BedrockService.cs +++ b/BedrockService/Service/Core/BedrockService.cs @@ -1,17 +1,8 @@ using BedrockService.Service.Core.Interfaces; -using BedrockService.Service.Management; -using BedrockService.Service.Networking; using BedrockService.Service.Server; -using BedrockService.Shared.Interfaces; -using BedrockService.Service.Core.Threads; using NCrontab; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; using System.Threading; using System.Timers; -using Topshelf; namespace BedrockService.Service.Core { @@ -62,7 +53,7 @@ public bool Start(HostControl hostControl) foreach (var brs in _bedrockServers) { - if(hostControl != null) + if (hostControl != null) _hostControl.RequestAdditionalTime(TimeSpan.FromSeconds(30)); brs.SetServerStatus(BedrockServer.ServerStatus.Starting); brs.StartWatchdog(_hostControl); @@ -223,7 +214,7 @@ private void UpdateTimer_Elapsed(object sender, ElapsedEventArgs e) if (_updater.CheckVersionChanged()) { _logger.AppendLine("Version change detected! Restarting server(s) to apply update..."); - foreach(IBedrockServer server in _bedrockServers) + foreach (IBedrockServer server in _bedrockServers) { server.RestartServer(false); } diff --git a/BedrockService/Service/Core/Interfaces/IBedrockService.cs b/BedrockService/Service/Core/Interfaces/IBedrockService.cs index 4fadcf8e..064d80fd 100644 --- a/BedrockService/Service/Core/Interfaces/IBedrockService.cs +++ b/BedrockService/Service/Core/Interfaces/IBedrockService.cs @@ -1,9 +1,6 @@ using BedrockService.Service.Server; -using BedrockService.Shared.Interfaces; -using System.Collections.Generic; -using Topshelf; -namespace BedrockService.Service.Core +namespace BedrockService.Service.Core.Interfaces { public interface IBedrockService : ServiceControl { diff --git a/BedrockService/Service/Core/Interfaces/IService.cs b/BedrockService/Service/Core/Interfaces/IService.cs index 578a6c4d..29ae3207 100644 --- a/BedrockService/Service/Core/Interfaces/IService.cs +++ b/BedrockService/Service/Core/Interfaces/IService.cs @@ -1,7 +1,4 @@ -using Microsoft.Extensions.Hosting; -using System.Threading.Tasks; - -namespace BedrockService.Service.Core +namespace BedrockService.Service.Core.Interfaces { public interface IService : IHostedService { diff --git a/BedrockService/Service/Core/Interfaces/IServiceThread.cs b/BedrockService/Service/Core/Interfaces/IServiceTask.cs similarity index 58% rename from BedrockService/Service/Core/Interfaces/IServiceThread.cs rename to BedrockService/Service/Core/Interfaces/IServiceTask.cs index 7ce99394..1df73910 100644 --- a/BedrockService/Service/Core/Interfaces/IServiceThread.cs +++ b/BedrockService/Service/Core/Interfaces/IServiceTask.cs @@ -1,8 +1,8 @@ namespace BedrockService.Service.Core.Interfaces { - public interface IServiceThread + public interface IServiceTask { bool IsAlive(); - void CloseThread(); + void CancelTask(); } } diff --git a/BedrockService/Service/Core/Service.cs b/BedrockService/Service/Core/Service.cs index f3a84c60..1741d31f 100644 --- a/BedrockService/Service/Core/Service.cs +++ b/BedrockService/Service/Core/Service.cs @@ -1,12 +1,6 @@ using BedrockService.Service.Core.Interfaces; -using BedrockService.Service.Core.Threads; -using BedrockService.Service.Networking; using BedrockService.Service.Server; -using BedrockService.Shared.Interfaces; -using System; using System.Threading; -using System.Threading.Tasks; -using Topshelf; using Topshelf.Runtime; namespace BedrockService.Service.Core @@ -14,7 +8,7 @@ namespace BedrockService.Service.Core public class Service : IService { private readonly IBedrockService _bedrockService; - private Host _host; + private Topshelf.Host _host; private readonly IBedrockLogger _logger; public Service(IBedrockLogger logger, IBedrockService bedrockService, NetworkStrategyLookup lookup) diff --git a/BedrockService/Service/Logging/ServiceLogger.cs b/BedrockService/Service/Logging/ServiceLogger.cs index e4ee0a8c..d24bb844 100644 --- a/BedrockService/Service/Logging/ServiceLogger.cs +++ b/BedrockService/Service/Logging/ServiceLogger.cs @@ -1,7 +1,4 @@ -using BedrockService.Shared.Interfaces; -using Newtonsoft.Json; -using System; -using System.IO; +using Newtonsoft.Json; using System.Text; namespace BedrockService.Service.Logging diff --git a/BedrockService/Service/Management/ConfigManager.cs b/BedrockService/Service/Management/ConfigManager.cs index 5c1b13b3..48d395fd 100644 --- a/BedrockService/Service/Management/ConfigManager.cs +++ b/BedrockService/Service/Management/ConfigManager.cs @@ -1,17 +1,9 @@ -using BedrockService.Service.Server; -using BedrockService.Shared.Classes; -using BedrockService.Shared.Interfaces; -using BedrockService.Shared.Utilities; -using System; -using System.Collections.Generic; -using System.IO; +using BedrockService.Shared.Utilities; using System.IO.Compression; +using System.Runtime.Serialization.Formatters.Binary; +using System.Security.Cryptography; using System.Text; using System.Threading; -using System.Threading.Tasks; -using System.Security.Cryptography; -using System.Security.Cryptography.X509Certificates; -using System.Runtime.Serialization.Formatters.Binary; namespace BedrockService.Service.Management { @@ -58,7 +50,7 @@ await Task.Run(() => Directory.CreateDirectory($@"{_configDir}\Backups"); if (File.Exists($@"{_configDir}\..\bedrock_ver.ini")) _loadedVersion = File.ReadAllText($@"{_configDir}\..\bedrock_ver.ini"); - if(!File.Exists(_commsKeyPath)) + if (!File.Exists(_commsKeyPath)) { RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(2048); using (AesCryptoServiceProvider aes = new AesCryptoServiceProvider()) @@ -194,9 +186,9 @@ await Task.Run(() => using (ZipArchive archive = ZipFile.OpenRead($@"{_processInfo.GetDirectory()}\Server\MCSFiles\Update_{ _serviceConfiguration.GetServerVersion()}.zip")) { int fileCount = archive.Entries.Count; - for(int i=0; i propList = JsonConvert.DeserializeObject>(stringData, settings); Property prop = propList.FirstOrDefault(p => p.KeyName == "server-name"); - if(prop == null) + if (prop == null) { _serviceConfiguration.SetAllProps(propList); _configurator.SaveGlobalFile(); @@ -488,7 +478,7 @@ class AddNewServer : IMessageParser private readonly IServiceConfiguration _serviceConfiguration; private readonly IConfigurator _configurator; private readonly IBedrockService _bedrockService; - + public AddNewServer(IConfigurator configurator, IMessageSender messageSender, IServiceConfiguration serviceConfiguration, IBedrockService bedrockService) { _bedrockService = bedrockService; @@ -591,9 +581,9 @@ public void ParseMessage(byte[] data, byte serverIndex) { try { - srvText = _service.GetBedrockServerByName(srvName).GetLogger(); + srvText = _service.GetBedrockServerByName(srvName).GetLogger(); } - catch(NullReferenceException) + catch (NullReferenceException) { break; } diff --git a/BedrockService/Service/Networking/Updater.cs b/BedrockService/Service/Networking/Updater.cs index 84ef3e91..d5cc030d 100644 --- a/BedrockService/Service/Networking/Updater.cs +++ b/BedrockService/Service/Networking/Updater.cs @@ -1,12 +1,8 @@ -using BedrockService.Shared.Interfaces; -using BedrockService.Shared.Utilities; -using System; -using System.IO; +using BedrockService.Shared.Utilities; using System.IO.Compression; using System.Net.Http; using System.Text.RegularExpressions; using System.Threading; -using System.Threading.Tasks; namespace BedrockService.Service.Networking { @@ -24,6 +20,7 @@ public Updater(IProcessInfo processInfo, IBedrockLogger logger, IServiceConfigur _processInfo = processInfo; _logger = logger; _version = "None"; + if (!Directory.Exists($@"{processInfo.GetDirectory()}\Server")) { Directory.CreateDirectory($@"{processInfo.GetDirectory()}\Server"); } if (!File.Exists($@"{processInfo.GetDirectory()}\Server\bedrock_ver.ini")) { logger.AppendLine("Version ini file missing, creating and fetching build..."); diff --git a/BedrockService/Service/Program.cs b/BedrockService/Service/Program.cs index b461fb2f..cf7438e9 100644 --- a/BedrockService/Service/Program.cs +++ b/BedrockService/Service/Program.cs @@ -3,25 +3,25 @@ // This file may need updated according to the specific scenario of the application being upgraded. // For more information on ASP.NET Core hosting, see https://docs.microsoft.com/aspnet/core/fundamentals/host/web-host -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Threading.Tasks; -using BedrockService.Service.Core; -using BedrockService.Service.Logging; -using BedrockService.Service.Management; -using BedrockService.Service.Networking; -using BedrockService.Shared.Classes; -using BedrockService.Shared.Interfaces; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using Topshelf; +global using BedrockService.Service.Core; +global using BedrockService.Service.Logging; +global using BedrockService.Service.Management; +global using BedrockService.Service.Networking; +global using BedrockService.Shared.Classes; +global using BedrockService.Shared.Interfaces; +global using Microsoft.AspNetCore.Hosting; +global using Microsoft.Extensions.Configuration; +global using Microsoft.Extensions.DependencyInjection; +global using Microsoft.Extensions.Hosting; +global using System; +global using System.Collections.Generic; +global using System.Diagnostics; +global using System.IO; +global using System.Linq; +global using System.Reflection; +global using System.Threading.Tasks; +global using Topshelf; +using BedrockService.Service.Core.Interfaces; namespace BedrockService.Service { diff --git a/BedrockService/Service/Properties/AssemblyInfo.cs b/BedrockService/Service/Properties/AssemblyInfo.cs index a875f734..2a913382 100644 --- a/BedrockService/Service/Properties/AssemblyInfo.cs +++ b/BedrockService/Service/Properties/AssemblyInfo.cs @@ -1,5 +1,4 @@ -using System.Reflection; -using System.Runtime.InteropServices; +using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information diff --git a/BedrockService/Service/Server/IBedrockServer.cs b/BedrockService/Service/Server/IBedrockServer.cs index 4f61eed1..eddf6a0f 100644 --- a/BedrockService/Service/Server/IBedrockServer.cs +++ b/BedrockService/Service/Server/IBedrockServer.cs @@ -1,7 +1,4 @@ using BedrockService.Service.Server.Management; -using BedrockService.Shared.Interfaces; -using System.Threading.Tasks; -using Topshelf; namespace BedrockService.Service.Server { diff --git a/BedrockService/Service/Server/Management/IPlayerManager.cs b/BedrockService/Service/Server/Management/IPlayerManager.cs index c3c0017b..917fd2ee 100644 --- a/BedrockService/Service/Server/Management/IPlayerManager.cs +++ b/BedrockService/Service/Server/Management/IPlayerManager.cs @@ -1,7 +1,4 @@ -using BedrockService.Shared.Interfaces; -using System.Collections.Generic; - -namespace BedrockService.Service.Server.Management +namespace BedrockService.Service.Server.Management { public interface IPlayerManager { diff --git a/BedrockService/Service/Server/Management/PlayerManager.cs b/BedrockService/Service/Server/Management/PlayerManager.cs index 473041cd..eb4eabcd 100644 --- a/BedrockService/Service/Server/Management/PlayerManager.cs +++ b/BedrockService/Service/Server/Management/PlayerManager.cs @@ -1,9 +1,4 @@ -using BedrockService.Shared.Classes; -using BedrockService.Shared.Interfaces; -using System; -using System.Collections.Generic; - -namespace BedrockService.Service.Server.Management +namespace BedrockService.Service.Server.Management { public class PlayerManager : IPlayerManager { diff --git a/BedrockService/Service/Startup.cs b/BedrockService/Service/Startup.cs index 489c64b8..1f81abd9 100644 --- a/BedrockService/Service/Startup.cs +++ b/BedrockService/Service/Startup.cs @@ -3,26 +3,9 @@ // This file will need updated according to the specific scenario of the application being upgraded. // For more information on ASP.NET Core startup files, see https://docs.microsoft.com/aspnet/core/fundamentals/startup -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Threading.Tasks; -using BedrockService.Service.Core; -using BedrockService.Service.Logging; -using BedrockService.Service.Management; -using BedrockService.Service.Networking; -using BedrockService.Shared.Classes; -using BedrockService.Shared.Interfaces; +using BedrockService.Service.Core.Interfaces; using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.NewtonsoftJson; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; namespace BedrockService.Service { @@ -82,7 +65,7 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env) } private void ConfigureMvcOptions(MvcOptions mvcOptions) - { + { } } } diff --git a/BedrockService/Service/app.config b/BedrockService/Service/app.config new file mode 100644 index 00000000..312bb3f2 --- /dev/null +++ b/BedrockService/Service/app.config @@ -0,0 +1,3 @@ + + + From 9d08e1a9edb8de1bd2800c5341f7d8236d9ffb79 Mon Sep 17 00:00:00 2001 From: crowbarmaster Date: Mon, 6 Dec 2021 14:31:46 -0500 Subject: [PATCH 08/62] Fixed task method loops to reference token. Renamed keepAlive to EsablishedLink to use as a trigger for connectTimer. --- BedrockService/Client/Networking/TCPClient.cs | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/BedrockService/Client/Networking/TCPClient.cs b/BedrockService/Client/Networking/TCPClient.cs index d0298ce5..1c142af6 100644 --- a/BedrockService/Client/Networking/TCPClient.cs +++ b/BedrockService/Client/Networking/TCPClient.cs @@ -19,6 +19,7 @@ public class TCPClient public TcpClient OpenedTcpClient; public string ClientName; public NetworkStream stream; + public bool EstablishedLink; public bool Connected; public bool EnableRead; public bool PlayerInfoArrived; @@ -29,9 +30,8 @@ public class TCPClient public Task HeartbeatTask; private CancellationTokenSource _netCancelSource; private int _heartbeatFailTimeout; - private const int _heartbeatFailTimeoutLimit = 250; + private const int _heartbeatFailTimeoutLimit = 350; private bool _heartbeatRecieved; - private bool _keepAlive; private readonly IBedrockLogger _logger; public TCPClient(IBedrockLogger logger) @@ -57,7 +57,7 @@ public bool EstablishConnection(string addr, int port) EnableRead = false; OpenedTcpClient = new TcpClient(addr, port); stream = OpenedTcpClient.GetStream(); - _keepAlive = true; + EstablishedLink = true; ClientReciever = Task.Factory.StartNew(new Action(ReceiveListener), _netCancelSource.Token); } catch @@ -68,7 +68,7 @@ public bool EstablishConnection(string addr, int port) ClientReciever = null; return false; } - return _keepAlive; + return EstablishedLink; } public void CloseConnection() @@ -79,12 +79,13 @@ public void CloseConnection() stream.Dispose(); stream = null; Connected = false; - _keepAlive = false; + EstablishedLink = false; + _netCancelSource.Cancel(); } catch (NullReferenceException) { Connected = false; - _keepAlive = false; + EstablishedLink = false; } catch (Exception e) { @@ -246,11 +247,11 @@ public void ReceiveListener() public void SendHeatbeatSignal() { _logger.AppendLine("HeartbeatThread started."); - while (_keepAlive) + while (!_netCancelSource.Token.IsCancellationRequested) { _heartbeatRecieved = false; SendData(NetworkMessageSource.Client, NetworkMessageDestination.Service, NetworkMessageTypes.Heartbeat); - while (!_heartbeatRecieved && _keepAlive) + while (!_heartbeatRecieved && EstablishedLink) { Thread.Sleep(100); _heartbeatFailTimeout++; @@ -261,7 +262,7 @@ public void SendHeatbeatSignal() _heartbeatFailTimeout = 0; } } - // Logger.AppendLine("ThumpThump"); + _logger.AppendLine("ThumpThump"); _heartbeatRecieved = false; _heartbeatFailTimeout = 0; Thread.Sleep(3000); @@ -279,7 +280,7 @@ public bool SendData(byte[] bytes, NetworkMessageSource source, NetworkMessageDe compiled[7] = (byte)type; compiled[8] = (byte)status; Buffer.BlockCopy(bytes, 0, compiled, 9, bytes.Length); - if (_keepAlive) + if (EstablishedLink) { try { @@ -311,9 +312,12 @@ public bool SendData(byte[] bytes, NetworkMessageSource source, NetworkMessageDe public void Dispose() { - _netCancelSource.Cancel(); - _netCancelSource.Dispose(); - _netCancelSource = null; + if(_netCancelSource != null) + { + _netCancelSource.Cancel(); + _netCancelSource.Dispose(); + _netCancelSource = null; + } HeartbeatTask = null; ClientReciever = null; if (OpenedTcpClient != null) From 7cbf39acdc616b57932eeda3b1876f623e0300bb Mon Sep 17 00:00:00 2001 From: crowbarmaster Date: Mon, 6 Dec 2021 14:37:12 -0500 Subject: [PATCH 09/62] Updated to use EstablishedLink bool, fixes repeating connect issue. --- BedrockService/Client/Forms/MainWindow.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BedrockService/Client/Forms/MainWindow.cs b/BedrockService/Client/Forms/MainWindow.cs index 9dd8783c..bcfd1944 100644 --- a/BedrockService/Client/Forms/MainWindow.cs +++ b/BedrockService/Client/Forms/MainWindow.cs @@ -45,12 +45,12 @@ public MainWindow(IProcessInfo processInfo, IBedrockLogger logger) private void ConnectTimer_Elapsed(object sender, ElapsedEventArgs e) { - if (_connectTimer.Enabled && !FormManager.TCPClient.Connected) + if (_connectTimer.Enabled && !FormManager.TCPClient.EstablishedLink) { if (_connectTimer.Interval == 100.0) _connectTimer.Interval = 5000.0; Invoke((MethodInvoker)delegate { FormManager.TCPClient.ConnectHost(_configManager.HostConnectList.FirstOrDefault(host => host.GetHostName() == (string)HostListBox.SelectedItem)); }); - if (connectedHost != null && FormManager.TCPClient.Connected) + if (connectedHost != null && FormManager.TCPClient.EstablishedLink) { ServerBusy = false; Invoke((MethodInvoker)delegate { ComponentEnableManager(); }); From 8aecce541080f6f5cff962725093e7c53c91607d Mon Sep 17 00:00:00 2001 From: crowbarmaster Date: Mon, 6 Dec 2021 14:38:20 -0500 Subject: [PATCH 10/62] Moved closing of form into parent. --- BedrockService/Client/Forms/NewPlayerRegistrationForm.cs | 1 - BedrockService/Client/Forms/PlayerManagerForm.cs | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/BedrockService/Client/Forms/NewPlayerRegistrationForm.cs b/BedrockService/Client/Forms/NewPlayerRegistrationForm.cs index 2de19efc..4f36ae20 100644 --- a/BedrockService/Client/Forms/NewPlayerRegistrationForm.cs +++ b/BedrockService/Client/Forms/NewPlayerRegistrationForm.cs @@ -19,7 +19,6 @@ private void saveClick(object sender, EventArgs e) { PlayerToAdd = new Player(xuidTextBox.Text, usernameTextBox.Text, DateTime.Now.Ticks.ToString(), "0", "0", whitelistedChkBox.Checked, permissionComboBox.SelectedItem.ToString(), ignoreLimitChkBox.Checked); DialogResult = DialogResult.OK; - Close(); } } } diff --git a/BedrockService/Client/Forms/PlayerManagerForm.cs b/BedrockService/Client/Forms/PlayerManagerForm.cs index 416e9ea9..1e66e2f4 100644 --- a/BedrockService/Client/Forms/PlayerManagerForm.cs +++ b/BedrockService/Client/Forms/PlayerManagerForm.cs @@ -163,6 +163,7 @@ private void registerPlayerBtn_Click(object sender, EventArgs e) _server.GetPlayerList().Add(form.PlayerToAdd); modifiedPlayers.Add(form.PlayerToAdd); RefreshGridContents(); + form.Close(); } } } From 40bcc3ea23e355759b35f698a8d3607b46844ad6 Mon Sep 17 00:00:00 2001 From: crowbarmaster Date: Mon, 6 Dec 2021 14:39:59 -0500 Subject: [PATCH 11/62] Console.WriteLine no longer appends to VS Debug console. Use System.Diagnostics.Debug.WriteLine instead. --- BedrockService/Client/Management/ClientLogger.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BedrockService/Client/Management/ClientLogger.cs b/BedrockService/Client/Management/ClientLogger.cs index b666ccb9..bba1cf5f 100644 --- a/BedrockService/Client/Management/ClientLogger.cs +++ b/BedrockService/Client/Management/ClientLogger.cs @@ -29,7 +29,7 @@ public void AppendLine(string text) Log.Add(addText); LogWriter.WriteLine(addText); LogWriter.Flush(); - Console.WriteLine(text); + System.Diagnostics.Debug.WriteLine(text); } public void AppendText(string text) => AppendLine(text); From 91f787b1cd6383d96b75ef1226ca267655147649 Mon Sep 17 00:00:00 2001 From: crowbarmaster Date: Mon, 6 Dec 2021 14:41:35 -0500 Subject: [PATCH 12/62] Add null check for selectedServer for rare intermitent exception during disconnect. --- BedrockService/Client/Management/LogManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BedrockService/Client/Management/LogManager.cs b/BedrockService/Client/Management/LogManager.cs index 17f170d4..c72d0242 100644 --- a/BedrockService/Client/Management/LogManager.cs +++ b/BedrockService/Client/Management/LogManager.cs @@ -55,7 +55,7 @@ private void LogManagerTask() { UpdateLogBoxInvoked(string.Join("\r\n", connectedHost.GetLog())); } - else if (!FormManager.MainWindow.ShowsSvcLog && FormManager.MainWindow.selectedServer.GetLog() != null && FormManager.MainWindow.selectedServer.GetLog().Count != currentLogBoxLength) + else if (!FormManager.MainWindow.ShowsSvcLog && FormManager.MainWindow.selectedServer != null && FormManager.MainWindow.selectedServer.GetLog() != null && FormManager.MainWindow.selectedServer.GetLog().Count != currentLogBoxLength) { UpdateLogBoxInvoked(string.Join("", FormManager.MainWindow.selectedServer.GetLog())); } From b8afa0d4e4af26420243036a309c56e3fcc845f2 Mon Sep 17 00:00:00 2001 From: crowbarmaster Date: Mon, 6 Dec 2021 14:42:47 -0500 Subject: [PATCH 13/62] Update Task methods to reference token in loops. --- BedrockService/Service/Networking/TCPListener.cs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/BedrockService/Service/Networking/TCPListener.cs b/BedrockService/Service/Networking/TCPListener.cs index cf7db9af..d023cbf7 100644 --- a/BedrockService/Service/Networking/TCPListener.cs +++ b/BedrockService/Service/Networking/TCPListener.cs @@ -22,7 +22,7 @@ public class TCPListener : ITCPListener, IMessageSender private bool _firstHeartbeatRecieved = false; private bool _keepAlive = false; private int _heartbeatFailTimeout; - private readonly int _heartbeatFailTimeoutLimit = 200; + private readonly int _heartbeatFailTimeoutLimit = 500; private Dictionary _standardMessageLookup; private Dictionary _flaggedMessageLookup; private readonly IPAddress _ipAddress = IPAddress.Parse("0.0.0.0"); @@ -55,7 +55,6 @@ public void StartListening(CancellationToken token) try { _inListener.Start(); - _keepAlive = true; } catch (SocketException e) { @@ -81,6 +80,8 @@ public void StartListening(CancellationToken token) { _logger.AppendLine(e.ToString()); } + if (token.IsCancellationRequested) + break; } } @@ -147,7 +148,7 @@ private void IncomingListener(CancellationToken token) byte serverIndex = 0xFF; NetworkMessageTypes msgType = 0; NetworkMessageFlags msgFlag = 0; - while (_keepAlive) + while (!token.IsCancellationRequested) { try { @@ -221,10 +222,10 @@ private void IncomingListener(CancellationToken token) private void SendBackHeatbeatSignal(CancellationToken token) { _logger.AppendLine("HeartBeatSender started."); - while (_keepAlive) + while (!token.IsCancellationRequested) { _heartbeatRecieved = false; - while (!_heartbeatRecieved && _keepAlive) + while (!_heartbeatRecieved) { Thread.Sleep(100); _heartbeatFailTimeout++; @@ -235,6 +236,7 @@ private void SendBackHeatbeatSignal(CancellationToken token) try { SendData(NetworkMessageSource.Service, NetworkMessageDestination.Client, NetworkMessageTypes.Heartbeat); + //_logger.AppendLine("ThumpThump"); _heartbeatFailTimeout = 0; } catch (Exception e) @@ -247,6 +249,7 @@ private void SendBackHeatbeatSignal(CancellationToken token) } _heartbeatRecieved = false; _heartbeatFailTimeout = 0; + //_logger.AppendLine("ThumpThump"); SendData(NetworkMessageSource.Service, NetworkMessageDestination.Client, NetworkMessageTypes.Heartbeat); int timeWaited = 0; while (timeWaited < 3000) From 53801469a165994f0be4b78bb78c9e1fc94231c7 Mon Sep 17 00:00:00 2001 From: crowbarmaster Date: Mon, 6 Dec 2021 14:43:58 -0500 Subject: [PATCH 14/62] Comment out heatbeat debug logging. --- BedrockService/Client/Networking/TCPClient.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BedrockService/Client/Networking/TCPClient.cs b/BedrockService/Client/Networking/TCPClient.cs index 1c142af6..458590fd 100644 --- a/BedrockService/Client/Networking/TCPClient.cs +++ b/BedrockService/Client/Networking/TCPClient.cs @@ -262,7 +262,7 @@ public void SendHeatbeatSignal() _heartbeatFailTimeout = 0; } } - _logger.AppendLine("ThumpThump"); + //_logger.AppendLine("ThumpThump"); _heartbeatRecieved = false; _heartbeatFailTimeout = 0; Thread.Sleep(3000); From 0e27ce1694b678a91548d7b48221683fc5cd989d Mon Sep 17 00:00:00 2001 From: crowbarmaster Date: Mon, 6 Dec 2021 14:48:21 -0500 Subject: [PATCH 15/62] Small sleep needed to allow link to establish. --- BedrockService/Client/Forms/MainWindow.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/BedrockService/Client/Forms/MainWindow.cs b/BedrockService/Client/Forms/MainWindow.cs index bcfd1944..7867b2be 100644 --- a/BedrockService/Client/Forms/MainWindow.cs +++ b/BedrockService/Client/Forms/MainWindow.cs @@ -50,6 +50,7 @@ private void ConnectTimer_Elapsed(object sender, ElapsedEventArgs e) if (_connectTimer.Interval == 100.0) _connectTimer.Interval = 5000.0; Invoke((MethodInvoker)delegate { FormManager.TCPClient.ConnectHost(_configManager.HostConnectList.FirstOrDefault(host => host.GetHostName() == (string)HostListBox.SelectedItem)); }); + Thread.Sleep(500); if (connectedHost != null && FormManager.TCPClient.EstablishedLink) { ServerBusy = false; From 0c18b5cd68409245403cd451771e97a1f9c3cdf8 Mon Sep 17 00:00:00 2001 From: crowbarmaster Date: Wed, 8 Dec 2021 11:39:16 -0500 Subject: [PATCH 16/62] Fix disconnect timer operation. Disconnect message sent to server is no longer needed and removed. --- BedrockService/Client/Forms/MainWindow.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/BedrockService/Client/Forms/MainWindow.cs b/BedrockService/Client/Forms/MainWindow.cs index 7867b2be..b2b83c3f 100644 --- a/BedrockService/Client/Forms/MainWindow.cs +++ b/BedrockService/Client/Forms/MainWindow.cs @@ -47,8 +47,7 @@ private void ConnectTimer_Elapsed(object sender, ElapsedEventArgs e) { if (_connectTimer.Enabled && !FormManager.TCPClient.EstablishedLink) { - if (_connectTimer.Interval == 100.0) - _connectTimer.Interval = 5000.0; + _connectTimer.Interval = 2000.0; Invoke((MethodInvoker)delegate { FormManager.TCPClient.ConnectHost(_configManager.HostConnectList.FirstOrDefault(host => host.GetHostName() == (string)HostListBox.SelectedItem)); }); Thread.Sleep(500); if (connectedHost != null && FormManager.TCPClient.EstablishedLink) @@ -63,17 +62,17 @@ private void ConnectTimer_Elapsed(object sender, ElapsedEventArgs e) _connectTimeout++; if (_connectTimeout >= _connectTimeoutLimit) { + _connectTimer.Enabled = false; + _connectTimer.Stop(); + _connectTimer.Close(); Invoke((MethodInvoker)delegate { RefreshServerContents(); HostInfoLabel.Text = $"Failed to connect to host!"; Connect.Enabled = true; ComponentEnableManager(); - _connectTimer.Enabled = false; - _connectTimer.Stop(); - _connectTimer.Close(); - return; }); + return; } } } @@ -364,13 +363,13 @@ private void PlayerManager_Click(object sender, EventArgs e) private void Disconn_Click(object sender, EventArgs e) { + _connectTimer.Stop(); if (_logManager.StopLogThread()) { try { if (FormManager.TCPClient.Connected) { - FormManager.TCPClient.SendData(NetworkMessageSource.Client, NetworkMessageDestination.Service, NetworkMessageTypes.Disconnect); Thread.Sleep(500); FormManager.TCPClient.CloseConnection(); } @@ -620,6 +619,7 @@ private void nbtStudioBtn_Click(object sender, EventArgs e) _configManager.NBTStudioPath = openFile.FileName; _configManager.SaveConfigFile(); } + else return; } ServerBusy = true; FormManager.TCPClient.SendData(NetworkMessageSource.Client, NetworkMessageDestination.Server, connectedHost.GetServerIndex(selectedServer), NetworkMessageTypes.LevelEditRequest); From 511aefb218e3f239b8a30419b9f936013a86688d Mon Sep 17 00:00:00 2001 From: crowbarmaster Date: Wed, 8 Dec 2021 11:41:51 -0500 Subject: [PATCH 17/62] Reinitialize CTS in start method for proper restarts of task. Private variables renamed to follow standards. --- .../Client/Management/LogManager.cs | 50 +++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/BedrockService/Client/Management/LogManager.cs b/BedrockService/Client/Management/LogManager.cs index c72d0242..c4055395 100644 --- a/BedrockService/Client/Management/LogManager.cs +++ b/BedrockService/Client/Management/LogManager.cs @@ -11,33 +11,33 @@ namespace BedrockService.Client.Management { class LogManager { - public Task LogThread; + public Task LogTask; public bool EnableFlag; public bool Working = false; public List ServiceLogs = new List(); - private CancellationTokenSource LogTaskCancelSource; - private IServiceConfiguration connectedHost; - private readonly IBedrockLogger Logger; + private CancellationTokenSource _logTaskCancelSource; + private IServiceConfiguration _connectedHost; + private readonly IBedrockLogger _logger; public LogManager(IBedrockLogger logger) { - Logger = logger; + _logger = logger; } private void LogManagerTask() { - while (!LogTaskCancelSource.Token.IsCancellationRequested) + while (!_logTaskCancelSource.Token.IsCancellationRequested) { try { Working = true; StringBuilder sendString = new StringBuilder(); - foreach (ServerInfo server in connectedHost.GetServerList()) + foreach (ServerInfo server in _connectedHost.GetServerList()) { server.ConsoleBuffer = server.ConsoleBuffer ?? new List(); sendString.Append($"{server.ServerName};{server.ConsoleBuffer.Count}|"); } - sendString.Append($"Service;{connectedHost.GetLog().Count}"); + sendString.Append($"Service;{_connectedHost.GetLog().Count}"); byte[] stringsToBytes = Encoding.UTF8.GetBytes(sendString.ToString()); FormManager.TCPClient.SendData(stringsToBytes, NetworkMessageSource.Client, NetworkMessageDestination.Service, NetworkMessageTypes.ConsoleLogUpdate); Thread.Sleep(200); @@ -51,9 +51,9 @@ private void LogManagerTask() { currentLogBoxLength = FormManager.MainWindow.LogBox.TextLength; }); - if (FormManager.MainWindow.ShowsSvcLog && connectedHost.GetLog().Count != currentLogBoxLength) + if (FormManager.MainWindow.ShowsSvcLog && _connectedHost.GetLog().Count != currentLogBoxLength) { - UpdateLogBoxInvoked(string.Join("\r\n", connectedHost.GetLog())); + UpdateLogBoxInvoked(string.Join("\r\n", _connectedHost.GetLog())); } else if (!FormManager.MainWindow.ShowsSvcLog && FormManager.MainWindow.selectedServer != null && FormManager.MainWindow.selectedServer.GetLog() != null && FormManager.MainWindow.selectedServer.GetLog().Count != currentLogBoxLength) { @@ -63,15 +63,15 @@ private void LogManagerTask() } catch (Exception e) { - Logger.AppendLine($"LogManager Error! Stacetrace: {e.StackTrace}"); + _logger.AppendLine($"LogManager Error! Stacetrace: {e.StackTrace}"); } } } public bool InitLogThread(IServiceConfiguration host) { - connectedHost = host; - LogTaskCancelSource = new CancellationTokenSource(); + _connectedHost = host; + _logTaskCancelSource = new CancellationTokenSource(); return StartLogThread(); } @@ -79,37 +79,37 @@ public bool StartLogThread() { try { - if (LogThread != null && !LogTaskCancelSource.IsCancellationRequested) - LogTaskCancelSource.Cancel(); - LogThread = new Task(new Action(LogManagerTask), LogTaskCancelSource.Token); - EnableFlag = true; - LogThread.Start(); - Logger.AppendLine("LogThread started"); + if (LogTask != null && !_logTaskCancelSource.IsCancellationRequested) + _logTaskCancelSource.Cancel(); + Thread.Sleep(500); + _logTaskCancelSource = new CancellationTokenSource(); + LogTask = Task.Factory.StartNew(new Action(LogManagerTask), _logTaskCancelSource.Token); + _logger.AppendLine("LogThread started"); return true; } catch (Exception e) { - Logger.AppendLine($"Error starting LogThread: {e.StackTrace}"); + _logger.AppendLine($"Error starting LogThread: {e.StackTrace}"); } return false; } public bool StopLogThread() { - if (LogThread == null) + if (LogTask == null) { return true; } try { - LogTaskCancelSource.Cancel(); + _logTaskCancelSource.Cancel(); } catch (ThreadAbortException e) { - Logger.AppendLine(e.StackTrace); + _logger.AppendLine(e.StackTrace); } - Logger.AppendLine("LogThread stopped"); - LogThread = null; + _logger.AppendLine("LogThread stopped"); + LogTask = null; return true; } From 8e6bc2a0ea22ca63157c8bb9667f8e986d183f6c Mon Sep 17 00:00:00 2001 From: crowbarmaster Date: Wed, 8 Dec 2021 11:44:23 -0500 Subject: [PATCH 18/62] Reworked Heartbeat system to send only, disconnects now handled by watching for network stream errors. --- BedrockService/Client/Networking/TCPClient.cs | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/BedrockService/Client/Networking/TCPClient.cs b/BedrockService/Client/Networking/TCPClient.cs index 458590fd..223c3227 100644 --- a/BedrockService/Client/Networking/TCPClient.cs +++ b/BedrockService/Client/Networking/TCPClient.cs @@ -28,9 +28,9 @@ public class TCPClient public List RecievedPacks; public Task ClientReciever; public Task HeartbeatTask; - private CancellationTokenSource _netCancelSource; + private CancellationTokenSource? _netCancelSource; private int _heartbeatFailTimeout; - private const int _heartbeatFailTimeoutLimit = 350; + private const int _heartbeatFailTimeoutLimit = 2; private bool _heartbeatRecieved; private readonly IBedrockLogger _logger; @@ -41,7 +41,6 @@ public TCPClient(IBedrockLogger logger) public void ConnectHost(IClientSideServiceConfiguration host) { - _netCancelSource = new CancellationTokenSource(); if (EstablishConnection(host.GetAddress(), int.Parse(host.GetPort()))) { SendData(NetworkMessageSource.Client, NetworkMessageDestination.Service, NetworkMessageTypes.Connect); @@ -52,6 +51,7 @@ public void ConnectHost(IClientSideServiceConfiguration host) public bool EstablishConnection(string addr, int port) { _logger.AppendLine("Connecting to Server"); + _netCancelSource = new CancellationTokenSource(); try { EnableRead = false; @@ -95,7 +95,6 @@ public void CloseConnection() public void ReceiveListener() { - List byteBlocks = new List(); while (!_netCancelSource.IsCancellationRequested) { try @@ -249,24 +248,17 @@ public void SendHeatbeatSignal() _logger.AppendLine("HeartbeatThread started."); while (!_netCancelSource.Token.IsCancellationRequested) { - _heartbeatRecieved = false; + Thread.Sleep(3000); SendData(NetworkMessageSource.Client, NetworkMessageDestination.Service, NetworkMessageTypes.Heartbeat); while (!_heartbeatRecieved && EstablishedLink) { Thread.Sleep(100); - _heartbeatFailTimeout++; - if (_heartbeatFailTimeout > _heartbeatFailTimeoutLimit) - { - FormManager.MainWindow.HeartbeatFailDisconnect(); - _netCancelSource.Cancel(); - _heartbeatFailTimeout = 0; - } } - //_logger.AppendLine("ThumpThump"); + _logger.AppendLine("ThumpThump"); _heartbeatRecieved = false; _heartbeatFailTimeout = 0; - Thread.Sleep(3000); } + _logger.AppendLine("HeartbeatThread exited."); } public bool SendData(byte[] bytes, NetworkMessageSource source, NetworkMessageDestination destination, byte serverIndex, NetworkMessageTypes type, NetworkMessageFlags status) @@ -286,12 +278,20 @@ public bool SendData(byte[] bytes, NetworkMessageSource source, NetworkMessageDe { stream.Write(compiled, 0, compiled.Length); stream.Flush(); + _heartbeatFailTimeout = 0; return true; - } catch { _logger.AppendLine("Error writing to network stream!"); + Thread.Sleep(100); + _heartbeatFailTimeout++; + if (_heartbeatFailTimeout > _heartbeatFailTimeoutLimit) + { + FormManager.MainWindow.HeartbeatFailDisconnect(); + _netCancelSource.Cancel(); + _heartbeatFailTimeout = 0; + } return false; } } From 946921e3e9b8b81800fed8a30a0b673bc9dd55d7 Mon Sep 17 00:00:00 2001 From: crowbarmaster Date: Wed, 8 Dec 2021 11:46:44 -0500 Subject: [PATCH 19/62] Network related task constructors now take a CancellationTokenSource for syncronized task cancelation. --- BedrockService/Service/Core/Tasks/ClientServiceTask.cs | 6 +++++- BedrockService/Service/Core/Tasks/HeartbeatTask.cs | 6 +++++- BedrockService/Service/Core/Tasks/TCPListenerTask.cs | 6 +++++- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/BedrockService/Service/Core/Tasks/ClientServiceTask.cs b/BedrockService/Service/Core/Tasks/ClientServiceTask.cs index da8263fc..384bd49e 100644 --- a/BedrockService/Service/Core/Tasks/ClientServiceTask.cs +++ b/BedrockService/Service/Core/Tasks/ClientServiceTask.cs @@ -7,7 +7,11 @@ class ClientServiceTask : IServiceTask { private Task _thread; private CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource(); - public ClientServiceTask(Action method) => _thread = Task.Factory.StartNew((CancellationToken) => method(_cancellationTokenSource.Token), _cancellationTokenSource.Token); + public ClientServiceTask(Action method, CancellationTokenSource source) + { + _cancellationTokenSource = source; + _thread = Task.Factory.StartNew((CancellationToken) => method(_cancellationTokenSource.Token), _cancellationTokenSource.Token); + } public void CancelTask() => _cancellationTokenSource.Cancel(); public bool IsAlive() => _thread != null && _thread.Status == TaskStatus.Running && !_cancellationTokenSource.IsCancellationRequested; } diff --git a/BedrockService/Service/Core/Tasks/HeartbeatTask.cs b/BedrockService/Service/Core/Tasks/HeartbeatTask.cs index a0173873..e56d8691 100644 --- a/BedrockService/Service/Core/Tasks/HeartbeatTask.cs +++ b/BedrockService/Service/Core/Tasks/HeartbeatTask.cs @@ -7,7 +7,11 @@ class HeartbeatTask : IServiceTask { private Task _thread; private CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource(); - public HeartbeatTask(Action method) => _thread = Task.Factory.StartNew((CancellationToken) => method(_cancellationTokenSource.Token), _cancellationTokenSource.Token); + public HeartbeatTask(Action method, CancellationTokenSource source) + { + _cancellationTokenSource = source; + _thread = Task.Factory.StartNew((CancellationToken) => method(_cancellationTokenSource.Token), _cancellationTokenSource.Token); + } public void CancelTask() => _cancellationTokenSource.Cancel(); public bool IsAlive() => _thread != null && _thread.Status == TaskStatus.Running && !_cancellationTokenSource.IsCancellationRequested; } diff --git a/BedrockService/Service/Core/Tasks/TCPListenerTask.cs b/BedrockService/Service/Core/Tasks/TCPListenerTask.cs index ede4d25a..8efa5bce 100644 --- a/BedrockService/Service/Core/Tasks/TCPListenerTask.cs +++ b/BedrockService/Service/Core/Tasks/TCPListenerTask.cs @@ -7,7 +7,11 @@ class TCPListenerTask : IServiceTask { private Task _thread; private CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource(); - public TCPListenerTask(Action method) => _thread = Task.Factory.StartNew((CancellationToken) => method(_cancellationTokenSource.Token), _cancellationTokenSource.Token); + public TCPListenerTask(Action method, CancellationTokenSource source) + { + _cancellationTokenSource = source; + _thread = Task.Factory.StartNew((CancellationToken) => method(_cancellationTokenSource.Token), _cancellationTokenSource.Token); + } public void CancelTask() => _cancellationTokenSource.Cancel(); public bool IsAlive() => _thread != null && _thread.Status == TaskStatus.Running && !_cancellationTokenSource.IsCancellationRequested; } From fb86896beb70fe8e1d96729765a7cc62506bcc43 Mon Sep 17 00:00:00 2001 From: crowbarmaster Date: Wed, 8 Dec 2021 11:49:16 -0500 Subject: [PATCH 20/62] Fixed incorrect value saved in whitelist entry. Bool condition for permissions loop found blocking permission saving if the player was not whitelisted. --- BedrockService/Service/Management/ConfigManager.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/BedrockService/Service/Management/ConfigManager.cs b/BedrockService/Service/Management/ConfigManager.cs index 48d395fd..99cf6f25 100644 --- a/BedrockService/Service/Management/ConfigManager.cs +++ b/BedrockService/Service/Management/ConfigManager.cs @@ -294,7 +294,7 @@ public void WriteJSONFiles(IServerConfiguration server) if (!player.IsDefaultRegistration() && playerReg[0] == "True") { sb.Append("\t{\n"); - sb.Append($"\t\t\"ignoresPlayerLimit\": {playerReg[0].ToLower()},\n"); + sb.Append($"\t\t\"ignoresPlayerLimit\": {playerReg[2].ToLower()},\n"); sb.Append($"\t\t\"name\": \"{player.GetUsername()}\",\n"); sb.Append($"\t\t\"xuid\": \"{player.GetXUID()}\"\n"); sb.Append("\t},\n"); @@ -312,7 +312,7 @@ public void WriteJSONFiles(IServerConfiguration server) foreach (Player player in server.GetPlayerList()) { string[] playerReg = player.GetRegistration(); - if (!player.IsDefaultRegistration() && playerReg[0] == "False") + if (!player.IsDefaultRegistration()) { sb.Append("\t{\n"); sb.Append($"\t\t\"permission\": \"{playerReg[1]}\",\n"); From 25d839c78c3281716b1945fc2994a4247837e54a Mon Sep 17 00:00:00 2001 From: crowbarmaster Date: Wed, 8 Dec 2021 12:07:28 -0500 Subject: [PATCH 21/62] Now check for pending clients to avoid blocking the loop. Proper cancelation of tasks via CTS during resets. Heartbeat now simply sends requests, disconnects handled by write to stream errors. Catch errors related to connect message parsing, and reset the listener. --- .../Service/Networking/TCPListener.cs | 120 ++++++++---------- 1 file changed, 51 insertions(+), 69 deletions(-) diff --git a/BedrockService/Service/Networking/TCPListener.cs b/BedrockService/Service/Networking/TCPListener.cs index d023cbf7..40de7fc5 100644 --- a/BedrockService/Service/Networking/TCPListener.cs +++ b/BedrockService/Service/Networking/TCPListener.cs @@ -15,32 +15,31 @@ public class TCPListener : ITCPListener, IMessageSender private NetworkStream _stream; private readonly IServiceConfiguration _serviceConfiguration; private readonly IBedrockLogger _logger; - private IServiceTask _tcpThread; - private IServiceTask _clientThread; - private IServiceTask _heartbeatThread; - private bool _heartbeatRecieved = false; - private bool _firstHeartbeatRecieved = false; - private bool _keepAlive = false; + private IServiceTask _tcpTask; + private IServiceTask _clientListenerTask; + private IServiceTask _heartbeatTask; private int _heartbeatFailTimeout; - private readonly int _heartbeatFailTimeoutLimit = 500; + private readonly int _heartbeatFailTimeoutLimit = 2; private Dictionary _standardMessageLookup; private Dictionary _flaggedMessageLookup; private readonly IPAddress _ipAddress = IPAddress.Parse("0.0.0.0"); private readonly System.Timers.Timer _reconnectTimer = new System.Timers.Timer(500.0); private CommsKeyContainer _keyContainer; + private CancellationTokenSource _cancelTokenSource = new CancellationTokenSource(); public TCPListener(IServiceConfiguration serviceConfiguration, IBedrockLogger logger) { _logger = logger; _serviceConfiguration = serviceConfiguration; _reconnectTimer.Elapsed += ReconnectTimer_Elapsed; - _tcpThread = new TCPListenerTask(new Action(StartListening)); + _tcpTask = new TCPListenerTask(new Action(StartListening), _cancelTokenSource); + _cancelTokenSource = new CancellationTokenSource(); } private void ReconnectTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) { _reconnectTimer.Stop(); - _tcpThread = new TCPListenerTask(new Action(StartListening)); + _tcpTask = new TCPListenerTask(new Action(StartListening), _cancelTokenSource); } public void SetStrategyDictionaries(Dictionary standard, Dictionary flagged) @@ -60,17 +59,21 @@ public void StartListening(CancellationToken token) { _logger.AppendLine($"Error! {e.Message}"); Thread.Sleep(2000); - //Environment.Exit(1); + Environment.Exit(1); } while (true) { try { - _client = _inListener.AcceptTcpClient(); - _stream = _client.GetStream(); - _clientThread = new ClientServiceTask(new Action(IncomingListener)); - _heartbeatThread = new HeartbeatTask(new Action(SendBackHeatbeatSignal)); + if (_inListener.Pending()) + { + _cancelTokenSource = new CancellationTokenSource(); + _client = _inListener.AcceptTcpClient(); + _stream = _client.GetStream(); + _clientListenerTask = new ClientServiceTask(new Action(IncomingListener), _cancelTokenSource); + _heartbeatTask = new HeartbeatTask(new Action(HeartbeatSender), _cancelTokenSource); + } } catch (ThreadStateException) { } catch (NullReferenceException) { } @@ -80,25 +83,23 @@ public void StartListening(CancellationToken token) { _logger.AppendLine(e.ToString()); } - if (token.IsCancellationRequested) - break; } } public void ResetListener() { - _keepAlive = false; - while (_heartbeatThread.IsAlive()) - { - Thread.Sleep(300); - } + _logger.AppendLine("Resetting listener!"); + _cancelTokenSource.Cancel(); + _client.Client.Blocking = false; _stream.Close(); _stream.Dispose(); - _client.Client.Blocking = false; + _client.Close(); _inListener.Stop(); - _tcpThread.CancelTask(); - _tcpThread = null; - _tcpThread = new TCPListenerTask(new Action(StartListening)); + _tcpTask = null; + _tcpTask = new TCPListenerTask(new Action(StartListening), _cancelTokenSource); + _clientListenerTask = null; + _heartbeatTask = null; + } public void SendData(byte[] bytesToSend, NetworkMessageSource source, NetworkMessageDestination destination, byte serverIndex, NetworkMessageTypes type, NetworkMessageFlags status) @@ -113,16 +114,23 @@ public void SendData(byte[] bytesToSend, NetworkMessageSource source, NetworkMes byteHeader[8] = (byte)status; Buffer.BlockCopy(bytesToSend, 0, byteHeader, 9, bytesToSend.Length); - if (_clientThread.IsAlive()) + if (_clientListenerTask != null && _clientListenerTask.IsAlive()) { try { _stream.Write(byteHeader, 0, byteHeader.Length); _stream.Flush(); + _heartbeatFailTimeout = 0; } catch { _logger.AppendLine("Error writing to network stream!"); + _heartbeatFailTimeout++; + if (_heartbeatFailTimeout >= _heartbeatFailTimeoutLimit) + { + ResetListener(); + _heartbeatFailTimeout = 0; + } } } } @@ -139,7 +147,6 @@ public void SendData(byte[] bytesToSend, NetworkMessageSource source, NetworkMes private void IncomingListener(CancellationToken token) { - _keepAlive = true; _logger.AppendLine("Packet listener thread started."); int AvailBytes = 0; int byteCount = 0; @@ -153,7 +160,7 @@ private void IncomingListener(CancellationToken token) try { byte[] buffer = new byte[4]; - while (_client.Client.Available != 0) // Recieve data from client. + while (_client.Client != null && _client.Client.Available != 0) // Recieve data from client. { byteCount = _stream.Read(buffer, 0, 4); int expectedLen = BitConverter.ToInt32(buffer, 0); @@ -164,22 +171,24 @@ private void IncomingListener(CancellationToken token) serverIndex = buffer[2]; msgType = (NetworkMessageTypes)buffer[3]; msgFlag = (NetworkMessageFlags)buffer[4]; - if (msgType == NetworkMessageTypes.Heartbeat) - { - if (!_firstHeartbeatRecieved) - _firstHeartbeatRecieved = true; - _heartbeatRecieved = true; - } if (msgType == NetworkMessageTypes.Disconnect) { ResetListener(); } if (msgType < NetworkMessageTypes.Heartbeat) { - if (_standardMessageLookup.ContainsKey(msgType)) - _standardMessageLookup[msgType].ParseMessage(buffer, serverIndex); - else - _flaggedMessageLookup[msgType].ParseMessage(buffer, serverIndex, msgFlag); + try + { + if (_standardMessageLookup.ContainsKey(msgType)) + _standardMessageLookup[msgType].ParseMessage(buffer, serverIndex); + else + _flaggedMessageLookup[msgType].ParseMessage(buffer, serverIndex, msgFlag); + } + catch + { + if(msgType == NetworkMessageTypes.Connect) + ResetListener(); + } } } Thread.Sleep(200); @@ -211,52 +220,25 @@ private void IncomingListener(CancellationToken token) } try { - AvailBytes = _client.Client.Available; + AvailBytes = _client.Client != null ? _client.Client.Available:0; } catch { } - if (!_clientThread.IsAlive()) - _clientThread.CancelTask(); } } - private void SendBackHeatbeatSignal(CancellationToken token) + private void HeartbeatSender(CancellationToken token) { _logger.AppendLine("HeartBeatSender started."); while (!token.IsCancellationRequested) { - _heartbeatRecieved = false; - while (!_heartbeatRecieved) - { - Thread.Sleep(100); - _heartbeatFailTimeout++; - if (_heartbeatFailTimeout > _heartbeatFailTimeoutLimit) - { - if (!_firstHeartbeatRecieved) - { - try - { - SendData(NetworkMessageSource.Service, NetworkMessageDestination.Client, NetworkMessageTypes.Heartbeat); - //_logger.AppendLine("ThumpThump"); - _heartbeatFailTimeout = 0; - } - catch (Exception e) - { - _logger.AppendLine($"HeartBeatSender exited with error: {e.Message}"); - return; - } - } - } - } - _heartbeatRecieved = false; - _heartbeatFailTimeout = 0; - //_logger.AppendLine("ThumpThump"); + SendData(NetworkMessageSource.Service, NetworkMessageDestination.Client, NetworkMessageTypes.Heartbeat); int timeWaited = 0; while (timeWaited < 3000) { Thread.Sleep(100); timeWaited += 100; - if (!_keepAlive) + if (token.IsCancellationRequested) { _logger.AppendLine("HeartBeatSender exited."); return; From b51fb2753da4db9925d05ddb421597a584cfb87a Mon Sep 17 00:00:00 2001 From: crowbarmaster Date: Wed, 8 Dec 2021 12:08:34 -0500 Subject: [PATCH 22/62] Add new blank ServiceTests project. --- BedrockService/BedrockService.sln | 6 +++++ .../ServiceTests/ServiceTests.csproj | 23 +++++++++++++++++++ BedrockService/ServiceTests/UnitTest1.cs | 13 +++++++++++ 3 files changed, 42 insertions(+) create mode 100644 BedrockService/ServiceTests/ServiceTests.csproj create mode 100644 BedrockService/ServiceTests/UnitTest1.cs diff --git a/BedrockService/BedrockService.sln b/BedrockService/BedrockService.sln index f94d9163..4df1abda 100644 --- a/BedrockService/BedrockService.sln +++ b/BedrockService/BedrockService.sln @@ -13,6 +13,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BedrockService.Shared", "Be EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BedrockManagementServiceASP", "BedrockManagementServiceASP\BedrockManagementServiceASP.csproj", "{4AA65E9F-E3BE-4173-BE3B-BD4DFCD21C40}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServiceTests", "ServiceTests\ServiceTests.csproj", "{B20CAE22-52BD-4A49-ADA8-04F7C7007155}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -35,6 +37,10 @@ Global {4AA65E9F-E3BE-4173-BE3B-BD4DFCD21C40}.Debug|Any CPU.Build.0 = Debug|Any CPU {4AA65E9F-E3BE-4173-BE3B-BD4DFCD21C40}.Release|Any CPU.ActiveCfg = Release|Any CPU {4AA65E9F-E3BE-4173-BE3B-BD4DFCD21C40}.Release|Any CPU.Build.0 = Release|Any CPU + {B20CAE22-52BD-4A49-ADA8-04F7C7007155}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B20CAE22-52BD-4A49-ADA8-04F7C7007155}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B20CAE22-52BD-4A49-ADA8-04F7C7007155}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B20CAE22-52BD-4A49-ADA8-04F7C7007155}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/BedrockService/ServiceTests/ServiceTests.csproj b/BedrockService/ServiceTests/ServiceTests.csproj new file mode 100644 index 00000000..9f77f5f6 --- /dev/null +++ b/BedrockService/ServiceTests/ServiceTests.csproj @@ -0,0 +1,23 @@ + + + + net6.0 + enable + + false + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + diff --git a/BedrockService/ServiceTests/UnitTest1.cs b/BedrockService/ServiceTests/UnitTest1.cs new file mode 100644 index 00000000..bca01d1c --- /dev/null +++ b/BedrockService/ServiceTests/UnitTest1.cs @@ -0,0 +1,13 @@ +using Xunit; + +namespace ServiceTests +{ + public class UnitTest1 + { + [Fact] + public void Test1() + { + + } + } +} \ No newline at end of file From e5fedcc5dd4c5143666d9ff27942a9d7e86e305a Mon Sep 17 00:00:00 2001 From: crowbarmaster Date: Tue, 14 Dec 2021 10:12:18 -0500 Subject: [PATCH 23/62] Add generic write file methods for later use. Wrote UpdateJArrayFile to handle writing of all MC Json files. --- .../Utilities/FileUtils.cs | 46 ++++++++++++++++++- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/BedrockService/BedrockService.Shared/Utilities/FileUtils.cs b/BedrockService/BedrockService.Shared/Utilities/FileUtils.cs index 6ef8cebb..de65d8d4 100644 --- a/BedrockService/BedrockService.Shared/Utilities/FileUtils.cs +++ b/BedrockService/BedrockService.Shared/Utilities/FileUtils.cs @@ -1,4 +1,6 @@ -using System.Collections.Generic; +using BedrockService.Shared.MincraftJson; +using Newtonsoft.Json.Linq; +using System.Collections.Generic; using System.IO; using System.Linq; @@ -9,7 +11,7 @@ public class FileUtils readonly string _servicePath; public FileUtils(string servicePath) { - this._servicePath = servicePath; + _servicePath = servicePath; } public void CopyFilesRecursively(DirectoryInfo source, DirectoryInfo target) { @@ -58,5 +60,45 @@ public void DeleteFilelist(string[] fileList, string serverPath) Directory.Delete(dir, true); } + public void WriteStringToFile(string path, string content) => File.WriteAllText(path, content); + + public void WriteStringArrayToFile(string path, string[] content) => File.WriteAllLines(path, content); + + public void UpdateJArrayFile(string path, JArray content, System.Type type) + { + try + { + string currentFileContents = null; + JArray jArray = new JArray(); + if (File.Exists(path)) + { + currentFileContents = File.ReadAllText(path); + jArray = JArray.Parse(currentFileContents); + } + if(type == typeof(WorldPacksJsonModel)) + { + foreach (JToken jToken in content) + { + bool doesContainToken = false; + foreach (JToken currentToken in jArray) + { + if (currentToken.ToObject().pack_id == jToken.ToObject().pack_id) + { + doesContainToken = true; + } + } + if (!doesContainToken) + { + jArray.Add(jToken); + } + } + } + File.WriteAllText(path, jArray.ToString()); + } + catch(System.Exception) + { + return; + } + } } } From c516ef54cfa96ab3d2b6b284dab73b000ed78579 Mon Sep 17 00:00:00 2001 From: crowbarmaster Date: Tue, 14 Dec 2021 10:14:31 -0500 Subject: [PATCH 24/62] Update GetTimes to utilize a Tuple, giving named parameters to provide details of the content. No longer using string[] GetRegistration over specific methods. --- BedrockService/BedrockService.Shared/Classes/Player.cs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/BedrockService/BedrockService.Shared/Classes/Player.cs b/BedrockService/BedrockService.Shared/Classes/Player.cs index 93b318b3..6860035c 100644 --- a/BedrockService/BedrockService.Shared/Classes/Player.cs +++ b/BedrockService/BedrockService.Shared/Classes/Player.cs @@ -86,14 +86,9 @@ public void UpdateRegistration(string whtlist, string perm, string ignoreLimit) IgnorePlayerLimits = bool.Parse(ignoreLimit); } - public string[] GetTimes() + public (string First, string Conn, string Disconn) GetTimes() { - return new string[] { FirstConnectedTime, LastConnectedTime, LastDisconnectTime }; - } - - public string[] GetRegistration() - { - return new string[] { Whitelisted.ToString(), PermissionLevel, IgnorePlayerLimits.ToString() }; + return ( FirstConnectedTime, LastConnectedTime, LastDisconnectTime ); } public bool IsDefaultRegistration() From d70dff35e5366ab929487b7f9f65e0986d91c838 Mon Sep 17 00:00:00 2001 From: crowbarmaster Date: Tue, 14 Dec 2021 10:17:15 -0500 Subject: [PATCH 25/62] Update interface to reflect changes with Player class. --- BedrockService/BedrockService.Shared/Interfaces/IPlayer.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/BedrockService/BedrockService.Shared/Interfaces/IPlayer.cs b/BedrockService/BedrockService.Shared/Interfaces/IPlayer.cs index f4a4876e..aa7c93ae 100644 --- a/BedrockService/BedrockService.Shared/Interfaces/IPlayer.cs +++ b/BedrockService/BedrockService.Shared/Interfaces/IPlayer.cs @@ -8,8 +8,7 @@ public interface IPlayer string SearchForProperty(string input); string GetUsername(); string GetXUID(); - string[] GetTimes(); - string[] GetRegistration(); + (string First, string Conn, string Disconn) GetTimes(); bool IsPlayerWhitelisted(); bool PlayerIgnoresLimit(); string GetPermissionLevel(); From 20860d1aa6cadf9250b1931988684d64725b9241 Mon Sep 17 00:00:00 2001 From: crowbarmaster Date: Tue, 14 Dec 2021 10:25:46 -0500 Subject: [PATCH 26/62] Json models for MC JSON files. --- .../MincraftJson/KnownPacksJsonModel.cs | 22 +++++++++++++++++++ .../MincraftJson/PermissionsJsonModel.cs | 19 ++++++++++++++++ .../MincraftJson/WhitelistJsonModel.cs | 20 +++++++++++++++++ .../MincraftJson/WorldPacksJsonModel.cs | 18 +++++++++++++++ 4 files changed, 79 insertions(+) create mode 100644 BedrockService/BedrockService.Shared/MincraftJson/KnownPacksJsonModel.cs create mode 100644 BedrockService/BedrockService.Shared/MincraftJson/PermissionsJsonModel.cs create mode 100644 BedrockService/BedrockService.Shared/MincraftJson/WhitelistJsonModel.cs create mode 100644 BedrockService/BedrockService.Shared/MincraftJson/WorldPacksJsonModel.cs diff --git a/BedrockService/BedrockService.Shared/MincraftJson/KnownPacksJsonModel.cs b/BedrockService/BedrockService.Shared/MincraftJson/KnownPacksJsonModel.cs new file mode 100644 index 00000000..74925003 --- /dev/null +++ b/BedrockService/BedrockService.Shared/MincraftJson/KnownPacksJsonModel.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BedrockService.Shared.MincraftJson +{ + internal class KnownPacksJsonModel + { + public int file_version { get; set; } + public string file_system { get; set; } + public bool? from_disk { get; set; } + public List hashes { get; set; } + public string path { get; set; } + public string uuid { get; set; } + public string version { get; set; } + + public override string ToString() + { + return path; + } + } +} diff --git a/BedrockService/BedrockService.Shared/MincraftJson/PermissionsJsonModel.cs b/BedrockService/BedrockService.Shared/MincraftJson/PermissionsJsonModel.cs new file mode 100644 index 00000000..9e16aaf8 --- /dev/null +++ b/BedrockService/BedrockService.Shared/MincraftJson/PermissionsJsonModel.cs @@ -0,0 +1,19 @@ +using System; +using BedrockService.Shared.Classes; +using System.Collections.Generic; +using System.Text; + +namespace BedrockService.Shared.MincraftJson +{ + internal class PermissionsJsonModel + { + public string permission { get; set; } + public string xuid { get; set; } + + public PermissionsJsonModel(string permission, string xuid) + { + this.permission = permission; + this.xuid = xuid; + } + } +} diff --git a/BedrockService/BedrockService.Shared/MincraftJson/WhitelistJsonModel.cs b/BedrockService/BedrockService.Shared/MincraftJson/WhitelistJsonModel.cs new file mode 100644 index 00000000..d0ad61b9 --- /dev/null +++ b/BedrockService/BedrockService.Shared/MincraftJson/WhitelistJsonModel.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BedrockService.Shared.MincraftJson +{ + internal class WhitelistJsonModel + { + public bool ignoresPlayerLimit { get; set; } + public string permission { get; set; } + public string xuid { get; set; } + + public WhitelistJsonModel (bool IgnoreLimits, string XUID, string Permission) + { + ignoresPlayerLimit = IgnoreLimits; + xuid = XUID; + permission = Permission; + } + } +} diff --git a/BedrockService/BedrockService.Shared/MincraftJson/WorldPacksJsonModel.cs b/BedrockService/BedrockService.Shared/MincraftJson/WorldPacksJsonModel.cs new file mode 100644 index 00000000..1b9e4976 --- /dev/null +++ b/BedrockService/BedrockService.Shared/MincraftJson/WorldPacksJsonModel.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace BedrockService.Shared.MincraftJson +{ + public class WorldPacksJsonModel + { + public string pack_id { get; set; } + public List version { get; set; } + + public WorldPacksJsonModel(string id, List ver) + { + pack_id = id; + version = ver; + } + } +} From e80730d69db8196a03fea25cb33c1c6e36733db0 Mon Sep 17 00:00:00 2001 From: crowbarmaster Date: Tue, 14 Dec 2021 10:27:51 -0500 Subject: [PATCH 27/62] Issue with .NET Core arose when serializing an object containing an instance of DirectoryInfo. Reworked this to send only the containers instead of the parser itself. --- .../PackParser/MinecraftPackParser.cs | 35 ++++++++++--------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/BedrockService/BedrockService.Shared/PackParser/MinecraftPackParser.cs b/BedrockService/BedrockService.Shared/PackParser/MinecraftPackParser.cs index 420bfe18..bb50152d 100644 --- a/BedrockService/BedrockService.Shared/PackParser/MinecraftPackParser.cs +++ b/BedrockService/BedrockService.Shared/PackParser/MinecraftPackParser.cs @@ -40,7 +40,7 @@ public class Manifest public class MinecraftPackContainer { public Manifest JsonManifest; - public DirectoryInfo PackContentLocation; + public string PackContentLocation; public string ManifestType; public string FolderName; public byte[] IconBytes; @@ -55,7 +55,7 @@ public class MinecraftPackParser { private readonly IProcessInfo _processInfo; private readonly IBedrockLogger _logger; - public DirectoryInfo PackExtractDirectory; + public string PackExtractDirectory; public List FoundPacks = new List(); [JsonConstructor] @@ -68,33 +68,33 @@ public MinecraftPackParser(IBedrockLogger logger, IProcessInfo processInfo) public MinecraftPackParser(byte[] fileContents, IBedrockLogger logger, IProcessInfo processInfo) { _logger = logger; - PackExtractDirectory = new DirectoryInfo($@"{processInfo.GetDirectory()}\Temp"); + PackExtractDirectory = $@"{processInfo.GetDirectory()}\Temp"; _processInfo = processInfo; new FileUtils(processInfo.GetDirectory()).ClearTempDir(); using (MemoryStream fileStream = new MemoryStream(fileContents, 5, fileContents.Length - 5)) { using (ZipArchive zipArchive = new ZipArchive(fileStream, ZipArchiveMode.Read)) { - zipArchive.ExtractToDirectory(PackExtractDirectory.FullName); + zipArchive.ExtractToDirectory(PackExtractDirectory); } } ParseDirectory(PackExtractDirectory); } - public MinecraftPackParser(string[] files, DirectoryInfo extractDir, IBedrockLogger logger, IProcessInfo processInfo) + public MinecraftPackParser(string[] files, string extractDir, IBedrockLogger logger, IProcessInfo processInfo) { PackExtractDirectory = extractDir; _logger = logger; _processInfo = processInfo; new FileUtils(_processInfo.GetDirectory()).ClearTempDir(); - if (Directory.Exists($@"{PackExtractDirectory.FullName}\ZipTemp")) + if (Directory.Exists($@"{PackExtractDirectory}\ZipTemp")) { - Directory.CreateDirectory($@"{PackExtractDirectory.FullName}\ZipTemp"); + Directory.CreateDirectory($@"{PackExtractDirectory}\ZipTemp"); } foreach (string file in files) { FileInfo fInfo = new FileInfo(file); - string zipFilePath = $@"{PackExtractDirectory.FullName}\{fInfo.Name.Replace(fInfo.Extension, "")}"; + string zipFilePath = $@"{PackExtractDirectory}\{fInfo.Name.Replace(fInfo.Extension, "")}"; ZipFile.ExtractToDirectory(file, zipFilePath); foreach (FileInfo extractedFile in new DirectoryInfo(zipFilePath).GetFiles()) { @@ -105,16 +105,17 @@ public MinecraftPackParser(string[] files, DirectoryInfo extractDir, IBedrockLog } } } - foreach (DirectoryInfo directory in PackExtractDirectory.GetDirectories()) - ParseDirectory(directory); + DirectoryInfo directoryInfo = new DirectoryInfo(PackExtractDirectory); + ParseDirectory(PackExtractDirectory); } - public void ParseDirectory(DirectoryInfo directoryToParse) + public void ParseDirectory(string directoryToParse) { - _logger.AppendLine($"Parsing directory {directoryToParse.Name}"); - if (directoryToParse.Exists) + DirectoryInfo directoryInfo = new DirectoryInfo(directoryToParse); + if (directoryInfo.Exists) { - foreach (FileInfo file in directoryToParse.GetFiles("*", SearchOption.AllDirectories)) + _logger.AppendLine($"Parsing directory {directoryInfo.Name}"); + foreach (FileInfo file in directoryInfo.GetFiles("*", SearchOption.AllDirectories)) { if (file.Name == "levelname.txt") { @@ -134,7 +135,7 @@ public void ParseDirectory(DirectoryInfo directoryToParse) FoundPacks.Add(new MinecraftPackContainer { JsonManifest = null, - PackContentLocation = file.Directory, + PackContentLocation = file.Directory.FullName, ManifestType = "WorldPack", FolderName = file.Directory.Name, IconBytes = iconBytes @@ -153,7 +154,7 @@ public void ParseDirectory(DirectoryInfo directoryToParse) MinecraftPackContainer container = new MinecraftPackContainer { JsonManifest = new Manifest(), - PackContentLocation = file.Directory, + PackContentLocation = file.Directory.FullName, ManifestType = "", FolderName = file.Directory.Name, IconBytes = iconBytes @@ -164,7 +165,7 @@ public void ParseDirectory(DirectoryInfo directoryToParse) }; container.JsonManifest = JsonConvert.DeserializeObject(File.ReadAllText(file.FullName), settings); container.ManifestType = container.JsonManifest.modules[0].type; - _logger.AppendLine($"{container.ManifestType} pack found, name: {container.JsonManifest.header.name}, path: {container.PackContentLocation.Name}"); + _logger.AppendLine($"{container.ManifestType} pack found, name: {container.JsonManifest.header.name}, path: {container.PackContentLocation}"); FoundPacks.Add(container); } } From e71bab89c6a23c6f53a6a0cdb258b664302ea8e5 Mon Sep 17 00:00:00 2001 From: crowbarmaster Date: Tue, 14 Dec 2021 10:34:11 -0500 Subject: [PATCH 28/62] Added method detailing steps to following a service restart. Moved Start command edit method's "byte[] serializeToBytes" field within the ShowDialog conditional statement. Array was filled with past info instead of newly aquired. Fixed a couple missing Wait()'s on a couple Delay tasks. --- BedrockService/Client/Forms/MainWindow.cs | 28 +++++++++++++++++------ 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/BedrockService/Client/Forms/MainWindow.cs b/BedrockService/Client/Forms/MainWindow.cs index b2b83c3f..27ef4bda 100644 --- a/BedrockService/Client/Forms/MainWindow.cs +++ b/BedrockService/Client/Forms/MainWindow.cs @@ -72,7 +72,7 @@ private void ConnectTimer_Elapsed(object sender, ElapsedEventArgs e) Connect.Enabled = true; ComponentEnableManager(); }); - return; + return; } } } @@ -312,7 +312,7 @@ private void EditGlobals_Click(object sender, EventArgs e) connectedHost.SetAllProps(_editDialog.workingProps); _editDialog.Close(); _editDialog.Dispose(); - RestartSrv_Click(null, null); + ServiceRestartFromClient(); } } @@ -322,6 +322,20 @@ private void GlobBackup_Click(object sender, EventArgs e) DisableUI(); } + private Task ServiceRestartFromClient() + { + return Task.Run(() => + { + Invoke(() => + { + Disconn_Click(null, null); + Connect.Enabled = false; + Task.Delay(6000).Wait(); + Connect_Click(null, null); + }); + }); + } + private void newSrvBtn_Click(object sender, EventArgs e) { if (clientSideServiceConfiguration == null) @@ -355,7 +369,7 @@ private void PlayerManager_Click(object sender, EventArgs e) WaitForServerData().Wait(); FormManager.TCPClient.PlayerInfoArrived = false; PlayerManagerForm form = new PlayerManagerForm(selectedServer); - if(form.ShowDialog() != DialogResult.OK) + if (form.ShowDialog() != DialogResult.OK) { ServerBusy = false; } @@ -370,6 +384,7 @@ private void Disconn_Click(object sender, EventArgs e) { if (FormManager.TCPClient.Connected) { + FormManager.TCPClient.SendData(NetworkMessageSource.Client, NetworkMessageDestination.Service, NetworkMessageTypes.Disconnect); Thread.Sleep(500); FormManager.TCPClient.CloseConnection(); } @@ -408,9 +423,9 @@ private void EditStCmd_Click(object sender, EventArgs e) { TypeNameHandling = TypeNameHandling.All }; - byte[] serializeToBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(editSrvDialog.startCmds, Formatting.Indented, settings)); if (editSrvDialog.ShowDialog() == DialogResult.OK) { + byte[] serializeToBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(editSrvDialog.startCmds, Formatting.Indented, settings)); DisableUI(); FormManager.TCPClient.SendData(serializeToBytes, NetworkMessageSource.Client, NetworkMessageDestination.Service, connectedHost.GetServerIndex(selectedServer), NetworkMessageTypes.StartCmdUpdate); selectedServer.SetStartCommands(editSrvDialog.startCmds); @@ -514,7 +529,7 @@ public Task DisableUI() Invoke((MethodInvoker)delegate { ComponentEnableManager(); }); while (ServerBusy) { - Task.Delay(250); + Task.Delay(250).Wait(); } Invoke((MethodInvoker)delegate { ComponentEnableManager(); }); }); @@ -526,7 +541,7 @@ public Task WaitForServerData() { while (!FormManager.TCPClient.EnumBackupsArrived && !FormManager.TCPClient.PlayerInfoArrived && FormManager.TCPClient.RecievedPacks == null) { - Task.Delay(250); + Task.Delay(250).Wait(); } }); } @@ -603,7 +618,6 @@ private void ManPacks_Click(object sender, EventArgs e) } form.Close(); } - FormManager.TCPClient.RecievedPacks = null; } private void nbtStudioBtn_Click(object sender, EventArgs e) From a7a10254353f99807fe1709f75d4e95f5343d611 Mon Sep 17 00:00:00 2001 From: crowbarmaster Date: Tue, 14 Dec 2021 10:39:32 -0500 Subject: [PATCH 29/62] Reworked to accept a list of containers instead of the parsers that contain a container. Remove packs by selection is now possible. Both sending packs as well as removing will trigger DialogResult.OK and close the window. --- .../Client/Forms/ManagePacksForms.Designer.cs | 68 ++++++++++++------- .../Client/Forms/ManagePacksForms.cs | 33 +++++---- .../Client/Forms/ManagePacksForms.resx | 62 +---------------- 3 files changed, 63 insertions(+), 100 deletions(-) diff --git a/BedrockService/Client/Forms/ManagePacksForms.Designer.cs b/BedrockService/Client/Forms/ManagePacksForms.Designer.cs index 3e09ec3a..e7a6ff77 100644 --- a/BedrockService/Client/Forms/ManagePacksForms.Designer.cs +++ b/BedrockService/Client/Forms/ManagePacksForms.Designer.cs @@ -49,9 +49,12 @@ private void InitializeComponent() this.serverListBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left))); this.serverListBox.FormattingEnabled = true; - this.serverListBox.Location = new System.Drawing.Point(12, 82); + this.serverListBox.ItemHeight = 15; + this.serverListBox.Location = new System.Drawing.Point(14, 95); + this.serverListBox.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.serverListBox.Name = "serverListBox"; - this.serverListBox.Size = new System.Drawing.Size(189, 251); + this.serverListBox.SelectionMode = System.Windows.Forms.SelectionMode.MultiSimple; + this.serverListBox.Size = new System.Drawing.Size(220, 289); this.serverListBox.TabIndex = 0; this.serverListBox.Click += new System.EventHandler(this.ListBox_SelectedIndexChanged); // @@ -60,19 +63,22 @@ private void InitializeComponent() this.parsedPacksListBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Right))); this.parsedPacksListBox.FormattingEnabled = true; - this.parsedPacksListBox.Location = new System.Drawing.Point(599, 82); + this.parsedPacksListBox.ItemHeight = 15; + this.parsedPacksListBox.Location = new System.Drawing.Point(699, 95); + this.parsedPacksListBox.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.parsedPacksListBox.Name = "parsedPacksListBox"; this.parsedPacksListBox.SelectionMode = System.Windows.Forms.SelectionMode.MultiSimple; - this.parsedPacksListBox.Size = new System.Drawing.Size(189, 251); + this.parsedPacksListBox.Size = new System.Drawing.Size(220, 289); this.parsedPacksListBox.TabIndex = 1; this.parsedPacksListBox.Click += new System.EventHandler(this.ListBox_SelectedIndexChanged); // // sendPacksBtn // this.sendPacksBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.sendPacksBtn.Location = new System.Drawing.Point(468, 82); + this.sendPacksBtn.Location = new System.Drawing.Point(546, 95); + this.sendPacksBtn.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.sendPacksBtn.Name = "sendPacksBtn"; - this.sendPacksBtn.Size = new System.Drawing.Size(125, 23); + this.sendPacksBtn.Size = new System.Drawing.Size(146, 27); this.sendPacksBtn.TabIndex = 2; this.sendPacksBtn.Text = "Send selected packs"; this.sendPacksBtn.UseVisualStyleBackColor = true; @@ -81,9 +87,10 @@ private void InitializeComponent() // sendAllBtn // this.sendAllBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.sendAllBtn.Location = new System.Drawing.Point(468, 111); + this.sendAllBtn.Location = new System.Drawing.Point(546, 128); + this.sendAllBtn.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.sendAllBtn.Name = "sendAllBtn"; - this.sendAllBtn.Size = new System.Drawing.Size(125, 23); + this.sendAllBtn.Size = new System.Drawing.Size(146, 27); this.sendAllBtn.TabIndex = 3; this.sendAllBtn.Text = "Send all packs"; this.sendAllBtn.UseVisualStyleBackColor = true; @@ -91,9 +98,10 @@ private void InitializeComponent() // // removePackBtn // - this.removePackBtn.Location = new System.Drawing.Point(207, 82); + this.removePackBtn.Location = new System.Drawing.Point(241, 95); + this.removePackBtn.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.removePackBtn.Name = "removePackBtn"; - this.removePackBtn.Size = new System.Drawing.Size(119, 23); + this.removePackBtn.Size = new System.Drawing.Size(139, 27); this.removePackBtn.TabIndex = 4; this.removePackBtn.Text = "Remove pack"; this.removePackBtn.UseVisualStyleBackColor = true; @@ -101,9 +109,10 @@ private void InitializeComponent() // // removeAllPacksBtn // - this.removeAllPacksBtn.Location = new System.Drawing.Point(207, 111); + this.removeAllPacksBtn.Location = new System.Drawing.Point(241, 128); + this.removeAllPacksBtn.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.removeAllPacksBtn.Name = "removeAllPacksBtn"; - this.removeAllPacksBtn.Size = new System.Drawing.Size(119, 23); + this.removeAllPacksBtn.Size = new System.Drawing.Size(139, 27); this.removeAllPacksBtn.TabIndex = 5; this.removeAllPacksBtn.Text = "Remove all packs"; this.removeAllPacksBtn.UseVisualStyleBackColor = true; @@ -113,19 +122,21 @@ private void InitializeComponent() // this.textBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); - this.textBox1.Location = new System.Drawing.Point(288, 211); + this.textBox1.Location = new System.Drawing.Point(336, 243); + this.textBox1.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.textBox1.Multiline = true; this.textBox1.Name = "textBox1"; this.textBox1.ReadOnly = true; - this.textBox1.Size = new System.Drawing.Size(219, 122); + this.textBox1.Size = new System.Drawing.Size(255, 140); this.textBox1.TabIndex = 6; // // selectedPackIcon // this.selectedPackIcon.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); - this.selectedPackIcon.Location = new System.Drawing.Point(332, 75); + this.selectedPackIcon.Location = new System.Drawing.Point(387, 87); + this.selectedPackIcon.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.selectedPackIcon.Name = "selectedPackIcon"; - this.selectedPackIcon.Size = new System.Drawing.Size(130, 130); + this.selectedPackIcon.Size = new System.Drawing.Size(152, 150); this.selectedPackIcon.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage; this.selectedPackIcon.TabIndex = 7; this.selectedPackIcon.TabStop = false; @@ -133,9 +144,10 @@ private void InitializeComponent() // openFileBtn // this.openFileBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.openFileBtn.Location = new System.Drawing.Point(599, 29); + this.openFileBtn.Location = new System.Drawing.Point(699, 33); + this.openFileBtn.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.openFileBtn.Name = "openFileBtn"; - this.openFileBtn.Size = new System.Drawing.Size(189, 23); + this.openFileBtn.Size = new System.Drawing.Size(220, 27); this.openFileBtn.TabIndex = 8; this.openFileBtn.Text = "Open pack file(s)"; this.openFileBtn.UseVisualStyleBackColor = true; @@ -144,9 +156,10 @@ private void InitializeComponent() // label1 // this.label1.AutoSize = true; - this.label1.Location = new System.Drawing.Point(12, 66); + this.label1.Location = new System.Drawing.Point(14, 76); + this.label1.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label1.Name = "label1"; - this.label1.Size = new System.Drawing.Size(153, 13); + this.label1.Size = new System.Drawing.Size(169, 15); this.label1.TabIndex = 9; this.label1.Text = "Current packs found on server:"; // @@ -154,9 +167,10 @@ private void InitializeComponent() // this.label2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.label2.AutoSize = true; - this.label2.Location = new System.Drawing.Point(596, 66); + this.label2.Location = new System.Drawing.Point(695, 76); + this.label2.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label2.Name = "label2"; - this.label2.Size = new System.Drawing.Size(146, 13); + this.label2.Size = new System.Drawing.Size(161, 15); this.label2.TabIndex = 10; this.label2.Text = "Packs found in archive file(s):"; // @@ -165,18 +179,19 @@ private void InitializeComponent() this.label3.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.label3.AutoSize = true; - this.label3.Location = new System.Drawing.Point(204, 9); + this.label3.Location = new System.Drawing.Point(238, 10); + this.label3.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label3.Name = "label3"; - this.label3.Size = new System.Drawing.Size(418, 13); + this.label3.Size = new System.Drawing.Size(454, 15); this.label3.TabIndex = 11; this.label3.Text = "!! NOTICE !! Send map pack alone, do not send all! Not all packs will parse serve" + "r-side!"; // // ManagePacksForms // - this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(800, 450); + this.ClientSize = new System.Drawing.Size(933, 519); this.Controls.Add(this.label3); this.Controls.Add(this.label2); this.Controls.Add(this.label1); @@ -189,6 +204,7 @@ private void InitializeComponent() this.Controls.Add(this.sendPacksBtn); this.Controls.Add(this.parsedPacksListBox); this.Controls.Add(this.serverListBox); + this.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.Name = "ManagePacksForms"; this.Text = "Form1"; ((System.ComponentModel.ISupportInitialize)(this.selectedPackIcon)).EndInit(); diff --git a/BedrockService/Client/Forms/ManagePacksForms.cs b/BedrockService/Client/Forms/ManagePacksForms.cs index 1345208d..ae0bd4a6 100644 --- a/BedrockService/Client/Forms/ManagePacksForms.cs +++ b/BedrockService/Client/Forms/ManagePacksForms.cs @@ -9,6 +9,7 @@ using System.IO; using System.IO.Compression; using System.Text; +using System.Threading.Tasks; using System.Windows.Forms; namespace BedrockService.Client.Forms @@ -28,11 +29,11 @@ public ManagePacksForms(byte serverIndex, IBedrockLogger logger, IProcessInfo pr InitializeComponent(); } - public void PopulateServerPacks(List packList) + public void PopulateServerPacks(List packList) { - foreach (MinecraftPackParser pack in packList) - foreach (MinecraftPackContainer container in pack.FoundPacks) - serverListBox.Items.Add(container); + foreach (MinecraftPackContainer container in packList) + serverListBox.Items.Add(container); + FormManager.TCPClient.RecievedPacks = null; } private void ListBox_SelectedIndexChanged(object sender, EventArgs e) @@ -53,7 +54,7 @@ private void ListBox_SelectedIndexChanged(object sender, EventArgs e) if (selectedPack.JsonManifest != null) textBox1.Text = $"{selectedPack.JsonManifest.header.name}\r\n{selectedPack.JsonManifest.header.description}\r\n{selectedPack.JsonManifest.header.uuid}\r\n{selectedPack.JsonManifest.header.version[0]}"; else - textBox1.Text = selectedPack.PackContentLocation.Name; + textBox1.Text = new DirectoryInfo(selectedPack.PackContentLocation).Name; } } @@ -62,15 +63,20 @@ private void removePackBtn_Click(object sender, EventArgs e) if (serverListBox.SelectedIndex != -1) { List temp = new List(); - temp.Add((MinecraftPackContainer)serverListBox.SelectedItem); + object[] items = new object[serverListBox.SelectedItems.Count]; + serverListBox.SelectedItems.CopyTo(items, 0); + foreach(object item in items) + { + temp.Add((MinecraftPackContainer)item); + serverListBox.Items.Remove(item); + } JsonSerializerSettings settings = new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All }; FormManager.TCPClient.SendData(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(temp, Formatting.Indented, settings)), NetworkMessageSource.Client, NetworkMessageDestination.Server, ServerIndex, NetworkMessageTypes.RemovePack); - serverListBox.Items.Remove(temp[0]); } - + DialogResult = DialogResult.OK; } private void removeAllPacksBtn_Click(object sender, EventArgs e) @@ -83,7 +89,7 @@ private void removeAllPacksBtn_Click(object sender, EventArgs e) TypeNameHandling = TypeNameHandling.All }; FormManager.TCPClient.SendData(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(temp, Formatting.Indented, settings)), NetworkMessageSource.Client, NetworkMessageDestination.Server, ServerIndex, NetworkMessageTypes.RemovePack); - serverListBox.Items.Clear(); + DialogResult = DialogResult.OK; } private void sendPacksBtn_Click(object sender, EventArgs e) @@ -112,7 +118,7 @@ private void openFileBtn_Click(object sender, EventArgs e) openFileDialog.Multiselect = true; if (openFileDialog.ShowDialog() == DialogResult.OK) { - MinecraftPackParser parser = new MinecraftPackParser(openFileDialog.FileNames, PackExtractDir, Logger, ProcessInfo); + MinecraftPackParser parser = new MinecraftPackParser(openFileDialog.FileNames, PackExtractDir.FullName, Logger, ProcessInfo); parsedPacksListBox.Items.Clear(); foreach (MinecraftPackContainer container in parser.FoundPacks) parsedPacksListBox.Items.Add(container); @@ -124,12 +130,13 @@ private void SendPacks(object[] packList) foreach (MinecraftPackContainer container in packList) { Directory.CreateDirectory($@"{PackExtractDir.FullName}\ZipTemp"); - container.PackContentLocation.MoveTo($@"{PackExtractDir.FullName}\ZipTemp\{container.PackContentLocation.Name}"); - container.PackContentLocation = new DirectoryInfo($@"{PackExtractDir.FullName}\ZipTemp\{container.PackContentLocation.Name}"); + DirectoryInfo directoryInfo = new DirectoryInfo(container.PackContentLocation); + directoryInfo.MoveTo($@"{PackExtractDir.FullName}\ZipTemp\{directoryInfo.Name}"); + container.PackContentLocation = $@"{PackExtractDir.FullName}\ZipTemp\{directoryInfo.Name}"; } ZipFile.CreateFromDirectory($@"{PackExtractDir.FullName}\ZipTemp", $@"{PackExtractDir.FullName}\SendZip.zip"); FormManager.TCPClient.SendData(File.ReadAllBytes($@"{PackExtractDir.FullName}\SendZip.zip"), NetworkMessageSource.Client, NetworkMessageDestination.Server, ServerIndex, NetworkMessageTypes.PackFile); - parsedPacksListBox.Items.Clear(); + DialogResult = DialogResult.OK; } } } diff --git a/BedrockService/Client/Forms/ManagePacksForms.resx b/BedrockService/Client/Forms/ManagePacksForms.resx index 1af7de15..f298a7be 100644 --- a/BedrockService/Client/Forms/ManagePacksForms.resx +++ b/BedrockService/Client/Forms/ManagePacksForms.resx @@ -1,64 +1,4 @@ - - - + From 2974e35d20e3bd1d68cfcf0c835d6b555d6b949e Mon Sep 17 00:00:00 2001 From: crowbarmaster Date: Tue, 14 Dec 2021 10:40:38 -0500 Subject: [PATCH 30/62] Rework GetTime calls to use new Tuple format. --- .../Client/Forms/PlayerManagerForm.cs | 27 +++++++------------ 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/BedrockService/Client/Forms/PlayerManagerForm.cs b/BedrockService/Client/Forms/PlayerManagerForm.cs index 1e66e2f4..a4097ee0 100644 --- a/BedrockService/Client/Forms/PlayerManagerForm.cs +++ b/BedrockService/Client/Forms/PlayerManagerForm.cs @@ -44,16 +44,10 @@ private void RefreshGridContents() gridView.Rows.Clear(); foreach (IPlayer player in playersFound) { - string[] playerReg = player.GetRegistration(); - string[] playerTimes = player.GetTimes(); - string playerFirstConnect = playerTimes[0]; - string playerConnectTime = playerTimes[1]; - string playerDisconnectTime = playerTimes[2]; - string playerWhitelist = playerReg[0]; + var playerTimes = player.GetTimes(); string playerPermission = player.GetPermissionLevel(); - string playerIgnoreLimit = playerReg[2]; - TimeSpan timeSpent = TimeSpan.FromTicks(long.Parse(playerConnectTime) - long.Parse(playerDisconnectTime)); - string[] list = new string[] { player.GetXUID(), player.GetUsername(), playerPermission, playerWhitelist, playerIgnoreLimit, playerFirstConnect, playerConnectTime, timeSpent.ToString("hhmmss") }; + TimeSpan timeSpent = TimeSpan.FromTicks(long.Parse(playerTimes.Conn) - long.Parse(playerTimes.Disconn)); + string[] list = new string[] { player.GetXUID(), player.GetUsername(), playerPermission, player.IsPlayerWhitelisted().ToString(), player.PlayerIgnoresLimit().ToString(), playerTimes.First, playerTimes.Conn, timeSpent.ToString("hhmmss") }; gridView.Rows.Add(list); } gridView.Refresh(); @@ -139,14 +133,13 @@ private void gridView_CellBeginEdit(object sender, DataGridViewCellCancelEventAr private void gridView_CellEndEdit(object sender, DataGridViewCellEventArgs e) { DataGridViewRow focusedRow = gridView.Rows[e.RowIndex]; - string[] playerReg = playerToEdit.GetRegistration(); - string[] playerTimes = playerToEdit.GetTimes(); - string playerFirstConnect = playerTimes[0]; - string playerConnectTime = playerTimes[1]; - string playerDisconnectTime = playerTimes[2]; - string playerWhitelist = playerReg[0]; - string playerPermission = playerReg[1]; - string playerIgnoreLimit = playerReg[2]; + var playerTimes = playerToEdit.GetTimes(); + string playerFirstConnect = playerTimes.First; + string playerConnectTime = playerTimes.Conn; + string playerDisconnectTime = playerTimes.Disconn; + string playerWhitelist = playerToEdit.IsPlayerWhitelisted().ToString(); + string playerPermission = playerToEdit.GetPermissionLevel(); + string playerIgnoreLimit = playerToEdit.PlayerIgnoresLimit().ToString(); if ((string)focusedRow.Cells[0].Value != playerToEdit.GetXUID() || (string)focusedRow.Cells[1].Value != playerToEdit.GetUsername() || (string)focusedRow.Cells[2].Value != playerPermission || (string)focusedRow.Cells[3].Value != playerWhitelist || (string)focusedRow.Cells[4].Value != playerIgnoreLimit) { playerToEdit = new Player((string)focusedRow.Cells[0].Value, (string)focusedRow.Cells[1].Value, playerFirstConnect, playerConnectTime, playerDisconnectTime, bool.Parse((string)focusedRow.Cells[3].Value), (string)focusedRow.Cells[2].Value, bool.Parse((string)focusedRow.Cells[4].Value)); From e02d44c315732c5e10267f08faf87473f827408f Mon Sep 17 00:00:00 2001 From: crowbarmaster Date: Tue, 14 Dec 2021 10:41:11 -0500 Subject: [PATCH 31/62] Allow user to delete entire rows if we are editing start commands. --- .../Client/Forms/PropEditorForm.Designer.cs | 35 ++++++---- BedrockService/Client/Forms/PropEditorForm.cs | 1 + .../Client/Forms/PropEditorForm.resx | 68 +------------------ 3 files changed, 23 insertions(+), 81 deletions(-) diff --git a/BedrockService/Client/Forms/PropEditorForm.Designer.cs b/BedrockService/Client/Forms/PropEditorForm.Designer.cs index c27c8bd2..4430f6cb 100644 --- a/BedrockService/Client/Forms/PropEditorForm.Designer.cs +++ b/BedrockService/Client/Forms/PropEditorForm.Designer.cs @@ -41,9 +41,10 @@ private void InitializeComponent() // CancelBtn // this.CancelBtn.Anchor = System.Windows.Forms.AnchorStyles.Bottom; - this.CancelBtn.Location = new System.Drawing.Point(13, 362); + this.CancelBtn.Location = new System.Drawing.Point(27, 628); + this.CancelBtn.Margin = new System.Windows.Forms.Padding(6, 5, 6, 5); this.CancelBtn.Name = "CancelBtn"; - this.CancelBtn.Size = new System.Drawing.Size(200, 23); + this.CancelBtn.Size = new System.Drawing.Size(333, 45); this.CancelBtn.TabIndex = 48; this.CancelBtn.Text = "Cancel"; this.CancelBtn.UseVisualStyleBackColor = true; @@ -52,9 +53,10 @@ private void InitializeComponent() // SaveBtn // this.SaveBtn.Anchor = System.Windows.Forms.AnchorStyles.Bottom; - this.SaveBtn.Location = new System.Drawing.Point(588, 362); + this.SaveBtn.Location = new System.Drawing.Point(936, 628); + this.SaveBtn.Margin = new System.Windows.Forms.Padding(6, 5, 6, 5); this.SaveBtn.Name = "SaveBtn"; - this.SaveBtn.Size = new System.Drawing.Size(200, 23); + this.SaveBtn.Size = new System.Drawing.Size(333, 45); this.SaveBtn.TabIndex = 49; this.SaveBtn.Text = "Save"; this.SaveBtn.UseVisualStyleBackColor = true; @@ -70,10 +72,12 @@ private void InitializeComponent() this.gridView.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] { this.EntryKey, this.EntryData}); - this.gridView.Location = new System.Drawing.Point(13, 13); + this.gridView.Location = new System.Drawing.Point(21, 25); + this.gridView.Margin = new System.Windows.Forms.Padding(6, 5, 6, 5); this.gridView.MultiSelect = false; this.gridView.Name = "gridView"; - this.gridView.Size = new System.Drawing.Size(775, 343); + this.gridView.RowHeadersWidth = 62; + this.gridView.Size = new System.Drawing.Size(1248, 560); this.gridView.TabIndex = 1; this.gridView.NewRowNeeded += new System.Windows.Forms.DataGridViewRowEventHandler(this.gridView_NewRowNeeded); // @@ -88,32 +92,35 @@ private void InitializeComponent() // EntryData // this.EntryData.HeaderText = "Value:"; + this.EntryData.MinimumWidth = 8; this.EntryData.Name = "EntryData"; - this.EntryData.Width = 600; + this.EntryData.Width = 500; // // DelBackupBtn // this.DelBackupBtn.Anchor = System.Windows.Forms.AnchorStyles.Bottom; this.DelBackupBtn.Enabled = false; - this.DelBackupBtn.Location = new System.Drawing.Point(301, 362); + this.DelBackupBtn.Location = new System.Drawing.Point(488, 628); + this.DelBackupBtn.Margin = new System.Windows.Forms.Padding(6, 5, 6, 5); this.DelBackupBtn.Name = "DelBackupBtn"; - this.DelBackupBtn.Size = new System.Drawing.Size(200, 23); + this.DelBackupBtn.Size = new System.Drawing.Size(333, 45); this.DelBackupBtn.TabIndex = 50; this.DelBackupBtn.Text = "Delete Backup"; this.DelBackupBtn.UseVisualStyleBackColor = true; this.DelBackupBtn.Visible = false; this.DelBackupBtn.Click += new System.EventHandler(this.DelBackupBtn_Click); // - // EditSrv + // PropEditorForm // - this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleDimensions = new System.Drawing.SizeF(10F, 25F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(800, 397); + this.ClientSize = new System.Drawing.Size(1344, 766); this.Controls.Add(this.DelBackupBtn); this.Controls.Add(this.gridView); this.Controls.Add(this.SaveBtn); this.Controls.Add(this.CancelBtn); - this.Name = "EditSrv"; + this.Margin = new System.Windows.Forms.Padding(6, 5, 6, 5); + this.Name = "PropEditorForm"; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; this.Text = "Form1"; ((System.ComponentModel.ISupportInitialize)(this.gridView)).EndInit(); @@ -125,8 +132,8 @@ private void InitializeComponent() private System.Windows.Forms.Button CancelBtn; private System.Windows.Forms.Button SaveBtn; private System.Windows.Forms.DataGridView gridView; + private System.Windows.Forms.Button DelBackupBtn; private System.Windows.Forms.DataGridViewTextBoxColumn EntryKey; private System.Windows.Forms.DataGridViewTextBoxColumn EntryData; - private System.Windows.Forms.Button DelBackupBtn; } } \ No newline at end of file diff --git a/BedrockService/Client/Forms/PropEditorForm.cs b/BedrockService/Client/Forms/PropEditorForm.cs index 2fc849e8..a1e7c739 100644 --- a/BedrockService/Client/Forms/PropEditorForm.cs +++ b/BedrockService/Client/Forms/PropEditorForm.cs @@ -42,6 +42,7 @@ public void PopulateStartCmds(List list) { startCmds = list; gridView.Columns.RemoveAt(0); + gridView.AllowUserToDeleteRows = true; foreach (StartCmdEntry entry in startCmds) { dataGrid.Rows.Add(new string[1] { entry.Command }); diff --git a/BedrockService/Client/Forms/PropEditorForm.resx b/BedrockService/Client/Forms/PropEditorForm.resx index 6f35f1cd..b1ad47f2 100644 --- a/BedrockService/Client/Forms/PropEditorForm.resx +++ b/BedrockService/Client/Forms/PropEditorForm.resx @@ -1,64 +1,4 @@ - - - + @@ -123,10 +63,4 @@ True - - True - - - True - \ No newline at end of file From df3e8c8461b2ff3b2ad56cee80da2f58fac5cde4 Mon Sep 17 00:00:00 2001 From: crowbarmaster Date: Tue, 14 Dec 2021 10:43:42 -0500 Subject: [PATCH 32/62] Move sending heartbeat data to listener loop. Removed now unused heartbeat variables and method. Fix PackList case in listener loop to reflect changes with Pack parsing. Check for network stream issues, and disconnect after failing twice. --- BedrockService/Client/Networking/TCPClient.cs | 38 ++++--------------- 1 file changed, 7 insertions(+), 31 deletions(-) diff --git a/BedrockService/Client/Networking/TCPClient.cs b/BedrockService/Client/Networking/TCPClient.cs index 223c3227..a1b15eeb 100644 --- a/BedrockService/Client/Networking/TCPClient.cs +++ b/BedrockService/Client/Networking/TCPClient.cs @@ -25,13 +25,11 @@ public class TCPClient public bool PlayerInfoArrived; public bool EnumBackupsArrived; public List BackupList; - public List RecievedPacks; + public List RecievedPacks; public Task ClientReciever; - public Task HeartbeatTask; private CancellationTokenSource? _netCancelSource; private int _heartbeatFailTimeout; private const int _heartbeatFailTimeoutLimit = 2; - private bool _heartbeatRecieved; private readonly IBedrockLogger _logger; public TCPClient(IBedrockLogger logger) @@ -97,6 +95,7 @@ public void ReceiveListener() { while (!_netCancelSource.IsCancellationRequested) { + SendData(NetworkMessageSource.Client, NetworkMessageDestination.Service, NetworkMessageTypes.Heartbeat); try { byte[] buffer = new byte[4]; @@ -134,17 +133,12 @@ public void ReceiveListener() FormManager.MainWindow.connectedHost = JsonConvert.DeserializeObject(data, settings); Connected = true; FormManager.MainWindow.RefreshServerContents(); - _heartbeatFailTimeout = 0; - HeartbeatTask = Task.Factory.StartNew(new Action(SendHeatbeatSignal), _netCancelSource.Token); } catch (Exception e) { _logger.AppendLine($"Error: ConnectMan reported error: {e.Message}\n{e.StackTrace}"); } break; - case NetworkMessageTypes.Heartbeat: - _heartbeatRecieved = true; - break; case NetworkMessageTypes.EnumBackups: @@ -207,10 +201,10 @@ public void ReceiveListener() break; case NetworkMessageTypes.PackList: - List temp = new List(); + List temp = new List(); JArray jArray = JArray.Parse(data); foreach (JToken token in jArray) - temp.Add(token.ToObject()); + temp.Add(token.ToObject()); RecievedPacks = temp; break; @@ -243,24 +237,6 @@ public void ReceiveListener() } } - public void SendHeatbeatSignal() - { - _logger.AppendLine("HeartbeatThread started."); - while (!_netCancelSource.Token.IsCancellationRequested) - { - Thread.Sleep(3000); - SendData(NetworkMessageSource.Client, NetworkMessageDestination.Service, NetworkMessageTypes.Heartbeat); - while (!_heartbeatRecieved && EstablishedLink) - { - Thread.Sleep(100); - } - _logger.AppendLine("ThumpThump"); - _heartbeatRecieved = false; - _heartbeatFailTimeout = 0; - } - _logger.AppendLine("HeartbeatThread exited."); - } - public bool SendData(byte[] bytes, NetworkMessageSource source, NetworkMessageDestination destination, byte serverIndex, NetworkMessageTypes type, NetworkMessageFlags status) { byte[] compiled = new byte[9 + bytes.Length]; @@ -288,8 +264,9 @@ public bool SendData(byte[] bytes, NetworkMessageSource source, NetworkMessageDe _heartbeatFailTimeout++; if (_heartbeatFailTimeout > _heartbeatFailTimeoutLimit) { - FormManager.MainWindow.HeartbeatFailDisconnect(); + Task.Run(() => { FormManager.MainWindow.HeartbeatFailDisconnect(); }); _netCancelSource.Cancel(); + EstablishedLink = false; _heartbeatFailTimeout = 0; } return false; @@ -312,13 +289,12 @@ public bool SendData(byte[] bytes, NetworkMessageSource source, NetworkMessageDe public void Dispose() { - if(_netCancelSource != null) + if (_netCancelSource != null) { _netCancelSource.Cancel(); _netCancelSource.Dispose(); _netCancelSource = null; } - HeartbeatTask = null; ClientReciever = null; if (OpenedTcpClient != null) { From 25585ad05ff1feb0177e83816b663924436bca3e Mon Sep 17 00:00:00 2001 From: crowbarmaster Date: Tue, 14 Dec 2021 10:45:18 -0500 Subject: [PATCH 33/62] Removing task classes as Tasks are better managed in code than the Threads that these used to me. --- .../Service/Core/Interfaces/IServiceTask.cs | 8 -------- .../Service/Core/Tasks/ClientServiceTask.cs | 18 ------------------ .../Service/Core/Tasks/HeartbeatTask.cs | 18 ------------------ .../Service/Core/Tasks/ServerProcessTask.cs | 14 -------------- .../Service/Core/Tasks/TCPListenerTask.cs | 18 ------------------ .../Service/Core/Tasks/WatchdogTask.cs | 14 -------------- 6 files changed, 90 deletions(-) delete mode 100644 BedrockService/Service/Core/Interfaces/IServiceTask.cs delete mode 100644 BedrockService/Service/Core/Tasks/ClientServiceTask.cs delete mode 100644 BedrockService/Service/Core/Tasks/HeartbeatTask.cs delete mode 100644 BedrockService/Service/Core/Tasks/ServerProcessTask.cs delete mode 100644 BedrockService/Service/Core/Tasks/TCPListenerTask.cs delete mode 100644 BedrockService/Service/Core/Tasks/WatchdogTask.cs diff --git a/BedrockService/Service/Core/Interfaces/IServiceTask.cs b/BedrockService/Service/Core/Interfaces/IServiceTask.cs deleted file mode 100644 index 1df73910..00000000 --- a/BedrockService/Service/Core/Interfaces/IServiceTask.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace BedrockService.Service.Core.Interfaces -{ - public interface IServiceTask - { - bool IsAlive(); - void CancelTask(); - } -} diff --git a/BedrockService/Service/Core/Tasks/ClientServiceTask.cs b/BedrockService/Service/Core/Tasks/ClientServiceTask.cs deleted file mode 100644 index 384bd49e..00000000 --- a/BedrockService/Service/Core/Tasks/ClientServiceTask.cs +++ /dev/null @@ -1,18 +0,0 @@ -using BedrockService.Service.Core.Interfaces; -using System.Threading; - -namespace BedrockService.Service.Core.Tasks -{ - class ClientServiceTask : IServiceTask - { - private Task _thread; - private CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource(); - public ClientServiceTask(Action method, CancellationTokenSource source) - { - _cancellationTokenSource = source; - _thread = Task.Factory.StartNew((CancellationToken) => method(_cancellationTokenSource.Token), _cancellationTokenSource.Token); - } - public void CancelTask() => _cancellationTokenSource.Cancel(); - public bool IsAlive() => _thread != null && _thread.Status == TaskStatus.Running && !_cancellationTokenSource.IsCancellationRequested; - } -} \ No newline at end of file diff --git a/BedrockService/Service/Core/Tasks/HeartbeatTask.cs b/BedrockService/Service/Core/Tasks/HeartbeatTask.cs deleted file mode 100644 index e56d8691..00000000 --- a/BedrockService/Service/Core/Tasks/HeartbeatTask.cs +++ /dev/null @@ -1,18 +0,0 @@ -using BedrockService.Service.Core.Interfaces; -using System.Threading; - -namespace BedrockService.Service.Core.Tasks -{ - class HeartbeatTask : IServiceTask - { - private Task _thread; - private CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource(); - public HeartbeatTask(Action method, CancellationTokenSource source) - { - _cancellationTokenSource = source; - _thread = Task.Factory.StartNew((CancellationToken) => method(_cancellationTokenSource.Token), _cancellationTokenSource.Token); - } - public void CancelTask() => _cancellationTokenSource.Cancel(); - public bool IsAlive() => _thread != null && _thread.Status == TaskStatus.Running && !_cancellationTokenSource.IsCancellationRequested; - } -} \ No newline at end of file diff --git a/BedrockService/Service/Core/Tasks/ServerProcessTask.cs b/BedrockService/Service/Core/Tasks/ServerProcessTask.cs deleted file mode 100644 index 371f0b7b..00000000 --- a/BedrockService/Service/Core/Tasks/ServerProcessTask.cs +++ /dev/null @@ -1,14 +0,0 @@ -using BedrockService.Service.Core.Interfaces; -using System.Threading; - -namespace BedrockService.Service.Core.Tasks -{ - class ServerProcessTask : IServiceTask - { - private Task _thread; - private CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource(); - public ServerProcessTask(Action method) => _thread = Task.Factory.StartNew((CancellationToken) => method(_cancellationTokenSource.Token), _cancellationTokenSource.Token); - public void CancelTask() => _cancellationTokenSource.Cancel(); - public bool IsAlive() => _thread != null && _thread.Status == TaskStatus.Running && !_cancellationTokenSource.IsCancellationRequested; - } -} \ No newline at end of file diff --git a/BedrockService/Service/Core/Tasks/TCPListenerTask.cs b/BedrockService/Service/Core/Tasks/TCPListenerTask.cs deleted file mode 100644 index 8efa5bce..00000000 --- a/BedrockService/Service/Core/Tasks/TCPListenerTask.cs +++ /dev/null @@ -1,18 +0,0 @@ -using BedrockService.Service.Core.Interfaces; -using System.Threading; - -namespace BedrockService.Service.Core.Tasks -{ - class TCPListenerTask : IServiceTask - { - private Task _thread; - private CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource(); - public TCPListenerTask(Action method, CancellationTokenSource source) - { - _cancellationTokenSource = source; - _thread = Task.Factory.StartNew((CancellationToken) => method(_cancellationTokenSource.Token), _cancellationTokenSource.Token); - } - public void CancelTask() => _cancellationTokenSource.Cancel(); - public bool IsAlive() => _thread != null && _thread.Status == TaskStatus.Running && !_cancellationTokenSource.IsCancellationRequested; - } -} \ No newline at end of file diff --git a/BedrockService/Service/Core/Tasks/WatchdogTask.cs b/BedrockService/Service/Core/Tasks/WatchdogTask.cs deleted file mode 100644 index 1b370288..00000000 --- a/BedrockService/Service/Core/Tasks/WatchdogTask.cs +++ /dev/null @@ -1,14 +0,0 @@ -using BedrockService.Service.Core.Interfaces; -using System.Threading; - -namespace BedrockService.Service.Core.Tasks -{ - class WatchdogTask : IServiceTask - { - private Task _thread; - private CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource(); - public WatchdogTask(Action method) => _thread = Task.Factory.StartNew((CancellationToken) => method(_cancellationTokenSource.Token), _cancellationTokenSource.Token); - public void CancelTask() => _cancellationTokenSource.Cancel(); - public bool IsAlive() => _thread != null && _thread.Status == TaskStatus.Running && !_cancellationTokenSource.IsCancellationRequested; - } -} \ No newline at end of file From da1807f91b13882d4cc3d89cefef9d6b2b4504d4 Mon Sep 17 00:00:00 2001 From: crowbarmaster Date: Tue, 14 Dec 2021 10:46:14 -0500 Subject: [PATCH 34/62] Remove calls to stop watchdog here. This is unwanted with rework to Task-based threading. --- BedrockService/Service/Core/BedrockService.cs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/BedrockService/Service/Core/BedrockService.cs b/BedrockService/Service/Core/BedrockService.cs index 587f9f00..6686defd 100644 --- a/BedrockService/Service/Core/BedrockService.cs +++ b/BedrockService/Service/Core/BedrockService.cs @@ -97,10 +97,6 @@ public void RestartService() while (brs.GetServerStatus() == BedrockServer.ServerStatus.Stopping && !Program.IsExiting) Thread.Sleep(100); } - foreach (IBedrockServer brs in _bedrockServers) - { - brs.StopWatchdog(); - } try { _tCPListener.ResetListener(); From a52e6178a48c9dd2540eca183e88a19d74d006cc Mon Sep 17 00:00:00 2001 From: crowbarmaster Date: Tue, 14 Dec 2021 10:48:41 -0500 Subject: [PATCH 35/62] Register appLifetime OnStarted event to start .NET Core service properly. Include configuration to TopShelf to acknowledge and automatically close after install and uninstall of service. --- BedrockService/Service/Core/Service.cs | 31 ++++++++++++++++++-------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/BedrockService/Service/Core/Service.cs b/BedrockService/Service/Core/Service.cs index 1741d31f..3d1fd704 100644 --- a/BedrockService/Service/Core/Service.cs +++ b/BedrockService/Service/Core/Service.cs @@ -10,11 +10,14 @@ public class Service : IService private readonly IBedrockService _bedrockService; private Topshelf.Host _host; private readonly IBedrockLogger _logger; + IHostApplicationLifetime _applicationLifetime; - public Service(IBedrockLogger logger, IBedrockService bedrockService, NetworkStrategyLookup lookup) + public Service(IBedrockLogger logger, IBedrockService bedrockService, NetworkStrategyLookup lookup, IHostApplicationLifetime appLifetime) { _logger = logger; _bedrockService = bedrockService; + _applicationLifetime = appLifetime; + appLifetime.ApplicationStarted.Register(OnStarted); } public async Task InitializeHost() @@ -40,20 +43,29 @@ await Task.Run(() => } }); }); - hostConfig.RunAsLocalSystem(); hostConfig.SetDescription("Windows Service Wrapper for Windows Bedrock Server"); hostConfig.SetDisplayName("BedrockService"); hostConfig.SetServiceName("BedrockService"); hostConfig.UnhandledExceptionPolicy = UnhandledExceptionPolicyCode.LogErrorOnly; - + hostConfig.AfterInstall(() => + { + _logger.AppendLine("Service install completed... Exiting!"); + Task.Delay(1000).Wait(); + _applicationLifetime.StopApplication(); + }); + hostConfig.AfterUninstall(() => + { + _logger.AppendLine("Service uninstall completed... Exiting!"); + Task.Delay(1000).Wait(); + _applicationLifetime.StopApplication(); + }); hostConfig.EnableServiceRecovery(src => { src.RestartService(delayInMinutes: 0); src.RestartService(delayInMinutes: 1); src.SetResetPeriod(days: 1); }); - hostConfig.OnException((ex) => { _logger.AppendLine("Exception occured Main : " + ex.Message); @@ -65,16 +77,17 @@ await Task.Run(() => public Task StartAsync(CancellationToken cancellationToken) { - return Task.Run(() => - { - InitializeHost().Wait(); - _host.Run(); - }); + return InitializeHost(); } public Task StopAsync(CancellationToken cancellationToken) { return Task.Delay(100); } + + private void OnStarted() + { + Task.Run(() => { _host.Run(); }); + } } } From 69f6ef21fd66888f61ac4a58c65b3213eea0e321 Mon Sep 17 00:00:00 2001 From: crowbarmaster Date: Tue, 14 Dec 2021 10:49:35 -0500 Subject: [PATCH 36/62] Updated to use new GetTimes() Tuple format. --- .../Service/Management/ConfigManager.cs | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/BedrockService/Service/Management/ConfigManager.cs b/BedrockService/Service/Management/ConfigManager.cs index 99cf6f25..53d8ce4a 100644 --- a/BedrockService/Service/Management/ConfigManager.cs +++ b/BedrockService/Service/Management/ConfigManager.cs @@ -147,8 +147,8 @@ public void LoadRegisteredPlayers(IServerConfiguration server) server.AddUpdatePlayer(new Player(split[0], split[1], DateTime.Now.Ticks.ToString(), "0", "0", split[3].ToLower() == "true", split[2], split[4].ToLower() == "true")); continue; } - string[] playerTimes = playerFound.GetTimes(); - server.AddUpdatePlayer(new Player(split[0], split[1], playerTimes[0], playerTimes[1], playerTimes[2], split[3].ToLower() == "true", split[2], split[4].ToLower() == "true")); + var playerTimes = playerFound.GetTimes(); + server.AddUpdatePlayer(new Player(split[0], split[1], playerTimes.First, playerTimes.Conn, playerTimes.Disconn, split[3].ToLower() == "true", split[2], split[4].ToLower() == "true")); } } @@ -239,8 +239,8 @@ public void LoadPlayerDatabase(IServerConfiguration server) server.AddUpdatePlayer(new Player(split[0], split[1], split[2], split[3], split[4], false, server.GetProp("default-player-permission-level").ToString(), false)); continue; } - string[] playerTimes = playerFound.GetTimes(); - server.AddUpdatePlayer(new Player(split[0], split[1], playerTimes[0], playerTimes[1], playerTimes[2], playerFound.IsPlayerWhitelisted(), playerFound.GetPermissionLevel(), playerFound.PlayerIgnoresLimit())); + var playerTimes = playerFound.GetTimes(); + server.AddUpdatePlayer(new Player(split[0], split[1], playerTimes.First, playerTimes.Conn, playerTimes.Disconn, playerFound.IsPlayerWhitelisted(), playerFound.GetPermissionLevel(), playerFound.PlayerIgnoresLimit())); } } @@ -290,11 +290,10 @@ public void WriteJSONFiles(IServerConfiguration server) foreach (IPlayer player in server.GetPlayerList()) { - string[] playerReg = player.GetRegistration(); - if (!player.IsDefaultRegistration() && playerReg[0] == "True") + if (!player.IsDefaultRegistration() && player.IsPlayerWhitelisted()) { sb.Append("\t{\n"); - sb.Append($"\t\t\"ignoresPlayerLimit\": {playerReg[2].ToLower()},\n"); + sb.Append($"\t\t\"ignoresPlayerLimit\": {player.PlayerIgnoresLimit().ToString().ToLower()},\n"); sb.Append($"\t\t\"name\": \"{player.GetUsername()}\",\n"); sb.Append($"\t\t\"xuid\": \"{player.GetXUID()}\"\n"); sb.Append("\t},\n"); @@ -311,11 +310,10 @@ public void WriteJSONFiles(IServerConfiguration server) foreach (Player player in server.GetPlayerList()) { - string[] playerReg = player.GetRegistration(); if (!player.IsDefaultRegistration()) { sb.Append("\t{\n"); - sb.Append($"\t\t\"permission\": \"{playerReg[1]}\",\n"); + sb.Append($"\t\t\"permission\": \"{player.GetPermissionLevel()}\",\n"); sb.Append($"\t\t\"xuid\": \"{player.GetXUID()}\"\n"); sb.Append("\t},\n"); } From f26124cd880f4386c5d87b776869a81b3d3a9ed0 Mon Sep 17 00:00:00 2001 From: crowbarmaster Date: Tue, 14 Dec 2021 10:51:57 -0500 Subject: [PATCH 37/62] Heavy fixes with connect/disconnection system. Heartbeat methods and variables removed. Heartbeat data sent within listener loop. Watch network stream for errors and disconnect on second fail. --- .../Service/Networking/ITCPListener.cs | 3 +- .../Service/Networking/TCPListener.cs | 277 +++++++++--------- 2 files changed, 138 insertions(+), 142 deletions(-) diff --git a/BedrockService/Service/Networking/ITCPListener.cs b/BedrockService/Service/Networking/ITCPListener.cs index b8d67958..818659cb 100644 --- a/BedrockService/Service/Networking/ITCPListener.cs +++ b/BedrockService/Service/Networking/ITCPListener.cs @@ -1,11 +1,10 @@ using BedrockService.Service.Networking.MessageInterfaces; -using System.Threading; namespace BedrockService.Service.Networking { public interface ITCPListener : IMessageSender { - void StartListening(CancellationToken token); + Task StartListening(); void ResetListener(); diff --git a/BedrockService/Service/Networking/TCPListener.cs b/BedrockService/Service/Networking/TCPListener.cs index 40de7fc5..de0f8908 100644 --- a/BedrockService/Service/Networking/TCPListener.cs +++ b/BedrockService/Service/Networking/TCPListener.cs @@ -1,10 +1,7 @@ -using BedrockService.Service.Core.Interfaces; -using BedrockService.Service.Core.Tasks; -using BedrockService.Service.Networking.MessageInterfaces; +using BedrockService.Service.Networking.MessageInterfaces; using System.Net; using System.Net.Sockets; using System.Security.Cryptography; -using System.Threading; namespace BedrockService.Service.Networking { @@ -15,31 +12,29 @@ public class TCPListener : ITCPListener, IMessageSender private NetworkStream _stream; private readonly IServiceConfiguration _serviceConfiguration; private readonly IBedrockLogger _logger; - private IServiceTask _tcpTask; - private IServiceTask _clientListenerTask; - private IServiceTask _heartbeatTask; private int _heartbeatFailTimeout; private readonly int _heartbeatFailTimeoutLimit = 2; private Dictionary _standardMessageLookup; private Dictionary _flaggedMessageLookup; private readonly IPAddress _ipAddress = IPAddress.Parse("0.0.0.0"); - private readonly System.Timers.Timer _reconnectTimer = new System.Timers.Timer(500.0); private CommsKeyContainer _keyContainer; private CancellationTokenSource _cancelTokenSource = new CancellationTokenSource(); + private Task _tcpTask; + private Task _recieverTask; public TCPListener(IServiceConfiguration serviceConfiguration, IBedrockLogger logger) { _logger = logger; _serviceConfiguration = serviceConfiguration; - _reconnectTimer.Elapsed += ReconnectTimer_Elapsed; - _tcpTask = new TCPListenerTask(new Action(StartListening), _cancelTokenSource); _cancelTokenSource = new CancellationTokenSource(); + InitializeTasks(); } - private void ReconnectTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) + private void InitializeTasks() { - _reconnectTimer.Stop(); - _tcpTask = new TCPListenerTask(new Action(StartListening), _cancelTokenSource); + _tcpTask = StartListening(); + _recieverTask = IncomingListener(); + _tcpTask.Start(); } public void SetStrategyDictionaries(Dictionary standard, Dictionary flagged) @@ -48,58 +43,77 @@ public void SetStrategyDictionaries(Dictionary { + _logger.AppendLine("TCP listener task started."); + _inListener = new TcpListener(_ipAddress, int.Parse(_serviceConfiguration.GetProp("ClientPort").ToString())); try { - if (_inListener.Pending()) - { - _cancelTokenSource = new CancellationTokenSource(); - _client = _inListener.AcceptTcpClient(); - _stream = _client.GetStream(); - _clientListenerTask = new ClientServiceTask(new Action(IncomingListener), _cancelTokenSource); - _heartbeatTask = new HeartbeatTask(new Action(HeartbeatSender), _cancelTokenSource); - } + + while (_standardMessageLookup == null) { Task.Delay(100).Wait(); } + _inListener.Start(); } - catch (ThreadStateException) { } - catch (NullReferenceException) { } - catch (InvalidOperationException) { } - catch (SocketException) { } - catch (Exception e) + catch (SocketException e) { - _logger.AppendLine(e.ToString()); + _logger.AppendLine($"Error! {e.Message}"); + Thread.Sleep(2000); + Environment.Exit(1); } - } + while (true) + { + try + { + if (_inListener.Pending()) + { + _cancelTokenSource = new CancellationTokenSource(); + _client = _inListener.AcceptTcpClient(); + _stream = _client.GetStream(); + _recieverTask.Start(); + } + if (_cancelTokenSource.IsCancellationRequested) + { + _logger.AppendLine("TCP Listener task canceled!"); + _inListener.Stop(); + _inListener = null; + return; + } + Task.Delay(500).Wait(); + } + catch (NullReferenceException) { } + catch (InvalidOperationException) + { + _inListener = null; + return; + } + catch (SocketException) { } + catch (Exception e) + { + _logger.AppendLine(e.ToString()); + } + } + }, _cancelTokenSource.Token); } public void ResetListener() { - _logger.AppendLine("Resetting listener!"); - _cancelTokenSource.Cancel(); - _client.Client.Blocking = false; - _stream.Close(); - _stream.Dispose(); - _client.Close(); - _inListener.Stop(); - _tcpTask = null; - _tcpTask = new TCPListenerTask(new Action(StartListening), _cancelTokenSource); - _clientListenerTask = null; - _heartbeatTask = null; - + Task.Run(() => + { + _logger.AppendLine("Resetting listener!"); + _client.Client.Blocking = false; + _stream.Close(); + _stream.Dispose(); + _client.Close(); + _inListener?.Stop(); + _cancelTokenSource?.Cancel(); + while (_tcpTask.Status == TaskStatus.Running || _recieverTask.Status == TaskStatus.Running) + { + Task.Delay(100).Wait(); + } + _cancelTokenSource = new CancellationTokenSource(); + InitializeTasks(); + }); } public void SendData(byte[] bytesToSend, NetworkMessageSource source, NetworkMessageDestination destination, byte serverIndex, NetworkMessageTypes type, NetworkMessageFlags status) @@ -114,7 +128,7 @@ public void SendData(byte[] bytesToSend, NetworkMessageSource source, NetworkMes byteHeader[8] = (byte)status; Buffer.BlockCopy(bytesToSend, 0, byteHeader, 9, bytesToSend.Length); - if (_clientListenerTask != null && _clientListenerTask.IsAlive()) + if (_tcpTask?.Status == TaskStatus.Running && _recieverTask?.Status == TaskStatus.Running) { try { @@ -145,107 +159,90 @@ public void SendData(byte[] bytesToSend, NetworkMessageSource source, NetworkMes public void SendData(NetworkMessageSource source, NetworkMessageDestination destination, NetworkMessageTypes type, NetworkMessageFlags status) => SendData(new byte[0], source, destination, 0xFF, type, status); - private void IncomingListener(CancellationToken token) + private Task IncomingListener() { - _logger.AppendLine("Packet listener thread started."); - int AvailBytes = 0; - int byteCount = 0; - NetworkMessageSource msgSource = 0; - NetworkMessageDestination msgDest = 0; - byte serverIndex = 0xFF; - NetworkMessageTypes msgType = 0; - NetworkMessageFlags msgFlag = 0; - while (!token.IsCancellationRequested) + return new Task(() => { - try + _logger.AppendLine("TCP Client packet listener started."); + int AvailBytes = 0; + int byteCount = 0; + NetworkMessageSource msgSource = 0; + NetworkMessageDestination msgDest = 0; + byte serverIndex = 0xFF; + NetworkMessageTypes msgType = 0; + NetworkMessageFlags msgFlag = 0; + while (true) { - byte[] buffer = new byte[4]; - while (_client.Client != null && _client.Client.Available != 0) // Recieve data from client. + SendData(NetworkMessageSource.Service, NetworkMessageDestination.Client, NetworkMessageTypes.Heartbeat); + if (_cancelTokenSource.IsCancellationRequested) { - byteCount = _stream.Read(buffer, 0, 4); - int expectedLen = BitConverter.ToInt32(buffer, 0); - buffer = new byte[expectedLen]; - byteCount = _stream.Read(buffer, 0, expectedLen); - msgSource = (NetworkMessageSource)buffer[0]; - msgDest = (NetworkMessageDestination)buffer[1]; - serverIndex = buffer[2]; - msgType = (NetworkMessageTypes)buffer[3]; - msgFlag = (NetworkMessageFlags)buffer[4]; - if (msgType == NetworkMessageTypes.Disconnect) - { - ResetListener(); - } - if (msgType < NetworkMessageTypes.Heartbeat) + _logger.AppendLine("TCP Client packet listener canceled!"); + return; + } + try + { + byte[] buffer = new byte[4]; + while (_client.Client != null && _client.Client.Available != 0) // Recieve data from client. { - try + byteCount = _stream.Read(buffer, 0, 4); + int expectedLen = BitConverter.ToInt32(buffer, 0); + buffer = new byte[expectedLen]; + byteCount = _stream.Read(buffer, 0, expectedLen); + msgSource = (NetworkMessageSource)buffer[0]; + msgDest = (NetworkMessageDestination)buffer[1]; + serverIndex = buffer[2]; + msgType = (NetworkMessageTypes)buffer[3]; + msgFlag = (NetworkMessageFlags)buffer[4]; + if (msgType == NetworkMessageTypes.Disconnect) { - if (_standardMessageLookup.ContainsKey(msgType)) - _standardMessageLookup[msgType].ParseMessage(buffer, serverIndex); - else - _flaggedMessageLookup[msgType].ParseMessage(buffer, serverIndex, msgFlag); + ResetListener(); } - catch + if (msgType < NetworkMessageTypes.Heartbeat) { - if(msgType == NetworkMessageTypes.Connect) - ResetListener(); + try + { + if (_standardMessageLookup.ContainsKey(msgType)) + _standardMessageLookup[msgType].ParseMessage(buffer, serverIndex); + else + _flaggedMessageLookup[msgType].ParseMessage(buffer, serverIndex, msgFlag); + } + catch + { + if (msgType == NetworkMessageTypes.Connect) + Task.Delay(1000).Wait(); + ResetListener(); + } } } + Task.Delay(500).Wait(); } - Thread.Sleep(200); - } - catch (OutOfMemoryException) - { - _logger.AppendLine("Out of memory exception thrown."); - } - catch (ObjectDisposedException) - { - _logger.AppendLine("Client was disposed! Killing thread..."); - break; - } - catch (InvalidOperationException e) - { - if (msgType != NetworkMessageTypes.ConsoleLogUpdate) + catch (OutOfMemoryException) { - _logger.AppendLine(e.Message); - _logger.AppendLine(e.StackTrace); + _logger.AppendLine("Out of memory exception thrown."); } - } - catch (ThreadAbortException) - { - _logger.AppendLine("ListenerThread aborted!"); - } - catch (Exception e) - { - _logger.AppendLine($"Error: {e.Message} {e.StackTrace}"); - } - try - { - AvailBytes = _client.Client != null ? _client.Client.Available:0; - } - catch { } - } - } - - private void HeartbeatSender(CancellationToken token) - { - _logger.AppendLine("HeartBeatSender started."); - while (!token.IsCancellationRequested) - { - - SendData(NetworkMessageSource.Service, NetworkMessageDestination.Client, NetworkMessageTypes.Heartbeat); - int timeWaited = 0; - while (timeWaited < 3000) - { - Thread.Sleep(100); - timeWaited += 100; - if (token.IsCancellationRequested) + catch (ObjectDisposedException) { - _logger.AppendLine("HeartBeatSender exited."); - return; + _logger.AppendLine("Client was disposed!"); } + catch (InvalidOperationException e) + { + if (msgType != NetworkMessageTypes.ConsoleLogUpdate) + { + _logger.AppendLine(e.Message); + _logger.AppendLine(e.StackTrace); + } + } + catch (Exception e) + { + _logger.AppendLine($"Error: {e.Message} {e.StackTrace}"); + } + try + { + AvailBytes = _client.Client != null ? _client.Client.Available : 0; + } + catch { } } - } - _logger.AppendLine("HeartBeatSender exited."); + }, _cancelTokenSource.Token); } public void SetKeyContainer(CommsKeyContainer keyContainer) From 907e3d1b405af7bc5c0d8105c4b8817b4fd2a80c Mon Sep 17 00:00:00 2001 From: crowbarmaster Date: Tue, 14 Dec 2021 10:53:32 -0500 Subject: [PATCH 38/62] Fix pack parser to use container. Write JSON files to enable new pack files within server world. --- .../Networking/NetworkStrategyLookup.cs | 54 ++++-- BedrockService/Service/Program.cs | 7 +- .../Service/Server/BedrockServer.cs | 176 +++++++++--------- .../Service/Server/IBedrockServer.cs | 1 - .../Server/Management/PlayerManager.cs | 6 +- BedrockService/Service/Startup.cs | 71 ------- .../ServiceTests/ConfiguratorTests.cs | 22 +++ BedrockService/ServiceTests/ServiceTests.cs | 21 +++ .../ServiceTests/ServiceTests.csproj | 4 + BedrockService/ServiceTests/UnitTest1.cs | 13 -- 10 files changed, 180 insertions(+), 195 deletions(-) delete mode 100644 BedrockService/Service/Startup.cs create mode 100644 BedrockService/ServiceTests/ConfiguratorTests.cs create mode 100644 BedrockService/ServiceTests/ServiceTests.cs delete mode 100644 BedrockService/ServiceTests/UnitTest1.cs diff --git a/BedrockService/Service/Networking/NetworkStrategyLookup.cs b/BedrockService/Service/Networking/NetworkStrategyLookup.cs index 17a94f7b..c3bff5da 100644 --- a/BedrockService/Service/Networking/NetworkStrategyLookup.cs +++ b/BedrockService/Service/Networking/NetworkStrategyLookup.cs @@ -1,6 +1,7 @@ using BedrockService.Service.Core.Interfaces; using BedrockService.Service.Networking.MessageInterfaces; using BedrockService.Service.Server; +using BedrockService.Shared.MincraftJson; using BedrockService.Shared.PackParser; using BedrockService.Shared.Utilities; using Newtonsoft.Json; @@ -27,8 +28,8 @@ public NetworkStrategyLookup(ITCPListener messageSender, IBedrockService service {NetworkMessageTypes.Restart, new ServerRestart(messageSender, service) }, {NetworkMessageTypes.Command, new ServerCommand(messageSender, service, logger) }, {NetworkMessageTypes.PackList, new PackList(messageSender, processInfo, serviceConfiguration, logger) }, - {NetworkMessageTypes.RemovePack, new RemovePack(messageSender, processInfo, serviceConfiguration) }, - {NetworkMessageTypes.PackFile, new PackFile(serviceConfiguration, processInfo, logger) }, + {NetworkMessageTypes.RemovePack, new RemovePack(messageSender, processInfo, serviceConfiguration, logger) }, + {NetworkMessageTypes.PackFile, new PackFile(messageSender, serviceConfiguration, processInfo, logger) }, {NetworkMessageTypes.Connect, new Connect(messageSender, serviceConfiguration) }, {NetworkMessageTypes.StartCmdUpdate, new StartCmdUpdate(configurator, serviceConfiguration) }, {NetworkMessageTypes.CheckUpdates, new CheckUpdates(messageSender, updater) }, @@ -229,12 +230,12 @@ public void ParseMessage(byte[] data, byte serverIndex) if (!File.Exists($@"{_processInfo.GetDirectory()}\Server\stock_packs.json")) File.Copy($@"{_serviceConfiguration.GetServerInfoByIndex(serverIndex).GetProp("ServerPath")}\valid_known_packs.json", $@"{_processInfo.GetDirectory()}\Server\stock_packs.json"); MinecraftKnownPacksClass knownPacks = new MinecraftKnownPacksClass($@"{_serviceConfiguration.GetServerInfoByIndex(serverIndex).GetProp("ServerPath")}\valid_known_packs.json", $@"{_processInfo.GetDirectory()}\Server\stock_packs.json"); - List list = new List(); + List list = new List(); foreach (MinecraftKnownPacksClass.KnownPack pack in knownPacks.KnownPacks) { MinecraftPackParser currentParser = new MinecraftPackParser(_logger, _processInfo); - currentParser.ParseDirectory(new DirectoryInfo($@"{_serviceConfiguration.GetServerInfoByIndex(serverIndex).GetProp("ServerPath")}\{pack.path.Replace(@"/", @"\")}")); - list.Add(currentParser); + currentParser.ParseDirectory($@"{_serviceConfiguration.GetServerInfoByIndex(serverIndex).GetProp("ServerPath")}\{pack.path.Replace(@"/", @"\")}"); + list.AddRange(currentParser.FoundPacks); } string arrayString = JArray.FromObject(list).ToString(); _messageSender.SendData(Encoding.UTF8.GetBytes(arrayString), NetworkMessageSource.Server, NetworkMessageDestination.Client, NetworkMessageTypes.PackList); @@ -246,12 +247,14 @@ class RemovePack : IMessageParser private readonly IMessageSender _messageSender; private readonly IServiceConfiguration _serviceConfiguration; private readonly IProcessInfo _processInfo; + private readonly IBedrockLogger _logger; - public RemovePack(IMessageSender messageSender, IProcessInfo processInfo, IServiceConfiguration serviceConfiguration) + public RemovePack(IMessageSender messageSender, IProcessInfo processInfo, IServiceConfiguration serviceConfiguration, IBedrockLogger logger) { _messageSender = messageSender; _serviceConfiguration = serviceConfiguration; _processInfo = processInfo; + _logger = logger; } public void ParseMessage(byte[] data, byte serverIndex) @@ -262,9 +265,9 @@ public void ParseMessage(byte[] data, byte serverIndex) { TypeNameHandling = TypeNameHandling.All }; - List MCContainer = JsonConvert.DeserializeObject>(stringData, settings); - foreach (MinecraftPackContainer cont in MCContainer) - knownPacks.RemovePackFromServer(_serviceConfiguration.GetServerInfoByIndex(serverIndex).GetProp("ServerPath").ToString(), cont); + List container = JsonConvert.DeserializeObject>(stringData, settings); + foreach (MinecraftPackContainer content in container) + knownPacks.RemovePackFromServer(_serviceConfiguration.GetServerInfoByIndex(serverIndex).GetProp("ServerPath").ToString(), content); _messageSender.SendData(NetworkMessageSource.Service, NetworkMessageDestination.Client, NetworkMessageTypes.UICallback); } } @@ -328,18 +331,19 @@ public void ParseMessage(byte[] data, byte serverIndex) _service.GetBedrockServerByIndex(serverIndex).WriteToStandardIn("ops reload"); _service.GetBedrockServerByIndex(serverIndex).WriteToStandardIn("whitelist reload"); _messageSender.SendData(NetworkMessageSource.Service, NetworkMessageDestination.Client, NetworkMessageTypes.UICallback); - } } class PackFile : IMessageParser { + private readonly IMessageSender _messageSender; private readonly IServiceConfiguration _serviceConfiguration; private readonly IProcessInfo _serviceProcessInfo; private readonly IBedrockLogger _logger; - public PackFile(IServiceConfiguration serviceConfiguration, IProcessInfo serviceProcessInfo, IBedrockLogger logger) + public PackFile(IMessageSender messageSender, IServiceConfiguration serviceConfiguration, IProcessInfo serviceProcessInfo, IBedrockLogger logger) { + _messageSender = messageSender; _logger = logger; _serviceProcessInfo = serviceProcessInfo; _serviceConfiguration = serviceConfiguration; @@ -351,12 +355,31 @@ public void ParseMessage(byte[] data, byte serverIndex) foreach (MinecraftPackContainer container in archiveParser.FoundPacks) { string serverPath = _serviceConfiguration.GetServerInfoByIndex(serverIndex).GetProp("ServerPath").ToString(); + string levelName = _serviceConfiguration.GetServerInfoByIndex(serverIndex).GetProp("level-name").ToString(); + string filePath = null; + FileUtils fileUtils = new FileUtils(_serviceProcessInfo.GetDirectory()); if (container.ManifestType == "WorldPack") - new FileUtils(_serviceProcessInfo.GetDirectory()).CopyFilesRecursively(container.PackContentLocation, new DirectoryInfo($@"{serverPath}\worlds\{container.FolderName}")); + { + fileUtils.CopyFilesRecursively(new DirectoryInfo(container.PackContentLocation), new DirectoryInfo($@"{serverPath}\worlds\{container.FolderName}")); + } if (container.ManifestType == "data") - new FileUtils(_serviceProcessInfo.GetDirectory()).CopyFilesRecursively(container.PackContentLocation, new DirectoryInfo($@"{serverPath}\behavior_packs\{container.FolderName}")); + { + fileUtils.CopyFilesRecursively(new DirectoryInfo(container.PackContentLocation), new DirectoryInfo($@"{serverPath}\behavior_packs\{container.FolderName}")); + filePath = $@"{serverPath}\worlds\{levelName}\world_behavior_packs.json"; + } if (container.ManifestType == "resources") - new FileUtils(_serviceProcessInfo.GetDirectory()).CopyFilesRecursively(container.PackContentLocation, new DirectoryInfo($@"{serverPath}\resource_packs\{container.FolderName}")); + { + fileUtils.CopyFilesRecursively(new DirectoryInfo(container.PackContentLocation), new DirectoryInfo($@"{serverPath}\resource_packs\{container.FolderName}")); + filePath = $@"{serverPath}\worlds\{levelName}\world_resource_packs.json"; + } + if(filePath != null) + { + WorldPacksJsonModel worldPackManifentClass = new WorldPacksJsonModel(container.JsonManifest.header.uuid, container.JsonManifest.header.version); + List list = new List(); + list.Add(worldPackManifentClass); + fileUtils.UpdateJArrayFile(filePath, JArray.FromObject(list), typeof(WorldPacksJsonModel)); + _messageSender.SendData(NetworkMessageSource.Service, NetworkMessageDestination.Client, NetworkMessageTypes.UICallback); + } } } } @@ -425,7 +448,8 @@ public void ParseMessage(byte[] data, byte serverIndex) { TypeNameHandling = TypeNameHandling.All }; - _serviceConfiguration.GetServerInfoByIndex(serverIndex).SetStartCommands(JsonConvert.DeserializeObject>(Encoding.UTF8.GetString(data, 5, data.Length - 5), settings)); + List entries = JsonConvert.DeserializeObject>(Encoding.UTF8.GetString(data, 5, data.Length - 5), settings); + _serviceConfiguration.GetServerInfoByIndex(serverIndex).SetStartCommands(entries); _configurator.SaveServerProps(_serviceConfiguration.GetServerInfoByIndex(serverIndex), true); } } diff --git a/BedrockService/Service/Program.cs b/BedrockService/Service/Program.cs index cf7438e9..2cbb5fd2 100644 --- a/BedrockService/Service/Program.cs +++ b/BedrockService/Service/Program.cs @@ -3,7 +3,6 @@ // This file may need updated according to the specific scenario of the application being upgraded. // For more information on ASP.NET Core hosting, see https://docs.microsoft.com/aspnet/core/fundamentals/host/web-host -global using BedrockService.Service.Core; global using BedrockService.Service.Logging; global using BedrockService.Service.Management; global using BedrockService.Service.Networking; @@ -19,6 +18,7 @@ global using System.IO; global using System.Linq; global using System.Reflection; +global using System.Threading; global using System.Threading.Tasks; global using Topshelf; using BedrockService.Service.Core.Interfaces; @@ -28,12 +28,15 @@ namespace BedrockService.Service public class Program { public static bool IsExiting = false; - private static bool _isDebugEnabled; + private static bool _isDebugEnabled = false; private static bool _isConsoleMode = false; + private static CancellationTokenSource CancellationTokenSource = new CancellationTokenSource(); + private static CancellationToken token = CancellationTokenSource.Token; public static void Main(string[] args) { if (args.Length > 0) { + Console.WriteLine(string.Join(" ", args)); _isDebugEnabled = args[0].ToLower() == "-debug"; } if (args.Length == 0 || Environment.UserInteractive) diff --git a/BedrockService/Service/Server/BedrockServer.cs b/BedrockService/Service/Server/BedrockServer.cs index 4882e957..01a777ac 100644 --- a/BedrockService/Service/Server/BedrockServer.cs +++ b/BedrockService/Service/Server/BedrockServer.cs @@ -1,6 +1,4 @@ -using BedrockService.Service.Core.Interfaces; -using BedrockService.Service.Core.Tasks; -using BedrockService.Service.Server.Management; +using BedrockService.Service.Server.Management; using BedrockService.Shared.Utilities; using System.Text.RegularExpressions; using System.Threading; @@ -9,8 +7,10 @@ namespace BedrockService.Service.Server { public class BedrockServer : IBedrockServer { - private IServiceTask _serverThread; - private IServiceTask _watchdogThread; + private Task _serverTask; + private Task _watchdogTask; + private CancellationTokenSource _serverCanceler = new CancellationTokenSource(); + private CancellationTokenSource _watchdogCanceler = new CancellationTokenSource(); private StreamWriter _stdInStream; private Process _serverProcess; private HostControl _hostController; @@ -49,11 +49,14 @@ public void WriteToStandardIn(string command) public void StartControl() { - _serverThread = new ServerProcessTask(new Action(RunServer)); + _serverCanceler = new CancellationTokenSource(); + _serverTask = RunServer(); + _serverTask.Start(); } public void StopControl() { + _serverCanceler.Cancel(); if (_serverProcess != null) { _logger.AppendLine("Sending Stop to Bedrock. Process.HasExited = " + _serverProcess.HasExited.ToString()); @@ -63,18 +66,16 @@ public void StopControl() _stdInStream.WriteLine("stop"); while (!_serverProcess.HasExited) { } } - _serverThread.CancelTask(); _serverProcess = null; _currentServerStatus = ServerStatus.Stopped; } public void StartWatchdog(HostControl hostControl) { + _watchdogCanceler = new CancellationTokenSource(); _hostController = hostControl; - if (_watchdogThread == null) - { - _watchdogThread = new WatchdogTask(new Action(ApplicationWatchdogMonitor)); - } + _watchdogTask = ApplicationWatchdogMonitor(); + _watchdogTask.Start(); } private bool Backup() @@ -146,69 +147,63 @@ private bool Backup() } } - public void StopWatchdog() - { - if (_watchdogThread != null) - _watchdogThread.CancelTask(); - _watchdogThread = null; - } - public ServerStatus GetServerStatus() => _currentServerStatus; public void SetServerStatus(ServerStatus newStatus) => _currentServerStatus = newStatus; - private void ApplicationWatchdogMonitor(CancellationToken token) + private Task ApplicationWatchdogMonitor() { - while (_watchdogThread.IsAlive()) + return new Task(() => { - string exeName = _serverConfiguration.GetProp("ServerExeName").ToString(); - string appName = exeName.Substring(0, exeName.Length - 4); - if (!MonitoredAppExists(appName) && _currentServerStatus == ServerStatus.Starting) + while (true) { - StartControl(); - _logger.AppendLine($"Recieved start signal for server {_serverConfiguration.GetServerName()}."); - Thread.Sleep(15000); - } - while (MonitoredAppExists(appName) && _currentServerStatus == ServerStatus.Started) - { - Thread.Sleep(5000); - } - if (MonitoredAppExists(appName) && _currentServerStatus == ServerStatus.Stopping) - { - _logger.AppendLine($"BedrockService signaled stop to application {appName}."); - _logger.AppendLine("Stopping..."); - StopControl(); - while (_currentServerStatus == ServerStatus.Stopping) + if (_watchdogCanceler.IsCancellationRequested) { - Thread.Sleep(250); + _logger.AppendLine("WatchDog Task was canceled. Stopping server!"); + StopControl(); + return; } + string exeName = _serverConfiguration.GetProp("ServerExeName").ToString(); + string appName = exeName.Substring(0, exeName.Length - 4); + if (!MonitoredAppExists(appName) && _currentServerStatus == ServerStatus.Starting) + { + StartControl(); + _logger.AppendLine($"Recieved start signal for server {_serverConfiguration.GetServerName()}."); + Thread.Sleep(15000); + } + if (MonitoredAppExists(appName) && _currentServerStatus == ServerStatus.Stopping) + { + _logger.AppendLine($"BedrockService signaled stop to application {appName}."); + _logger.AppendLine("Stopping..."); + StopControl(); + while (_currentServerStatus == ServerStatus.Stopping) + { + Thread.Sleep(250); + } + } + if (!MonitoredAppExists(appName) && _currentServerStatus == ServerStatus.Started) + { + StopControl(); + _logger.AppendLine($"Started application {appName} was not found in running processes... Resarting {appName}."); + StartControl(); + Thread.Sleep(1500); + } + if (!MonitoredAppExists(appName) && _currentServerStatus == ServerStatus.Stopped) + { + _logger.AppendLine("Server stopped successfully."); + } + if (!MonitoredAppExists(appName) && _currentServerStatus == ServerStatus.Stopping) + { + _logger.AppendLine("Server stopped unexpectedly. Setting server status to stopped."); + _currentServerStatus = ServerStatus.Stopped; + } + if (!MonitoredAppExists(appName) && _currentServerStatus == ServerStatus.Stopped && Program.IsExiting) + { + return; + } + Task.Delay(3000).Wait(); } - if (!MonitoredAppExists(appName) && _currentServerStatus == ServerStatus.Started) - { - StopControl(); - _logger.AppendLine($"Started application {appName} was not found in running processes... Resarting {appName}."); - StartControl(); - Thread.Sleep(1500); - } - if (!MonitoredAppExists(appName) && _currentServerStatus == ServerStatus.Stopped) - { - _logger.AppendLine("Server stopped successfully."); - } - if (!MonitoredAppExists(appName) && _currentServerStatus == ServerStatus.Stopping) - { - _logger.AppendLine("Server stopped unexpectedly. Setting server status to stopped."); - _currentServerStatus = ServerStatus.Stopped; - } - while (!MonitoredAppExists(appName) && _currentServerStatus == ServerStatus.Stopped && !Program.IsExiting) - { - Thread.Sleep(1000); - } - if (!MonitoredAppExists(appName) && _currentServerStatus == ServerStatus.Stopped && Program.IsExiting) - { - return; - } - - } + }, _watchdogCanceler.Token); } public bool RestartServer(bool ShouldPerformBackup) @@ -232,42 +227,43 @@ public bool RestartServer(bool ShouldPerformBackup) public string GetServerName() => _serverConfiguration.GetServerName(); - private void RunServer(CancellationToken token) + private Task RunServer() { - string exeName = _serverConfiguration.GetProp("ServerExeName").ToString(); - string appName = exeName.Substring(0, exeName.Length - 4); - _configurator.WriteJSONFiles(_serverConfiguration); - _configurator.SaveServerProps(_serverConfiguration, false); - - try + return new Task(() => { - if (File.Exists($@"{_serverConfiguration.GetProp("ServerPath")}\{_serverConfiguration.GetProp("ServerExeName")}")) + string exeName = _serverConfiguration.GetProp("ServerExeName").ToString(); + string appName = exeName.Substring(0, exeName.Length - 4); + _configurator.WriteJSONFiles(_serverConfiguration); + _configurator.SaveServerProps(_serverConfiguration, false); + + try { - if (MonitoredAppExists(appName)) + if (File.Exists($@"{_serverConfiguration.GetProp("ServerPath")}\{_serverConfiguration.GetProp("ServerExeName")}")) { - Process[] processList = Process.GetProcessesByName(appName); - if (processList.Length != 0) + if (MonitoredAppExists(appName)) { - _logger.AppendLine($@"Application {appName} was found running! Killing to proceed."); - KillProcess(processList); + Process[] processList = Process.GetProcessesByName(appName); + if (processList.Length != 0) + { + _logger.AppendLine($@"Application {appName} was found running! Killing to proceed."); + KillProcess(processList); + } } + // Fires up a new process to run inside this one + CreateProcess(); + } + else + { + _logger.AppendLine($"The Bedrock Server is not accessible at {$@"{_serverConfiguration.GetProp("ServerPath")}\{_serverConfiguration.GetProp("ServerExeName")}"}\r\nCheck if the file is at that location and that permissions are correct."); + _hostController.Stop(); } - // Fires up a new process to run inside this one - CreateProcess(); } - else + catch (Exception e) { - _logger.AppendLine($"The Bedrock Server is not accessible at {$@"{_serverConfiguration.GetProp("ServerPath")}\{_serverConfiguration.GetProp("ServerExeName")}"}\r\nCheck if the file is at that location and that permissions are correct."); + _logger.AppendLine($"Error Running Bedrock Server: {e.Message}\n{e.StackTrace}"); _hostController.Stop(); } - } - catch (Exception e) - { - _logger.AppendLine($"Error Running Bedrock Server: {e.Message}\n{e.StackTrace}"); - _hostController.Stop(); - - } - + }, _serverCanceler.Token); } private void CreateProcess() @@ -342,7 +338,7 @@ private void StdOutToLog(object sender, DataReceivedEventArgs e) if (dataMsg.Contains(_startupMessage)) { _currentServerStatus = ServerStatus.Started; - Thread.Sleep(1000); + Task.Delay(3000).Wait(); if (_serverConfiguration.GetStartCommands().Count > 0) { diff --git a/BedrockService/Service/Server/IBedrockServer.cs b/BedrockService/Service/Server/IBedrockServer.cs index eddf6a0f..fdcb9ba7 100644 --- a/BedrockService/Service/Server/IBedrockServer.cs +++ b/BedrockService/Service/Server/IBedrockServer.cs @@ -5,7 +5,6 @@ namespace BedrockService.Service.Server public interface IBedrockServer { void StartWatchdog(HostControl hostControl); - void StopWatchdog(); string GetServerName(); void WriteToStandardIn(string command); bool RestartServer(bool shouldPerformBackup); diff --git a/BedrockService/Service/Server/Management/PlayerManager.cs b/BedrockService/Service/Server/Management/PlayerManager.cs index eb4eabcd..1bc3153e 100644 --- a/BedrockService/Service/Server/Management/PlayerManager.cs +++ b/BedrockService/Service/Server/Management/PlayerManager.cs @@ -19,15 +19,15 @@ public void PlayerConnected(string username, string xuid) _serverConfiguration.AddUpdatePlayer(new Player(xuid, username, DateTime.Now.Ticks.ToString(), "0", "0", false, _serverConfiguration.GetProp("default-player-permission-level").ToString(), false)); return; } - playerFound.UpdateTimes(DateTime.Now.Ticks.ToString(), playerFound.GetTimes()[2]); + playerFound.UpdateTimes(DateTime.Now.Ticks.ToString(), playerFound.GetTimes().Disconn); _serverConfiguration.AddUpdatePlayer(playerFound); } public void PlayerDisconnected(string xuid) { IPlayer playerFound = _serverConfiguration.GetPlayerByXuid(xuid); - string[] oldTimes = playerFound.GetTimes(); - playerFound.UpdateTimes(oldTimes[1], DateTime.Now.Ticks.ToString()); + var oldTimes = playerFound.GetTimes(); + playerFound.UpdateTimes(oldTimes.Conn, DateTime.Now.Ticks.ToString()); _serverConfiguration.AddUpdatePlayer(playerFound); } diff --git a/BedrockService/Service/Startup.cs b/BedrockService/Service/Startup.cs deleted file mode 100644 index 1f81abd9..00000000 --- a/BedrockService/Service/Startup.cs +++ /dev/null @@ -1,71 +0,0 @@ -// This Startup file is based on ASP.NET Core new project templates and is included -// as a starting point for DI registration and HTTP request processing pipeline configuration. -// This file will need updated according to the specific scenario of the application being upgraded. -// For more information on ASP.NET Core startup files, see https://docs.microsoft.com/aspnet/core/fundamentals/startup - -using BedrockService.Service.Core.Interfaces; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Mvc; - -namespace BedrockService.Service -{ - public class Startup - { - private readonly bool _isDebug; - private readonly bool _isConsole; - public Startup(bool isDebug, bool isConsole) - { - _isDebug = isDebug; - _isConsole = isConsole; - } - - public IConfiguration Configuration { get; } - - // This method gets called by the runtime. Use this method to add services to the container. - public void ConfigureServices(IServiceCollection services) - { - services.AddControllersWithViews(ConfigureMvcOptions) - // Newtonsoft.Json is added for compatibility reasons - // The recommended approach is to use System.Text.Json for serialization - // Visit the following link for more guidance about moving away from Newtonsoft.Json to System.Text.Json - // https://docs.microsoft.com/dotnet/standard/serialization/system-text-json-migrate-from-newtonsoft-how-to - .AddNewtonsoftJson(options => - { - options.UseMemberCasing(); - }); - IProcessInfo processInfo = new ServiceProcessInfo(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), Path.GetFileName(Assembly.GetExecutingAssembly().Location), Process.GetCurrentProcess().Id, _isDebug, _isConsole); - services.AddSingleton(processInfo); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); - } - - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) - { - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } - - app.UseStaticFiles(); - app.UseRouting(); - app.UseAuthorization(); - app.UseEndpoints(endpoints => - { - endpoints.MapControllerRoute( - name: "default", - pattern: "{controller=Home}/{action=Index}/{id?}"); - }); - } - - private void ConfigureMvcOptions(MvcOptions mvcOptions) - { - } - } -} diff --git a/BedrockService/ServiceTests/ConfiguratorTests.cs b/BedrockService/ServiceTests/ConfiguratorTests.cs new file mode 100644 index 00000000..e7f04af9 --- /dev/null +++ b/BedrockService/ServiceTests/ConfiguratorTests.cs @@ -0,0 +1,22 @@ +using BedrockService.Service; +using BedrockService.Service.Management; +using Microsoft.Extensions.DependencyInjection; +using System; +using Xunit; + +namespace ServiceTests +{ + public class ConfiguratorTests + { + readonly IServiceProvider _services = + Program.CreateHostBuilder(new string[] { }).Build().Services; // one liner + IConfigurator myService; + + [Fact] + public void VerifyServiceInContainer() + { + myService = _services.GetRequiredService(); + Assert.NotNull(myService); + } + } +} \ No newline at end of file diff --git a/BedrockService/ServiceTests/ServiceTests.cs b/BedrockService/ServiceTests/ServiceTests.cs new file mode 100644 index 00000000..e9d57b48 --- /dev/null +++ b/BedrockService/ServiceTests/ServiceTests.cs @@ -0,0 +1,21 @@ +using BedrockService.Service; +using BedrockService.Service.Core.Interfaces; +using Microsoft.Extensions.DependencyInjection; +using System; +using Xunit; + +namespace ServiceTests +{ + public class ServiceTests + { + readonly IServiceProvider _services = + Program.CreateHostBuilder(new string[] { }).Build().Services; // one liner + + [Fact] + public void VerifyServiceInContainer() + { + IBedrockService myService = _services.GetRequiredService(); + Assert.NotNull(myService); + } + } +} \ No newline at end of file diff --git a/BedrockService/ServiceTests/ServiceTests.csproj b/BedrockService/ServiceTests/ServiceTests.csproj index 9f77f5f6..7bbcb6d5 100644 --- a/BedrockService/ServiceTests/ServiceTests.csproj +++ b/BedrockService/ServiceTests/ServiceTests.csproj @@ -20,4 +20,8 @@ + + + + diff --git a/BedrockService/ServiceTests/UnitTest1.cs b/BedrockService/ServiceTests/UnitTest1.cs deleted file mode 100644 index bca01d1c..00000000 --- a/BedrockService/ServiceTests/UnitTest1.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Xunit; - -namespace ServiceTests -{ - public class UnitTest1 - { - [Fact] - public void Test1() - { - - } - } -} \ No newline at end of file From b112b168e2620075093f7d018a409905b0cd4af5 Mon Sep 17 00:00:00 2001 From: crowbarmaster Date: Wed, 15 Dec 2021 12:07:21 -0500 Subject: [PATCH 39/62] Missing call to SaveServerProps added, Closes issue #14 on GH. --- BedrockService/Service/Networking/NetworkStrategyLookup.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/BedrockService/Service/Networking/NetworkStrategyLookup.cs b/BedrockService/Service/Networking/NetworkStrategyLookup.cs index c3bff5da..6692eac2 100644 --- a/BedrockService/Service/Networking/NetworkStrategyLookup.cs +++ b/BedrockService/Service/Networking/NetworkStrategyLookup.cs @@ -161,6 +161,7 @@ public void ParseMessage(byte[] data, byte serverIndex) return; } _serviceConfiguration.GetServerInfoByIndex(serverIndex).SetAllProps(propList); + _configurator.SaveServerProps(_serviceConfiguration.GetServerInfoByIndex(serverIndex), true); _bedrockService.GetBedrockServerByIndex(serverIndex).SetServerStatus(BedrockServer.ServerStatus.Stopping); while (_bedrockService.GetBedrockServerByIndex(serverIndex).GetServerStatus() == BedrockServer.ServerStatus.Stopping) { From 8b8fdac5e8031369affd2aabc249d3e303a111b1 Mon Sep 17 00:00:00 2001 From: crowbarmaster Date: Wed, 15 Dec 2021 12:36:43 -0500 Subject: [PATCH 40/62] Add boolean parameter to StopServer method to determine whether or not this action should also kill the server's watchdog task. Fixes GH issue #13. --- .../Service/Networking/NetworkStrategyLookup.cs | 4 ++-- BedrockService/Service/Server/BedrockServer.cs | 11 +++++++---- BedrockService/Service/Server/IBedrockServer.cs | 2 +- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/BedrockService/Service/Networking/NetworkStrategyLookup.cs b/BedrockService/Service/Networking/NetworkStrategyLookup.cs index 6692eac2..a0305791 100644 --- a/BedrockService/Service/Networking/NetworkStrategyLookup.cs +++ b/BedrockService/Service/Networking/NetworkStrategyLookup.cs @@ -402,7 +402,7 @@ public void ParseMessage(byte[] data, byte serverIndex) Buffer.BlockCopy(data, 5, stripHeaderFromBuffer, 0, stripHeaderFromBuffer.Length); IServerConfiguration server = _serviceConfiguration.GetServerInfoByIndex(serverIndex); string pathToLevelDat = $@"{_serviceConfiguration.GetProp("ServersPath")}\{server.GetProp("server-name")}\worlds\{server.GetProp("level-name")}\level.dat"; - _bedrockService.GetBedrockServerByIndex(serverIndex).StopServer().Wait(); + _bedrockService.GetBedrockServerByIndex(serverIndex).StopServer(false).Wait(); File.WriteAllBytes(pathToLevelDat, stripHeaderFromBuffer); _bedrockService.GetBedrockServerByIndex(serverIndex).SetServerStatus(BedrockServer.ServerStatus.Starting); } @@ -562,7 +562,7 @@ public RemoveServer(IConfigurator configurator, IMessageSender messageSender, IS public void ParseMessage(byte[] data, byte serverIndex, NetworkMessageFlags flag) { - _bedrockService.GetBedrockServerByIndex(serverIndex).StopServer().Wait(); + _bedrockService.GetBedrockServerByIndex(serverIndex).StopServer(true).Wait(); _configurator.RemoveServerConfigs(_serviceConfiguration.GetServerInfoByIndex(serverIndex), flag); _bedrockService.RemoveBedrockServerByIndex(serverIndex); JsonSerializerSettings settings = new JsonSerializerSettings() diff --git a/BedrockService/Service/Server/BedrockServer.cs b/BedrockService/Service/Server/BedrockServer.cs index 01a777ac..1d162557 100644 --- a/BedrockService/Service/Server/BedrockServer.cs +++ b/BedrockService/Service/Server/BedrockServer.cs @@ -387,7 +387,7 @@ private void StdOutToLog(object sender, DataReceivedEventArgs e) if (currentVersion != versionString) { _logger.AppendLine($"Server {GetServerName()} version found out-of-date! Now updating!"); - StopServer().Wait(); + StopServer(false).Wait(); _configurator.ReplaceServerBuild(_serverConfiguration).Wait(); StartControl(); } @@ -408,7 +408,7 @@ private void RunStartupCommands() public bool RollbackToBackup(byte serverIndex, string folderName) { IServerConfiguration server = _serviceConfiguration.GetServerInfoByIndex(serverIndex); - StopServer().Wait(); + StopServer(false).Wait(); try { foreach (DirectoryInfo dir in new DirectoryInfo($@"{_serviceConfiguration.GetProp("BackupPath")}\{server.GetServerName()}").GetDirectories()) @@ -436,7 +436,7 @@ public bool RollbackToBackup(byte serverIndex, string folderName) public IPlayerManager GetPlayerManager() => _playerManager; - public Task StopServer() + public Task StopServer(bool stopWatchdog) { return Task.Run(() => { @@ -445,7 +445,10 @@ public Task StopServer() { Thread.Sleep(100); } - + if (stopWatchdog) + { + _watchdogCanceler.Cancel(); + } }); } } diff --git a/BedrockService/Service/Server/IBedrockServer.cs b/BedrockService/Service/Server/IBedrockServer.cs index fdcb9ba7..c7c2763b 100644 --- a/BedrockService/Service/Server/IBedrockServer.cs +++ b/BedrockService/Service/Server/IBedrockServer.cs @@ -9,7 +9,7 @@ public interface IBedrockServer void WriteToStandardIn(string command); bool RestartServer(bool shouldPerformBackup); bool RollbackToBackup(byte serverIndex, string folderName); - Task StopServer(); + Task StopServer(bool stopWatchdog); BedrockServer.ServerStatus GetServerStatus(); void SetServerStatus(BedrockServer.ServerStatus newStatus); IPlayerManager GetPlayerManager(); From 6260c6576ab281a9a21ed0c9b71eea4b8b3c1d95 Mon Sep 17 00:00:00 2001 From: crowbarmaster Date: Thu, 16 Dec 2021 23:19:09 -0500 Subject: [PATCH 41/62] Write method used to backup File Name/size pairs parsed from the backup query string output. --- .../Utilities/FileUtils.cs | 41 ++++++++++++++++++- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/BedrockService/BedrockService.Shared/Utilities/FileUtils.cs b/BedrockService/BedrockService.Shared/Utilities/FileUtils.cs index de65d8d4..bdf518b4 100644 --- a/BedrockService/BedrockService.Shared/Utilities/FileUtils.cs +++ b/BedrockService/BedrockService.Shared/Utilities/FileUtils.cs @@ -1,8 +1,10 @@ using BedrockService.Shared.MincraftJson; using Newtonsoft.Json.Linq; +using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Threading.Tasks; namespace BedrockService.Shared.Utilities { @@ -75,7 +77,7 @@ public void UpdateJArrayFile(string path, JArray content, System.Type type) currentFileContents = File.ReadAllText(path); jArray = JArray.Parse(currentFileContents); } - if(type == typeof(WorldPacksJsonModel)) + if (type == typeof(WorldPacksJsonModel)) { foreach (JToken jToken in content) { @@ -95,10 +97,45 @@ public void UpdateJArrayFile(string path, JArray content, System.Type type) } File.WriteAllText(path, jArray.ToString()); } - catch(System.Exception) + catch (System.Exception) { return; } } + + public Task BackupWorldFilesFromQuery(Dictionary fileNameSizePairs, string worldPath, string destinationPath) + { + return Task.Run(() => + { + try + { + foreach (KeyValuePair file in fileNameSizePairs) + { + string fileName = file.Key.Replace('/', '\\'); + int fileSize = file.Value; + string filePath = $@"{worldPath}\{fileName}"; + string destFilePath = $@"{destinationPath}\{fileName}"; + byte[] fileData = null; + using (FileStream fs = File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + using (MemoryStream ms = new MemoryStream()) + { + fs.CopyTo(ms); + ms.Position = 0; + fileData = ms.ToArray(); + } + byte[] destData = new byte[fileSize]; + Buffer.BlockCopy(fileData, 0, destData, 0, fileSize); + Directory.CreateDirectory(new FileInfo(destFilePath).DirectoryName); + File.WriteAllBytes(destFilePath, fileData); + } + return true; + } + catch (System.Exception ex) + { + Console.WriteLine($"Error! {ex.Message}"); + } + return false; + }); + } } } From ff8b520f55454a32b7a51bd5ce631d4535e43ecc Mon Sep 17 00:00:00 2001 From: crowbarmaster Date: Thu, 16 Dec 2021 23:20:16 -0500 Subject: [PATCH 42/62] Rename Backup() to BackupAllServers() to specify this method is used to backup all servers. --- BedrockService/Service/Core/BedrockService.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/BedrockService/Service/Core/BedrockService.cs b/BedrockService/Service/Core/BedrockService.cs index 6686defd..d0640d95 100644 --- a/BedrockService/Service/Core/BedrockService.cs +++ b/BedrockService/Service/Core/BedrockService.cs @@ -182,8 +182,7 @@ private void CronTimer_Elapsed(object sender, ElapsedEventArgs e) } if (_serviceConfiguration.GetProp("BackupEnabled").ToString() == "true" && _shed != null) { - Backup(); - + BackupAllServers(); _cronTimer = new System.Timers.Timer((_shed.GetNextOccurrence(DateTime.Now) - DateTime.Now).TotalMilliseconds); _cronTimer.Elapsed += CronTimer_Elapsed; _cronTimer.Start(); @@ -227,12 +226,12 @@ private void UpdateTimer_Elapsed(object sender, ElapsedEventArgs e) } } - private void Backup() + private void BackupAllServers() { _logger.AppendLine("Service started backup manager."); foreach (var brs in _bedrockServers) { - brs.RestartServer(true); + brs.InitializeBackup(); } _logger.AppendLine("Backups have been completed."); } From 608e38ee3f073b0ac6c2e3727f69b93433c34b17 Mon Sep 17 00:00:00 2001 From: crowbarmaster Date: Thu, 16 Dec 2021 23:24:58 -0500 Subject: [PATCH 43/62] Impliements downtime-free backup system. Initial tests prove working as desired. Satisfies issue #9. --- .../Networking/NetworkStrategyLookup.cs | 2 +- .../Service/Server/BedrockServer.cs | 50 +++++++++++++++---- .../Service/Server/IBedrockServer.cs | 1 + 3 files changed, 42 insertions(+), 11 deletions(-) diff --git a/BedrockService/Service/Networking/NetworkStrategyLookup.cs b/BedrockService/Service/Networking/NetworkStrategyLookup.cs index a0305791..52850a19 100644 --- a/BedrockService/Service/Networking/NetworkStrategyLookup.cs +++ b/BedrockService/Service/Networking/NetworkStrategyLookup.cs @@ -100,7 +100,7 @@ public ServerBackup(IMessageSender messageSender, IBedrockService service) public void ParseMessage(byte[] data, byte serverIndex) { - _service.GetBedrockServerByIndex(serverIndex).RestartServer(true); + _service.GetBedrockServerByIndex(serverIndex).InitializeBackup(); _messageSender.SendData(NetworkMessageSource.Service, NetworkMessageDestination.Client, NetworkMessageTypes.UICallback); } } diff --git a/BedrockService/Service/Server/BedrockServer.cs b/BedrockService/Service/Server/BedrockServer.cs index 1d162557..0a0f7f57 100644 --- a/BedrockService/Service/Server/BedrockServer.cs +++ b/BedrockService/Service/Server/BedrockServer.cs @@ -78,10 +78,18 @@ public void StartWatchdog(HostControl hostControl) _watchdogTask.Start(); } - private bool Backup() + public void InitializeBackup() + { + WriteToStandardIn("save hold"); + Task.Delay(1000).Wait(); + WriteToStandardIn("save query"); + } + + private bool PerformBackup(string queryString) { try { + FileUtils fileUtils = new FileUtils(_servicePath); FileInfo exe = new FileInfo($@"{_serverConfiguration.GetProp("ServerPath")}\{_serverConfiguration.GetProp("ServerExeName")}"); string configBackupPath = _serviceConfiguration.GetProp("BackupPath").ToString(); DirectoryInfo backupDir = new DirectoryInfo($@"{configBackupPath}\{_serverConfiguration.GetServerName()}"); @@ -91,6 +99,15 @@ private bool Backup() { backupDir.Create(); } + Dictionary backupFileInfoPairs = new Dictionary(); + string[] files = queryString.Split(", "); + foreach (string file in files) + { + string[] fileInfoSplit = file.Split(':'); + string fileName = fileInfoSplit[0]; + int fileSize = int.Parse(fileInfoSplit[1]); + backupFileInfoPairs.Add(fileName, fileSize); + } int dirCount = backupDir.GetDirectories().Length; try { @@ -134,11 +151,14 @@ private bool Backup() _logger.AppendLine($"Backing up files for server {_serverConfiguration.GetServerName()}. Please wait!"); if (_serviceConfiguration.GetProp("EntireBackups").ToString() == "false") { - new FileUtils(_servicePath).CopyFilesRecursively(worldsDir, targetDirectory); - return true; + bool resuilt = fileUtils.BackupWorldFilesFromQuery(backupFileInfoPairs, worldsDir.FullName, $@"{targetDirectory.FullName}").Result; + WriteToStandardIn("save resume"); + return resuilt; } - new FileUtils(_servicePath).CopyFilesRecursively(serverDir, targetDirectory); - return true; + fileUtils.CopyFilesRecursively(serverDir, targetDirectory); + bool result = fileUtils.BackupWorldFilesFromQuery(backupFileInfoPairs, worldsDir.FullName, $@"{targetDirectory.FullName}\{_serverConfiguration.GetProp("level-name")}").Result; + WriteToStandardIn("save resume"); + return result; } catch (Exception e) { @@ -215,11 +235,6 @@ public bool RestartServer(bool ShouldPerformBackup) { Thread.Sleep(100); } - if (ShouldPerformBackup) - { - if (!Backup()) - return false; - } _currentServerStatus = ServerStatus.Starting; } return false; @@ -392,6 +407,21 @@ private void StdOutToLog(object sender, DataReceivedEventArgs e) StartControl(); } } + if(dataMsg.Contains("A previous save has not been completed.")) + { + Task.Delay(1000).Wait(); + WriteToStandardIn("save query"); + } + if (dataMsg.Contains($@"{_serverConfiguration.GetProp("level-name")}/db/")) + { + _logger.AppendLine("Save data string detected! Performing backup now!"); + if (PerformBackup(dataMsg)) + { + _logger.AppendLine($"Backup for server {_serverConfiguration.GetServerName()} Completed."); + return; + } + _logger.AppendLine($"Backup for server {_serverConfiguration.GetServerName()} Failed. Check logs!"); + } } } } diff --git a/BedrockService/Service/Server/IBedrockServer.cs b/BedrockService/Service/Server/IBedrockServer.cs index c7c2763b..071ef542 100644 --- a/BedrockService/Service/Server/IBedrockServer.cs +++ b/BedrockService/Service/Server/IBedrockServer.cs @@ -10,6 +10,7 @@ public interface IBedrockServer bool RestartServer(bool shouldPerformBackup); bool RollbackToBackup(byte serverIndex, string folderName); Task StopServer(bool stopWatchdog); + void InitializeBackup(); BedrockServer.ServerStatus GetServerStatus(); void SetServerStatus(BedrockServer.ServerStatus newStatus); IPlayerManager GetPlayerManager(); From d29c9bec432b2f9b74eb7409a05b4a7a4e87fb8e Mon Sep 17 00:00:00 2001 From: crowbarmaster Date: Thu, 16 Dec 2021 23:43:28 -0500 Subject: [PATCH 44/62] Code cleanup, Open bracing no longer occupies newline. --- .../BedrockManagementServiceASP/Program.cs | 3 +- .../BedrockManagementServiceASP/Worker.cs | 15 +- .../Classes/ClientSideServiceConfiguration.cs | 9 +- .../Classes/CommsKeyContainer.cs | 33 +- .../Classes/NetworkEnums.cs | 15 +- .../BedrockService.Shared/Classes/Player.cs | 41 +-- .../BedrockService.Shared/Classes/Property.cs | 15 +- .../Classes/ServerInfo.cs | 87 ++--- .../Classes/ServerLogger.cs | 54 ++-- .../Classes/ServiceInfo.cs | 72 ++--- .../Classes/ServiceProcessInfo.cs | 12 +- .../Classes/StartCmdEntry.cs | 9 +- .../Interfaces/IBedrockConfiguration.cs | 6 +- .../Interfaces/IBedrockLogger.cs | 6 +- .../IClientSideServiceConfiguration.cs | 6 +- .../Interfaces/IPlayer.cs | 6 +- .../Interfaces/IProcessInfo.cs | 6 +- .../Interfaces/IServerConfiguration.cs | 6 +- .../Interfaces/IServiceConfiguration.cs | 6 +- .../MincraftJson/KnownPacksJsonModel.cs | 13 +- .../MincraftJson/PermissionsJsonModel.cs | 14 +- .../MincraftJson/WhitelistJsonModel.cs | 13 +- .../MincraftJson/WorldPacksJsonModel.cs | 13 +- .../PackParser/MinecraftKnownPacksClass.cs | 24 +- .../PackParser/MinecraftPackParser.cs | 81 ++--- .../Utilities/FileUtils.cs | 69 ++-- .../Client/Forms/AddNewServerForm.cs | 27 +- .../Client/Forms/ClientConfigForm.cs | 36 +-- BedrockService/Client/Forms/MainWindow.cs | 297 ++++++----------- .../Client/Forms/ManagePacksForms.cs | 61 ++-- .../Client/Forms/NewPlayerRegistrationForm.cs | 15 +- .../Client/Forms/PlayerManagerForm.cs | 75 ++--- BedrockService/Client/Forms/PropEditorForm.cs | 72 ++--- .../Client/Management/ClientLogger.cs | 27 +- .../Client/Management/ConfigManager.cs | 42 +-- .../Client/Management/FormManager.cs | 24 +- .../Client/Management/LogManager.cs | 66 ++-- BedrockService/Client/Networking/TCPClient.cs | 110 +++---- BedrockService/Service/Core/BedrockService.cs | 169 ++++------ .../Core/Interfaces/IBedrockService.cs | 6 +- .../Service/Core/Interfaces/IService.cs | 6 +- BedrockService/Service/Core/Service.cs | 49 +-- .../Service/Logging/ServiceLogger.cs | 54 ++-- .../Service/Management/ConfigManager.cs | 256 +++++---------- .../Service/Management/IConfigurator.cs | 6 +- .../Service/Networking/ITCPListener.cs | 6 +- BedrockService/Service/Networking/IUpdater.cs | 6 +- .../IFlaggedMessageParser.cs | 6 +- .../MessageInterfaces/IMessageParser.cs | 6 +- .../MessageInterfaces/IMessageSender.cs | 6 +- .../Networking/NetworkStrategyLookup.cs | 298 ++++++------------ .../Service/Networking/TCPListener.cs | 131 +++----- BedrockService/Service/Networking/Updater.cs | 118 +++---- BedrockService/Service/Program.cs | 19 +- .../Service/Server/BedrockServer.cs | 259 ++++++--------- .../Service/Server/IBedrockServer.cs | 6 +- .../Server/Management/IPlayerManager.cs | 6 +- .../Server/Management/PlayerManager.cs | 36 +-- .../ServiceTests/ConfiguratorTests.cs | 9 +- BedrockService/ServiceTests/ServiceTests.cs | 9 +- 60 files changed, 983 insertions(+), 1970 deletions(-) diff --git a/BedrockService/BedrockManagementServiceASP/Program.cs b/BedrockService/BedrockManagementServiceASP/Program.cs index 3a33c382..aa303a14 100644 --- a/BedrockService/BedrockManagementServiceASP/Program.cs +++ b/BedrockService/BedrockManagementServiceASP/Program.cs @@ -1,8 +1,7 @@ using BedrockManagementServiceASP; IHost host = Host.CreateDefaultBuilder(args) - .ConfigureServices(services => - { + .ConfigureServices(services => { services.AddHostedService(); }) .Build(); diff --git a/BedrockService/BedrockManagementServiceASP/Worker.cs b/BedrockService/BedrockManagementServiceASP/Worker.cs index 0bc53b32..c8f104ba 100644 --- a/BedrockService/BedrockManagementServiceASP/Worker.cs +++ b/BedrockService/BedrockManagementServiceASP/Worker.cs @@ -1,18 +1,13 @@ -namespace BedrockManagementServiceASP -{ - public class Worker : BackgroundService - { +namespace BedrockManagementServiceASP { + public class Worker : BackgroundService { private readonly ILogger _logger; - public Worker(ILogger logger) - { + public Worker(ILogger logger) { _logger = logger; } - protected override async Task ExecuteAsync(CancellationToken stoppingToken) - { - while (!stoppingToken.IsCancellationRequested) - { + protected override async Task ExecuteAsync(CancellationToken stoppingToken) { + while (!stoppingToken.IsCancellationRequested) { _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now); await Task.Delay(1000, stoppingToken); } diff --git a/BedrockService/BedrockService.Shared/Classes/ClientSideServiceConfiguration.cs b/BedrockService/BedrockService.Shared/Classes/ClientSideServiceConfiguration.cs index f1936fa2..ae96a8e1 100644 --- a/BedrockService/BedrockService.Shared/Classes/ClientSideServiceConfiguration.cs +++ b/BedrockService/BedrockService.Shared/Classes/ClientSideServiceConfiguration.cs @@ -1,16 +1,13 @@ using BedrockService.Shared.Interfaces; -namespace BedrockService.Shared.Classes -{ - public class ClientSideServiceConfiguration : IClientSideServiceConfiguration - { +namespace BedrockService.Shared.Classes { + public class ClientSideServiceConfiguration : IClientSideServiceConfiguration { private readonly string _hostName; private readonly string _address; private readonly string _port; private readonly string _displayName; - public ClientSideServiceConfiguration(string hostName, string address, string port) - { + public ClientSideServiceConfiguration(string hostName, string address, string port) { this._hostName = hostName; this._address = address; this._port = port; diff --git a/BedrockService/BedrockService.Shared/Classes/CommsKeyContainer.cs b/BedrockService/BedrockService.Shared/Classes/CommsKeyContainer.cs index cf54fe2d..41ca6c24 100644 --- a/BedrockService/BedrockService.Shared/Classes/CommsKeyContainer.cs +++ b/BedrockService/BedrockService.Shared/Classes/CommsKeyContainer.cs @@ -1,11 +1,9 @@ using System; using System.Security.Cryptography; -namespace BedrockService.Shared.Classes -{ +namespace BedrockService.Shared.Classes { [Serializable] - public class RSAContainer - { + public class RSAContainer { public byte[] D; public byte[] DP; public byte[] DQ; @@ -15,8 +13,7 @@ public class RSAContainer public byte[] P; public byte[] Q; - public RSAContainer(RSAParameters input) - { + public RSAContainer(RSAParameters input) { D = input.D; P = input.DP; DP = input.DP; @@ -27,10 +24,8 @@ public RSAContainer(RSAParameters input) Exponent = input.Exponent; } - public RSAParameters GetPrivateKey() - { - return new RSAParameters() - { + public RSAParameters GetPrivateKey() { + return new RSAParameters() { D = this.D, P = this.P, DP = this.DP, @@ -42,17 +37,14 @@ public RSAParameters GetPrivateKey() }; } - public RSAParameters GetPublicKey() - { - return new RSAParameters() - { + public RSAParameters GetPublicKey() { + return new RSAParameters() { Modulus = this.Modulus, Exponent = this.Exponent }; } - public void SetPrivateKey(RSAParameters privateKey) - { + public void SetPrivateKey(RSAParameters privateKey) { this.D = privateKey.D; this.DP = privateKey.DP; this.P = privateKey.P; @@ -63,16 +55,14 @@ public void SetPrivateKey(RSAParameters privateKey) this.Exponent = privateKey.Exponent; } - public void SetPublicKey(RSAParameters publicKey) - { + public void SetPublicKey(RSAParameters publicKey) { this.Exponent = publicKey.Exponent; this.Modulus = publicKey.Modulus; } } [Serializable] - public class CommsKeyContainer - { + public class CommsKeyContainer { public RSAContainer LocalPrivateKey = new RSAContainer(new RSAParameters()); public RSAContainer RemotePublicKey = new RSAContainer(new RSAParameters()); public byte[] AesKey; @@ -80,8 +70,7 @@ public class CommsKeyContainer public CommsKeyContainer() { } - public CommsKeyContainer(RSAParameters priv, RSAParameters pub, byte[] key, byte[] IV) - { + public CommsKeyContainer(RSAParameters priv, RSAParameters pub, byte[] key, byte[] IV) { LocalPrivateKey = new RSAContainer(priv); RemotePublicKey = new RSAContainer(pub); AesKey = key; diff --git a/BedrockService/BedrockService.Shared/Classes/NetworkEnums.cs b/BedrockService/BedrockService.Shared/Classes/NetworkEnums.cs index 3cc62590..11ea024e 100644 --- a/BedrockService/BedrockService.Shared/Classes/NetworkEnums.cs +++ b/BedrockService/BedrockService.Shared/Classes/NetworkEnums.cs @@ -1,22 +1,18 @@ -namespace BedrockService.Shared.Classes -{ - public enum NetworkMessageSource - { +namespace BedrockService.Shared.Classes { + public enum NetworkMessageSource { Client, Server, Service } - public enum NetworkMessageDestination - { + public enum NetworkMessageDestination { Client, Server, Service } - public enum NetworkMessageTypes - { + public enum NetworkMessageTypes { Connect, AddNewServer, RemoveServer, @@ -43,8 +39,7 @@ public enum NetworkMessageTypes UICallback } - public enum NetworkMessageFlags - { + public enum NetworkMessageFlags { Failed, Passed, RemoveBackups, diff --git a/BedrockService/BedrockService.Shared/Classes/Player.cs b/BedrockService/BedrockService.Shared/Classes/Player.cs index 6860035c..7d2b9197 100644 --- a/BedrockService/BedrockService.Shared/Classes/Player.cs +++ b/BedrockService/BedrockService.Shared/Classes/Player.cs @@ -2,10 +2,8 @@ using Newtonsoft.Json; using System; -namespace BedrockService.Shared.Classes -{ - public class Player : IPlayer - { +namespace BedrockService.Shared.Classes { + public class Player : IPlayer { [JsonProperty] private string Username { get; set; } [JsonProperty] @@ -28,8 +26,7 @@ public class Player : IPlayer private bool FromConfig { get; set; } [JsonConstructor] - public Player(string xuid, string username, string firstConn, string lastConn, string lastDiscon, bool whtlist, string perm, bool ignoreLimit) - { + public Player(string xuid, string username, string firstConn, string lastConn, string lastDiscon, bool whtlist, string perm, bool ignoreLimit) { Username = username; XUID = xuid; FirstConnectedTime = firstConn; @@ -40,22 +37,19 @@ public Player(string xuid, string username, string firstConn, string lastConn, s IgnorePlayerLimits = ignoreLimit; } - public Player(string serverDefaultPermission) - { + public Player(string serverDefaultPermission) { ServerDefaultPerm = serverDefaultPermission; PermissionLevel = serverDefaultPermission; } - public void Initialize(string xuid, string username) - { + public void Initialize(string xuid, string username) { XUID = xuid; Username = username; } public string GetUsername() => Username; - public string SearchForProperty(string input) - { + public string SearchForProperty(string input) { if (input == "name" || input == "username" || input == "un") return Username; if (input == "xuid" || input == "id") @@ -71,39 +65,32 @@ public string SearchForProperty(string input) public string GetXUID() => XUID; - public void UpdateTimes(string lastConn, string lastDiscon) - { + public void UpdateTimes(string lastConn, string lastDiscon) { if (FirstConnectedTime == "") FirstConnectedTime = DateTime.Now.Ticks.ToString(); LastConnectedTime = lastConn; LastDisconnectTime = lastDiscon; } - public void UpdateRegistration(string whtlist, string perm, string ignoreLimit) - { + public void UpdateRegistration(string whtlist, string perm, string ignoreLimit) { Whitelisted = bool.Parse(whtlist); PermissionLevel = perm; IgnorePlayerLimits = bool.Parse(ignoreLimit); } - public (string First, string Conn, string Disconn) GetTimes() - { - return ( FirstConnectedTime, LastConnectedTime, LastDisconnectTime ); + public (string First, string Conn, string Disconn) GetTimes() { + return (FirstConnectedTime, LastConnectedTime, LastDisconnectTime); } - public bool IsDefaultRegistration() - { + public bool IsDefaultRegistration() { return Whitelisted == false && IgnorePlayerLimits == false && PermissionLevel == ServerDefaultPerm; } - public string ToString(string format) - { - if (format == "Known") - { + public string ToString(string format) { + if (format == "Known") { return $"{XUID},{Username},{FirstConnectedTime},{LastConnectedTime},{LastDisconnectTime}"; } - if (format == "Registered") - { + if (format == "Registered") { return $"{XUID},{Username},{PermissionLevel},{Whitelisted},{IgnorePlayerLimits}"; } return null; diff --git a/BedrockService/BedrockService.Shared/Classes/Property.cs b/BedrockService/BedrockService.Shared/Classes/Property.cs index ffc64a7a..6c9cd1aa 100644 --- a/BedrockService/BedrockService.Shared/Classes/Property.cs +++ b/BedrockService/BedrockService.Shared/Classes/Property.cs @@ -1,28 +1,23 @@ using Newtonsoft.Json; -namespace BedrockService.Shared.Classes -{ - public class Property - { +namespace BedrockService.Shared.Classes { + public class Property { public string KeyName { get; set; } public string Value { get; set; } public string DefaultValue { get; set; } [JsonConstructor] - public Property(string key, string defaultValue) - { + public Property(string key, string defaultValue) { KeyName = key; Value = defaultValue; DefaultValue = defaultValue; } - public override string ToString() - { + public override string ToString() { return Value; } - public void SetValue(string newValue) - { + public void SetValue(string newValue) { Value = newValue; } } diff --git a/BedrockService/BedrockService.Shared/Classes/ServerInfo.cs b/BedrockService/BedrockService.Shared/Classes/ServerInfo.cs index 67c07f19..1908914f 100644 --- a/BedrockService/BedrockService.Shared/Classes/ServerInfo.cs +++ b/BedrockService/BedrockService.Shared/Classes/ServerInfo.cs @@ -2,10 +2,8 @@ using System.Collections.Generic; using System.Linq; -namespace BedrockService.Shared.Classes -{ - public class ServerInfo : IServerConfiguration - { +namespace BedrockService.Shared.Classes { + public class ServerInfo : IServerConfiguration { public string serversPath; public string ServerName { get; set; } public string FileName { get; set; } @@ -16,15 +14,13 @@ public class ServerInfo : IServerConfiguration public List ServerPropList = new List(); public List StartCmds = new List(); - public ServerInfo(string[] configEntries, string coreServersPath) - { + public ServerInfo(string[] configEntries, string coreServersPath) { serversPath = coreServersPath; InitializeDefaults(); ProcessConfiguration(configEntries); } - public void InitializeDefaults() - { + public void InitializeDefaults() { ServerName = "Default"; FileName = "Default.conf"; ServerPath = new Property("ServerPath", $@"{serversPath}\{ServerName}"); @@ -62,26 +58,20 @@ public void InitializeDefaults() public void SetStartCommands(List newEntries) => StartCmds = newEntries; - public void ProcessConfiguration(string[] fileEntries) - { + public void ProcessConfiguration(string[] fileEntries) { if (fileEntries == null) return; - foreach (string line in fileEntries) - { - if (!line.StartsWith("#") && !string.IsNullOrEmpty(line)) - { + foreach (string line in fileEntries) { + if (!line.StartsWith("#") && !string.IsNullOrEmpty(line)) { string[] split = line.Split('='); - if (split.Length == 1) - { + if (split.Length == 1) { split = new string[] { split[0], "" }; } Property SrvProp = ServerPropList.FirstOrDefault(prop => prop.KeyName == split[0]); - if (SrvProp != null) - { + if (SrvProp != null) { SetProp(split[0], split[1]); } - switch (split[0]) - { + switch (split[0]) { case "server-name": ServerName = split[1]; ServerPath.SetValue($@"{serversPath}\{ServerName}"); @@ -97,43 +87,34 @@ public void ProcessConfiguration(string[] fileEntries) } } - public bool SetProp(string name, string newValue) - { - try - { + public bool SetProp(string name, string newValue) { + try { Property serverProp = ServerPropList.First(prop => prop.KeyName == name); ServerPropList[ServerPropList.IndexOf(serverProp)].SetValue(newValue); return true; } - catch - { + catch { } return false; } - public bool SetProp(Property propToSet) - { - try - { + public bool SetProp(Property propToSet) { + try { Property serverProp = ServerPropList.First(prop => prop.KeyName == propToSet.KeyName); ServerPropList[ServerPropList.IndexOf(serverProp)] = propToSet; return true; } - catch - { + catch { } return false; } - public Property GetProp(string name) - { + public Property GetProp(string name) { Property foundProp = ServerPropList.FirstOrDefault(prop => prop.KeyName == name); - if (foundProp == null) - { - switch (name) - { + if (foundProp == null) { + switch (name) { case "ServerPath": return ServerPath; case "ServerExeName": @@ -145,26 +126,22 @@ public Property GetProp(string name) public void SetAllInfos() => throw new System.NotImplementedException(); - public void AddStartCommand(string command) - { + public void AddStartCommand(string command) { StartCmds.Add(new StartCmdEntry(command)); } - public bool DeleteStartCommand(string command) - { + public bool DeleteStartCommand(string command) { StartCmdEntry entry = StartCmds.FirstOrDefault(prop => prop.Command == command); return StartCmds.Remove(entry); } public List GetStartCommands() => StartCmds; - public override string ToString() - { + public override string ToString() { return ServerName; } - public override int GetHashCode() - { + public override int GetHashCode() { int hashCode = -298215838; hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(ServerName); hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(FileName); @@ -172,8 +149,7 @@ public override int GetHashCode() return hashCode; } - public override bool Equals(object obj) - { + public override bool Equals(object obj) { return obj is ServerInfo info && ServerName == info.ServerName && EqualityComparer.Default.Equals(ServerPath, info.ServerPath); @@ -185,29 +161,24 @@ public override bool Equals(object obj) public void SetAllProps(List newPropList) => ServerPropList = newPropList; - public void AddUpdatePlayer(IPlayer player) - { + public void AddUpdatePlayer(IPlayer player) { IPlayer foundPlayer = PlayersList.FirstOrDefault(p => p.GetXUID() == player.GetXUID()); - if (foundPlayer == null) - { + if (foundPlayer == null) { PlayersList.Add(player); return; } PlayersList[PlayersList.IndexOf(foundPlayer)] = player; } - public IPlayer GetPlayerByXuid(string xuid) - { + public IPlayer GetPlayerByXuid(string xuid) { IPlayer foundPlayer = PlayersList.FirstOrDefault(p => p.GetXUID() == xuid); - if (foundPlayer == null) - { + if (foundPlayer == null) { return null; } return foundPlayer; } - public string GetFileName() - { + public string GetFileName() { return FileName; } diff --git a/BedrockService/BedrockService.Shared/Classes/ServerLogger.cs b/BedrockService/BedrockService.Shared/Classes/ServerLogger.cs index c7a43f5d..c3757ad8 100644 --- a/BedrockService/BedrockService.Shared/Classes/ServerLogger.cs +++ b/BedrockService/BedrockService.Shared/Classes/ServerLogger.cs @@ -4,10 +4,8 @@ using System.IO; using System.Text; -namespace BedrockService.Shared.Classes -{ - public class ServerLogger : IBedrockLogger - { +namespace BedrockService.Shared.Classes { + public class ServerLogger : IBedrockLogger { private StringBuilder OutString = new StringBuilder(); [NonSerialized] private readonly StreamWriter _logWriter; @@ -17,15 +15,13 @@ public class ServerLogger : IBedrockLogger private readonly string _logDir; private readonly IServerConfiguration _serverConfiguration; - public ServerLogger(IProcessInfo processInfo, IServiceConfiguration serviceConfiguration, IServerConfiguration serverConfiguration, string parent) - { + public ServerLogger(IProcessInfo processInfo, IServiceConfiguration serviceConfiguration, IServerConfiguration serverConfiguration, string parent) { _serverConfiguration = serverConfiguration; _logDir = $@"{processInfo.GetDirectory()}\Server\Logs"; _logToFile = bool.Parse(serviceConfiguration.GetProp("LogServersToFile").ToString()); _parent = parent; _logToConsole = true; - if (_logToFile) - { + if (_logToFile) { if (!Directory.Exists(_logDir)) Directory.CreateDirectory(_logDir); _logWriter = new StreamWriter($@"{_logDir}\ServerLog_{parent}_{DateTime.Now:yyyymmddhhmmss}.log", true); @@ -33,67 +29,53 @@ public ServerLogger(IProcessInfo processInfo, IServiceConfiguration serviceConfi } [JsonConstructor] - public ServerLogger(string serverName) - { + public ServerLogger(string serverName) { _parent = serverName; _logToFile = false; _logToConsole = false; } - public void AppendLine(string text) - { - try - { + public void AppendLine(string text) { + try { _serverConfiguration.GetLog().Add(text); - if (_logToFile && _logWriter != null) - { + if (_logToFile && _logWriter != null) { _logWriter.WriteLine(text); _logWriter.Flush(); } if (_logToConsole) Console.WriteLine(text); } - catch - { + catch { } } - public void AppendText(string text) - { - try - { + public void AppendText(string text) { + try { _serverConfiguration.GetLog().Add(text); - if (_logToFile && _logWriter != null) - { + if (_logToFile && _logWriter != null) { _logWriter.Write(text); _logWriter.Flush(); } - if (_logToConsole) - { + if (_logToConsole) { Console.Write(text); Console.Out.Flush(); } } - catch - { + catch { } } - public int Count() - { + public int Count() { return _serverConfiguration.GetLog().Count; } - public string FromIndex(int index) - { + public string FromIndex(int index) { return _serverConfiguration.GetLog()[index]; } - public override string ToString() - { + public override string ToString() { OutString = new StringBuilder(); - foreach (string s in _serverConfiguration.GetLog()) - { + foreach (string s in _serverConfiguration.GetLog()) { OutString.Append(s); } return OutString.ToString(); diff --git a/BedrockService/BedrockService.Shared/Classes/ServiceInfo.cs b/BedrockService/BedrockService.Shared/Classes/ServiceInfo.cs index b7fea589..75b46a38 100644 --- a/BedrockService/BedrockService.Shared/Classes/ServiceInfo.cs +++ b/BedrockService/BedrockService.Shared/Classes/ServiceInfo.cs @@ -3,10 +3,8 @@ using System.Collections.Generic; using System.Linq; -namespace BedrockService.Shared.Classes -{ - public class ServiceInfo : IServiceConfiguration - { +namespace BedrockService.Shared.Classes { + public class ServiceInfo : IServiceConfiguration { private string hostName { get; set; } private string address { get; set; } private string hostDisplayName { get; set; } @@ -20,14 +18,12 @@ public class ServiceInfo : IServiceConfiguration private List globals = new List(); private readonly IProcessInfo _processInfo; - public ServiceInfo(IProcessInfo processInfo) - { + public ServiceInfo(IProcessInfo processInfo) { _processInfo = processInfo; InitializeDefaults(); } - public void InitializeDefaults() - { + public void InitializeDefaults() { globals.Clear(); globals.Add(new Property("ServersPath", @"C:\MCBedrockService")); globals.Add(new Property("AcceptedMojangLic", "false")); @@ -43,62 +39,49 @@ public void InitializeDefaults() globals.Add(new Property("LogServiceToFile", "true")); } - public void ProcessConfiguration(string[] fileEntries) - { - foreach (string line in fileEntries) - { - if (!line.StartsWith("#") && !string.IsNullOrEmpty(line)) - { + public void ProcessConfiguration(string[] fileEntries) { + foreach (string line in fileEntries) { + if (!line.StartsWith("#") && !string.IsNullOrEmpty(line)) { string[] split = line.Split('='); - if (split.Length == 1) - { + if (split.Length == 1) { split[1] = ""; } - if (split[0] == "BackupPath") - { + if (split[0] == "BackupPath") { if (split[1] == "Default") split[1] = $@"{_processInfo.GetDirectory()}\Server\Backups"; } - if (!SetProp(split[0], split[1])) - { + if (!SetProp(split[0], split[1])) { //Logger.AppendLine($"Error! Key \"{split[0]}\" was not found! Check configs!"); } } } } - public bool SetProp(string name, string newValue) - { - try - { + public bool SetProp(string name, string newValue) { + try { Property GlobalToEdit = globals.First(glob => glob.KeyName == name); globals[globals.IndexOf(GlobalToEdit)].SetValue(newValue); return true; } - catch - { + catch { // handle soon. return false; } } - public bool SetProp(Property propToSet) - { - try - { + public bool SetProp(Property propToSet) { + try { Property GlobalToEdit = globals.First(glob => glob.KeyName == propToSet.KeyName); globals[globals.IndexOf(GlobalToEdit)] = propToSet; } - catch - { + catch { // handle soon. return false; } return true; } - public Property GetProp(string name) - { + public Property GetProp(string name) { return globals.FirstOrDefault(prop => prop.KeyName == name); } @@ -106,40 +89,33 @@ public Property GetProp(string name) public void SetAllProps(List props) => globals = props; - public IServerConfiguration GetServerInfoByName(string serverName) - { + public IServerConfiguration GetServerInfoByName(string serverName) { return ServerList.FirstOrDefault(info => info.GetServerName() == serverName); } - public IServerConfiguration GetServerInfoByIndex(int index) - { + public IServerConfiguration GetServerInfoByIndex(int index) { return ServerList[index]; } - public void RemoveServerInfo(IServerConfiguration serverConfiguration) - { + public void RemoveServerInfo(IServerConfiguration serverConfiguration) { ServerList.Remove(serverConfiguration.GetServerInfo()); } - public void SetServerInfo(IServerConfiguration newInfo) - { + public void SetServerInfo(IServerConfiguration newInfo) { ServerList[ServerList.IndexOf(newInfo.GetServerInfo())] = newInfo.GetServerInfo(); } public List GetServerList() => ServerList; - public void SetAllServerInfos(List newInfos) - { + public void SetAllServerInfos(List newInfos) { ServerList = newInfos; } - public void AddNewServerInfo(IServerConfiguration serverConfiguration) - { + public void AddNewServerInfo(IServerConfiguration serverConfiguration) { ServerList.Add(serverConfiguration); } - public void RemoveServerInfoByIndex(int serverIndex) - { + public void RemoveServerInfoByIndex(int serverIndex) { ServerList.RemoveAt(serverIndex); } diff --git a/BedrockService/BedrockService.Shared/Classes/ServiceProcessInfo.cs b/BedrockService/BedrockService.Shared/Classes/ServiceProcessInfo.cs index 9f9e0645..ba42731e 100644 --- a/BedrockService/BedrockService.Shared/Classes/ServiceProcessInfo.cs +++ b/BedrockService/BedrockService.Shared/Classes/ServiceProcessInfo.cs @@ -1,17 +1,14 @@ using BedrockService.Shared.Interfaces; -namespace BedrockService.Shared.Classes -{ - public class ServiceProcessInfo : IProcessInfo - { +namespace BedrockService.Shared.Classes { + public class ServiceProcessInfo : IProcessInfo { private readonly string _serviceDirectory; private readonly string _serviceExeName; private readonly int _processPid; private bool _debugEnabled; private bool _isConsoleMode; - public ServiceProcessInfo(string serviceDirectory, string serviceExeName, int processPid, bool debugEnabled, bool isConsoleMode) - { + public ServiceProcessInfo(string serviceDirectory, string serviceExeName, int processPid, bool debugEnabled, bool isConsoleMode) { _serviceDirectory = serviceDirectory; _serviceExeName = serviceExeName; _processPid = processPid; @@ -29,8 +26,7 @@ public ServiceProcessInfo(string serviceDirectory, string serviceExeName, int pr public bool IsConsoleMode() => _isConsoleMode; - public void SetArguments(bool isDebug, bool isConsole) - { + public void SetArguments(bool isDebug, bool isConsole) { _debugEnabled = isDebug; _isConsoleMode = isConsole; } diff --git a/BedrockService/BedrockService.Shared/Classes/StartCmdEntry.cs b/BedrockService/BedrockService.Shared/Classes/StartCmdEntry.cs index 5200e2d8..554c2c2a 100644 --- a/BedrockService/BedrockService.Shared/Classes/StartCmdEntry.cs +++ b/BedrockService/BedrockService.Shared/Classes/StartCmdEntry.cs @@ -1,11 +1,8 @@ -namespace BedrockService.Shared.Classes -{ - public class StartCmdEntry - { +namespace BedrockService.Shared.Classes { + public class StartCmdEntry { public string Command = "help 1"; - public StartCmdEntry(string entry) - { + public StartCmdEntry(string entry) { Command = entry; } } diff --git a/BedrockService/BedrockService.Shared/Interfaces/IBedrockConfiguration.cs b/BedrockService/BedrockService.Shared/Interfaces/IBedrockConfiguration.cs index 98c64609..346a0efa 100644 --- a/BedrockService/BedrockService.Shared/Interfaces/IBedrockConfiguration.cs +++ b/BedrockService/BedrockService.Shared/Interfaces/IBedrockConfiguration.cs @@ -1,10 +1,8 @@ using BedrockService.Shared.Classes; using System.Collections.Generic; -namespace BedrockService.Shared.Interfaces -{ - public interface IBedrockConfiguration - { +namespace BedrockService.Shared.Interfaces { + public interface IBedrockConfiguration { void InitializeDefaults(); diff --git a/BedrockService/BedrockService.Shared/Interfaces/IBedrockLogger.cs b/BedrockService/BedrockService.Shared/Interfaces/IBedrockLogger.cs index 2d3706f6..40c443d8 100644 --- a/BedrockService/BedrockService.Shared/Interfaces/IBedrockLogger.cs +++ b/BedrockService/BedrockService.Shared/Interfaces/IBedrockLogger.cs @@ -1,7 +1,5 @@ -namespace BedrockService.Shared.Interfaces -{ - public interface IBedrockLogger - { +namespace BedrockService.Shared.Interfaces { + public interface IBedrockLogger { void AppendLine(string incomingText); void AppendText(string incomingText); int Count(); diff --git a/BedrockService/BedrockService.Shared/Interfaces/IClientSideServiceConfiguration.cs b/BedrockService/BedrockService.Shared/Interfaces/IClientSideServiceConfiguration.cs index 4d4c8f7a..9f1907d4 100644 --- a/BedrockService/BedrockService.Shared/Interfaces/IClientSideServiceConfiguration.cs +++ b/BedrockService/BedrockService.Shared/Interfaces/IClientSideServiceConfiguration.cs @@ -1,7 +1,5 @@ -namespace BedrockService.Shared.Interfaces -{ - public interface IClientSideServiceConfiguration - { +namespace BedrockService.Shared.Interfaces { + public interface IClientSideServiceConfiguration { string GetAddress(); string GetDisplayName(); string GetHostName(); diff --git a/BedrockService/BedrockService.Shared/Interfaces/IPlayer.cs b/BedrockService/BedrockService.Shared/Interfaces/IPlayer.cs index aa7c93ae..28315449 100644 --- a/BedrockService/BedrockService.Shared/Interfaces/IPlayer.cs +++ b/BedrockService/BedrockService.Shared/Interfaces/IPlayer.cs @@ -1,7 +1,5 @@ -namespace BedrockService.Shared.Interfaces -{ - public interface IPlayer - { +namespace BedrockService.Shared.Interfaces { + public interface IPlayer { void Initialize(string xuid, string username); void UpdateTimes(string lastConn, string lastDiscon); void UpdateRegistration(string permission, string whitelisted, string ignoreMaxPlayerLimit); diff --git a/BedrockService/BedrockService.Shared/Interfaces/IProcessInfo.cs b/BedrockService/BedrockService.Shared/Interfaces/IProcessInfo.cs index b6af2a12..0592dae7 100644 --- a/BedrockService/BedrockService.Shared/Interfaces/IProcessInfo.cs +++ b/BedrockService/BedrockService.Shared/Interfaces/IProcessInfo.cs @@ -1,7 +1,5 @@ -namespace BedrockService.Shared.Interfaces -{ - public interface IProcessInfo - { +namespace BedrockService.Shared.Interfaces { + public interface IProcessInfo { string GetDirectory(); string GetExecutableName(); diff --git a/BedrockService/BedrockService.Shared/Interfaces/IServerConfiguration.cs b/BedrockService/BedrockService.Shared/Interfaces/IServerConfiguration.cs index 5ddf71dd..6b6c43e0 100644 --- a/BedrockService/BedrockService.Shared/Interfaces/IServerConfiguration.cs +++ b/BedrockService/BedrockService.Shared/Interfaces/IServerConfiguration.cs @@ -1,10 +1,8 @@ using BedrockService.Shared.Classes; using System.Collections.Generic; -namespace BedrockService.Shared.Interfaces -{ - public interface IServerConfiguration : IBedrockConfiguration - { +namespace BedrockService.Shared.Interfaces { + public interface IServerConfiguration : IBedrockConfiguration { string GetServerName(); string GetFileName(); diff --git a/BedrockService/BedrockService.Shared/Interfaces/IServiceConfiguration.cs b/BedrockService/BedrockService.Shared/Interfaces/IServiceConfiguration.cs index 794f9e10..a86ee787 100644 --- a/BedrockService/BedrockService.Shared/Interfaces/IServiceConfiguration.cs +++ b/BedrockService/BedrockService.Shared/Interfaces/IServiceConfiguration.cs @@ -1,9 +1,7 @@ using System.Collections.Generic; -namespace BedrockService.Shared.Interfaces -{ - public interface IServiceConfiguration : IBedrockConfiguration - { +namespace BedrockService.Shared.Interfaces { + public interface IServiceConfiguration : IBedrockConfiguration { IServerConfiguration GetServerInfoByName(string serverName); IServerConfiguration GetServerInfoByIndex(int index); diff --git a/BedrockService/BedrockService.Shared/MincraftJson/KnownPacksJsonModel.cs b/BedrockService/BedrockService.Shared/MincraftJson/KnownPacksJsonModel.cs index 74925003..5d076e90 100644 --- a/BedrockService/BedrockService.Shared/MincraftJson/KnownPacksJsonModel.cs +++ b/BedrockService/BedrockService.Shared/MincraftJson/KnownPacksJsonModel.cs @@ -1,11 +1,7 @@ -using System; -using System.Collections.Generic; -using System.Text; +using System.Collections.Generic; -namespace BedrockService.Shared.MincraftJson -{ - internal class KnownPacksJsonModel - { +namespace BedrockService.Shared.MincraftJson { + internal class KnownPacksJsonModel { public int file_version { get; set; } public string file_system { get; set; } public bool? from_disk { get; set; } @@ -14,8 +10,7 @@ internal class KnownPacksJsonModel public string uuid { get; set; } public string version { get; set; } - public override string ToString() - { + public override string ToString() { return path; } } diff --git a/BedrockService/BedrockService.Shared/MincraftJson/PermissionsJsonModel.cs b/BedrockService/BedrockService.Shared/MincraftJson/PermissionsJsonModel.cs index 9e16aaf8..ddfff9a9 100644 --- a/BedrockService/BedrockService.Shared/MincraftJson/PermissionsJsonModel.cs +++ b/BedrockService/BedrockService.Shared/MincraftJson/PermissionsJsonModel.cs @@ -1,17 +1,9 @@ -using System; -using BedrockService.Shared.Classes; -using System.Collections.Generic; -using System.Text; - -namespace BedrockService.Shared.MincraftJson -{ - internal class PermissionsJsonModel - { +namespace BedrockService.Shared.MincraftJson { + internal class PermissionsJsonModel { public string permission { get; set; } public string xuid { get; set; } - public PermissionsJsonModel(string permission, string xuid) - { + public PermissionsJsonModel(string permission, string xuid) { this.permission = permission; this.xuid = xuid; } diff --git a/BedrockService/BedrockService.Shared/MincraftJson/WhitelistJsonModel.cs b/BedrockService/BedrockService.Shared/MincraftJson/WhitelistJsonModel.cs index d0ad61b9..70bc67eb 100644 --- a/BedrockService/BedrockService.Shared/MincraftJson/WhitelistJsonModel.cs +++ b/BedrockService/BedrockService.Shared/MincraftJson/WhitelistJsonModel.cs @@ -1,17 +1,10 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace BedrockService.Shared.MincraftJson -{ - internal class WhitelistJsonModel - { +namespace BedrockService.Shared.MincraftJson { + internal class WhitelistJsonModel { public bool ignoresPlayerLimit { get; set; } public string permission { get; set; } public string xuid { get; set; } - public WhitelistJsonModel (bool IgnoreLimits, string XUID, string Permission) - { + public WhitelistJsonModel(bool IgnoreLimits, string XUID, string Permission) { ignoresPlayerLimit = IgnoreLimits; xuid = XUID; permission = Permission; diff --git a/BedrockService/BedrockService.Shared/MincraftJson/WorldPacksJsonModel.cs b/BedrockService/BedrockService.Shared/MincraftJson/WorldPacksJsonModel.cs index 1b9e4976..1b371b05 100644 --- a/BedrockService/BedrockService.Shared/MincraftJson/WorldPacksJsonModel.cs +++ b/BedrockService/BedrockService.Shared/MincraftJson/WorldPacksJsonModel.cs @@ -1,16 +1,11 @@ -using System; -using System.Collections.Generic; -using System.Text; +using System.Collections.Generic; -namespace BedrockService.Shared.MincraftJson -{ - public class WorldPacksJsonModel - { +namespace BedrockService.Shared.MincraftJson { + public class WorldPacksJsonModel { public string pack_id { get; set; } public List version { get; set; } - public WorldPacksJsonModel(string id, List ver) - { + public WorldPacksJsonModel(string id, List ver) { pack_id = id; version = ver; } diff --git a/BedrockService/BedrockService.Shared/PackParser/MinecraftKnownPacksClass.cs b/BedrockService/BedrockService.Shared/PackParser/MinecraftKnownPacksClass.cs index 68f1dfc8..0c197cb3 100644 --- a/BedrockService/BedrockService.Shared/PackParser/MinecraftKnownPacksClass.cs +++ b/BedrockService/BedrockService.Shared/PackParser/MinecraftKnownPacksClass.cs @@ -3,14 +3,11 @@ using System.IO; using System.Linq; -namespace BedrockService.Shared.PackParser -{ - public class MinecraftKnownPacksClass - { +namespace BedrockService.Shared.PackParser { + public class MinecraftKnownPacksClass { public List KnownPacks = new List(); private readonly List _stockPacks = new List(); - public class KnownPack - { + public class KnownPack { public int file_version { get; set; } public string file_system { get; set; } public bool? from_disk { get; set; } @@ -19,19 +16,16 @@ public class KnownPack public string uuid { get; set; } public string version { get; set; } - public override string ToString() - { + public override string ToString() { return path; } } - public MinecraftKnownPacksClass(string serverFile, string stockFile) - { + public MinecraftKnownPacksClass(string serverFile, string stockFile) { KnownPacks = ParseJsonArray(serverFile); _stockPacks = ParseJsonArray(stockFile); KnownPacks.RemoveAt(0); // Strip file version entry. - foreach (KnownPack pack in _stockPacks) - { + foreach (KnownPack pack in _stockPacks) { KnownPack packToRemove = new KnownPack(); if (pack.uuid == null) continue; @@ -40,8 +34,7 @@ public MinecraftKnownPacksClass(string serverFile, string stockFile) } } - public void RemovePackFromServer(string serverPath, MinecraftPackContainer pack) - { + public void RemovePackFromServer(string serverPath, MinecraftPackContainer pack) { if (pack.ManifestType == "WorldPack") Directory.Delete($@"{serverPath}\worlds\{pack.FolderName}", true); if (pack.ManifestType == "data") @@ -55,8 +48,7 @@ public void RemovePackFromServer(string serverPath, MinecraftPackContainer pack) File.WriteAllText($@"{serverPath}\valid_known_packs.json", JArray.FromObject(packsToWrite).ToString()); } - private List ParseJsonArray(string jsonFile) - { + private List ParseJsonArray(string jsonFile) { JArray packList = JArray.Parse(File.ReadAllText(jsonFile)); List parsedPackInfos = new List(); foreach (JToken token in packList) diff --git a/BedrockService/BedrockService.Shared/PackParser/MinecraftPackParser.cs b/BedrockService/BedrockService.Shared/PackParser/MinecraftPackParser.cs index bb50152d..c799f8ac 100644 --- a/BedrockService/BedrockService.Shared/PackParser/MinecraftPackParser.cs +++ b/BedrockService/BedrockService.Shared/PackParser/MinecraftPackParser.cs @@ -5,101 +5,84 @@ using System.IO; using System.IO.Compression; -namespace BedrockService.Shared.PackParser -{ - public class Header - { +namespace BedrockService.Shared.PackParser { + public class Header { public string description { get; set; } public string name { get; set; } public string uuid { get; set; } public List version { get; set; } } - public class Module - { + public class Module { public string description { get; set; } public string type { get; set; } public string uuid { get; set; } public List version { get; set; } } - public class Dependency - { + public class Dependency { public string uuid { get; set; } public List version { get; set; } } - public class Manifest - { + public class Manifest { public int format_version { get; set; } public Header header { get; set; } public List modules { get; set; } public List dependencies { get; set; } } - public class MinecraftPackContainer - { + public class MinecraftPackContainer { public Manifest JsonManifest; public string PackContentLocation; public string ManifestType; public string FolderName; public byte[] IconBytes; - public override string ToString() - { + public override string ToString() { return JsonManifest != null ? JsonManifest.header.name : "WorldPack"; } } - public class MinecraftPackParser - { + public class MinecraftPackParser { private readonly IProcessInfo _processInfo; private readonly IBedrockLogger _logger; public string PackExtractDirectory; public List FoundPacks = new List(); [JsonConstructor] - public MinecraftPackParser(IBedrockLogger logger, IProcessInfo processInfo) - { + public MinecraftPackParser(IBedrockLogger logger, IProcessInfo processInfo) { _processInfo = processInfo; _logger = logger; } - public MinecraftPackParser(byte[] fileContents, IBedrockLogger logger, IProcessInfo processInfo) - { + public MinecraftPackParser(byte[] fileContents, IBedrockLogger logger, IProcessInfo processInfo) { _logger = logger; PackExtractDirectory = $@"{processInfo.GetDirectory()}\Temp"; _processInfo = processInfo; new FileUtils(processInfo.GetDirectory()).ClearTempDir(); - using (MemoryStream fileStream = new MemoryStream(fileContents, 5, fileContents.Length - 5)) - { - using (ZipArchive zipArchive = new ZipArchive(fileStream, ZipArchiveMode.Read)) - { + using (MemoryStream fileStream = new MemoryStream(fileContents, 5, fileContents.Length - 5)) { + using (ZipArchive zipArchive = new ZipArchive(fileStream, ZipArchiveMode.Read)) { zipArchive.ExtractToDirectory(PackExtractDirectory); } } ParseDirectory(PackExtractDirectory); } - public MinecraftPackParser(string[] files, string extractDir, IBedrockLogger logger, IProcessInfo processInfo) - { + public MinecraftPackParser(string[] files, string extractDir, IBedrockLogger logger, IProcessInfo processInfo) { PackExtractDirectory = extractDir; _logger = logger; _processInfo = processInfo; new FileUtils(_processInfo.GetDirectory()).ClearTempDir(); - if (Directory.Exists($@"{PackExtractDirectory}\ZipTemp")) - { + if (Directory.Exists($@"{PackExtractDirectory}\ZipTemp")) { Directory.CreateDirectory($@"{PackExtractDirectory}\ZipTemp"); } - foreach (string file in files) - { + foreach (string file in files) { FileInfo fInfo = new FileInfo(file); string zipFilePath = $@"{PackExtractDirectory}\{fInfo.Name.Replace(fInfo.Extension, "")}"; ZipFile.ExtractToDirectory(file, zipFilePath); - foreach (FileInfo extractedFile in new DirectoryInfo(zipFilePath).GetFiles()) - { - if (extractedFile.Extension == ".mcpack") - { + foreach (FileInfo extractedFile in new DirectoryInfo(zipFilePath).GetFiles()) { + if (extractedFile.Extension == ".mcpack") { Directory.CreateDirectory($@"{zipFilePath}\{extractedFile.Name.Replace(extractedFile.Extension, "")}"); ZipFile.ExtractToDirectory(extractedFile.FullName, $@"{zipFilePath}\{fInfo.Name.Replace(fInfo.Extension, "")}_{extractedFile.Name.Replace(extractedFile.Extension, "")}"); } @@ -109,31 +92,24 @@ public MinecraftPackParser(string[] files, string extractDir, IBedrockLogger log ParseDirectory(PackExtractDirectory); } - public void ParseDirectory(string directoryToParse) - { + public void ParseDirectory(string directoryToParse) { DirectoryInfo directoryInfo = new DirectoryInfo(directoryToParse); - if (directoryInfo.Exists) - { + if (directoryInfo.Exists) { _logger.AppendLine($"Parsing directory {directoryInfo.Name}"); - foreach (FileInfo file in directoryInfo.GetFiles("*", SearchOption.AllDirectories)) - { - if (file.Name == "levelname.txt") - { + foreach (FileInfo file in directoryInfo.GetFiles("*", SearchOption.AllDirectories)) { + if (file.Name == "levelname.txt") { byte[] iconBytes; - try - { + try { if (File.Exists($@"{file.Directory.FullName}\world_icon.jpeg")) iconBytes = File.ReadAllBytes($@"{file.Directory.FullName}\world_icon.jpeg"); else iconBytes = File.ReadAllBytes($@"{file.Directory.FullName}\world_icon.png"); } - catch - { + catch { iconBytes = null; } - FoundPacks.Add(new MinecraftPackContainer - { + FoundPacks.Add(new MinecraftPackContainer { JsonManifest = null, PackContentLocation = file.Directory.FullName, ManifestType = "WorldPack", @@ -143,24 +119,21 @@ public void ParseDirectory(string directoryToParse) _logger.AppendLine("Pack was detected as MCWorld"); return; } - if (file.Name == "manifest.json") - { + if (file.Name == "manifest.json") { byte[] iconBytes; if (File.Exists($@"{file.Directory.FullName}\pack_icon.jpeg")) iconBytes = File.ReadAllBytes($@"{file.Directory.FullName}\pack_icon.jpeg"); else iconBytes = File.ReadAllBytes($@"{file.Directory.FullName}\pack_icon.png"); - MinecraftPackContainer container = new MinecraftPackContainer - { + MinecraftPackContainer container = new MinecraftPackContainer { JsonManifest = new Manifest(), PackContentLocation = file.Directory.FullName, ManifestType = "", FolderName = file.Directory.Name, IconBytes = iconBytes }; - JsonSerializerSettings settings = new JsonSerializerSettings() - { + JsonSerializerSettings settings = new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All }; container.JsonManifest = JsonConvert.DeserializeObject(File.ReadAllText(file.FullName), settings); diff --git a/BedrockService/BedrockService.Shared/Utilities/FileUtils.cs b/BedrockService/BedrockService.Shared/Utilities/FileUtils.cs index bdf518b4..bf0044b7 100644 --- a/BedrockService/BedrockService.Shared/Utilities/FileUtils.cs +++ b/BedrockService/BedrockService.Shared/Utilities/FileUtils.cs @@ -6,25 +6,20 @@ using System.Linq; using System.Threading.Tasks; -namespace BedrockService.Shared.Utilities -{ - public class FileUtils - { +namespace BedrockService.Shared.Utilities { + public class FileUtils { readonly string _servicePath; - public FileUtils(string servicePath) - { + public FileUtils(string servicePath) { _servicePath = servicePath; } - public void CopyFilesRecursively(DirectoryInfo source, DirectoryInfo target) - { + public void CopyFilesRecursively(DirectoryInfo source, DirectoryInfo target) { foreach (DirectoryInfo dir in source.GetDirectories()) CopyFilesRecursively(dir, target.CreateSubdirectory(dir.Name)); foreach (FileInfo file in source.GetFiles()) file.CopyTo(Path.Combine(target.FullName, file.Name), true); } - public void DeleteFilesRecursively(DirectoryInfo source, bool removeSourceFolder) - { + public void DeleteFilesRecursively(DirectoryInfo source, bool removeSourceFolder) { foreach (DirectoryInfo dir in source.GetDirectories()) DeleteFilesRecursively(dir, removeSourceFolder); foreach (FileInfo file in source.GetFiles()) @@ -35,8 +30,7 @@ public void DeleteFilesRecursively(DirectoryInfo source, bool removeSourceFolder source.Delete(true); } - public void ClearTempDir() - { + public void ClearTempDir() { DirectoryInfo tempDirectory = new DirectoryInfo($@"{_servicePath}\Temp"); if (!tempDirectory.Exists) tempDirectory.Create(); @@ -46,11 +40,9 @@ public void ClearTempDir() directory.Delete(true); } - public void DeleteFilelist(string[] fileList, string serverPath) - { + public void DeleteFilelist(string[] fileList, string serverPath) { foreach (string file in fileList) - try - { + try { File.Delete($@"{serverPath}\{file}"); } catch { } @@ -66,59 +58,45 @@ public void DeleteFilelist(string[] fileList, string serverPath) public void WriteStringArrayToFile(string path, string[] content) => File.WriteAllLines(path, content); - public void UpdateJArrayFile(string path, JArray content, System.Type type) - { - try - { + public void UpdateJArrayFile(string path, JArray content, System.Type type) { + try { string currentFileContents = null; JArray jArray = new JArray(); - if (File.Exists(path)) - { + if (File.Exists(path)) { currentFileContents = File.ReadAllText(path); jArray = JArray.Parse(currentFileContents); } - if (type == typeof(WorldPacksJsonModel)) - { - foreach (JToken jToken in content) - { + if (type == typeof(WorldPacksJsonModel)) { + foreach (JToken jToken in content) { bool doesContainToken = false; - foreach (JToken currentToken in jArray) - { - if (currentToken.ToObject().pack_id == jToken.ToObject().pack_id) - { + foreach (JToken currentToken in jArray) { + if (currentToken.ToObject().pack_id == jToken.ToObject().pack_id) { doesContainToken = true; } } - if (!doesContainToken) - { + if (!doesContainToken) { jArray.Add(jToken); } } } File.WriteAllText(path, jArray.ToString()); } - catch (System.Exception) - { + catch (System.Exception) { return; } } - public Task BackupWorldFilesFromQuery(Dictionary fileNameSizePairs, string worldPath, string destinationPath) - { - return Task.Run(() => - { - try - { - foreach (KeyValuePair file in fileNameSizePairs) - { + public Task BackupWorldFilesFromQuery(Dictionary fileNameSizePairs, string worldPath, string destinationPath) { + return Task.Run(() => { + try { + foreach (KeyValuePair file in fileNameSizePairs) { string fileName = file.Key.Replace('/', '\\'); int fileSize = file.Value; string filePath = $@"{worldPath}\{fileName}"; string destFilePath = $@"{destinationPath}\{fileName}"; byte[] fileData = null; using (FileStream fs = File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) - using (MemoryStream ms = new MemoryStream()) - { + using (MemoryStream ms = new MemoryStream()) { fs.CopyTo(ms); ms.Position = 0; fileData = ms.ToArray(); @@ -130,8 +108,7 @@ public Task BackupWorldFilesFromQuery(Dictionary fileNameSize } return true; } - catch (System.Exception ex) - { + catch (System.Exception ex) { Console.WriteLine($"Error! {ex.Message}"); } return false; diff --git a/BedrockService/Client/Forms/AddNewServerForm.cs b/BedrockService/Client/Forms/AddNewServerForm.cs index cd9b4fe0..9cd89859 100644 --- a/BedrockService/Client/Forms/AddNewServerForm.cs +++ b/BedrockService/Client/Forms/AddNewServerForm.cs @@ -5,15 +5,12 @@ using System.Linq; using System.Windows.Forms; -namespace BedrockService.Client.Forms -{ - public partial class AddNewServerForm : Form - { +namespace BedrockService.Client.Forms { + public partial class AddNewServerForm : Form { public List DefaultProps = new List(); private readonly List serverConfigurations; private readonly IClientSideServiceConfiguration serviceConfiguration; - public AddNewServerForm(IClientSideServiceConfiguration serviceConfiguration, List serverConfigurations) - { + public AddNewServerForm(IClientSideServiceConfiguration serviceConfiguration, List serverConfigurations) { this.serviceConfiguration = serviceConfiguration; this.serverConfigurations = serverConfigurations; InitializeComponent(); @@ -22,8 +19,7 @@ public AddNewServerForm(IClientSideServiceConfiguration serviceConfiguration, Li DefaultProps = server.GetAllProps(); } - private void editPropsBtn_Click(object sender, System.EventArgs e) - { + private void editPropsBtn_Click(object sender, System.EventArgs e) { PropEditorForm editSrvDialog = new PropEditorForm(); if (srvNameBox.TextLength > 0) DefaultProps.First(prop => prop.KeyName == "server-name").Value = srvNameBox.Text; @@ -32,27 +28,22 @@ private void editPropsBtn_Click(object sender, System.EventArgs e) if (ipV6Box.TextLength > 0) DefaultProps.First(prop => prop.KeyName == "server-portv6").Value = ipV6Box.Text; editSrvDialog.PopulateBoxes(DefaultProps); - if (editSrvDialog.ShowDialog() == DialogResult.OK) - { + if (editSrvDialog.ShowDialog() == DialogResult.OK) { DefaultProps = editSrvDialog.workingProps; editSrvDialog.Close(); editSrvDialog.Dispose(); } } - private void saveBtn_Click(object sender, System.EventArgs e) - { + private void saveBtn_Click(object sender, System.EventArgs e) { List usedPorts = new List(); usedPorts.Add(serviceConfiguration.GetPort()); - foreach (IServerConfiguration serverConfiguration in serverConfigurations) - { + foreach (IServerConfiguration serverConfiguration in serverConfigurations) { usedPorts.Add(serverConfiguration.GetProp("server-port").ToString()); usedPorts.Add(serverConfiguration.GetProp("server-portv6").ToString()); } - foreach (string port in usedPorts) - { - if (ipV4Box.Text == port || ipV6Box.Text == port) - { + foreach (string port in usedPorts) { + if (ipV4Box.Text == port || ipV6Box.Text == port) { MessageBox.Show($"You have selected port {port} to use, but this port is already used. Please select another port!"); return; } diff --git a/BedrockService/Client/Forms/ClientConfigForm.cs b/BedrockService/Client/Forms/ClientConfigForm.cs index 57b7d641..716e450f 100644 --- a/BedrockService/Client/Forms/ClientConfigForm.cs +++ b/BedrockService/Client/Forms/ClientConfigForm.cs @@ -5,54 +5,42 @@ using System.Collections.Generic; using System.Windows.Forms; -namespace BedrockService.Client.Forms -{ - public partial class ClientConfigForm : Form - { +namespace BedrockService.Client.Forms { + public partial class ClientConfigForm : Form { private readonly List _clientConfigs; private readonly ConfigManager _configManager; - public ClientConfigForm(ConfigManager configManager) - { + public ClientConfigForm(ConfigManager configManager) { InitializeComponent(); _configManager = configManager; _clientConfigs = _configManager.HostConnectList; - if (!string.IsNullOrEmpty(_configManager.NBTStudioPath)) - { + if (!string.IsNullOrEmpty(_configManager.NBTStudioPath)) { nbtPathLabel.Text = $"NBT Studio path: {_configManager.NBTStudioPath}"; } - foreach (IClientSideServiceConfiguration config in _clientConfigs) - { + foreach (IClientSideServiceConfiguration config in _clientConfigs) { serverGridView.Rows.Add(new string[3] { config.GetHostName(), config.GetAddress(), config.GetPort() }); } } - public void SimulateTests() - { + public void SimulateTests() { nbtButton.PerformClick(); } - private void nbtButton_Click(object sender, EventArgs e) - { - using (OpenFileDialog fileDialog = new OpenFileDialog()) - { + private void nbtButton_Click(object sender, EventArgs e) { + using (OpenFileDialog fileDialog = new OpenFileDialog()) { fileDialog.Filter = "EXE Files|*.exe"; fileDialog.FileName = "NbtStudio.exe"; fileDialog.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Desktop); - if (fileDialog.ShowDialog() == DialogResult.OK) - { + if (fileDialog.ShowDialog() == DialogResult.OK) { _configManager.NBTStudioPath = fileDialog.FileName; nbtPathLabel.Text = $"NBT Studio path: {_configManager.NBTStudioPath}"; } } } - private void saveBtn_Click(object sender, EventArgs e) - { + private void saveBtn_Click(object sender, EventArgs e) { List newConfigs = new List(); - foreach (DataGridViewRow row in serverGridView.Rows) - { - if (!string.IsNullOrEmpty((string)row.Cells[0].Value)) - { + foreach (DataGridViewRow row in serverGridView.Rows) { + if (!string.IsNullOrEmpty((string)row.Cells[0].Value)) { newConfigs.Add(new ClientSideServiceConfiguration((string)row.Cells[0].Value, (string)row.Cells[1].Value, (string)row.Cells[2].Value)); } } diff --git a/BedrockService/Client/Forms/MainWindow.cs b/BedrockService/Client/Forms/MainWindow.cs index 27ef4bda..2137b3b5 100644 --- a/BedrockService/Client/Forms/MainWindow.cs +++ b/BedrockService/Client/Forms/MainWindow.cs @@ -13,10 +13,8 @@ using System.Timers; using System.Windows.Forms; -namespace BedrockService.Client.Forms -{ - public partial class MainWindow : Form - { +namespace BedrockService.Client.Forms { + public partial class MainWindow : Form { public IServiceConfiguration connectedHost; public IServerConfiguration selectedServer; public IClientSideServiceConfiguration clientSideServiceConfiguration; @@ -31,8 +29,7 @@ public partial class MainWindow : Form private readonly System.Timers.Timer _connectTimer = new System.Timers.Timer(100.0); private readonly LogManager _logManager; private readonly ConfigManager _configManager; - public MainWindow(IProcessInfo processInfo, IBedrockLogger logger) - { + public MainWindow(IProcessInfo processInfo, IBedrockLogger logger) { _processInfo = processInfo; _logger = logger; _logManager = new LogManager(_logger); @@ -43,15 +40,12 @@ public MainWindow(IProcessInfo processInfo, IBedrockLogger logger) _connectTimer.Elapsed += ConnectTimer_Elapsed; } - private void ConnectTimer_Elapsed(object sender, ElapsedEventArgs e) - { - if (_connectTimer.Enabled && !FormManager.TCPClient.EstablishedLink) - { + private void ConnectTimer_Elapsed(object sender, ElapsedEventArgs e) { + if (_connectTimer.Enabled && !FormManager.TCPClient.EstablishedLink) { _connectTimer.Interval = 2000.0; Invoke((MethodInvoker)delegate { FormManager.TCPClient.ConnectHost(_configManager.HostConnectList.FirstOrDefault(host => host.GetHostName() == (string)HostListBox.SelectedItem)); }); Thread.Sleep(500); - if (connectedHost != null && FormManager.TCPClient.EstablishedLink) - { + if (connectedHost != null && FormManager.TCPClient.EstablishedLink) { ServerBusy = false; Invoke((MethodInvoker)delegate { ComponentEnableManager(); }); _connectTimer.Enabled = false; @@ -60,13 +54,11 @@ private void ConnectTimer_Elapsed(object sender, ElapsedEventArgs e) return; } _connectTimeout++; - if (_connectTimeout >= _connectTimeoutLimit) - { + if (_connectTimeout >= _connectTimeoutLimit) { _connectTimer.Enabled = false; _connectTimer.Stop(); _connectTimer.Close(); - Invoke((MethodInvoker)delegate - { + Invoke((MethodInvoker)delegate { RefreshServerContents(); HostInfoLabel.Text = $"Failed to connect to host!"; Connect.Enabled = true; @@ -91,8 +83,7 @@ private void ConnectTimer_Elapsed(object sender, ElapsedEventArgs e) [DllImport("user32.dll")] static extern IntPtr SendMessage(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam); - struct ScrollInfo - { + struct ScrollInfo { public uint Size; public uint Mask; public int Min; @@ -102,8 +93,7 @@ struct ScrollInfo public int TrackPos; } - enum ScrollInfoMask - { + enum ScrollInfoMask { Range = 0x1, Page = 0x2, Pos = 0x4, @@ -112,8 +102,7 @@ enum ScrollInfoMask All = Range + Page + Pos + TrackPos } - enum ScrollBarDirection - { + enum ScrollBarDirection { Horizontal = 0, Vertical = 1, Ctl = 2, @@ -140,34 +129,28 @@ enum ScrollBarDirection [STAThread] - public static void Main() - { + public static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); Application.ApplicationExit += OnExit; Application.Run(FormManager.MainWindow); } - public void RefreshServerContents() - { - Invoke((MethodInvoker)delegate - { + public void RefreshServerContents() { + Invoke((MethodInvoker)delegate { Refresh(); return; }); } - public override void Refresh() - { + public override void Refresh() { HostInfoLabel.Text = $"Connected to host:"; ServerSelectBox.Items.Clear(); - if (connectedHost != null) - { + if (connectedHost != null) { _logManager.InitLogThread(connectedHost); foreach (ServerInfo server in connectedHost.GetServerList()) ServerSelectBox.Items.Add(server.ServerName); - if (ServerSelectBox.Items.Count > 0) - { + if (ServerSelectBox.Items.Count > 0) { ServerSelectBox.SelectedIndex = 0; selectedServer = connectedHost.GetServerInfoByName((string)ServerSelectBox.SelectedItem); } @@ -176,27 +159,22 @@ public override void Refresh() base.Refresh(); } - public void InitForm() - { + public void InitForm() { _configManager.LoadConfigs(); HostListBox.Items.Clear(); - foreach (IClientSideServiceConfiguration host in _configManager.HostConnectList) - { + foreach (IClientSideServiceConfiguration host in _configManager.HostConnectList) { HostListBox.Items.Add(host.GetHostName()); } - if (HostListBox.Items.Count > 0) - { + if (HostListBox.Items.Count > 0) { HostListBox.SelectedIndex = 0; } HostListBox.Refresh(); FormClosing += MainWindow_FormClosing; } - public void HeartbeatFailDisconnect() - { + public void HeartbeatFailDisconnect() { Disconn_Click(null, null); - try - { + try { HostInfoLabel.Invoke((MethodInvoker)delegate { HostInfoLabel.Text = "Lost connection to host!"; }); ServerInfoBox.Invoke((MethodInvoker)delegate { ServerInfoBox.Text = "Lost connection to host!"; }); ServerBusy = false; @@ -208,8 +186,7 @@ public void HeartbeatFailDisconnect() connectedHost = null; } - public void UpdateLogBox(string contents) - { + public void UpdateLogBox(string contents) { int curPos = HorizontalScrollPosition; LogBox.Text = contents; HorizontalScrollPosition = curPos; @@ -217,21 +194,17 @@ public void UpdateLogBox(string contents) ScrollToEnd(); } - private static void OnExit(object sender, EventArgs e) - { + private static void OnExit(object sender, EventArgs e) { FormManager.TCPClient.Dispose(); } - private void SvcLog_CheckedChanged(object sender, EventArgs e) - { + private void SvcLog_CheckedChanged(object sender, EventArgs e) { ShowsSvcLog = SvcLog.Checked; } - private void MainWindow_FormClosing(object sender, FormClosingEventArgs e) - { + private void MainWindow_FormClosing(object sender, FormClosingEventArgs e) { _logger.AppendLine("Stopping log thread..."); - if (_logManager.StopLogThread()) - { + if (_logManager.StopLogThread()) { _logger.AppendLine("Sending disconnect msg..."); FormManager.TCPClient.SendData(NetworkMessageSource.Client, NetworkMessageDestination.Service, NetworkMessageTypes.Disconnect); _logger.AppendLine("Closing connection..."); @@ -241,8 +214,7 @@ private void MainWindow_FormClosing(object sender, FormClosingEventArgs e) } } - private void Connect_Click(object sender, EventArgs e) - { + private void Connect_Click(object sender, EventArgs e) { HostInfoLabel.Text = $"Connecting to host {(string)HostListBox.SelectedItem}..."; Connect.Enabled = false; _connectTimeout = 0; @@ -250,14 +222,10 @@ private void Connect_Click(object sender, EventArgs e) _connectTimer.Start(); } - private void ServerSelectBox_SelectedIndexChanged(object sender, EventArgs e) - { - if (connectedHost != null) - { - foreach (ServerInfo server in connectedHost.GetServerList()) - { - if (ServerSelectBox.SelectedItem != null && ServerSelectBox.SelectedItem.ToString() == server.GetServerName()) - { + private void ServerSelectBox_SelectedIndexChanged(object sender, EventArgs e) { + if (connectedHost != null) { + foreach (ServerInfo server in connectedHost.GetServerList()) { + if (ServerSelectBox.SelectedItem != null && ServerSelectBox.SelectedItem.ToString() == server.GetServerName()) { selectedServer = server; ServerInfoBox.Text = server.GetServerName(); ComponentEnableManager(); @@ -266,20 +234,16 @@ private void ServerSelectBox_SelectedIndexChanged(object sender, EventArgs e) } } - private void SingBackup_Click(object sender, EventArgs e) - { + private void SingBackup_Click(object sender, EventArgs e) { FormManager.TCPClient.SendData(NetworkMessageSource.Client, NetworkMessageDestination.Server, connectedHost.GetServerIndex(selectedServer), NetworkMessageTypes.Backup); DisableUI(); } - private void EditCfg_Click(object sender, EventArgs e) - { + private void EditCfg_Click(object sender, EventArgs e) { _editDialog = new PropEditorForm(); _editDialog.PopulateBoxes(selectedServer.GetAllProps()); - if (_editDialog.ShowDialog() == DialogResult.OK) - { - JsonSerializerSettings settings = new JsonSerializerSettings() - { + if (_editDialog.ShowDialog() == DialogResult.OK) { + JsonSerializerSettings settings = new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All }; byte[] serializeToBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(_editDialog.workingProps, Formatting.Indented, settings)); @@ -291,20 +255,16 @@ private void EditCfg_Click(object sender, EventArgs e) } } - private void RestartSrv_Click(object sender, EventArgs e) - { + private void RestartSrv_Click(object sender, EventArgs e) { FormManager.TCPClient.SendData(NetworkMessageSource.Client, NetworkMessageDestination.Server, connectedHost.GetServerIndex(selectedServer), NetworkMessageTypes.Restart); DisableUI(); } - private void EditGlobals_Click(object sender, EventArgs e) - { + private void EditGlobals_Click(object sender, EventArgs e) { _editDialog = new PropEditorForm(); _editDialog.PopulateBoxes(connectedHost.GetAllProps()); - if (_editDialog.ShowDialog() == DialogResult.OK) - { - JsonSerializerSettings settings = new JsonSerializerSettings() - { + if (_editDialog.ShowDialog() == DialogResult.OK) { + JsonSerializerSettings settings = new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All }; byte[] serializeToBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(_editDialog.workingProps, Formatting.Indented, settings)); @@ -316,18 +276,14 @@ private void EditGlobals_Click(object sender, EventArgs e) } } - private void GlobBackup_Click(object sender, EventArgs e) - { + private void GlobBackup_Click(object sender, EventArgs e) { FormManager.TCPClient.SendData(NetworkMessageSource.Client, NetworkMessageDestination.Service, NetworkMessageTypes.BackupAll); DisableUI(); } - private Task ServiceRestartFromClient() - { - return Task.Run(() => - { - Invoke(() => - { + private Task ServiceRestartFromClient() { + return Task.Run(() => { + Invoke(() => { Disconn_Click(null, null); Connect.Enabled = false; Task.Delay(6000).Wait(); @@ -336,17 +292,13 @@ private Task ServiceRestartFromClient() }); } - private void newSrvBtn_Click(object sender, EventArgs e) - { - if (clientSideServiceConfiguration == null) - { + private void newSrvBtn_Click(object sender, EventArgs e) { + if (clientSideServiceConfiguration == null) { clientSideServiceConfiguration = _configManager.HostConnectList.First(host => host.GetHostName() == HostListBox.Text); } AddNewServerForm newServerForm = new AddNewServerForm(clientSideServiceConfiguration, connectedHost.GetServerList()); - if (newServerForm.ShowDialog() == DialogResult.OK) - { - JsonSerializerSettings settings = new JsonSerializerSettings() - { + if (newServerForm.ShowDialog() == DialogResult.OK) { + JsonSerializerSettings settings = new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All }; byte[] serializeToBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(newServerForm.DefaultProps, Formatting.Indented, settings)); @@ -356,34 +308,27 @@ private void newSrvBtn_Click(object sender, EventArgs e) } } - private void RemoveSrvBtn_Click(object sender, EventArgs e) - { + private void RemoveSrvBtn_Click(object sender, EventArgs e) { FormManager.TCPClient.SendData(NetworkMessageSource.Client, NetworkMessageDestination.Service, connectedHost.GetServerIndex(selectedServer), NetworkMessageTypes.RemoveServer, NetworkMessageFlags.RemoveAll); DisableUI(); } - private void PlayerManager_Click(object sender, EventArgs e) - { + private void PlayerManager_Click(object sender, EventArgs e) { FormManager.TCPClient.SendData(NetworkMessageSource.Client, NetworkMessageDestination.Server, connectedHost.GetServerIndex(selectedServer), NetworkMessageTypes.PlayersRequest); DisableUI(); WaitForServerData().Wait(); FormManager.TCPClient.PlayerInfoArrived = false; PlayerManagerForm form = new PlayerManagerForm(selectedServer); - if (form.ShowDialog() != DialogResult.OK) - { + if (form.ShowDialog() != DialogResult.OK) { ServerBusy = false; } } - private void Disconn_Click(object sender, EventArgs e) - { + private void Disconn_Click(object sender, EventArgs e) { _connectTimer.Stop(); - if (_logManager.StopLogThread()) - { - try - { - if (FormManager.TCPClient.Connected) - { + if (_logManager.StopLogThread()) { + try { + if (FormManager.TCPClient.Connected) { FormManager.TCPClient.SendData(NetworkMessageSource.Client, NetworkMessageDestination.Service, NetworkMessageTypes.Disconnect); Thread.Sleep(500); FormManager.TCPClient.CloseConnection(); @@ -391,8 +336,7 @@ private void Disconn_Click(object sender, EventArgs e) selectedServer = null; connectedHost = null; LogBox.Invoke((MethodInvoker)delegate { LogBox.Text = ""; }); - FormManager.MainWindow.Invoke((MethodInvoker)delegate - { + FormManager.MainWindow.Invoke((MethodInvoker)delegate { ComponentEnableManager(); ServerSelectBox.Items.Clear(); ServerSelectBox.SelectedIndex = -1; @@ -405,26 +349,21 @@ private void Disconn_Click(object sender, EventArgs e) } } - private void SendCmd_Click(object sender, EventArgs e) - { - if (cmdTextBox.Text.Length > 0) - { + private void SendCmd_Click(object sender, EventArgs e) { + if (cmdTextBox.Text.Length > 0) { byte[] msg = Encoding.UTF8.GetBytes(cmdTextBox.Text); FormManager.TCPClient.SendData(msg, NetworkMessageSource.Client, NetworkMessageDestination.Server, connectedHost.GetServerIndex(selectedServer), NetworkMessageTypes.Command); } cmdTextBox.Text = ""; } - private void EditStCmd_Click(object sender, EventArgs e) - { + private void EditStCmd_Click(object sender, EventArgs e) { PropEditorForm editSrvDialog = new PropEditorForm(); editSrvDialog.PopulateStartCmds(selectedServer.GetStartCommands()); - JsonSerializerSettings settings = new JsonSerializerSettings() - { + JsonSerializerSettings settings = new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All }; - if (editSrvDialog.ShowDialog() == DialogResult.OK) - { + if (editSrvDialog.ShowDialog() == DialogResult.OK) { byte[] serializeToBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(editSrvDialog.startCmds, Formatting.Indented, settings)); DisableUI(); FormManager.TCPClient.SendData(serializeToBytes, NetworkMessageSource.Client, NetworkMessageDestination.Service, connectedHost.GetServerIndex(selectedServer), NetworkMessageTypes.StartCmdUpdate); @@ -435,24 +374,20 @@ private void EditStCmd_Click(object sender, EventArgs e) } } - private void ChkUpdates_Click(object sender, EventArgs e) - { + private void ChkUpdates_Click(object sender, EventArgs e) { FormManager.TCPClient.SendData(NetworkMessageSource.Client, NetworkMessageDestination.Service, NetworkMessageTypes.CheckUpdates); DisableUI(); } - private int HorizontalScrollPosition - { - get - { + private int HorizontalScrollPosition { + get { ScrollInfo si = new ScrollInfo(); si.Size = (uint)Marshal.SizeOf(si); si.Mask = (uint)ScrollInfoMask.All; GetScrollInfo(LogBox.Handle, (int)ScrollBarDirection.Vertical, ref si); return si.Pos; } - set - { + set { ScrollInfo si = new ScrollInfo(); si.Size = (uint)Marshal.SizeOf(si); si.Mask = (uint)ScrollInfoMask.All; @@ -463,8 +398,7 @@ private int HorizontalScrollPosition } } - public void ScrollToEnd() - { + public void ScrollToEnd() { // Get the current scroll info. ScrollInfo si = new ScrollInfo(); @@ -480,8 +414,7 @@ public void ScrollToEnd() _followTail = true; } - public void PerformBackupTests() - { + public void PerformBackupTests() { HostListBox.SelectedIndex = 0; Connect_Click(null, null); GlobBackup_Click(null, null); @@ -489,16 +422,14 @@ public void PerformBackupTests() ServerSelectBox.SelectedIndex = 0; FormManager.TCPClient.SendData(NetworkMessageSource.Client, NetworkMessageDestination.Service, connectedHost.GetServerIndex(selectedServer), NetworkMessageTypes.EnumBackups); FormManager.TCPClient.EnumBackupsArrived = false; - JsonSerializerSettings settings = new JsonSerializerSettings() - { + JsonSerializerSettings settings = new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All }; byte[] serializeToBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(new string[] { FormManager.TCPClient.BackupList[0].ToString() }, Formatting.Indented, settings)); FormManager.TCPClient.SendData(serializeToBytes, NetworkMessageSource.Client, NetworkMessageDestination.Service, FormManager.MainWindow.connectedHost.GetServerIndex(FormManager.MainWindow.selectedServer), NetworkMessageTypes.DelBackups); } - private void ComponentEnableManager() - { + private void ComponentEnableManager() { Connect.Enabled = connectedHost == null; Disconn.Enabled = connectedHost != null; newSrvBtn.Enabled = connectedHost != null && !ServerBusy; @@ -521,115 +452,92 @@ private void ComponentEnableManager() SvcLog.Enabled = (connectedHost != null && selectedServer != null && !ServerBusy); } - public Task DisableUI() - { + public Task DisableUI() { ServerBusy = true; - return Task.Run(() => - { + return Task.Run(() => { Invoke((MethodInvoker)delegate { ComponentEnableManager(); }); - while (ServerBusy) - { + while (ServerBusy) { Task.Delay(250).Wait(); } Invoke((MethodInvoker)delegate { ComponentEnableManager(); }); }); } - public Task WaitForServerData() - { - return Task.Run(() => - { - while (!FormManager.TCPClient.EnumBackupsArrived && !FormManager.TCPClient.PlayerInfoArrived && FormManager.TCPClient.RecievedPacks == null) - { + public Task WaitForServerData() { + return Task.Run(() => { + while (!FormManager.TCPClient.EnumBackupsArrived && !FormManager.TCPClient.PlayerInfoArrived && FormManager.TCPClient.RecievedPacks == null) { Task.Delay(250).Wait(); } }); } - public class ServerConnectException : Exception - { + public class ServerConnectException : Exception { public ServerConnectException() { } public ServerConnectException(string message) - : base(message) - { + : base(message) { } public ServerConnectException(string message, Exception inner) - : base(message, inner) - { + : base(message, inner) { } } private void scrollLockChkBox_CheckedChanged(object sender, EventArgs e) => _followTail = scrollLockChkBox.Checked; - private void cmdTextBox_KeyPress(object sender, KeyPressEventArgs e) - { - if (e.KeyChar == (char)Keys.Enter) - { + private void cmdTextBox_KeyPress(object sender, KeyPressEventArgs e) { + if (e.KeyChar == (char)Keys.Enter) { SendCmd_Click(null, null); } } - private void HostListBox_KeyPress(object sender, KeyPressEventArgs e) - { - if (e.KeyChar == (char)Keys.Enter) - { + private void HostListBox_KeyPress(object sender, KeyPressEventArgs e) { + if (e.KeyChar == (char)Keys.Enter) { Connect_Click(null, null); } } - private void BackupManager_Click(object sender, EventArgs e) - { - using (PropEditorForm editDialog = new PropEditorForm()) - { + private void BackupManager_Click(object sender, EventArgs e) { + using (PropEditorForm editDialog = new PropEditorForm()) { editDialog.EnableBackupManager(); FormManager.TCPClient.SendData(NetworkMessageSource.Client, NetworkMessageDestination.Service, connectedHost.GetServerIndex(selectedServer), NetworkMessageTypes.EnumBackups); DisableUI(); WaitForServerData().Wait(); FormManager.TCPClient.EnumBackupsArrived = false; editDialog.PopulateBoxes(FormManager.TCPClient.BackupList); - if (editDialog.ShowDialog() == DialogResult.OK) - { + if (editDialog.ShowDialog() == DialogResult.OK) { FormManager.TCPClient.SendData(Encoding.UTF8.GetBytes(editDialog.RollbackFolderName), NetworkMessageSource.Client, NetworkMessageDestination.Service, connectedHost.GetServerIndex(selectedServer), NetworkMessageTypes.BackupRollback); ServerBusy = true; } - else - { + else { ServerBusy = false; } editDialog.Close(); } } - private void ManPacks_Click(object sender, EventArgs e) - { + private void ManPacks_Click(object sender, EventArgs e) { FormManager.TCPClient.SendData(NetworkMessageSource.Client, NetworkMessageDestination.Server, connectedHost.GetServerIndex(selectedServer), NetworkMessageTypes.PackList); DisableUI(); WaitForServerData().Wait(); - using (ManagePacksForms form = new ManagePacksForms(connectedHost.GetServerIndex(selectedServer), _logger, _processInfo)) - { + using (ManagePacksForms form = new ManagePacksForms(connectedHost.GetServerIndex(selectedServer), _logger, _processInfo)) { form.PopulateServerPacks(FormManager.TCPClient.RecievedPacks); - if (form.ShowDialog() != DialogResult.OK) - { + if (form.ShowDialog() != DialogResult.OK) { ServerBusy = false; } form.Close(); } } - private void nbtStudioBtn_Click(object sender, EventArgs e) - { + private void nbtStudioBtn_Click(object sender, EventArgs e) { if (string.IsNullOrEmpty(_configManager.NBTStudioPath)) - using (OpenFileDialog openFile = new OpenFileDialog()) - { + using (OpenFileDialog openFile = new OpenFileDialog()) { openFile.FileName = "NBTStudio.exe"; openFile.Title = "Please locate NBT Studio executable..."; openFile.Filter = "NBTStudio.exe|NBTStudio.exe"; - if (openFile.ShowDialog() == DialogResult.OK) - { + if (openFile.ShowDialog() == DialogResult.OK) { _configManager.NBTStudioPath = openFile.FileName; _configManager.SaveConfigFile(); } @@ -638,8 +546,7 @@ private void nbtStudioBtn_Click(object sender, EventArgs e) ServerBusy = true; FormManager.TCPClient.SendData(NetworkMessageSource.Client, NetworkMessageDestination.Server, connectedHost.GetServerIndex(selectedServer), NetworkMessageTypes.LevelEditRequest); DisableUI(); - using (Process nbtStudioProcess = new Process()) - { + using (Process nbtStudioProcess = new Process()) { string tempPath = $@"{Path.GetTempPath()}level.dat"; nbtStudioProcess.StartInfo = new ProcessStartInfo(_configManager.NBTStudioPath, tempPath); nbtStudioProcess.Start(); @@ -649,25 +556,19 @@ private void nbtStudioBtn_Click(object sender, EventArgs e) ServerBusy = false; } - private void HostListBox_SelectedIndexChanged(object sender, EventArgs e) - { - if (HostListBox.SelectedIndex != -1) - { + private void HostListBox_SelectedIndexChanged(object sender, EventArgs e) { + if (HostListBox.SelectedIndex != -1) { clientSideServiceConfiguration = _configManager.HostConnectList.FirstOrDefault(host => host.GetHostName() == (string)HostListBox.SelectedItem); } } - private void MainWindow_Load(object sender, EventArgs e) - { + private void MainWindow_Load(object sender, EventArgs e) { } - private void clientConfigBtn_Click(object sender, EventArgs e) - { - using (ClientConfigForm form = new ClientConfigForm(_configManager)) - { - if (form.ShowDialog() == DialogResult.OK) - { + private void clientConfigBtn_Click(object sender, EventArgs e) { + using (ClientConfigForm form = new ClientConfigForm(_configManager)) { + if (form.ShowDialog() == DialogResult.OK) { form.Close(); InitForm(); } diff --git a/BedrockService/Client/Forms/ManagePacksForms.cs b/BedrockService/Client/Forms/ManagePacksForms.cs index ae0bd4a6..001829c6 100644 --- a/BedrockService/Client/Forms/ManagePacksForms.cs +++ b/BedrockService/Client/Forms/ManagePacksForms.cs @@ -9,19 +9,15 @@ using System.IO; using System.IO.Compression; using System.Text; -using System.Threading.Tasks; using System.Windows.Forms; -namespace BedrockService.Client.Forms -{ - public partial class ManagePacksForms : Form - { +namespace BedrockService.Client.Forms { + public partial class ManagePacksForms : Form { private readonly byte ServerIndex = 0x00; private readonly IBedrockLogger Logger; private readonly IProcessInfo ProcessInfo; private readonly DirectoryInfo PackExtractDir; - public ManagePacksForms(byte serverIndex, IBedrockLogger logger, IProcessInfo processInfo) - { + public ManagePacksForms(byte serverIndex, IBedrockLogger logger, IProcessInfo processInfo) { Logger = logger; PackExtractDir = new DirectoryInfo($@"{processInfo.GetDirectory()}\Temp"); ProcessInfo = processInfo; @@ -29,26 +25,22 @@ public ManagePacksForms(byte serverIndex, IBedrockLogger logger, IProcessInfo pr InitializeComponent(); } - public void PopulateServerPacks(List packList) - { + public void PopulateServerPacks(List packList) { foreach (MinecraftPackContainer container in packList) serverListBox.Items.Add(container); FormManager.TCPClient.RecievedPacks = null; } - private void ListBox_SelectedIndexChanged(object sender, EventArgs e) - { + private void ListBox_SelectedIndexChanged(object sender, EventArgs e) { ListBox thisBox = (ListBox)sender; if (thisBox == serverListBox) parsedPacksListBox.SelectedIndex = -1; if (thisBox == parsedPacksListBox) serverListBox.SelectedIndex = -1; - if (thisBox.SelectedIndex != -1) - { + if (thisBox.SelectedIndex != -1) { MinecraftPackContainer selectedPack = (MinecraftPackContainer)thisBox.SelectedItem; if (selectedPack.IconBytes != null) - using (MemoryStream ms = new MemoryStream(selectedPack.IconBytes)) - { + using (MemoryStream ms = new MemoryStream(selectedPack.IconBytes)) { selectedPackIcon.Image = Image.FromStream(ms); } if (selectedPack.JsonManifest != null) @@ -58,20 +50,16 @@ private void ListBox_SelectedIndexChanged(object sender, EventArgs e) } } - private void removePackBtn_Click(object sender, EventArgs e) - { - if (serverListBox.SelectedIndex != -1) - { + private void removePackBtn_Click(object sender, EventArgs e) { + if (serverListBox.SelectedIndex != -1) { List temp = new List(); object[] items = new object[serverListBox.SelectedItems.Count]; serverListBox.SelectedItems.CopyTo(items, 0); - foreach(object item in items) - { + foreach (object item in items) { temp.Add((MinecraftPackContainer)item); serverListBox.Items.Remove(item); } - JsonSerializerSettings settings = new JsonSerializerSettings() - { + JsonSerializerSettings settings = new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All }; FormManager.TCPClient.SendData(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(temp, Formatting.Indented, settings)), NetworkMessageSource.Client, NetworkMessageDestination.Server, ServerIndex, NetworkMessageTypes.RemovePack); @@ -79,45 +67,38 @@ private void removePackBtn_Click(object sender, EventArgs e) DialogResult = DialogResult.OK; } - private void removeAllPacksBtn_Click(object sender, EventArgs e) - { + private void removeAllPacksBtn_Click(object sender, EventArgs e) { List temp = new List(); foreach (object item in serverListBox.Items) temp.Add((MinecraftPackContainer)item); - JsonSerializerSettings settings = new JsonSerializerSettings() - { + JsonSerializerSettings settings = new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All }; FormManager.TCPClient.SendData(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(temp, Formatting.Indented, settings)), NetworkMessageSource.Client, NetworkMessageDestination.Server, ServerIndex, NetworkMessageTypes.RemovePack); DialogResult = DialogResult.OK; } - private void sendPacksBtn_Click(object sender, EventArgs e) - { - if (parsedPacksListBox.SelectedIndex != -1) - { + private void sendPacksBtn_Click(object sender, EventArgs e) { + if (parsedPacksListBox.SelectedIndex != -1) { object[] items = new object[parsedPacksListBox.SelectedItems.Count]; parsedPacksListBox.SelectedItems.CopyTo(items, 0); SendPacks(items); } } - private void sendAllBtn_Click(object sender, EventArgs e) - { + private void sendAllBtn_Click(object sender, EventArgs e) { object[] items = new object[parsedPacksListBox.Items.Count]; parsedPacksListBox.Items.CopyTo(items, 0); SendPacks(items); } - private void openFileBtn_Click(object sender, EventArgs e) - { + private void openFileBtn_Click(object sender, EventArgs e) { OpenFileDialog openFileDialog = new OpenFileDialog(); openFileDialog.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); openFileDialog.Filter = "MC pack file (.MCWORLD, .MCPACK, .MCADDON, .Zip)|*.mcworld;*.mcpack;*.mcaddon;*.zip"; openFileDialog.Title = "Select pack file(s)"; openFileDialog.Multiselect = true; - if (openFileDialog.ShowDialog() == DialogResult.OK) - { + if (openFileDialog.ShowDialog() == DialogResult.OK) { MinecraftPackParser parser = new MinecraftPackParser(openFileDialog.FileNames, PackExtractDir.FullName, Logger, ProcessInfo); parsedPacksListBox.Items.Clear(); foreach (MinecraftPackContainer container in parser.FoundPacks) @@ -125,10 +106,8 @@ private void openFileBtn_Click(object sender, EventArgs e) } } - private void SendPacks(object[] packList) - { - foreach (MinecraftPackContainer container in packList) - { + private void SendPacks(object[] packList) { + foreach (MinecraftPackContainer container in packList) { Directory.CreateDirectory($@"{PackExtractDir.FullName}\ZipTemp"); DirectoryInfo directoryInfo = new DirectoryInfo(container.PackContentLocation); directoryInfo.MoveTo($@"{PackExtractDir.FullName}\ZipTemp\{directoryInfo.Name}"); diff --git a/BedrockService/Client/Forms/NewPlayerRegistrationForm.cs b/BedrockService/Client/Forms/NewPlayerRegistrationForm.cs index 4f36ae20..b63c8607 100644 --- a/BedrockService/Client/Forms/NewPlayerRegistrationForm.cs +++ b/BedrockService/Client/Forms/NewPlayerRegistrationForm.cs @@ -3,20 +3,15 @@ using System; using System.Windows.Forms; -namespace BedrockService.Client.Forms -{ - public partial class NewPlayerRegistrationForm : Form - { +namespace BedrockService.Client.Forms { + public partial class NewPlayerRegistrationForm : Form { public IPlayer PlayerToAdd; - public NewPlayerRegistrationForm() - { + public NewPlayerRegistrationForm() { InitializeComponent(); } - private void saveClick(object sender, EventArgs e) - { - if (usernameTextBox.TextLength > 0 && xuidTextBox.TextLength == 16) - { + private void saveClick(object sender, EventArgs e) { + if (usernameTextBox.TextLength > 0 && xuidTextBox.TextLength == 16) { PlayerToAdd = new Player(xuidTextBox.Text, usernameTextBox.Text, DateTime.Now.Ticks.ToString(), "0", "0", whitelistedChkBox.Checked, permissionComboBox.SelectedItem.ToString(), ignoreLimitChkBox.Checked); DialogResult = DialogResult.OK; } diff --git a/BedrockService/Client/Forms/PlayerManagerForm.cs b/BedrockService/Client/Forms/PlayerManagerForm.cs index a4097ee0..02c10459 100644 --- a/BedrockService/Client/Forms/PlayerManagerForm.cs +++ b/BedrockService/Client/Forms/PlayerManagerForm.cs @@ -7,26 +7,22 @@ using System.Text; using System.Windows.Forms; -namespace BedrockService.Client.Forms -{ - public partial class PlayerManagerForm : Form - { +namespace BedrockService.Client.Forms { + public partial class PlayerManagerForm : Form { private readonly IServerConfiguration _server; private readonly string[] RegisteredPlayerColumnArray = new string[8] { "XUID:", "Username:", "Permission:", "Whitelisted:", "Ignores max players:", "First connected on:", "Last connected on:", "Time spent in game:" }; private List playersFound = new List(); private readonly List modifiedPlayers = new List(); private IPlayer playerToEdit; - public PlayerManagerForm(IServerConfiguration server) - { + public PlayerManagerForm(IServerConfiguration server) { InitializeComponent(); _server = server; playersFound = _server.GetPlayerList(); gridView.Columns.Clear(); gridView.Rows.Clear(); - foreach (string s in RegisteredPlayerColumnArray) - { + foreach (string s in RegisteredPlayerColumnArray) { gridView.Columns.Add(s.Replace(" ", "").Replace(":", ""), s); } gridView.Columns[5].ReadOnly = true; @@ -39,11 +35,9 @@ public PlayerManagerForm(IServerConfiguration server) RefreshGridContents(); } - private void RefreshGridContents() - { + private void RefreshGridContents() { gridView.Rows.Clear(); - foreach (IPlayer player in playersFound) - { + foreach (IPlayer player in playersFound) { var playerTimes = player.GetTimes(); string playerPermission = player.GetPermissionLevel(); TimeSpan timeSpent = TimeSpan.FromTicks(long.Parse(playerTimes.Conn) - long.Parse(playerTimes.Disconn)); @@ -53,17 +47,13 @@ private void RefreshGridContents() gridView.Refresh(); } - private void saveBtn_Click(object sender, EventArgs e) - { - if (modifiedPlayers.Count > 0) - { - foreach (IPlayer player in modifiedPlayers) - { + private void saveBtn_Click(object sender, EventArgs e) { + if (modifiedPlayers.Count > 0) { + foreach (IPlayer player in modifiedPlayers) { _server.AddUpdatePlayer(player); } } - JsonSerializerSettings settings = new JsonSerializerSettings() - { + JsonSerializerSettings settings = new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All }; byte[] sendBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(modifiedPlayers, Formatting.Indented, settings)); @@ -74,8 +64,7 @@ private void saveBtn_Click(object sender, EventArgs e) Dispose(); } - private void searchEntryBox_TextChanged(object sender, EventArgs e) - { + private void searchEntryBox_TextChanged(object sender, EventArgs e) { playersFound = _server.GetPlayerList(); string curText = searchEntryBox.Text; List tempList = new List(); @@ -83,23 +72,17 @@ private void searchEntryBox_TextChanged(object sender, EventArgs e) string cmd; string value; - if (curText.Contains(":")) - { + if (curText.Contains(":")) { splitCommands = curText.Split(','); - if (splitCommands.Length > 1) - { - foreach (string s in splitCommands) - { - if (s.Contains(":")) - { + if (splitCommands.Length > 1) { + foreach (string s in splitCommands) { + if (s.Contains(":")) { string[] finalSplit = s.Split(':'); cmd = finalSplit[0]; value = finalSplit[1]; tempList = new List(); - foreach (IPlayer player in playersFound) - { - if (player.SearchForProperty(cmd).Contains(value)) - { + foreach (IPlayer player in playersFound) { + if (player.SearchForProperty(cmd).Contains(value)) { tempList.Add(player); } } @@ -111,10 +94,8 @@ private void searchEntryBox_TextChanged(object sender, EventArgs e) splitCommands = curText.Split(':'); cmd = splitCommands[0]; value = splitCommands[1]; - foreach (IPlayer player in playersFound) - { - if (player.SearchForProperty(cmd).Contains(value)) - { + foreach (IPlayer player in playersFound) { + if (player.SearchForProperty(cmd).Contains(value)) { tempList.Add(player); } } @@ -124,14 +105,12 @@ private void searchEntryBox_TextChanged(object sender, EventArgs e) } } - private void gridView_CellBeginEdit(object sender, DataGridViewCellCancelEventArgs e) - { + private void gridView_CellBeginEdit(object sender, DataGridViewCellCancelEventArgs e) { DataGridViewRow focusedRow = gridView.Rows[e.RowIndex]; playerToEdit = _server.GetPlayerByXuid((string)focusedRow.Cells[0].Value); } - private void gridView_CellEndEdit(object sender, DataGridViewCellEventArgs e) - { + private void gridView_CellEndEdit(object sender, DataGridViewCellEventArgs e) { DataGridViewRow focusedRow = gridView.Rows[e.RowIndex]; var playerTimes = playerToEdit.GetTimes(); string playerFirstConnect = playerTimes.First; @@ -140,19 +119,15 @@ private void gridView_CellEndEdit(object sender, DataGridViewCellEventArgs e) string playerWhitelist = playerToEdit.IsPlayerWhitelisted().ToString(); string playerPermission = playerToEdit.GetPermissionLevel(); string playerIgnoreLimit = playerToEdit.PlayerIgnoresLimit().ToString(); - if ((string)focusedRow.Cells[0].Value != playerToEdit.GetXUID() || (string)focusedRow.Cells[1].Value != playerToEdit.GetUsername() || (string)focusedRow.Cells[2].Value != playerPermission || (string)focusedRow.Cells[3].Value != playerWhitelist || (string)focusedRow.Cells[4].Value != playerIgnoreLimit) - { + if ((string)focusedRow.Cells[0].Value != playerToEdit.GetXUID() || (string)focusedRow.Cells[1].Value != playerToEdit.GetUsername() || (string)focusedRow.Cells[2].Value != playerPermission || (string)focusedRow.Cells[3].Value != playerWhitelist || (string)focusedRow.Cells[4].Value != playerIgnoreLimit) { playerToEdit = new Player((string)focusedRow.Cells[0].Value, (string)focusedRow.Cells[1].Value, playerFirstConnect, playerConnectTime, playerDisconnectTime, bool.Parse((string)focusedRow.Cells[3].Value), (string)focusedRow.Cells[2].Value, bool.Parse((string)focusedRow.Cells[4].Value)); modifiedPlayers.Add(playerToEdit); } } - private void registerPlayerBtn_Click(object sender, EventArgs e) - { - using (NewPlayerRegistrationForm form = new NewPlayerRegistrationForm()) - { - if (form.ShowDialog() == DialogResult.OK) - { + private void registerPlayerBtn_Click(object sender, EventArgs e) { + using (NewPlayerRegistrationForm form = new NewPlayerRegistrationForm()) { + if (form.ShowDialog() == DialogResult.OK) { _server.GetPlayerList().Add(form.PlayerToAdd); modifiedPlayers.Add(form.PlayerToAdd); RefreshGridContents(); diff --git a/BedrockService/Client/Forms/PropEditorForm.cs b/BedrockService/Client/Forms/PropEditorForm.cs index a1e7c739..917123ed 100644 --- a/BedrockService/Client/Forms/PropEditorForm.cs +++ b/BedrockService/Client/Forms/PropEditorForm.cs @@ -6,30 +6,24 @@ using System.Text; using System.Windows.Forms; -namespace BedrockService.Client.Forms -{ - public partial class PropEditorForm : Form - { +namespace BedrockService.Client.Forms { + public partial class PropEditorForm : Form { readonly DataGridView dataGrid; public List workingProps; public List startCmds; public string RollbackFolderName = ""; - public PropEditorForm() - { + public PropEditorForm() { InitializeComponent(); dataGrid = gridView; } - public void PopulateBoxes(List propList) - { + public void PopulateBoxes(List propList) { int index = 0; workingProps = propList; - foreach (Property prop in workingProps) - { + foreach (Property prop in workingProps) { dataGrid.Rows.Add(new string[2] { prop.KeyName, prop.Value }); - if (prop.KeyName == "server-name" || prop.KeyName == "server-port" || prop.KeyName == "server-portv6") - { + if (prop.KeyName == "server-name" || prop.KeyName == "server-port" || prop.KeyName == "server-portv6") { dataGrid.Rows[index].ReadOnly = true; dataGrid.Rows[index].DefaultCellStyle.BackColor = System.Drawing.Color.FromArgb(225, 225, 225); } @@ -38,39 +32,31 @@ public void PopulateBoxes(List propList) gridView.AllowUserToAddRows = false; } - public void PopulateStartCmds(List list) - { + public void PopulateStartCmds(List list) { startCmds = list; gridView.Columns.RemoveAt(0); gridView.AllowUserToDeleteRows = true; - foreach (StartCmdEntry entry in startCmds) - { + foreach (StartCmdEntry entry in startCmds) { dataGrid.Rows.Add(new string[1] { entry.Command }); } } - public void EnableBackupManager() - { + public void EnableBackupManager() { gridView.MultiSelect = true; DelBackupBtn.Enabled = true; DelBackupBtn.Visible = true; SaveBtn.Text = "Rollback this date"; } - private void CancelBtn_Click(object sender, EventArgs e) - { + private void CancelBtn_Click(object sender, EventArgs e) { Close(); Dispose(); } - private void SaveBtn_Click(object sender, EventArgs e) - { - if (DelBackupBtn.Enabled) - { - if (dataGrid.SelectedRows.Count == 0) - { - if (dataGrid.SelectedCells.Count == 1) - { + private void SaveBtn_Click(object sender, EventArgs e) { + if (DelBackupBtn.Enabled) { + if (dataGrid.SelectedRows.Count == 0) { + if (dataGrid.SelectedCells.Count == 1) { dataGrid.SelectedCells[0].OwningRow.Selected = true; } } @@ -80,20 +66,15 @@ private void SaveBtn_Click(object sender, EventArgs e) Close(); } startCmds = new List(); - foreach (DataGridViewRow row in dataGrid.Rows) - { - if (workingProps != null) - { - foreach (Property prop in workingProps) - { - if ((string)row.Cells[0].Value == prop.KeyName) - { + foreach (DataGridViewRow row in dataGrid.Rows) { + if (workingProps != null) { + foreach (Property prop in workingProps) { + if ((string)row.Cells[0].Value == prop.KeyName) { prop.Value = (string)row.Cells[1].Value; } } } - else - { + else { if ((string)row.Cells[0].Value != null) startCmds.Add(new StartCmdEntry((string)row.Cells[0].Value)); } @@ -103,19 +84,15 @@ private void SaveBtn_Click(object sender, EventArgs e) Close(); } - private void gridView_NewRowNeeded(object sender, DataGridViewRowEventArgs e) - { + private void gridView_NewRowNeeded(object sender, DataGridViewRowEventArgs e) { DataGridViewRow focusedRow = gridView.CurrentRow; focusedRow.Cells[0].Value = "Cmd:"; gridView.Refresh(); } - private void DelBackupBtn_Click(object sender, EventArgs e) - { - if (dataGrid.SelectedRows.Count == 0) - { - if (dataGrid.SelectedCells.Count == 1) - { + private void DelBackupBtn_Click(object sender, EventArgs e) { + if (dataGrid.SelectedRows.Count == 0) { + if (dataGrid.SelectedCells.Count == 1) { dataGrid.SelectedCells[0].OwningRow.Selected = true; } } @@ -123,8 +100,7 @@ private void DelBackupBtn_Click(object sender, EventArgs e) if (dataGrid.SelectedRows.Count > 0) foreach (DataGridViewRow viewRow in dataGrid.SelectedRows) removeBackups.Add((string)viewRow.Cells[0].Value); - JsonSerializerSettings settings = new JsonSerializerSettings() - { + JsonSerializerSettings settings = new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All }; byte[] serializeToBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(removeBackups, Formatting.Indented, settings)); diff --git a/BedrockService/Client/Management/ClientLogger.cs b/BedrockService/Client/Management/ClientLogger.cs index bba1cf5f..8587a41a 100644 --- a/BedrockService/Client/Management/ClientLogger.cs +++ b/BedrockService/Client/Management/ClientLogger.cs @@ -4,27 +4,22 @@ using System.IO; using System.Text; -namespace BedrockService.Client.Management -{ - public class ClientLogger : IBedrockLogger - { +namespace BedrockService.Client.Management { + public class ClientLogger : IBedrockLogger { public List Log = new List(); public StringBuilder OutString = new StringBuilder(); public StreamWriter LogWriter; private readonly string LogDir; - public ClientLogger(IProcessInfo processInfo) - { + public ClientLogger(IProcessInfo processInfo) { LogDir = $@"{processInfo.GetDirectory()}\Client\ClientLogs"; - if (!Directory.Exists(LogDir)) - { + if (!Directory.Exists(LogDir)) { Directory.CreateDirectory(LogDir); } LogWriter = new StreamWriter($@"{LogDir}\ClientLog_{DateTime.Now:yyyymmddhhmmss}.log", true); } - public void AppendLine(string text) - { + public void AppendLine(string text) { string addText = $"Client: {text}\r\n"; Log.Add(addText); LogWriter.WriteLine(addText); @@ -34,21 +29,17 @@ public void AppendLine(string text) public void AppendText(string text) => AppendLine(text); - public int Count() - { + public int Count() { return Log.Count; } - public string FromIndex(int index) - { + public string FromIndex(int index) { return Log[index]; } - public override string ToString() - { + public override string ToString() { OutString = new StringBuilder(); - foreach (string s in Log) - { + foreach (string s in Log) { OutString.Append(s); } return OutString.ToString(); diff --git a/BedrockService/Client/Management/ConfigManager.cs b/BedrockService/Client/Management/ConfigManager.cs index c695e631..d23b07d1 100644 --- a/BedrockService/Client/Management/ConfigManager.cs +++ b/BedrockService/Client/Management/ConfigManager.cs @@ -4,67 +4,55 @@ using System.IO; using System.Text; -namespace BedrockService.Client.Management -{ - public class ConfigManager - { +namespace BedrockService.Client.Management { + public class ConfigManager { public string ConfigDir = $@"{Directory.GetCurrentDirectory()}\Client\Configs"; public string ConfigFile; public List HostConnectList = new List(); public string NBTStudioPath; private readonly IBedrockLogger Logger; - public ConfigManager(IBedrockLogger logger) - { + public ConfigManager(IBedrockLogger logger) { Logger = logger; ConfigFile = $@"{ConfigDir}\Config.conf"; } - public void LoadConfigs() - { + public void LoadConfigs() { HostConnectList.Clear(); - if (!Directory.Exists(ConfigDir)) - { + if (!Directory.Exists(ConfigDir)) { Directory.CreateDirectory(ConfigDir); } - if (!File.Exists(ConfigFile)) - { + if (!File.Exists(ConfigFile)) { Logger.AppendLine("Config file missing! Regenerating default file..."); CreateDefaultConfig(); LoadConfigs(); return; } string[] lines = File.ReadAllLines(ConfigFile); - foreach (string line in lines) - { + foreach (string line in lines) { string[] entrySplit = line.Split('='); - if (!string.IsNullOrEmpty(line) && !line.StartsWith("#")) - { - if (entrySplit[0] == "HostEntry") - { + if (!string.IsNullOrEmpty(line) && !line.StartsWith("#")) { + if (entrySplit[0] == "HostEntry") { string[] hostSplit = entrySplit[1].Split(';'); string[] addressSplit = hostSplit[1].Split(':'); IClientSideServiceConfiguration hostToList = new ClientSideServiceConfiguration(hostSplit[0], addressSplit[0], addressSplit[1]); HostConnectList.Add(hostToList); } - if (entrySplit[0] == "NBTStudioPath") - { + if (entrySplit[0] == "NBTStudioPath") { NBTStudioPath = entrySplit[1]; } } } } - public void CreateDefaultConfig() - { + public void CreateDefaultConfig() { string[] Config = new string[] { "HostEntry=host1;127.0.0.1:19134" }; StringBuilder builder = new StringBuilder(); builder.Append("# Hosts\n"); - foreach (string entry in Config) - { + foreach (string entry in Config) { builder.Append($"{entry}\n"); } builder.Append("\n# Settings\n"); @@ -72,12 +60,10 @@ public void CreateDefaultConfig() File.WriteAllText(ConfigFile, builder.ToString()); } - public void SaveConfigFile() - { + public void SaveConfigFile() { StringBuilder fileContent = new StringBuilder(); fileContent.Append("# hosts\n\n"); - foreach (ClientSideServiceConfiguration host in HostConnectList) - { + foreach (ClientSideServiceConfiguration host in HostConnectList) { fileContent.Append($"HostEntry={host.GetHostName()};{host.GetAddress()}:{host.GetPort()}\n"); } fileContent.Append("\n# Settings\n"); diff --git a/BedrockService/Client/Management/FormManager.cs b/BedrockService/Client/Management/FormManager.cs index 917961b5..31c7d132 100644 --- a/BedrockService/Client/Management/FormManager.cs +++ b/BedrockService/Client/Management/FormManager.cs @@ -6,32 +6,24 @@ using System.IO; using System.Reflection; -namespace BedrockService.Client.Management -{ - public sealed class FormManager - { +namespace BedrockService.Client.Management { + public sealed class FormManager { private static readonly IProcessInfo processInfo = new ServiceProcessInfo(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), Path.GetFileName(Assembly.GetExecutingAssembly().Location), Process.GetCurrentProcess().Id, false, true); private static readonly IBedrockLogger Logger = new ClientLogger(processInfo); private static MainWindow main; private static TCPClient client; - public static MainWindow MainWindow - { - get - { - if (main == null || main.IsDisposed) - { + public static MainWindow MainWindow { + get { + if (main == null || main.IsDisposed) { main = new MainWindow(processInfo, Logger); } return main; } } - public static TCPClient TCPClient - { - get - { - if (client == null) - { + public static TCPClient TCPClient { + get { + if (client == null) { client = new TCPClient(Logger); } return client; diff --git a/BedrockService/Client/Management/LogManager.cs b/BedrockService/Client/Management/LogManager.cs index c4055395..45383779 100644 --- a/BedrockService/Client/Management/LogManager.cs +++ b/BedrockService/Client/Management/LogManager.cs @@ -7,10 +7,8 @@ using System.Threading.Tasks; using System.Windows.Forms; -namespace BedrockService.Client.Management -{ - class LogManager - { +namespace BedrockService.Client.Management { + class LogManager { public Task LogTask; public bool EnableFlag; public bool Working = false; @@ -19,21 +17,16 @@ class LogManager private IServiceConfiguration _connectedHost; private readonly IBedrockLogger _logger; - public LogManager(IBedrockLogger logger) - { + public LogManager(IBedrockLogger logger) { _logger = logger; } - private void LogManagerTask() - { - while (!_logTaskCancelSource.Token.IsCancellationRequested) - { - try - { + private void LogManagerTask() { + while (!_logTaskCancelSource.Token.IsCancellationRequested) { + try { Working = true; StringBuilder sendString = new StringBuilder(); - foreach (ServerInfo server in _connectedHost.GetServerList()) - { + foreach (ServerInfo server in _connectedHost.GetServerList()) { server.ConsoleBuffer = server.ConsoleBuffer ?? new List(); sendString.Append($"{server.ServerName};{server.ConsoleBuffer.Count}|"); } @@ -43,42 +36,34 @@ private void LogManagerTask() Thread.Sleep(200); int currentLogBoxLength = 0; - if (FormManager.MainWindow.selectedServer == null) - { + if (FormManager.MainWindow.selectedServer == null) { UpdateLogBoxInvoked(""); } - FormManager.MainWindow.LogBox.Invoke((MethodInvoker)delegate - { + FormManager.MainWindow.LogBox.Invoke((MethodInvoker)delegate { currentLogBoxLength = FormManager.MainWindow.LogBox.TextLength; }); - if (FormManager.MainWindow.ShowsSvcLog && _connectedHost.GetLog().Count != currentLogBoxLength) - { + if (FormManager.MainWindow.ShowsSvcLog && _connectedHost.GetLog().Count != currentLogBoxLength) { UpdateLogBoxInvoked(string.Join("\r\n", _connectedHost.GetLog())); } - else if (!FormManager.MainWindow.ShowsSvcLog && FormManager.MainWindow.selectedServer != null && FormManager.MainWindow.selectedServer.GetLog() != null && FormManager.MainWindow.selectedServer.GetLog().Count != currentLogBoxLength) - { + else if (!FormManager.MainWindow.ShowsSvcLog && FormManager.MainWindow.selectedServer != null && FormManager.MainWindow.selectedServer.GetLog() != null && FormManager.MainWindow.selectedServer.GetLog().Count != currentLogBoxLength) { UpdateLogBoxInvoked(string.Join("", FormManager.MainWindow.selectedServer.GetLog())); } } - catch (Exception e) - { + catch (Exception e) { _logger.AppendLine($"LogManager Error! Stacetrace: {e.StackTrace}"); } } } - public bool InitLogThread(IServiceConfiguration host) - { + public bool InitLogThread(IServiceConfiguration host) { _connectedHost = host; _logTaskCancelSource = new CancellationTokenSource(); return StartLogThread(); } - public bool StartLogThread() - { - try - { + public bool StartLogThread() { + try { if (LogTask != null && !_logTaskCancelSource.IsCancellationRequested) _logTaskCancelSource.Cancel(); Thread.Sleep(500); @@ -87,25 +72,20 @@ public bool StartLogThread() _logger.AppendLine("LogThread started"); return true; } - catch (Exception e) - { + catch (Exception e) { _logger.AppendLine($"Error starting LogThread: {e.StackTrace}"); } return false; } - public bool StopLogThread() - { - if (LogTask == null) - { + public bool StopLogThread() { + if (LogTask == null) { return true; } - try - { + try { _logTaskCancelSource.Cancel(); } - catch (ThreadAbortException e) - { + catch (ThreadAbortException e) { _logger.AppendLine(e.StackTrace); } _logger.AppendLine("LogThread stopped"); @@ -113,10 +93,8 @@ public bool StopLogThread() return true; } - private static void UpdateLogBoxInvoked(string contents) - { - FormManager.MainWindow.LogBox.Invoke((MethodInvoker)delegate - { + private static void UpdateLogBoxInvoked(string contents) { + FormManager.MainWindow.LogBox.Invoke((MethodInvoker)delegate { FormManager.MainWindow.UpdateLogBox(contents); }); } diff --git a/BedrockService/Client/Networking/TCPClient.cs b/BedrockService/Client/Networking/TCPClient.cs index a1b15eeb..c4ec0b9f 100644 --- a/BedrockService/Client/Networking/TCPClient.cs +++ b/BedrockService/Client/Networking/TCPClient.cs @@ -12,10 +12,8 @@ using System.Threading; using System.Threading.Tasks; -namespace BedrockService.Client.Networking -{ - public class TCPClient - { +namespace BedrockService.Client.Networking { + public class TCPClient { public TcpClient OpenedTcpClient; public string ClientName; public NetworkStream stream; @@ -32,34 +30,28 @@ public class TCPClient private const int _heartbeatFailTimeoutLimit = 2; private readonly IBedrockLogger _logger; - public TCPClient(IBedrockLogger logger) - { + public TCPClient(IBedrockLogger logger) { _logger = logger; } - public void ConnectHost(IClientSideServiceConfiguration host) - { - if (EstablishConnection(host.GetAddress(), int.Parse(host.GetPort()))) - { + public void ConnectHost(IClientSideServiceConfiguration host) { + if (EstablishConnection(host.GetAddress(), int.Parse(host.GetPort()))) { SendData(NetworkMessageSource.Client, NetworkMessageDestination.Service, NetworkMessageTypes.Connect); return; } } - public bool EstablishConnection(string addr, int port) - { + public bool EstablishConnection(string addr, int port) { _logger.AppendLine("Connecting to Server"); _netCancelSource = new CancellationTokenSource(); - try - { + try { EnableRead = false; OpenedTcpClient = new TcpClient(addr, port); stream = OpenedTcpClient.GetStream(); EstablishedLink = true; ClientReciever = Task.Factory.StartNew(new Action(ReceiveListener), _netCancelSource.Token); } - catch - { + catch { _logger.AppendLine("Could not connect to Server"); if (ClientReciever != null) _netCancelSource.Cancel(); @@ -69,10 +61,8 @@ public bool EstablishConnection(string addr, int port) return EstablishedLink; } - public void CloseConnection() - { - try - { + public void CloseConnection() { + try { if (stream != null) stream.Dispose(); stream = null; @@ -80,27 +70,21 @@ public void CloseConnection() EstablishedLink = false; _netCancelSource.Cancel(); } - catch (NullReferenceException) - { + catch (NullReferenceException) { Connected = false; EstablishedLink = false; } - catch (Exception e) - { + catch (Exception e) { _logger.AppendLine($"Error closing connection: {e.StackTrace}"); } } - public void ReceiveListener() - { - while (!_netCancelSource.IsCancellationRequested) - { + public void ReceiveListener() { + while (!_netCancelSource.IsCancellationRequested) { SendData(NetworkMessageSource.Client, NetworkMessageDestination.Service, NetworkMessageTypes.Heartbeat); - try - { + try { byte[] buffer = new byte[4]; - while (OpenedTcpClient.Client.Available > 0) - { + while (OpenedTcpClient.Client.Available > 0) { int byteCount = stream.Read(buffer, 0, 4); int expectedLen = BitConverter.ToInt32(buffer, 0); buffer = new byte[expectedLen]; @@ -116,26 +100,21 @@ public void ReceiveListener() if (destination != NetworkMessageDestination.Client) continue; int srvCurLen = 0; - JsonSerializerSettings settings = new JsonSerializerSettings() - { + JsonSerializerSettings settings = new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All }; - switch (source) - { + switch (source) { case NetworkMessageSource.Service: - switch (msgType) - { + switch (msgType) { case NetworkMessageTypes.Connect: - try - { + try { _logger.AppendLine("Connection to Host successful!"); FormManager.MainWindow.connectedHost = null; FormManager.MainWindow.connectedHost = JsonConvert.DeserializeObject(data, settings); Connected = true; FormManager.MainWindow.RefreshServerContents(); } - catch (Exception e) - { + catch (Exception e) { _logger.AppendLine($"Error: ConnectMan reported error: {e.Message}\n{e.StackTrace}"); } break; @@ -160,30 +139,24 @@ public void ReceiveListener() } break; case NetworkMessageSource.Server: - switch (msgType) - { + switch (msgType) { case NetworkMessageTypes.ConsoleLogUpdate: string[] strings = data.Split('|'); - for (int i = 0; i < strings.Length; i++) - { + for (int i = 0; i < strings.Length; i++) { string[] srvSplit = strings[i].Split(';'); string srvName = srvSplit[0]; string srvText = srvSplit[1]; srvCurLen = int.Parse(srvSplit[2]); - if (srvName != "Service") - { + if (srvName != "Service") { IServerConfiguration bedrockServer = FormManager.MainWindow.connectedHost.GetServerInfoByName(srvName); int curCount = bedrockServer.GetLog().Count; - if (curCount == srvCurLen) - { + if (curCount == srvCurLen) { bedrockServer.GetLog().Add(srvText); } } - else - { + else { int curCount = FormManager.MainWindow.connectedHost.GetLog().Count; - if (curCount == srvCurLen) - { + if (curCount == srvCurLen) { FormManager.MainWindow.connectedHost.GetLog().Add(srvText); } } @@ -229,16 +202,14 @@ public void ReceiveListener() } } } - catch (Exception e) - { + catch (Exception e) { _logger.AppendLine($"TCPClient error! Stacktrace: {e.Message}\n{e.StackTrace}"); } Thread.Sleep(200); } } - public bool SendData(byte[] bytes, NetworkMessageSource source, NetworkMessageDestination destination, byte serverIndex, NetworkMessageTypes type, NetworkMessageFlags status) - { + public bool SendData(byte[] bytes, NetworkMessageSource source, NetworkMessageDestination destination, byte serverIndex, NetworkMessageTypes type, NetworkMessageFlags status) { byte[] compiled = new byte[9 + bytes.Length]; byte[] len = BitConverter.GetBytes(5 + bytes.Length); Buffer.BlockCopy(len, 0, compiled, 0, 4); @@ -248,23 +219,19 @@ public bool SendData(byte[] bytes, NetworkMessageSource source, NetworkMessageDe compiled[7] = (byte)type; compiled[8] = (byte)status; Buffer.BlockCopy(bytes, 0, compiled, 9, bytes.Length); - if (EstablishedLink) - { - try - { + if (EstablishedLink) { + try { stream.Write(compiled, 0, compiled.Length); stream.Flush(); _heartbeatFailTimeout = 0; return true; } - catch - { + catch { _logger.AppendLine("Error writing to network stream!"); Thread.Sleep(100); _heartbeatFailTimeout++; - if (_heartbeatFailTimeout > _heartbeatFailTimeoutLimit) - { - Task.Run(() => { FormManager.MainWindow.HeartbeatFailDisconnect(); }); + if (_heartbeatFailTimeout > _heartbeatFailTimeoutLimit) { + Task.Run(() => { FormManager.MainWindow.HeartbeatFailDisconnect(); }); _netCancelSource.Cancel(); EstablishedLink = false; _heartbeatFailTimeout = 0; @@ -287,17 +254,14 @@ public bool SendData(byte[] bytes, NetworkMessageSource source, NetworkMessageDe public void SendData(NetworkMessageSource source, NetworkMessageDestination destination, NetworkMessageTypes type, NetworkMessageFlags status) => SendData(new byte[0], source, destination, 0xFF, type, status); - public void Dispose() - { - if (_netCancelSource != null) - { + public void Dispose() { + if (_netCancelSource != null) { _netCancelSource.Cancel(); _netCancelSource.Dispose(); _netCancelSource = null; } ClientReciever = null; - if (OpenedTcpClient != null) - { + if (OpenedTcpClient != null) { OpenedTcpClient.Close(); OpenedTcpClient.Dispose(); } diff --git a/BedrockService/Service/Core/BedrockService.cs b/BedrockService/Service/Core/BedrockService.cs index d0640d95..7614fe60 100644 --- a/BedrockService/Service/Core/BedrockService.cs +++ b/BedrockService/Service/Core/BedrockService.cs @@ -1,15 +1,11 @@ using BedrockService.Service.Core.Interfaces; using BedrockService.Service.Server; using NCrontab; -using System.Threading; using System.Timers; -namespace BedrockService.Service.Core -{ - public class BedrockService : ServiceControl, IBedrockService - { - private enum ServiceStatus - { +namespace BedrockService.Service.Core { + public class BedrockService : ServiceControl, IBedrockService { + private enum ServiceStatus { Stopped, Starting, Started, @@ -28,8 +24,7 @@ private enum ServiceStatus private System.Timers.Timer _updaterTimer; private System.Timers.Timer _cronTimer; - public BedrockService(IConfigurator configurator, IUpdater updater, IBedrockLogger logger, IServiceConfiguration serviceConfiguration, IProcessInfo serviceProcessInfo, ITCPListener tCPListener) - { + public BedrockService(IConfigurator configurator, IUpdater updater, IBedrockLogger logger, IServiceConfiguration serviceConfiguration, IProcessInfo serviceProcessInfo, ITCPListener tCPListener) { _tCPListener = tCPListener; _configurator = configurator; _configurator.LoadAllConfigurations().Wait(); @@ -44,15 +39,12 @@ public BedrockService(IConfigurator configurator, IUpdater updater, IBedrockLogg _tCPListener.SetKeyContainer(_configurator.GetKeyContainer()); } - public bool Start(HostControl hostControl) - { + public bool Start(HostControl hostControl) { _hostControl = hostControl; - try - { + try { ValidSettingsCheck(); - foreach (var brs in _bedrockServers) - { + foreach (var brs in _bedrockServers) { if (hostControl != null) _hostControl.RequestAdditionalTime(TimeSpan.FromSeconds(30)); brs.SetServerStatus(BedrockServer.ServerStatus.Starting); @@ -60,157 +52,125 @@ public bool Start(HostControl hostControl) } return true; } - catch (Exception e) - { + catch (Exception e) { _logger.AppendLine($"Error Starting BedrockServiceWrapper {e.StackTrace}"); return false; } } - public bool Stop(HostControl hostControl) - { + public bool Stop(HostControl hostControl) { _hostControl = hostControl; - try - { - foreach (var brs in _bedrockServers) - { + try { + foreach (var brs in _bedrockServers) { brs.SetServerStatus(BedrockServer.ServerStatus.Stopping); while (brs.GetServerStatus() == BedrockServer.ServerStatus.Stopping && !Program.IsExiting) Thread.Sleep(100); } return true; } - catch (Exception e) - { + catch (Exception e) { _logger.AppendLine($"Error Stopping BedrockServiceWrapper {e.StackTrace}"); return false; } } - public void RestartService() - { - try - { - foreach (IBedrockServer brs in _bedrockServers) - { + public void RestartService() { + try { + foreach (IBedrockServer brs in _bedrockServers) { brs.SetServerStatus(BedrockServer.ServerStatus.Stopping); while (brs.GetServerStatus() == BedrockServer.ServerStatus.Stopping && !Program.IsExiting) Thread.Sleep(100); } - try - { + try { _tCPListener.ResetListener(); } catch (ThreadAbortException) { } _configurator.LoadAllConfigurations().Wait(); Initialize(); - foreach (var brs in _bedrockServers) - { + foreach (var brs in _bedrockServers) { brs.SetServerStatus(BedrockServer.ServerStatus.Starting); } Start(_hostControl); } - catch (Exception e) - { + catch (Exception e) { _logger.AppendLine($"Error Stopping BedrockServiceWrapper {e.StackTrace}"); } } - public IBedrockServer GetBedrockServerByIndex(int serverIndex) - { + public IBedrockServer GetBedrockServerByIndex(int serverIndex) { return _bedrockServers[serverIndex]; } - public IBedrockServer GetBedrockServerByName(string name) - { + public IBedrockServer GetBedrockServerByName(string name) { return _bedrockServers.FirstOrDefault(brs => brs.GetServerName() == name); } public List GetAllServers() => _bedrockServers; - public void InitializeNewServer(IServerConfiguration server) - { + public void InitializeNewServer(IServerConfiguration server) { IBedrockServer bedrockServer = new BedrockServer(server, _configurator, _logger, _serviceConfiguration, _processInfo); _bedrockServers.Add(bedrockServer); _serviceConfiguration.AddNewServerInfo(server); - if (ValidSettingsCheck()) - { + if (ValidSettingsCheck()) { bedrockServer.SetServerStatus(BedrockServer.ServerStatus.Starting); bedrockServer.StartWatchdog(_hostControl); } } - private void Initialize() - { + private void Initialize() { _bedrockServers.Clear(); - if (_serviceConfiguration.GetProp("BackupEnabled").ToString() == "true" && _shed != null) - { + if (_serviceConfiguration.GetProp("BackupEnabled").ToString() == "true" && _shed != null) { _cronTimer = new System.Timers.Timer((_shed.GetNextOccurrence(DateTime.Now) - DateTime.Now).TotalMilliseconds); _cronTimer.Elapsed += CronTimer_Elapsed; _cronTimer.Start(); } - if (_serviceConfiguration.GetProp("CheckUpdates").ToString() == "true" && _updaterCron != null) - { + if (_serviceConfiguration.GetProp("CheckUpdates").ToString() == "true" && _updaterCron != null) { _updaterTimer = new System.Timers.Timer((_updaterCron.GetNextOccurrence(DateTime.Now) - DateTime.Now).TotalMilliseconds); _updaterTimer.Elapsed += UpdateTimer_Elapsed; _logger.AppendLine($"Updates Enabled, will be checked in: {((float)_updaterTimer.Interval / 1000)} seconds."); _updaterTimer.Start(); } - try - { + try { List temp = _serviceConfiguration.GetServerList(); - foreach (IServerConfiguration server in temp) - { + foreach (IServerConfiguration server in temp) { IBedrockServer bedrockServer = new BedrockServer(server, _configurator, _logger, _serviceConfiguration, _processInfo); _bedrockServers.Add(bedrockServer); } } - catch (Exception e) - { + catch (Exception e) { _logger.AppendLine($"Error Instantiating BedrockServiceWrapper: {e.StackTrace}"); } } - private void CronTimer_Elapsed(object sender, ElapsedEventArgs e) - { - try - { - if (_cronTimer != null) - { + private void CronTimer_Elapsed(object sender, ElapsedEventArgs e) { + try { + if (_cronTimer != null) { _cronTimer.Stop(); _cronTimer = null; } - if (_serviceConfiguration.GetProp("BackupEnabled").ToString() == "true" && _shed != null) - { + if (_serviceConfiguration.GetProp("BackupEnabled").ToString() == "true" && _shed != null) { BackupAllServers(); _cronTimer = new System.Timers.Timer((_shed.GetNextOccurrence(DateTime.Now) - DateTime.Now).TotalMilliseconds); _cronTimer.Elapsed += CronTimer_Elapsed; _cronTimer.Start(); } } - catch (Exception ex) - { + catch (Exception ex) { _logger.AppendLine($"Error in BackupTimer_Elapsed {ex}"); } } - private void UpdateTimer_Elapsed(object sender, ElapsedEventArgs e) - { - try - { - if (_updaterTimer != null) - { + private void UpdateTimer_Elapsed(object sender, ElapsedEventArgs e) { + try { + if (_updaterTimer != null) { _updaterTimer.Stop(); _updaterTimer = null; } _updater.CheckUpdates().Wait(); - if (_serviceConfiguration.GetProp("CheckUpdates").ToString() == "true" && _updater != null) - { - if (_updater.CheckVersionChanged()) - { + if (_serviceConfiguration.GetProp("CheckUpdates").ToString() == "true" && _updater != null) { + if (_updater.CheckVersionChanged()) { _logger.AppendLine("Version change detected! Restarting server(s) to apply update..."); - foreach (IBedrockServer server in _bedrockServers) - { + foreach (IBedrockServer server in _bedrockServers) { server.RestartServer(false); } } @@ -220,69 +180,53 @@ private void UpdateTimer_Elapsed(object sender, ElapsedEventArgs e) _updaterTimer.Start(); } } - catch (Exception ex) - { + catch (Exception ex) { _logger.AppendLine($"Error in UpdateTimer_Elapsed {ex}"); } } - private void BackupAllServers() - { + private void BackupAllServers() { _logger.AppendLine("Service started backup manager."); - foreach (var brs in _bedrockServers) - { + foreach (var brs in _bedrockServers) { brs.InitializeBackup(); } _logger.AppendLine("Backups have been completed."); } - private bool ValidSettingsCheck() - { + private bool ValidSettingsCheck() { bool validating = true; bool dupedSettingsFound = false; - while (validating) - { - if (_serviceConfiguration.GetServerList().Count() < 1) - { + while (validating) { + if (_serviceConfiguration.GetServerList().Count() < 1) { throw new Exception("No Servers Configured"); } - else - { - foreach (IServerConfiguration server in _serviceConfiguration.GetServerList()) - { - foreach (IServerConfiguration compareServer in _serviceConfiguration.GetServerList()) - { - if (server != compareServer) - { + else { + foreach (IServerConfiguration server in _serviceConfiguration.GetServerList()) { + foreach (IServerConfiguration compareServer in _serviceConfiguration.GetServerList()) { + if (server != compareServer) { if (server.GetProp("server-port").Equals(compareServer.GetProp("server-port")) || server.GetProp("server-portv6").Equals(compareServer.GetProp("server-portv6")) || - server.GetProp("server-name").Equals(compareServer.GetProp("server-name"))) - { + server.GetProp("server-name").Equals(compareServer.GetProp("server-name"))) { _logger.AppendLine($"Duplicate server settings between servers {server.GetFileName()} and {compareServer.GetFileName()}."); dupedSettingsFound = true; } } } } - if (dupedSettingsFound) - { + if (dupedSettingsFound) { throw new Exception("Duplicate settings found! Check logs."); } - foreach (var server in _serviceConfiguration.GetServerList()) - { - if (_updater.CheckVersionChanged() || !File.Exists(server.GetProp("ServerPath") + "\\bedrock_server.exe")) - { + foreach (var server in _serviceConfiguration.GetServerList()) { + if (_updater.CheckVersionChanged() || !File.Exists(server.GetProp("ServerPath") + "\\bedrock_server.exe")) { _configurator.ReplaceServerBuild(server).Wait(); } - if (server.GetProp("ServerExeName").ToString() != "bedrock_server.exe" && File.Exists(server.GetProp("ServerPath") + "\\bedrock_server.exe") && !File.Exists(server.GetProp("ServerPath") + "\\" + server.GetProp("ServerExeName"))) - { + if (server.GetProp("ServerExeName").ToString() != "bedrock_server.exe" && File.Exists(server.GetProp("ServerPath") + "\\bedrock_server.exe") && !File.Exists(server.GetProp("ServerPath") + "\\" + server.GetProp("ServerExeName"))) { File.Copy(server.GetProp("ServerPath") + "\\bedrock_server.exe", server.GetProp("ServerPath") + "\\" + server.GetProp("ServerExeName")); } } if (_updater.CheckVersionChanged()) _updater.MarkUpToDate(); - else - { + else { validating = false; } } @@ -290,8 +234,7 @@ private bool ValidSettingsCheck() return true; } - public void RemoveBedrockServerByIndex(int serverIndex) - { + public void RemoveBedrockServerByIndex(int serverIndex) { _bedrockServers.RemoveAt(serverIndex); } } diff --git a/BedrockService/Service/Core/Interfaces/IBedrockService.cs b/BedrockService/Service/Core/Interfaces/IBedrockService.cs index 064d80fd..ea197552 100644 --- a/BedrockService/Service/Core/Interfaces/IBedrockService.cs +++ b/BedrockService/Service/Core/Interfaces/IBedrockService.cs @@ -1,9 +1,7 @@ using BedrockService.Service.Server; -namespace BedrockService.Service.Core.Interfaces -{ - public interface IBedrockService : ServiceControl - { +namespace BedrockService.Service.Core.Interfaces { + public interface IBedrockService : ServiceControl { IBedrockServer GetBedrockServerByIndex(int index); void RemoveBedrockServerByIndex(int serverIndex); diff --git a/BedrockService/Service/Core/Interfaces/IService.cs b/BedrockService/Service/Core/Interfaces/IService.cs index 29ae3207..b7bd2294 100644 --- a/BedrockService/Service/Core/Interfaces/IService.cs +++ b/BedrockService/Service/Core/Interfaces/IService.cs @@ -1,7 +1,5 @@ -namespace BedrockService.Service.Core.Interfaces -{ - public interface IService : IHostedService - { +namespace BedrockService.Service.Core.Interfaces { + public interface IService : IHostedService { Task InitializeHost(); } } diff --git a/BedrockService/Service/Core/Service.cs b/BedrockService/Service/Core/Service.cs index 3d1fd704..4c1f1c87 100644 --- a/BedrockService/Service/Core/Service.cs +++ b/BedrockService/Service/Core/Service.cs @@ -1,42 +1,32 @@ using BedrockService.Service.Core.Interfaces; using BedrockService.Service.Server; -using System.Threading; using Topshelf.Runtime; -namespace BedrockService.Service.Core -{ - public class Service : IService - { +namespace BedrockService.Service.Core { + public class Service : IService { private readonly IBedrockService _bedrockService; private Topshelf.Host _host; private readonly IBedrockLogger _logger; IHostApplicationLifetime _applicationLifetime; - public Service(IBedrockLogger logger, IBedrockService bedrockService, NetworkStrategyLookup lookup, IHostApplicationLifetime appLifetime) - { + public Service(IBedrockLogger logger, IBedrockService bedrockService, NetworkStrategyLookup lookup, IHostApplicationLifetime appLifetime) { _logger = logger; _bedrockService = bedrockService; _applicationLifetime = appLifetime; appLifetime.ApplicationStarted.Register(OnStarted); } - public async Task InitializeHost() - { - await Task.Run(() => - { - _host = HostFactory.New(hostConfig => - { + public async Task InitializeHost() { + await Task.Run(() => { + _host = HostFactory.New(hostConfig => { hostConfig.SetStartTimeout(TimeSpan.FromSeconds(10)); hostConfig.SetStopTimeout(TimeSpan.FromSeconds(10)); hostConfig.UseAssemblyInfoForServiceInfo(); - hostConfig.Service(settings => _bedrockService, s => - { + hostConfig.Service(settings => _bedrockService, s => { s.BeforeStartingService(_ => _logger.AppendLine("Starting service...")); - s.BeforeStoppingService(_ => - { + s.BeforeStoppingService(_ => { _logger.AppendLine("Stopping service..."); - foreach (IBedrockServer server in _bedrockService.GetAllServers()) - { + foreach (IBedrockServer server in _bedrockService.GetAllServers()) { server.SetServerStatus(BedrockServer.ServerStatus.Stopping); while (server.GetServerStatus() != BedrockServer.ServerStatus.Stopped) Thread.Sleep(100); @@ -48,26 +38,22 @@ await Task.Run(() => hostConfig.SetDisplayName("BedrockService"); hostConfig.SetServiceName("BedrockService"); hostConfig.UnhandledExceptionPolicy = UnhandledExceptionPolicyCode.LogErrorOnly; - hostConfig.AfterInstall(() => - { + hostConfig.AfterInstall(() => { _logger.AppendLine("Service install completed... Exiting!"); Task.Delay(1000).Wait(); _applicationLifetime.StopApplication(); }); - hostConfig.AfterUninstall(() => - { + hostConfig.AfterUninstall(() => { _logger.AppendLine("Service uninstall completed... Exiting!"); Task.Delay(1000).Wait(); _applicationLifetime.StopApplication(); }); - hostConfig.EnableServiceRecovery(src => - { + hostConfig.EnableServiceRecovery(src => { src.RestartService(delayInMinutes: 0); src.RestartService(delayInMinutes: 1); src.SetResetPeriod(days: 1); }); - hostConfig.OnException((ex) => - { + hostConfig.OnException((ex) => { _logger.AppendLine("Exception occured Main : " + ex.Message); }); }); @@ -75,18 +61,15 @@ await Task.Run(() => }); } - public Task StartAsync(CancellationToken cancellationToken) - { + public Task StartAsync(CancellationToken cancellationToken) { return InitializeHost(); } - public Task StopAsync(CancellationToken cancellationToken) - { + public Task StopAsync(CancellationToken cancellationToken) { return Task.Delay(100); } - private void OnStarted() - { + private void OnStarted() { Task.Run(() => { _host.Run(); }); } } diff --git a/BedrockService/Service/Logging/ServiceLogger.cs b/BedrockService/Service/Logging/ServiceLogger.cs index d24bb844..7fece697 100644 --- a/BedrockService/Service/Logging/ServiceLogger.cs +++ b/BedrockService/Service/Logging/ServiceLogger.cs @@ -1,10 +1,8 @@ using Newtonsoft.Json; using System.Text; -namespace BedrockService.Service.Logging -{ - public class ServiceLogger : IBedrockLogger - { +namespace BedrockService.Service.Logging { + public class ServiceLogger : IBedrockLogger { private readonly IServiceConfiguration _serviceConfiguration; private StringBuilder _outputString = new StringBuilder(); [NonSerialized] @@ -14,14 +12,12 @@ public class ServiceLogger : IBedrockLogger private readonly string _parent = "Service"; private readonly string _logPath; - public ServiceLogger(IProcessInfo processInfo, IServiceConfiguration serviceConfiguration) - { + public ServiceLogger(IProcessInfo processInfo, IServiceConfiguration serviceConfiguration) { _serviceConfiguration = serviceConfiguration; _logPath = $@"{processInfo.GetDirectory()}\Service\Logs"; _logToFile = bool.Parse(serviceConfiguration.GetProp("LogServiceToFile").ToString()); _logToConsole = true; - if (_logToFile) - { + if (_logToFile) { if (!Directory.Exists(_logPath)) Directory.CreateDirectory(_logPath); _logWriter = new StreamWriter($@"{_logPath}\ServiceLog_{_parent}_{DateTime.Now:yyyymmddhhmmss}.log", true); @@ -29,68 +25,54 @@ public ServiceLogger(IProcessInfo processInfo, IServiceConfiguration serviceConf } [JsonConstructor] - public ServiceLogger(IServiceConfiguration serviceConfiguration, string serverName) - { + public ServiceLogger(IServiceConfiguration serviceConfiguration, string serverName) { _serviceConfiguration = serviceConfiguration; _parent = serverName; _logToFile = false; _logToConsole = false; } - public void AppendLine(string text) - { - try - { + public void AppendLine(string text) { + try { _serviceConfiguration.GetLog().Add(text); - if (_logToFile && _logWriter != null) - { + if (_logToFile && _logWriter != null) { _logWriter.WriteLine(text); _logWriter.Flush(); } if (_logToConsole) Console.WriteLine(text); } - catch - { + catch { } } - public void AppendText(string text) - { - try - { + public void AppendText(string text) { + try { _serviceConfiguration.GetLog().Add(text); - if (_logToFile && _logWriter != null) - { + if (_logToFile && _logWriter != null) { _logWriter.Write(text); _logWriter.Flush(); } - if (_logToConsole) - { + if (_logToConsole) { Console.Write(text); Console.Out.Flush(); } } - catch - { + catch { } } - public int Count() - { + public int Count() { return _serviceConfiguration.GetLog().Count; } - public string FromIndex(int index) - { + public string FromIndex(int index) { return _serviceConfiguration.GetLog()[index]; } - public override string ToString() - { + public override string ToString() { _outputString = new StringBuilder(); - foreach (string s in _serviceConfiguration.GetLog()) - { + foreach (string s in _serviceConfiguration.GetLog()) { _outputString.Append(s); } return _outputString.ToString(); diff --git a/BedrockService/Service/Management/ConfigManager.cs b/BedrockService/Service/Management/ConfigManager.cs index 53d8ce4a..b0a1d1d4 100644 --- a/BedrockService/Service/Management/ConfigManager.cs +++ b/BedrockService/Service/Management/ConfigManager.cs @@ -3,12 +3,9 @@ using System.Runtime.Serialization.Formatters.Binary; using System.Security.Cryptography; using System.Text; -using System.Threading; -namespace BedrockService.Service.Management -{ - public class ConfigManager : IConfigurator - { +namespace BedrockService.Service.Management { + public class ConfigManager : IConfigurator { private readonly string _configDir; private readonly string _globalFile; private readonly string _clientKeyPath; @@ -22,8 +19,7 @@ public class ConfigManager : IConfigurator private RSAParameters _serviceKey; private RSAParameters _clientKey; - public ConfigManager(IProcessInfo processInfo, IServiceConfiguration serviceConfiguration, IBedrockLogger logger) - { + public ConfigManager(IProcessInfo processInfo, IServiceConfiguration serviceConfiguration, IBedrockLogger logger) { _processInfo = processInfo; _serviceConfiguration = serviceConfiguration; _logger = logger; @@ -33,10 +29,8 @@ public ConfigManager(IProcessInfo processInfo, IServiceConfiguration serviceConf _commsKeyPath = $@"{_processInfo.GetDirectory()}\Service\CommsKey.dat"; } - public async Task LoadAllConfigurations() - { - await Task.Run(() => - { + public async Task LoadAllConfigurations() { + await Task.Run(() => { BinaryFormatter formatter = new BinaryFormatter(); if (!Directory.Exists(_configDir)) Directory.CreateDirectory(_configDir); @@ -50,11 +44,9 @@ await Task.Run(() => Directory.CreateDirectory($@"{_configDir}\Backups"); if (File.Exists($@"{_configDir}\..\bedrock_ver.ini")) _loadedVersion = File.ReadAllText($@"{_configDir}\..\bedrock_ver.ini"); - if (!File.Exists(_commsKeyPath)) - { + if (!File.Exists(_commsKeyPath)) { RSACryptoServiceProvider rsa = new RSACryptoServiceProvider(2048); - using (AesCryptoServiceProvider aes = new AesCryptoServiceProvider()) - { + using (AesCryptoServiceProvider aes = new AesCryptoServiceProvider()) { CommsKeyContainer serviceKeys = new CommsKeyContainer(); CommsKeyContainer clientKeys = new CommsKeyContainer(); serviceKeys.LocalPrivateKey.SetPrivateKey(rsa.ExportParameters(true)); @@ -74,16 +66,13 @@ await Task.Run(() => rsa.Clear(); rsa.Dispose(); } - else - { - try - { + else { + try { _keyContainer = (CommsKeyContainer)formatter.Deserialize(File.Open(_commsKeyPath, FileMode.Open)); _serviceKey = _keyContainer.LocalPrivateKey.GetPrivateKey(); _clientKey = _keyContainer.RemotePublicKey.GetPrivateKey(); } - catch - { + catch { _logger.AppendLine("Error loading Encryption keys!"); } } @@ -92,8 +81,7 @@ await Task.Run(() => LoadGlobals(); _serviceConfiguration.GetServerList().Clear(); string[] files = Directory.GetFiles(_configDir, "*.conf"); - foreach (string file in files) - { + foreach (string file in files) { FileInfo FInfo = new FileInfo(file); string[] fileEntries = File.ReadAllLines(file); serverInfo = new ServerInfo(fileEntries, _serviceConfiguration.GetProp("ServersPath").ToString()); @@ -101,8 +89,7 @@ await Task.Run(() => LoadRegisteredPlayers(serverInfo); _serviceConfiguration.AddNewServerInfo(serverInfo); } - if (_serviceConfiguration.GetServerList().Count == 0) - { + if (_serviceConfiguration.GetServerList().Count == 0) { serverInfo = new ServerInfo(null, _serviceConfiguration.GetProp("ServersPath").ToString()); serverInfo.InitializeDefaults(); SaveServerProps(serverInfo, true); @@ -111,14 +98,12 @@ await Task.Run(() => }); } - public void SaveGlobalFile() - { + public void SaveGlobalFile() { string[] output = new string[_serviceConfiguration.GetAllProps().Count + 3]; int index = 0; output[index++] = "#Globals"; output[index++] = string.Empty; - foreach (Property prop in _serviceConfiguration.GetAllProps()) - { + foreach (Property prop in _serviceConfiguration.GetAllProps()) { output[index++] = $"{prop.KeyName}={prop}"; } output[index++] = string.Empty; @@ -126,24 +111,20 @@ public void SaveGlobalFile() File.WriteAllLines(_globalFile, output); } - public void LoadRegisteredPlayers(IServerConfiguration server) - { + public void LoadRegisteredPlayers(IServerConfiguration server) { string serverName = server.GetServerName(); string filePath = $@"{_configDir}\RegisteredPlayers\{serverName}.preg"; - if (!File.Exists(filePath)) - { + if (!File.Exists(filePath)) { File.Create(filePath).Close(); return; } - foreach (string entry in File.ReadLines(filePath)) - { + foreach (string entry in File.ReadLines(filePath)) { if (entry.StartsWith("#") || string.IsNullOrWhiteSpace(entry)) continue; string[] split = entry.Split(','); _logger.AppendLine($"Server \"{server.GetServerName()}\" Loaded registered player: {split[1]}"); IPlayer playerFound = server.GetPlayerByXuid(split[0]); - if (playerFound == null) - { + if (playerFound == null) { server.AddUpdatePlayer(new Player(split[0], split[1], DateTime.Now.Ticks.ToString(), "0", "0", split[3].ToLower() == "true", split[2], split[4].ToLower() == "true")); continue; } @@ -152,10 +133,8 @@ public void LoadRegisteredPlayers(IServerConfiguration server) } } - private void LoadGlobals() - { - if (File.Exists(_globalFile)) - { + private void LoadGlobals() { + if (File.Exists(_globalFile)) { _logger.AppendLine("Loading Globals..."); _serviceConfiguration.ProcessConfiguration(File.ReadAllLines(_globalFile)); _serviceConfiguration.SetServerVersion(_loadedVersion); @@ -166,44 +145,32 @@ private void LoadGlobals() SaveGlobalFile(); } - private int RoundOff(int i) - { + private int RoundOff(int i) { return ((int)Math.Round(i / 10.0)) * 10; } - public async Task ReplaceServerBuild(IServerConfiguration server) - { - await Task.Run(() => - { - try - { + public async Task ReplaceServerBuild(IServerConfiguration server) { + await Task.Run(() => { + try { if (!Directory.Exists(server.GetProp("ServerPath").ToString())) Directory.CreateDirectory(server.GetProp("ServerPath").ToString()); - while (_serviceConfiguration.GetServerVersion() == null || _serviceConfiguration.GetServerVersion() == "None") - { + while (_serviceConfiguration.GetServerVersion() == null || _serviceConfiguration.GetServerVersion() == "None") { Thread.Sleep(150); } - using (ZipArchive archive = ZipFile.OpenRead($@"{_processInfo.GetDirectory()}\Server\MCSFiles\Update_{ _serviceConfiguration.GetServerVersion()}.zip")) - { + using (ZipArchive archive = ZipFile.OpenRead($@"{_processInfo.GetDirectory()}\Server\MCSFiles\Update_{ _serviceConfiguration.GetServerVersion()}.zip")) { int fileCount = archive.Entries.Count; - for (int i = 0; i < fileCount; i++) - { - if (i % (RoundOff(fileCount) / 6) == 0) - { + for (int i = 0; i < fileCount; i++) { + if (i % (RoundOff(fileCount) / 6) == 0) { _logger.AppendLine($"Extracting server files for server {server.GetServerName()}, {Math.Round((double)i / (double)fileCount, 2) * 100}% completed..."); } - if (!archive.Entries[i].FullName.EndsWith("/")) - { - if (File.Exists($@"{server.GetProp("ServerPath")}\{archive.Entries[i].FullName.Replace('/', '\\')}")) - { + if (!archive.Entries[i].FullName.EndsWith("/")) { + if (File.Exists($@"{server.GetProp("ServerPath")}\{archive.Entries[i].FullName.Replace('/', '\\')}")) { File.Delete($@"{server.GetProp("ServerPath")}\{archive.Entries[i].FullName.Replace('/', '\\')}"); } archive.Entries[i].ExtractToFile($@"{server.GetProp("ServerPath")}\{archive.Entries[i].FullName.Replace('/', '\\')}"); } - else - { - if (!Directory.Exists($@"{server.GetProp("ServerPath")}\{archive.Entries[i].FullName.Replace('/', '\\')}")) - { + else { + if (!Directory.Exists($@"{server.GetProp("ServerPath")}\{archive.Entries[i].FullName.Replace('/', '\\')}")) { Directory.CreateDirectory($@"{server.GetProp("ServerPath")}\{archive.Entries[i].FullName.Replace('/', '\\')}"); } } @@ -212,30 +179,25 @@ await Task.Run(() => } File.Copy(server.GetProp("ServerPath").ToString() + "\\bedrock_server.exe", server.GetProp("ServerPath").ToString() + "\\" + server.GetProp("ServerExeName").ToString(), true); } - catch (Exception e) - { + catch (Exception e) { _logger.AppendLine($"ERROR: Got an exception deleting entire directory! {e.Message}"); } }); } - public void LoadPlayerDatabase(IServerConfiguration server) - { + public void LoadPlayerDatabase(IServerConfiguration server) { string filePath = $@"{_configDir}\KnownPlayers\{server.GetServerName()}.playerdb"; - if (!File.Exists(filePath)) - { + if (!File.Exists(filePath)) { File.Create(filePath).Close(); return; } - foreach (string entry in File.ReadLines(filePath)) - { + foreach (string entry in File.ReadLines(filePath)) { if (entry.StartsWith("#") || string.IsNullOrWhiteSpace(entry)) continue; string[] split = entry.Split(','); _logger.AppendLine($"Server \"{server.GetServerName()}\" loaded known player: {split[1]}"); IPlayer playerFound = server.GetPlayerByXuid(split[0]); - if (playerFound == null) - { + if (playerFound == null) { server.AddUpdatePlayer(new Player(split[0], split[1], split[2], split[3], split[4], false, server.GetProp("default-player-permission-level").ToString(), false)); continue; } @@ -244,28 +206,22 @@ public void LoadPlayerDatabase(IServerConfiguration server) } } - public void SaveKnownPlayerDatabase(IServerConfiguration server) - { - lock (_fileLock) - { + public void SaveKnownPlayerDatabase(IServerConfiguration server) { + lock (_fileLock) { string filePath = $@"{_configDir}\KnownPlayers\{server.GetServerName()}.playerdb"; - if (File.Exists(filePath)) - { + if (File.Exists(filePath)) { File.Copy(filePath, $@"{_configDir}\KnownPlayers\Backups\{server.GetServerName()}_{DateTime.Now:mmddyyhhmmssff}.dbbak", true); } TextWriter writer = new StreamWriter(filePath); - foreach (Player entry in server.GetPlayerList()) - { + foreach (Player entry in server.GetPlayerList()) { writer.WriteLine(entry.ToString("Known")); } writer.Flush(); writer.Close(); } - lock (_fileLock) - { + lock (_fileLock) { string filePath = $@"{_configDir}\RegisteredPlayers\{server.GetServerName()}.preg"; - if (File.Exists(filePath)) - { + if (File.Exists(filePath)) { File.Copy(filePath, $@"{_configDir}\RegisteredPlayers\Backups\{server.GetServerName()}_{DateTime.Now:mmddyyhhmmssff}.bak", true); } TextWriter writer = new StreamWriter(filePath); @@ -273,8 +229,7 @@ public void SaveKnownPlayerDatabase(IServerConfiguration server) writer.WriteLine("# Register player entries: PlayerEntry=xuid,username,permission,isWhitelisted,ignoreMaxPlayers"); writer.WriteLine("# Example: 1234111222333444,TestUser,visitor,false,false"); writer.WriteLine(""); - foreach (IPlayer player in server.GetPlayerList()) - { + foreach (IPlayer player in server.GetPlayerList()) { if (!player.IsDefaultRegistration()) writer.WriteLine(player.ToString("Registered")); } @@ -283,15 +238,12 @@ public void SaveKnownPlayerDatabase(IServerConfiguration server) } } - public void WriteJSONFiles(IServerConfiguration server) - { + public void WriteJSONFiles(IServerConfiguration server) { StringBuilder sb = new StringBuilder(); sb.Append("[\n"); - foreach (IPlayer player in server.GetPlayerList()) - { - if (!player.IsDefaultRegistration() && player.IsPlayerWhitelisted()) - { + foreach (IPlayer player in server.GetPlayerList()) { + if (!player.IsDefaultRegistration() && player.IsPlayerWhitelisted()) { sb.Append("\t{\n"); sb.Append($"\t\t\"ignoresPlayerLimit\": {player.PlayerIgnoresLimit().ToString().ToLower()},\n"); sb.Append($"\t\t\"name\": \"{player.GetUsername()}\",\n"); @@ -299,8 +251,7 @@ public void WriteJSONFiles(IServerConfiguration server) sb.Append("\t},\n"); } } - if (sb.Length > 2) - { + if (sb.Length > 2) { sb.Remove(sb.Length - 2, 2); } sb.Append("\n]"); @@ -308,48 +259,39 @@ public void WriteJSONFiles(IServerConfiguration server) sb = new StringBuilder(); sb.Append("[\n"); - foreach (Player player in server.GetPlayerList()) - { - if (!player.IsDefaultRegistration()) - { + foreach (Player player in server.GetPlayerList()) { + if (!player.IsDefaultRegistration()) { sb.Append("\t{\n"); sb.Append($"\t\t\"permission\": \"{player.GetPermissionLevel()}\",\n"); sb.Append($"\t\t\"xuid\": \"{player.GetXUID()}\"\n"); sb.Append("\t},\n"); } } - if (sb.Length > 2) - { + if (sb.Length > 2) { sb.Remove(sb.Length - 2, 2); } sb.Append("\n]"); File.WriteAllText($@"{server.GetProp("ServerPath")}\permissions.json", sb.ToString()); } - public void SaveServerProps(IServerConfiguration server, bool SaveServerInfo) - { + public void SaveServerProps(IServerConfiguration server, bool SaveServerInfo) { int index = 0; string[] output = new string[5 + server.GetAllProps().Count + server.GetStartCommands().Count]; output[index++] = "#Server"; - foreach (Property prop in server.GetAllProps()) - { + foreach (Property prop in server.GetAllProps()) { output[index++] = $"{prop.KeyName}={prop}"; } - if (!SaveServerInfo) - { - if (!Directory.Exists(server.GetProp("ServerPath").ToString())) - { + if (!SaveServerInfo) { + if (!Directory.Exists(server.GetProp("ServerPath").ToString())) { Directory.CreateDirectory(server.GetProp("ServerPath").ToString()); } File.WriteAllLines($@"{server.GetProp("ServerPath")}\server.properties", output); } - else - { + else { output[index++] = string.Empty; output[index++] = "#StartCmds"; - foreach (StartCmdEntry startCmd in server.GetStartCommands()) - { + foreach (StartCmdEntry startCmd in server.GetStartCommands()) { output[index++] = $"AddStartCmd={startCmd.Command}"; } output[index++] = string.Empty; @@ -357,21 +299,17 @@ public void SaveServerProps(IServerConfiguration server, bool SaveServerInfo) File.WriteAllLines($@"{_configDir}\{server.GetFileName()}", output); if (server.GetProp("ServerPath").ToString() == null) server.GetProp("ServerPath").SetValue(server.GetProp("ServerPath").DefaultValue); - if (!Directory.Exists(server.GetProp("ServerPath").ToString())) - { + if (!Directory.Exists(server.GetProp("ServerPath").ToString())) { Directory.CreateDirectory(server.GetProp("ServerPath").ToString()); } File.WriteAllLines($@"{server.GetProp("ServerPath")}\server.properties", output); } } - public void RemoveServerConfigs(IServerConfiguration serverInfo, NetworkMessageFlags flag) - { - try - { + public void RemoveServerConfigs(IServerConfiguration serverInfo, NetworkMessageFlags flag) { + try { File.Delete($@"{_configDir}\{serverInfo.GetFileName()}"); - switch (flag) - { + switch (flag) { case NetworkMessageFlags.RemoveBckPly: if (DeleteBackups(serverInfo)) _logger.AppendLine($"Deleted Backups for server {serverInfo.GetServerName()}"); @@ -419,62 +357,48 @@ public void RemoveServerConfigs(IServerConfiguration serverInfo, NetworkMessageF catch { } } - public List EnumerateBackupsForServer(byte serverIndex) - { + public List EnumerateBackupsForServer(byte serverIndex) { string serverName = _serviceConfiguration.GetServerInfoByIndex(serverIndex).GetServerName(); List newList = new List(); - try - { - foreach (DirectoryInfo dir in new DirectoryInfo($@"{_serviceConfiguration.GetProp("BackupPath")}\{serverName}").GetDirectories()) - { + try { + foreach (DirectoryInfo dir in new DirectoryInfo($@"{_serviceConfiguration.GetProp("BackupPath")}\{serverName}").GetDirectories()) { string[] splitName = dir.Name.Split('_'); newList.Add(new Property(dir.Name, new DateTime(long.Parse(splitName[1])).ToString("G"))); } } - catch (IOException) - { + catch (IOException) { return newList; } return newList; } - public void DeleteBackupsForServer(byte serverIndex, List list) - { + public void DeleteBackupsForServer(byte serverIndex, List list) { string serverName = _serviceConfiguration.GetServerInfoByIndex(serverIndex).GetServerName(); - try - { + try { foreach (string deleteDir in list) foreach (DirectoryInfo dir in new DirectoryInfo($@"{_serviceConfiguration.GetProp("BackupPath")}\{serverName}").GetDirectories()) - if (dir.Name == deleteDir) - { + if (dir.Name == deleteDir) { new FileUtils(_processInfo.GetDirectory()).DeleteFilesRecursively(new DirectoryInfo($@"{_serviceConfiguration.GetProp("BackupPath")}\{serverName}\{deleteDir}"), true); _logger.AppendLine($"Deleted backup {deleteDir}."); } } - catch (IOException e) - { + catch (IOException e) { _logger.AppendLine($"Error deleting selected backups! {e.Message}"); } } - private bool DeleteBackups(IServerConfiguration serverInfo) - { - try - { + private bool DeleteBackups(IServerConfiguration serverInfo) { + try { string configBackupPath = ""; DirectoryInfo backupDirInfo = new DirectoryInfo($@"{configBackupPath}\{serverInfo.GetServerName()}"); DirectoryInfo configBackupDirInfo = new DirectoryInfo($@"{_configDir}\Backups"); - foreach (DirectoryInfo dir in backupDirInfo.GetDirectories()) - { - if (dir.Name.Contains($"{serverInfo.GetServerName()}")) - { + foreach (DirectoryInfo dir in backupDirInfo.GetDirectories()) { + if (dir.Name.Contains($"{serverInfo.GetServerName()}")) { dir.Delete(true); } } - foreach (FileInfo file in configBackupDirInfo.GetFiles()) - { - if (file.Name.Contains($"{serverInfo.GetServerName()}_")) - { + foreach (FileInfo file in configBackupDirInfo.GetFiles()) { + if (file.Name.Contains($"{serverInfo.GetServerName()}_")) { file.Delete(); } } @@ -483,29 +407,21 @@ private bool DeleteBackups(IServerConfiguration serverInfo) catch { return false; } } - private bool DeleteServerFiles(IServerConfiguration serverInfo) - { - try - { + private bool DeleteServerFiles(IServerConfiguration serverInfo) { + try { new FileUtils(_processInfo.GetDirectory()).DeleteFilesRecursively(new DirectoryInfo(serverInfo.GetProp("ServerPath").ToString()), false); return true; } catch { return false; } } - private bool DeletePlayerFiles(IServerConfiguration serverInfo) - { - try - { + private bool DeletePlayerFiles(IServerConfiguration serverInfo) { + try { DirectoryInfo configDirInfo = new DirectoryInfo(_configDir); - foreach (DirectoryInfo dir in configDirInfo.GetDirectories()) - { - if (dir.Name == "KnownPlayers" || dir.Name == "RegisteredPlayers") - { - foreach (FileInfo file in dir.GetFiles()) - { - if (file.Name.Contains($"{serverInfo.GetServerName()}")) - { + foreach (DirectoryInfo dir in configDirInfo.GetDirectories()) { + if (dir.Name == "KnownPlayers" || dir.Name == "RegisteredPlayers") { + foreach (FileInfo file in dir.GetFiles()) { + if (file.Name.Contains($"{serverInfo.GetServerName()}")) { file.Delete(); } } @@ -518,13 +434,11 @@ private bool DeletePlayerFiles(IServerConfiguration serverInfo) public CommsKeyContainer GetKeyContainer() => _keyContainer; - public Task LoadConfiguration(IBedrockConfiguration configuration) - { + public Task LoadConfiguration(IBedrockConfiguration configuration) { throw new NotImplementedException(); } - public Task SaveConfiguration(IBedrockConfiguration configuration) - { + public Task SaveConfiguration(IBedrockConfiguration configuration) { throw new NotImplementedException(); } } diff --git a/BedrockService/Service/Management/IConfigurator.cs b/BedrockService/Service/Management/IConfigurator.cs index a63ca7af..28d5c7cd 100644 --- a/BedrockService/Service/Management/IConfigurator.cs +++ b/BedrockService/Service/Management/IConfigurator.cs @@ -1,7 +1,5 @@ -namespace BedrockService.Service.Management -{ - public interface IConfigurator - { +namespace BedrockService.Service.Management { + public interface IConfigurator { void DeleteBackupsForServer(byte serverIndex, List list); List EnumerateBackupsForServer(byte serverIndex); Task LoadAllConfigurations(); diff --git a/BedrockService/Service/Networking/ITCPListener.cs b/BedrockService/Service/Networking/ITCPListener.cs index 818659cb..173edf42 100644 --- a/BedrockService/Service/Networking/ITCPListener.cs +++ b/BedrockService/Service/Networking/ITCPListener.cs @@ -1,9 +1,7 @@ using BedrockService.Service.Networking.MessageInterfaces; -namespace BedrockService.Service.Networking -{ - public interface ITCPListener : IMessageSender - { +namespace BedrockService.Service.Networking { + public interface ITCPListener : IMessageSender { Task StartListening(); void ResetListener(); diff --git a/BedrockService/Service/Networking/IUpdater.cs b/BedrockService/Service/Networking/IUpdater.cs index ce70d144..8653d1a2 100644 --- a/BedrockService/Service/Networking/IUpdater.cs +++ b/BedrockService/Service/Networking/IUpdater.cs @@ -1,7 +1,5 @@ -namespace BedrockService.Service.Networking -{ - public interface IUpdater - { +namespace BedrockService.Service.Networking { + public interface IUpdater { Task CheckUpdates(); Task FetchBuild(string path, string version); bool CheckVersionChanged(); diff --git a/BedrockService/Service/Networking/MessageInterfaces/IFlaggedMessageParser.cs b/BedrockService/Service/Networking/MessageInterfaces/IFlaggedMessageParser.cs index 43deb217..715b1347 100644 --- a/BedrockService/Service/Networking/MessageInterfaces/IFlaggedMessageParser.cs +++ b/BedrockService/Service/Networking/MessageInterfaces/IFlaggedMessageParser.cs @@ -1,7 +1,5 @@ -namespace BedrockService.Service.Networking.MessageInterfaces -{ - public interface IFlaggedMessageParser - { +namespace BedrockService.Service.Networking.MessageInterfaces { + public interface IFlaggedMessageParser { void ParseMessage(byte[] data, byte serverIndex, NetworkMessageFlags flag); } } diff --git a/BedrockService/Service/Networking/MessageInterfaces/IMessageParser.cs b/BedrockService/Service/Networking/MessageInterfaces/IMessageParser.cs index e746c9d2..23218552 100644 --- a/BedrockService/Service/Networking/MessageInterfaces/IMessageParser.cs +++ b/BedrockService/Service/Networking/MessageInterfaces/IMessageParser.cs @@ -1,7 +1,5 @@ -namespace BedrockService.Service.Networking.MessageInterfaces -{ - public interface IMessageParser - { +namespace BedrockService.Service.Networking.MessageInterfaces { + public interface IMessageParser { void ParseMessage(byte[] data, byte serverIndex); } } diff --git a/BedrockService/Service/Networking/MessageInterfaces/IMessageSender.cs b/BedrockService/Service/Networking/MessageInterfaces/IMessageSender.cs index 05cbd3eb..10653abb 100644 --- a/BedrockService/Service/Networking/MessageInterfaces/IMessageSender.cs +++ b/BedrockService/Service/Networking/MessageInterfaces/IMessageSender.cs @@ -1,7 +1,5 @@ -namespace BedrockService.Service.Networking.MessageInterfaces -{ - public interface IMessageSender - { +namespace BedrockService.Service.Networking.MessageInterfaces { + public interface IMessageSender { void SendData(byte[] bytes, NetworkMessageSource source, NetworkMessageDestination destination, byte serverIndex, NetworkMessageTypes type, NetworkMessageFlags status); void SendData(NetworkMessageSource source, NetworkMessageDestination destination, NetworkMessageTypes type); diff --git a/BedrockService/Service/Networking/NetworkStrategyLookup.cs b/BedrockService/Service/Networking/NetworkStrategyLookup.cs index 52850a19..56a64e4f 100644 --- a/BedrockService/Service/Networking/NetworkStrategyLookup.cs +++ b/BedrockService/Service/Networking/NetworkStrategyLookup.cs @@ -7,17 +7,13 @@ using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System.Text; -using System.Threading; -namespace BedrockService.Service.Networking -{ - public class NetworkStrategyLookup - { +namespace BedrockService.Service.Networking { + public class NetworkStrategyLookup { private readonly Dictionary _standardMessageLookup; private readonly Dictionary _flaggedMessageLookup; - public NetworkStrategyLookup(ITCPListener messageSender, IBedrockService service, IBedrockLogger logger, IConfigurator configurator, IServiceConfiguration serviceConfiguration, IProcessInfo processInfo, IUpdater updater) - { + public NetworkStrategyLookup(ITCPListener messageSender, IBedrockService service, IBedrockLogger logger, IConfigurator configurator, IServiceConfiguration serviceConfiguration, IProcessInfo processInfo, IUpdater updater) { _standardMessageLookup = new Dictionary() { {NetworkMessageTypes.DelBackups, new DeleteBackups(configurator) }, @@ -48,19 +44,15 @@ public NetworkStrategyLookup(ITCPListener messageSender, IBedrockService service messageSender.SetStrategyDictionaries(_standardMessageLookup, _flaggedMessageLookup); } - class DeleteBackups : IMessageParser - { + class DeleteBackups : IMessageParser { private readonly IConfigurator _configurator; - public DeleteBackups(IConfigurator configurator) - { + public DeleteBackups(IConfigurator configurator) { _configurator = configurator; } - public void ParseMessage(byte[] data, byte serverIndex) - { - JsonSerializerSettings settings = new JsonSerializerSettings() - { + public void ParseMessage(byte[] data, byte serverIndex) { + JsonSerializerSettings settings = new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All }; string stringData = Encoding.UTF8.GetString(data, 5, data.Length - 5); @@ -69,57 +61,47 @@ public void ParseMessage(byte[] data, byte serverIndex) } } - class ServerBackupAll : IMessageParser - { + class ServerBackupAll : IMessageParser { private readonly IMessageSender _messageSender; private readonly IBedrockService _service; - public ServerBackupAll(IMessageSender messageSender, IBedrockService service) - { + public ServerBackupAll(IMessageSender messageSender, IBedrockService service) { _messageSender = messageSender; _service = service; } - public void ParseMessage(byte[] data, byte serverIndex) - { + public void ParseMessage(byte[] data, byte serverIndex) { foreach (IBedrockServer server in _service.GetAllServers()) server.RestartServer(true); _messageSender.SendData(NetworkMessageSource.Service, NetworkMessageDestination.Client, NetworkMessageTypes.UICallback); } } - class ServerBackup : IMessageParser - { + class ServerBackup : IMessageParser { private readonly IMessageSender _messageSender; private readonly IBedrockService _service; - public ServerBackup(IMessageSender messageSender, IBedrockService service) - { + public ServerBackup(IMessageSender messageSender, IBedrockService service) { _messageSender = messageSender; _service = service; } - public void ParseMessage(byte[] data, byte serverIndex) - { + public void ParseMessage(byte[] data, byte serverIndex) { _service.GetBedrockServerByIndex(serverIndex).InitializeBackup(); _messageSender.SendData(NetworkMessageSource.Service, NetworkMessageDestination.Client, NetworkMessageTypes.UICallback); } } - class EnumBackups : IMessageParser - { + class EnumBackups : IMessageParser { private readonly IConfigurator _configurator; private readonly IMessageSender _messageSender; - public EnumBackups(IConfigurator configurator, IMessageSender messageSender) - { + public EnumBackups(IConfigurator configurator, IMessageSender messageSender) { _configurator = configurator; _messageSender = messageSender; } - public void ParseMessage(byte[] data, byte serverIndex) - { + public void ParseMessage(byte[] data, byte serverIndex) { Formatting indented = Formatting.Indented; - JsonSerializerSettings settings = new JsonSerializerSettings() - { + JsonSerializerSettings settings = new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All }; byte[] serializeToBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(_configurator.EnumerateBackupsForServer(serverIndex), indented, settings)); @@ -128,32 +110,27 @@ public void ParseMessage(byte[] data, byte serverIndex) } } - class ServerPropUpdate : IMessageParser - { + class ServerPropUpdate : IMessageParser { private readonly IServiceConfiguration _serviceConfiguration; private readonly IMessageSender _messageSender; private readonly IBedrockService _bedrockService; private readonly IConfigurator _configurator; - public ServerPropUpdate(IConfigurator configurator, IServiceConfiguration serviceConfiguration, IMessageSender messageSender, IBedrockService bedrockService) - { + public ServerPropUpdate(IConfigurator configurator, IServiceConfiguration serviceConfiguration, IMessageSender messageSender, IBedrockService bedrockService) { _configurator = configurator; _serviceConfiguration = serviceConfiguration; _messageSender = messageSender; _bedrockService = bedrockService; } - public void ParseMessage(byte[] data, byte serverIndex) - { - JsonSerializerSettings settings = new JsonSerializerSettings() - { + public void ParseMessage(byte[] data, byte serverIndex) { + JsonSerializerSettings settings = new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All }; string stringData = Encoding.UTF8.GetString(data, 5, data.Length - 5); List propList = JsonConvert.DeserializeObject>(stringData, settings); Property prop = propList.FirstOrDefault(p => p.KeyName == "server-name"); - if (prop == null) - { + if (prop == null) { _serviceConfiguration.SetAllProps(propList); _configurator.SaveGlobalFile(); _bedrockService.RestartService(); @@ -163,8 +140,7 @@ public void ParseMessage(byte[] data, byte serverIndex) _serviceConfiguration.GetServerInfoByIndex(serverIndex).SetAllProps(propList); _configurator.SaveServerProps(_serviceConfiguration.GetServerInfoByIndex(serverIndex), true); _bedrockService.GetBedrockServerByIndex(serverIndex).SetServerStatus(BedrockServer.ServerStatus.Stopping); - while (_bedrockService.GetBedrockServerByIndex(serverIndex).GetServerStatus() == BedrockServer.ServerStatus.Stopping) - { + while (_bedrockService.GetBedrockServerByIndex(serverIndex).GetServerStatus() == BedrockServer.ServerStatus.Stopping) { Thread.Sleep(100); } _bedrockService.GetBedrockServerByIndex(serverIndex).SetServerStatus(BedrockServer.ServerStatus.Starting); @@ -172,38 +148,32 @@ public void ParseMessage(byte[] data, byte serverIndex) } } - class ServerRestart : IMessageParser - { + class ServerRestart : IMessageParser { private readonly IMessageSender _messageSender; private readonly IBedrockService _service; - public ServerRestart(IMessageSender messageSender, IBedrockService service) - { + public ServerRestart(IMessageSender messageSender, IBedrockService service) { _messageSender = messageSender; _service = service; } - public void ParseMessage(byte[] data, byte serverIndex) - { + public void ParseMessage(byte[] data, byte serverIndex) { _service.GetBedrockServerByIndex(serverIndex).RestartServer(false); _messageSender.SendData(NetworkMessageSource.Service, NetworkMessageDestination.Client, NetworkMessageTypes.UICallback); } } - class ServerCommand : IMessageParser - { + class ServerCommand : IMessageParser { private readonly IBedrockService _service; private readonly IBedrockLogger _logger; private readonly IMessageSender _messageSender; - public ServerCommand(IMessageSender messageSender, IBedrockService service, IBedrockLogger logger) - { + public ServerCommand(IMessageSender messageSender, IBedrockService service, IBedrockLogger logger) { _messageSender = messageSender; _service = service; _logger = logger; } - public void ParseMessage(byte[] data, byte serverIndex) - { + public void ParseMessage(byte[] data, byte serverIndex) { string stringData = Encoding.UTF8.GetString(data, 5, data.Length - 5); _service.GetBedrockServerByIndex(serverIndex).WriteToStandardIn(stringData); _logger.AppendLine($"Sent command {stringData} to stdInput stream"); @@ -211,29 +181,25 @@ public void ParseMessage(byte[] data, byte serverIndex) } } - class PackList : IMessageParser - { + class PackList : IMessageParser { private readonly IMessageSender _messageSender; private readonly IServiceConfiguration _serviceConfiguration; private readonly IProcessInfo _processInfo; private readonly IBedrockLogger _logger; - public PackList(IMessageSender messageSender, IProcessInfo processInfo, IServiceConfiguration serviceConfiguration, IBedrockLogger logger) - { + public PackList(IMessageSender messageSender, IProcessInfo processInfo, IServiceConfiguration serviceConfiguration, IBedrockLogger logger) { _logger = logger; _messageSender = messageSender; _serviceConfiguration = serviceConfiguration; _processInfo = processInfo; } - public void ParseMessage(byte[] data, byte serverIndex) - { + public void ParseMessage(byte[] data, byte serverIndex) { if (!File.Exists($@"{_processInfo.GetDirectory()}\Server\stock_packs.json")) File.Copy($@"{_serviceConfiguration.GetServerInfoByIndex(serverIndex).GetProp("ServerPath")}\valid_known_packs.json", $@"{_processInfo.GetDirectory()}\Server\stock_packs.json"); MinecraftKnownPacksClass knownPacks = new MinecraftKnownPacksClass($@"{_serviceConfiguration.GetServerInfoByIndex(serverIndex).GetProp("ServerPath")}\valid_known_packs.json", $@"{_processInfo.GetDirectory()}\Server\stock_packs.json"); List list = new List(); - foreach (MinecraftKnownPacksClass.KnownPack pack in knownPacks.KnownPacks) - { + foreach (MinecraftKnownPacksClass.KnownPack pack in knownPacks.KnownPacks) { MinecraftPackParser currentParser = new MinecraftPackParser(_logger, _processInfo); currentParser.ParseDirectory($@"{_serviceConfiguration.GetServerInfoByIndex(serverIndex).GetProp("ServerPath")}\{pack.path.Replace(@"/", @"\")}"); list.AddRange(currentParser.FoundPacks); @@ -243,27 +209,23 @@ public void ParseMessage(byte[] data, byte serverIndex) } } - class RemovePack : IMessageParser - { + class RemovePack : IMessageParser { private readonly IMessageSender _messageSender; private readonly IServiceConfiguration _serviceConfiguration; private readonly IProcessInfo _processInfo; private readonly IBedrockLogger _logger; - public RemovePack(IMessageSender messageSender, IProcessInfo processInfo, IServiceConfiguration serviceConfiguration, IBedrockLogger logger) - { + public RemovePack(IMessageSender messageSender, IProcessInfo processInfo, IServiceConfiguration serviceConfiguration, IBedrockLogger logger) { _messageSender = messageSender; _serviceConfiguration = serviceConfiguration; _processInfo = processInfo; _logger = logger; } - public void ParseMessage(byte[] data, byte serverIndex) - { + public void ParseMessage(byte[] data, byte serverIndex) { string stringData = Encoding.UTF8.GetString(data, 5, data.Length - 5); MinecraftKnownPacksClass knownPacks = new MinecraftKnownPacksClass($@"{_serviceConfiguration.GetServerInfoByIndex(serverIndex).GetProp("ServerPath")}\valid_known_packs.json", $@"{_processInfo.GetDirectory()}\Server\stock_packs.json"); - JsonSerializerSettings settings = new JsonSerializerSettings() - { + JsonSerializerSettings settings = new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All }; List container = JsonConvert.DeserializeObject>(stringData, settings); @@ -273,19 +235,16 @@ public void ParseMessage(byte[] data, byte serverIndex) } } - class LevelEditRequest : IMessageParser - { + class LevelEditRequest : IMessageParser { private readonly IMessageSender _messageSender; private readonly IServiceConfiguration _serviceConfiguration; - public LevelEditRequest(IMessageSender messageSender, IServiceConfiguration serviceConfiguration) - { + public LevelEditRequest(IMessageSender messageSender, IServiceConfiguration serviceConfiguration) { _messageSender = messageSender; _serviceConfiguration = serviceConfiguration; } - public void ParseMessage(byte[] data, byte serverIndex) - { + public void ParseMessage(byte[] data, byte serverIndex) { IServerConfiguration server = _serviceConfiguration.GetServerInfoByIndex(serverIndex); string pathToLevelDat = $@"{_serviceConfiguration.GetServerInfoByIndex(serverIndex).GetProp("ServerPath")}\worlds\{server.GetProp("level-name")}\level.dat"; byte[] levelDatToBytes = File.ReadAllBytes(pathToLevelDat); @@ -293,37 +252,30 @@ public void ParseMessage(byte[] data, byte serverIndex) } } - class PlayersUpdate : IMessageParser - { + class PlayersUpdate : IMessageParser { private readonly IMessageSender _messageSender; private readonly IServiceConfiguration _serviceConfiguration; private readonly IConfigurator _configurator; private readonly IBedrockService _service; - public PlayersUpdate(IConfigurator configurator, IMessageSender messageSender, IServiceConfiguration serviceConfiguration, IBedrockService service) - { + public PlayersUpdate(IConfigurator configurator, IMessageSender messageSender, IServiceConfiguration serviceConfiguration, IBedrockService service) { _service = service; _configurator = configurator; _messageSender = messageSender; _serviceConfiguration = serviceConfiguration; } - public void ParseMessage(byte[] data, byte serverIndex) - { + public void ParseMessage(byte[] data, byte serverIndex) { string stringData = Encoding.UTF8.GetString(data, 5, data.Length - 5); - JsonSerializerSettings settings = new JsonSerializerSettings() - { + JsonSerializerSettings settings = new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All }; List fetchedPlayers = JsonConvert.DeserializeObject>(stringData, settings); - foreach (IPlayer player in fetchedPlayers) - { - try - { + foreach (IPlayer player in fetchedPlayers) { + try { _serviceConfiguration.GetServerInfoByIndex(serverIndex).AddUpdatePlayer(player); } - catch (Exception) - { + catch (Exception) { } } _configurator.SaveKnownPlayerDatabase(_serviceConfiguration.GetServerInfoByIndex(serverIndex)); @@ -335,46 +287,38 @@ public void ParseMessage(byte[] data, byte serverIndex) } } - class PackFile : IMessageParser - { + class PackFile : IMessageParser { private readonly IMessageSender _messageSender; private readonly IServiceConfiguration _serviceConfiguration; private readonly IProcessInfo _serviceProcessInfo; private readonly IBedrockLogger _logger; - public PackFile(IMessageSender messageSender, IServiceConfiguration serviceConfiguration, IProcessInfo serviceProcessInfo, IBedrockLogger logger) - { + public PackFile(IMessageSender messageSender, IServiceConfiguration serviceConfiguration, IProcessInfo serviceProcessInfo, IBedrockLogger logger) { _messageSender = messageSender; _logger = logger; _serviceProcessInfo = serviceProcessInfo; _serviceConfiguration = serviceConfiguration; } - public void ParseMessage(byte[] data, byte serverIndex) - { + public void ParseMessage(byte[] data, byte serverIndex) { MinecraftPackParser archiveParser = new MinecraftPackParser(data, _logger, _serviceProcessInfo); - foreach (MinecraftPackContainer container in archiveParser.FoundPacks) - { + foreach (MinecraftPackContainer container in archiveParser.FoundPacks) { string serverPath = _serviceConfiguration.GetServerInfoByIndex(serverIndex).GetProp("ServerPath").ToString(); string levelName = _serviceConfiguration.GetServerInfoByIndex(serverIndex).GetProp("level-name").ToString(); string filePath = null; FileUtils fileUtils = new FileUtils(_serviceProcessInfo.GetDirectory()); - if (container.ManifestType == "WorldPack") - { + if (container.ManifestType == "WorldPack") { fileUtils.CopyFilesRecursively(new DirectoryInfo(container.PackContentLocation), new DirectoryInfo($@"{serverPath}\worlds\{container.FolderName}")); } - if (container.ManifestType == "data") - { + if (container.ManifestType == "data") { fileUtils.CopyFilesRecursively(new DirectoryInfo(container.PackContentLocation), new DirectoryInfo($@"{serverPath}\behavior_packs\{container.FolderName}")); filePath = $@"{serverPath}\worlds\{levelName}\world_behavior_packs.json"; } - if (container.ManifestType == "resources") - { + if (container.ManifestType == "resources") { fileUtils.CopyFilesRecursively(new DirectoryInfo(container.PackContentLocation), new DirectoryInfo($@"{serverPath}\resource_packs\{container.FolderName}")); filePath = $@"{serverPath}\worlds\{levelName}\world_resource_packs.json"; } - if(filePath != null) - { + if (filePath != null) { WorldPacksJsonModel worldPackManifentClass = new WorldPacksJsonModel(container.JsonManifest.header.uuid, container.JsonManifest.header.version); List list = new List(); list.Add(worldPackManifentClass); @@ -385,19 +329,16 @@ public void ParseMessage(byte[] data, byte serverIndex) } } - class LevelEditFile : IMessageParser - { + class LevelEditFile : IMessageParser { private readonly IServiceConfiguration _serviceConfiguration; private readonly IBedrockService _bedrockService; - public LevelEditFile(IServiceConfiguration serviceConfiguration, IBedrockService bedrockService) - { + public LevelEditFile(IServiceConfiguration serviceConfiguration, IBedrockService bedrockService) { _bedrockService = bedrockService; _serviceConfiguration = serviceConfiguration; } - public void ParseMessage(byte[] data, byte serverIndex) - { + public void ParseMessage(byte[] data, byte serverIndex) { byte[] stripHeaderFromBuffer = new byte[data.Length - 5]; Buffer.BlockCopy(data, 5, stripHeaderFromBuffer, 0, stripHeaderFromBuffer.Length); IServerConfiguration server = _serviceConfiguration.GetServerInfoByIndex(serverIndex); @@ -408,22 +349,18 @@ public void ParseMessage(byte[] data, byte serverIndex) } } - class Connect : IMessageParser - { + class Connect : IMessageParser { private readonly IMessageSender _iTCPListener; private readonly IServiceConfiguration _serviceConfiguration; - public Connect(ITCPListener iTCPListener, IServiceConfiguration serviceConfiguration) - { + public Connect(ITCPListener iTCPListener, IServiceConfiguration serviceConfiguration) { _iTCPListener = iTCPListener; _serviceConfiguration = serviceConfiguration; } - public void ParseMessage(byte[] data, byte serverIndex) - { + public void ParseMessage(byte[] data, byte serverIndex) { Formatting indented = Formatting.Indented; - JsonSerializerSettings settings = new JsonSerializerSettings() - { + JsonSerializerSettings settings = new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All }; string jsonString = JsonConvert.SerializeObject(_serviceConfiguration, indented, settings); @@ -432,21 +369,17 @@ public void ParseMessage(byte[] data, byte serverIndex) } } - class StartCmdUpdate : IMessageParser - { + class StartCmdUpdate : IMessageParser { private readonly IServiceConfiguration _serviceConfiguration; private readonly IConfigurator _configurator; - public StartCmdUpdate(IConfigurator configurator, IServiceConfiguration serviceConfiguration) - { + public StartCmdUpdate(IConfigurator configurator, IServiceConfiguration serviceConfiguration) { _configurator = configurator; _serviceConfiguration = serviceConfiguration; } - public void ParseMessage(byte[] data, byte serverIndex) - { - JsonSerializerSettings settings = new JsonSerializerSettings() - { + public void ParseMessage(byte[] data, byte serverIndex) { + JsonSerializerSettings settings = new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All }; List entries = JsonConvert.DeserializeObject>(Encoding.UTF8.GetString(data, 5, data.Length - 5), settings); @@ -455,22 +388,18 @@ public void ParseMessage(byte[] data, byte serverIndex) } } - class CheckUpdates : IMessageParser - { + class CheckUpdates : IMessageParser { private readonly IMessageSender _messageSender; private readonly IUpdater _updater; - public CheckUpdates(IMessageSender messageSender, IUpdater updater) - { + public CheckUpdates(IMessageSender messageSender, IUpdater updater) { _updater = updater; _messageSender = messageSender; } - public void ParseMessage(byte[] data, byte serverIndex) - { + public void ParseMessage(byte[] data, byte serverIndex) { _updater.CheckUpdates().Wait(); - if (_updater.CheckVersionChanged()) - { + if (_updater.CheckVersionChanged()) { _messageSender.SendData(NetworkMessageSource.Service, NetworkMessageDestination.Client, NetworkMessageTypes.CheckUpdates); } @@ -478,59 +407,49 @@ public void ParseMessage(byte[] data, byte serverIndex) } } - class BackupRollback : IMessageParser - { + class BackupRollback : IMessageParser { private readonly IMessageSender _messageSender; private readonly IBedrockService _service; - public BackupRollback(IMessageSender messageSender, IBedrockService service) - { + public BackupRollback(IMessageSender messageSender, IBedrockService service) { _service = service; _messageSender = messageSender; } - public void ParseMessage(byte[] data, byte serverIndex) - { + public void ParseMessage(byte[] data, byte serverIndex) { string stringData = Encoding.UTF8.GetString(data, 5, data.Length - 5); _service.GetBedrockServerByIndex(serverIndex).RollbackToBackup(serverIndex, stringData); _messageSender.SendData(NetworkMessageSource.Service, NetworkMessageDestination.Client, NetworkMessageTypes.UICallback); } } - class AddNewServer : IMessageParser - { + class AddNewServer : IMessageParser { private readonly IMessageSender _messageSender; private readonly IServiceConfiguration _serviceConfiguration; private readonly IConfigurator _configurator; private readonly IBedrockService _bedrockService; - public AddNewServer(IConfigurator configurator, IMessageSender messageSender, IServiceConfiguration serviceConfiguration, IBedrockService bedrockService) - { + public AddNewServer(IConfigurator configurator, IMessageSender messageSender, IServiceConfiguration serviceConfiguration, IBedrockService bedrockService) { _bedrockService = bedrockService; _configurator = configurator; _messageSender = messageSender; _serviceConfiguration = serviceConfiguration; } - public void ParseMessage(byte[] data, byte serverIndex) - { + public void ParseMessage(byte[] data, byte serverIndex) { string stringData = Encoding.UTF8.GetString(data, 5, data.Length - 5); - JsonSerializerSettings settings = new JsonSerializerSettings() - { + JsonSerializerSettings settings = new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All }; List propList = JsonConvert.DeserializeObject>(stringData, settings); Property serverNameProp = propList.First(p => p.KeyName == "server-name"); - ServerInfo newServer = new ServerInfo(null, _serviceConfiguration.GetProp("ServersPath").ToString()) - { + ServerInfo newServer = new ServerInfo(null, _serviceConfiguration.GetProp("ServersPath").ToString()) { ServerName = serverNameProp.ToString(), ServerPropList = propList, - ServerPath = new Property("ServerPath", "") - { + ServerPath = new Property("ServerPath", "") { Value = $@"{_serviceConfiguration.GetProp("ServersPath")}\{serverNameProp}" }, - ServerExeName = new Property("ServerExeName", "") - { + ServerExeName = new Property("ServerExeName", "") { Value = $"BedrockService.{serverNameProp}.exe" }, FileName = $@"{serverNameProp}.conf" @@ -545,28 +464,24 @@ public void ParseMessage(byte[] data, byte serverIndex) } } - class RemoveServer : IFlaggedMessageParser - { + class RemoveServer : IFlaggedMessageParser { private readonly IMessageSender _messageSender; private readonly IServiceConfiguration _serviceConfiguration; private readonly IConfigurator _configurator; private readonly IBedrockService _bedrockService; - public RemoveServer(IConfigurator configurator, IMessageSender messageSender, IServiceConfiguration serviceConfiguration, IBedrockService bedrockService) - { + public RemoveServer(IConfigurator configurator, IMessageSender messageSender, IServiceConfiguration serviceConfiguration, IBedrockService bedrockService) { this._bedrockService = bedrockService; _configurator = configurator; _messageSender = messageSender; _serviceConfiguration = serviceConfiguration; } - public void ParseMessage(byte[] data, byte serverIndex, NetworkMessageFlags flag) - { + public void ParseMessage(byte[] data, byte serverIndex, NetworkMessageFlags flag) { _bedrockService.GetBedrockServerByIndex(serverIndex).StopServer(true).Wait(); _configurator.RemoveServerConfigs(_serviceConfiguration.GetServerInfoByIndex(serverIndex), flag); _bedrockService.RemoveBedrockServerByIndex(serverIndex); - JsonSerializerSettings settings = new JsonSerializerSettings() - { + JsonSerializerSettings settings = new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All }; byte[] serializeToBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(_serviceConfiguration, Formatting.Indented, settings)); @@ -576,88 +491,73 @@ public void ParseMessage(byte[] data, byte serverIndex, NetworkMessageFlags flag } } - class ConsoleLogUpdate : IMessageParser - { + class ConsoleLogUpdate : IMessageParser { private readonly IMessageSender _messageSender; private readonly IBedrockService _service; private readonly IServiceConfiguration _serviceConfiguration; - public ConsoleLogUpdate(IMessageSender messageSender, IServiceConfiguration serviceConfiguration, IBedrockService service) - { + public ConsoleLogUpdate(IMessageSender messageSender, IServiceConfiguration serviceConfiguration, IBedrockService service) { _service = service; _messageSender = messageSender; _serviceConfiguration = serviceConfiguration; } - public void ParseMessage(byte[] data, byte serverIndex) - { + public void ParseMessage(byte[] data, byte serverIndex) { string stringData = Encoding.UTF8.GetString(data, 5, data.Length - 5); StringBuilder srvString = new StringBuilder(); string[] split = stringData.Split('|'); - for (int i = 0; i < split.Length; i++) - { + for (int i = 0; i < split.Length; i++) { string[] dataSplit = split[i].Split(';'); string srvName = dataSplit[0]; int srvTextLen; int clientCurLen; int loop; IBedrockLogger srvText; - if (srvName != "Service") - { - try - { + if (srvName != "Service") { + try { srvText = _service.GetBedrockServerByName(srvName).GetLogger(); } - catch (NullReferenceException) - { + catch (NullReferenceException) { break; } srvTextLen = srvText.Count(); clientCurLen = int.Parse(dataSplit[1]); loop = clientCurLen; - while (loop < srvTextLen) - { + while (loop < srvTextLen) { srvString.Append($"{srvName};{srvText.FromIndex(loop)};{loop}|"); loop++; } } - else - { + else { srvTextLen = _serviceConfiguration.GetLog().Count; clientCurLen = int.Parse(dataSplit[1]); loop = clientCurLen; - while (loop < srvTextLen) - { + while (loop < srvTextLen) { srvString.Append($"{srvName};{_serviceConfiguration.GetLog()[loop]};{loop}|"); loop++; } } } - if (srvString.Length > 1) - { + if (srvString.Length > 1) { srvString.Remove(srvString.Length - 1, 1); _messageSender.SendData(Encoding.UTF8.GetBytes(srvString.ToString()), NetworkMessageSource.Server, NetworkMessageDestination.Client, NetworkMessageTypes.ConsoleLogUpdate); } } } - class PlayerRequest : IMessageParser - { + class PlayerRequest : IMessageParser { private readonly IMessageSender _messageSender; private readonly IServiceConfiguration _serviceConfiguration; - public PlayerRequest(IMessageSender messageSender, IServiceConfiguration serviceConfiguration) - { + public PlayerRequest(IMessageSender messageSender, IServiceConfiguration serviceConfiguration) { _messageSender = messageSender; _serviceConfiguration = serviceConfiguration; } - public void ParseMessage(byte[] data, byte serverIndex) - { + public void ParseMessage(byte[] data, byte serverIndex) { IServerConfiguration server = _serviceConfiguration.GetServerInfoByIndex(serverIndex); - JsonSerializerSettings settings = new JsonSerializerSettings() - { + JsonSerializerSettings settings = new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All }; byte[] serializeToBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(server.GetPlayerList(), Formatting.Indented, settings)); diff --git a/BedrockService/Service/Networking/TCPListener.cs b/BedrockService/Service/Networking/TCPListener.cs index de0f8908..91762995 100644 --- a/BedrockService/Service/Networking/TCPListener.cs +++ b/BedrockService/Service/Networking/TCPListener.cs @@ -3,10 +3,8 @@ using System.Net.Sockets; using System.Security.Cryptography; -namespace BedrockService.Service.Networking -{ - public class TCPListener : ITCPListener, IMessageSender - { +namespace BedrockService.Service.Networking { + public class TCPListener : ITCPListener, IMessageSender { private TcpClient _client; private TcpListener _inListener; private NetworkStream _stream; @@ -22,58 +20,47 @@ public class TCPListener : ITCPListener, IMessageSender private Task _tcpTask; private Task _recieverTask; - public TCPListener(IServiceConfiguration serviceConfiguration, IBedrockLogger logger) - { + public TCPListener(IServiceConfiguration serviceConfiguration, IBedrockLogger logger) { _logger = logger; _serviceConfiguration = serviceConfiguration; _cancelTokenSource = new CancellationTokenSource(); InitializeTasks(); } - private void InitializeTasks() - { + private void InitializeTasks() { _tcpTask = StartListening(); _recieverTask = IncomingListener(); _tcpTask.Start(); } - public void SetStrategyDictionaries(Dictionary standard, Dictionary flagged) - { + public void SetStrategyDictionaries(Dictionary standard, Dictionary flagged) { _standardMessageLookup = standard; _flaggedMessageLookup = flagged; } - public Task StartListening() - { - return new Task(() => - { + public Task StartListening() { + return new Task(() => { _logger.AppendLine("TCP listener task started."); _inListener = new TcpListener(_ipAddress, int.Parse(_serviceConfiguration.GetProp("ClientPort").ToString())); - try - { + try { while (_standardMessageLookup == null) { Task.Delay(100).Wait(); } _inListener.Start(); } - catch (SocketException e) - { + catch (SocketException e) { _logger.AppendLine($"Error! {e.Message}"); Thread.Sleep(2000); Environment.Exit(1); } - while (true) - { - try - { - if (_inListener.Pending()) - { + while (true) { + try { + if (_inListener.Pending()) { _cancelTokenSource = new CancellationTokenSource(); _client = _inListener.AcceptTcpClient(); _stream = _client.GetStream(); _recieverTask.Start(); } - if (_cancelTokenSource.IsCancellationRequested) - { + if (_cancelTokenSource.IsCancellationRequested) { _logger.AppendLine("TCP Listener task canceled!"); _inListener.Stop(); _inListener = null; @@ -82,24 +69,20 @@ public Task StartListening() Task.Delay(500).Wait(); } catch (NullReferenceException) { } - catch (InvalidOperationException) - { + catch (InvalidOperationException) { _inListener = null; return; } catch (SocketException) { } - catch (Exception e) - { + catch (Exception e) { _logger.AppendLine(e.ToString()); } } }, _cancelTokenSource.Token); } - public void ResetListener() - { - Task.Run(() => - { + public void ResetListener() { + Task.Run(() => { _logger.AppendLine("Resetting listener!"); _client.Client.Blocking = false; _stream.Close(); @@ -107,8 +90,7 @@ public void ResetListener() _client.Close(); _inListener?.Stop(); _cancelTokenSource?.Cancel(); - while (_tcpTask.Status == TaskStatus.Running || _recieverTask.Status == TaskStatus.Running) - { + while (_tcpTask.Status == TaskStatus.Running || _recieverTask.Status == TaskStatus.Running) { Task.Delay(100).Wait(); } _cancelTokenSource = new CancellationTokenSource(); @@ -116,8 +98,7 @@ public void ResetListener() }); } - public void SendData(byte[] bytesToSend, NetworkMessageSource source, NetworkMessageDestination destination, byte serverIndex, NetworkMessageTypes type, NetworkMessageFlags status) - { + public void SendData(byte[] bytesToSend, NetworkMessageSource source, NetworkMessageDestination destination, byte serverIndex, NetworkMessageTypes type, NetworkMessageFlags status) { byte[] byteHeader = new byte[9 + bytesToSend.Length]; byte[] len = BitConverter.GetBytes(5 + bytesToSend.Length); Buffer.BlockCopy(len, 0, byteHeader, 0, 4); @@ -128,20 +109,16 @@ public void SendData(byte[] bytesToSend, NetworkMessageSource source, NetworkMes byteHeader[8] = (byte)status; Buffer.BlockCopy(bytesToSend, 0, byteHeader, 9, bytesToSend.Length); - if (_tcpTask?.Status == TaskStatus.Running && _recieverTask?.Status == TaskStatus.Running) - { - try - { + if (_tcpTask?.Status == TaskStatus.Running && _recieverTask?.Status == TaskStatus.Running) { + try { _stream.Write(byteHeader, 0, byteHeader.Length); _stream.Flush(); _heartbeatFailTimeout = 0; } - catch - { + catch { _logger.AppendLine("Error writing to network stream!"); _heartbeatFailTimeout++; - if (_heartbeatFailTimeout >= _heartbeatFailTimeoutLimit) - { + if (_heartbeatFailTimeout >= _heartbeatFailTimeoutLimit) { ResetListener(); _heartbeatFailTimeout = 0; } @@ -159,10 +136,8 @@ public void SendData(byte[] bytesToSend, NetworkMessageSource source, NetworkMes public void SendData(NetworkMessageSource source, NetworkMessageDestination destination, NetworkMessageTypes type, NetworkMessageFlags status) => SendData(new byte[0], source, destination, 0xFF, type, status); - private Task IncomingListener() - { - return new Task(() => - { + private Task IncomingListener() { + return new Task(() => { _logger.AppendLine("TCP Client packet listener started."); int AvailBytes = 0; int byteCount = 0; @@ -171,16 +146,13 @@ private Task IncomingListener() byte serverIndex = 0xFF; NetworkMessageTypes msgType = 0; NetworkMessageFlags msgFlag = 0; - while (true) - { + while (true) { SendData(NetworkMessageSource.Service, NetworkMessageDestination.Client, NetworkMessageTypes.Heartbeat); - if (_cancelTokenSource.IsCancellationRequested) - { + if (_cancelTokenSource.IsCancellationRequested) { _logger.AppendLine("TCP Client packet listener canceled!"); return; } - try - { + try { byte[] buffer = new byte[4]; while (_client.Client != null && _client.Client.Available != 0) // Recieve data from client. { @@ -193,51 +165,41 @@ private Task IncomingListener() serverIndex = buffer[2]; msgType = (NetworkMessageTypes)buffer[3]; msgFlag = (NetworkMessageFlags)buffer[4]; - if (msgType == NetworkMessageTypes.Disconnect) - { + if (msgType == NetworkMessageTypes.Disconnect) { ResetListener(); } - if (msgType < NetworkMessageTypes.Heartbeat) - { - try - { + if (msgType < NetworkMessageTypes.Heartbeat) { + try { if (_standardMessageLookup.ContainsKey(msgType)) _standardMessageLookup[msgType].ParseMessage(buffer, serverIndex); else _flaggedMessageLookup[msgType].ParseMessage(buffer, serverIndex, msgFlag); } - catch - { + catch { if (msgType == NetworkMessageTypes.Connect) Task.Delay(1000).Wait(); - ResetListener(); + ResetListener(); } } } Task.Delay(500).Wait(); } - catch (OutOfMemoryException) - { + catch (OutOfMemoryException) { _logger.AppendLine("Out of memory exception thrown."); } - catch (ObjectDisposedException) - { + catch (ObjectDisposedException) { _logger.AppendLine("Client was disposed!"); } - catch (InvalidOperationException e) - { - if (msgType != NetworkMessageTypes.ConsoleLogUpdate) - { + catch (InvalidOperationException e) { + if (msgType != NetworkMessageTypes.ConsoleLogUpdate) { _logger.AppendLine(e.Message); _logger.AppendLine(e.StackTrace); } } - catch (Exception e) - { + catch (Exception e) { _logger.AppendLine($"Error: {e.Message} {e.StackTrace}"); } - try - { + try { AvailBytes = _client.Client != null ? _client.Client.Available : 0; } catch { } @@ -245,23 +207,18 @@ private Task IncomingListener() }, _cancelTokenSource.Token); } - public void SetKeyContainer(CommsKeyContainer keyContainer) - { + public void SetKeyContainer(CommsKeyContainer keyContainer) { _keyContainer = keyContainer; } - public bool VerifyClientData(byte[] certificate) - { - if (certificate != null) - { + public bool VerifyClientData(byte[] certificate) { + if (certificate != null) { byte[] decrypted; - using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider()) - { + using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider()) { rsa.ImportParameters(_keyContainer.LocalPrivateKey.GetPrivateKey()); } - using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider()) - { + using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider()) { rsa.ImportParameters(_keyContainer.RemotePublicKey.GetPrivateKey()); } diff --git a/BedrockService/Service/Networking/Updater.cs b/BedrockService/Service/Networking/Updater.cs index d5cc030d..10372e79 100644 --- a/BedrockService/Service/Networking/Updater.cs +++ b/BedrockService/Service/Networking/Updater.cs @@ -2,37 +2,34 @@ using System.IO.Compression; using System.Net.Http; using System.Text.RegularExpressions; -using System.Threading; -namespace BedrockService.Service.Networking -{ - public class Updater : IUpdater - { +namespace BedrockService.Service.Networking { + public class Updater : IUpdater { private bool _versionChanged = false; private readonly IBedrockLogger _logger; private readonly IServiceConfiguration _serviceConfiguration; private readonly IProcessInfo _processInfo; private readonly string _version; - public Updater(IProcessInfo processInfo, IBedrockLogger logger, IServiceConfiguration serviceConfiguration) - { + public Updater(IProcessInfo processInfo, IBedrockLogger logger, IServiceConfiguration serviceConfiguration) { _serviceConfiguration = serviceConfiguration; _processInfo = processInfo; _logger = logger; _version = "None"; if (!Directory.Exists($@"{processInfo.GetDirectory()}\Server")) { Directory.CreateDirectory($@"{processInfo.GetDirectory()}\Server"); } - if (!File.Exists($@"{processInfo.GetDirectory()}\Server\bedrock_ver.ini")) - { + if (!File.Exists($@"{processInfo.GetDirectory()}\Server\bedrock_ver.ini")) { logger.AppendLine("Version ini file missing, creating and fetching build..."); File.Create($@"{processInfo.GetDirectory()}\Server\bedrock_ver.ini").Close(); } _version = File.ReadAllText($@"{processInfo.GetDirectory()}\Server\bedrock_ver.ini"); } - public async Task CheckUpdates() - { - await Task.Run(async () => - { + public async Task CheckUpdates() { + await Task.Run(async () => { + if (bool.Parse(_serviceConfiguration.GetProp("AcceptedMojangLic").ToString())) { + _logger.AppendLine("You have not accepted the license. Please visit the readme for more info!"); + return false; + } _logger.AppendLine("Checking MCS Version and fetching update if needed..."); HttpClient client = new HttpClient(); client.DefaultRequestHeaders.Add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/apng,*/*;q=0.8"); @@ -42,7 +39,6 @@ await Task.Run(async () => client.DefaultRequestHeaders.Add("Pragma", "no-cache"); client.DefaultRequestHeaders.Add("User-Agent", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko; Google Page Speed Insights) Chrome/27.0.1453 Safari/537.36"); client.Timeout = new TimeSpan(0, 0, 3); - string content = FetchHTTPContent(client).Result; if (content == null) // This really doesn't fail often. Give it one more try or fail. { @@ -53,8 +49,7 @@ await Task.Run(async () => return false; Regex regex = new Regex(@"(https://minecraft.azureedge.net/bin-win/bedrock-server-)(.*)(\.zip)", RegexOptions.IgnoreCase); Match m = regex.Match(content); - if (!m.Success) - { + if (!m.Success) { _logger.AppendLine("Checking for updates failed. Check website functionality!"); return false; } @@ -62,14 +57,12 @@ await Task.Run(async () => string fetchedVersion = m.Groups[2].Value; client.Dispose(); - if (_version == fetchedVersion) - { + if (_version == fetchedVersion) { _logger.AppendLine($"Current version \"{fetchedVersion}\" is up to date!"); return true; } _logger.AppendLine($"New version detected! Now fetching from {downloadPath}..."); - if (!FetchBuild(downloadPath, fetchedVersion).Wait(60000)) - { + if (!FetchBuild(downloadPath, fetchedVersion).Wait(60000)) { _logger.AppendLine("Fetching build timed out. If this is a new service instance, please restart service!"); return false; } @@ -78,40 +71,25 @@ await Task.Run(async () => _serviceConfiguration.SetServerVersion(fetchedVersion); GenerateFileList(fetchedVersion); return true; - - }); } - public async Task FetchBuild(string path, string version) - { + public async Task FetchBuild(string path, string version) { string ZipDir = $@"{_processInfo.GetDirectory()}\Server\MCSFiles\Update_{version}.zip"; - if (!Directory.Exists($@"{_processInfo.GetDirectory()}\Server\MCSFiles")) - { + if (!Directory.Exists($@"{_processInfo.GetDirectory()}\Server\MCSFiles")) { Directory.CreateDirectory($@"{_processInfo.GetDirectory()}\Server\MCSFiles"); } - if (File.Exists(ZipDir)) - { + if (File.Exists(ZipDir)) { return; } - //if (InstanceProvider.HostInfo.GetGlobalValue("AcceptedMojangLic") == "false") - //{ - // logger.AppendLine("You have not accepted the license. Please visit the readme for more info!"); - // return; - //} _logger.AppendLine("Now downloading latest build of Minecraft Bedrock Server. Please wait..."); - using (var httpClient = new HttpClient()) - { - using (var request = new HttpRequestMessage(HttpMethod.Get, path)) - { - using (Stream contentStream = await (await httpClient.SendAsync(request)).Content.ReadAsStreamAsync(), stream = new FileStream(ZipDir, FileMode.Create, FileAccess.Write, FileShare.None, 256000, true)) - { - try - { + using (var httpClient = new HttpClient()) { + using (var request = new HttpRequestMessage(HttpMethod.Get, path)) { + using (Stream contentStream = await (await httpClient.SendAsync(request)).Content.ReadAsStreamAsync(), stream = new FileStream(ZipDir, FileMode.Create, FileAccess.Write, FileShare.None, 256000, true)) { + try { await contentStream.CopyToAsync(stream); } - catch (Exception e) - { + catch (Exception e) { _logger.AppendLine($"Download zip resulted in error: {e.StackTrace}"); } httpClient.Dispose(); @@ -121,68 +99,54 @@ public async Task FetchBuild(string path, string version) } } } - } public bool CheckVersionChanged() => _versionChanged; public void MarkUpToDate() => _versionChanged = false; - public async Task ReplaceBuild(IServerConfiguration server) - { - await Task.Run(() => - { - try - { - if (!Directory.Exists(server.GetProp("ServerPath").ToString())) + public async Task ReplaceBuild(IServerConfiguration server) { + await Task.Run(() => { + try { + if (!Directory.Exists(server.GetProp("ServerPath").ToString())) { Directory.CreateDirectory(server.GetProp("ServerPath").ToString()); - else if (File.Exists($@"{_processInfo.GetDirectory()}\Server\MCSFiles\stock_filelist.ini")) + } + else if (File.Exists($@"{_processInfo.GetDirectory()}\Server\MCSFiles\stock_filelist.ini")) { new FileUtils(_processInfo.GetDirectory()).DeleteFilelist(File.ReadAllLines($@"{_processInfo.GetDirectory()}\Server\MCSFiles\stock_filelist.ini"), server.GetProp("ServerPath").ToString()); - else + } + else { new FileUtils(_processInfo.GetDirectory()).DeleteFilesRecursively(new DirectoryInfo(server.GetProp("ServerPath").ToString()), false); - - ZipFile.ExtractToDirectory($@"{_processInfo.GetDirectory()}\Server\MCSFiles\Update_{_version}.zip", server.GetProp("ServerPath").ToString()); - File.Copy(server.GetProp("ServerPath") + "\\bedrock_server.exe", server.GetProp("ServerPath") + "\\" + server.GetProp("ServerExeName"), true); + ZipFile.ExtractToDirectory($@"{_processInfo.GetDirectory()}\Server\MCSFiles\Update_{_version}.zip", server.GetProp("ServerPath").ToString()); + File.Copy(server.GetProp("ServerPath") + "\\bedrock_server.exe", server.GetProp("ServerPath") + "\\" + server.GetProp("ServerExeName"), true); + } } - catch (Exception e) - { + catch (Exception e) { _logger.AppendLine($"ERROR: Got an exception deleting entire directory! {e.Message}"); } - - }); } - - private async Task FetchHTTPContent(HttpClient client) - { - try - { + private async Task FetchHTTPContent(HttpClient client) { + try { return await client.GetStringAsync("https://www.minecraft.net/en-us/download/server/bedrock"); } - catch (HttpRequestException) - { + catch (HttpRequestException) { _logger.AppendLine($"Error! Updater timed out, could not fetch current build!"); } - catch (TaskCanceledException) - { + catch (TaskCanceledException) { Thread.Sleep(200); return await FetchHTTPContent(client); } - catch (Exception e) - { + catch (Exception e) { _logger.AppendLine($"Updater resulted in error: {e.Message}\n{e.InnerException}\n{e.StackTrace}"); } return null; } - private void GenerateFileList(string version) - { - using (ZipArchive zip = ZipFile.OpenRead($@"{_processInfo.GetDirectory()}\Server\MCSFiles\Update_{version}.zip")) - { + private void GenerateFileList(string version) { + using (ZipArchive zip = ZipFile.OpenRead($@"{_processInfo.GetDirectory()}\Server\MCSFiles\Update_{version}.zip")) { string[] fileList = new string[zip.Entries.Count]; - for (int i = 0; i < zip.Entries.Count; i++) - { + for (int i = 0; i < zip.Entries.Count; i++) { fileList[i] = zip.Entries[i].FullName.Replace('/', '\\'); } File.WriteAllLines($@"{_processInfo.GetDirectory()}\Server\MCSFiles\stock_filelist.ini", fileList); diff --git a/BedrockService/Service/Program.cs b/BedrockService/Service/Program.cs index 2cbb5fd2..95c3dab5 100644 --- a/BedrockService/Service/Program.cs +++ b/BedrockService/Service/Program.cs @@ -9,7 +9,6 @@ global using BedrockService.Shared.Classes; global using BedrockService.Shared.Interfaces; global using Microsoft.AspNetCore.Hosting; -global using Microsoft.Extensions.Configuration; global using Microsoft.Extensions.DependencyInjection; global using Microsoft.Extensions.Hosting; global using System; @@ -23,24 +22,19 @@ global using Topshelf; using BedrockService.Service.Core.Interfaces; -namespace BedrockService.Service -{ - public class Program - { +namespace BedrockService.Service { + public class Program { public static bool IsExiting = false; private static bool _isDebugEnabled = false; private static bool _isConsoleMode = false; private static CancellationTokenSource CancellationTokenSource = new CancellationTokenSource(); private static CancellationToken token = CancellationTokenSource.Token; - public static void Main(string[] args) - { - if (args.Length > 0) - { + public static void Main(string[] args) { + if (args.Length > 0) { Console.WriteLine(string.Join(" ", args)); _isDebugEnabled = args[0].ToLower() == "-debug"; } - if (args.Length == 0 || Environment.UserInteractive) - { + if (args.Length == 0 || Environment.UserInteractive) { _isConsoleMode = true; } CreateHostBuilder(args).Build().Run(); @@ -48,8 +42,7 @@ public static void Main(string[] args) public static IHostBuilder CreateHostBuilder(string[] args) => Microsoft.Extensions.Hosting.Host.CreateDefaultBuilder(args) - .ConfigureServices((hostContext, services) => - { + .ConfigureServices((hostContext, services) => { IProcessInfo processInfo = new ServiceProcessInfo(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), Path.GetFileName(Assembly.GetExecutingAssembly().Location), Process.GetCurrentProcess().Id, _isDebugEnabled, _isConsoleMode); services.AddHostedService() .AddSingleton(processInfo) diff --git a/BedrockService/Service/Server/BedrockServer.cs b/BedrockService/Service/Server/BedrockServer.cs index 0a0f7f57..6d1d1c1d 100644 --- a/BedrockService/Service/Server/BedrockServer.cs +++ b/BedrockService/Service/Server/BedrockServer.cs @@ -1,12 +1,9 @@ using BedrockService.Service.Server.Management; using BedrockService.Shared.Utilities; using System.Text.RegularExpressions; -using System.Threading; -namespace BedrockService.Service.Server -{ - public class BedrockServer : IBedrockServer - { +namespace BedrockService.Service.Server { + public class BedrockServer : IBedrockServer { private Task _serverTask; private Task _watchdogTask; private CancellationTokenSource _serverCanceler = new CancellationTokenSource(); @@ -23,16 +20,14 @@ public class BedrockServer : IBedrockServer private readonly IBedrockLogger _serverLogger; private readonly string _servicePath; private const string _startupMessage = "INFO] Server started."; - public enum ServerStatus - { + public enum ServerStatus { Stopped, Starting, Stopping, Started } - public BedrockServer(IServerConfiguration serverConfiguration, IConfigurator configurator, IBedrockLogger logger, IServiceConfiguration serviceConfiguration, IProcessInfo processInfo) - { + public BedrockServer(IServerConfiguration serverConfiguration, IConfigurator configurator, IBedrockLogger logger, IServiceConfiguration serviceConfiguration, IProcessInfo processInfo) { _serverConfiguration = serverConfiguration; _serviceConfiguration = serviceConfiguration; _configurator = configurator; @@ -42,23 +37,19 @@ public BedrockServer(IServerConfiguration serverConfiguration, IConfigurator con _playerManager = new PlayerManager(serverConfiguration, logger); } - public void WriteToStandardIn(string command) - { + public void WriteToStandardIn(string command) { _stdInStream.WriteLine(command); } - public void StartControl() - { + public void StartControl() { _serverCanceler = new CancellationTokenSource(); _serverTask = RunServer(); _serverTask.Start(); } - public void StopControl() - { + public void StopControl() { _serverCanceler.Cancel(); - if (_serverProcess != null) - { + if (_serverProcess != null) { _logger.AppendLine("Sending Stop to Bedrock. Process.HasExited = " + _serverProcess.HasExited.ToString()); _serverProcess.CancelOutputRead(); @@ -70,87 +61,71 @@ public void StopControl() _currentServerStatus = ServerStatus.Stopped; } - public void StartWatchdog(HostControl hostControl) - { + public void StartWatchdog(HostControl hostControl) { _watchdogCanceler = new CancellationTokenSource(); _hostController = hostControl; _watchdogTask = ApplicationWatchdogMonitor(); _watchdogTask.Start(); } - public void InitializeBackup() - { + public void InitializeBackup() { WriteToStandardIn("save hold"); Task.Delay(1000).Wait(); WriteToStandardIn("save query"); } - private bool PerformBackup(string queryString) - { - try - { + private bool PerformBackup(string queryString) { + try { FileUtils fileUtils = new FileUtils(_servicePath); FileInfo exe = new FileInfo($@"{_serverConfiguration.GetProp("ServerPath")}\{_serverConfiguration.GetProp("ServerExeName")}"); string configBackupPath = _serviceConfiguration.GetProp("BackupPath").ToString(); DirectoryInfo backupDir = new DirectoryInfo($@"{configBackupPath}\{_serverConfiguration.GetServerName()}"); DirectoryInfo serverDir = new DirectoryInfo(_serverConfiguration.GetProp("ServerPath").ToString()); DirectoryInfo worldsDir = new DirectoryInfo($@"{_serverConfiguration.GetProp("ServerPath")}\worlds"); - if (!backupDir.Exists) - { + if (!backupDir.Exists) { backupDir.Create(); } Dictionary backupFileInfoPairs = new Dictionary(); string[] files = queryString.Split(", "); - foreach (string file in files) - { + foreach (string file in files) { string[] fileInfoSplit = file.Split(':'); string fileName = fileInfoSplit[0]; int fileSize = int.Parse(fileInfoSplit[1]); backupFileInfoPairs.Add(fileName, fileSize); } int dirCount = backupDir.GetDirectories().Length; - try - { - if (dirCount >= int.Parse(_serviceConfiguration.GetProp("MaxBackupCount").ToString())) - { + try { + if (dirCount >= int.Parse(_serviceConfiguration.GetProp("MaxBackupCount").ToString())) { Regex reg = new Regex(@"Backup_(.*)$"); List Dates = new List(); - foreach (DirectoryInfo dir in backupDir.GetDirectories()) - { - if (reg.IsMatch(dir.Name)) - { + foreach (DirectoryInfo dir in backupDir.GetDirectories()) { + if (reg.IsMatch(dir.Name)) { Match match = reg.Match(dir.Name); Dates.Add(Convert.ToInt64(match.Groups[1].Value)); } } long OldestDate = 0; - foreach (long date in Dates) - { - if (OldestDate == 0) - { + foreach (long date in Dates) { + if (OldestDate == 0) { OldestDate = date; } - else if (date < OldestDate) - { + else if (date < OldestDate) { OldestDate = date; } } Directory.Delete($@"{backupDir}\Backup_{OldestDate}", true); } } - catch (Exception e) - { - if (e.GetType() == typeof(FormatException)) - { + catch (Exception e) { + if (e.GetType() == typeof(FormatException)) { _logger.AppendLine("Error in Config! MaxBackupCount must be nothing but a number!"); } } DirectoryInfo targetDirectory = backupDir.CreateSubdirectory($"Backup_{DateTime.Now.Ticks}"); _logger.AppendLine($"Backing up files for server {_serverConfiguration.GetServerName()}. Please wait!"); - if (_serviceConfiguration.GetProp("EntireBackups").ToString() == "false") - { + if (_serviceConfiguration.GetProp("EntireBackups").ToString() == "false") { bool resuilt = fileUtils.BackupWorldFilesFromQuery(backupFileInfoPairs, worldsDir.FullName, $@"{targetDirectory.FullName}").Result; WriteToStandardIn("save resume"); return resuilt; @@ -160,8 +135,7 @@ private bool PerformBackup(string queryString) WriteToStandardIn("save resume"); return result; } - catch (Exception e) - { + catch (Exception e) { _logger.AppendLine($"Error with Backup: {e.StackTrace}"); return false; } @@ -171,54 +145,43 @@ private bool PerformBackup(string queryString) public void SetServerStatus(ServerStatus newStatus) => _currentServerStatus = newStatus; - private Task ApplicationWatchdogMonitor() - { - return new Task(() => - { - while (true) - { - if (_watchdogCanceler.IsCancellationRequested) - { + private Task ApplicationWatchdogMonitor() { + return new Task(() => { + while (true) { + if (_watchdogCanceler.IsCancellationRequested) { _logger.AppendLine("WatchDog Task was canceled. Stopping server!"); StopControl(); return; } string exeName = _serverConfiguration.GetProp("ServerExeName").ToString(); string appName = exeName.Substring(0, exeName.Length - 4); - if (!MonitoredAppExists(appName) && _currentServerStatus == ServerStatus.Starting) - { + if (!MonitoredAppExists(appName) && _currentServerStatus == ServerStatus.Starting) { StartControl(); _logger.AppendLine($"Recieved start signal for server {_serverConfiguration.GetServerName()}."); Thread.Sleep(15000); } - if (MonitoredAppExists(appName) && _currentServerStatus == ServerStatus.Stopping) - { + if (MonitoredAppExists(appName) && _currentServerStatus == ServerStatus.Stopping) { _logger.AppendLine($"BedrockService signaled stop to application {appName}."); _logger.AppendLine("Stopping..."); StopControl(); - while (_currentServerStatus == ServerStatus.Stopping) - { + while (_currentServerStatus == ServerStatus.Stopping) { Thread.Sleep(250); } } - if (!MonitoredAppExists(appName) && _currentServerStatus == ServerStatus.Started) - { + if (!MonitoredAppExists(appName) && _currentServerStatus == ServerStatus.Started) { StopControl(); _logger.AppendLine($"Started application {appName} was not found in running processes... Resarting {appName}."); StartControl(); Thread.Sleep(1500); } - if (!MonitoredAppExists(appName) && _currentServerStatus == ServerStatus.Stopped) - { + if (!MonitoredAppExists(appName) && _currentServerStatus == ServerStatus.Stopped) { _logger.AppendLine("Server stopped successfully."); } - if (!MonitoredAppExists(appName) && _currentServerStatus == ServerStatus.Stopping) - { + if (!MonitoredAppExists(appName) && _currentServerStatus == ServerStatus.Stopping) { _logger.AppendLine("Server stopped unexpectedly. Setting server status to stopped."); _currentServerStatus = ServerStatus.Stopped; } - if (!MonitoredAppExists(appName) && _currentServerStatus == ServerStatus.Stopped && Program.IsExiting) - { + if (!MonitoredAppExists(appName) && _currentServerStatus == ServerStatus.Stopped && Program.IsExiting) { return; } Task.Delay(3000).Wait(); @@ -226,13 +189,10 @@ private Task ApplicationWatchdogMonitor() }, _watchdogCanceler.Token); } - public bool RestartServer(bool ShouldPerformBackup) - { - if (_currentServerStatus == ServerStatus.Started) - { + public bool RestartServer(bool ShouldPerformBackup) { + if (_currentServerStatus == ServerStatus.Started) { _currentServerStatus = ServerStatus.Stopping; - while (_currentServerStatus == ServerStatus.Stopping) - { + while (_currentServerStatus == ServerStatus.Stopping) { Thread.Sleep(100); } _currentServerStatus = ServerStatus.Starting; @@ -242,24 +202,18 @@ public bool RestartServer(bool ShouldPerformBackup) public string GetServerName() => _serverConfiguration.GetServerName(); - private Task RunServer() - { - return new Task(() => - { + private Task RunServer() { + return new Task(() => { string exeName = _serverConfiguration.GetProp("ServerExeName").ToString(); string appName = exeName.Substring(0, exeName.Length - 4); _configurator.WriteJSONFiles(_serverConfiguration); _configurator.SaveServerProps(_serverConfiguration, false); - try - { - if (File.Exists($@"{_serverConfiguration.GetProp("ServerPath")}\{_serverConfiguration.GetProp("ServerExeName")}")) - { - if (MonitoredAppExists(appName)) - { + try { + if (File.Exists($@"{_serverConfiguration.GetProp("ServerPath")}\{_serverConfiguration.GetProp("ServerExeName")}")) { + if (MonitoredAppExists(appName)) { Process[] processList = Process.GetProcessesByName(appName); - if (processList.Length != 0) - { + if (processList.Length != 0) { _logger.AppendLine($@"Application {appName} was found running! Killing to proceed."); KillProcess(processList); } @@ -267,25 +221,21 @@ private Task RunServer() // Fires up a new process to run inside this one CreateProcess(); } - else - { + else { _logger.AppendLine($"The Bedrock Server is not accessible at {$@"{_serverConfiguration.GetProp("ServerPath")}\{_serverConfiguration.GetProp("ServerExeName")}"}\r\nCheck if the file is at that location and that permissions are correct."); _hostController.Stop(); } } - catch (Exception e) - { + catch (Exception e) { _logger.AppendLine($"Error Running Bedrock Server: {e.Message}\n{e.StackTrace}"); _hostController.Stop(); } }, _serverCanceler.Token); } - private void CreateProcess() - { + private void CreateProcess() { string fileName = $@"{_serverConfiguration.GetProp("ServerPath")}\{_serverConfiguration.GetProp("ServerExeName")}"; - _serverProcess = Process.Start(new ProcessStartInfo - { + _serverProcess = Process.Start(new ProcessStartInfo { UseShellExecute = false, RedirectStandardError = true, RedirectStandardInput = true, @@ -300,68 +250,53 @@ private void CreateProcess() _stdInStream = _serverProcess.StandardInput; } - private void KillProcess(Process[] processList) - { - foreach (Process process in processList) - { - try - { + private void KillProcess(Process[] processList) { + foreach (Process process in processList) { + try { process.Kill(); Thread.Sleep(1000); _logger.AppendLine($@"App {_serverConfiguration.GetProp("ServerExeName")} killed!"); } - catch (Exception e) - { + catch (Exception e) { _logger.AppendLine($"Killing proccess resulted in error: {e.StackTrace}"); } } } - private bool MonitoredAppExists(string monitoredAppName) - { - try - { + private bool MonitoredAppExists(string monitoredAppName) { + try { Process[] processList = Process.GetProcessesByName(monitoredAppName); - if (processList.Length == 0) - { + if (processList.Length == 0) { return false; } - else - { + else { return true; } } - catch (Exception ex) - { + catch (Exception ex) { _logger.AppendLine("ApplicationWatcher MonitoredAppExists Exception: " + ex.StackTrace); return true; } } - private void StdOutToLog(object sender, DataReceivedEventArgs e) - { - if (e.Data != null && !e.Data.Contains("INFO] Running AutoCompaction...")) - { + private void StdOutToLog(object sender, DataReceivedEventArgs e) { + if (e.Data != null && !e.Data.Contains("INFO] Running AutoCompaction...")) { string dataMsg = e.Data; string logFileText = "NO LOG FILE! - "; if (dataMsg.StartsWith(logFileText)) dataMsg = dataMsg.Substring(logFileText.Length, dataMsg.Length - logFileText.Length); _serverLogger.AppendText($"{_serverConfiguration.GetServerName()}: {dataMsg}\r\n"); - if (e.Data != null) - { + if (e.Data != null) { - if (dataMsg.Contains(_startupMessage)) - { + if (dataMsg.Contains(_startupMessage)) { _currentServerStatus = ServerStatus.Started; Task.Delay(3000).Wait(); - if (_serverConfiguration.GetStartCommands().Count > 0) - { + if (_serverConfiguration.GetStartCommands().Count > 0) { RunStartupCommands(); } } - if (dataMsg.StartsWith("[INFO] Player connected")) - { + if (dataMsg.StartsWith("[INFO] Player connected")) { int usernameStart = dataMsg.IndexOf(':') + 2; int usernameEnd = dataMsg.IndexOf(','); int usernameLength = usernameEnd - usernameStart; @@ -372,8 +307,7 @@ private void StdOutToLog(object sender, DataReceivedEventArgs e) _playerManager.PlayerConnected(username, xuid); _configurator.SaveKnownPlayerDatabase(_serverConfiguration); } - if (dataMsg.StartsWith("[INFO] Player disconnected")) - { + if (dataMsg.StartsWith("[INFO] Player disconnected")) { int usernameStart = dataMsg.IndexOf(':') + 2; int usernameEnd = dataMsg.IndexOf(','); int usernameLength = usernameEnd - usernameStart; @@ -384,39 +318,33 @@ private void StdOutToLog(object sender, DataReceivedEventArgs e) _playerManager.PlayerDisconnected(xuid); _configurator.SaveKnownPlayerDatabase(_serverConfiguration); } - if (dataMsg.Contains("Failed to load Vanilla")) - { + if (dataMsg.Contains("Failed to load Vanilla")) { _currentServerStatus = ServerStatus.Stopping; while (_currentServerStatus != ServerStatus.Stopped) Thread.Sleep(200); if (_configurator.ReplaceServerBuild(_serverConfiguration).Wait(30000)) _currentServerStatus = ServerStatus.Starting; } - if (dataMsg.Contains("Version ")) - { + if (dataMsg.Contains("Version ")) { int msgStartIndex = dataMsg.IndexOf(']') + 2; string focusedMsg = dataMsg.Substring(msgStartIndex, dataMsg.Length - msgStartIndex); int versionIndex = focusedMsg.IndexOf(' ') + 1; string versionString = focusedMsg.Substring(versionIndex, focusedMsg.Length - versionIndex); string currentVersion = _serviceConfiguration.GetServerVersion(); - if (currentVersion != versionString) - { + if (currentVersion != versionString) { _logger.AppendLine($"Server {GetServerName()} version found out-of-date! Now updating!"); StopServer(false).Wait(); _configurator.ReplaceServerBuild(_serverConfiguration).Wait(); StartControl(); } } - if(dataMsg.Contains("A previous save has not been completed.")) - { + if (dataMsg.Contains("A previous save has not been completed.")) { Task.Delay(1000).Wait(); WriteToStandardIn("save query"); } - if (dataMsg.Contains($@"{_serverConfiguration.GetProp("level-name")}/db/")) - { + if (dataMsg.Contains($@"{_serverConfiguration.GetProp("level-name")}/db/")) { _logger.AppendLine("Save data string detected! Performing backup now!"); - if (PerformBackup(dataMsg)) - { + if (PerformBackup(dataMsg)) { _logger.AppendLine($"Backup for server {_serverConfiguration.GetServerName()} Completed."); return; } @@ -426,28 +354,22 @@ private void StdOutToLog(object sender, DataReceivedEventArgs e) } } - private void RunStartupCommands() - { - foreach (StartCmdEntry cmd in _serverConfiguration.GetStartCommands()) - { + private void RunStartupCommands() { + foreach (StartCmdEntry cmd in _serverConfiguration.GetStartCommands()) { _stdInStream.WriteLine(cmd.Command.Trim()); Thread.Sleep(1000); } } - public bool RollbackToBackup(byte serverIndex, string folderName) - { + public bool RollbackToBackup(byte serverIndex, string folderName) { IServerConfiguration server = _serviceConfiguration.GetServerInfoByIndex(serverIndex); StopServer(false).Wait(); - try - { + try { foreach (DirectoryInfo dir in new DirectoryInfo($@"{_serviceConfiguration.GetProp("BackupPath")}\{server.GetServerName()}").GetDirectories()) - if (dir.Name == folderName) - { + if (dir.Name == folderName) { new FileUtils(_servicePath).DeleteFilesRecursively(new DirectoryInfo($@"{server.GetProp("ServerPath")}\worlds"), false); _logger.AppendLine($"Deleted world folder contents."); - foreach (DirectoryInfo worldDir in new DirectoryInfo($@"{_serviceConfiguration.GetProp("BackupPath")}\{server.GetServerName()}\{folderName}").GetDirectories()) - { + foreach (DirectoryInfo worldDir in new DirectoryInfo($@"{_serviceConfiguration.GetProp("BackupPath")}\{server.GetServerName()}\{folderName}").GetDirectories()) { new FileUtils(_servicePath).CopyFilesRecursively(worldDir, new DirectoryInfo($@"{server.GetProp("ServerPath")}\worlds\{worldDir.Name}")); _logger.AppendLine($@"Copied {worldDir.Name} to path {server.GetProp("ServerPath")}\worlds"); } @@ -455,8 +377,7 @@ public bool RollbackToBackup(byte serverIndex, string folderName) return true; } } - catch (IOException e) - { + catch (IOException e) { _logger.AppendLine($"Error deleting selected backups! {e.Message}"); } return false; @@ -466,20 +387,16 @@ public bool RollbackToBackup(byte serverIndex, string folderName) public IPlayerManager GetPlayerManager() => _playerManager; - public Task StopServer(bool stopWatchdog) - { - return Task.Run(() => - { - _currentServerStatus = ServerStatus.Stopping; - while (_currentServerStatus != ServerStatus.Stopped) - { - Thread.Sleep(100); - } - if (stopWatchdog) - { - _watchdogCanceler.Cancel(); - } - }); + public Task StopServer(bool stopWatchdog) { + return Task.Run(() => { + _currentServerStatus = ServerStatus.Stopping; + while (_currentServerStatus != ServerStatus.Stopped) { + Thread.Sleep(100); + } + if (stopWatchdog) { + _watchdogCanceler.Cancel(); + } + }); } } } diff --git a/BedrockService/Service/Server/IBedrockServer.cs b/BedrockService/Service/Server/IBedrockServer.cs index 071ef542..4b064d0e 100644 --- a/BedrockService/Service/Server/IBedrockServer.cs +++ b/BedrockService/Service/Server/IBedrockServer.cs @@ -1,9 +1,7 @@ using BedrockService.Service.Server.Management; -namespace BedrockService.Service.Server -{ - public interface IBedrockServer - { +namespace BedrockService.Service.Server { + public interface IBedrockServer { void StartWatchdog(HostControl hostControl); string GetServerName(); void WriteToStandardIn(string command); diff --git a/BedrockService/Service/Server/Management/IPlayerManager.cs b/BedrockService/Service/Server/Management/IPlayerManager.cs index 917fd2ee..41d9609d 100644 --- a/BedrockService/Service/Server/Management/IPlayerManager.cs +++ b/BedrockService/Service/Server/Management/IPlayerManager.cs @@ -1,7 +1,5 @@ -namespace BedrockService.Service.Server.Management -{ - public interface IPlayerManager - { +namespace BedrockService.Service.Server.Management { + public interface IPlayerManager { IPlayer GetPlayerByXUID(string xuid); void SetPlayer(IPlayer player); List GetPlayers(); diff --git a/BedrockService/Service/Server/Management/PlayerManager.cs b/BedrockService/Service/Server/Management/PlayerManager.cs index 1bc3153e..3fa132b1 100644 --- a/BedrockService/Service/Server/Management/PlayerManager.cs +++ b/BedrockService/Service/Server/Management/PlayerManager.cs @@ -1,21 +1,16 @@ -namespace BedrockService.Service.Server.Management -{ - public class PlayerManager : IPlayerManager - { +namespace BedrockService.Service.Server.Management { + public class PlayerManager : IPlayerManager { readonly IServerConfiguration _serverConfiguration; readonly IBedrockLogger _logger; - public PlayerManager(IServerConfiguration serverConfiguration, IBedrockLogger logger) - { + public PlayerManager(IServerConfiguration serverConfiguration, IBedrockLogger logger) { this._serverConfiguration = serverConfiguration; this._logger = logger; } - public void PlayerConnected(string username, string xuid) - { + public void PlayerConnected(string username, string xuid) { IPlayer playerFound = _serverConfiguration.GetPlayerByXuid(xuid); - if (playerFound == null) - { + if (playerFound == null) { _serverConfiguration.AddUpdatePlayer(new Player(xuid, username, DateTime.Now.Ticks.ToString(), "0", "0", false, _serverConfiguration.GetProp("default-player-permission-level").ToString(), false)); return; } @@ -23,40 +18,33 @@ public void PlayerConnected(string username, string xuid) _serverConfiguration.AddUpdatePlayer(playerFound); } - public void PlayerDisconnected(string xuid) - { + public void PlayerDisconnected(string xuid) { IPlayer playerFound = _serverConfiguration.GetPlayerByXuid(xuid); var oldTimes = playerFound.GetTimes(); playerFound.UpdateTimes(oldTimes.Conn, DateTime.Now.Ticks.ToString()); _serverConfiguration.AddUpdatePlayer(playerFound); } - public void UpdatePlayerFromCfg(string xuid, string username, string permission, string whitelisted, string ignoreMaxPlayerLimit) - { + public void UpdatePlayerFromCfg(string xuid, string username, string permission, string whitelisted, string ignoreMaxPlayerLimit) { IPlayer playerFound = _serverConfiguration.GetPlayerByXuid(xuid); - if (playerFound == null) - { + if (playerFound == null) { playerFound = new Player(_serverConfiguration.GetProp("default-player-permission-level").ToString()); playerFound.Initialize(xuid, username); } playerFound.UpdateRegistration(permission, whitelisted, ignoreMaxPlayerLimit); } - public IPlayer GetPlayerByXUID(string xuid) - { + public IPlayer GetPlayerByXUID(string xuid) { if (GetPlayers().Count > 0) return _serverConfiguration.GetPlayerByXuid(xuid); return null; } - public void SetPlayer(IPlayer player) - { - try - { + public void SetPlayer(IPlayer player) { + try { _serverConfiguration.GetPlayerList()[_serverConfiguration.GetPlayerList().IndexOf(player)] = player; } - catch - { + catch { _serverConfiguration.GetPlayerList().Add(player); } } diff --git a/BedrockService/ServiceTests/ConfiguratorTests.cs b/BedrockService/ServiceTests/ConfiguratorTests.cs index e7f04af9..6288a919 100644 --- a/BedrockService/ServiceTests/ConfiguratorTests.cs +++ b/BedrockService/ServiceTests/ConfiguratorTests.cs @@ -4,17 +4,14 @@ using System; using Xunit; -namespace ServiceTests -{ - public class ConfiguratorTests - { +namespace ServiceTests { + public class ConfiguratorTests { readonly IServiceProvider _services = Program.CreateHostBuilder(new string[] { }).Build().Services; // one liner IConfigurator myService; [Fact] - public void VerifyServiceInContainer() - { + public void VerifyServiceInContainer() { myService = _services.GetRequiredService(); Assert.NotNull(myService); } diff --git a/BedrockService/ServiceTests/ServiceTests.cs b/BedrockService/ServiceTests/ServiceTests.cs index e9d57b48..29a39b36 100644 --- a/BedrockService/ServiceTests/ServiceTests.cs +++ b/BedrockService/ServiceTests/ServiceTests.cs @@ -4,16 +4,13 @@ using System; using Xunit; -namespace ServiceTests -{ - public class ServiceTests - { +namespace ServiceTests { + public class ServiceTests { readonly IServiceProvider _services = Program.CreateHostBuilder(new string[] { }).Build().Services; // one liner [Fact] - public void VerifyServiceInContainer() - { + public void VerifyServiceInContainer() { IBedrockService myService = _services.GetRequiredService(); Assert.NotNull(myService); } From 959f08aa249a0af7b5fc2b05d7b068eb9e8c744e Mon Sep 17 00:00:00 2001 From: crowbarmaster Date: Thu, 16 Dec 2021 23:47:45 -0500 Subject: [PATCH 45/62] Specify Target OS to Windows to build in same output directory as client again. --- BedrockService/Service/BedrockService.Service.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BedrockService/Service/BedrockService.Service.csproj b/BedrockService/Service/BedrockService.Service.csproj index e179c8b1..2a1bcbef 100644 --- a/BedrockService/Service/BedrockService.Service.csproj +++ b/BedrockService/Service/BedrockService.Service.csproj @@ -45,7 +45,7 @@ - net6.0 + net6.0-windows false From 935b262e032bdcac75665d339ae4d355aa09216f Mon Sep 17 00:00:00 2001 From: crowbarmaster Date: Fri, 17 Dec 2021 01:26:11 -0500 Subject: [PATCH 46/62] Fixed minor issue with single file publishing where applicaton was taking a temporary working directory as the service path. --- .../BedrockService.Shared/Classes/ServiceProcessInfo.cs | 6 +----- .../BedrockService.Shared/Interfaces/IProcessInfo.cs | 2 -- BedrockService/Client/Management/FormManager.cs | 2 +- 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/BedrockService/BedrockService.Shared/Classes/ServiceProcessInfo.cs b/BedrockService/BedrockService.Shared/Classes/ServiceProcessInfo.cs index ba42731e..c18551a7 100644 --- a/BedrockService/BedrockService.Shared/Classes/ServiceProcessInfo.cs +++ b/BedrockService/BedrockService.Shared/Classes/ServiceProcessInfo.cs @@ -3,14 +3,12 @@ namespace BedrockService.Shared.Classes { public class ServiceProcessInfo : IProcessInfo { private readonly string _serviceDirectory; - private readonly string _serviceExeName; private readonly int _processPid; private bool _debugEnabled; private bool _isConsoleMode; - public ServiceProcessInfo(string serviceDirectory, string serviceExeName, int processPid, bool debugEnabled, bool isConsoleMode) { + public ServiceProcessInfo(string serviceDirectory, int processPid, bool debugEnabled, bool isConsoleMode) { _serviceDirectory = serviceDirectory; - _serviceExeName = serviceExeName; _processPid = processPid; _debugEnabled = debugEnabled; _isConsoleMode = isConsoleMode; @@ -18,8 +16,6 @@ public ServiceProcessInfo(string serviceDirectory, string serviceExeName, int pr public string GetDirectory() => _serviceDirectory; - public string GetExecutableName() => _serviceExeName; - public int GetProcessPID() => _processPid; public bool IsDebugEnabled() => _debugEnabled; diff --git a/BedrockService/BedrockService.Shared/Interfaces/IProcessInfo.cs b/BedrockService/BedrockService.Shared/Interfaces/IProcessInfo.cs index 0592dae7..ed1a86b0 100644 --- a/BedrockService/BedrockService.Shared/Interfaces/IProcessInfo.cs +++ b/BedrockService/BedrockService.Shared/Interfaces/IProcessInfo.cs @@ -2,8 +2,6 @@ public interface IProcessInfo { string GetDirectory(); - string GetExecutableName(); - int GetProcessPID(); bool IsDebugEnabled(); diff --git a/BedrockService/Client/Management/FormManager.cs b/BedrockService/Client/Management/FormManager.cs index 31c7d132..176d9452 100644 --- a/BedrockService/Client/Management/FormManager.cs +++ b/BedrockService/Client/Management/FormManager.cs @@ -8,7 +8,7 @@ namespace BedrockService.Client.Management { public sealed class FormManager { - private static readonly IProcessInfo processInfo = new ServiceProcessInfo(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), Path.GetFileName(Assembly.GetExecutingAssembly().Location), Process.GetCurrentProcess().Id, false, true); + private static readonly IProcessInfo processInfo = new ServiceProcessInfo(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), Process.GetCurrentProcess().Id, false, true); private static readonly IBedrockLogger Logger = new ClientLogger(processInfo); private static MainWindow main; private static TCPClient client; From 40028817f193330f10786b11f7788a5edf85162f Mon Sep 17 00:00:00 2001 From: crowbarmaster Date: Fri, 17 Dec 2021 01:26:58 -0500 Subject: [PATCH 47/62] Add an icon, Update versions, and fix reference in test project. --- .../BedrockManagementServiceASP.csproj | 2 + .../BedrockService.Shared.csproj | 14 ++++-- BedrockService/BedrockService.sln | 46 +++++++++++++++++- .../Client/BedrockService.Client.csproj | 21 ++++++-- .../Client/Properties/AssemblyInfo.cs | 4 +- BedrockService/Client/mc_icon.ico | Bin 0 -> 140991 bytes .../Service/BedrockService.Service.csproj | 24 ++++++--- BedrockService/Service/Program.cs | 2 +- .../Service/Properties/AssemblyInfo.cs | 4 +- BedrockService/Service/mc_icon.ico | Bin 0 -> 140991 bytes .../ServiceTests/ServiceTests.csproj | 8 ++- 11 files changed, 104 insertions(+), 21 deletions(-) create mode 100644 BedrockService/Client/mc_icon.ico create mode 100644 BedrockService/Service/mc_icon.ico diff --git a/BedrockService/BedrockManagementServiceASP/BedrockManagementServiceASP.csproj b/BedrockService/BedrockManagementServiceASP/BedrockManagementServiceASP.csproj index e86a23dd..dd4c66d4 100644 --- a/BedrockService/BedrockManagementServiceASP/BedrockManagementServiceASP.csproj +++ b/BedrockService/BedrockManagementServiceASP/BedrockManagementServiceASP.csproj @@ -5,6 +5,8 @@ enable enable dotnet-BedrockManagementServiceASP-4ABD78ED-14F3-43D6-821D-3F1C9B453A6F + Debug;Release;Publish + AnyCPU;x64 diff --git a/BedrockService/BedrockService.Shared/BedrockService.Shared.csproj b/BedrockService/BedrockService.Shared/BedrockService.Shared.csproj index 36708052..48275aa3 100644 --- a/BedrockService/BedrockService.Shared/BedrockService.Shared.csproj +++ b/BedrockService/BedrockService.Shared/BedrockService.Shared.csproj @@ -3,13 +3,21 @@ netstandard2.0 Library false + Debug;Release;Publish + AnyCPU;x64 2 - - - + + 2 + + + 2 + + + 2 + diff --git a/BedrockService/BedrockService.sln b/BedrockService/BedrockService.sln index 4df1abda..528b8100 100644 --- a/BedrockService/BedrockService.sln +++ b/BedrockService/BedrockService.sln @@ -13,34 +13,78 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BedrockService.Shared", "Be EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BedrockManagementServiceASP", "BedrockManagementServiceASP\BedrockManagementServiceASP.csproj", "{4AA65E9F-E3BE-4173-BE3B-BD4DFCD21C40}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ServiceTests", "ServiceTests\ServiceTests.csproj", "{B20CAE22-52BD-4A49-ADA8-04F7C7007155}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ServiceTests", "ServiceTests\ServiceTests.csproj", "{B20CAE22-52BD-4A49-ADA8-04F7C7007155}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Publish|Any CPU = Publish|Any CPU + Publish|x64 = Publish|x64 Release|Any CPU = Release|Any CPU + Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {13B2B5A8-71E9-49F4-9BFB-3C3B3C0A054C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {13B2B5A8-71E9-49F4-9BFB-3C3B3C0A054C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {13B2B5A8-71E9-49F4-9BFB-3C3B3C0A054C}.Debug|x64.ActiveCfg = Debug|x64 + {13B2B5A8-71E9-49F4-9BFB-3C3B3C0A054C}.Debug|x64.Build.0 = Debug|x64 + {13B2B5A8-71E9-49F4-9BFB-3C3B3C0A054C}.Publish|Any CPU.ActiveCfg = Publish|Any CPU + {13B2B5A8-71E9-49F4-9BFB-3C3B3C0A054C}.Publish|Any CPU.Build.0 = Publish|Any CPU + {13B2B5A8-71E9-49F4-9BFB-3C3B3C0A054C}.Publish|x64.ActiveCfg = Publish|x64 + {13B2B5A8-71E9-49F4-9BFB-3C3B3C0A054C}.Publish|x64.Build.0 = Publish|x64 {13B2B5A8-71E9-49F4-9BFB-3C3B3C0A054C}.Release|Any CPU.ActiveCfg = Release|Any CPU {13B2B5A8-71E9-49F4-9BFB-3C3B3C0A054C}.Release|Any CPU.Build.0 = Release|Any CPU + {13B2B5A8-71E9-49F4-9BFB-3C3B3C0A054C}.Release|x64.ActiveCfg = Release|x64 + {13B2B5A8-71E9-49F4-9BFB-3C3B3C0A054C}.Release|x64.Build.0 = Release|x64 {A2DAF70A-925A-4F4B-B7EE-CAACE75D6740}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A2DAF70A-925A-4F4B-B7EE-CAACE75D6740}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A2DAF70A-925A-4F4B-B7EE-CAACE75D6740}.Debug|x64.ActiveCfg = Debug|x64 + {A2DAF70A-925A-4F4B-B7EE-CAACE75D6740}.Debug|x64.Build.0 = Debug|x64 + {A2DAF70A-925A-4F4B-B7EE-CAACE75D6740}.Publish|Any CPU.ActiveCfg = Publish|Any CPU + {A2DAF70A-925A-4F4B-B7EE-CAACE75D6740}.Publish|Any CPU.Build.0 = Publish|Any CPU + {A2DAF70A-925A-4F4B-B7EE-CAACE75D6740}.Publish|x64.ActiveCfg = Publish|x64 + {A2DAF70A-925A-4F4B-B7EE-CAACE75D6740}.Publish|x64.Build.0 = Publish|x64 {A2DAF70A-925A-4F4B-B7EE-CAACE75D6740}.Release|Any CPU.ActiveCfg = Release|Any CPU {A2DAF70A-925A-4F4B-B7EE-CAACE75D6740}.Release|Any CPU.Build.0 = Release|Any CPU + {A2DAF70A-925A-4F4B-B7EE-CAACE75D6740}.Release|x64.ActiveCfg = Release|x64 + {A2DAF70A-925A-4F4B-B7EE-CAACE75D6740}.Release|x64.Build.0 = Release|x64 {F146D5E8-EF1F-4785-9150-182631F059B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F146D5E8-EF1F-4785-9150-182631F059B7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F146D5E8-EF1F-4785-9150-182631F059B7}.Debug|x64.ActiveCfg = Debug|x64 + {F146D5E8-EF1F-4785-9150-182631F059B7}.Debug|x64.Build.0 = Debug|x64 + {F146D5E8-EF1F-4785-9150-182631F059B7}.Publish|Any CPU.ActiveCfg = Publish|Any CPU + {F146D5E8-EF1F-4785-9150-182631F059B7}.Publish|Any CPU.Build.0 = Publish|Any CPU + {F146D5E8-EF1F-4785-9150-182631F059B7}.Publish|x64.ActiveCfg = Publish|x64 + {F146D5E8-EF1F-4785-9150-182631F059B7}.Publish|x64.Build.0 = Publish|x64 {F146D5E8-EF1F-4785-9150-182631F059B7}.Release|Any CPU.ActiveCfg = Release|Any CPU {F146D5E8-EF1F-4785-9150-182631F059B7}.Release|Any CPU.Build.0 = Release|Any CPU + {F146D5E8-EF1F-4785-9150-182631F059B7}.Release|x64.ActiveCfg = Release|x64 + {F146D5E8-EF1F-4785-9150-182631F059B7}.Release|x64.Build.0 = Release|x64 {4AA65E9F-E3BE-4173-BE3B-BD4DFCD21C40}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4AA65E9F-E3BE-4173-BE3B-BD4DFCD21C40}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4AA65E9F-E3BE-4173-BE3B-BD4DFCD21C40}.Debug|x64.ActiveCfg = Debug|x64 + {4AA65E9F-E3BE-4173-BE3B-BD4DFCD21C40}.Debug|x64.Build.0 = Debug|x64 + {4AA65E9F-E3BE-4173-BE3B-BD4DFCD21C40}.Publish|Any CPU.ActiveCfg = Publish|Any CPU + {4AA65E9F-E3BE-4173-BE3B-BD4DFCD21C40}.Publish|Any CPU.Build.0 = Publish|Any CPU + {4AA65E9F-E3BE-4173-BE3B-BD4DFCD21C40}.Publish|x64.ActiveCfg = Publish|x64 + {4AA65E9F-E3BE-4173-BE3B-BD4DFCD21C40}.Publish|x64.Build.0 = Publish|x64 {4AA65E9F-E3BE-4173-BE3B-BD4DFCD21C40}.Release|Any CPU.ActiveCfg = Release|Any CPU {4AA65E9F-E3BE-4173-BE3B-BD4DFCD21C40}.Release|Any CPU.Build.0 = Release|Any CPU + {4AA65E9F-E3BE-4173-BE3B-BD4DFCD21C40}.Release|x64.ActiveCfg = Release|x64 + {4AA65E9F-E3BE-4173-BE3B-BD4DFCD21C40}.Release|x64.Build.0 = Release|x64 {B20CAE22-52BD-4A49-ADA8-04F7C7007155}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B20CAE22-52BD-4A49-ADA8-04F7C7007155}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B20CAE22-52BD-4A49-ADA8-04F7C7007155}.Debug|x64.ActiveCfg = Debug|x64 + {B20CAE22-52BD-4A49-ADA8-04F7C7007155}.Debug|x64.Build.0 = Debug|x64 + {B20CAE22-52BD-4A49-ADA8-04F7C7007155}.Publish|Any CPU.ActiveCfg = Publish|Any CPU + {B20CAE22-52BD-4A49-ADA8-04F7C7007155}.Publish|Any CPU.Build.0 = Publish|Any CPU + {B20CAE22-52BD-4A49-ADA8-04F7C7007155}.Publish|x64.ActiveCfg = Publish|x64 + {B20CAE22-52BD-4A49-ADA8-04F7C7007155}.Publish|x64.Build.0 = Publish|x64 {B20CAE22-52BD-4A49-ADA8-04F7C7007155}.Release|Any CPU.ActiveCfg = Release|Any CPU {B20CAE22-52BD-4A49-ADA8-04F7C7007155}.Release|Any CPU.Build.0 = Release|Any CPU + {B20CAE22-52BD-4A49-ADA8-04F7C7007155}.Release|x64.ActiveCfg = Release|x64 + {B20CAE22-52BD-4A49-ADA8-04F7C7007155}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/BedrockService/Client/BedrockService.Client.csproj b/BedrockService/Client/BedrockService.Client.csproj index 763a70bd..03761923 100644 --- a/BedrockService/Client/BedrockService.Client.csproj +++ b/BedrockService/Client/BedrockService.Client.csproj @@ -20,19 +20,31 @@ false true true + Debug;Release;Publish + AnyCPU;x64 ..\bin\Debug\ + + ..\bin\Debug\ + ..\bin\Release\ + + ..\bin\Release\ + + + ..\bin\Release\ + + + ..\bin\Release\ + BedrockService.Client.Forms.MainWindow + mc_icon.ico - - - @@ -48,6 +60,9 @@ false + + + diff --git a/BedrockService/Client/Properties/AssemblyInfo.cs b/BedrockService/Client/Properties/AssemblyInfo.cs index 83c81492..24522be6 100644 --- a/BedrockService/Client/Properties/AssemblyInfo.cs +++ b/BedrockService/Client/Properties/AssemblyInfo.cs @@ -31,5 +31,5 @@ // 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("2.5.5.0")] +[assembly: AssemblyFileVersion("2.5.5.0")] diff --git a/BedrockService/Client/mc_icon.ico b/BedrockService/Client/mc_icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..0c675e0b448e27e9ecadc06fbaf4649bbe5cec14 GIT binary patch literal 140991 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৖P3bqp61L9_#N#1D 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 literal 0 HcmV?d00001 diff --git a/BedrockService/Service/BedrockService.Service.csproj b/BedrockService/Service/BedrockService.Service.csproj index 2a1bcbef..cb523b9f 100644 --- a/BedrockService/Service/BedrockService.Service.csproj +++ b/BedrockService/Service/BedrockService.Service.csproj @@ -20,14 +20,29 @@ false true true + Debug;Release;Publish + AnyCPU;x64 ..\bin\Debug\ 2 + + ..\bin\Debug\ + 2 + ..\bin\Release\ + + ..\bin\Release\ + + + ..\bin\Release\ + + + ..\bin\Release\ + EE2403F7477A35F08B98B0A8FB2404C95BE04FEB @@ -47,14 +62,8 @@ net6.0-windows false + mc_icon.ico - - - - False - - - False @@ -91,5 +100,6 @@ + \ No newline at end of file diff --git a/BedrockService/Service/Program.cs b/BedrockService/Service/Program.cs index 95c3dab5..b5718e81 100644 --- a/BedrockService/Service/Program.cs +++ b/BedrockService/Service/Program.cs @@ -43,7 +43,7 @@ public static void Main(string[] args) { public static IHostBuilder CreateHostBuilder(string[] args) => Microsoft.Extensions.Hosting.Host.CreateDefaultBuilder(args) .ConfigureServices((hostContext, services) => { - IProcessInfo processInfo = new ServiceProcessInfo(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), Path.GetFileName(Assembly.GetExecutingAssembly().Location), Process.GetCurrentProcess().Id, _isDebugEnabled, _isConsoleMode); + IProcessInfo processInfo = new ServiceProcessInfo(hostContext.HostingEnvironment.ContentRootPath, Process.GetCurrentProcess().Id, _isDebugEnabled, _isConsoleMode); services.AddHostedService() .AddSingleton(processInfo) .AddSingleton() diff --git a/BedrockService/Service/Properties/AssemblyInfo.cs b/BedrockService/Service/Properties/AssemblyInfo.cs index 2a913382..214159ab 100644 --- a/BedrockService/Service/Properties/AssemblyInfo.cs +++ b/BedrockService/Service/Properties/AssemblyInfo.cs @@ -30,5 +30,5 @@ // 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("2.5.0.0")] -[assembly: AssemblyFileVersion("2.5.0.0")] +[assembly: AssemblyVersion("2.5.5.0")] +[assembly: AssemblyFileVersion("2.5.5.0")] diff --git a/BedrockService/Service/mc_icon.ico b/BedrockService/Service/mc_icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..0c675e0b448e27e9ecadc06fbaf4649bbe5cec14 GIT binary patch literal 140991 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৖P3bqp61L9_#N#1D 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 literal 0 HcmV?d00001 diff --git a/BedrockService/ServiceTests/ServiceTests.csproj b/BedrockService/ServiceTests/ServiceTests.csproj index 7bbcb6d5..9bbe4d51 100644 --- a/BedrockService/ServiceTests/ServiceTests.csproj +++ b/BedrockService/ServiceTests/ServiceTests.csproj @@ -1,10 +1,14 @@ - + - net6.0 + net6.0-windows enable false + + Debug;Release;Publish + + AnyCPU;x64 From 37f455de107e2b5ebc8cafe91fe54b107ccc5979 Mon Sep 17 00:00:00 2001 From: crowbarmaster Date: Fri, 17 Dec 2021 12:57:43 -0500 Subject: [PATCH 48/62] Fixup dialog display properties to format properly in various aspect ratios. --- .../Client/Forms/PropEditorForm.Designer.cs | 43 ++++++++++--------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/BedrockService/Client/Forms/PropEditorForm.Designer.cs b/BedrockService/Client/Forms/PropEditorForm.Designer.cs index 4430f6cb..ef6d257c 100644 --- a/BedrockService/Client/Forms/PropEditorForm.Designer.cs +++ b/BedrockService/Client/Forms/PropEditorForm.Designer.cs @@ -40,11 +40,11 @@ private void InitializeComponent() // // CancelBtn // - this.CancelBtn.Anchor = System.Windows.Forms.AnchorStyles.Bottom; - this.CancelBtn.Location = new System.Drawing.Point(27, 628); - this.CancelBtn.Margin = new System.Windows.Forms.Padding(6, 5, 6, 5); + this.CancelBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.CancelBtn.Location = new System.Drawing.Point(13, 322); + this.CancelBtn.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.CancelBtn.Name = "CancelBtn"; - this.CancelBtn.Size = new System.Drawing.Size(333, 45); + this.CancelBtn.Size = new System.Drawing.Size(170, 30); this.CancelBtn.TabIndex = 48; this.CancelBtn.Text = "Cancel"; this.CancelBtn.UseVisualStyleBackColor = true; @@ -52,11 +52,11 @@ private void InitializeComponent() // // SaveBtn // - this.SaveBtn.Anchor = System.Windows.Forms.AnchorStyles.Bottom; - this.SaveBtn.Location = new System.Drawing.Point(936, 628); - this.SaveBtn.Margin = new System.Windows.Forms.Padding(6, 5, 6, 5); + this.SaveBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.SaveBtn.Location = new System.Drawing.Point(531, 322); + this.SaveBtn.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.SaveBtn.Name = "SaveBtn"; - this.SaveBtn.Size = new System.Drawing.Size(333, 45); + this.SaveBtn.Size = new System.Drawing.Size(170, 30); this.SaveBtn.TabIndex = 49; this.SaveBtn.Text = "Save"; this.SaveBtn.UseVisualStyleBackColor = true; @@ -72,12 +72,12 @@ private void InitializeComponent() this.gridView.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] { this.EntryKey, this.EntryData}); - this.gridView.Location = new System.Drawing.Point(21, 25); - this.gridView.Margin = new System.Windows.Forms.Padding(6, 5, 6, 5); + this.gridView.Location = new System.Drawing.Point(13, 15); + this.gridView.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.gridView.MultiSelect = false; this.gridView.Name = "gridView"; this.gridView.RowHeadersWidth = 62; - this.gridView.Size = new System.Drawing.Size(1248, 560); + this.gridView.Size = new System.Drawing.Size(686, 301); this.gridView.TabIndex = 1; this.gridView.NewRowNeeded += new System.Windows.Forms.DataGridViewRowEventHandler(this.gridView_NewRowNeeded); // @@ -94,16 +94,17 @@ private void InitializeComponent() this.EntryData.HeaderText = "Value:"; this.EntryData.MinimumWidth = 8; this.EntryData.Name = "EntryData"; - this.EntryData.Width = 500; + this.EntryData.Width = 300; // // DelBackupBtn // - this.DelBackupBtn.Anchor = System.Windows.Forms.AnchorStyles.Bottom; + this.DelBackupBtn.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); this.DelBackupBtn.Enabled = false; - this.DelBackupBtn.Location = new System.Drawing.Point(488, 628); - this.DelBackupBtn.Margin = new System.Windows.Forms.Padding(6, 5, 6, 5); + this.DelBackupBtn.Location = new System.Drawing.Point(271, 322); + this.DelBackupBtn.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.DelBackupBtn.Name = "DelBackupBtn"; - this.DelBackupBtn.Size = new System.Drawing.Size(333, 45); + this.DelBackupBtn.Size = new System.Drawing.Size(170, 30); this.DelBackupBtn.TabIndex = 50; this.DelBackupBtn.Text = "Delete Backup"; this.DelBackupBtn.UseVisualStyleBackColor = true; @@ -112,14 +113,16 @@ private void InitializeComponent() // // PropEditorForm // - this.AutoScaleDimensions = new System.Drawing.SizeF(10F, 25F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(1344, 766); + this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi; + this.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + this.ClientSize = new System.Drawing.Size(714, 361); this.Controls.Add(this.DelBackupBtn); this.Controls.Add(this.gridView); this.Controls.Add(this.SaveBtn); this.Controls.Add(this.CancelBtn); - this.Margin = new System.Windows.Forms.Padding(6, 5, 6, 5); + this.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + this.MinimumSize = new System.Drawing.Size(730, 145); this.Name = "PropEditorForm"; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; this.Text = "Form1"; From 9aba304b6d627a9b3a0147867af1e55ae39602da Mon Sep 17 00:00:00 2001 From: crowbarmaster Date: Fri, 17 Dec 2021 13:01:41 -0500 Subject: [PATCH 49/62] Bug fixed where watchdog starting application would hang if an instance of the server was already running. Wrote a condition in which the watchdog can kill the old instance before proceeding. --- BedrockService/Service/Server/BedrockServer.cs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/BedrockService/Service/Server/BedrockServer.cs b/BedrockService/Service/Server/BedrockServer.cs index 6d1d1c1d..1541eebd 100644 --- a/BedrockService/Service/Server/BedrockServer.cs +++ b/BedrockService/Service/Server/BedrockServer.cs @@ -155,10 +155,9 @@ private Task ApplicationWatchdogMonitor() { } string exeName = _serverConfiguration.GetProp("ServerExeName").ToString(); string appName = exeName.Substring(0, exeName.Length - 4); - if (!MonitoredAppExists(appName) && _currentServerStatus == ServerStatus.Starting) { - StartControl(); - _logger.AppendLine($"Recieved start signal for server {_serverConfiguration.GetServerName()}."); - Thread.Sleep(15000); + if (MonitoredAppExists(appName) && _currentServerStatus == ServerStatus.Starting) { + _logger.AppendLine($"BedrockService found {appName} already running! Killing to proceed..."); + KillProcesses(Process.GetProcessesByName(appName)); } if (MonitoredAppExists(appName) && _currentServerStatus == ServerStatus.Stopping) { _logger.AppendLine($"BedrockService signaled stop to application {appName}."); @@ -168,6 +167,11 @@ private Task ApplicationWatchdogMonitor() { Thread.Sleep(250); } } + if (!MonitoredAppExists(appName) && _currentServerStatus == ServerStatus.Starting) { + StartControl(); + _logger.AppendLine($"Recieved start signal for server {_serverConfiguration.GetServerName()}."); + Thread.Sleep(15000); + } if (!MonitoredAppExists(appName) && _currentServerStatus == ServerStatus.Started) { StopControl(); _logger.AppendLine($"Started application {appName} was not found in running processes... Resarting {appName}."); @@ -215,7 +219,7 @@ private Task RunServer() { Process[] processList = Process.GetProcessesByName(appName); if (processList.Length != 0) { _logger.AppendLine($@"Application {appName} was found running! Killing to proceed."); - KillProcess(processList); + KillProcesses(processList); } } // Fires up a new process to run inside this one @@ -250,7 +254,7 @@ private void CreateProcess() { _stdInStream = _serverProcess.StandardInput; } - private void KillProcess(Process[] processList) { + private void KillProcesses(Process[] processList) { foreach (Process process in processList) { try { process.Kill(); From e589c7df8d9f6cb15bab7cadc7d953d750b1689d Mon Sep 17 00:00:00 2001 From: crowbarmaster Date: Fri, 17 Dec 2021 15:05:27 -0500 Subject: [PATCH 50/62] Revert "Fixed minor issue with single file publishing where applicaton was taking a temporary working directory as the service path." This reverts commit 935b262e032bdcac75665d339ae4d355aa09216f. --- .../BedrockService.Shared/Classes/ServiceProcessInfo.cs | 6 +++++- .../BedrockService.Shared/Interfaces/IProcessInfo.cs | 2 ++ BedrockService/Client/Management/FormManager.cs | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/BedrockService/BedrockService.Shared/Classes/ServiceProcessInfo.cs b/BedrockService/BedrockService.Shared/Classes/ServiceProcessInfo.cs index c18551a7..ba42731e 100644 --- a/BedrockService/BedrockService.Shared/Classes/ServiceProcessInfo.cs +++ b/BedrockService/BedrockService.Shared/Classes/ServiceProcessInfo.cs @@ -3,12 +3,14 @@ namespace BedrockService.Shared.Classes { public class ServiceProcessInfo : IProcessInfo { private readonly string _serviceDirectory; + private readonly string _serviceExeName; private readonly int _processPid; private bool _debugEnabled; private bool _isConsoleMode; - public ServiceProcessInfo(string serviceDirectory, int processPid, bool debugEnabled, bool isConsoleMode) { + public ServiceProcessInfo(string serviceDirectory, string serviceExeName, int processPid, bool debugEnabled, bool isConsoleMode) { _serviceDirectory = serviceDirectory; + _serviceExeName = serviceExeName; _processPid = processPid; _debugEnabled = debugEnabled; _isConsoleMode = isConsoleMode; @@ -16,6 +18,8 @@ public ServiceProcessInfo(string serviceDirectory, int processPid, bool debugEna public string GetDirectory() => _serviceDirectory; + public string GetExecutableName() => _serviceExeName; + public int GetProcessPID() => _processPid; public bool IsDebugEnabled() => _debugEnabled; diff --git a/BedrockService/BedrockService.Shared/Interfaces/IProcessInfo.cs b/BedrockService/BedrockService.Shared/Interfaces/IProcessInfo.cs index ed1a86b0..0592dae7 100644 --- a/BedrockService/BedrockService.Shared/Interfaces/IProcessInfo.cs +++ b/BedrockService/BedrockService.Shared/Interfaces/IProcessInfo.cs @@ -2,6 +2,8 @@ public interface IProcessInfo { string GetDirectory(); + string GetExecutableName(); + int GetProcessPID(); bool IsDebugEnabled(); diff --git a/BedrockService/Client/Management/FormManager.cs b/BedrockService/Client/Management/FormManager.cs index 176d9452..31c7d132 100644 --- a/BedrockService/Client/Management/FormManager.cs +++ b/BedrockService/Client/Management/FormManager.cs @@ -8,7 +8,7 @@ namespace BedrockService.Client.Management { public sealed class FormManager { - private static readonly IProcessInfo processInfo = new ServiceProcessInfo(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), Process.GetCurrentProcess().Id, false, true); + private static readonly IProcessInfo processInfo = new ServiceProcessInfo(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), Path.GetFileName(Assembly.GetExecutingAssembly().Location), Process.GetCurrentProcess().Id, false, true); private static readonly IBedrockLogger Logger = new ClientLogger(processInfo); private static MainWindow main; private static TCPClient client; From 071b641acb67ec2b56c0a1906eb222307c43ddee Mon Sep 17 00:00:00 2001 From: crowbarmaster Date: Fri, 17 Dec 2021 15:08:34 -0500 Subject: [PATCH 51/62] Revert "Revert "Fixed minor issue with single file publishing where applicaton was taking a temporary working directory as the service path."" This reverts commit e589c7df8d9f6cb15bab7cadc7d953d750b1689d. --- .../BedrockService.Shared/Classes/ServiceProcessInfo.cs | 6 +----- .../BedrockService.Shared/Interfaces/IProcessInfo.cs | 2 -- BedrockService/Client/Management/FormManager.cs | 2 +- 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/BedrockService/BedrockService.Shared/Classes/ServiceProcessInfo.cs b/BedrockService/BedrockService.Shared/Classes/ServiceProcessInfo.cs index ba42731e..c18551a7 100644 --- a/BedrockService/BedrockService.Shared/Classes/ServiceProcessInfo.cs +++ b/BedrockService/BedrockService.Shared/Classes/ServiceProcessInfo.cs @@ -3,14 +3,12 @@ namespace BedrockService.Shared.Classes { public class ServiceProcessInfo : IProcessInfo { private readonly string _serviceDirectory; - private readonly string _serviceExeName; private readonly int _processPid; private bool _debugEnabled; private bool _isConsoleMode; - public ServiceProcessInfo(string serviceDirectory, string serviceExeName, int processPid, bool debugEnabled, bool isConsoleMode) { + public ServiceProcessInfo(string serviceDirectory, int processPid, bool debugEnabled, bool isConsoleMode) { _serviceDirectory = serviceDirectory; - _serviceExeName = serviceExeName; _processPid = processPid; _debugEnabled = debugEnabled; _isConsoleMode = isConsoleMode; @@ -18,8 +16,6 @@ public ServiceProcessInfo(string serviceDirectory, string serviceExeName, int pr public string GetDirectory() => _serviceDirectory; - public string GetExecutableName() => _serviceExeName; - public int GetProcessPID() => _processPid; public bool IsDebugEnabled() => _debugEnabled; diff --git a/BedrockService/BedrockService.Shared/Interfaces/IProcessInfo.cs b/BedrockService/BedrockService.Shared/Interfaces/IProcessInfo.cs index 0592dae7..ed1a86b0 100644 --- a/BedrockService/BedrockService.Shared/Interfaces/IProcessInfo.cs +++ b/BedrockService/BedrockService.Shared/Interfaces/IProcessInfo.cs @@ -2,8 +2,6 @@ public interface IProcessInfo { string GetDirectory(); - string GetExecutableName(); - int GetProcessPID(); bool IsDebugEnabled(); diff --git a/BedrockService/Client/Management/FormManager.cs b/BedrockService/Client/Management/FormManager.cs index 31c7d132..176d9452 100644 --- a/BedrockService/Client/Management/FormManager.cs +++ b/BedrockService/Client/Management/FormManager.cs @@ -8,7 +8,7 @@ namespace BedrockService.Client.Management { public sealed class FormManager { - private static readonly IProcessInfo processInfo = new ServiceProcessInfo(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), Path.GetFileName(Assembly.GetExecutingAssembly().Location), Process.GetCurrentProcess().Id, false, true); + private static readonly IProcessInfo processInfo = new ServiceProcessInfo(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), Process.GetCurrentProcess().Id, false, true); private static readonly IBedrockLogger Logger = new ClientLogger(processInfo); private static MainWindow main; private static TCPClient client; From e884ab910d1627c267f438a6fbe5c87b2fbaf9ff Mon Sep 17 00:00:00 2001 From: crowbarmaster Date: Fri, 17 Dec 2021 15:50:21 -0500 Subject: [PATCH 52/62] Once again fix working path to function properly when running as Local System. Add log entry at log start with current working path. --- BedrockService/Service/Logging/ServiceLogger.cs | 1 + BedrockService/Service/Program.cs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/BedrockService/Service/Logging/ServiceLogger.cs b/BedrockService/Service/Logging/ServiceLogger.cs index 7fece697..c6d6e2ae 100644 --- a/BedrockService/Service/Logging/ServiceLogger.cs +++ b/BedrockService/Service/Logging/ServiceLogger.cs @@ -22,6 +22,7 @@ public ServiceLogger(IProcessInfo processInfo, IServiceConfiguration serviceConf Directory.CreateDirectory(_logPath); _logWriter = new StreamWriter($@"{_logPath}\ServiceLog_{_parent}_{DateTime.Now:yyyymmddhhmmss}.log", true); } + AppendLine($"Service logging started. Service working directory: {processInfo.GetDirectory()}"); } [JsonConstructor] diff --git a/BedrockService/Service/Program.cs b/BedrockService/Service/Program.cs index b5718e81..4710734c 100644 --- a/BedrockService/Service/Program.cs +++ b/BedrockService/Service/Program.cs @@ -43,7 +43,7 @@ public static void Main(string[] args) { public static IHostBuilder CreateHostBuilder(string[] args) => Microsoft.Extensions.Hosting.Host.CreateDefaultBuilder(args) .ConfigureServices((hostContext, services) => { - IProcessInfo processInfo = new ServiceProcessInfo(hostContext.HostingEnvironment.ContentRootPath, Process.GetCurrentProcess().Id, _isDebugEnabled, _isConsoleMode); + IProcessInfo processInfo = new ServiceProcessInfo(Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName), Process.GetCurrentProcess().Id, _isDebugEnabled, _isConsoleMode); services.AddHostedService() .AddSingleton(processInfo) .AddSingleton() From 5a7b955c18d81b9f869cf88270b797421748a0d4 Mon Sep 17 00:00:00 2001 From: crowbarmaster Date: Fri, 17 Dec 2021 16:11:26 -0500 Subject: [PATCH 53/62] Add support for other methods of graceful shutdown. .NET Core host server now causes service to stay alive after shutting down servers. Call Enviorment.Exit(0); to finish shutting down service. --- BedrockService/Service/Core/Service.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/BedrockService/Service/Core/Service.cs b/BedrockService/Service/Core/Service.cs index 4c1f1c87..edad1768 100644 --- a/BedrockService/Service/Core/Service.cs +++ b/BedrockService/Service/Core/Service.cs @@ -31,8 +31,11 @@ await Task.Run(() => { while (server.GetServerStatus() != BedrockServer.ServerStatus.Stopped) Thread.Sleep(100); } + Environment.Exit(0); }); }); + hostConfig.EnableShutdown(); + hostConfig.EnableHandleCtrlBreak(); hostConfig.RunAsLocalSystem(); hostConfig.SetDescription("Windows Service Wrapper for Windows Bedrock Server"); hostConfig.SetDisplayName("BedrockService"); @@ -72,5 +75,9 @@ public Task StopAsync(CancellationToken cancellationToken) { private void OnStarted() { Task.Run(() => { _host.Run(); }); } + + private void OnStopping() { + Task.Run(() => { _host.Run(); }); + } } } From bf535d0a89c8eb1803e165a3460428a2ae73e071 Mon Sep 17 00:00:00 2001 From: crowbarmaster Date: Fri, 17 Dec 2021 16:13:41 -0500 Subject: [PATCH 54/62] Update version to 2.5.5.1 bugfix revision. --- BedrockService/Client/Properties/AssemblyInfo.cs | 4 ++-- BedrockService/Service/Properties/AssemblyInfo.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/BedrockService/Client/Properties/AssemblyInfo.cs b/BedrockService/Client/Properties/AssemblyInfo.cs index 24522be6..aae11eb6 100644 --- a/BedrockService/Client/Properties/AssemblyInfo.cs +++ b/BedrockService/Client/Properties/AssemblyInfo.cs @@ -31,5 +31,5 @@ // 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("2.5.5.0")] -[assembly: AssemblyFileVersion("2.5.5.0")] +[assembly: AssemblyVersion("2.5.5.1")] +[assembly: AssemblyFileVersion("2.5.5.1")] diff --git a/BedrockService/Service/Properties/AssemblyInfo.cs b/BedrockService/Service/Properties/AssemblyInfo.cs index 214159ab..3de58346 100644 --- a/BedrockService/Service/Properties/AssemblyInfo.cs +++ b/BedrockService/Service/Properties/AssemblyInfo.cs @@ -30,5 +30,5 @@ // 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("2.5.5.0")] -[assembly: AssemblyFileVersion("2.5.5.0")] +[assembly: AssemblyVersion("2.5.5.1")] +[assembly: AssemblyFileVersion("2.5.5.1")] From 61c01755724bbc9147f7a2e6c3621fd8c1a1c532 Mon Sep 17 00:00:00 2001 From: crowbarmaster Date: Sat, 18 Dec 2021 11:13:09 -0500 Subject: [PATCH 55/62] Add shouldStartService bool to process info container. Used to block service features from executing when cmd line arguments destined for TopShelf are found at startup. --- .../Classes/ServiceInfo.cs | 4 ++- .../Classes/ServiceProcessInfo.cs | 6 ++++- .../Interfaces/IProcessInfo.cs | 2 ++ BedrockService/Service/Core/BedrockService.cs | 26 ++++++++++--------- BedrockService/Service/Core/Service.cs | 4 --- .../Service/Logging/ServiceLogger.cs | 16 +++++++----- .../Networking/NetworkStrategyLookup.cs | 4 ++- .../Service/Networking/TCPListener.cs | 6 +++-- BedrockService/Service/Networking/Updater.cs | 12 +++++---- BedrockService/Service/Program.cs | 12 ++++++--- .../Service/Properties/AssemblyInfo.cs | 4 +-- 11 files changed, 58 insertions(+), 38 deletions(-) diff --git a/BedrockService/BedrockService.Shared/Classes/ServiceInfo.cs b/BedrockService/BedrockService.Shared/Classes/ServiceInfo.cs index 75b46a38..9cc24aee 100644 --- a/BedrockService/BedrockService.Shared/Classes/ServiceInfo.cs +++ b/BedrockService/BedrockService.Shared/Classes/ServiceInfo.cs @@ -20,7 +20,9 @@ public class ServiceInfo : IServiceConfiguration { public ServiceInfo(IProcessInfo processInfo) { _processInfo = processInfo; - InitializeDefaults(); + if (processInfo.ShouldStartService()) { + InitializeDefaults(); + } } public void InitializeDefaults() { diff --git a/BedrockService/BedrockService.Shared/Classes/ServiceProcessInfo.cs b/BedrockService/BedrockService.Shared/Classes/ServiceProcessInfo.cs index c18551a7..7681bd5f 100644 --- a/BedrockService/BedrockService.Shared/Classes/ServiceProcessInfo.cs +++ b/BedrockService/BedrockService.Shared/Classes/ServiceProcessInfo.cs @@ -6,12 +6,14 @@ public class ServiceProcessInfo : IProcessInfo { private readonly int _processPid; private bool _debugEnabled; private bool _isConsoleMode; + private bool _shouldStartService; - public ServiceProcessInfo(string serviceDirectory, int processPid, bool debugEnabled, bool isConsoleMode) { + public ServiceProcessInfo(string serviceDirectory, int processPid, bool debugEnabled, bool isConsoleMode, bool shouldStartService) { _serviceDirectory = serviceDirectory; _processPid = processPid; _debugEnabled = debugEnabled; _isConsoleMode = isConsoleMode; + _shouldStartService = shouldStartService; } public string GetDirectory() => _serviceDirectory; @@ -22,6 +24,8 @@ public ServiceProcessInfo(string serviceDirectory, int processPid, bool debugEna public bool IsConsoleMode() => _isConsoleMode; + public bool ShouldStartService() => _shouldStartService; + public void SetArguments(bool isDebug, bool isConsole) { _debugEnabled = isDebug; _isConsoleMode = isConsole; diff --git a/BedrockService/BedrockService.Shared/Interfaces/IProcessInfo.cs b/BedrockService/BedrockService.Shared/Interfaces/IProcessInfo.cs index ed1a86b0..a0e22cf3 100644 --- a/BedrockService/BedrockService.Shared/Interfaces/IProcessInfo.cs +++ b/BedrockService/BedrockService.Shared/Interfaces/IProcessInfo.cs @@ -8,6 +8,8 @@ public interface IProcessInfo { bool IsConsoleMode(); + bool ShouldStartService(); + void SetArguments(bool isDebug, bool isConsole); } } diff --git a/BedrockService/Service/Core/BedrockService.cs b/BedrockService/Service/Core/BedrockService.cs index 7614fe60..01280d89 100644 --- a/BedrockService/Service/Core/BedrockService.cs +++ b/BedrockService/Service/Core/BedrockService.cs @@ -25,18 +25,20 @@ private enum ServiceStatus { private System.Timers.Timer _cronTimer; public BedrockService(IConfigurator configurator, IUpdater updater, IBedrockLogger logger, IServiceConfiguration serviceConfiguration, IProcessInfo serviceProcessInfo, ITCPListener tCPListener) { - _tCPListener = tCPListener; - _configurator = configurator; - _configurator.LoadAllConfigurations().Wait(); - _serviceConfiguration = serviceConfiguration; - _processInfo = serviceProcessInfo; - _updater = updater; - _updater.CheckUpdates().Wait(); - _logger = logger; - _shed = CrontabSchedule.TryParse(serviceConfiguration.GetProp("BackupCron").ToString()); - _updaterCron = CrontabSchedule.TryParse(serviceConfiguration.GetProp("UpdateCron").ToString()); - Initialize(); - _tCPListener.SetKeyContainer(_configurator.GetKeyContainer()); + if (serviceProcessInfo.ShouldStartService()) { + _tCPListener = tCPListener; + _configurator = configurator; + _configurator.LoadAllConfigurations().Wait(); + _serviceConfiguration = serviceConfiguration; + _processInfo = serviceProcessInfo; + _updater = updater; + _updater.CheckUpdates().Wait(); + _logger = logger; + _shed = CrontabSchedule.TryParse(serviceConfiguration.GetProp("BackupCron").ToString()); + _updaterCron = CrontabSchedule.TryParse(serviceConfiguration.GetProp("UpdateCron").ToString()); + Initialize(); + _tCPListener.SetKeyContainer(_configurator.GetKeyContainer()); + } } public bool Start(HostControl hostControl) { diff --git a/BedrockService/Service/Core/Service.cs b/BedrockService/Service/Core/Service.cs index edad1768..14e9b86f 100644 --- a/BedrockService/Service/Core/Service.cs +++ b/BedrockService/Service/Core/Service.cs @@ -75,9 +75,5 @@ public Task StopAsync(CancellationToken cancellationToken) { private void OnStarted() { Task.Run(() => { _host.Run(); }); } - - private void OnStopping() { - Task.Run(() => { _host.Run(); }); - } } } diff --git a/BedrockService/Service/Logging/ServiceLogger.cs b/BedrockService/Service/Logging/ServiceLogger.cs index c6d6e2ae..28615fac 100644 --- a/BedrockService/Service/Logging/ServiceLogger.cs +++ b/BedrockService/Service/Logging/ServiceLogger.cs @@ -15,14 +15,16 @@ public class ServiceLogger : IBedrockLogger { public ServiceLogger(IProcessInfo processInfo, IServiceConfiguration serviceConfiguration) { _serviceConfiguration = serviceConfiguration; _logPath = $@"{processInfo.GetDirectory()}\Service\Logs"; - _logToFile = bool.Parse(serviceConfiguration.GetProp("LogServiceToFile").ToString()); - _logToConsole = true; - if (_logToFile) { - if (!Directory.Exists(_logPath)) - Directory.CreateDirectory(_logPath); - _logWriter = new StreamWriter($@"{_logPath}\ServiceLog_{_parent}_{DateTime.Now:yyyymmddhhmmss}.log", true); + if (processInfo.ShouldStartService()) { + _logToFile = bool.Parse(serviceConfiguration.GetProp("LogServiceToFile").ToString()); + _logToConsole = true; + if (_logToFile) { + if (!Directory.Exists(_logPath)) + Directory.CreateDirectory(_logPath); + _logWriter = new StreamWriter($@"{_logPath}\ServiceLog_{_parent}_{DateTime.Now:yyyymmddhhmmss}.log", true); + } + AppendLine($"Service logging started. Service working directory: {processInfo.GetDirectory()}"); } - AppendLine($"Service logging started. Service working directory: {processInfo.GetDirectory()}"); } [JsonConstructor] diff --git a/BedrockService/Service/Networking/NetworkStrategyLookup.cs b/BedrockService/Service/Networking/NetworkStrategyLookup.cs index 56a64e4f..6ddbdbd1 100644 --- a/BedrockService/Service/Networking/NetworkStrategyLookup.cs +++ b/BedrockService/Service/Networking/NetworkStrategyLookup.cs @@ -41,7 +41,9 @@ public NetworkStrategyLookup(ITCPListener messageSender, IBedrockService service { {NetworkMessageTypes.RemoveServer, new RemoveServer(configurator, messageSender, serviceConfiguration, service) }, }; - messageSender.SetStrategyDictionaries(_standardMessageLookup, _flaggedMessageLookup); + if (processInfo.ShouldStartService()) { + messageSender.SetStrategyDictionaries(_standardMessageLookup, _flaggedMessageLookup); + } } class DeleteBackups : IMessageParser { diff --git a/BedrockService/Service/Networking/TCPListener.cs b/BedrockService/Service/Networking/TCPListener.cs index 91762995..94b7e279 100644 --- a/BedrockService/Service/Networking/TCPListener.cs +++ b/BedrockService/Service/Networking/TCPListener.cs @@ -20,11 +20,13 @@ public class TCPListener : ITCPListener, IMessageSender { private Task _tcpTask; private Task _recieverTask; - public TCPListener(IServiceConfiguration serviceConfiguration, IBedrockLogger logger) { + public TCPListener(IServiceConfiguration serviceConfiguration, IBedrockLogger logger, IProcessInfo processInfo) { _logger = logger; _serviceConfiguration = serviceConfiguration; _cancelTokenSource = new CancellationTokenSource(); - InitializeTasks(); + if (processInfo.ShouldStartService()) { + InitializeTasks(); + } } private void InitializeTasks() { diff --git a/BedrockService/Service/Networking/Updater.cs b/BedrockService/Service/Networking/Updater.cs index 10372e79..2f2bf4ba 100644 --- a/BedrockService/Service/Networking/Updater.cs +++ b/BedrockService/Service/Networking/Updater.cs @@ -16,12 +16,14 @@ public Updater(IProcessInfo processInfo, IBedrockLogger logger, IServiceConfigur _processInfo = processInfo; _logger = logger; _version = "None"; - if (!Directory.Exists($@"{processInfo.GetDirectory()}\Server")) { Directory.CreateDirectory($@"{processInfo.GetDirectory()}\Server"); } - if (!File.Exists($@"{processInfo.GetDirectory()}\Server\bedrock_ver.ini")) { - logger.AppendLine("Version ini file missing, creating and fetching build..."); - File.Create($@"{processInfo.GetDirectory()}\Server\bedrock_ver.ini").Close(); + if (processInfo.ShouldStartService()) { + if (!Directory.Exists($@"{processInfo.GetDirectory()}\Server")) { Directory.CreateDirectory($@"{processInfo.GetDirectory()}\Server"); } + if (!File.Exists($@"{processInfo.GetDirectory()}\Server\bedrock_ver.ini")) { + logger.AppendLine("Version ini file missing, creating and fetching build..."); + File.Create($@"{processInfo.GetDirectory()}\Server\bedrock_ver.ini").Close(); + } + _version = File.ReadAllText($@"{processInfo.GetDirectory()}\Server\bedrock_ver.ini"); } - _version = File.ReadAllText($@"{processInfo.GetDirectory()}\Server\bedrock_ver.ini"); } public async Task CheckUpdates() { diff --git a/BedrockService/Service/Program.cs b/BedrockService/Service/Program.cs index 4710734c..66028d83 100644 --- a/BedrockService/Service/Program.cs +++ b/BedrockService/Service/Program.cs @@ -27,12 +27,18 @@ public class Program { public static bool IsExiting = false; private static bool _isDebugEnabled = false; private static bool _isConsoleMode = false; + private static bool _shouldStartService = true; private static CancellationTokenSource CancellationTokenSource = new CancellationTokenSource(); private static CancellationToken token = CancellationTokenSource.Token; public static void Main(string[] args) { if (args.Length > 0) { Console.WriteLine(string.Join(" ", args)); _isDebugEnabled = args[0].ToLower() == "-debug"; + _shouldStartService = + args[0].ToLower() != "install" && + args[0].ToLower() != "uninstall" && + args[0].ToLower() != "start" && + args[0].ToLower() != "stop"; } if (args.Length == 0 || Environment.UserInteractive) { _isConsoleMode = true; @@ -43,15 +49,15 @@ public static void Main(string[] args) { public static IHostBuilder CreateHostBuilder(string[] args) => Microsoft.Extensions.Hosting.Host.CreateDefaultBuilder(args) .ConfigureServices((hostContext, services) => { - IProcessInfo processInfo = new ServiceProcessInfo(Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName), Process.GetCurrentProcess().Id, _isDebugEnabled, _isConsoleMode); + IProcessInfo processInfo = new ServiceProcessInfo(Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName), Process.GetCurrentProcess().Id, _isDebugEnabled, _isConsoleMode, _shouldStartService); services.AddHostedService() .AddSingleton(processInfo) .AddSingleton() .AddSingleton() - .AddSingleton() .AddSingleton() - .AddSingleton() .AddSingleton() + .AddSingleton() + .AddSingleton() .AddSingleton(); }); } diff --git a/BedrockService/Service/Properties/AssemblyInfo.cs b/BedrockService/Service/Properties/AssemblyInfo.cs index 3de58346..a7ec8c64 100644 --- a/BedrockService/Service/Properties/AssemblyInfo.cs +++ b/BedrockService/Service/Properties/AssemblyInfo.cs @@ -30,5 +30,5 @@ // 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("2.5.5.1")] -[assembly: AssemblyFileVersion("2.5.5.1")] +[assembly: AssemblyVersion("2.5.5.2")] +[assembly: AssemblyFileVersion("2.5.5.2")] From 399b688efc93527c636da46b445a1ec7de596475 Mon Sep 17 00:00:00 2001 From: crowbarmaster Date: Sat, 18 Dec 2021 11:15:29 -0500 Subject: [PATCH 56/62] Fix forms to contain a minimum size to prevent the cropping issue detailed in issue #17. --- .../Client/Forms/AddNewServerForm.Designer.cs | 53 ++++--- .../Client/Forms/AddNewServerForm.resx | 62 +------- .../Client/Forms/ClientConfigForm.Designer.cs | 34 +++-- .../Client/Forms/ClientConfigForm.resx | 62 +------- .../Client/Forms/MainWindow.Designer.cs | 134 +++++++++++------- BedrockService/Client/Forms/MainWindow.resx | 62 +------- .../Client/Forms/ManagePacksForms.Designer.cs | 26 ++-- .../NewPlayerRegistrationForm.Designer.cs | 58 +++++--- .../Forms/NewPlayerRegistrationForm.resx | 62 +------- .../Forms/PlayerManagerForm.Designer.cs | 91 ++++++------ .../Client/Forms/PlayerManagerForm.resx | 62 +------- .../Client/Forms/PropEditorForm.Designer.cs | 2 +- .../Client/Management/FormManager.cs | 2 +- .../Client/Properties/AssemblyInfo.cs | 4 +- 14 files changed, 242 insertions(+), 472 deletions(-) diff --git a/BedrockService/Client/Forms/AddNewServerForm.Designer.cs b/BedrockService/Client/Forms/AddNewServerForm.Designer.cs index 1291703f..da5d8b41 100644 --- a/BedrockService/Client/Forms/AddNewServerForm.Designer.cs +++ b/BedrockService/Client/Forms/AddNewServerForm.Designer.cs @@ -42,57 +42,64 @@ private void InitializeComponent() // // srvNameBox // - this.srvNameBox.Location = new System.Drawing.Point(106, 56); + this.srvNameBox.Location = new System.Drawing.Point(124, 65); + this.srvNameBox.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.srvNameBox.Name = "srvNameBox"; - this.srvNameBox.Size = new System.Drawing.Size(100, 20); + this.srvNameBox.Size = new System.Drawing.Size(116, 23); this.srvNameBox.TabIndex = 0; // // label1 // this.label1.AutoSize = true; - this.label1.Location = new System.Drawing.Point(30, 59); + this.label1.Location = new System.Drawing.Point(35, 68); + this.label1.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label1.Name = "label1"; - this.label1.Size = new System.Drawing.Size(70, 13); + this.label1.Size = new System.Drawing.Size(75, 15); this.label1.TabIndex = 1; this.label1.Text = "Server name:"; // // label2 // this.label2.AutoSize = true; - this.label2.Location = new System.Drawing.Point(30, 85); + this.label2.Location = new System.Drawing.Point(35, 98); + this.label2.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label2.Name = "label2"; - this.label2.Size = new System.Drawing.Size(56, 13); + this.label2.Size = new System.Drawing.Size(60, 15); this.label2.TabIndex = 3; this.label2.Text = "IP v4 port:"; // // ipV4Box // - this.ipV4Box.Location = new System.Drawing.Point(106, 82); + this.ipV4Box.Location = new System.Drawing.Point(124, 95); + this.ipV4Box.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.ipV4Box.Name = "ipV4Box"; - this.ipV4Box.Size = new System.Drawing.Size(100, 20); + this.ipV4Box.Size = new System.Drawing.Size(116, 23); this.ipV4Box.TabIndex = 2; // // label3 // this.label3.AutoSize = true; - this.label3.Location = new System.Drawing.Point(30, 111); + this.label3.Location = new System.Drawing.Point(35, 128); + this.label3.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label3.Name = "label3"; - this.label3.Size = new System.Drawing.Size(56, 13); + this.label3.Size = new System.Drawing.Size(60, 15); this.label3.TabIndex = 5; this.label3.Text = "IP v6 port:"; // // ipV6Box // - this.ipV6Box.Location = new System.Drawing.Point(106, 108); + this.ipV6Box.Location = new System.Drawing.Point(124, 125); + this.ipV6Box.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.ipV6Box.Name = "ipV6Box"; - this.ipV6Box.Size = new System.Drawing.Size(100, 20); + this.ipV6Box.Size = new System.Drawing.Size(116, 23); this.ipV6Box.TabIndex = 4; // // editPropsBtn // - this.editPropsBtn.Location = new System.Drawing.Point(33, 149); + this.editPropsBtn.Location = new System.Drawing.Point(38, 172); + this.editPropsBtn.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.editPropsBtn.Name = "editPropsBtn"; - this.editPropsBtn.Size = new System.Drawing.Size(173, 23); + this.editPropsBtn.Size = new System.Drawing.Size(202, 27); this.editPropsBtn.TabIndex = 6; this.editPropsBtn.Text = "Edit server settings..."; this.editPropsBtn.UseVisualStyleBackColor = true; @@ -100,9 +107,10 @@ private void InitializeComponent() // // saveBtn // - this.saveBtn.Location = new System.Drawing.Point(106, 191); + this.saveBtn.Location = new System.Drawing.Point(124, 220); + this.saveBtn.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.saveBtn.Name = "saveBtn"; - this.saveBtn.Size = new System.Drawing.Size(100, 23); + this.saveBtn.Size = new System.Drawing.Size(117, 27); this.saveBtn.TabIndex = 7; this.saveBtn.Text = "Save Server"; this.saveBtn.UseVisualStyleBackColor = true; @@ -112,18 +120,19 @@ private void InitializeComponent() // this.label4.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); - this.label4.Location = new System.Drawing.Point(30, 9); + this.label4.Location = new System.Drawing.Point(35, 10); + this.label4.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label4.Name = "label4"; - this.label4.Size = new System.Drawing.Size(176, 44); + this.label4.Size = new System.Drawing.Size(201, 51); this.label4.TabIndex = 8; this.label4.Text = "Add a new server to the service. Note: Requires a Global restart to run new serve" + "r!"; // // AddNewServerForm // - this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(238, 226); + this.ClientSize = new System.Drawing.Size(274, 261); this.Controls.Add(this.label4); this.Controls.Add(this.saveBtn); this.Controls.Add(this.editPropsBtn); @@ -133,10 +142,14 @@ private void InitializeComponent() this.Controls.Add(this.ipV4Box); this.Controls.Add(this.label1); this.Controls.Add(this.srvNameBox); + this.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.MaximizeBox = false; + this.MaximumSize = new System.Drawing.Size(290, 300); this.MinimizeBox = false; + this.MinimumSize = new System.Drawing.Size(290, 300); this.Name = "AddNewServerForm"; this.ShowInTaskbar = false; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; this.Text = "AddNewServerForm"; this.ResumeLayout(false); this.PerformLayout(); diff --git a/BedrockService/Client/Forms/AddNewServerForm.resx b/BedrockService/Client/Forms/AddNewServerForm.resx index 1af7de15..f298a7be 100644 --- a/BedrockService/Client/Forms/AddNewServerForm.resx +++ b/BedrockService/Client/Forms/AddNewServerForm.resx @@ -1,64 +1,4 @@ - - - + diff --git a/BedrockService/Client/Forms/ClientConfigForm.Designer.cs b/BedrockService/Client/Forms/ClientConfigForm.Designer.cs index bf9ac45d..66a746e4 100644 --- a/BedrockService/Client/Forms/ClientConfigForm.Designer.cs +++ b/BedrockService/Client/Forms/ClientConfigForm.Designer.cs @@ -41,16 +41,20 @@ private void InitializeComponent() // // serverGridView // + this.serverGridView.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); this.serverGridView.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; this.serverGridView.Columns.AddRange(new System.Windows.Forms.DataGridViewColumn[] { this.HostName, this.HostAddress, this.Port}); - this.serverGridView.Location = new System.Drawing.Point(12, 30); + this.serverGridView.Location = new System.Drawing.Point(9, 22); + this.serverGridView.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2); this.serverGridView.Name = "serverGridView"; this.serverGridView.RowHeadersWidth = 62; this.serverGridView.RowTemplate.Height = 28; - this.serverGridView.Size = new System.Drawing.Size(1154, 381); + this.serverGridView.Size = new System.Drawing.Size(786, 184); this.serverGridView.TabIndex = 0; // // HostName @@ -76,18 +80,22 @@ private void InitializeComponent() // // nbtPathLabel // + this.nbtPathLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.nbtPathLabel.AutoSize = true; - this.nbtPathLabel.Location = new System.Drawing.Point(12, 437); + this.nbtPathLabel.Location = new System.Drawing.Point(9, 226); + this.nbtPathLabel.Margin = new System.Windows.Forms.Padding(2, 0, 2, 0); this.nbtPathLabel.Name = "nbtPathLabel"; - this.nbtPathLabel.Size = new System.Drawing.Size(130, 20); + this.nbtPathLabel.Size = new System.Drawing.Size(95, 15); this.nbtPathLabel.TabIndex = 1; this.nbtPathLabel.Text = "NBT Studio path:"; // // nbtButton // - this.nbtButton.Location = new System.Drawing.Point(16, 473); + this.nbtButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.nbtButton.Location = new System.Drawing.Point(12, 253); + this.nbtButton.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2); this.nbtButton.Name = "nbtButton"; - this.nbtButton.Size = new System.Drawing.Size(209, 37); + this.nbtButton.Size = new System.Drawing.Size(163, 28); this.nbtButton.TabIndex = 2; this.nbtButton.Text = "Set NBT Studio path"; this.nbtButton.UseVisualStyleBackColor = true; @@ -95,9 +103,11 @@ private void InitializeComponent() // // saveBtn // - this.saveBtn.Location = new System.Drawing.Point(957, 473); + this.saveBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); + this.saveBtn.Location = new System.Drawing.Point(632, 253); + this.saveBtn.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2); this.saveBtn.Name = "saveBtn"; - this.saveBtn.Size = new System.Drawing.Size(209, 37); + this.saveBtn.Size = new System.Drawing.Size(163, 28); this.saveBtn.TabIndex = 3; this.saveBtn.Text = "Save settings"; this.saveBtn.UseVisualStyleBackColor = true; @@ -105,15 +115,17 @@ private void InitializeComponent() // // ClientConfigForm // - this.AutoScaleDimensions = new System.Drawing.SizeF(9F, 20F); + this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(1178, 544); + this.ClientSize = new System.Drawing.Size(804, 306); this.Controls.Add(this.saveBtn); this.Controls.Add(this.nbtButton); this.Controls.Add(this.nbtPathLabel); this.Controls.Add(this.serverGridView); + this.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2); + this.MinimumSize = new System.Drawing.Size(820, 345); this.Name = "ClientConfigForm"; - this.Text = "ClientConfigForm"; + this.Text = "Client Configuration"; ((System.ComponentModel.ISupportInitialize)(this.serverGridView)).EndInit(); this.ResumeLayout(false); this.PerformLayout(); diff --git a/BedrockService/Client/Forms/ClientConfigForm.resx b/BedrockService/Client/Forms/ClientConfigForm.resx index 2d8f1f02..731e337c 100644 --- a/BedrockService/Client/Forms/ClientConfigForm.resx +++ b/BedrockService/Client/Forms/ClientConfigForm.resx @@ -1,64 +1,4 @@ - - - + diff --git a/BedrockService/Client/Forms/MainWindow.Designer.cs b/BedrockService/Client/Forms/MainWindow.Designer.cs index 7b72ed20..f63ebee1 100644 --- a/BedrockService/Client/Forms/MainWindow.Designer.cs +++ b/BedrockService/Client/Forms/MainWindow.Designer.cs @@ -58,9 +58,10 @@ private void InitializeComponent() // Connect // this.Connect.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.Connect.Location = new System.Drawing.Point(622, 57); + this.Connect.Location = new System.Drawing.Point(580, 66); + this.Connect.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.Connect.Name = "Connect"; - this.Connect.Size = new System.Drawing.Size(170, 25); + this.Connect.Size = new System.Drawing.Size(198, 29); this.Connect.TabIndex = 0; this.Connect.Text = "Connect"; this.Connect.UseVisualStyleBackColor = true; @@ -70,9 +71,10 @@ private void InitializeComponent() // this.HostInfoLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.HostInfoLabel.AutoSize = true; - this.HostInfoLabel.Location = new System.Drawing.Point(620, 12); + this.HostInfoLabel.Location = new System.Drawing.Point(577, 14); + this.HostInfoLabel.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.HostInfoLabel.Name = "HostInfoLabel"; - this.HostInfoLabel.Size = new System.Drawing.Size(87, 13); + this.HostInfoLabel.Size = new System.Drawing.Size(98, 15); this.HostInfoLabel.TabIndex = 1; this.HostInfoLabel.Text = "HostConnectInfo"; // @@ -81,9 +83,10 @@ private void InitializeComponent() this.HostListBox.AllowDrop = true; this.HostListBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.HostListBox.FormattingEnabled = true; - this.HostListBox.Location = new System.Drawing.Point(622, 28); + this.HostListBox.Location = new System.Drawing.Point(580, 32); + this.HostListBox.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.HostListBox.Name = "HostListBox"; - this.HostListBox.Size = new System.Drawing.Size(355, 21); + this.HostListBox.Size = new System.Drawing.Size(414, 23); this.HostListBox.TabIndex = 2; this.HostListBox.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.HostListBox_KeyPress); // @@ -92,12 +95,13 @@ private void InitializeComponent() this.LogBox.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); - this.LogBox.Location = new System.Drawing.Point(12, 28); + this.LogBox.Location = new System.Drawing.Point(14, 32); + this.LogBox.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.LogBox.Multiline = true; this.LogBox.Name = "LogBox"; this.LogBox.ReadOnly = true; this.LogBox.ScrollBars = System.Windows.Forms.ScrollBars.Both; - this.LogBox.Size = new System.Drawing.Size(580, 361); + this.LogBox.Size = new System.Drawing.Size(530, 402); this.LogBox.TabIndex = 3; this.LogBox.WordWrap = false; // @@ -106,9 +110,11 @@ private void InitializeComponent() this.ServerSelectBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Right))); this.ServerSelectBox.FormattingEnabled = true; - this.ServerSelectBox.Location = new System.Drawing.Point(808, 96); + this.ServerSelectBox.ItemHeight = 15; + this.ServerSelectBox.Location = new System.Drawing.Point(797, 111); + this.ServerSelectBox.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.ServerSelectBox.Name = "ServerSelectBox"; - this.ServerSelectBox.Size = new System.Drawing.Size(170, 95); + this.ServerSelectBox.Size = new System.Drawing.Size(198, 94); this.ServerSelectBox.TabIndex = 4; this.ServerSelectBox.SelectedIndexChanged += new System.EventHandler(this.ServerSelectBox_SelectedIndexChanged); // @@ -116,9 +122,10 @@ private void InitializeComponent() // this.Disconn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.Disconn.Enabled = false; - this.Disconn.Location = new System.Drawing.Point(808, 57); + this.Disconn.Location = new System.Drawing.Point(797, 66); + this.Disconn.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.Disconn.Name = "Disconn"; - this.Disconn.Size = new System.Drawing.Size(170, 25); + this.Disconn.Size = new System.Drawing.Size(198, 29); this.Disconn.TabIndex = 5; this.Disconn.Text = "Disconnect"; this.Disconn.UseVisualStyleBackColor = true; @@ -128,9 +135,10 @@ private void InitializeComponent() // this.EditGlobals.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.EditGlobals.Enabled = false; - this.EditGlobals.Location = new System.Drawing.Point(808, 266); + this.EditGlobals.Location = new System.Drawing.Point(797, 293); + this.EditGlobals.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.EditGlobals.Name = "EditGlobals"; - this.EditGlobals.Size = new System.Drawing.Size(170, 23); + this.EditGlobals.Size = new System.Drawing.Size(198, 27); this.EditGlobals.TabIndex = 8; this.EditGlobals.Text = "Edit global service settings"; this.EditGlobals.UseVisualStyleBackColor = true; @@ -140,9 +148,10 @@ private void InitializeComponent() // this.removeSrvBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.removeSrvBtn.Enabled = false; - this.removeSrvBtn.Location = new System.Drawing.Point(808, 353); + this.removeSrvBtn.Location = new System.Drawing.Point(797, 393); + this.removeSrvBtn.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.removeSrvBtn.Name = "removeSrvBtn"; - this.removeSrvBtn.Size = new System.Drawing.Size(170, 23); + this.removeSrvBtn.Size = new System.Drawing.Size(198, 27); this.removeSrvBtn.TabIndex = 9; this.removeSrvBtn.Text = "Remove selected server"; this.removeSrvBtn.UseVisualStyleBackColor = true; @@ -152,9 +161,10 @@ private void InitializeComponent() // this.ChkUpdates.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.ChkUpdates.Enabled = false; - this.ChkUpdates.Location = new System.Drawing.Point(808, 294); + this.ChkUpdates.Location = new System.Drawing.Point(797, 325); + this.ChkUpdates.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.ChkUpdates.Name = "ChkUpdates"; - this.ChkUpdates.Size = new System.Drawing.Size(170, 23); + this.ChkUpdates.Size = new System.Drawing.Size(198, 27); this.ChkUpdates.TabIndex = 10; this.ChkUpdates.Text = "Check for updates"; this.ChkUpdates.UseVisualStyleBackColor = true; @@ -164,9 +174,10 @@ private void InitializeComponent() // this.GlobBackup.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.GlobBackup.Enabled = false; - this.GlobBackup.Location = new System.Drawing.Point(808, 237); + this.GlobBackup.Location = new System.Drawing.Point(797, 259); + this.GlobBackup.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.GlobBackup.Name = "GlobBackup"; - this.GlobBackup.Size = new System.Drawing.Size(170, 23); + this.GlobBackup.Size = new System.Drawing.Size(198, 27); this.GlobBackup.TabIndex = 11; this.GlobBackup.Text = "Backup all servers"; this.GlobBackup.UseVisualStyleBackColor = true; @@ -176,9 +187,10 @@ private void InitializeComponent() // this.cmdTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.cmdTextBox.Enabled = false; - this.cmdTextBox.Location = new System.Drawing.Point(446, 429); + this.cmdTextBox.Location = new System.Drawing.Point(374, 481); + this.cmdTextBox.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.cmdTextBox.Name = "cmdTextBox"; - this.cmdTextBox.Size = new System.Drawing.Size(355, 20); + this.cmdTextBox.Size = new System.Drawing.Size(414, 23); this.cmdTextBox.TabIndex = 12; this.cmdTextBox.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.cmdTextBox_KeyPress); // @@ -186,9 +198,10 @@ private void InitializeComponent() // this.SendCmd.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.SendCmd.Enabled = false; - this.SendCmd.Location = new System.Drawing.Point(808, 426); + this.SendCmd.Location = new System.Drawing.Point(797, 478); + this.SendCmd.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.SendCmd.Name = "SendCmd"; - this.SendCmd.Size = new System.Drawing.Size(170, 23); + this.SendCmd.Size = new System.Drawing.Size(198, 27); this.SendCmd.TabIndex = 13; this.SendCmd.Text = "Send command to server"; this.SendCmd.UseVisualStyleBackColor = true; @@ -198,9 +211,10 @@ private void InitializeComponent() // this.EditCfg.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.EditCfg.Enabled = false; - this.EditCfg.Location = new System.Drawing.Point(622, 208); + this.EditCfg.Location = new System.Drawing.Point(580, 226); + this.EditCfg.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.EditCfg.Name = "EditCfg"; - this.EditCfg.Size = new System.Drawing.Size(170, 23); + this.EditCfg.Size = new System.Drawing.Size(198, 27); this.EditCfg.TabIndex = 15; this.EditCfg.Text = "Edit server config"; this.EditCfg.UseVisualStyleBackColor = true; @@ -210,9 +224,10 @@ private void InitializeComponent() // this.PlayerManagerBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.PlayerManagerBtn.Enabled = false; - this.PlayerManagerBtn.Location = new System.Drawing.Point(622, 382); + this.PlayerManagerBtn.Location = new System.Drawing.Point(580, 427); + this.PlayerManagerBtn.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.PlayerManagerBtn.Name = "PlayerManagerBtn"; - this.PlayerManagerBtn.Size = new System.Drawing.Size(170, 23); + this.PlayerManagerBtn.Size = new System.Drawing.Size(198, 27); this.PlayerManagerBtn.TabIndex = 16; this.PlayerManagerBtn.Text = "Player Manager"; this.PlayerManagerBtn.UseVisualStyleBackColor = true; @@ -222,9 +237,10 @@ private void InitializeComponent() // this.EditStCmd.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.EditStCmd.Enabled = false; - this.EditStCmd.Location = new System.Drawing.Point(622, 237); + this.EditStCmd.Location = new System.Drawing.Point(580, 259); + this.EditStCmd.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.EditStCmd.Name = "EditStCmd"; - this.EditStCmd.Size = new System.Drawing.Size(170, 23); + this.EditStCmd.Size = new System.Drawing.Size(198, 27); this.EditStCmd.TabIndex = 18; this.EditStCmd.Text = "Edit start commands"; this.EditStCmd.UseVisualStyleBackColor = true; @@ -234,9 +250,10 @@ private void InitializeComponent() // this.ManPacks.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.ManPacks.Enabled = false; - this.ManPacks.Location = new System.Drawing.Point(622, 353); + this.ManPacks.Location = new System.Drawing.Point(580, 393); + this.ManPacks.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.ManPacks.Name = "ManPacks"; - this.ManPacks.Size = new System.Drawing.Size(170, 23); + this.ManPacks.Size = new System.Drawing.Size(198, 27); this.ManPacks.TabIndex = 19; this.ManPacks.Text = "R/B Pack Manager"; this.ManPacks.UseVisualStyleBackColor = true; @@ -246,9 +263,10 @@ private void InitializeComponent() // this.SingBackup.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.SingBackup.Enabled = false; - this.SingBackup.Location = new System.Drawing.Point(622, 266); + this.SingBackup.Location = new System.Drawing.Point(580, 293); + this.SingBackup.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.SingBackup.Name = "SingBackup"; - this.SingBackup.Size = new System.Drawing.Size(170, 23); + this.SingBackup.Size = new System.Drawing.Size(198, 27); this.SingBackup.TabIndex = 20; this.SingBackup.Text = "Backup selected server"; this.SingBackup.UseVisualStyleBackColor = true; @@ -258,9 +276,10 @@ private void InitializeComponent() // this.RestartSrv.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.RestartSrv.Enabled = false; - this.RestartSrv.Location = new System.Drawing.Point(622, 294); + this.RestartSrv.Location = new System.Drawing.Point(580, 325); + this.RestartSrv.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.RestartSrv.Name = "RestartSrv"; - this.RestartSrv.Size = new System.Drawing.Size(170, 23); + this.RestartSrv.Size = new System.Drawing.Size(198, 27); this.RestartSrv.TabIndex = 21; this.RestartSrv.Text = "Restart selected server"; this.RestartSrv.UseVisualStyleBackColor = true; @@ -270,9 +289,10 @@ private void InitializeComponent() // this.BackupManagerBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.BackupManagerBtn.Enabled = false; - this.BackupManagerBtn.Location = new System.Drawing.Point(622, 324); + this.BackupManagerBtn.Location = new System.Drawing.Point(580, 360); + this.BackupManagerBtn.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.BackupManagerBtn.Name = "BackupManagerBtn"; - this.BackupManagerBtn.Size = new System.Drawing.Size(170, 23); + this.BackupManagerBtn.Size = new System.Drawing.Size(198, 27); this.BackupManagerBtn.TabIndex = 22; this.BackupManagerBtn.Text = "Backup Manager"; this.BackupManagerBtn.UseVisualStyleBackColor = true; @@ -282,10 +302,11 @@ private void InitializeComponent() // this.SvcLog.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.SvcLog.Enabled = false; - this.SvcLog.Location = new System.Drawing.Point(17, 395); + this.SvcLog.Location = new System.Drawing.Point(20, 442); + this.SvcLog.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.SvcLog.Name = "SvcLog"; this.SvcLog.RightToLeft = System.Windows.Forms.RightToLeft.No; - this.SvcLog.Size = new System.Drawing.Size(131, 26); + this.SvcLog.Size = new System.Drawing.Size(153, 30); this.SvcLog.TabIndex = 24; this.SvcLog.Text = "Switch to service logs"; this.SvcLog.TextAlign = System.Drawing.ContentAlignment.MiddleRight; @@ -295,20 +316,22 @@ private void InitializeComponent() // this.ServerInfoBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Right))); - this.ServerInfoBox.Location = new System.Drawing.Point(622, 96); + this.ServerInfoBox.Location = new System.Drawing.Point(580, 111); + this.ServerInfoBox.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.ServerInfoBox.Multiline = true; this.ServerInfoBox.Name = "ServerInfoBox"; this.ServerInfoBox.ReadOnly = true; - this.ServerInfoBox.Size = new System.Drawing.Size(170, 95); + this.ServerInfoBox.Size = new System.Drawing.Size(198, 95); this.ServerInfoBox.TabIndex = 25; // // newSrvBtn // this.newSrvBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.newSrvBtn.Enabled = false; - this.newSrvBtn.Location = new System.Drawing.Point(808, 208); + this.newSrvBtn.Location = new System.Drawing.Point(797, 226); + this.newSrvBtn.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.newSrvBtn.Name = "newSrvBtn"; - this.newSrvBtn.Size = new System.Drawing.Size(170, 23); + this.newSrvBtn.Size = new System.Drawing.Size(198, 27); this.newSrvBtn.TabIndex = 26; this.newSrvBtn.Text = "Deploy new server"; this.newSrvBtn.UseVisualStyleBackColor = true; @@ -318,10 +341,11 @@ private void InitializeComponent() // this.scrollLockChkBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.scrollLockChkBox.Enabled = false; - this.scrollLockChkBox.Location = new System.Drawing.Point(464, 395); + this.scrollLockChkBox.Location = new System.Drawing.Point(395, 442); + this.scrollLockChkBox.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.scrollLockChkBox.Name = "scrollLockChkBox"; this.scrollLockChkBox.RightToLeft = System.Windows.Forms.RightToLeft.No; - this.scrollLockChkBox.Size = new System.Drawing.Size(131, 26); + this.scrollLockChkBox.Size = new System.Drawing.Size(153, 30); this.scrollLockChkBox.TabIndex = 27; this.scrollLockChkBox.Text = "Lock scrollbar to end"; this.scrollLockChkBox.TextAlign = System.Drawing.ContentAlignment.MiddleRight; @@ -332,9 +356,10 @@ private void InitializeComponent() // this.nbtStudioBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.nbtStudioBtn.Enabled = false; - this.nbtStudioBtn.Location = new System.Drawing.Point(808, 324); + this.nbtStudioBtn.Location = new System.Drawing.Point(797, 360); + this.nbtStudioBtn.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.nbtStudioBtn.Name = "nbtStudioBtn"; - this.nbtStudioBtn.Size = new System.Drawing.Size(170, 23); + this.nbtStudioBtn.Size = new System.Drawing.Size(198, 27); this.nbtStudioBtn.TabIndex = 28; this.nbtStudioBtn.Text = "Edit world via NBTStudio"; this.nbtStudioBtn.UseVisualStyleBackColor = true; @@ -343,10 +368,10 @@ private void InitializeComponent() // clientConfigBtn // this.clientConfigBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.clientConfigBtn.Location = new System.Drawing.Point(808, 382); - this.clientConfigBtn.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); + this.clientConfigBtn.Location = new System.Drawing.Point(797, 427); + this.clientConfigBtn.Margin = new System.Windows.Forms.Padding(5, 6, 5, 6); this.clientConfigBtn.Name = "clientConfigBtn"; - this.clientConfigBtn.Size = new System.Drawing.Size(170, 23); + this.clientConfigBtn.Size = new System.Drawing.Size(198, 27); this.clientConfigBtn.TabIndex = 29; this.clientConfigBtn.Text = "Edit client config"; this.clientConfigBtn.UseVisualStyleBackColor = true; @@ -354,9 +379,9 @@ private void InitializeComponent() // // MainWindow // - this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(989, 467); + this.ClientSize = new System.Drawing.Size(1008, 525); this.Controls.Add(this.clientConfigBtn); this.Controls.Add(this.nbtStudioBtn); this.Controls.Add(this.scrollLockChkBox); @@ -382,7 +407,8 @@ private void InitializeComponent() this.Controls.Add(this.HostListBox); this.Controls.Add(this.HostInfoLabel); this.Controls.Add(this.Connect); - this.Margin = new System.Windows.Forms.Padding(4, 5, 4, 5); + this.Margin = new System.Windows.Forms.Padding(5, 6, 5, 6); + this.MinimumSize = new System.Drawing.Size(1024, 564); this.Name = "MainWindow"; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; this.Text = "Bedrock Service Management"; diff --git a/BedrockService/Client/Forms/MainWindow.resx b/BedrockService/Client/Forms/MainWindow.resx index 26b2a12a..ff8110ee 100644 --- a/BedrockService/Client/Forms/MainWindow.resx +++ b/BedrockService/Client/Forms/MainWindow.resx @@ -1,64 +1,4 @@ - - - + diff --git a/BedrockService/Client/Forms/ManagePacksForms.Designer.cs b/BedrockService/Client/Forms/ManagePacksForms.Designer.cs index e7a6ff77..2256b867 100644 --- a/BedrockService/Client/Forms/ManagePacksForms.Designer.cs +++ b/BedrockService/Client/Forms/ManagePacksForms.Designer.cs @@ -46,8 +46,6 @@ private void InitializeComponent() // // serverListBox // - this.serverListBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) - | System.Windows.Forms.AnchorStyles.Left))); this.serverListBox.FormattingEnabled = true; this.serverListBox.ItemHeight = 15; this.serverListBox.Location = new System.Drawing.Point(14, 95); @@ -64,7 +62,7 @@ private void InitializeComponent() | System.Windows.Forms.AnchorStyles.Right))); this.parsedPacksListBox.FormattingEnabled = true; this.parsedPacksListBox.ItemHeight = 15; - this.parsedPacksListBox.Location = new System.Drawing.Point(699, 95); + this.parsedPacksListBox.Location = new System.Drawing.Point(690, 95); this.parsedPacksListBox.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.parsedPacksListBox.Name = "parsedPacksListBox"; this.parsedPacksListBox.SelectionMode = System.Windows.Forms.SelectionMode.MultiSimple; @@ -75,7 +73,7 @@ private void InitializeComponent() // sendPacksBtn // this.sendPacksBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.sendPacksBtn.Location = new System.Drawing.Point(546, 95); + this.sendPacksBtn.Location = new System.Drawing.Point(537, 95); this.sendPacksBtn.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.sendPacksBtn.Name = "sendPacksBtn"; this.sendPacksBtn.Size = new System.Drawing.Size(146, 27); @@ -87,7 +85,7 @@ private void InitializeComponent() // sendAllBtn // this.sendAllBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.sendAllBtn.Location = new System.Drawing.Point(546, 128); + this.sendAllBtn.Location = new System.Drawing.Point(537, 128); this.sendAllBtn.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.sendAllBtn.Name = "sendAllBtn"; this.sendAllBtn.Size = new System.Drawing.Size(146, 27); @@ -120,9 +118,8 @@ private void InitializeComponent() // // textBox1 // - this.textBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.textBox1.Location = new System.Drawing.Point(336, 243); + this.textBox1.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom))); + this.textBox1.Location = new System.Drawing.Point(332, 243); this.textBox1.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.textBox1.Multiline = true; this.textBox1.Name = "textBox1"; @@ -132,8 +129,8 @@ private void InitializeComponent() // // selectedPackIcon // - this.selectedPackIcon.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); - this.selectedPackIcon.Location = new System.Drawing.Point(387, 87); + this.selectedPackIcon.Anchor = System.Windows.Forms.AnchorStyles.Top; + this.selectedPackIcon.Location = new System.Drawing.Point(383, 87); this.selectedPackIcon.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.selectedPackIcon.Name = "selectedPackIcon"; this.selectedPackIcon.Size = new System.Drawing.Size(152, 150); @@ -144,7 +141,7 @@ private void InitializeComponent() // openFileBtn // this.openFileBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.openFileBtn.Location = new System.Drawing.Point(699, 33); + this.openFileBtn.Location = new System.Drawing.Point(690, 33); this.openFileBtn.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.openFileBtn.Name = "openFileBtn"; this.openFileBtn.Size = new System.Drawing.Size(220, 27); @@ -167,7 +164,7 @@ private void InitializeComponent() // this.label2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); this.label2.AutoSize = true; - this.label2.Location = new System.Drawing.Point(695, 76); + this.label2.Location = new System.Drawing.Point(686, 76); this.label2.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label2.Name = "label2"; this.label2.Size = new System.Drawing.Size(161, 15); @@ -191,7 +188,7 @@ private void InitializeComponent() // this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(933, 519); + this.ClientSize = new System.Drawing.Size(924, 405); this.Controls.Add(this.label3); this.Controls.Add(this.label2); this.Controls.Add(this.label1); @@ -205,8 +202,9 @@ private void InitializeComponent() this.Controls.Add(this.parsedPacksListBox); this.Controls.Add(this.serverListBox); this.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + this.MinimumSize = new System.Drawing.Size(940, 444); this.Name = "ManagePacksForms"; - this.Text = "Form1"; + this.Text = "Pack Manager"; ((System.ComponentModel.ISupportInitialize)(this.selectedPackIcon)).EndInit(); this.ResumeLayout(false); this.PerformLayout(); diff --git a/BedrockService/Client/Forms/NewPlayerRegistrationForm.Designer.cs b/BedrockService/Client/Forms/NewPlayerRegistrationForm.Designer.cs index 66d091df..11647a25 100644 --- a/BedrockService/Client/Forms/NewPlayerRegistrationForm.Designer.cs +++ b/BedrockService/Client/Forms/NewPlayerRegistrationForm.Designer.cs @@ -45,50 +45,56 @@ private void InitializeComponent() // label2 // this.label2.AutoSize = true; - this.label2.Location = new System.Drawing.Point(70, 33); + this.label2.Location = new System.Drawing.Point(82, 38); + this.label2.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label2.Name = "label2"; - this.label2.Size = new System.Drawing.Size(36, 13); + this.label2.Size = new System.Drawing.Size(36, 15); this.label2.TabIndex = 1; this.label2.Text = "XUID:"; // // xuidTextBox // - this.xuidTextBox.Location = new System.Drawing.Point(111, 30); + this.xuidTextBox.Location = new System.Drawing.Point(130, 35); + this.xuidTextBox.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.xuidTextBox.Name = "xuidTextBox"; - this.xuidTextBox.Size = new System.Drawing.Size(185, 20); + this.xuidTextBox.Size = new System.Drawing.Size(215, 23); this.xuidTextBox.TabIndex = 2; // // usernameTextBox // - this.usernameTextBox.Location = new System.Drawing.Point(111, 56); + this.usernameTextBox.Location = new System.Drawing.Point(130, 65); + this.usernameTextBox.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.usernameTextBox.Name = "usernameTextBox"; - this.usernameTextBox.Size = new System.Drawing.Size(185, 20); + this.usernameTextBox.Size = new System.Drawing.Size(215, 23); this.usernameTextBox.TabIndex = 4; // // label3 // this.label3.AutoSize = true; - this.label3.Location = new System.Drawing.Point(48, 59); + this.label3.Location = new System.Drawing.Point(56, 68); + this.label3.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label3.Name = "label3"; - this.label3.Size = new System.Drawing.Size(58, 13); + this.label3.Size = new System.Drawing.Size(63, 15); this.label3.TabIndex = 3; this.label3.Text = "Username:"; // // label4 // this.label4.AutoSize = true; - this.label4.Location = new System.Drawing.Point(46, 85); + this.label4.Location = new System.Drawing.Point(54, 98); + this.label4.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label4.Name = "label4"; - this.label4.Size = new System.Drawing.Size(60, 13); + this.label4.Size = new System.Drawing.Size(68, 15); this.label4.TabIndex = 5; this.label4.Text = "Permission:"; // // label5 // this.label5.AutoSize = true; - this.label5.Location = new System.Drawing.Point(40, 109); + this.label5.Location = new System.Drawing.Point(47, 126); + this.label5.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label5.Name = "label5"; - this.label5.Size = new System.Drawing.Size(65, 13); + this.label5.Size = new System.Drawing.Size(71, 15); this.label5.TabIndex = 6; this.label5.Text = "Whitelisted?"; // @@ -99,15 +105,17 @@ private void InitializeComponent() "visitor", "member", "operator"}); - this.permissionComboBox.Location = new System.Drawing.Point(111, 82); + this.permissionComboBox.Location = new System.Drawing.Point(130, 95); + this.permissionComboBox.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.permissionComboBox.Name = "permissionComboBox"; - this.permissionComboBox.Size = new System.Drawing.Size(185, 21); + this.permissionComboBox.Size = new System.Drawing.Size(215, 23); this.permissionComboBox.TabIndex = 7; // // whitelistedChkBox // this.whitelistedChkBox.AutoSize = true; - this.whitelistedChkBox.Location = new System.Drawing.Point(111, 109); + this.whitelistedChkBox.Location = new System.Drawing.Point(130, 126); + this.whitelistedChkBox.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.whitelistedChkBox.Name = "whitelistedChkBox"; this.whitelistedChkBox.Size = new System.Drawing.Size(15, 14); this.whitelistedChkBox.TabIndex = 8; @@ -116,7 +124,8 @@ private void InitializeComponent() // ignoreLimitChkBox // this.ignoreLimitChkBox.AutoSize = true; - this.ignoreLimitChkBox.Location = new System.Drawing.Point(111, 132); + this.ignoreLimitChkBox.Location = new System.Drawing.Point(130, 152); + this.ignoreLimitChkBox.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.ignoreLimitChkBox.Name = "ignoreLimitChkBox"; this.ignoreLimitChkBox.Size = new System.Drawing.Size(15, 14); this.ignoreLimitChkBox.TabIndex = 9; @@ -125,17 +134,19 @@ private void InitializeComponent() // label6 // this.label6.AutoSize = true; - this.label6.Location = new System.Drawing.Point(5, 132); + this.label6.Location = new System.Drawing.Point(6, 152); + this.label6.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label6.Name = "label6"; - this.label6.Size = new System.Drawing.Size(101, 13); + this.label6.Size = new System.Drawing.Size(112, 15); this.label6.TabIndex = 10; this.label6.Text = "Ignore max players?"; // // button1 // - this.button1.Location = new System.Drawing.Point(221, 123); + this.button1.Location = new System.Drawing.Point(258, 142); + this.button1.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.button1.Name = "button1"; - this.button1.Size = new System.Drawing.Size(75, 25); + this.button1.Size = new System.Drawing.Size(88, 29); this.button1.TabIndex = 11; this.button1.Text = "Save"; this.button1.UseVisualStyleBackColor = true; @@ -143,9 +154,9 @@ private void InitializeComponent() // // NewPlayerRegistrationForm // - this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(323, 171); + this.ClientSize = new System.Drawing.Size(359, 191); this.Controls.Add(this.button1); this.Controls.Add(this.label6); this.Controls.Add(this.ignoreLimitChkBox); @@ -157,8 +168,11 @@ private void InitializeComponent() this.Controls.Add(this.label3); this.Controls.Add(this.xuidTextBox); this.Controls.Add(this.label2); + this.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.MaximizeBox = false; + this.MaximumSize = new System.Drawing.Size(375, 230); this.MinimizeBox = false; + this.MinimumSize = new System.Drawing.Size(375, 230); this.Name = "NewPlayerRegistrationForm"; this.Text = "New Player Registration Form"; this.ResumeLayout(false); diff --git a/BedrockService/Client/Forms/NewPlayerRegistrationForm.resx b/BedrockService/Client/Forms/NewPlayerRegistrationForm.resx index 1af7de15..f298a7be 100644 --- a/BedrockService/Client/Forms/NewPlayerRegistrationForm.resx +++ b/BedrockService/Client/Forms/NewPlayerRegistrationForm.resx @@ -1,64 +1,4 @@ - - - + diff --git a/BedrockService/Client/Forms/PlayerManagerForm.Designer.cs b/BedrockService/Client/Forms/PlayerManagerForm.Designer.cs index d548d659..bd450113 100644 --- a/BedrockService/Client/Forms/PlayerManagerForm.Designer.cs +++ b/BedrockService/Client/Forms/PlayerManagerForm.Designer.cs @@ -29,9 +29,9 @@ protected override void Dispose(bool disposing) /// private void InitializeComponent() { - System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle7 = new System.Windows.Forms.DataGridViewCellStyle(); - System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle8 = new System.Windows.Forms.DataGridViewCellStyle(); - System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle9 = new System.Windows.Forms.DataGridViewCellStyle(); + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle1 = new System.Windows.Forms.DataGridViewCellStyle(); + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle2 = new System.Windows.Forms.DataGridViewCellStyle(); + System.Windows.Forms.DataGridViewCellStyle dataGridViewCellStyle3 = new System.Windows.Forms.DataGridViewCellStyle(); this.searchEntryBox = new System.Windows.Forms.TextBox(); this.saveBtn = new System.Windows.Forms.Button(); this.label2 = new System.Windows.Forms.Label(); @@ -44,20 +44,22 @@ private void InitializeComponent() // this.searchEntryBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); - this.searchEntryBox.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.searchEntryBox.Location = new System.Drawing.Point(177, 390); + this.searchEntryBox.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point); + this.searchEntryBox.Location = new System.Drawing.Point(206, 349); + this.searchEntryBox.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.searchEntryBox.Name = "searchEntryBox"; - this.searchEntryBox.Size = new System.Drawing.Size(729, 26); + this.searchEntryBox.Size = new System.Drawing.Size(629, 26); this.searchEntryBox.TabIndex = 4; this.searchEntryBox.TextChanged += new System.EventHandler(this.searchEntryBox_TextChanged); // // saveBtn // this.saveBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.saveBtn.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.saveBtn.Location = new System.Drawing.Point(912, 390); + this.saveBtn.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point); + this.saveBtn.Location = new System.Drawing.Point(843, 349); + this.saveBtn.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.saveBtn.Name = "saveBtn"; - this.saveBtn.Size = new System.Drawing.Size(87, 26); + this.saveBtn.Size = new System.Drawing.Size(102, 30); this.saveBtn.TabIndex = 5; this.saveBtn.Text = "Save"; this.saveBtn.UseVisualStyleBackColor = true; @@ -67,8 +69,9 @@ private void InitializeComponent() // this.label2.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.label2.AutoSize = true; - this.label2.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.label2.Location = new System.Drawing.Point(127, 393); + this.label2.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point); + this.label2.Location = new System.Drawing.Point(148, 352); + this.label2.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); this.label2.Name = "label2"; this.label2.Size = new System.Drawing.Size(44, 20); this.label2.TabIndex = 6; @@ -81,44 +84,47 @@ private void InitializeComponent() | System.Windows.Forms.AnchorStyles.Right))); this.gridView.AutoSizeColumnsMode = System.Windows.Forms.DataGridViewAutoSizeColumnsMode.AllCells; this.gridView.AutoSizeRowsMode = System.Windows.Forms.DataGridViewAutoSizeRowsMode.AllCells; - dataGridViewCellStyle7.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft; - dataGridViewCellStyle7.BackColor = System.Drawing.SystemColors.Control; - dataGridViewCellStyle7.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - dataGridViewCellStyle7.ForeColor = System.Drawing.SystemColors.WindowText; - dataGridViewCellStyle7.SelectionBackColor = System.Drawing.SystemColors.Highlight; - dataGridViewCellStyle7.SelectionForeColor = System.Drawing.SystemColors.HighlightText; - dataGridViewCellStyle7.WrapMode = System.Windows.Forms.DataGridViewTriState.True; - this.gridView.ColumnHeadersDefaultCellStyle = dataGridViewCellStyle7; + dataGridViewCellStyle1.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft; + dataGridViewCellStyle1.BackColor = System.Drawing.SystemColors.Control; + dataGridViewCellStyle1.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point); + dataGridViewCellStyle1.ForeColor = System.Drawing.SystemColors.WindowText; + dataGridViewCellStyle1.SelectionBackColor = System.Drawing.SystemColors.Highlight; + dataGridViewCellStyle1.SelectionForeColor = System.Drawing.SystemColors.HighlightText; + dataGridViewCellStyle1.WrapMode = System.Windows.Forms.DataGridViewTriState.True; + this.gridView.ColumnHeadersDefaultCellStyle = dataGridViewCellStyle1; this.gridView.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; - dataGridViewCellStyle8.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft; - dataGridViewCellStyle8.BackColor = System.Drawing.SystemColors.Window; - dataGridViewCellStyle8.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - dataGridViewCellStyle8.ForeColor = System.Drawing.SystemColors.ControlText; - dataGridViewCellStyle8.SelectionBackColor = System.Drawing.SystemColors.Highlight; - dataGridViewCellStyle8.SelectionForeColor = System.Drawing.SystemColors.HighlightText; - dataGridViewCellStyle8.WrapMode = System.Windows.Forms.DataGridViewTriState.False; - this.gridView.DefaultCellStyle = dataGridViewCellStyle8; - this.gridView.Location = new System.Drawing.Point(16, 27); + dataGridViewCellStyle2.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft; + dataGridViewCellStyle2.BackColor = System.Drawing.SystemColors.Window; + dataGridViewCellStyle2.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point); + dataGridViewCellStyle2.ForeColor = System.Drawing.SystemColors.ControlText; + dataGridViewCellStyle2.SelectionBackColor = System.Drawing.SystemColors.Highlight; + dataGridViewCellStyle2.SelectionForeColor = System.Drawing.SystemColors.HighlightText; + dataGridViewCellStyle2.WrapMode = System.Windows.Forms.DataGridViewTriState.False; + this.gridView.DefaultCellStyle = dataGridViewCellStyle2; + this.gridView.Location = new System.Drawing.Point(19, 31); + this.gridView.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.gridView.Name = "gridView"; - dataGridViewCellStyle9.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft; - dataGridViewCellStyle9.BackColor = System.Drawing.SystemColors.Control; - dataGridViewCellStyle9.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - dataGridViewCellStyle9.ForeColor = System.Drawing.SystemColors.WindowText; - dataGridViewCellStyle9.SelectionBackColor = System.Drawing.SystemColors.Highlight; - dataGridViewCellStyle9.SelectionForeColor = System.Drawing.SystemColors.HighlightText; - dataGridViewCellStyle9.WrapMode = System.Windows.Forms.DataGridViewTriState.True; - this.gridView.RowHeadersDefaultCellStyle = dataGridViewCellStyle9; - this.gridView.Size = new System.Drawing.Size(983, 338); + dataGridViewCellStyle3.Alignment = System.Windows.Forms.DataGridViewContentAlignment.MiddleLeft; + dataGridViewCellStyle3.BackColor = System.Drawing.SystemColors.Control; + dataGridViewCellStyle3.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point); + dataGridViewCellStyle3.ForeColor = System.Drawing.SystemColors.WindowText; + dataGridViewCellStyle3.SelectionBackColor = System.Drawing.SystemColors.Highlight; + dataGridViewCellStyle3.SelectionForeColor = System.Drawing.SystemColors.HighlightText; + dataGridViewCellStyle3.WrapMode = System.Windows.Forms.DataGridViewTriState.True; + this.gridView.RowHeadersDefaultCellStyle = dataGridViewCellStyle3; + this.gridView.Size = new System.Drawing.Size(926, 289); this.gridView.TabIndex = 3; this.gridView.CellBeginEdit += new System.Windows.Forms.DataGridViewCellCancelEventHandler(this.gridView_CellBeginEdit); this.gridView.CellEndEdit += new System.Windows.Forms.DataGridViewCellEventHandler(this.gridView_CellEndEdit); // // registerPlayerBtn // - this.registerPlayerBtn.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.registerPlayerBtn.Location = new System.Drawing.Point(16, 390); + this.registerPlayerBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); + this.registerPlayerBtn.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point); + this.registerPlayerBtn.Location = new System.Drawing.Point(19, 348); + this.registerPlayerBtn.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.registerPlayerBtn.Name = "registerPlayerBtn"; - this.registerPlayerBtn.Size = new System.Drawing.Size(105, 26); + this.registerPlayerBtn.Size = new System.Drawing.Size(122, 30); this.registerPlayerBtn.TabIndex = 7; this.registerPlayerBtn.Text = "Register new..."; this.registerPlayerBtn.UseVisualStyleBackColor = true; @@ -126,15 +132,16 @@ private void InitializeComponent() // // PlayerManagerForm // - this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(1016, 435); + this.ClientSize = new System.Drawing.Size(964, 401); this.Controls.Add(this.registerPlayerBtn); this.Controls.Add(this.gridView); this.Controls.Add(this.label2); this.Controls.Add(this.saveBtn); this.Controls.Add(this.searchEntryBox); this.Margin = new System.Windows.Forms.Padding(2); + this.MinimumSize = new System.Drawing.Size(980, 440); this.Name = "PlayerManagerForm"; this.Text = "Player Manager"; ((System.ComponentModel.ISupportInitialize)(this.gridView)).EndInit(); diff --git a/BedrockService/Client/Forms/PlayerManagerForm.resx b/BedrockService/Client/Forms/PlayerManagerForm.resx index 1af7de15..f298a7be 100644 --- a/BedrockService/Client/Forms/PlayerManagerForm.resx +++ b/BedrockService/Client/Forms/PlayerManagerForm.resx @@ -1,64 +1,4 @@ - - - + diff --git a/BedrockService/Client/Forms/PropEditorForm.Designer.cs b/BedrockService/Client/Forms/PropEditorForm.Designer.cs index ef6d257c..45aac6b8 100644 --- a/BedrockService/Client/Forms/PropEditorForm.Designer.cs +++ b/BedrockService/Client/Forms/PropEditorForm.Designer.cs @@ -122,7 +122,7 @@ private void InitializeComponent() this.Controls.Add(this.SaveBtn); this.Controls.Add(this.CancelBtn); this.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); - this.MinimumSize = new System.Drawing.Size(730, 145); + this.MinimumSize = new System.Drawing.Size(730, 400); this.Name = "PropEditorForm"; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; this.Text = "Form1"; diff --git a/BedrockService/Client/Management/FormManager.cs b/BedrockService/Client/Management/FormManager.cs index 176d9452..e4eca0f6 100644 --- a/BedrockService/Client/Management/FormManager.cs +++ b/BedrockService/Client/Management/FormManager.cs @@ -8,7 +8,7 @@ namespace BedrockService.Client.Management { public sealed class FormManager { - private static readonly IProcessInfo processInfo = new ServiceProcessInfo(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), Process.GetCurrentProcess().Id, false, true); + private static readonly IProcessInfo processInfo = new ServiceProcessInfo(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), Process.GetCurrentProcess().Id, false, true, true); private static readonly IBedrockLogger Logger = new ClientLogger(processInfo); private static MainWindow main; private static TCPClient client; diff --git a/BedrockService/Client/Properties/AssemblyInfo.cs b/BedrockService/Client/Properties/AssemblyInfo.cs index aae11eb6..8e5289cf 100644 --- a/BedrockService/Client/Properties/AssemblyInfo.cs +++ b/BedrockService/Client/Properties/AssemblyInfo.cs @@ -31,5 +31,5 @@ // 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("2.5.5.1")] -[assembly: AssemblyFileVersion("2.5.5.1")] +[assembly: AssemblyVersion("2.5.5.2")] +[assembly: AssemblyFileVersion("2.5.5.2")] From 2e11ff66d8d67aa575bd9cb987663568bb42a82c Mon Sep 17 00:00:00 2001 From: crowbarmaster Date: Sat, 18 Dec 2021 11:44:21 -0500 Subject: [PATCH 57/62] Null check on processInfo, client doesn't use this here and the check is server-side only. Service should never pass a null object since the container handles this check, making this a valid solution. --- BedrockService/BedrockService.Shared/Classes/ServiceInfo.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BedrockService/BedrockService.Shared/Classes/ServiceInfo.cs b/BedrockService/BedrockService.Shared/Classes/ServiceInfo.cs index 9cc24aee..a9a83ad4 100644 --- a/BedrockService/BedrockService.Shared/Classes/ServiceInfo.cs +++ b/BedrockService/BedrockService.Shared/Classes/ServiceInfo.cs @@ -20,7 +20,7 @@ public class ServiceInfo : IServiceConfiguration { public ServiceInfo(IProcessInfo processInfo) { _processInfo = processInfo; - if (processInfo.ShouldStartService()) { + if (processInfo == null || processInfo.ShouldStartService()) { InitializeDefaults(); } } From c859c1dc80eca2eff9111f0bda883d7c643fc707 Mon Sep 17 00:00:00 2001 From: crowbarmaster Date: Sat, 18 Dec 2021 11:45:50 -0500 Subject: [PATCH 58/62] Fixed start positions to center parent on forms. --- BedrockService/Client/Forms/ClientConfigForm.Designer.cs | 9 +++++---- BedrockService/Client/Forms/ClientConfigForm.resx | 9 --------- BedrockService/Client/Forms/ManagePacksForms.Designer.cs | 1 + .../Client/Forms/NewPlayerRegistrationForm.Designer.cs | 1 + 4 files changed, 7 insertions(+), 13 deletions(-) diff --git a/BedrockService/Client/Forms/ClientConfigForm.Designer.cs b/BedrockService/Client/Forms/ClientConfigForm.Designer.cs index 66a746e4..28860705 100644 --- a/BedrockService/Client/Forms/ClientConfigForm.Designer.cs +++ b/BedrockService/Client/Forms/ClientConfigForm.Designer.cs @@ -50,7 +50,7 @@ private void InitializeComponent() this.HostAddress, this.Port}); this.serverGridView.Location = new System.Drawing.Point(9, 22); - this.serverGridView.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2); + this.serverGridView.Margin = new System.Windows.Forms.Padding(2); this.serverGridView.Name = "serverGridView"; this.serverGridView.RowHeadersWidth = 62; this.serverGridView.RowTemplate.Height = 28; @@ -93,7 +93,7 @@ private void InitializeComponent() // this.nbtButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); this.nbtButton.Location = new System.Drawing.Point(12, 253); - this.nbtButton.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2); + this.nbtButton.Margin = new System.Windows.Forms.Padding(2); this.nbtButton.Name = "nbtButton"; this.nbtButton.Size = new System.Drawing.Size(163, 28); this.nbtButton.TabIndex = 2; @@ -105,7 +105,7 @@ private void InitializeComponent() // this.saveBtn.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.saveBtn.Location = new System.Drawing.Point(632, 253); - this.saveBtn.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2); + this.saveBtn.Margin = new System.Windows.Forms.Padding(2); this.saveBtn.Name = "saveBtn"; this.saveBtn.Size = new System.Drawing.Size(163, 28); this.saveBtn.TabIndex = 3; @@ -122,9 +122,10 @@ private void InitializeComponent() this.Controls.Add(this.nbtButton); this.Controls.Add(this.nbtPathLabel); this.Controls.Add(this.serverGridView); - this.Margin = new System.Windows.Forms.Padding(2, 2, 2, 2); + this.Margin = new System.Windows.Forms.Padding(2); this.MinimumSize = new System.Drawing.Size(820, 345); this.Name = "ClientConfigForm"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; this.Text = "Client Configuration"; ((System.ComponentModel.ISupportInitialize)(this.serverGridView)).EndInit(); this.ResumeLayout(false); diff --git a/BedrockService/Client/Forms/ClientConfigForm.resx b/BedrockService/Client/Forms/ClientConfigForm.resx index 731e337c..51332f02 100644 --- a/BedrockService/Client/Forms/ClientConfigForm.resx +++ b/BedrockService/Client/Forms/ClientConfigForm.resx @@ -66,13 +66,4 @@ True - - True - - - True - - - True - \ No newline at end of file diff --git a/BedrockService/Client/Forms/ManagePacksForms.Designer.cs b/BedrockService/Client/Forms/ManagePacksForms.Designer.cs index 2256b867..db310b6f 100644 --- a/BedrockService/Client/Forms/ManagePacksForms.Designer.cs +++ b/BedrockService/Client/Forms/ManagePacksForms.Designer.cs @@ -204,6 +204,7 @@ private void InitializeComponent() this.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); this.MinimumSize = new System.Drawing.Size(940, 444); this.Name = "ManagePacksForms"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; this.Text = "Pack Manager"; ((System.ComponentModel.ISupportInitialize)(this.selectedPackIcon)).EndInit(); this.ResumeLayout(false); diff --git a/BedrockService/Client/Forms/NewPlayerRegistrationForm.Designer.cs b/BedrockService/Client/Forms/NewPlayerRegistrationForm.Designer.cs index 11647a25..1f27ef77 100644 --- a/BedrockService/Client/Forms/NewPlayerRegistrationForm.Designer.cs +++ b/BedrockService/Client/Forms/NewPlayerRegistrationForm.Designer.cs @@ -174,6 +174,7 @@ private void InitializeComponent() this.MinimizeBox = false; this.MinimumSize = new System.Drawing.Size(375, 230); this.Name = "NewPlayerRegistrationForm"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; this.Text = "New Player Registration Form"; this.ResumeLayout(false); this.PerformLayout(); From 891af65e0b6877e38f3d64b0761e727404d6b72b Mon Sep 17 00:00:00 2001 From: crowbarmaster Date: Sat, 18 Dec 2021 11:57:05 -0500 Subject: [PATCH 59/62] Fixed start position of form to center parent. --- BedrockService/Client/Forms/PlayerManagerForm.Designer.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/BedrockService/Client/Forms/PlayerManagerForm.Designer.cs b/BedrockService/Client/Forms/PlayerManagerForm.Designer.cs index bd450113..2d2b0d5f 100644 --- a/BedrockService/Client/Forms/PlayerManagerForm.Designer.cs +++ b/BedrockService/Client/Forms/PlayerManagerForm.Designer.cs @@ -143,6 +143,7 @@ private void InitializeComponent() this.Margin = new System.Windows.Forms.Padding(2); this.MinimumSize = new System.Drawing.Size(980, 440); this.Name = "PlayerManagerForm"; + this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; this.Text = "Player Manager"; ((System.ComponentModel.ISupportInitialize)(this.gridView)).EndInit(); this.ResumeLayout(false); From 1e56d5c16f37074efd9f0ec8b5dc4ccdfca91caa Mon Sep 17 00:00:00 2001 From: crowbarmaster Date: Sat, 18 Dec 2021 12:40:54 -0500 Subject: [PATCH 60/62] Change `Thread.Sleep(time)` to `Task.Delay(time).Wait()`. Give server startup 10 more seconds. Slower disks prove this could be needed, as not everyone will have the best hardware. --- BedrockService/Service/Server/BedrockServer.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/BedrockService/Service/Server/BedrockServer.cs b/BedrockService/Service/Server/BedrockServer.cs index 1541eebd..1ed343bf 100644 --- a/BedrockService/Service/Server/BedrockServer.cs +++ b/BedrockService/Service/Server/BedrockServer.cs @@ -164,19 +164,19 @@ private Task ApplicationWatchdogMonitor() { _logger.AppendLine("Stopping..."); StopControl(); while (_currentServerStatus == ServerStatus.Stopping) { - Thread.Sleep(250); + Task.Delay(500).Wait(); } } if (!MonitoredAppExists(appName) && _currentServerStatus == ServerStatus.Starting) { StartControl(); _logger.AppendLine($"Recieved start signal for server {_serverConfiguration.GetServerName()}."); - Thread.Sleep(15000); + Task.Delay(25000).Wait(); } if (!MonitoredAppExists(appName) && _currentServerStatus == ServerStatus.Started) { StopControl(); _logger.AppendLine($"Started application {appName} was not found in running processes... Resarting {appName}."); StartControl(); - Thread.Sleep(1500); + Task.Delay(1500).Wait(); } if (!MonitoredAppExists(appName) && _currentServerStatus == ServerStatus.Stopped) { _logger.AppendLine("Server stopped successfully."); From c3bd1fe83761021df9e2b6939c64784aa15bf0b0 Mon Sep 17 00:00:00 2001 From: crowbarmaster Date: Sat, 18 Dec 2021 13:07:18 -0500 Subject: [PATCH 61/62] Add names to prop editor form window tasks. --- BedrockService/Client/Forms/MainWindow.cs | 2 +- BedrockService/Client/Forms/PropEditorForm.cs | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/BedrockService/Client/Forms/MainWindow.cs b/BedrockService/Client/Forms/MainWindow.cs index 2137b3b5..ab831456 100644 --- a/BedrockService/Client/Forms/MainWindow.cs +++ b/BedrockService/Client/Forms/MainWindow.cs @@ -501,12 +501,12 @@ private void HostListBox_KeyPress(object sender, KeyPressEventArgs e) { private void BackupManager_Click(object sender, EventArgs e) { using (PropEditorForm editDialog = new PropEditorForm()) { - editDialog.EnableBackupManager(); FormManager.TCPClient.SendData(NetworkMessageSource.Client, NetworkMessageDestination.Service, connectedHost.GetServerIndex(selectedServer), NetworkMessageTypes.EnumBackups); DisableUI(); WaitForServerData().Wait(); FormManager.TCPClient.EnumBackupsArrived = false; editDialog.PopulateBoxes(FormManager.TCPClient.BackupList); + editDialog.EnableBackupManager(); if (editDialog.ShowDialog() == DialogResult.OK) { FormManager.TCPClient.SendData(Encoding.UTF8.GetBytes(editDialog.RollbackFolderName), NetworkMessageSource.Client, NetworkMessageDestination.Service, connectedHost.GetServerIndex(selectedServer), NetworkMessageTypes.BackupRollback); ServerBusy = true; diff --git a/BedrockService/Client/Forms/PropEditorForm.cs b/BedrockService/Client/Forms/PropEditorForm.cs index 917123ed..c0c09b45 100644 --- a/BedrockService/Client/Forms/PropEditorForm.cs +++ b/BedrockService/Client/Forms/PropEditorForm.cs @@ -21,6 +21,7 @@ public PropEditorForm() { public void PopulateBoxes(List propList) { int index = 0; workingProps = propList; + this.Text = "Properties Editor"; foreach (Property prop in workingProps) { dataGrid.Rows.Add(new string[2] { prop.KeyName, prop.Value }); if (prop.KeyName == "server-name" || prop.KeyName == "server-port" || prop.KeyName == "server-portv6") { @@ -33,6 +34,7 @@ public void PopulateBoxes(List propList) { } public void PopulateStartCmds(List list) { + this.Text = "Start Command Editor"; startCmds = list; gridView.Columns.RemoveAt(0); gridView.AllowUserToDeleteRows = true; @@ -42,6 +44,7 @@ public void PopulateStartCmds(List list) { } public void EnableBackupManager() { + this.Text = "Backup Manager"; gridView.MultiSelect = true; DelBackupBtn.Enabled = true; DelBackupBtn.Visible = true; From bee79efa2fe3d0922102e20dcaf7622f3056ea70 Mon Sep 17 00:00:00 2001 From: crowbarmaster Date: Sat, 18 Dec 2021 13:07:46 -0500 Subject: [PATCH 62/62] Update version info to 2.5.6 Beta. --- .../BedrockService.Shared/Properties/AssemblyInfo.cs | 4 ++-- BedrockService/Service/Properties/AssemblyInfo.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/BedrockService/BedrockService.Shared/Properties/AssemblyInfo.cs b/BedrockService/BedrockService.Shared/Properties/AssemblyInfo.cs index 0b64fbcf..33322d86 100644 --- a/BedrockService/BedrockService.Shared/Properties/AssemblyInfo.cs +++ b/BedrockService/BedrockService.Shared/Properties/AssemblyInfo.cs @@ -31,5 +31,5 @@ // 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("2.5.6.0")] +[assembly: AssemblyFileVersion("2.5.6.0")] diff --git a/BedrockService/Service/Properties/AssemblyInfo.cs b/BedrockService/Service/Properties/AssemblyInfo.cs index a7ec8c64..298c0a0c 100644 --- a/BedrockService/Service/Properties/AssemblyInfo.cs +++ b/BedrockService/Service/Properties/AssemblyInfo.cs @@ -30,5 +30,5 @@ // 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("2.5.5.2")] -[assembly: AssemblyFileVersion("2.5.5.2")] +[assembly: AssemblyVersion("2.5.6.0")] +[assembly: AssemblyFileVersion("2.5.6.0")]