diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index f750428a65..8889aa3fd1 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -90,7 +90,11 @@ Instead you can use object orientation, or simply placing repeated code in a fun This means stuff like having a "mode" variable for an object set to "1" or "2" with no clear indicator of what that means. Make these #defines with a name that more clearly states what it's for. This is clearer and enhances readability of your code! Get used to doing it! ### Do not commit modifications to Version.cs -This file will be updated by maintainers when they deem it prudent to release a new version +This file will be updated by maintainers when they deem it prudent to release a new version. For reference here is the version format we use 3.\.\.\ The criteria for changing a version number is as follows + +- Major: A breaking change to the DMAPI +- Minor: Additions or changes to the interface or DMAPI +- Patch: Non-breaking changes internal to each of the 3 modules (Service, Interface, DMAPI) ### Formatting @@ -158,3 +162,7 @@ Just becuase something isn't on this list doesn't mean that it's acceptable. Use Yes, we know that the files have a tonne of mixed Windows and Linux line endings. Attempts to fix this have been met with less than stellar success, and as such we have decided to give up caring until there comes a time when it matters. Therefore, EOF settings of main repo are forbidden territory one must avoid wandering into, at risk of losing body and/or mind to the Git gods. + +## Other Notes + +* Feel free to add your nuget account to TGServiceInterface/Packages.nuspec authors list if you modify the interface \ No newline at end of file diff --git a/README.md b/README.md index 0002660659..615c7ed7ff 100644 --- a/README.md +++ b/README.md @@ -76,6 +76,7 @@ This process is identical to the above steps in command line mode. You can alway 1. [Bind the SSL certificate to the port](https://docs.microsoft.com/en-us/dotnet/framework/wcf/feature-details/how-to-configure-a-port-with-an-ssl-certificate) - e.g. `netsh http add sslcert ipport=0.0.0.0: certhash= appid={F32EDA25-0855-411C-AF5E-F0D042917E2D}` - The `appid` GUID actually doesn't matter, but for sanity, you should use the GUID of TGServerService.exe as printed above + - Power shell users remember to quit the appid: `netsh http add sslcert ipport=0.0.0.0: certhash= appid="{F32EDA25-0855-411C-AF5E-F0D042917E2D}"` as {} has special meaning in powershell 1. Ensure the port can be acccessed from the internet 1. Log in from any computer using a username and password from the service computer in either the CLI or GUI @@ -124,6 +125,9 @@ The service supports updates while running a DreamDaemon instance. Simply instal * `TGS3.json` * This is a copy of TGS3.json from the Repository. If a repostory change creates differences between the two, update operations will be blocked until the user confirms they want to change it +### Codebase integration +To get the TGS3 API for your code base, import the 3 .dm files in the `DMAPI` folder into your include structure, then fill out the configuration as documented in the comments of server_tools.dm. Then, you may want to add a TGS3.json file to specify any static directories and .dlls your codebase uses, along with the optional changelog compile options. + ### Starting the game server: To run the game server, open the `Server` tab of the control panel and click either `Start` diff --git a/TGCommandLine/AdminCommands.cs b/TGCommandLine/AdminCommands.cs index b50ef3201f..9838c8eece 100644 --- a/TGCommandLine/AdminCommands.cs +++ b/TGCommandLine/AdminCommands.cs @@ -18,7 +18,7 @@ namespace TGCommandLine } } - class AdminMoveServerCommand : Command + class AdminMoveServerCommand : ConsoleCommand { public AdminMoveServerCommand() { @@ -43,7 +43,7 @@ namespace TGCommandLine return "Move the server installation (BYOND, Repo, Game) to a new location. Nothing else may be running for this task to complete"; } } - class AdminRecreateStaticCommand : Command + class AdminRecreateStaticCommand : ConsoleCommand { public AdminRecreateStaticCommand() { @@ -63,7 +63,7 @@ namespace TGCommandLine } } - class AdminSetPortCommand : Command + class AdminSetPortCommand : ConsoleCommand { public AdminSetPortCommand() { @@ -98,7 +98,8 @@ namespace TGCommandLine } } - class AdminViewPortCommand : Command { + class AdminViewPortCommand : ConsoleCommand + { public AdminViewPortCommand() { Keyword = "view-port"; @@ -116,7 +117,7 @@ namespace TGCommandLine } } - class AdminViewGroupCommand : Command + class AdminViewGroupCommand : ConsoleCommand { public AdminViewGroupCommand() { @@ -135,7 +136,7 @@ namespace TGCommandLine } } - class AdminSetGroupCommand : Command + class AdminSetGroupCommand : ConsoleCommand { public AdminSetGroupCommand() { @@ -168,7 +169,7 @@ namespace TGCommandLine } } - class AdminClearGroupCommand : Command + class AdminClearGroupCommand : ConsoleCommand { public AdminClearGroupCommand() { diff --git a/TGCommandLine/BYONDCommands.cs b/TGCommandLine/BYONDCommands.cs index 12b0c1a420..875cca63a1 100644 --- a/TGCommandLine/BYONDCommands.cs +++ b/TGCommandLine/BYONDCommands.cs @@ -19,7 +19,7 @@ namespace TGCommandLine } } - class BYONDVersionCommand : Command + class BYONDVersionCommand : ConsoleCommand { public BYONDVersionCommand() { @@ -48,7 +48,7 @@ namespace TGCommandLine } - class BYONDStatusCommand : Command + class BYONDStatusCommand : ConsoleCommand { public BYONDStatusCommand() { @@ -88,7 +88,7 @@ namespace TGCommandLine } } - class BYONDUpdateCommand : Command + class BYONDUpdateCommand : ConsoleCommand { public BYONDUpdateCommand() { diff --git a/TGCommandLine/ChatCommands.cs b/TGCommandLine/ChatCommands.cs index b5b8ee41e4..268fc39d6a 100644 --- a/TGCommandLine/ChatCommands.cs +++ b/TGCommandLine/ChatCommands.cs @@ -29,7 +29,7 @@ namespace TGCommandLine return "Manages the Discord bot"; } } - class IRCNickCommand : Command + class IRCNickCommand : ConsoleCommand { public IRCNickCommand() { @@ -57,7 +57,7 @@ namespace TGCommandLine } } - class ChatJoinCommand : Command + class ChatJoinCommand : ConsoleCommand { readonly int providerIndex; public ChatJoinCommand(ChatProvider pI) @@ -136,7 +136,7 @@ namespace TGCommandLine } } - class ChatPartCommand : Command + class ChatPartCommand : ConsoleCommand { readonly int providerIndex; public ChatPartCommand(ChatProvider pI) @@ -206,7 +206,7 @@ namespace TGCommandLine return ExitCode.Normal; } } - class ChatListAdminsCommand : Command + class ChatListAdminsCommand : ConsoleCommand { readonly int providerIndex; public ChatListAdminsCommand(ChatProvider pI) @@ -265,7 +265,7 @@ namespace TGCommandLine return ExitCode.Normal; } } - class ChatReconnectCommand : Command + class ChatReconnectCommand : ConsoleCommand { readonly ChatProvider providerIndex; public ChatReconnectCommand(ChatProvider pI) @@ -290,7 +290,7 @@ namespace TGCommandLine return ExitCode.Normal; } } - class ChatAddminCommand : Command + class ChatAddminCommand : ConsoleCommand { readonly int providerIndex; public ChatAddminCommand(ChatProvider pI) @@ -337,7 +337,7 @@ namespace TGCommandLine return ExitCode.Normal; } } - class IRCAuthModeCommand : Command + class IRCAuthModeCommand : ConsoleCommand { public IRCAuthModeCommand() { @@ -376,7 +376,7 @@ namespace TGCommandLine return ExitCode.Normal; } } - class DiscordAuthModeCommand : Command + class DiscordAuthModeCommand : ConsoleCommand { public DiscordAuthModeCommand() { @@ -415,7 +415,7 @@ namespace TGCommandLine return ExitCode.Normal; } } - class IRCAuthLevelCommand : Command + class IRCAuthLevelCommand : ConsoleCommand { public IRCAuthLevelCommand() { @@ -463,7 +463,7 @@ namespace TGCommandLine return ExitCode.Normal; } } - class ChatDeadminCommand : Command + class ChatDeadminCommand : ConsoleCommand { readonly int providerIndex; public ChatDeadminCommand(ChatProvider pI) @@ -511,7 +511,7 @@ namespace TGCommandLine } } - class IRCAuthCommand : Command + class IRCAuthCommand : ConsoleCommand { public IRCAuthCommand() { @@ -539,7 +539,7 @@ namespace TGCommandLine } } - class IRCDisableAuthCommand : Command + class IRCDisableAuthCommand : ConsoleCommand { public IRCDisableAuthCommand() { @@ -561,7 +561,7 @@ namespace TGCommandLine } } - class ChatStatusCommand : Command + class ChatStatusCommand : ConsoleCommand { readonly int providerIndex; public ChatStatusCommand(ChatProvider pI) @@ -594,7 +594,7 @@ namespace TGCommandLine return ExitCode.Normal; } } - class ChatEnableCommand : Command + class ChatEnableCommand : ConsoleCommand { readonly int providerIndex; public ChatEnableCommand(ChatProvider pI) @@ -622,7 +622,7 @@ namespace TGCommandLine return ExitCode.Normal; } } - class ChatDisableCommand : Command + class ChatDisableCommand : ConsoleCommand { readonly int providerIndex; public ChatDisableCommand(ChatProvider pI) @@ -651,7 +651,7 @@ namespace TGCommandLine } } - class IRCServerCommand : Command + class IRCServerCommand : ConsoleCommand { public IRCServerCommand() { @@ -696,7 +696,7 @@ namespace TGCommandLine } } - class DiscordSetTokenCommand : Command + class DiscordSetTokenCommand : ConsoleCommand { public DiscordSetTokenCommand() { diff --git a/TGCommandLine/ConfigCommands.cs b/TGCommandLine/ConfigCommands.cs index 16192311e4..3f203877bb 100644 --- a/TGCommandLine/ConfigCommands.cs +++ b/TGCommandLine/ConfigCommands.cs @@ -18,7 +18,7 @@ namespace TGCommandLine return "Manage settings"; } } - class ConfigDeleteCommand : Command + class ConfigDeleteCommand : ConsoleCommand { public ConfigDeleteCommand() { @@ -46,7 +46,7 @@ namespace TGCommandLine } } - class ConfigListCommand : Command + class ConfigListCommand : ConsoleCommand { public ConfigListCommand() { @@ -79,7 +79,7 @@ namespace TGCommandLine } } - class ConfigServerDirectoryCommand : Command + class ConfigServerDirectoryCommand : ConsoleCommand { public ConfigServerDirectoryCommand() { @@ -98,7 +98,7 @@ namespace TGCommandLine } } - class ConfigDownloadCommand : Command + class ConfigDownloadCommand : ConsoleCommand { public ConfigDownloadCommand() { @@ -136,7 +136,7 @@ namespace TGCommandLine } } - class ConfigUploadCommand : Command + class ConfigUploadCommand : ConsoleCommand { public ConfigUploadCommand() { diff --git a/TGCommandLine/ConsoleCommand.cs b/TGCommandLine/ConsoleCommand.cs new file mode 100644 index 0000000000..8584017716 --- /dev/null +++ b/TGCommandLine/ConsoleCommand.cs @@ -0,0 +1,12 @@ +using TGServiceInterface; + +namespace TGCommandLine +{ + abstract class ConsoleCommand : Command + { + /// + /// The currently in use by the + /// + public static Interface Interface; + } +} diff --git a/TGCommandLine/DDCommands.cs b/TGCommandLine/DDCommands.cs index e14f763eae..a0d073b9be 100644 --- a/TGCommandLine/DDCommands.cs +++ b/TGCommandLine/DDCommands.cs @@ -18,7 +18,7 @@ namespace TGCommandLine } } - class DDWorldAnnounceCommand : Command + class DDWorldAnnounceCommand : ConsoleCommand { public DDWorldAnnounceCommand() { @@ -44,7 +44,7 @@ namespace TGCommandLine } } - class DDStartCommand : Command + class DDStartCommand : ConsoleCommand { public DDStartCommand() { @@ -64,7 +64,7 @@ namespace TGCommandLine } } - class DDStopCommand : Command + class DDStopCommand : ConsoleCommand { public DDStopCommand() { @@ -98,7 +98,7 @@ namespace TGCommandLine return res == null ? ExitCode.Normal : ExitCode.ServerError; } } - class DDRestartCommand : Command + class DDRestartCommand : ConsoleCommand { public DDRestartCommand() { @@ -132,7 +132,7 @@ namespace TGCommandLine return "Restarts the server and watchdog optionally waiting for the current round to end"; } } - class DDStatusCommand : Command + class DDStatusCommand : ConsoleCommand { public DDStatusCommand() { @@ -157,7 +157,7 @@ namespace TGCommandLine } } - class DDAutostartCommand : Command + class DDAutostartCommand : ConsoleCommand { public DDAutostartCommand() { @@ -195,7 +195,7 @@ namespace TGCommandLine return "Change or check autostarting of the game server with the service"; } } - class DDWebclientCommand : Command + class DDWebclientCommand : ConsoleCommand { public DDWebclientCommand() { @@ -234,7 +234,7 @@ namespace TGCommandLine } } - class DDPortCommand : Command + class DDPortCommand : ConsoleCommand { public DDPortCommand() { @@ -270,7 +270,7 @@ namespace TGCommandLine } } - class DDSecurityCommand : Command + class DDSecurityCommand : ConsoleCommand { public DDSecurityCommand() { diff --git a/TGCommandLine/DMCommands.cs b/TGCommandLine/DMCommands.cs index 4a61f9e13d..acdd7bf686 100644 --- a/TGCommandLine/DMCommands.cs +++ b/TGCommandLine/DMCommands.cs @@ -19,7 +19,7 @@ namespace TGCommandLine } } - class DMCompileCommand : Command + class DMCompileCommand : ConsoleCommand { public DMCompileCommand() { @@ -75,7 +75,7 @@ namespace TGCommandLine return "Starts a compile/update job optionally waiting for completion"; } } - class DMStatusCommand : Command + class DMStatusCommand : ConsoleCommand { public DMStatusCommand() { @@ -123,7 +123,7 @@ namespace TGCommandLine } } - class DMSetProjectNameCommand : Command + class DMSetProjectNameCommand : ConsoleCommand { public DMSetProjectNameCommand() { @@ -147,7 +147,7 @@ namespace TGCommandLine } } - class DMInitializeCommand : Command + class DMInitializeCommand : ConsoleCommand { public DMInitializeCommand() { @@ -197,7 +197,7 @@ namespace TGCommandLine } } - class DMCancelCommand : Command + class DMCancelCommand : ConsoleCommand { public DMCancelCommand() { diff --git a/TGCommandLine/Program.cs b/TGCommandLine/Program.cs index 1cc910a9cd..20565f33cc 100644 --- a/TGCommandLine/Program.cs +++ b/TGCommandLine/Program.cs @@ -9,6 +9,7 @@ namespace TGCommandLine class Program { + static Interface currentInterface; static Command.ExitCode RunCommandLine(IList argsAsList) { //first lookup the connection string @@ -51,7 +52,7 @@ namespace TGCommandLine } argsAsList.RemoveAt(I); argsAsList.RemoveAt(I); - Interface.SetRemoteLoginInformation(address, port, username, password); + ReplaceInterface(new Interface(address, port, username, password)); break; } } @@ -62,7 +63,7 @@ namespace TGCommandLine return Command.ExitCode.BadCommand; } - var res = Interface.VerifyConnection(); + var res = currentInterface.VerifyConnection(); if (res != null) { Console.WriteLine("Unable to connect to service: " + res); @@ -70,13 +71,13 @@ namespace TGCommandLine return Command.ExitCode.ConnectionError; } - if (!Interface.Authenticate()) + if (!currentInterface.Authenticate()) { Console.WriteLine("Authentication error: Username/password/windows identity is not authorized!"); return Command.ExitCode.ConnectionError; } - if (!SentVMMWarning && Interface.VersionMismatch(out string error)) + if (!SentVMMWarning && currentInterface.VersionMismatch(out string error)) { SentVMMWarning = true; Console.WriteLine(error); @@ -84,7 +85,7 @@ namespace TGCommandLine try { - return new CLICommand().DoRun(argsAsList); + return new CLICommand(currentInterface).DoRun(argsAsList); } catch (Exception e) { @@ -92,6 +93,13 @@ namespace TGCommandLine return Command.ExitCode.ConnectionError; }; } + + static void ReplaceInterface(Interface I) + { + currentInterface = I; + ConsoleCommand.Interface = I; + } + public static string ReadLineSecure() { string result = ""; @@ -138,23 +146,23 @@ namespace TGCommandLine static int Main(string[] args) { + ReplaceInterface(new Interface()); Command.OutputProcVar.Value = Console.WriteLine; if (args.Length != 0) { - Interface.SetBadCertificateHandler((message) => { - foreach (var I in args) - if (I.ToLower() == "--disable-ssl-verification") //im just not even going to document this because i hate it so much + Interface.SetBadCertificateHandler((message) => + { + foreach (var J in args) + if (J.ToLower() == "--disable-ssl-verification") //im just not even going to document this because i hate it so much return true; return false; }); - //allow self signed certs in debug mode return (int)RunCommandLine(new List(args)); } - Interface.SetBadCertificateHandler(BadCertificateInteractive); - - Console.WriteLine("Type 'remote' to connect to a remote service"); //interactive mode + Interface.SetBadCertificateHandler(BadCertificateInteractive); + Console.WriteLine("Type 'remote' to connect to a remote service"); while (true) { Console.Write("Enter command: "); @@ -179,22 +187,22 @@ namespace TGCommandLine var username = Console.ReadLine(); Console.Write("Enter password: "); var password = ReadLineSecure(); - Interface.SetRemoteLoginInformation(address, port, username, password); - var res = Interface.VerifyConnection(); + ReplaceInterface(new Interface(address, port, username, password)); + var res = currentInterface.VerifyConnection(); if (res != null) { Console.WriteLine("Unable to connect: " + res); - Interface.MakeLocalConnection(); + ReplaceInterface(new Interface()); } - else if (!Interface.Authenticate()) + else if (!currentInterface.Authenticate()) { Console.WriteLine("Authentication error: Username/password/windows identity is not authorized! Returning to local mode..."); - Interface.MakeLocalConnection(); + ReplaceInterface(new Interface()); } else { Console.WriteLine("Connected remotely"); - if (Interface.VersionMismatch(out res)) + if (currentInterface.VersionMismatch(out res)) { SentVMMWarning = true; Console.WriteLine(res); @@ -204,7 +212,7 @@ namespace TGCommandLine break; case "disconnect": SentVMMWarning = false; - Interface.MakeLocalConnection(); + ReplaceInterface(new Interface()); Console.WriteLine("Switch to local mode"); break; case "quit": @@ -212,7 +220,7 @@ namespace TGCommandLine return (int)Command.ExitCode.Normal; #if DEBUG case "debug-upgrade": - Interface.GetComponent().PrepareForUpdate(); + currentInterface.GetComponent().PrepareForUpdate(); return (int)Command.ExitCode.Normal; #endif default: diff --git a/TGCommandLine/RepoCommands.cs b/TGCommandLine/RepoCommands.cs index 04d646e8f2..4d6d9e1204 100644 --- a/TGCommandLine/RepoCommands.cs +++ b/TGCommandLine/RepoCommands.cs @@ -18,7 +18,7 @@ namespace TGCommandLine } } - class RepoUpdateJsonCommand : Command + class RepoUpdateJsonCommand : ConsoleCommand { public RepoUpdateJsonCommand() { @@ -42,7 +42,7 @@ namespace TGCommandLine } } - class RepoSetupCommand : Command + class RepoSetupCommand : ConsoleCommand { public RepoSetupCommand() { @@ -70,7 +70,7 @@ namespace TGCommandLine } } - class RepoStatusCommand : Command + class RepoStatusCommand : ConsoleCommand { public RepoStatusCommand() { @@ -123,7 +123,7 @@ namespace TGCommandLine } } - class RepoResetCommand : Command + class RepoResetCommand : ConsoleCommand { public RepoResetCommand() { @@ -145,7 +145,7 @@ namespace TGCommandLine } } - class RepoUpdateCommand : Command + class RepoUpdateCommand : ConsoleCommand { public RepoUpdateCommand() { @@ -180,7 +180,7 @@ namespace TGCommandLine return ""; } } - class RepoGenChangelogCommand : Command + class RepoGenChangelogCommand : ConsoleCommand { public RepoGenChangelogCommand() { @@ -200,7 +200,7 @@ namespace TGCommandLine return "Compiles the html changelog"; } } - class RepoPushChangelogCommand : Command + class RepoPushChangelogCommand : ConsoleCommand { public RepoPushChangelogCommand() { @@ -219,7 +219,7 @@ namespace TGCommandLine return "Pushes the html changelog if the SSH authentication is configured correctly"; } } - class RepoSetEmailCommand : Command + class RepoSetEmailCommand : ConsoleCommand { public RepoSetEmailCommand() { @@ -241,7 +241,7 @@ namespace TGCommandLine return "Set the e-mail used for commits"; } } - class RepoSetNameCommand : Command + class RepoSetNameCommand : ConsoleCommand { public RepoSetNameCommand() { @@ -262,7 +262,7 @@ namespace TGCommandLine return "Set the name used for commits"; } } - class RepoPythonPathCommand : Command + class RepoPythonPathCommand : ConsoleCommand { public RepoPythonPathCommand() { @@ -284,7 +284,7 @@ namespace TGCommandLine } } - class RepoMergePRCommand : Command + class RepoMergePRCommand : ConsoleCommand { public RepoMergePRCommand() { @@ -319,7 +319,7 @@ namespace TGCommandLine } } - class RepoListPRsCommand : Command + class RepoListPRsCommand : ConsoleCommand { public RepoListPRsCommand() { @@ -346,7 +346,7 @@ namespace TGCommandLine } } - class RepoListBackupsCommand : Command + class RepoListBackupsCommand : ConsoleCommand { public RepoListBackupsCommand() { @@ -372,7 +372,7 @@ namespace TGCommandLine return ExitCode.Normal; } } - class RepoCheckoutCommand : Command + class RepoCheckoutCommand : ConsoleCommand { public RepoCheckoutCommand() { diff --git a/TGCommandLine/RootCommands.cs b/TGCommandLine/RootCommands.cs index e78740e7e0..37c6cb00db 100644 --- a/TGCommandLine/RootCommands.cs +++ b/TGCommandLine/RootCommands.cs @@ -7,10 +7,10 @@ namespace TGCommandLine { class CLICommand : RootCommand { - public CLICommand() + public CLICommand(Interface I) { var tmp = new List { new UpdateCommand(), new TestmergeCommand(), new RepoCommand(), new BYONDCommand(), new DMCommand(), new DDCommand(), new ConfigCommand(), new IRCCommand(), new DiscordCommand(), new AutoUpdateCommand(), new SetAutoUpdateCommand() }; - if (Interface.VerifyConnection() == null && Interface.Authenticate() && Interface.AuthenticateAdmin()) + if (I.VerifyConnection() == null && I.Authenticate() && I.AuthenticateAdmin()) tmp.Add(new AdminCommand()); Children = tmp.ToArray(); } @@ -21,7 +21,7 @@ namespace TGCommandLine base.PrintHelp(); } } - class AutoUpdateCommand : Command + class AutoUpdateCommand : ConsoleCommand { public AutoUpdateCommand() { @@ -40,7 +40,7 @@ namespace TGCommandLine return ExitCode.Normal; } } - class SetAutoUpdateCommand : Command + class SetAutoUpdateCommand : ConsoleCommand { public SetAutoUpdateCommand() { @@ -79,7 +79,7 @@ namespace TGCommandLine } } - class UpdateCommand : Command + class UpdateCommand : ConsoleCommand { public UpdateCommand() { @@ -140,7 +140,7 @@ namespace TGCommandLine } } - class TestmergeCommand : Command + class TestmergeCommand : ConsoleCommand { public TestmergeCommand() { diff --git a/TGCommandLine/TGCommandLine.csproj b/TGCommandLine/TGCommandLine.csproj index 5ea566ee82..830f882f28 100644 --- a/TGCommandLine/TGCommandLine.csproj +++ b/TGCommandLine/TGCommandLine.csproj @@ -44,6 +44,7 @@ + diff --git a/TGControlPanel/ByondPage.cs b/TGControlPanel/ByondPage.cs index 7f120c3705..df284edc12 100644 --- a/TGControlPanel/ByondPage.cs +++ b/TGControlPanel/ByondPage.cs @@ -5,7 +5,7 @@ using TGServiceInterface.Components; namespace TGControlPanel { - partial class Main + partial class ControlPanel { string lastReadError = null; void InitBYONDPage() diff --git a/TGControlPanel/ChatPage.cs b/TGControlPanel/ChatPage.cs index 1bf6460e54..bea75efbff 100644 --- a/TGControlPanel/ChatPage.cs +++ b/TGControlPanel/ChatPage.cs @@ -6,7 +6,7 @@ using TGServiceInterface.Components; namespace TGControlPanel { - partial class Main + partial class ControlPanel { bool updatingChat = false; diff --git a/TGControlPanel/Main.Designer.cs b/TGControlPanel/ControlPanel.Designer.cs similarity index 99% rename from TGControlPanel/Main.Designer.cs rename to TGControlPanel/ControlPanel.Designer.cs index 3f787f03be..76beb7fd0a 100644 --- a/TGControlPanel/Main.Designer.cs +++ b/TGControlPanel/ControlPanel.Designer.cs @@ -1,6 +1,6 @@ namespace TGControlPanel { - partial class Main + partial class ControlPanel { /// /// Required designer variable. @@ -15,6 +15,7 @@ { if (disposing && (components != null)) { + Interface.Dispose(); components.Dispose(); } base.Dispose(disposing); @@ -28,7 +29,7 @@ /// private void InitializeComponent() { - System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Main)); + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ControlPanel)); this.RepoBGW = new System.ComponentModel.BackgroundWorker(); this.FullUpdateWorker = new System.ComponentModel.BackgroundWorker(); this.ServerStartBGW = new System.ComponentModel.BackgroundWorker(); diff --git a/TGControlPanel/Main.cs b/TGControlPanel/ControlPanel.cs similarity index 57% rename from TGControlPanel/Main.cs rename to TGControlPanel/ControlPanel.cs index 4c1e9af2e7..a7a8b9fc4c 100644 --- a/TGControlPanel/Main.cs +++ b/TGControlPanel/ControlPanel.cs @@ -1,60 +1,83 @@ -using System; -using System.Drawing; -using System.Windows.Forms; -using TGServiceInterface; - -namespace TGControlPanel -{ - /// - /// The main form - /// - partial class Main : Form - { - /// - /// Create the control panel. Requires the has had it's connection info setup - /// - public Main() - { - InitializeComponent(); - if (Interface.VersionMismatch(out string error) && MessageBox.Show(error, "Warning", MessageBoxButtons.OKCancel) == DialogResult.Cancel) - { - Close(); - return; - } - Panels.SelectedIndexChanged += Panels_SelectedIndexChanged; - Panels.SelectedIndex += Math.Min(Properties.Settings.Default.LastPageIndex, Panels.TabCount - 1); - InitRepoPage(); - InitBYONDPage(); - InitServerPage(); - LoadChatPage(); - InitStaticPage(); - } - - private void Main_Resize(object sender, EventArgs e) - { - Panels.Location = new Point(10, 10); - Panels.Width = ClientSize.Width - 20; - Panels.Height = ClientSize.Height - 20; - } - - private void Panels_SelectedIndexChanged(object sender, EventArgs e) - { - switch (Panels.SelectedIndex) - { - case 0: //repo - PopulateRepoFields(); - break; - case 1: //byond - UpdateBYONDButtons(); - break; - case 2: //scp - LoadServerPage(); - break; - case 3: //chat - LoadChatPage(); - break; - } - Properties.Settings.Default.LastPageIndex = Panels.SelectedIndex; - } - } -} +using System; +using System.Drawing; +using System.Windows.Forms; +using TGServiceInterface; +using TGServiceInterface.Components; + +namespace TGControlPanel +{ + /// + /// The main form + /// + partial class ControlPanel : Form + { + /// + /// The instance for this + /// + readonly Interface Interface; + + /// + /// Constructs a + /// + /// The for the + public ControlPanel(Interface I) + { + Interface = I; + InitializeComponent(); + if (Interface.IsRemoteConnection) + { + var splits = Interface.GetComponent().Version().Split(' '); + Text = String.Format("TGS {0}: {1}:{2}", splits[splits.Length - 1], Interface.HTTPSURL, Interface.HTTPSPort); + } + if (Interface.VersionMismatch(out string error) && MessageBox.Show(error, "Warning", MessageBoxButtons.OKCancel) == DialogResult.Cancel) + { + Close(); + return; + } + Panels.SelectedIndexChanged += Panels_SelectedIndexChanged; + Panels.SelectedIndex += Math.Min(Properties.Settings.Default.LastPageIndex, Panels.TabCount - 1); + InitRepoPage(); + InitBYONDPage(); + InitServerPage(); + LoadChatPage(); + InitStaticPage(); + } + + private void Main_Resize(object sender, EventArgs e) + { + Panels.Location = new Point(10, 10); + Panels.Width = ClientSize.Width - 20; + Panels.Height = ClientSize.Height - 20; + } + + private void Panels_SelectedIndexChanged(object sender, EventArgs e) + { + switch (Panels.SelectedIndex) + { + case 0: //repo + PopulateRepoFields(); + break; + case 1: //byond + UpdateBYONDButtons(); + break; + case 2: //scp + LoadServerPage(); + break; + case 3: //chat + LoadChatPage(); + break; + } + Properties.Settings.Default.LastPageIndex = Panels.SelectedIndex; + } + + bool CheckAdminWithWarning() + { + if (!Interface.AuthenticateAdmin()) + { + MessageBox.Show("Only system administrators may use this command!"); + return false; + } + return true; + } + } +} diff --git a/TGControlPanel/Main.resx b/TGControlPanel/ControlPanel.resx similarity index 100% rename from TGControlPanel/Main.resx rename to TGControlPanel/ControlPanel.resx diff --git a/TGControlPanel/Login.cs b/TGControlPanel/Login.cs index 8c4b28437f..aacb6806ee 100644 --- a/TGControlPanel/Login.cs +++ b/TGControlPanel/Login.cs @@ -31,46 +31,51 @@ namespace TGControlPanel { IPTextBox.Text = IPTextBox.Text.Trim(); UsernameTextBox.Text = UsernameTextBox.Text.Trim(); - Interface.SetRemoteLoginInformation(IPTextBox.Text, (ushort)PortSelector.Value, UsernameTextBox.Text, PasswordTextBox.Text); - var Config = Properties.Settings.Default; - Config.RemoteIP = IPTextBox.Text; - Config.RemoteUsername = UsernameTextBox.Text; - if (SavePasswordCheckBox.Checked) + using (var I = new Interface(IPTextBox.Text, (ushort)PortSelector.Value, UsernameTextBox.Text, PasswordTextBox.Text)) { - Config.RemotePassword = Helpers.EncryptData(PasswordTextBox.Text, out string entrop); - Config.RemoteEntropy = entrop; + var Config = Properties.Settings.Default; + Config.RemoteIP = IPTextBox.Text; + Config.RemotePort = (ushort)PortSelector.Value; + Config.RemoteUsername = UsernameTextBox.Text; + if (SavePasswordCheckBox.Checked) + { + Config.RemotePassword = Helpers.EncryptData(PasswordTextBox.Text, out string entrop); + Config.RemoteEntropy = entrop; + } + else + { + Config.RemotePassword = null; + Config.RemoteEntropy = null; + } + Config.RemoteDefault = true; + VerifyAndConnect(I); } - else - { - Config.RemotePassword = null; - Config.RemoteEntropy = null; - } - Config.RemoteDefault = true; - VerifyAndConnect(); } private void LocalLoginButton_Click(object sender, EventArgs e) { - Interface.MakeLocalConnection(); - Properties.Settings.Default.RemoteDefault = false; - VerifyAndConnect(); + using (var I = new Interface()) + { + Properties.Settings.Default.RemoteDefault = false; + VerifyAndConnect(I); + } } - void VerifyAndConnect() + void VerifyAndConnect(Interface I) { - var res = Interface.VerifyConnection(); + var res = I.VerifyConnection(); if (res != null) { MessageBox.Show("Unable to connect to service! Error: " + res); return; } - if (!Interface.Authenticate()) + if (!I.Authenticate()) { MessageBox.Show("Authentication error: Username/password/windows identity is not authorized! Ensure you are a system administrator or in the correct Windows group on the service machine."); return; } Hide(); - using (var M = new Main()) + using (var M = new ControlPanel(I)) M.ShowDialog(); Close(); } diff --git a/TGControlPanel/Program.cs b/TGControlPanel/Program.cs index b4eec9afc3..1121948ccb 100644 --- a/TGControlPanel/Program.cs +++ b/TGControlPanel/Program.cs @@ -9,7 +9,6 @@ namespace TGControlPanel [STAThread] static void Main(string[] args) { - Interface.SetBadCertificateHandler(BadCertificateHandler); try { if (Properties.Settings.Default.UpgradeRequired) @@ -20,6 +19,7 @@ namespace TGControlPanel } Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); + Interface.SetBadCertificateHandler(BadCertificateHandler); using(var L = new Login()) Application.Run(L); } @@ -44,16 +44,6 @@ namespace TGControlPanel return true; } - public static bool CheckAdminWithWarning() - { - if (!Interface.AuthenticateAdmin()) - { - MessageBox.Show("Only system administrators may use this command!"); - return false; - } - return true; - } - public static void ServiceDisconnectException(Exception e) { MessageBox.Show("An unhandled exception occurred. This usually means we lost connection to the service. Error" + e.ToString()); diff --git a/TGControlPanel/RepoPage.cs b/TGControlPanel/RepoPage.cs index 076933a6b6..e33799692e 100644 --- a/TGControlPanel/RepoPage.cs +++ b/TGControlPanel/RepoPage.cs @@ -7,7 +7,7 @@ using TGServiceInterface.Components; namespace TGControlPanel { - partial class Main + partial class ControlPanel { enum RepoAction { Clone, @@ -238,7 +238,7 @@ namespace TGControlPanel } void DoAsyncOp(RepoAction ra, string message) { - if (ra != RepoAction.Wait && RepoBusyCheck()) + if (RepoBGW.IsBusy || (ra != RepoAction.Wait && RepoBusyCheck())) return; CurrentRevisionLabel.Visible = false; diff --git a/TGControlPanel/ServerPage.cs b/TGControlPanel/ServerPage.cs index 75f7b63ce4..f8b4addcd7 100644 --- a/TGControlPanel/ServerPage.cs +++ b/TGControlPanel/ServerPage.cs @@ -6,7 +6,7 @@ using TGServiceInterface.Components; namespace TGControlPanel { - partial class Main + partial class ControlPanel { enum FullUpdateAction { @@ -83,7 +83,7 @@ namespace TGControlPanel void UpdateServerPath() { - if (!Program.CheckAdminWithWarning()) + if (!CheckAdminWithWarning()) { ServerPathTextbox.Enabled = false; ServerPathTextbox.ReadOnly = true; @@ -95,7 +95,7 @@ namespace TGControlPanel if (DialogResult != DialogResult.Yes) return; - if (!Program.CheckAdminWithWarning()) + if (!CheckAdminWithWarning()) { ServerPathTextbox.Enabled = false; ServerPathTextbox.ReadOnly = true; diff --git a/TGControlPanel/StaticPage.cs b/TGControlPanel/StaticPage.cs index e04eb02c38..61a3ca7155 100644 --- a/TGControlPanel/StaticPage.cs +++ b/TGControlPanel/StaticPage.cs @@ -7,7 +7,7 @@ using TGServiceInterface.Components; namespace TGControlPanel { - partial class Main + partial class ControlPanel { IDictionary IndexesToPaths = new Dictionary(); IList EnumeratedPaths = new List() { "" }; @@ -325,14 +325,14 @@ namespace TGControlPanel private void RecreateStaticButton_Click(object sender, EventArgs e) { - if (!Program.CheckAdminWithWarning()) + if (!CheckAdminWithWarning()) { RecreateStaticButton.Visible = false; return; } if (MessageBox.Show("This will rename the current static directory to a backup and recreate it. Continue?", "Confirm", MessageBoxButtons.YesNo) != DialogResult.Yes) return; - if (!Program.CheckAdminWithWarning()) + if (!CheckAdminWithWarning()) { RecreateStaticButton.Visible = false; return; diff --git a/TGControlPanel/TGControlPanel.csproj b/TGControlPanel/TGControlPanel.csproj index 7caa85d9cd..88da087e32 100644 --- a/TGControlPanel/TGControlPanel.csproj +++ b/TGControlPanel/TGControlPanel.csproj @@ -61,11 +61,11 @@ Login.cs - + Form - - Main.cs + + ControlPanel.cs @@ -87,8 +87,8 @@ Login.cs - - Main.cs + + ControlPanel.cs diff --git a/TGDreamDaemonBridge/DreamDaemonBridge.cs b/TGDreamDaemonBridge/DreamDaemonBridge.cs index 7f95089c7a..f02aa1e3ef 100644 --- a/TGDreamDaemonBridge/DreamDaemonBridge.cs +++ b/TGDreamDaemonBridge/DreamDaemonBridge.cs @@ -22,13 +22,8 @@ namespace TGDreamDaemonBridge { try { - var channel = Interface.CreateChannel(); - try - { - channel.CreateChannel().InteropMessage(String.Join(" ", args)); - } - catch { } - Interface.CloseChannel(channel); + using (var I = new Interface()) + I.GetComponent().InteropMessage(String.Join(" ", args)); } catch { } return 0; diff --git a/TGDreamDaemonBridge/packages.config b/TGDreamDaemonBridge/packages.config index 2cc6157f53..6250dba03c 100644 --- a/TGDreamDaemonBridge/packages.config +++ b/TGDreamDaemonBridge/packages.config @@ -1,4 +1,4 @@  - + \ No newline at end of file diff --git a/TGInstallerWrapper/Main.cs b/TGInstallerWrapper/Main.cs index 436e6bdbf8..4bdaeaff12 100644 --- a/TGInstallerWrapper/Main.cs +++ b/TGInstallerWrapper/Main.cs @@ -20,6 +20,8 @@ namespace TGInstallerWrapper bool cancelled = false; bool pathIsDefault = true; + Interface Interface; + /// /// Construct an installer form /// @@ -67,6 +69,7 @@ namespace TGInstallerWrapper } void CheckForExistingVersion() { + Interface = new Interface(); var verifiedConnection = Interface.VerifyConnection() == null; try { diff --git a/TGServerService/ChatCommands.cs b/TGServerService/ChatCommands.cs deleted file mode 100644 index 51a4014329..0000000000 --- a/TGServerService/ChatCommands.cs +++ /dev/null @@ -1,290 +0,0 @@ -using TGServiceInterface; -using System; -using System.Collections.Generic; -using System.Threading; - -namespace TGServerService.ChatCommands -{ - /// - /// Metadata about the currently running - /// - sealed class CommandInfo - { - /// - /// If the was invoked by an admin - /// - public bool IsAdmin { get; set; } - /// - /// If the was invoked from an admin chat channel - /// - public bool IsAdminChannel { get; set; } - /// - /// The name of the invoker - /// - public string Speaker { get; set; } - /// - /// A reference to the that runs the that heard the - /// - public ServerInstance Server { get; set; } - } - /// - /// A command heard by a - /// - abstract class ChatCommand : Command - { - /// - /// for the - /// - public static ThreadLocal CommandInfo { get; private set; } = new ThreadLocal(); - /// - /// If set to , the cannot be invoked by a non-admin or outside an admin chat channel - /// - public bool RequiresAdmin { get; protected set; } - /// - /// Shorthand for accessing - /// - protected ServerInstance Instance { get { return CommandInfo.Value.Server; } } - - /// - public override ExitCode DoRun(IList parameters) - { - if (RequiresAdmin) - { - var Info = CommandInfo.Value; - if (!Info.IsAdmin) - { - OutputProc("You are not authorized to use that command!"); - return ExitCode.BadCommand; - } - if (!Info.IsAdminChannel) - { - OutputProc("Use this command in an admin channel!"); - return ExitCode.BadCommand; - } - } - return base.DoRun(parameters); - } - } - - /// - /// s generated by DreamDaemon via the API - /// - sealed class ServerChatCommand : ChatCommand - { - /// - /// The help text for the - /// - readonly string HelpText; - - /// - /// Construct a - /// - /// The invocation of the - /// The help text of the - /// If set to , the cannot be invoked by a non-admin or outside an admin chat channel - /// The number of parameters the requires - public ServerChatCommand(string name, string helpText, bool adminOnly, int requiredParameters) - { - Keyword = name; - RequiresAdmin = adminOnly; - HelpText = helpText; - RequiredParameters = requiredParameters; - } - - /// - public override string GetHelpText() - { - return HelpText; - } - - /// - protected override ExitCode Run(IList parameters) - { - var res = Instance.SendCommand(String.Format("{0};sender={1};custom={2}", Keyword, CommandInfo.Value.Speaker, Program.SanitizeTopicString(String.Join(" ", parameters)))); - if (res != "SUCCESS" && !String.IsNullOrWhiteSpace(res)) - OutputProc(res); - return ExitCode.Normal; - } - } - - /// - /// The main root chat command - /// - sealed class RootChatCommand : RootCommand - { - /// - /// Construct a - /// - /// List of s supplied by DreamDaemon - public RootChatCommand(List serverCommands) - { - var tmp = new List { new PRsCommand(), new VersionCommand(), new RevisionCommand(), new ByondCommand(), new KekCommand() }; - if (serverCommands != null) - tmp.AddRange(serverCommands); - Children = tmp.ToArray(); - serverCommands = new List(); - PrintHelpList = true; - } - } - /// - /// Retrieves the git SHA of the live DreamDaemon code - /// - sealed class RevisionCommand : ChatCommand - { - /// - /// Construct a - /// - public RevisionCommand() - { - Keyword = "revision"; - } - /// - protected override ExitCode Run(IList parameters) - { - var res = Instance.LiveSha(); - if (res == "UNKNOWN") { - OutputProc(res); - return ExitCode.ServerError; - } - OutputProc(String.Format("^{0}", res)); - return ExitCode.Normal; - } - - /// - public override string GetHelpText() - { - return "Prints the current code revision of the repository (not the server)"; - } - } - /// - /// Retrieve the installed, staged, or latest availab - /// - sealed class ByondCommand : ChatCommand - { - /// - /// Construct a - /// - public ByondCommand() - { - Keyword = "byond"; - } - - /// - protected override ExitCode Run(IList parameters) - { - var type = ByondVersion.Installed; - if (parameters.Count > 0) - if (parameters[0].ToLower() == "--staged") - type = ByondVersion.Staged; - else if (parameters[0].ToLower() == "--latest") - type = ByondVersion.Latest; - OutputProc(Instance.GetVersion(type) ?? "None"); - return ExitCode.Normal; - } - - /// - public override string GetHelpText() - { - return "Gets the specified BYOND version"; - } - - /// - public override string GetArgumentString() - { - return "[--staged|--latest]"; - } - } - /// - /// Retrieve the current service version - /// - sealed class VersionCommand : ChatCommand - { - /// - /// Construct a - /// - public VersionCommand() - { - Keyword = "version"; - } - - /// - protected override ExitCode Run(IList parameters) - { - OutputProc(Instance.Version()); - return ExitCode.Normal; - } - - /// - public override string GetHelpText() - { - return "Gets the running service version"; - } - } - /// - /// kek - /// - sealed class KekCommand : ChatCommand - { - /// - /// Construct a - /// - public KekCommand() - { - Keyword = "kek"; - } - - /// - protected override ExitCode Run(IList parameters) - { - OutputProc("kek"); - return ExitCode.Normal; - } - - /// - public override string GetHelpText() - { - return "kek"; - } - } - /// - /// Retrieve the list of test-merged github pull requests - /// - sealed class PRsCommand : ChatCommand - { - /// - /// Construct a - /// - public PRsCommand() - { - Keyword = "prs"; - } - - /// - protected override ExitCode Run(IList parameters) - { - var PRs = Instance.MergedPullRequests(out string res); - if (PRs == null) - { - OutputProc(res); - return ExitCode.ServerError; - } - if (PRs.Count == 0) - OutputProc("None!"); - else - { - res = ""; - foreach (var I in PRs) - res += "#" + I.Number + " "; - OutputProc(res); - } - return ExitCode.Normal; - } - - /// - public override string GetHelpText() - { - return "Gets the currently merged pull requests in the repository"; - } - } - -} diff --git a/TGServerService/ChatCommands/ByondCommand.cs b/TGServerService/ChatCommands/ByondCommand.cs new file mode 100644 index 0000000000..27cd19dadf --- /dev/null +++ b/TGServerService/ChatCommands/ByondCommand.cs @@ -0,0 +1,44 @@ +using System.Collections.Generic; +using TGServiceInterface; + +namespace TGServerService.ChatCommands +{ + /// + /// Retrieve the installed, staged, or latest availab + /// + sealed class ByondCommand : ChatCommand + { + /// + /// Construct a + /// + public ByondCommand() + { + Keyword = "byond"; + } + + /// + protected override ExitCode Run(IList parameters) + { + var type = ByondVersion.Installed; + if (parameters.Count > 0) + if (parameters[0].ToLower() == "--staged") + type = ByondVersion.Staged; + else if (parameters[0].ToLower() == "--latest") + type = ByondVersion.Latest; + OutputProc(Instance.GetVersion(type) ?? "None"); + return ExitCode.Normal; + } + + /// + public override string GetHelpText() + { + return "Gets the specified BYOND version"; + } + + /// + public override string GetArgumentString() + { + return "[--staged|--latest]"; + } + } +} diff --git a/TGServerService/ChatCommands/ChatCommand.cs b/TGServerService/ChatCommands/ChatCommand.cs new file mode 100644 index 0000000000..17794613fa --- /dev/null +++ b/TGServerService/ChatCommands/ChatCommand.cs @@ -0,0 +1,45 @@ +using System.Collections.Generic; +using System.Threading; +using TGServiceInterface; + +namespace TGServerService.ChatCommands +{ + /// + /// A command heard by a + /// + abstract class ChatCommand : Command + { + /// + /// for the + /// + public static ThreadLocal CommandInfo { get; private set; } = new ThreadLocal(); + /// + /// If set to , the cannot be invoked by a non-admin or outside an admin chat channel + /// + public bool RequiresAdmin { get; protected set; } + /// + /// Shorthand for accessing + /// + protected ServerInstance Instance { get { return CommandInfo.Value.Server; } } + + /// + public override ExitCode DoRun(IList parameters) + { + if (RequiresAdmin) + { + var Info = CommandInfo.Value; + if (!Info.IsAdmin) + { + OutputProc("You are not authorized to use that command!"); + return ExitCode.BadCommand; + } + if (!Info.IsAdminChannel) + { + OutputProc("Use this command in an admin channel!"); + return ExitCode.BadCommand; + } + } + return base.DoRun(parameters); + } + } +} diff --git a/TGServerService/ChatCommands/CommandInfo.cs b/TGServerService/ChatCommands/CommandInfo.cs new file mode 100644 index 0000000000..913bd50e70 --- /dev/null +++ b/TGServerService/ChatCommands/CommandInfo.cs @@ -0,0 +1,25 @@ +namespace TGServerService.ChatCommands +{ + /// + /// Metadata about the currently running + /// + sealed class CommandInfo + { + /// + /// If the was invoked by an admin + /// + public bool IsAdmin { get; set; } + /// + /// If the was invoked from an admin chat channel + /// + public bool IsAdminChannel { get; set; } + /// + /// The name of the invoker + /// + public string Speaker { get; set; } + /// + /// A reference to the that runs the that heard the + /// + public ServerInstance Server { get; set; } + } +} diff --git a/TGServerService/ChatCommands/KekCommand.cs b/TGServerService/ChatCommands/KekCommand.cs new file mode 100644 index 0000000000..1c2eda51c6 --- /dev/null +++ b/TGServerService/ChatCommands/KekCommand.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; + +namespace TGServerService.ChatCommands +{ + /// + /// kek + /// + sealed class KekCommand : ChatCommand + { + /// + /// Construct a + /// + public KekCommand() + { + Keyword = "kek"; + } + + /// + protected override ExitCode Run(IList parameters) + { + OutputProc("kek"); + return ExitCode.Normal; + } + + /// + public override string GetHelpText() + { + return "kek"; + } + } +} diff --git a/TGServerService/ChatCommands/PullRequestsCommand.cs b/TGServerService/ChatCommands/PullRequestsCommand.cs new file mode 100644 index 0000000000..50495174c5 --- /dev/null +++ b/TGServerService/ChatCommands/PullRequestsCommand.cs @@ -0,0 +1,45 @@ +using System.Collections.Generic; + +namespace TGServerService.ChatCommands +{ + /// + /// Retrieve the list of test-merged github pull requests + /// + sealed class PullRequestsCommand : ChatCommand + { + /// + /// Construct a + /// + public PullRequestsCommand() + { + Keyword = "prs"; + } + + /// + protected override ExitCode Run(IList parameters) + { + var PRs = Instance.MergedPullRequests(out string res); + if (PRs == null) + { + OutputProc(res); + return ExitCode.ServerError; + } + if (PRs.Count == 0) + OutputProc("None!"); + else + { + res = ""; + foreach (var I in PRs) + res += "#" + I.Number + " "; + OutputProc(res); + } + return ExitCode.Normal; + } + + /// + public override string GetHelpText() + { + return "Gets the currently merged pull requests in the repository"; + } + } +} diff --git a/TGServerService/ChatCommands/RevisionCommand.cs b/TGServerService/ChatCommands/RevisionCommand.cs new file mode 100644 index 0000000000..18b28b96ac --- /dev/null +++ b/TGServerService/ChatCommands/RevisionCommand.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; + +namespace TGServerService.ChatCommands +{ + /// + /// Retrieves the git SHA of the live DreamDaemon code + /// + sealed class RevisionCommand : ChatCommand + { + /// + /// Construct a + /// + public RevisionCommand() + { + Keyword = "revision"; + } + /// + protected override ExitCode Run(IList parameters) + { + var res = Instance.LiveSha(); + if (res == "UNKNOWN") + { + OutputProc(res); + return ExitCode.ServerError; + } + OutputProc(String.Format("^{0}", res)); + return ExitCode.Normal; + } + + /// + public override string GetHelpText() + { + return "Prints the current code revision of the repository (not the server)"; + } + } +} diff --git a/TGServerService/ChatCommands/RootChatCommand.cs b/TGServerService/ChatCommands/RootChatCommand.cs new file mode 100644 index 0000000000..82bb3e172e --- /dev/null +++ b/TGServerService/ChatCommands/RootChatCommand.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; +using TGServiceInterface; + +namespace TGServerService.ChatCommands +{ + /// + /// The main root chat command + /// + sealed class RootChatCommand : RootCommand + { + /// + /// Construct a + /// + /// List of s supplied by DreamDaemon + public RootChatCommand(List serverCommands) + { + var tmp = new List { new PullRequestsCommand(), new VersionCommand(), new RevisionCommand(), new ByondCommand(), new KekCommand() }; + if (serverCommands != null) + tmp.AddRange(serverCommands); + Children = tmp.ToArray(); + serverCommands = new List(); + PrintHelpList = true; + } + } +} diff --git a/TGServerService/ChatCommands/ServerChatCommand.cs b/TGServerService/ChatCommands/ServerChatCommand.cs new file mode 100644 index 0000000000..f7551e60af --- /dev/null +++ b/TGServerService/ChatCommands/ServerChatCommand.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; + +namespace TGServerService.ChatCommands +{ + /// + /// s generated by DreamDaemon via the API + /// + sealed class ServerChatCommand : ChatCommand + { + /// + /// The help text for the + /// + readonly string HelpText; + + /// + /// Construct a + /// + /// The invocation of the + /// The help text of the + /// If set to , the cannot be invoked by a non-admin or outside an admin chat channel + /// The number of parameters the requires + public ServerChatCommand(string name, string helpText, bool adminOnly, int requiredParameters) + { + Keyword = name; + RequiresAdmin = adminOnly; + HelpText = helpText; + RequiredParameters = requiredParameters; + } + + /// + public override string GetHelpText() + { + return HelpText; + } + + /// + protected override ExitCode Run(IList parameters) + { + var res = Instance.SendCommand(String.Format("{0};sender={1};custom={2}", Keyword, CommandInfo.Value.Speaker, Program.SanitizeTopicString(String.Join(" ", parameters)))); + if (res != "SUCCESS" && !String.IsNullOrWhiteSpace(res)) + OutputProc(res); + return ExitCode.Normal; + } + } +} diff --git a/TGServerService/ChatCommands/VersionCommand.cs b/TGServerService/ChatCommands/VersionCommand.cs new file mode 100644 index 0000000000..d066d83971 --- /dev/null +++ b/TGServerService/ChatCommands/VersionCommand.cs @@ -0,0 +1,31 @@ +using System.Collections.Generic; + +namespace TGServerService.ChatCommands +{ + /// + /// Retrieve the current service version + /// + sealed class VersionCommand : ChatCommand + { + /// + /// Construct a + /// + public VersionCommand() + { + Keyword = "version"; + } + + /// + protected override ExitCode Run(IList parameters) + { + OutputProc(Instance.Version()); + return ExitCode.Normal; + } + + /// + public override string GetHelpText() + { + return "Gets the running service version"; + } + } +} diff --git a/TGServerService/EventID.cs b/TGServerService/EventID.cs index 114173498d..02824101ab 100644 --- a/TGServerService/EventID.cs +++ b/TGServerService/EventID.cs @@ -276,8 +276,9 @@ namespace TGServerService IRCLogModes = 6500, /// /// Warning: When the Repository submodule handler has to reclone a submodule entirely. This is a long operation and is due to an upstream bug + /// Error: When a submodule update operation fails completely /// - SubmoduleReclone = 6600, + Submodule = 6600, /// /// This event is of type or . It occurs when a user different from the previous one tries and either succeeds or fails to access a . DreamDaemon itself successfully accessing will not trigger this /// diff --git a/TGServerService/LockDependancies.txt b/TGServerService/LockDependancies.txt deleted file mode 100644 index 032a584196..0000000000 --- a/TGServerService/LockDependancies.txt +++ /dev/null @@ -1,12 +0,0 @@ -RepoLock protects the repo for the full duration of short operations -RepoBusy protects the repo for long operations - -lock RepoLock and check RepoBusy to see if you can use the repo - -configLock is only for atomically reading and writing the config directory - -compilerLock protects the compilerCurrentStatus and lastCompilerError vars - -byondLock protects the updateStat and lastError vars - -watchdogLock protects the currentStatus var \ No newline at end of file diff --git a/TGServerService/Program.cs b/TGServerService/Program.cs index 30e08ba944..fea0b5ef0e 100644 --- a/TGServerService/Program.cs +++ b/TGServerService/Program.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; @@ -42,11 +42,8 @@ namespace TGServerService if (excludeRoot != null) for (var I = 0; I < excludeRoot.Count; ++I) excludeRoot[I] = excludeRoot[I].ToLower(); - if (!di.Attributes.HasFlag(FileAttributes.Directory)) - { //this is probably a symlink - Directory.Delete(di.FullName); + if (CheckDeleteSymlinkDir(di)) return; - } NormalizeAndDelete(di, excludeRoot); if (!ContentsOnly) { @@ -56,6 +53,22 @@ namespace TGServerService } } + + /// + /// Properly unlinks directory if it is a symlink + /// + /// for the directory in question + /// if was a symlink and deleted, otherwise + static bool CheckDeleteSymlinkDir(DirectoryInfo di) + { + if (!di.Attributes.HasFlag(FileAttributes.Directory)) + { //this is probably a symlink + Directory.Delete(di.FullName); + return true; + } + return false; + } + /// /// Recursively empty a directory /// @@ -67,6 +80,8 @@ namespace TGServerService { if (excludeRoot != null && excludeRoot.Contains(subDir.Name.ToLower())) continue; + if (CheckDeleteSymlinkDir(subDir)) + continue; NormalizeAndDelete(subDir, null); subDir.Delete(true); } diff --git a/TGServerService/ProjectInstaller.Designer.cs b/TGServerService/ProjectInstaller.Designer.cs deleted file mode 100644 index 30e5f127e4..0000000000 --- a/TGServerService/ProjectInstaller.Designer.cs +++ /dev/null @@ -1,66 +0,0 @@ -namespace TGServerService -{ - partial class ProjectInstaller - { - /// - /// 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 Component Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - this.serviceProcessInstaller1 = new System.ServiceProcess.ServiceProcessInstaller(); - this.serviceInstaller1 = new System.ServiceProcess.ServiceInstaller(); - // - // serviceProcessInstaller1 - // - this.serviceProcessInstaller1.Account = System.ServiceProcess.ServiceAccount.LocalSystem; - this.serviceProcessInstaller1.Password = null; - this.serviceProcessInstaller1.Username = null; - // - // serviceInstaller1 - // - this.serviceInstaller1.Description = "/tg/station Server Service"; - this.serviceInstaller1.DisplayName = "TG Station Server"; - this.serviceInstaller1.ServiceName = "TG Station Server"; - this.serviceInstaller1.StartType = System.ServiceProcess.ServiceStartMode.Automatic; - // - // ProjectInstaller - // - this.Installers.AddRange(new System.Configuration.Install.Installer[] { - this.serviceProcessInstaller1, - this.serviceInstaller1}); - - } - - #endregion - - /// - /// The project's - /// - private System.ServiceProcess.ServiceProcessInstaller serviceProcessInstaller1; - /// - /// The project's - /// - private System.ServiceProcess.ServiceInstaller serviceInstaller1; - } -} \ No newline at end of file diff --git a/TGServerService/ProjectInstaller.cs b/TGServerService/ProjectInstaller.cs index 1852d1797b..2f4d75524d 100644 --- a/TGServerService/ProjectInstaller.cs +++ b/TGServerService/ProjectInstaller.cs @@ -1,20 +1,35 @@ using System.ComponentModel; using System.Configuration.Install; +using System.ServiceProcess; namespace TGServerService { /// - /// This tells the .msi there is a Windows in this that needs installation + /// This tells the .msi there is a Windows in this that needs installation /// [RunInstaller(true)] - partial class ProjectInstaller : Installer + public partial class ProjectInstaller : Installer { /// /// Construct a /// public ProjectInstaller() { - InitializeComponent(); + Installers.AddRange(new Installer[] { + new ServiceProcessInstaller + { + Account = ServiceAccount.LocalSystem, + Password = null, + Username = null + }, + new ServiceInstaller + { + Description = "/tg/station Server Service", + DisplayName = "TG Station Server", + ServiceName = "TG Station Server", + StartType = ServiceStartMode.Automatic + } + }); } } } diff --git a/TGServerService/ProjectInstaller.resx b/TGServerService/ProjectInstaller.resx deleted file mode 100644 index 235f1b0bfb..0000000000 --- a/TGServerService/ProjectInstaller.resx +++ /dev/null @@ -1,129 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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 - - - 17, 56 - - - 196, 17 - - - False - - \ No newline at end of file diff --git a/TGServerService/ServerInstance/Administration.cs b/TGServerService/ServerInstance/Administration.cs index edd69e730c..50e42e7f5f 100644 --- a/TGServerService/ServerInstance/Administration.cs +++ b/TGServerService/ServerInstance/Administration.cs @@ -38,9 +38,14 @@ namespace TGServerService { if (TheDroidsWereLookingFor == null) return "ADMIN"; - - var pc = new PrincipalContext(ContextType.Machine); - return GroupPrincipal.FindByIdentity(pc, IdentityType.Sid, TheDroidsWereLookingFor.Value).Name; + + string res = null; + try + { + res = GroupPrincipal.FindByIdentity(new PrincipalContext(ContextType.Machine), IdentityType.Sid, TheDroidsWereLookingFor.Value).Name; + } + catch { } + return res ?? GroupPrincipal.FindByIdentity(new PrincipalContext(ContextType.Domain), IdentityType.Sid, TheDroidsWereLookingFor.Value).Name; } catch { @@ -66,11 +71,12 @@ namespace TGServerService /// Set based off either an ed name or a string from the config /// /// The name of the group to search for + /// Recursive parameter used to check for the group using instead of /// The name of the group allowed to access the if it could be found, otherwise - string FindTheDroidsWereLookingFor(string search = null) + string FindTheDroidsWereLookingFor(string search = null, bool useDomain = false) { //find the group that is authorized to use the tools - var pc = new PrincipalContext(ContextType.Machine); + var pc = new PrincipalContext(useDomain ? ContextType.Domain : ContextType.Machine); var config = Properties.Settings.Default; var groupName = search ?? config.AuthorizedGroupSID; if (String.IsNullOrWhiteSpace(groupName)) @@ -82,7 +88,7 @@ namespace TGServerService //try again with all types gp = GroupPrincipal.FindByIdentity(pc, search); if (gp == null) - return null; + return useDomain ? null : FindTheDroidsWereLookingFor(search, true); } TheDroidsWereLookingFor = gp.Sid; if (search != null) diff --git a/TGServerService/ServerInstance/Compiler.cs b/TGServerService/ServerInstance/Compiler.cs index ad630f1b3b..9fe9af2e22 100644 --- a/TGServerService/ServerInstance/Compiler.cs +++ b/TGServerService/ServerInstance/Compiler.cs @@ -25,8 +25,6 @@ namespace TGServerService const string StaticDirs = "Static"; const string StaticBackupDir = "Static_BACKUP"; - const string LibMySQLFile = "/libmysql.dll"; - const string GameDir = "Game"; const string GameDirA = GameDir + "/A"; const string GameDirB = GameDir + "/B"; @@ -119,7 +117,7 @@ namespace TGServerService //what is says on the tin CompilerStatus IsInitialized() { - if (File.Exists(GameDirLive + LibMySQLFile)) //its a good tell, jim + if (File.Exists(Path.Combine(GameDirLive, InterfaceDLLName))) //its a good tell, jim return CompilerStatus.Initialized; return CompilerStatus.Uninitialized; } @@ -497,37 +495,21 @@ namespace TGServerService { lock (watchdogLock) { + //gotta go fast + var online = currentStatus == DreamDaemonStatus.Online; + if (online) + Proc.Suspend(); try { - //gotta go fast - var online = currentStatus == DreamDaemonStatus.Online; - if (online) - Proc.Suspend(); - try - { - if (Directory.Exists(GameDirLive)) - //these two lines should be atomic but this is the best we can do - Directory.Delete(GameDirLive); - CreateSymlink(GameDirLive, resurrectee); - } - finally - { - if (online && !Proc.HasExited) - Proc.Resume(); - } + if (Directory.Exists(GameDirLive)) + //these two lines should be atomic but this is the best we can do + Directory.Delete(GameDirLive); + CreateSymlink(GameDirLive, resurrectee); } finally { - if (currentStatus == DreamDaemonStatus.Online) - { - try - { - Proc.PriorityClass = ProcessPriorityClass.Normal; - } - catch { } - Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.Normal; - Thread.CurrentThread.Priority = ThreadPriority.Normal; - } + if (online && !Proc.HasExited) + Proc.Resume(); } } var staged = DaemonStatus() != DreamDaemonStatus.Offline; diff --git a/TGServerService/ServerInstance/DreamDaemon.cs b/TGServerService/ServerInstance/DreamDaemon.cs index ae8e8362d9..a8e7b845d5 100644 --- a/TGServerService/ServerInstance/DreamDaemon.cs +++ b/TGServerService/ServerInstance/DreamDaemon.cs @@ -361,14 +361,20 @@ namespace TGServerService { CurrentDDLog = String.Format("{0} {1} Diagnostics.txt", Now.ToLongDateString(), Now.ToLongTimeString()).Replace(':', '-'); WriteCurrentDDLog("Starting monitoring..."); - pcpu = new PerformanceCounter("Process", "% Processor Time", Proc.ProcessName, true); } + pcpu = new PerformanceCounter("Process", "% Processor Time", Proc.ProcessName, true); MemTrackTimer.Start(); - Proc.WaitForExit(); - lock (watchdogLock) //synchronize + try { - MemTrackTimer.Stop(); - pcpu.Dispose(); + Proc.WaitForExit(); + } + finally + { + lock (watchdogLock) //synchronize + { + MemTrackTimer.Stop(); + pcpu.Dispose(); + } } WriteCurrentDDLog("Crash detected!"); @@ -578,7 +584,7 @@ namespace TGServerService return; //no need } File.Copy(path, targetPath, overwrite); - Service.WriteInfo("Updated interface DLL " + targetPath, TGServerService.EventID.InterfaceDLLUpdated); + Service.WriteInfo("Updated interface DLL", EventID.InterfaceDLLUpdated); } catch { @@ -591,13 +597,13 @@ namespace TGServerService catch (Exception e) { //intentionally using the fi - Service.WriteError(String.Format("Failed to update interface DLL {0}! Error: {1}", targetPath, e.ToString()), TGServerService.EventID.InterfaceDLLUpdateFail); + Service.WriteError(String.Format("Failed to update interface DLL {0}! Error: {1}", targetPath, e.ToString()), EventID.InterfaceDLLUpdateFail); } } } /// - /// Clears the current , calls with a parameter, and attempts to start the DreamDaemon + /// Clears the current , calls with a parameter, and attempts to start the DreamDaemon /// /// If , sets to a new pointing to and starts it /// on success, error message on failure @@ -623,6 +629,7 @@ namespace TGServerService GameAPIVersion = null; //needs updating } Proc.Start(); + Proc.PriorityClass = ProcessPriorityClass.AboveNormal; if (!Proc.WaitForInputIdle(DDHangStartTime * 1000)) { diff --git a/TGServerService/ServerInstance/Interop.cs b/TGServerService/ServerInstance/Interop.cs index ff0d428bca..62bd60b49c 100644 --- a/TGServerService/ServerInstance/Interop.cs +++ b/TGServerService/ServerInstance/Interop.cs @@ -21,8 +21,7 @@ namespace TGServerService string serviceCommsKey; //regenerated every DD restart //range of supported api versions - readonly Version MinAPIVersion = new Version("3.1.0.0"); - readonly Version MaxAPIVersion = new Version("3.1.0.99"); + const int AllowedMajorAPIVersion = 1; Version GameAPIVersion; //See code/modules/server_tools/server_tools.dm for command switch @@ -118,7 +117,7 @@ namespace TGServerService } catch { - Service.WriteWarning(String.Format("API version of the game ({0}) is incompatible with the current supported API versions (Min: {1}. Max: {2}). Interop disabled.", splits.Count > 1 ? splits[1] : "NULL", MinAPIVersion, MaxAPIVersion), EventID.APIVersionMismatch); + Service.WriteWarning(String.Format("API version of the game ({0}) is incompatible with the current supported API versions (3.{2}.x.x). Interop disabled.", splits.Count > 1 ? splits[1] : "NULL", AllowedMajorAPIVersion), EventID.APIVersionMismatch); GameAPIVersion = null; break; } @@ -154,7 +153,9 @@ namespace TGServerService //requires topiclock bool CheckAPIVersionConstraints() { - return !(GameAPIVersion == null || GameAPIVersion < MinAPIVersion || GameAPIVersion > MaxAPIVersion); + //major will never change for all of TGS3 + //we treat minor as major, build as minor, and revision as patch + return !(GameAPIVersion == null || GameAPIVersion.Minor != AllowedMajorAPIVersion); } //Fuckery to diddle byond with the right packet to accept our girth diff --git a/TGServerService/ServerInstance/Repository.cs b/TGServerService/ServerInstance/Repository.cs index 1cd2bc13f6..8f9629c8c0 100644 --- a/TGServerService/ServerInstance/Repository.cs +++ b/TGServerService/ServerInstance/Repository.cs @@ -637,20 +637,27 @@ namespace TGServerService } catch (Exception e) { - //workaround for https://github.com/libgit2/libgit2/issues/3820 - //kill off the modules/ folder in .git and try again try { - Program.DeleteDirectory(String.Format("{0}/.git/modules/{1}", RepoPath, I.Path)); + //workaround for https://github.com/libgit2/libgit2/issues/3820 + //kill off the modules/ folder in .git and try again + try + { + Program.DeleteDirectory(String.Format("{0}/.git/modules/{1}", RepoPath, I.Path)); + } + catch + { + throw e; + } + Repo.Submodules.Update(I.Name, suo); + var msg = String.Format("I had to reclone submodule {0}. If this is happening a lot find a better hack or fix https://github.com/libgit2/libgit2/issues/3820!", I.Name); + SendMessage(String.Format("REPO: {0}", msg), MessageType.DeveloperInfo); + Service.WriteWarning(msg, EventID.Submodule); } - catch + catch (Exception ex) { - throw e; + Service.WriteError(String.Format("Failed to update submodule {0}! Error: {1}", I.Name, ex.ToString()), EventID.Submodule); } - Repo.Submodules.Update(I.Name, suo); - var msg = String.Format("I had to reclone submodule {0}. If this is happening a lot find a better hack or fix https://github.com/libgit2/libgit2/issues/3820!", I.Name); - SendMessage(String.Format("REPO: {0}", msg), MessageType.DeveloperInfo); - Service.WriteWarning(msg, EventID.SubmoduleReclone); } } diff --git a/TGServerService/Service.Designer.cs b/TGServerService/Service.Designer.cs deleted file mode 100644 index 7c2721b451..0000000000 --- a/TGServerService/Service.Designer.cs +++ /dev/null @@ -1,40 +0,0 @@ -namespace TGServerService -{ - sealed partial class Service - { - /// - /// 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 Component Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - // - // Service1 - // - this.ServiceName = "TG Station Server"; - - } - - #endregion - } -} diff --git a/TGServerService/Service.cs b/TGServerService/Service.cs index 58b52c11ab..b42b50dcdf 100644 --- a/TGServerService/Service.cs +++ b/TGServerService/Service.cs @@ -96,7 +96,7 @@ namespace TGServerService Properties.Settings.Default.UpgradeRequired = false; Properties.Settings.Default.Save(); } - InitializeComponent(); + ServiceName = "TG Station Server"; ActiveService = this; Run(this); } diff --git a/TGServerService/Service.resx b/TGServerService/Service.resx deleted file mode 100644 index e5858cc294..0000000000 --- a/TGServerService/Service.resx +++ /dev/null @@ -1,123 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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 - - - False - - \ No newline at end of file diff --git a/TGServerService/TGServerService.csproj b/TGServerService/TGServerService.csproj index bc546d9423..d7e2da4dfe 100644 --- a/TGServerService/TGServerService.csproj +++ b/TGServerService/TGServerService.csproj @@ -82,12 +82,20 @@ + + + + + + + + + - @@ -108,15 +116,9 @@ Component - - ProjectInstaller.cs - Component - - Service.cs - @@ -133,15 +135,6 @@ Settings.Designer.cs - - - ProjectInstaller.cs - - - Service.cs - Designer - - {ac4e7e8b-f83a-481c-a8b0-8fa4e8ae59ab} @@ -149,9 +142,9 @@ - + diff --git a/TGServiceInterface/Interface.cs b/TGServiceInterface/Interface.cs index 21a196edba..b07c8e6e6e 100644 --- a/TGServiceInterface/Interface.cs +++ b/TGServiceInterface/Interface.cs @@ -14,7 +14,7 @@ namespace TGServiceInterface /// /// Main inteface class for the service /// - public class Interface + sealed public class Interface : IDisposable { /// /// List of s that can be used with and @@ -40,27 +40,27 @@ namespace TGServiceInterface /// /// If this is set, we will try and connect to an HTTPS server running at this address /// - static string HTTPSURL; + public readonly string HTTPSURL; /// /// The port used by the service /// - static ushort HTTPSPort = 38607; + public readonly ushort HTTPSPort; /// /// Username for remote operations /// - static string HTTPSUsername; + readonly string HTTPSUsername; /// /// Password for remote operations /// - static string HTTPSPassword; + readonly string HTTPSPassword; /// /// Associated list of open s keyed by type. A in this list may close or fault at any time. Must be locked before being accessed /// - static IDictionary ChannelFactoryCache = new Dictionary(); + IDictionary ChannelFactoryCache = new Dictionary(); /// /// Returns a of s that can be used with the service @@ -103,25 +103,40 @@ namespace TGServiceInterface ErrorMessage = "An unknown error occurred."; break; } - ErrorMessage = String.Format("The certificate failed to verify for {0}:{1}. {2} {3}", HTTPSURL, HTTPSPort, ErrorMessage, cert.ToString()); + ErrorMessage = String.Format("The certificate failed to verify! Error: {2} Cert: {3}", ErrorMessage, cert.ToString()); return handler(ErrorMessage); }; } /// - /// Set the interface to look for services on the current computer + /// Construct an for a local connection /// - public static void MakeLocalConnection() + public Interface() { } + + /// + /// Construct an for a remote connection + /// + /// The address of the remote server + /// The port the remote server runs on + /// Windows account username for the remote server + /// Windows account password for the remote server + public Interface(string address, ushort port, string username, string password) { - HTTPSURL = null; - HTTPSPassword = null; - ClearCachedChannels(); + HTTPSURL = address; + HTTPSPort = port; + HTTPSUsername = username; + HTTPSPassword = password; } + /// + /// Checks if the is setup for a remote connection + /// + public bool IsRemoteConnection { get { return HTTPSURL != null; } } + /// /// Closes all s stored in and clears it /// - static void ClearCachedChannels() + void ClearCachedChannels() { lock (ChannelFactoryCache) { @@ -136,12 +151,12 @@ namespace TGServiceInterface /// /// An error message to display to the user should this function return /// if the interface being used to connect to a service does not have the same release version as the service - public static bool VersionMismatch(out string errorMessage) + public bool VersionMismatch(out string errorMessage) { - var splits = Interface.GetComponent().Version().Split(' '); + var splits = GetComponent().Version().Split(' '); var theirs = new Version(splits[splits.Length - 1].Substring(1)); var ours = new Version(FileVersionInfo.GetVersionInfo(System.Reflection.Assembly.GetExecutingAssembly().Location).FileVersion); - if(theirs != ours) + if(theirs.Major != ours.Major || theirs.Minor != ours.Minor || theirs.Revision != ours.Revision) //don't care about the patch level { errorMessage = String.Format("Version mismatch between interface version ({0}) and service version ({1}). Some functionality may crash this program.", ours, theirs); return true; @@ -150,30 +165,15 @@ namespace TGServiceInterface return false; } - /// - /// Set the remote to connect to along with - /// - /// The address of the remote server - /// The port the remote server runs on - /// Windows account username for the remote server - /// Windows account password for the remote server - public static void SetRemoteLoginInformation(string address, ushort port, string username, string password) - { - HTTPSURL = address; - HTTPSPort = port; - HTTPSUsername = username; - HTTPSPassword = password; - ClearCachedChannels(); - } - /// /// Safely shuts down a single /// /// The to shutdown - public static void CloseChannel(ChannelFactory cf) + static void CloseChannel(ChannelFactory cf) { try { + cf.Closed += ChannelFactory_Closed; cf.Close(); } catch @@ -182,12 +182,22 @@ namespace TGServiceInterface } } + /// + /// Disposes a closed + /// + /// The channel factory that was closed + /// The event arguments + static void ChannelFactory_Closed(object sender, EventArgs e) + { + (sender as IDisposable).Dispose(); + } + /// /// Returns the requested component . This does not guarantee a successful connection. s created this way are recycled for minimum latency and bandwidth usage /// /// The component to retrieve /// The correct component - public static T GetComponent() + public T GetComponent() { var tot = typeof(T); ChannelFactory cf; @@ -219,13 +229,13 @@ namespace TGServiceInterface /// The component of the channel to be created /// The correct /// Thrown if isn't a valid component - public static ChannelFactory CreateChannel() + public ChannelFactory CreateChannel() { var ToT = typeof(T); if (!ValidInterfaces.Contains(ToT)) throw new Exception("Invalid type!"); var InterfaceName = typeof(T).Name; - if (HTTPSURL == null) + if (!IsRemoteConnection) { var res2 = new ChannelFactory( new NetNamedPipeBinding { SendTimeout = new TimeSpan(0, 0, 30), MaxReceivedMessageSize = TransferLimitLocal }, new EndpointAddress(String.Format("net.pipe://localhost/{0}/{1}", MasterInterfaceName, InterfaceName))); //10 megs @@ -257,7 +267,7 @@ namespace TGServiceInterface /// Used to test if the service is avaiable on the machine. Note that state can technically change at any time and any call to the service may throw an exception because it failed /// /// on successful connection, error message on failure - public static string VerifyConnection() + public string VerifyConnection() { try { @@ -274,7 +284,7 @@ namespace TGServiceInterface /// Checks if the supplied user's credentials have permission to use the service. Requires a successful prior call to /// /// if credentials are valid, otherwise - public static bool Authenticate() + public bool Authenticate() { try { @@ -291,7 +301,7 @@ namespace TGServiceInterface /// Checks if the current login can use . Requires a successful prior call to /// /// if the connection may use , otherwise - public static bool AuthenticateAdmin() + public bool AuthenticateAdmin() { try { @@ -303,5 +313,49 @@ namespace TGServiceInterface return false; } } + + #region IDisposable Support + /// + /// To detect redundant calls + /// + private bool disposedValue = false; + + /// + /// Implements the pattern. Calls + /// + /// if was called manually, if it was from the finalizer + void Dispose(bool disposing) + { + if (!disposedValue) + { + if (disposing) + { + ClearCachedChannels(); + } + + // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below. + // TODO: set large fields to null. + + disposedValue = true; + } + } + + // TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources. + // ~Interface() { + // // Do not change this code. Put cleanup code in Dispose(bool disposing) above. + // Dispose(false); + // } + + /// + /// Implements the pattern + /// + public void Dispose() + { + // Do not change this code. Put cleanup code in Dispose(bool disposing) above. + Dispose(true); + // TODO: uncomment the following line if the finalizer is overridden above. + // GC.SuppressFinalize(this); + } + #endregion } } diff --git a/TGServiceInterface/TGServiceInterface.csproj b/TGServiceInterface/TGServiceInterface.csproj index 016005f981..e1832533a1 100644 --- a/TGServiceInterface/TGServiceInterface.csproj +++ b/TGServiceInterface/TGServiceInterface.csproj @@ -67,5 +67,9 @@ + + + + \ No newline at end of file diff --git a/TGServiceInterface/TGServiceInterface.nuspec b/TGServiceInterface/TGServiceInterface.nuspec new file mode 100644 index 0000000000..43fb6d2479 --- /dev/null +++ b/TGServiceInterface/TGServiceInterface.nuspec @@ -0,0 +1,19 @@ + + + + $id$ + $version$ + Cyberboss + https://github.com/tgstation/tgstation-server/blob/master/LICENSE + https://github.com/tgstation/tgstation-server + https://tgstation.github.io/tgstation-server/tgs.ico + false + Interface for managing /tg/station server + Copyright 2017 + https://github.com/tgstation/tgstation-server/releases/tag/tgstation-server-v$version$ + tgstation game-server byond service toolset wcf ss13 client-lib tgstation-server api wcf-service soap + + + + + diff --git a/TGStationServer3.sln b/TGStationServer3.sln index 4127b6d655..e5cf5403d5 100644 --- a/TGStationServer3.sln +++ b/TGStationServer3.sln @@ -91,64 +91,35 @@ EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU - Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU - Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {F32EDA25-0855-411C-AF5E-F0D042917E2D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F32EDA25-0855-411C-AF5E-F0D042917E2D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F32EDA25-0855-411C-AF5E-F0D042917E2D}.Debug|x86.ActiveCfg = Debug|Any CPU - {F32EDA25-0855-411C-AF5E-F0D042917E2D}.Debug|x86.Build.0 = Debug|Any CPU {F32EDA25-0855-411C-AF5E-F0D042917E2D}.Release|Any CPU.ActiveCfg = Release|Any CPU {F32EDA25-0855-411C-AF5E-F0D042917E2D}.Release|Any CPU.Build.0 = Release|Any CPU - {F32EDA25-0855-411C-AF5E-F0D042917E2D}.Release|x86.ActiveCfg = Release|Any CPU - {F32EDA25-0855-411C-AF5E-F0D042917E2D}.Release|x86.Build.0 = Release|Any CPU {394E7643-6B8C-416F-AB18-95AC12648CDC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {394E7643-6B8C-416F-AB18-95AC12648CDC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {394E7643-6B8C-416F-AB18-95AC12648CDC}.Debug|x86.ActiveCfg = Debug|Any CPU - {394E7643-6B8C-416F-AB18-95AC12648CDC}.Debug|x86.Build.0 = Debug|Any CPU {394E7643-6B8C-416F-AB18-95AC12648CDC}.Release|Any CPU.ActiveCfg = Release|Any CPU {394E7643-6B8C-416F-AB18-95AC12648CDC}.Release|Any CPU.Build.0 = Release|Any CPU - {394E7643-6B8C-416F-AB18-95AC12648CDC}.Release|x86.ActiveCfg = Release|Any CPU - {394E7643-6B8C-416F-AB18-95AC12648CDC}.Release|x86.Build.0 = Release|Any CPU {89191F69-B18E-4B59-B72E-E12F9B6811A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {89191F69-B18E-4B59-B72E-E12F9B6811A0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {89191F69-B18E-4B59-B72E-E12F9B6811A0}.Debug|x86.ActiveCfg = Debug|Any CPU - {89191F69-B18E-4B59-B72E-E12F9B6811A0}.Debug|x86.Build.0 = Debug|Any CPU {89191F69-B18E-4B59-B72E-E12F9B6811A0}.Release|Any CPU.ActiveCfg = Release|Any CPU {89191F69-B18E-4B59-B72E-E12F9B6811A0}.Release|Any CPU.Build.0 = Release|Any CPU - {89191F69-B18E-4B59-B72E-E12F9B6811A0}.Release|x86.ActiveCfg = Release|Any CPU - {89191F69-B18E-4B59-B72E-E12F9B6811A0}.Release|x86.Build.0 = Release|Any CPU {AC4E7E8B-F83A-481C-A8B0-8FA4E8AE59AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {AC4E7E8B-F83A-481C-A8B0-8FA4E8AE59AB}.Debug|Any CPU.Build.0 = Debug|Any CPU - {AC4E7E8B-F83A-481C-A8B0-8FA4E8AE59AB}.Debug|x86.ActiveCfg = Debug|Any CPU - {AC4E7E8B-F83A-481C-A8B0-8FA4E8AE59AB}.Debug|x86.Build.0 = Debug|Any CPU {AC4E7E8B-F83A-481C-A8B0-8FA4E8AE59AB}.Release|Any CPU.ActiveCfg = Release|Any CPU {AC4E7E8B-F83A-481C-A8B0-8FA4E8AE59AB}.Release|Any CPU.Build.0 = Release|Any CPU - {AC4E7E8B-F83A-481C-A8B0-8FA4E8AE59AB}.Release|x86.ActiveCfg = Release|Any CPU - {AC4E7E8B-F83A-481C-A8B0-8FA4E8AE59AB}.Release|x86.Build.0 = Release|Any CPU {154435F6-0890-42D4-9AEC-B743D4FBC1CB}.Debug|Any CPU.ActiveCfg = Debug|x86 - {154435F6-0890-42D4-9AEC-B743D4FBC1CB}.Debug|x86.ActiveCfg = Debug|x86 {154435F6-0890-42D4-9AEC-B743D4FBC1CB}.Release|Any CPU.ActiveCfg = Release|x86 {154435F6-0890-42D4-9AEC-B743D4FBC1CB}.Release|Any CPU.Build.0 = Release|x86 - {154435F6-0890-42D4-9AEC-B743D4FBC1CB}.Release|x86.ActiveCfg = Release|x86 - {154435F6-0890-42D4-9AEC-B743D4FBC1CB}.Release|x86.Build.0 = Release|x86 {8956D4C3-BFB9-448E-BF5F-EE7E6F9996F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8956D4C3-BFB9-448E-BF5F-EE7E6F9996F9}.Debug|x86.ActiveCfg = Debug|Any CPU - {8956D4C3-BFB9-448E-BF5F-EE7E6F9996F9}.Debug|x86.Build.0 = Debug|Any CPU {8956D4C3-BFB9-448E-BF5F-EE7E6F9996F9}.Release|Any CPU.ActiveCfg = Release|Any CPU {8956D4C3-BFB9-448E-BF5F-EE7E6F9996F9}.Release|Any CPU.Build.0 = Release|Any CPU - {8956D4C3-BFB9-448E-BF5F-EE7E6F9996F9}.Release|x86.ActiveCfg = Release|Any CPU - {8956D4C3-BFB9-448E-BF5F-EE7E6F9996F9}.Release|x86.Build.0 = Release|Any CPU {9A01EF03-8EAE-45CB-8B87-4A17BD904557}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9A01EF03-8EAE-45CB-8B87-4A17BD904557}.Debug|Any CPU.Build.0 = Debug|Any CPU - {9A01EF03-8EAE-45CB-8B87-4A17BD904557}.Debug|x86.ActiveCfg = Debug|Any CPU - {9A01EF03-8EAE-45CB-8B87-4A17BD904557}.Debug|x86.Build.0 = Debug|Any CPU {9A01EF03-8EAE-45CB-8B87-4A17BD904557}.Release|Any CPU.ActiveCfg = Release|Any CPU {9A01EF03-8EAE-45CB-8B87-4A17BD904557}.Release|Any CPU.Build.0 = Release|Any CPU - {9A01EF03-8EAE-45CB-8B87-4A17BD904557}.Release|x86.ActiveCfg = Release|Any CPU - {9A01EF03-8EAE-45CB-8B87-4A17BD904557}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Version.cs b/Version.cs index 87f3405212..8255119c01 100644 --- a/Version.cs +++ b/Version.cs @@ -12,6 +12,6 @@ using System.Reflection; // [assembly: AssemblyVersion("1.0.*")] //It's impossible to make these a define, don't say I didn't warn you -[assembly: AssemblyVersion("3.1.5.1")] -[assembly: AssemblyFileVersion("3.1.5.1")] -[assembly: AssemblyInformationalVersion("3.1.5.1")] +[assembly: AssemblyVersion("3.1.6.5")] +[assembly: AssemblyFileVersion("3.1.6.5")] +[assembly: AssemblyInformationalVersion("3.1.6.5")] diff --git a/appveyor.yml b/appveyor.yml index 8b30349763..11c3ad0381 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -16,6 +16,8 @@ artifacts: name: TGS3Client - path: MD5-SHA1-Client-v*.txt name: MD5SHA1Client +cache: + - packages -> **\packages.config install: - choco install fciv doxygen.portable graphviz.portable before_build: @@ -24,17 +26,25 @@ build: project: TGStationServer3.sln parallel: true verbosity: minimal + publish_nuget: true after_build: - ps: .\Tools\TGS3Build.ps1 - ps: if($env:APPVEYOR_REPO_COMMIT_MESSAGE -match "\[TGSDeploy\]"){$env:TGSDeploy = "Do it."} - ps: $env:TGSVersion = [System.Diagnostics.FileVersionInfo]::GetVersionInfo("$env:APPVEYOR_BUILD_FOLDER/TGServerService/bin/x86/Release/TGServerService.exe").FileVersion deploy: - release: "tgstation-server-v$(TGSVersion)" - description: 'The /tg/station server suite' - provider: GitHub - auth_token: - secure: lJNGAXwiB5HlWdthz3K4PetqpTG5IEAyRgKaiKxFMQ8HW8CcOjRtB97B05op7BsK - artifact: TGS3Server,MD5SHA1Server,TGS3Client,MD5SHA1Client - draft: false - on: - TGSDeploy: "Do it." + - provider: GitHub + release: "tgstation-server-v$(TGSVersion)" + description: 'The /tg/station server suite' + auth_token: + secure: lJNGAXwiB5HlWdthz3K4PetqpTG5IEAyRgKaiKxFMQ8HW8CcOjRtB97B05op7BsK + artifact: TGS3Server,MD5SHA1Server,TGS3Client,MD5SHA1Client + draft: false + on: + TGSDeploy: "Do it." + - provider: NuGet + api_key: + secure: bedsYuLMqGREzkVkJqRx+BTMgOvDO76tgaNc8sW5E3Ao6iw8oGHdJ/BZov8y0iKa + skip_symbols: true + artifact: /.*\.nupkg/ + on: + TGSDeploy: "Do it."