Merge pull request #554 from Cyberboss/V4

V4 Prototype
This commit is contained in:
Jordan Brown
2018-08-01 18:34:15 -04:00
committed by GitHub
524 changed files with 21880 additions and 34839 deletions
+8
View File
@@ -0,0 +1,8 @@
.dockerignore
.git
.gitignore
.vs
.vscode
packages
*/bin
*/obj
+12 -13
View File
@@ -53,15 +53,10 @@ As mentioned before, you are expected to follow these specifications in order to
### Object Oriented Code
As C# is an object-oriented language, code must be object-oriented when possible in order to be more flexible when adding content to it. If you don't know what "object-oriented" means, we highly recommend you do some light research to grasp the basics.
### Follow the [C# coding guidelines](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/inside-a-program/coding-conventions)
### Tabs, not spaces
You must use tabs to indent your code, NOT SPACES.
With the following amendments for a focus on minimal code
- Tabs, not spaces
- Use `var` whenever possible
- Prefer `using` statements to inline namespace imports
- One line blocks should not have braces where unneccessary (nested `if` statements may circumvent this to avoid confusion)
- Do not use the LINQ query syntax (The functions are acceptable)
(You may use spaces to align something, but you should tab to the block level first, then add the remaining spaces)
### No hacky code
Hacky code, such as adding specific checks, is highly discouraged and only allowed when there is ***no*** other option. (Protip: 'I couldn't immediately think of a proper way so thus there must be no other option' is not gonna cut it here! If you can't think of anything else, say that outright and admit that you need help with it. Maintainers exist for exactly that reason.)
@@ -73,8 +68,8 @@ Copying code from one place to another may be suitable for small, short-time pro
Instead you can use object orientation, or simply placing repeated code in a function, to obey this specification easily.
### No duplicated magic numbers or strings
This means stuff like having a "mode" variable for an object set to "1" or "2" with no clear indicator of what that means. If it's used in more than one place, make these consts 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!
### No magic numbers or strings
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 the version numbers in AssemblyInfo.global.cs
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.\<major\>.\<minor\>.\<patch\> The criteria for changing a version number is as follows
@@ -115,9 +110,13 @@ void Hello()
This prevents nesting levels from getting deeper then they need to be.
### Other Notes
* Code should always be modular
* You are expected to help maintain the code that you add, meaning that if there is a problem then you are likely to be approached in order to fix any bugs.
* Non-prototype code must be unit tested with 100% code coverage
* Code should be modular where possible; if you are working on a new addition, then strongly consider putting it in its own file unless it makes sense to put it with similar ones.
* Bloated code may be necessary to add a certain feature, which means there has to be a judgement over whether the feature is worth having or not. You can help make this decision easier by making sure your code is modular.
* You are expected to help maintain the code that you add, meaning that if there is a problem then you are likely to be approached in order to fix any issues, runtimes, or bugs.
* If you used regex to replace code during development of your code, post the regex in your PR for the benefit of future developers and downstream users.
## Pull Request Process
+15 -14
View File
@@ -1,16 +1,17 @@
#Visual studio stuff
.vscode/*
.vs/*
#VS stuff
*/bin/*
*/obj/*
*/Debug/*
*/Release/*
packages/*
[Oo]bj/
[Bb]in/
.nuget/
_ReSharper.*
packages/
artifacts/
.vs/
*.user
*.dmb
*.int
*.suo
*.userprefs
*DS_Store
*.sln.ide
/TestResults
/src/Tgstation.Server.Host/appsettings.Development.json
/tests/DMAPI/travistester.lk
/tests/DMAPI/travistester.int
/tests/DMAPI/travistester.dmb
+6 -8
View File
@@ -1,14 +1,12 @@
language: csharp
language: generic
sudo: false
git:
depth: 1
env:
global:
- BYOND_MAJOR="511"
- BYOND_MINOR="1385"
- DMEName="Tools/travistester.dme"
matrix:
- BUILD_CLIENT=true
- BUILD_CLIENT=false
- DMEName="tests/DMAPI/travistester.dme"
cache:
directories:
@@ -21,7 +19,7 @@ addons:
- libstdc++6:i386
install:
- if [ "$BUILD_CLIENT" = true ]; then nuget restore; else ./Tools/install_byond.sh; fi
- build/install_byond.sh
script:
- if [ "$BUILD_CLIENT" = true ]; then msbuild /p:Configuration=Release-Client TGStationServer3.sln; else ./Tools/build_byond.sh; fi
- tests/DMAPI/build_byond.sh
-10
View File
@@ -1,10 +0,0 @@
using System.Reflection;
using System.Runtime.CompilerServices;
//You cannot one definition the version number
//Believe me, I've tried, the compiler hates it so much
[assembly: AssemblyVersion("3.2.3.7")]
[assembly: AssemblyFileVersion("3.2.3.7")]
[assembly: AssemblyInformationalVersion("3.2.3.7")]
[assembly: InternalsVisibleTo("TGS.Tests")]
+5 -216
View File
@@ -1,4 +1,4 @@
# Tgstation Toolkit:
# tgstation-server v4:
[![Build status](https://ci.appveyor.com/api/projects/status/7t1h7bvuha0p9j5f/branch/master?svg=true)](https://ci.appveyor.com/project/Cyberboss/tgstation-server-tools/branch/master) [![Build Status](https://travis-ci.org/tgstation/tgstation-server.svg?branch=master)](https://travis-ci.org/tgstation/tgstation-server) [![codecov](https://codecov.io/gh/tgstation/tgstation-server/branch/master/graph/badge.svg)](https://codecov.io/gh/tgstation/tgstation-server) [![Waffle.io - Columns and their card count](https://badge.waffle.io/tgstation/tgstation-server.png?columns=all)](https://waffle.io/tgstation/tgstation-server?utm_source=badge)
@@ -9,223 +9,12 @@
[![forthebadge](http://forthebadge.com/images/badges/built-with-love.svg)](http://forthebadge.com) [![forthebadge](http://forthebadge.com/images/badges/60-percent-of-the-time-works-every-time.svg)](http://forthebadge.com)
This is a toolset to manage a production server of /tg/Station13 (and its forks). It includes the ability to update the server without having to stop or shutdown the server (the update will take effect next round) the ability start the server and restart it if it crashes, as well as systems for fixing errors and merging GitHub Pull Requests locally.
This is a toolset to manage a production BYOND server. It includes the ability to update the server without having to stop or shutdown the server (the update will take effect on a "reboot" of the server) the ability start the server and restart it if it crashes, as well as systems for fixing errors and merging GitHub Pull Requests locally.
Generally, updates force a live tracking of the configured git repo, resetting local modifications. If you plan to make modifications, set up a new git repo to store your version of the code in, and point this script to that in the config (explained below). This can be on github or a local repo using file:/// urls.
Generally, updates force a live tracking of the configured git repo, resetting local modifications. If you plan to make modifications, set up a new git repo to store your version of the code in, and point this script to that in the config (explained below). This can be on GitHub or a local repo using file:/// urls.
Requires python 2.7/3.6 to be installed for changelog generation
### Legacy Server
* The old cmd script server can be found in the legacy tree
## Installing
1. Either compile from source (requires .NET Framework 4.5.2, Nuget, and WiX toolset 4.0) or download the latest [release from github](https://github.com/tgstation/tgstation-server-tools/releases)
1. Unzip setup files
1. Run the installer or use the console server
## Installing (GUI):
1. Launch `TGControlPanel.exe` as an administrator. A shortcut can be found on your desktop
1. Use the `Create Instance` button to create a new server instance
1. Go to the `Repository` Tab and set the remote address and branch of the git you with to track. Note: To grab the tgstation repo, use the following URL: git://github.com/tgstation/tgstation.git
1. Hit the clone button
1. While waiting go to the `BYOND` tab and install the BYOND version you wish
1. You may also configure an IRC and/or discord bot for the server on the chat tab
1. Once the clone is complete you may set up a committer identity, user name, and password on the `Repository` tab for pushing changelog updates
1. Go to the `Server` tab and click the `Initialize Game Folders` button
1. Optionally change the `Project Path` Setting from tgstation to wherever the dme/dmb pair are in your repository
1. Optionally tick the Autostart box if you wish to have your server start with Windows
1. When game folder initialization is complete, click the `Copy from Repo and Compile` option
## Installing (CL example):
This process is identical to the above steps in command line mode. You can always learn more about a command using `?` i.e. `repo ?`
1. Launch TGCommandLine.exe as an administrator (running with no parameters puts you in interactive mode)
1. `service set-python-path C:\Python27` (If Your Python Install is in Program Files, You must encase the path in Quotes, Ex: "C:\Program Files\Python36")
1. `service create-instance "TGS" D:\tgstation`
1. `instance` And enter `TGS`. If you aren't using interactive mode, the following commands must be suffixed with `--instance TGS`
1. `repo setup https://github.com/tgstation/tgstation master`
1. `byond update 511.1385`
1. `irc nick TGS3Test`
1. `irc set-auth-mode channel-mode`
1. `irc set-auth-level %`
1. `irc setup-auth NickServ "id hunter2"` Yes this is the real password, please use it only for testing
1. `irc join botbus dev`
1. `irc join botbus admin`
1. `irc join botbus wd`
1. `irc join botbus game`
1. `irc enable`
1. `discord set-token Rjfa93jlksjfj934jlkjasf8a08wfl.asdjfj08e44` See https://discordapp.com/developers/docs/topics/oauth2#bots
1. `discord set-auth-mode role-id`
1. `discord addmin 192837419273409` See how to get a role id: https://www.reddit.com/r/discordapp/comments/5bezg2/role_id/. Note that if you `discord set-auth-mode user-id` you'll need to use user ids (Enable developer mode, right click user, `Copy ID`)
1. `discord join 12341234453235 dev` This is a channel id (Enable developer mode, right click channel, `Copy ID`)
1. `discord join 34563456344245 dev`
1. `discord join 23452362574456 admin`
1. `discord join 23452362574456 wd`
1. `discord join 53457345736788 game`
1. `discord enable`
1. `repo set-name tgstation-server` These two lines specify the changelog's committer identity. They are not mirrored in the GUI
1. `repo set-email tgstation-server@tgstation13.org`
1. `repo set-credentials` And follow the prompts
1. `dm project-name tgstation`
1. `dd autostart on`
1. `repo status` To check the clone job status
1. `dm initialize --wait`
1. `dd start`
## Setting up Multi-User
1. Create windows accounts for those you wish to have access to the service
1. Join them in a common windows group
1. Run TGCommandLine.exe as an administrator
1. `admin set-group <Name of the group you created> --instance "<instance name>"`
Note: Due to internal functionality, a user who has access to at least one server instance will be able to view the metadata (Name, path, Logging ID, and Enabled status) of all instances.
## Setting up Remote access
1. Obtain an SSL certificate to secure the connection (this is beyond the scope of this guide)
1. Either stick with the default port `38607` or change it with `service set-port <port #>`
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:<port #> certhash=<certificate hash> 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:<port #> certhash=<certificate hash> 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
## Updating
The service supports updates while running a DreamDaemon instance. Simply install an updated version as you normally would and process ownership will transfer smoothly to the new service.
### Folders and Files (None of these should be touched):
* `Game/<A/B>/`
* This will house two copies of the game code, one for updating and one for live. When updating, it will automatically swap them.
* `Static/`
* This contains the `data/` and `config/` folders from the code. They are stored here and a symbolic link is created in the `Game` folders pointing to here.
* Resetting the repository will create a backup of this and reinitalize it
* `Game/Live/`
* This is a symbolic link pointing to current "live" folder.
* When the server is updated, we just point this to the updating folder so that the update takes place next round.
* `Diagnostics`
* This contains various timestamped diagnostic information for DreamDaemon invocations
* `Repository/`
* This contains the actual git repository, all changes in here will be overwritten during update operations.
* `RepoKey/`
* This contains ssh key information for automatic changelog pushing.
* `EventHandlers/`
* This contains batch files that run after certain events, currently only precompile and postcompile events are implemented. If you'd like an event handler you should create a file in this folder with the following name: `{event_name}.bat` for example: if I want an event handler for precompile, I would name it `precompile.bat`.
* `BYOND/`
* This contains the actual BYOND installation the server uses
* `BYOND_staging/`
* This appears when a BYOND update is queued but can't currently be applied due to usage of the current BYOND version. It will be applied at the first possible moment. Restarting the service deletes this folder
* `BYOND_revision.zip`
* This is a queued update downloaded from BYOND, it will be unzipped into BYOND_staging and deleted. Restarting the service deletes this file
* `TGDreamDaemonBridge.dll`
* This is the .dll the TGS3 API `call()()`s into to RPC the server instance that runs it
* Instance -> DreamDaemon communication is achieved via `world/Topic()`
* `prtestjob.json`
* This contains information about current test merged pull requests in the Repository folder
* `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
* `Instance.json`
* The internal configuration settings for a server instance. Note that access to this file bypasses API user restrictions
### 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. Any changes to `TGS3.json` will block compiler operations until a server operator manually approves them.
### Starting the game server:
To run the game server, open the `Server` tab of the control panel and click either `Start`
It will restart the game server if it shutdowns for any reason, giving up after 5 tries in under a minute
### Updating the server:
To update the server, open the `Server` tab of the control panel. Click `Update Server`. (it will git pull, compile, all that jazz). Note that this button won't clear test merges.
(Note: Updating automatically does a code reset, clearing ALL changes to the local git repo, including manual changes (This will not change any data in the `Static/` folder))
Updates do not require the server to be shutdown, changes will apply next round if the server is currently running.
All DM compilation will log to the server what commit they happened at and create a backup tag in the local repository.
### Locally merge GitHub Pull Requests (PR test merge):
This feature currently only works if github is the remote (git server).
Running these will merge the pull request then recompile the server, changes take effect the next round if the server is currently running.
There are two flavors to this.
* The manual method is to use the repo page's merge PR button for fine grain control over PR merging. Each time this button is pressed, the latest commit of the specified PR# will be merged into the repo
* The managed method is the Server page's `Test Merge Manager` which uses the GitHub API to get information about available PRs.
* Open PRs are listed by default
* Currently test merged PRs are checked off and listed at the top when it's opened
* If a PR contains a label that contains the text `test` (case-insensitive) it will be listed on top and marked as `TESTING REQUESTED`
* If a PR has been updated since it was test merged, two entries for it will appear. One listed as `OUTDATED` and specifying the commit it was merged at
* You can change the initial update action of the server with the radio buttons in the bottom left. `Update to Remote` fully resets and updates the server before merging PRs. `Update To Origin` does the same based off the local repository's `origin` remote. `No Update` will merge the PRs without any prior action
* Checking off PRs here and then hitting apply will run the selected update action, optionally generate and push a changelog (see below), merge the PRs, and then compile. `Update Server` will keep any active merged PRs until they are manually removed or merged on the `origin` remote.
* Given that the control panel uses GitHub's API to populate the `Test Merge Manager` you may be prompted for your credentials if you make too many requests. This will create a personal access token with public access on your account specifying its use to bypass the GitHub API rate limit.
You can clear all active test merges using `Reset to Origin Branch` in the `Repository` tab and the using `Copy from Repo and Compile` in the server tab (explained below). You can also use the `Reset All Test Merges` button on the server tab for a concise solution.
### The Compiler
* `Server` -> `Copy from Repo and Compile`
* Copies the local repository code, compiles it, and stages it to apply next round
* `Server` -> `Initialize Game Folders`
* Requires the server not be running, rebuilds the `Game` folder and then does `Copy from Repo and Compile`
* Required on first setup
### Starting everything when the computer/server boots
Just tick the `Autostart` option in the `Server` tab or run `dd autostart on` on the command line. As it's a windows service, it will automatically run without having to log ing
### Enabling the BYOND Webclient
Just tick the `Webclient` option in the `Server` tab or run `dd webclient on` on the command line.
### Static Configuration
* The `Static Files` page of the control panel lets you modify all files in directories you specified in `TGS3.json`
* The files are modified here using the Windows credentials of the active user, feel free to manually set ACLs on them
* You may optionally rebuild the entire `Static` folder from the repository using the `Recreate Static Directory` button. This will copy the original files from the repository based on the current `TGS3.json`. This requires DreamDaemon not be running
### Modifying Your Code
Any `.dm` files included in the root level of the `Static` directory are automatically copied over and included before anything else in your `.dme` for compilation. Use this to configure compile options as you see fit.
### Moving, Renaming, and Detaching instances
* Instances can be renamed, but this requires a temporary offlining of them (this includes interface access, DreamDaemon, and chat bots)
* Detaching an instance simply removes it from the main server's configuration and control, leaving it free for the user to manipulate
* Importing an instance will work as long as
1. No other instance currently in the server has the same name
1. It is not the same path as another instance
Note that importing an instance as a different windows user (this is always different across machines) will result in a loss of chat configuration due to the encryption scheme
### Viewing Server Logs
* Service logs are stored in the Windows event viewer under `Windows Logs` -> `Application`. You'll need to filter this list for `TG Station Server`
* Every event type is keyed with an ID. A complete listing of these IDs and their purpose can be found [here](https://github.com/tgstation/tgstation-server/blob/master/TGS.Server/EventID.cs). Event IDs from different instances are offset by the logging ID the instance was assigned when it was started.
* You can also import the custom view `View TGS3 Logs.xml` in this folder to have them automatically filtered
* Servers running in console mode only use std_out
### Enabling upstream changelog generation
* The repository will automatically create an ssh version of the initial origin remote and can optionally push generated changelogs to your git through it
* The repository can only authenticate using ssh public key authentication
* To enable this feature, simply create `public_key.txt` and `private_key.txt` in a folder called RepoKey in the server directory
* The private key must be in `-----BEGIN RSA PRIVATE KEY-----` format and the public key must be in `ssh-rsa` format. You can generate a keypair like this using the converter in the dropdown menu of PuTTYGen. See github guidelines for setting this up here: https://help.github.com/articles/connecting-to-github-with-ssh/
* The server will be able to read these files regardless of their permissions, so the responsibility is on you to set their ACL's so they can't be read by those that shouldn't
### Synchronized test merge commits
* An instance can push branchless test merge commits to GitHub if the `RepoKey` folder is setup
* This pushes all test merge commits to the remote branch `___TGS3TempBranch` and then deletes it
* This provides public reference information about the test merge commit and time since it will appear in the PR in questing
* To enable this, check the `Sync Commits` button on the `Repository` page of the control panel or run `repo push-testmerges on --instance "<instance name>"` from TGCommandLine
### Legacy Servers
* Versions 3 and 4 can be found in the `legacy/` directory
## CONTRIBUTING
-114
View File
@@ -1,114 +0,0 @@
using System.Collections.Generic;
using TGS.Interface;
namespace TGS.CommandLine
{
sealed class AdminCommand : RootCommand
{
public AdminCommand()
{
Keyword = "admin";
Children = new Command[] { new AdminViewGroupCommand(), new AdminSetGroupCommand(), new AdminClearGroupCommand(), new AdminRecreateStaticCommand() };
}
public override string GetHelpText()
{
return "Manage instance authentication";
}
}
class AdminRecreateStaticCommand : ConsoleCommand
{
public AdminRecreateStaticCommand()
{
Keyword = "recreate-static-directory";
}
protected override ExitCode Run(IList<string> parameters)
{
var res = Instance.Administration.RecreateStaticFolder();
OutputProc(res ?? "Success");
return res == null ? ExitCode.Normal : ExitCode.ServerError;
}
public override string GetHelpText()
{
return "Backup the current static directory and repopulate it from the TGS3.json in the repository";
}
}
class AdminViewGroupCommand : ConsoleCommand
{
public AdminViewGroupCommand()
{
Keyword = "view-group";
}
public override string GetHelpText()
{
return "Print the name of the windows group that is allowed to use the service";
}
protected override ExitCode Run(IList<string> parameters)
{
var group = Instance.Administration.GetCurrentAuthorizedGroup();
OutputProc(group ?? "ERROR");
return group != null ? ExitCode.Normal : ExitCode.ServerError;
}
}
class AdminSetGroupCommand : ConsoleCommand
{
public AdminSetGroupCommand()
{
Keyword = "set-group";
RequiredParameters = 1;
}
public override string GetHelpText()
{
return "Set the windows group allowed to use the service";
}
public override string GetArgumentString()
{
return "<windows group name>";
}
protected override ExitCode Run(IList<string> parameters)
{
var result = Instance.Administration.SetAuthorizedGroup(parameters[0]);
if(result != null)
{
OutputProc("Group set to: " + result);
return ExitCode.Normal;
}
else
{
OutputProc("Failed to find a group named: " + parameters[0]);
return ExitCode.ServerError;
}
}
}
class AdminClearGroupCommand : ConsoleCommand
{
public AdminClearGroupCommand()
{
Keyword = "clear-group";
}
public override string GetHelpText()
{
return "Clears the groups allowed to use the service, leaving only windows administrators";
}
protected override ExitCode Run(IList<string> parameters)
{
var res = Instance.Administration.SetAuthorizedGroup(null);
if(res != "ADMIN")
{
OutputProc("Failed to clear the group??? We are currently set to: " + res);
return ExitCode.ServerError;
}
return ExitCode.Normal;
}
}
}
-6
View File
@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
</startup>
</configuration>
-138
View File
@@ -1,138 +0,0 @@
using System;
using System.Collections.Generic;
using System.Threading;
using TGS.Interface;
namespace TGS.CommandLine
{
class BYONDCommand : RootCommand
{
public BYONDCommand()
{
Keyword = "byond";
Children = new Command[] { new BYONDUpdateCommand(), new BYONDVersionCommand(), new BYONDStatusCommand() };
}
public override string GetHelpText()
{
return "Manage BYOND installation";
}
}
class BYONDVersionCommand : ConsoleCommand
{
public BYONDVersionCommand()
{
Keyword = "version";
}
protected override ExitCode Run(IList<string> 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.Byond.GetVersion(type) ?? "Unistalled");
return ExitCode.Normal;
}
public override string GetArgumentString()
{
return "[--staged|--latest]";
}
public override string GetHelpText()
{
return "Print the currently installed BYOND version";
}
}
class BYONDStatusCommand : ConsoleCommand
{
public BYONDStatusCommand()
{
Keyword = "status";
}
protected override ExitCode Run(IList<string> parameters)
{
switch (Instance.Byond.CurrentStatus())
{
case ByondStatus.Downloading:
OutputProc("Downloading update...");
break;
case ByondStatus.Idle:
OutputProc("Updater Idle");
break;
case ByondStatus.Staged:
OutputProc("Update staged and awaiting server restart");
break;
case ByondStatus.Staging:
OutputProc("Staging update...");
break;
case ByondStatus.Starting:
OutputProc("Starting update...");
break;
case ByondStatus.Updating:
OutputProc("Applying update...");
break;
default:
OutputProc("Limmexing (This is an error).");
return ExitCode.ServerError;
}
return ExitCode.Normal;
}
public override string GetHelpText()
{
return "Print the current status of the BYOND updater";
}
}
class BYONDUpdateCommand : ConsoleCommand
{
public BYONDUpdateCommand()
{
Keyword = "update";
RequiredParameters = 2;
}
protected override ExitCode Run(IList<string> parameters)
{
int Major = 0, Minor = 0;
try
{
Major = Convert.ToInt32(parameters[0]);
Minor = Convert.ToInt32(parameters[1]);
}
catch
{
OutputProc("Please enter version as <Major>.<Minor>");
return ExitCode.BadCommand;
}
var BYOND = Instance.Byond;
if (!BYOND.UpdateToVersion(Major, Minor))
{
OutputProc("Failed to begin update!");
return ExitCode.ServerError;
}
var stat = BYOND.CurrentStatus();
while (stat != ByondStatus.Idle && stat != ByondStatus.Staged)
{
Thread.Sleep(100);
stat = BYOND.CurrentStatus();
}
var res = BYOND.GetError();
OutputProc(res ?? (stat == ByondStatus.Staged ? "Update staged and will apply next DD reboot" : "Update finished"));
return res == null ? ExitCode.Normal : ExitCode.ServerError;
}
public override string GetArgumentString()
{
return "<Major> <Minor>";
}
public override string GetHelpText()
{
return "Updates the BYOND installation to the specified version";
}
}
}
-721
View File
@@ -1,721 +0,0 @@
using System;
using System.Collections.Generic;
using TGS.Interface;
namespace TGS.CommandLine
{
class IRCCommand : RootCommand
{
public IRCCommand()
{
Keyword = "irc";
Children = new Command[] { new IRCNickCommand(), new IRCAuthCommand(), new IRCDisableAuthCommand(), new IRCServerCommand(), new ChatJoinCommand(ChatProvider.IRC), new ChatPartCommand(ChatProvider.IRC), new ChatListAdminsCommand(ChatProvider.IRC), new ChatReconnectCommand(ChatProvider.IRC), new ChatAddminCommand(ChatProvider.IRC), new ChatDeadminCommand(ChatProvider.IRC), new ChatEnableCommand(ChatProvider.IRC), new ChatDisableCommand(ChatProvider.IRC), new ChatStatusCommand(ChatProvider.IRC), new IRCAuthModeCommand(), new IRCAuthLevelCommand() };
}
public override string GetHelpText()
{
return "Manages the IRC bot";
}
}
class DiscordCommand : RootCommand
{
public DiscordCommand()
{
Keyword = "discord";
Children = new Command[] { new DiscordSetTokenCommand(), new ChatJoinCommand(ChatProvider.Discord), new ChatPartCommand(ChatProvider.Discord), new ChatListAdminsCommand(ChatProvider.Discord), new ChatReconnectCommand(ChatProvider.Discord), new ChatAddminCommand(ChatProvider.Discord), new ChatDeadminCommand(ChatProvider.Discord), new ChatEnableCommand(ChatProvider.Discord), new ChatDisableCommand(ChatProvider.Discord), new ChatStatusCommand(ChatProvider.Discord) , new DiscordAuthModeCommand() };
}
public override string GetHelpText()
{
return "Manages the Discord bot";
}
}
class IRCNickCommand : ConsoleCommand
{
public IRCNickCommand()
{
Keyword = "nick";
RequiredParameters = 1;
}
public override string GetArgumentString()
{
return "<name>";
}
public override string GetHelpText()
{
return "Sets the IRC nickname";
}
protected override ExitCode Run(IList<string> parameters)
{
var Chat = Instance.Chat;
Chat.SetProviderInfo(new IRCSetupInfo(Chat.ProviderInfos()[(int)ChatProvider.IRC])
{
Nickname = parameters[0],
});
return ExitCode.Normal;
}
}
class ChatJoinCommand : ConsoleCommand
{
readonly int providerIndex;
public ChatJoinCommand(ChatProvider pI)
{
Keyword = "join";
RequiredParameters = 2;
providerIndex = (int)pI;
}
public override string GetArgumentString()
{
return "<channel> <dev|wd|game|admin>";
}
public override string GetHelpText()
{
return "Joins a channel for listening and broadcasting of the specified message type (Developer, Watchdog, Game, Admin)";
}
protected override ExitCode Run(IList<string> parameters)
{
var IRC = Instance.Chat;
var info = IRC.ProviderInfos()[providerIndex];
List<string> channels;
switch (parameters[1].ToLower())
{
case "dev":
channels = info.DevChannels;
break;
case "wd":
channels = info.WatchdogChannels;
break;
case "game":
channels = info.GameChannels;
break;
case "admin":
channels = info.AdminChannels;
break;
default:
OutputProc("Invalid parameter: " + parameters[1]);
return ExitCode.BadCommand;
}
var lowerParam = parameters[0].ToLower();
foreach (var I in channels)
{
if (I.ToLower() == lowerParam)
{
OutputProc("Already in this channel!");
return ExitCode.BadCommand;
}
}
channels.Add(parameters[0]);
switch (parameters[1].ToLower())
{
case "dev":
info.DevChannels = channels;
break;
case "wd":
info.WatchdogChannels = channels;
break;
case "game":
info.GameChannels = channels;
break;
case "admin":
info.AdminChannels = channels;
break;
}
var res = IRC.SetProviderInfo(info);
if(res != null)
{
OutputProc(res);
return ExitCode.ServerError;
}
return ExitCode.Normal;
}
}
class ChatPartCommand : ConsoleCommand
{
readonly int providerIndex;
public ChatPartCommand(ChatProvider pI)
{
Keyword = "part";
RequiredParameters = 2;
providerIndex = (int)pI;
}
public override string GetArgumentString()
{
return "<channel> <dev|wd|game|admin>";
}
public override string GetHelpText()
{
return "Stops listening and broadcasting on a channel for the specified message type (Developer, Watchdog, Game, Admin)";
}
protected override ExitCode Run(IList<string> parameters)
{
var IRC = Instance.Chat;
var info = IRC.ProviderInfos()[providerIndex];
List<string> channels;
switch (parameters[1].ToLower())
{
case "dev":
channels = info.DevChannels;
break;
case "wd":
channels = info.WatchdogChannels;
break;
case "game":
channels = info.GameChannels;
break;
case "admin":
channels = info.AdminChannels;
break;
default:
OutputProc("Invalid parameter: " + parameters[1]);
return ExitCode.BadCommand;
}
var lowerParam = parameters[0].ToLower();
if ((ChatProvider)providerIndex == ChatProvider.IRC && lowerParam[0] != '#')
lowerParam = "#" + lowerParam;
channels.Remove(lowerParam);
switch (parameters[1].ToLower())
{
case "dev":
info.DevChannels = channels;
break;
case "wd":
info.WatchdogChannels = channels;
break;
case "game":
info.GameChannels = channels;
break;
case "admin":
info.AdminChannels = channels;
break;
}
var res = IRC.SetProviderInfo(info);
if (res != null)
{
OutputProc(res);
return ExitCode.ServerError;
}
return ExitCode.Normal;
}
}
class ChatListAdminsCommand : ConsoleCommand
{
readonly int providerIndex;
public ChatListAdminsCommand(ChatProvider pI)
{
Keyword = "list-admins";
providerIndex = (int)pI;
}
public override string GetHelpText()
{
return "List users which can use restricted commands in the admin channel";
}
protected override ExitCode Run(IList<string> parameters)
{
var info = Instance.Chat.ProviderInfos()[providerIndex];
string authType;
switch ((ChatProvider)providerIndex)
{
case ChatProvider.IRC:
if (info.AdminsAreSpecial)
authType = "Mode:";
else
authType = "Nicknames:";
break;
case ChatProvider.Discord:
if (info.AdminsAreSpecial)
authType = "Role IDs:";
else
authType = "User IDs:";
break;
default:
OutputProc(String.Format("Invalid provider: {0}!", providerIndex));
return ExitCode.ServerError;
}
OutputProc("Authorized " + authType);
if (info.AdminsAreSpecial && (ChatProvider)providerIndex == ChatProvider.IRC)
switch(new IRCSetupInfo(info).AuthLevel)
{
case IRCMode.Voice:
OutputProc("+");
break;
case IRCMode.Halfop:
OutputProc("%");
break;
case IRCMode.Op:
OutputProc("@");
break;
case IRCMode.Owner:
OutputProc("~");
break;
}
else
foreach (var I in info.AdminList)
OutputProc(I);
return ExitCode.Normal;
}
}
class ChatReconnectCommand : ConsoleCommand
{
readonly ChatProvider providerIndex;
public ChatReconnectCommand(ChatProvider pI)
{
Keyword = "reconnect";
providerIndex = pI;
}
public override string GetHelpText()
{
return "Restablish the chat connection";
}
protected override ExitCode Run(IList<string> parameters)
{
var res = Instance.Chat.Reconnect(providerIndex);
if (res != null)
{
OutputProc("Error: " + res);
return ExitCode.ServerError;
}
return ExitCode.Normal;
}
}
class ChatAddminCommand : ConsoleCommand
{
readonly int providerIndex;
public ChatAddminCommand(ChatProvider pI)
{
Keyword = "addmin";
RequiredParameters = 1;
providerIndex = (int)pI;
}
public override string GetArgumentString()
{
return "[nick]";
}
public override string GetHelpText()
{
return "Add a user which can use restricted commands in the admin channels";
}
protected override ExitCode Run(IList<string> parameters)
{
var IRC = Instance.Chat;
var info = IRC.ProviderInfos()[providerIndex];
var newmin = parameters[0].ToLower();
if (info.AdminsAreSpecial && (ChatProvider)providerIndex == ChatProvider.IRC)
{
OutputProc("Invalid auth mode for this command!");
return ExitCode.BadCommand;
}
if (info.AdminList.Contains(newmin))
{
OutputProc(parameters[0] + " is already an admin!");
return ExitCode.BadCommand;
}
var al = info.AdminList;
al.Add(newmin);
info.AdminList = al;
var res = IRC.SetProviderInfo(info);
if (res != null)
{
OutputProc(res);
return ExitCode.ServerError;
}
return ExitCode.Normal;
}
}
class IRCAuthModeCommand : ConsoleCommand
{
public IRCAuthModeCommand()
{
Keyword = "set-auth-mode";
RequiredParameters = 1;
}
public override string GetArgumentString()
{
return "<channel-mode|nickname>";
}
public override string GetHelpText()
{
return "Switch between admin command authorization via user channel mode or nicknames";
}
protected override ExitCode Run(IList<string> parameters)
{
var IRC = Instance.Chat;
var info = IRC.ProviderInfos()[(int)ChatProvider.IRC];
var lowerparam = parameters[0].ToLower();
if (lowerparam == "channel-mode")
info.AdminsAreSpecial = true;
else if (lowerparam == "nickname")
info.AdminsAreSpecial = false;
else
{
OutputProc("Invalid parameter: " + parameters[0]);
return ExitCode.BadCommand;
}
var res = IRC.SetProviderInfo(info);
if (res != null)
{
OutputProc(res);
return ExitCode.ServerError;
}
return ExitCode.Normal;
}
}
class DiscordAuthModeCommand : ConsoleCommand
{
public DiscordAuthModeCommand()
{
Keyword = "set-auth-mode";
RequiredParameters = 1;
}
public override string GetArgumentString()
{
return "<role-id|user-id>";
}
public override string GetHelpText()
{
return "Switch between admin command authorization via user roles or individual users";
}
protected override ExitCode Run(IList<string> parameters)
{
var IRC = Instance.Chat;
var info = IRC.ProviderInfos()[(int)ChatProvider.Discord];
var lowerparam = parameters[0].ToLower();
if (lowerparam == "role-id")
info.AdminsAreSpecial = true;
else if (lowerparam == "user-id")
info.AdminsAreSpecial = false;
else
{
OutputProc("Invalid parameter: " + parameters[0]);
return ExitCode.BadCommand;
}
var res = IRC.SetProviderInfo(info);
if (res != null)
{
OutputProc(res);
return ExitCode.ServerError;
}
return ExitCode.Normal;
}
}
class IRCAuthLevelCommand : ConsoleCommand
{
public IRCAuthLevelCommand()
{
Keyword = "set-auth-level";
RequiredParameters = 1;
}
public override string GetArgumentString()
{
return "<+|%|@|~>";
}
public override string GetHelpText()
{
return "Set the required channel mode for users to use admin commands";
}
protected override ExitCode Run(IList<string> parameters)
{
var IRC = Instance.Chat;
var info = new IRCSetupInfo(IRC.ProviderInfos()[(int)ChatProvider.IRC]);
switch (parameters[0])
{
case "+":
info.AuthLevel = IRCMode.Voice;
break;
case "%":
info.AuthLevel = IRCMode.Halfop;
break;
case "@":
info.AuthLevel = IRCMode.Op;
break;
case "~":
info.AuthLevel = IRCMode.Owner;
break;
default:
OutputProc("Invalid parameter: " + parameters[0]);
return ExitCode.BadCommand;
}
var res = IRC.SetProviderInfo(info);
if (res != null)
{
OutputProc(res);
return ExitCode.ServerError;
}
return ExitCode.Normal;
}
}
class ChatDeadminCommand : ConsoleCommand
{
readonly int providerIndex;
public ChatDeadminCommand(ChatProvider pI)
{
Keyword = "deadmin";
RequiredParameters = 1;
providerIndex = (int)pI;
}
public override string GetArgumentString()
{
return "<nick>";
}
public override string GetHelpText()
{
return "Remove a user which can use restricted commands in the admin channels";
}
protected override ExitCode Run(IList<string> parameters)
{
var IRC = Instance.Chat;
var info = IRC.ProviderInfos()[providerIndex];
var newmin = parameters[0].ToLower();
if (info.AdminsAreSpecial && (ChatProvider)providerIndex == ChatProvider.IRC)
{
OutputProc("Invalid auth mode for this command!");
return ExitCode.BadCommand;
}
if (!info.AdminList.Contains(newmin))
{
OutputProc(parameters[0] + " is not an admin!");
return ExitCode.BadCommand;
}
var al = info.AdminList;
al.Remove(newmin);
info.AdminList = al;
var res = IRC.SetProviderInfo(info);
if (res != null)
{
OutputProc(res);
return ExitCode.ServerError;
}
return ExitCode.Normal;
}
}
class IRCAuthCommand : ConsoleCommand
{
public IRCAuthCommand()
{
Keyword = "setup-auth";
RequiredParameters = 2;
}
public override string GetArgumentString()
{
return "<target> <message>";
}
public override string GetHelpText()
{
return "Set the authentication message to send to target for identification. e.g. NickServ \"identify hunter2\"";
}
protected override ExitCode Run(IList<string> parameters)
{
var IRC = Instance.Chat;
IRC.SetProviderInfo(new IRCSetupInfo(IRC.ProviderInfos()[(int)ChatProvider.IRC])
{
AuthTarget = parameters[0],
AuthMessage = parameters[1]
});
return ExitCode.Normal;
}
}
class IRCDisableAuthCommand : ConsoleCommand
{
public IRCDisableAuthCommand()
{
Keyword = "disable-auth";
}
public override string GetHelpText()
{
return "Turns off IRC authentication";
}
protected override ExitCode Run(IList<string> parameters)
{
var IRC = Instance.Chat;
IRC.SetProviderInfo(new IRCSetupInfo(IRC.ProviderInfos()[(int)ChatProvider.IRC])
{
AuthTarget = null,
AuthMessage = null,
});
return ExitCode.Normal;
}
}
class ChatStatusCommand : ConsoleCommand
{
readonly int providerIndex;
public ChatStatusCommand(ChatProvider pI)
{
Keyword = "status";
providerIndex = (int)pI;
}
public override string GetHelpText()
{
return "Lists channels and connections status";
}
protected override ExitCode Run(IList<string> parameters)
{
var IRC = Instance.Chat;
var info = IRC.ProviderInfos()[providerIndex];
OutputProc("Currently configured channels:");
OutputProc("Admin:");
foreach (var I in info.AdminChannels)
OutputProc("\t" + I);
OutputProc("Watchdog:");
foreach (var I in info.WatchdogChannels)
OutputProc("\t" + I);
OutputProc("Game:");
foreach (var I in info.GameChannels)
OutputProc("\t" + I);
OutputProc("Developer:");
foreach (var I in info.DevChannels)
OutputProc("\t" + I);
OutputProc("Chat bot is: " + (!info.Enabled ? "Disabled" : IRC.Connected(info.Provider) ? "Connected" : "Disconnected"));
return ExitCode.Normal;
}
}
class ChatEnableCommand : ConsoleCommand
{
readonly int providerIndex;
public ChatEnableCommand(ChatProvider pI)
{
Keyword = "enable";
providerIndex = (int)pI;
}
public override string GetHelpText()
{
return "Enables the chat bot";
}
protected override ExitCode Run(IList<string> parameters)
{
var Chat = Instance.Chat;
var info = Chat.ProviderInfos()[providerIndex];
info.Enabled = true;
var res = Chat.SetProviderInfo(info);
if (res != null)
{
OutputProc(res);
return ExitCode.ServerError;
}
return ExitCode.Normal;
}
}
class ChatDisableCommand : ConsoleCommand
{
readonly int providerIndex;
public ChatDisableCommand(ChatProvider pI)
{
Keyword = "disable";
providerIndex = (int)pI;
}
public override string GetHelpText()
{
return "Disables the chat bot";
}
protected override ExitCode Run(IList<string> parameters)
{
var Chat = Instance.Chat;
var info = Chat.ProviderInfos()[providerIndex];
info.Enabled = false;
var res = Chat.SetProviderInfo(info);
if (res != null)
{
OutputProc(res);
return ExitCode.ServerError;
}
return ExitCode.Normal;
}
}
class IRCServerCommand : ConsoleCommand
{
public IRCServerCommand()
{
Keyword = "set-server";
RequiredParameters = 1;
}
public override string GetArgumentString()
{
return "<url>:<port>";
}
public override string GetHelpText()
{
return "Sets the IRC server";
}
protected override ExitCode Run(IList<string> parameters)
{
var splits = parameters[0].Split(':');
if(splits.Length < 2)
{
OutputProc("Invalid parameter!");
return ExitCode.BadCommand;
}
var Chat = Instance.Chat;
var PI = new IRCSetupInfo(Chat.ProviderInfos()[(int)ChatProvider.IRC])
{
URL = splits[0]
};
try
{
PI.Port = Convert.ToUInt16(splits[1]);
}
catch
{
OutputProc("Invalid port number!");
return ExitCode.BadCommand;
}
var res = Chat.SetProviderInfo(PI);
OutputProc(res ?? "Success");
return res == null ? ExitCode.Normal : ExitCode.ServerError;
}
}
class DiscordSetTokenCommand : ConsoleCommand
{
public DiscordSetTokenCommand()
{
Keyword = "set-token";
RequiredParameters = 1;
}
public override string GetArgumentString()
{
return "<bot-token>";
}
public override string GetHelpText()
{
return "Sets the discord API bot token";
}
protected override ExitCode Run(IList<string> parameters)
{
var Chat = Instance.Chat;
var res = Chat.SetProviderInfo(new DiscordSetupInfo(Chat.ProviderInfos()[(int)ChatProvider.Discord]) { BotToken = parameters[0] });
OutputProc(res ?? "Success");
return res == null ? ExitCode.Normal : ExitCode.ServerError;
}
}
}
-174
View File
@@ -1,174 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using TGS.Interface;
namespace TGS.CommandLine
{
class ConfigCommand : RootCommand
{
public ConfigCommand()
{
Keyword = "config";
Children = new Command[] { new ConfigDeleteCommand(), new ConfigServerDirectoryCommand(), new ConfigDownloadCommand(), new ConfigUploadCommand(), new ConfigListCommand() };
}
public override string GetHelpText()
{
return "Manage settings";
}
}
class ConfigDeleteCommand : ConsoleCommand
{
public ConfigDeleteCommand()
{
Keyword = "delete";
RequiredParameters = 1;
}
protected override ExitCode Run(IList<string> parameters)
{
var res = Instance.Config.DeleteFile(parameters[0], out bool unauthorized);
if (res != null)
{
OutputProc(res);
return ExitCode.ServerError;
}
return ExitCode.Normal;
}
public override string GetArgumentString()
{
return "<static file>";
}
public override string GetHelpText()
{
return "Deletes the specified file from the static tree.";
}
}
class ConfigListCommand : ConsoleCommand
{
public ConfigListCommand()
{
Keyword = "list";
}
public override string GetHelpText()
{
return "Lists the contents of the server's static directory, optionally specifying a subdirectory";
}
public override string GetArgumentString()
{
return "[path to subdirectory]";
}
protected override ExitCode Run(IList<string> parameters)
{
var list = Instance.Config.ListStaticDirectory(parameters.Count > 0 ? parameters[0] : null, out string error, out bool unauthorized);
if(list == null)
{
OutputProc(error);
return ExitCode.ServerError;
}
if (list.Count == 0)
OutputProc("The static directory is empty!");
else
foreach (var I in list)
OutputProc(I);
return ExitCode.Normal;
}
}
class ConfigServerDirectoryCommand : ConsoleCommand
{
public ConfigServerDirectoryCommand()
{
Keyword = "server-dir";
}
protected override ExitCode Run(IList<string> parameters)
{
OutputProc(Instance.ServerDirectory());
return ExitCode.Normal;
}
public override string GetHelpText()
{
return "Print the directory the server is installed in";
}
}
class ConfigDownloadCommand : ConsoleCommand
{
public ConfigDownloadCommand()
{
Keyword = "download";
RequiredParameters = 2;
}
protected override ExitCode Run(IList<string> parameters)
{
var bytes = Instance.Config.ReadText(parameters[0], parameters.Count > 2 && parameters[2].ToLower() == "--repo", out string error, out bool unauthorized);
if(bytes == null)
{
OutputProc("Error: " + error);
return ExitCode.ServerError;
}
try
{
File.WriteAllText(parameters[1], bytes);
}
catch (Exception e)
{
OutputProc("Error: " + e.ToString());
}
return ExitCode.Normal;
}
public override string GetArgumentString()
{
return "<source static file> <out file> [--repo]";
}
public override string GetHelpText()
{
return "Downloads the specified file from the static tree and writes it to out file. --repo will fetch it from the repository instead of the Static directory";
}
}
class ConfigUploadCommand : ConsoleCommand
{
public ConfigUploadCommand()
{
Keyword = "upload";
RequiredParameters = 2;
}
protected override ExitCode Run(IList<string> parameters)
{
try
{
var res = Instance.Config.WriteText(parameters[0], File.ReadAllText(parameters[1]), null, out bool unauthorized);
if (res != null)
{
OutputProc("Error: " + res);
return ExitCode.ServerError;
}
}
catch (Exception e)
{
OutputProc("Error: " + e.ToString());
}
return ExitCode.Normal;
}
public override string GetArgumentString()
{
return "<destination statoc file> <source file> [--repo]";
}
public override string GetHelpText()
{
return "Uploads the specified file to the static tree from source file";
}
}
}
-16
View File
@@ -1,16 +0,0 @@
using TGS.Interface;
namespace TGS.CommandLine
{
abstract class ConsoleCommand : Command
{
/// <summary>
/// The <see cref="IServer"/> currently in use by the <see cref="Program"/>
/// </summary>
public static IServer Server;
/// <summary>
/// The <see cref="IInstance"/> currently in use by the <see cref="Program"/>
/// </summary>
public static IInstance Instance;
}
}
-314
View File
@@ -1,314 +0,0 @@
using System;
using System.Collections.Generic;
using TGS.Interface;
namespace TGS.CommandLine
{
class DDCommand : RootCommand
{
public DDCommand()
{
Keyword = "dd";
Children = new Command[] { new DDStartCommand(), new DDStopCommand(), new DDRestartCommand(), new DDStatusCommand(), new DDAutostartCommand(), new DDPortCommand(), new DDSecurityCommand(), new DDWorldAnnounceCommand(), new DDWebclientCommand() };
}
public override string GetHelpText()
{
return "Manage DreamDaemon";
}
}
class DDWorldAnnounceCommand : ConsoleCommand
{
public DDWorldAnnounceCommand()
{
Keyword = "announce";
RequiredParameters = 1;
}
public override string GetHelpText()
{
return "Sends a message all players on the server";
}
public override string GetArgumentString()
{
return "<message>";
}
protected override ExitCode Run(IList<string> parameters)
{
var res = Instance.DreamDaemon.WorldAnnounce(String.Join(" ", parameters));
OutputProc(res ?? "Success!");
return res == null ? ExitCode.Normal : ExitCode.ServerError;
}
}
class DDStartCommand : ConsoleCommand
{
public DDStartCommand()
{
Keyword = "start";
}
public override string GetHelpText()
{
return "Starts the server and watchdog";
}
protected override ExitCode Run(IList<string> parameters)
{
var res = Instance.DreamDaemon.Start();
OutputProc(res ?? "Success!");
return res == null ? ExitCode.Normal : ExitCode.ServerError;
}
}
class DDStopCommand : ConsoleCommand
{
public DDStopCommand()
{
Keyword = "stop";
}
public override string GetArgumentString()
{
return "[--graceful]";
}
public override string GetHelpText()
{
return "Stops the server and watchdog optionally waiting for the current round to end";
}
protected override ExitCode Run(IList<string> parameters)
{
var DD = Instance.DreamDaemon;
if (parameters.Count > 0 && parameters[0].ToLower() == "--graceful")
{
if (DD.DaemonStatus() != DreamDaemonStatus.Online)
{
OutputProc("Error: The game is not currently running!");
return ExitCode.ServerError;
}
DD.RequestStop();
return ExitCode.Normal;
}
var res = DD.Stop();
OutputProc(res ?? "Success!");
return res == null ? ExitCode.Normal : ExitCode.ServerError;
}
}
class DDRestartCommand : ConsoleCommand
{
public DDRestartCommand()
{
Keyword = "restart";
}
public override string GetArgumentString()
{
return "[--graceful]";
}
protected override ExitCode Run(IList<string> parameters)
{
var DD = Instance.DreamDaemon;
if (parameters.Count > 0 && parameters[0].ToLower() == "--graceful")
{
if (DD.DaemonStatus() != DreamDaemonStatus.Online)
{
OutputProc("Error: The game is not currently running!");
return ExitCode.ServerError;
}
DD.RequestRestart();
return ExitCode.Normal;
}
var res = DD.Restart();
OutputProc(res ?? "Success!");
return res == null ? ExitCode.Normal : ExitCode.ServerError;
}
public override string GetHelpText()
{
return "Restarts the server and watchdog optionally waiting for the current round to end";
}
}
class DDStatusCommand : ConsoleCommand
{
public DDStatusCommand()
{
Keyword = "status";
}
public override string GetHelpText()
{
return "Gets the current status of the watchdog and server";
}
protected override ExitCode Run(IList<string> parameters)
{
var DD = Instance.DreamDaemon;
OutputProc(DD.StatusString(true));
if (DD.ShutdownInProgress())
OutputProc("The server will shutdown once the current round completes.");
var pc = DD.PlayerCount();
if (pc != -1)
OutputProc(pc + " connected clients");
return ExitCode.Normal;
}
}
class DDAutostartCommand : ConsoleCommand
{
public DDAutostartCommand()
{
Keyword = "autostart";
RequiredParameters = 1;
}
protected override ExitCode Run(IList<string> parameters)
{
var DD = Instance.DreamDaemon;
switch (parameters[0].ToLower())
{
case "on":
DD.SetAutostart(true);
break;
case "off":
DD.SetAutostart(false);
break;
case "check":
OutputProc("Autostart is: " + (DD.Autostart() ? "On" : "Off"));
break;
default:
OutputProc("Invalid parameter: " + parameters[0]);
return ExitCode.BadCommand;
}
return ExitCode.Normal;
}
public override string GetArgumentString()
{
return "<on|off|check>";
}
public override string GetHelpText()
{
return "Change or check autostarting of the game server with the service";
}
}
class DDWebclientCommand : ConsoleCommand
{
public DDWebclientCommand()
{
Keyword = "webclient";
RequiredParameters = 1;
}
protected override ExitCode Run(IList<string> parameters)
{
var DD = Instance.DreamDaemon;
switch (parameters[0].ToLower())
{
case "on":
DD.SetWebclient(true);
break;
case "off":
DD.SetWebclient(false);
break;
case "check":
OutputProc("Webclient is: " + (DD.Webclient() ? "Enabled" : "Disabled"));
break;
default:
OutputProc("Invalid parameter: " + parameters[0]);
return ExitCode.BadCommand;
}
return ExitCode.Normal;
}
public override string GetArgumentString()
{
return "<on|off|check>";
}
public override string GetHelpText()
{
return "Change or check if the BYOND webclient is enabled for the game server";
}
}
class DDPortCommand : ConsoleCommand
{
public DDPortCommand()
{
Keyword = "set-port";
RequiredParameters = 1;
}
protected override ExitCode Run(IList<string> parameters)
{
ushort port;
try
{
port = Convert.ToUInt16(parameters[0]);
}
catch
{
OutputProc("Invalid port number!");
return ExitCode.BadCommand;
}
Instance.DreamDaemon.SetPort(port);
return ExitCode.Normal;
}
public override string GetArgumentString()
{
return "<number>";
}
public override string GetHelpText()
{
return "Sets the port DreamDaemon will open the server on. Requires a server restart to apply and queues a graceful one up";
}
}
class DDSecurityCommand : ConsoleCommand
{
public DDSecurityCommand()
{
Keyword = "set-security";
RequiredParameters = 1;
}
protected override ExitCode Run(IList<string> parameters)
{
DreamDaemonSecurity sec;
switch (parameters[0].ToLower())
{
case "safe":
sec = DreamDaemonSecurity.Safe;
break;
case "ultra":
case "ultrasafe":
sec = DreamDaemonSecurity.Ultrasafe;
break;
case "trust":
case "trusted":
sec = DreamDaemonSecurity.Trusted;
break;
default:
OutputProc("Invalid security word!");
return ExitCode.BadCommand;
}
Instance.DreamDaemon.SetSecurityLevel(sec);
return ExitCode.Normal;
}
public override string GetArgumentString()
{
return "<safe|ultrasafe|trusted>";
}
public override string GetHelpText()
{
return "Sets the visibility option for the DreamDaemon world";
}
}
}
-218
View File
@@ -1,218 +0,0 @@
using System;
using System.Collections.Generic;
using System.Threading;
using TGS.Interface;
namespace TGS.CommandLine
{
class DMCommand : RootCommand
{
public DMCommand()
{
Keyword = "dm";
Children = new Command[] { new DMCompileCommand(), new DMInitializeCommand(), new DMStatusCommand(), new DMSetProjectNameCommand(), new DMCancelCommand() };
}
public override string GetHelpText()
{
return "Manage compiling the server";
}
}
class DMCompileCommand : ConsoleCommand
{
public DMCompileCommand()
{
Keyword = "compile";
}
protected override ExitCode Run(IList<string> parameters)
{
var DM = Instance.Compiler;
var stat = DM.GetStatus();
if (stat != CompilerStatus.Initialized)
{
OutputProc("Error: Compiler is " + ((stat == CompilerStatus.Uninitialized) ? "unintialized!" : "busy with another task!"));
return ExitCode.ServerError;
}
if (Instance.Byond.GetVersion(ByondVersion.Installed) == null)
{
Console.Write("Error: BYOND is not installed!");
return ExitCode.ServerError;
}
if (!DM.Compile())
{
OutputProc("Error: Unable to start compilation!");
var err = DM.CompileError();
if (err != null)
OutputProc(err);
return ExitCode.ServerError;
}
OutputProc("Compile job started");
if (parameters.Count > 0 && parameters[0] == "--wait")
{
do
{
Thread.Sleep(1000);
} while (DM.GetStatus() == CompilerStatus.Compiling);
var res = DM.CompileError();
OutputProc(res ?? "Compilation successful");
if (res != null)
return ExitCode.ServerError;
}
return ExitCode.Normal;
}
public override string GetArgumentString()
{
return "[--wait]";
}
public override string GetHelpText()
{
return "Starts a compile/update job optionally waiting for completion";
}
}
class DMStatusCommand : ConsoleCommand
{
public DMStatusCommand()
{
Keyword = "status";
}
void ShowError()
{
var error = Instance.Compiler.CompileError();
if (error != null)
OutputProc("Last error: " + error);
}
protected override ExitCode Run(IList<string> parameters)
{
var DM = Instance.Compiler;
OutputProc(String.Format("Target Project: /{0}.dme", DM.ProjectName()));
Console.Write("Compilier is currently: ");
switch (DM.GetStatus())
{
case CompilerStatus.Compiling:
OutputProc("Compiling...");
break;
case CompilerStatus.Initialized:
OutputProc("Idle");
ShowError();
break;
case CompilerStatus.Initializing:
OutputProc("Setting up...");
break;
case CompilerStatus.Uninitialized:
OutputProc("Uninitialized");
ShowError();
break;
default:
OutputProc("Seizing the means of production (This is an error).");
return ExitCode.ServerError;
}
return ExitCode.Normal;
}
public override string GetHelpText()
{
return "Get the current status of the compiler";
}
}
class DMSetProjectNameCommand : ConsoleCommand
{
public DMSetProjectNameCommand()
{
Keyword = "project-name";
RequiredParameters = 1;
}
public override string GetArgumentString()
{
return "<path>";
}
public override string GetHelpText()
{
return "Set the relative path of the .dme/.dmb to compile/run";
}
protected override ExitCode Run(IList<string> parameters)
{
Instance.Compiler.SetProjectName(parameters[0]);
return ExitCode.Normal;
}
}
class DMInitializeCommand : ConsoleCommand
{
public DMInitializeCommand()
{
Keyword = "initialize";
}
public override string GetArgumentString()
{
return "[--wait]";
}
public override string GetHelpText()
{
return "Starts an initialization job optionally waiting for completion";
}
protected override ExitCode Run(IList<string> parameters)
{
var DM = Instance.Compiler;
var stat = DM.GetStatus();
if (stat == CompilerStatus.Compiling || stat == CompilerStatus.Initializing)
{
OutputProc("Error: Compiler is " + ((stat == CompilerStatus.Initializing) ? "already initialized!" : " already running!"));
return ExitCode.ServerError;
}
if (!DM.Initialize())
{
OutputProc("Error: Unable to start initialization!");
var err = DM.CompileError();
if (err != null)
OutputProc(err);
return ExitCode.ServerError;
}
OutputProc("Initialize job started");
if (parameters.Count > 0 && parameters[0] == "--wait")
{
do
{
Thread.Sleep(1000);
} while (DM.GetStatus() == CompilerStatus.Initializing);
var res = DM.CompileError();
OutputProc(res ?? "Initialization successful");
if (res != null)
return ExitCode.ServerError;
}
return ExitCode.Normal;
}
}
class DMCancelCommand : ConsoleCommand
{
public DMCancelCommand()
{
Keyword = "cancel";
}
protected override ExitCode Run(IList<string> parameters)
{
var res = Instance.Compiler.Cancel();
OutputProc(res ?? "Success!");
return ExitCode.Normal; //because failing cancellation implys it's already cancelled
}
public override string GetHelpText()
{
return "Cancels the current compilation job";
}
}
}
-282
View File
@@ -1,282 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using TGS.Interface;
using TGS.Interface.Components;
namespace TGS.CommandLine
{
class Program
{
static bool interactive = false, saidSrvVersion = false;
static IClient currentInterface;
static Command.ExitCode RunCommandLine(IList<string> argsAsList)
{
//first lookup the connection string
bool badConnectionString = false;
for (var I = 0; I < argsAsList.Count - 1; ++I) {
var lowerarg = argsAsList[I].ToLower();
if (lowerarg == "-c" || lowerarg == "--connect")
{
var connectionString = argsAsList[I + 1];
var splits = connectionString.Split('@');
var userpass = splits[0].Split(':');
if (splits.Length != 2 || userpass.Length != 2)
{
badConnectionString = true;
break;
}
var addrport = splits[1].Split(':');
if (addrport.Length != 2)
{
badConnectionString = true;
break;
}
var username = userpass[0];
var password = userpass[1];
var address = addrport[0];
ushort port;
try
{
port = Convert.ToUInt16(addrport[1]);
}
catch
{
badConnectionString = true;
break;
}
if(String.IsNullOrWhiteSpace(username) || String.IsNullOrWhiteSpace(password) || String.IsNullOrWhiteSpace(address))
{
badConnectionString = true;
break;
}
argsAsList.RemoveAt(I);
argsAsList.RemoveAt(I);
ReplaceInterface(new Client(new RemoteLoginInfo(address, port, username, password)));
break;
}
}
if (badConnectionString)
{
Console.WriteLine("Remote connection usage: <-c/--connect> username:password@address:port");
return Command.ExitCode.BadCommand;
}
var res = currentInterface.ConnectionStatus(out string error);
if (!res.HasFlag(ConnectivityLevel.Connected))
{
Console.WriteLine("Unable to connect to service: " + error);
Console.WriteLine("Remote connection usage: <-c/--connect> username:password@address:port");
return Command.ExitCode.ConnectionError;
}
if (!res.HasFlag(ConnectivityLevel.Authenticated))
{
Console.WriteLine("Authentication error: Username/password/windows identity is not authorized!");
return Command.ExitCode.ConnectionError;
}
if (!SentVMMWarning && currentInterface.VersionMismatch(out error))
{
SentVMMWarning = true;
Console.WriteLine(error);
}
else if (interactive && !saidSrvVersion)
{
Console.WriteLine("Connectd to service version: " + currentInterface.Server.Version);
saidSrvVersion = true;
}
try
{
return new CLICommand(currentInterface).DoRun(argsAsList);
}
catch (Exception e)
{
Console.WriteLine("Error: " + e.ToString());
return Command.ExitCode.ConnectionError;
};
}
static void ReplaceInterface(IClient I)
{
currentInterface = I;
ConsoleCommand.Server = I.Server;
ConsoleCommand.Instance = null;
saidSrvVersion = false;
}
public static string ReadLineSecure()
{
string result = "";
while (true)
{
ConsoleKeyInfo i = Console.ReadKey(true);
if (i.Key == ConsoleKey.Enter)
{
break;
}
else if (i.Key == ConsoleKey.Backspace)
{
if (result.Length > 0)
{
result = result.Substring(0, result.Length - 1);
Console.Write("\b \b");
}
}
else
{
result += i.KeyChar;
Console.Write("*");
}
}
Console.WriteLine();
return result;
}
static bool SentVMMWarning = false;
static string AcceptedBadCert;
static bool BadCertificateInteractive(string message)
{
if (AcceptedBadCert == message)
return true;
Console.WriteLine(message);
Console.Write("Do you wish to continue? NOT RECCOMENDED! (y/N): ");
var result = Console.ReadLine().Trim().ToLower();
if (result == "y" || result == "yes")
{
AcceptedBadCert = message;
return true;
}
return false;
}
/// <summary>
/// Tries to set <see cref="currentInterface"/>'s <see cref="ITGInstance"/> to <paramref name="instanceName"/>, outputting appropriate messages
/// </summary>
/// <param name="instanceName">The name of the <see cref="ITGInstance"/> to test</param>
/// <param name="silentSuccess">If <see langword="true"/>, does not output on success</param>
/// <returns><see langword="true"/> if the connection was made, <see langword="false"/> otherwise</returns>
static bool CheckInstanceConnectivity(string instanceName, bool silentSuccess)
{
var res = currentInterface.Server.Instances.Where(x => x.Metadata.Name == instanceName).FirstOrDefault();
if (res != null)
{
ConsoleCommand.Instance = res;
if (!silentSuccess)
Console.WriteLine("Successfully conected to instance!");
return true;
}
Console.WriteLine("Unable to connect to instance! Does it exist?");
return false;
}
static int Main(string[] args)
{
ReplaceInterface(new Client());
Command.OutputProcVar.Value = Console.WriteLine;
if (args.Length != 0)
{
var argsAsList = new List<string>(args);
for (var I = 0; I < argsAsList.Count - 1; ++I)
{
if (argsAsList[I].ToLower() == "--instance")
{
if (!CheckInstanceConnectivity(args[I + 1], true))
return (int)Command.ExitCode.ConnectionError;
argsAsList.RemoveRange(I, 2);
break;
}
else if (argsAsList[I].ToLower() == "--disable-ssl-verification") //im just not even going to document this because i hate it so much
{
argsAsList.RemoveAt(I);
--I;
Client.SetBadCertificateHandler(_ => false);
}
}
return (int)RunCommandLine(argsAsList);
}
//interactive mode
Client.SetBadCertificateHandler(BadCertificateInteractive);
Console.WriteLine("Type 'instance' to connect to a server instance");
Console.WriteLine("Type 'remote' to connect to a remote service");
while (true)
{
Console.Write("Enter command: ");
var NextCommand = Console.ReadLine();
switch (NextCommand.ToLower())
{
case "instance":
Console.Write("Enter instance name: ");
CheckInstanceConnectivity(Console.ReadLine(), false);
break;
case "remote":
SentVMMWarning = false;
Console.Write("Enter server address: ");
var address = Console.ReadLine();
Console.Write("Enter server port: ");
ushort port;
try{
port = Convert.ToUInt16(Console.ReadLine());
}
catch
{
Console.WriteLine("Error: Bad port!");
break;
}
Console.Write("Enter username: ");
var username = Console.ReadLine();
Console.Write("Enter password: ");
var password = ReadLineSecure();
ReplaceInterface(new Client(new RemoteLoginInfo(address, port, username, password)));
var res = currentInterface.ConnectionStatus(out string error);
if (!res.HasFlag(ConnectivityLevel.Connected))
{
Console.WriteLine("Unable to connect: " + error);
ReplaceInterface(new Client());
}
else if (!res.HasFlag(ConnectivityLevel.Authenticated))
{
Console.WriteLine("Authentication error: Username/password/windows identity is not authorized! Returning to local mode...");
ReplaceInterface(new Client());
}
else
{
Console.WriteLine("Connected remotely");
Console.WriteLine("Type 'instance' to connect to a server instance");
if (currentInterface.VersionMismatch(out error))
{
SentVMMWarning = true;
Console.WriteLine(error);
}
Console.WriteLine("Type 'disconnect' to return to local mode");
}
break;
case "disconnect":
SentVMMWarning = false;
ReplaceInterface(new Client());
Console.WriteLine("Switch to local mode");
break;
case "quit":
case "exit":
return (int)Command.ExitCode.Normal;
case "debug-upgrade":
currentInterface.Server.Management.PrepareForUpdate();
return (int)Command.ExitCode.Normal;
default:
//linq voodoo to get quoted strings
var formattedCommand = NextCommand.Split('"')
.Select((element, index) => index % 2 == 0 // If even index
? element.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries) // Split the item
: new string[] { element }) // Keep the entire item
.SelectMany(element => element).ToList();
formattedCommand = formattedCommand.Select(x => x.Trim()).ToList();
formattedCommand.Remove("");
RunCommandLine(formattedCommand);
break;
}
}
}
}
}
@@ -1,17 +0,0 @@
using System.Reflection;
using System.Runtime.CompilerServices;
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("TGStation Server Commandline")]
[assembly: AssemblyDescription("CLI for the TG Station Server Service")]
// 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("9ad1f086-a83e-4d14-a844-58a9471106b6")]
-407
View File
@@ -1,407 +0,0 @@
using System;
using System.Collections.Generic;
using TGS.Interface;
namespace TGS.CommandLine
{
class RepoCommand : RootCommand
{
public RepoCommand()
{
Keyword = "repo";
Children = new Command[] { new RepoSetupCommand(), new RepoUpdateCommand(), new RepoGenChangelogCommand(), new RepoPushChangelogCommand(), new RepoSetEmailCommand(), new RepoSetNameCommand(), new RepoMergePRCommand(), new RepoListPRsCommand(), new RepoStatusCommand(), new RepoListBackupsCommand(), new RepoCheckoutCommand(), new RepoResetCommand(), new RepoUpdateJsonCommand(), new RepoSetPushTestmergeCommitsCommand() };
}
public override string GetHelpText()
{
return "Manage the git repository";
}
}
class RepoSetPushTestmergeCommitsCommand : ConsoleCommand
{
public RepoSetPushTestmergeCommitsCommand()
{
Keyword = "push-testmerges";
RequiredParameters = 1;
}
public override string GetHelpText()
{
return "Set if a temporary branch is to the remote when we make testmerge commits and then delete it";
}
public override string GetArgumentString()
{
return "<on|off>";
}
protected override ExitCode Run(IList<string> parameters)
{
switch (parameters[0].ToLower())
{
case "on":
Instance.Repository.SetPushTestmergeCommits(true);
break;
case "off":
Instance.Repository.SetPushTestmergeCommits(false);
break;
default:
OutputProc("Invalid option!");
return ExitCode.BadCommand;
}
return ExitCode.Normal;
}
}
class RepoUpdateJsonCommand : ConsoleCommand
{
public RepoUpdateJsonCommand()
{
Keyword = "update-json";
}
public override string GetHelpText()
{
return "Updates the cached TGS3.json with the one from the repo. Compilation is blocked if these two do not match.";
}
protected override ExitCode Run(IList<string> parameters)
{
var res = Instance.Repository.UpdateTGS3Json();
if (res != null)
{
OutputProc(res);
return ExitCode.ServerError;
}
return ExitCode.Normal;
}
}
class RepoSetupCommand : ConsoleCommand
{
public RepoSetupCommand()
{
Keyword = "setup";
RequiredParameters = 1;
}
protected override ExitCode Run(IList<string> parameters)
{
var res = Instance.Repository.Setup(parameters[0], parameters.Count > 1 ? parameters[1] : "master");
if (res != null)
{
OutputProc("Error: " + res);
return ExitCode.ServerError;
}
OutputProc("Setting up repo. This will take a while...");
return ExitCode.Normal;
}
public override string GetArgumentString()
{
return "<git-url> [branchname]";
}
public override string GetHelpText()
{
return "Clean up everything and clones the repo at git-url with optional branch name";
}
}
class RepoStatusCommand : ConsoleCommand
{
public RepoStatusCommand()
{
Keyword = "status";
}
protected override ExitCode Run(IList<string> parameters)
{
var Repo = Instance.Repository;
var busy = Repo.OperationInProgress();
if (!busy)
{
OutputProc("Repo: Idle");
var head = Repo.GetHead(false, out string error);
if (head == null)
head = "Error: " + error;
var remotehead = Repo.GetHead(true, out error);
if (remotehead == null)
remotehead = "Error: " + error;
var branch = Repo.GetBranch(out error);
if (branch == null)
branch = "Error: " + error;
var remote = Repo.GetRemote(out error);
if (remote == null)
remote = "Error: " + error;
OutputProc("Remote: " + remote + " (" + remotehead + ")");
OutputProc("Branch: " + branch);
OutputProc("HEAD: " + head);
OutputProc("Push testmerge commits: " + (Repo.PushTestmergeCommits() ? "ON" : "OFF"));
OutputProc(String.Format("Committer Identity: {0} ({1})", Repo.GetCommitterName(), Repo.GetCommitterEmail()));
}
else
{
OutputProc("Repo: Busy");
var progress = Repo.CheckoutProgress();
if (progress != -1)
{
var eqs = "";
for (var I = 0; I < progress / 10; ++I)
eqs += "=";
var dshs = "";
for (var I = 0; I < 10 - (progress / 10); ++I)
eqs += "-";
OutputProc(String.Format("Progress: [{0}{1}] {2}%", eqs, dshs, progress));
}
}
return ExitCode.Normal;
}
public override string GetHelpText()
{
return "Shows the busy status of the repo, remote, branch, and HEAD information";
}
}
class RepoResetCommand : ConsoleCommand
{
public RepoResetCommand()
{
Keyword = "reset";
}
protected override ExitCode Run(IList<string> parameters)
{
var result = Instance.Repository.Reset(parameters.Count > 0 && parameters[0].ToLower() == "--origin");
OutputProc(result ?? "Success!");
return result == null ? ExitCode.Normal : ExitCode.ServerError;
}
public override string GetArgumentString()
{
return "[--origin]";
}
public override string GetHelpText()
{
return "Hard resets the repo. If a target is specified, the current branch is reset to that branch";
}
}
class RepoUpdateCommand : ConsoleCommand
{
public RepoUpdateCommand()
{
Keyword = "update";
RequiredParameters = 1;
}
protected override ExitCode Run(IList<string> parameters)
{
bool hard;
switch (parameters[0].ToLower())
{
case "hard":
hard = true;
break;
case "merge":
hard = false;
break;
default:
OutputProc("Invalid parameter: " + parameters[0]);
return ExitCode.BadCommand;
}
var res = Instance.Repository.Update(hard);
OutputProc(res ?? "Success");
return res == null ? ExitCode.Normal : ExitCode.ServerError;
}
public override string GetHelpText()
{
return "Updates the current branch the repo is on either via a merge or hard reset";
}
public override string GetArgumentString()
{
return "<hard|merge>";
}
}
class RepoGenChangelogCommand : ConsoleCommand
{
public RepoGenChangelogCommand()
{
Keyword = "gen-changelog";
}
protected override ExitCode Run(IList<string> parameters)
{
var result = Instance.Repository.GenerateChangelog(out string error);
OutputProc(error ?? "Success!");
if (result != null)
OutputProc(result);
return error == null ? ExitCode.Normal : ExitCode.ServerError;
}
public override string GetHelpText()
{
return "Compiles the html changelog";
}
}
class RepoPushChangelogCommand : ConsoleCommand
{
public RepoPushChangelogCommand()
{
Keyword = "push-changelog";
}
protected override ExitCode Run(IList<string> parameters)
{
var result = Instance.Repository.SynchronizePush();
if(result != null)
OutputProc(result);
return result == null ? ExitCode.Normal : ExitCode.ServerError;
}
public override string GetHelpText()
{
return "Pushes the html changelog if the SSH authentication is configured correctly";
}
}
class RepoSetEmailCommand : ConsoleCommand
{
public RepoSetEmailCommand()
{
Keyword = "set-email";
RequiredParameters = 1;
}
protected override ExitCode Run(IList<string> parameters)
{
Instance.Repository.SetCommitterEmail(parameters[0]);
return ExitCode.Normal;
}
public override string GetArgumentString()
{
return "<e-mail>";
}
public override string GetHelpText()
{
return "Set the e-mail used for commits";
}
}
class RepoSetNameCommand : ConsoleCommand
{
public RepoSetNameCommand()
{
Keyword = "set-name";
RequiredParameters = 1;
}
protected override ExitCode Run(IList<string> parameters)
{
Instance.Repository.SetCommitterName(parameters[0]);
return ExitCode.Normal;
}
public override string GetArgumentString()
{
return "<name>";
}
public override string GetHelpText()
{
return "Set the name used for commits";
}
}
class RepoMergePRCommand : ConsoleCommand
{
public RepoMergePRCommand()
{
Keyword = "merge-pr";
RequiredParameters = 1;
}
protected override ExitCode Run(IList<string> parameters)
{
ushort PR;
try
{
PR = Convert.ToUInt16(parameters[0]);
}
catch
{
OutputProc("Invalid PR Number!");
return ExitCode.BadCommand;
}
var res = Instance.Repository.MergePullRequest(PR);
OutputProc(res ?? "Success");
return res == null ? ExitCode.Normal : ExitCode.ServerError;
}
public override string GetArgumentString()
{
return "<pr #>";
}
public override string GetHelpText()
{
return "Merge the given pull request from the origin repository into the current branch. Only supported with github remotes";
}
}
class RepoListPRsCommand : ConsoleCommand
{
public RepoListPRsCommand()
{
Keyword = "list-prs";
}
public override string GetHelpText()
{
return "Lists currently merge pull requests";
}
protected override ExitCode Run(IList<string> parameters)
{
var data = Instance.Repository.MergedPullRequests(out string error);
if (data == null)
{
OutputProc(error);
return ExitCode.ServerError;
}
if (data.Count == 0)
OutputProc("None!");
else
foreach (var I in data)
OutputProc(String.Format("#{0}: {2} by {3} at commit {1}", I.Number, I.Sha, I.Title, I.Author));
return ExitCode.Normal;
}
}
class RepoListBackupsCommand : ConsoleCommand
{
public RepoListBackupsCommand()
{
Keyword = "list-backups";
}
public override string GetHelpText()
{
return "Lists backup tags created by compilation";
}
protected override ExitCode Run(IList<string> parameters)
{
var data = Instance.Repository.ListBackups(out string error);
if (data == null)
{
OutputProc(error);
return ExitCode.ServerError;
}
if (data.Count == 0)
OutputProc("None!");
else
foreach (var I in data)
OutputProc(String.Format("{0} at commit {1}", I.Key, I.Value));
return ExitCode.Normal;
}
}
class RepoCheckoutCommand : ConsoleCommand
{
public RepoCheckoutCommand()
{
Keyword = "checkout";
RequiredParameters = 1;
}
public override string GetHelpText()
{
return "Checks out the targeted object";
}
protected override ExitCode Run(IList<string> parameters)
{
var res = Instance.Repository.Checkout(parameters[0]);
OutputProc(res ?? "Success");
return res == null ? ExitCode.Normal : ExitCode.ServerError;
}
}
}
-191
View File
@@ -1,191 +0,0 @@
using System;
using System.Collections.Generic;
using TGS.Interface;
namespace TGS.CommandLine
{
class CLICommand : RootCommand
{
public CLICommand(IClient I)
{
var tmp = ConsoleCommand.Instance != null ? new List<Command> { new UpdateCommand(), new TestmergeCommand(), new RepoCommand(), new BYONDCommand(), new DMCommand(), new DDCommand(), new ConfigCommand(), new IRCCommand(), new DiscordCommand(), new AutoUpdateCommand(), new SetAutoUpdateCommand() } : new List<Command>();
if (ConsoleCommand.Instance?.Administration != null)
tmp.Add(new AdminCommand());
if (ConsoleCommand.Server.Management != null)
tmp.Add(new ServiceCommand());
Children = tmp.ToArray();
}
public override void PrintHelp()
{
OutputProc("/tg/station 13 Server Command Line");
base.PrintHelp();
}
}
class AutoUpdateCommand : ConsoleCommand
{
public AutoUpdateCommand()
{
Keyword = "auto-update";
}
public override string GetHelpText()
{
return "Get the interval in minutes that the server automatically updates";
}
protected override ExitCode Run(IList<string> parameters)
{
var res = Instance.Repository.AutoUpdateInterval();
OutputProc(res == 0 ? "OFF" : String.Format("Auto updating every {0} minutes", res));
return ExitCode.Normal;
}
}
class SetAutoUpdateCommand : ConsoleCommand
{
public SetAutoUpdateCommand()
{
Keyword = "set-auto-update";
RequiredParameters = 1;
}
public override string GetArgumentString()
{
return "<off|interval in minutes>";
}
public override string GetHelpText()
{
return "Set the interval in minutes that the server automatically updates";
}
protected override ExitCode Run(IList<string> parameters)
{
ulong NewInterval;
if (parameters[0].ToLower() == "off")
NewInterval = 0;
else
try
{
NewInterval = Convert.ToUInt64(parameters[0]);
}
catch
{
OutputProc("Invalid interval specified!");
return ExitCode.BadCommand;
}
Instance.Repository.SetAutoUpdateInterval(NewInterval);
return ExitCode.Normal;
}
}
class UpdateCommand : ConsoleCommand
{
public UpdateCommand()
{
Keyword = "update";
RequiredParameters = 1;
}
protected override ExitCode Run(IList<string> parameters)
{
var gen_cl = parameters.Count > 1 && parameters[1].ToLower() == "--cl";
var Repo = Instance.Repository;
switch (parameters[0].ToLower())
{
case "hard":
var res = Repo.Update(true);
if (res != null)
{
OutputProc(res);
return ExitCode.ServerError;
}
break;
case "merge":
res = Repo.Update(false);
if (res != null)
{
OutputProc(res);
return ExitCode.ServerError;
}
break;
default:
OutputProc("Please specify hard or merge");
return ExitCode.BadCommand;
}
if (gen_cl)
{
Repo.GenerateChangelog(out string res);
if (res != null)
OutputProc(res);
else
{
res = Repo.SynchronizePush();
if (res != null)
OutputProc(res);
}
}
var resu = Instance.Compiler.Compile(true);
OutputProc(resu ? "Compilation started!" : "Compilation could not be started!");
return resu ? ExitCode.Normal : ExitCode.ServerError;
}
public override string GetArgumentString()
{
return "<merge|hard> [--cl]";
}
public override string GetHelpText()
{
return "Updates the server fully, optionally generating and pushing a changelog. Runs asynchronously once compilation starts";
}
}
class TestmergeCommand : ConsoleCommand
{
public TestmergeCommand()
{
Keyword = "testmerge";
RequiredParameters = 1;
}
protected override ExitCode Run(IList<string> parameters)
{
ushort tm;
try
{
tm = Convert.ToUInt16(parameters[0]);
if (tm == 0)
throw new Exception();
}
catch
{
OutputProc("Invalid tesmerge #: " + parameters[0]);
return ExitCode.BadCommand;
}
var Repo = Instance.Repository;
var res = Repo.MergePullRequest(tm);
if (res != null)
{
OutputProc(res);
return ExitCode.ServerError;
}
Repo.GenerateChangelog(out res);
if (res != null)
{
OutputProc(res);
return ExitCode.ServerError;
}
var resu = Instance.Compiler.Compile(true);
OutputProc(resu ? "Compilation started!" : "Compilation could not be started!");
return resu ? ExitCode.Normal : ExitCode.ServerError;
}
public override string GetArgumentString()
{
return "<pull request #>";
}
public override string GetHelpText()
{
return "Merges the specified pull request and updates the server";
}
}
}
-434
View File
@@ -1,434 +0,0 @@
using System;
using System.Collections.Generic;
using TGS.Interface;
using TGS.Interface.Components;
namespace TGS.CommandLine
{
/// <summary>
/// Used for managing the <see cref="ITGSService"/> components
/// </summary>
class ServiceCommand : RootCommand
{
/// <summary>
/// Construct a <see cref="ServiceCommand"/>
/// </summary>
public ServiceCommand()
{
Keyword = "service";
Children = new Command[] { new ServiceCreateInstanceCommand(), new ServiceDetachInstanceCommand(), new ServiceEnableInstanceCommand(), new ServiceImportInstanceCommand(), new ServiceListInstancesCommand(), new ServicePythonPathCommand(), new ServiceSetPythonPathCommand(), new ServiceSetRemoteAccessPortCommand(), new ServiceRemoteAccessPortCommand(), new ServiceDisableInstanceCommand(), new ServiceRenameInstanceCommand() };
}
/// <inheritdoc />
public override string GetHelpText()
{
return "Manage service wide settings";
}
}
/// <summary>
/// Command for calling <see cref="ITGInstanceManager.CreateInstance(string, string)"/>
/// </summary>
class ServiceCreateInstanceCommand : ConsoleCommand
{
/// <summary>
/// Construct a <see cref="ServiceCreateInstanceCommand"/>
/// </summary>
public ServiceCreateInstanceCommand()
{
Keyword = "create-instance";
RequiredParameters = 2;
}
/// <inheritdoc />
public override string GetHelpText()
{
return "Creates a new instance at the given path";
}
/// <inheritdoc />
public override string GetArgumentString()
{
return "<name> <path>";
}
/// <inheritdoc />
protected override ExitCode Run(IList<string> parameters)
{
var res = Server.InstanceManager.CreateInstance(parameters[0], parameters[1]);
if (res != null)
{
OutputProc(res);
return ExitCode.ServerError;
}
return ExitCode.Normal;
}
}
/// <summary>
/// Command for calling <see cref="ITGLanding.ListInstances"/>
/// </summary>
class ServiceListInstancesCommand : ConsoleCommand
{
/// <summary>
/// Construct a <see cref="ServiceListInstancesCommand"/>
/// </summary>
public ServiceListInstancesCommand()
{
Keyword = "list-instances";
}
/// <inheritdoc />
public override string GetHelpText()
{
return "Lists all instances";
}
/// <inheritdoc />
protected override ExitCode Run(IList<string> parameters)
{
foreach (var I in Server.Instances)
OutputProc(I.ToString());
return ExitCode.Normal;
}
}
/// <summary>
/// Command for calling <see cref="ITGInstanceManager.DetachInstance(string)"/>
/// </summary>
class ServiceDetachInstanceCommand : ConsoleCommand
{
/// <summary>
/// Construct a <see cref="ServiceDetachInstanceCommand"/>
/// </summary>
public ServiceDetachInstanceCommand()
{
Keyword = "detach-instance";
RequiredParameters = 1;
}
/// <inheritdoc />
public override string GetHelpText()
{
return "Detaches an instance";
}
/// <inheritdoc />
public override string GetArgumentString()
{
return "<name>";
}
/// <inheritdoc />
protected override ExitCode Run(IList<string> parameters)
{
var res = Server.InstanceManager.DetachInstance(parameters[0]);
if (res != null)
{
OutputProc(res);
return ExitCode.ServerError;
}
return ExitCode.Normal;
}
}
/// <summary>
/// Command for calling <see cref="ITGInstanceManager.ImportInstance(string)"/>
/// </summary>
class ServiceImportInstanceCommand : ConsoleCommand
{
/// <summary>
/// Construct a <see cref="ServiceImportInstanceCommand"/>
/// </summary>
public ServiceImportInstanceCommand()
{
Keyword = "import-instance";
RequiredParameters = 1;
}
/// <inheritdoc />
public override string GetHelpText()
{
return "Imports an instance (Chat settings will be lost if they are from a different windows installation)";
}
/// <inheritdoc />
public override string GetArgumentString()
{
return "<path>";
}
/// <inheritdoc />
protected override ExitCode Run(IList<string> parameters)
{
var res = Server.InstanceManager.ImportInstance(parameters[0]);
if (res != null)
{
OutputProc(res);
return ExitCode.ServerError;
}
return ExitCode.Normal;
}
}
/// <summary>
/// Command for calling <see cref="ITGSService.PythonPath"/>
/// </summary>
class ServicePythonPathCommand : ConsoleCommand
{
/// <summary>
/// Construct a <see cref="ServicePythonPathCommand"/>
/// </summary>
public ServicePythonPathCommand()
{
Keyword = "python";
}
/// <inheritdoc />
public override string GetHelpText()
{
return "Displays configured path the service uses for python";
}
/// <inheritdoc />
protected override ExitCode Run(IList<string> parameters)
{
var res = Server.Management.PythonPath();
if (res != null)
{
OutputProc(res);
return ExitCode.ServerError;
}
return ExitCode.Normal;
}
}
/// <summary>
/// Command for calling <see cref="ITGSService.SetPythonPath(string)"/>
/// </summary>
class ServiceSetPythonPathCommand : ConsoleCommand
{
/// <summary>
/// Construct a <see cref="ServiceSetPythonPathCommand"/>
/// </summary>
public ServiceSetPythonPathCommand()
{
Keyword = "set-python";
RequiredParameters = 1;
}
/// <inheritdoc />
public override string GetHelpText()
{
return "Sets the path to the python installation";
}
/// <inheritdoc />
public override string GetArgumentString()
{
return "<path>";
}
/// <inheritdoc />
protected override ExitCode Run(IList<string> parameters)
{
Server.Management.SetPythonPath(parameters[0]);
return ExitCode.Normal;
}
}
/// <summary>
/// Command for calling <see cref="ITGInstanceManager.SetInstanceEnabled(string, bool)"/> with a <see langword="true"/> parameter
/// </summary>
class ServiceEnableInstanceCommand : ConsoleCommand
{
/// <summary>
/// Construct a <see cref="ServiceEnableInstanceCommand"/>
/// </summary>
public ServiceEnableInstanceCommand()
{
Keyword = "enable-instance";
RequiredParameters = 1;
}
/// <inheritdoc />
public override string GetHelpText()
{
return "Enables the specified instance";
}
/// <inheritdoc />
public override string GetArgumentString()
{
return "<name>";
}
/// <inheritdoc />
protected override ExitCode Run(IList<string> parameters)
{
var res = Server.InstanceManager.SetInstanceEnabled(parameters[0], true);
if (res != null)
{
OutputProc(res);
return ExitCode.ServerError;
}
return ExitCode.Normal;
}
}
/// <summary>
/// Command for calling <see cref="ITGInstanceManager.SetInstanceEnabled(string, bool)"/> with a <see langword="false"/> parameter
/// </summary>
class ServiceDisableInstanceCommand : ConsoleCommand
{
/// <summary>
/// Construct a <see cref="ServiceDisableInstanceCommand"/>
/// </summary>
public ServiceDisableInstanceCommand()
{
Keyword = "disable-instance";
RequiredParameters = 1;
}
/// <inheritdoc />
public override string GetHelpText()
{
return "Disables the specified instance";
}
/// <inheritdoc />
public override string GetArgumentString()
{
return "<name>";
}
/// <inheritdoc />
protected override ExitCode Run(IList<string> parameters)
{
var res = Server.InstanceManager.SetInstanceEnabled(parameters[0], false);
if (res != null)
{
OutputProc(res);
return ExitCode.ServerError;
}
return ExitCode.Normal;
}
}
/// <summary>
/// Command for calling <see cref="ITGSService.RemoteAccessPort"/>
/// </summary>
class ServiceRemoteAccessPortCommand : ConsoleCommand
{
/// <summary>
/// Construct a <see cref="ServiceRemoteAccessPortCommand"/>
/// </summary>
public ServiceRemoteAccessPortCommand()
{
Keyword = "port";
}
/// <inheritdoc />
public override string GetHelpText()
{
return "Displays the service's remote access port";
}
/// <inheritdoc />
protected override ExitCode Run(IList<string> parameters)
{
OutputProc(Server.Management.RemoteAccessPort().ToString());
return ExitCode.Normal;
}
}
/// <summary>
/// Command for calling <see cref="TGS.Interface.Components.ITGSService.SetRemoteAccessPort(ushort)"/>
/// </summary>
class ServiceSetRemoteAccessPortCommand : ConsoleCommand
{
/// <summary>
/// Construct a <see cref="ServiceSetRemoteAccessPortCommand"/>
/// </summary>
public ServiceSetRemoteAccessPortCommand()
{
Keyword = "set-port";
RequiredParameters = 1;
}
/// <inheritdoc />
public override string GetHelpText()
{
return "Sets the service's remote access port";
}
/// <inheritdoc />
public override string GetArgumentString()
{
return "<port>";
}
/// <inheritdoc />
protected override ExitCode Run(IList<string> parameters)
{
ushort port;
try
{
port = Convert.ToUInt16(parameters[0]);
}
catch
{
OutputProc("Invalid port number!");
return ExitCode.BadCommand;
}
var res = Server.Management.SetRemoteAccessPort(port);
if (res != null)
{
OutputProc(res);
return ExitCode.ServerError;
}
OutputProc("Change will be applied after service restart");
return ExitCode.Normal;
}
}
/// <summary>
/// Command for calling <see cref="ITGInstanceManager.RenameInstance(string, string)"/>
/// </summary>
class ServiceRenameInstanceCommand : ConsoleCommand
{
/// <summary>
/// Construct a <see cref="ServiceRenameInstanceCommand"/>
/// </summary>
public ServiceRenameInstanceCommand()
{
Keyword = "rename-instance";
RequiredParameters = 1;
}
/// <inheritdoc />
public override string GetHelpText()
{
return "Renames an instance. Will temporarily disable the instance if it is active";
}
/// <inheritdoc />
public override string GetArgumentString()
{
return "<name> <new_name>";
}
/// <inheritdoc />
protected override ExitCode Run(IList<string> parameters)
{
var res = Server.InstanceManager.RenameInstance(parameters[0], parameters[1]);
if (res != null)
{
OutputProc(res);
return ExitCode.ServerError;
}
return ExitCode.Normal;
}
}
}
-74
View File
@@ -1,74 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{89191F69-B18E-4B59-B72E-E12F9B6811A0}</ProjectGuid>
<OutputType>Exe</OutputType>
<RootNamespace>TGS.CommandLine</RootNamespace>
<AssemblyName>TGCommandLine</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
</PropertyGroup>
<PropertyGroup>
<StartupObject />
</PropertyGroup>
<PropertyGroup>
<ApplicationIcon>tgs.ico</ApplicationIcon>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|AnyCPU'">
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<DocumentationFile>bin\x86\Release\TGS.CommandLine.xml</DocumentationFile>
<Optimize>true</Optimize>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<DebugType>pdbonly</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup>
<SignAssembly>false</SignAssembly>
</PropertyGroup>
<ItemGroup>
<Compile Include="AdminCommands.cs" />
<Compile Include="BYONDCommands.cs" />
<Compile Include="ConfigCommands.cs" />
<Compile Include="ConsoleCommand.cs" />
<Compile Include="DDCommands.cs" />
<Compile Include="DMCommands.cs" />
<Compile Include="ChatCommands.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="RepoCommands.cs" />
<Compile Include="RootCommands.cs" />
<Compile Include="..\AssemblyInfo.global.cs" />
<Compile Include="ServiceCommands.cs" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\TGS.Interface\TGS.Interface.csproj">
<Project>{ac4e7e8b-f83a-481c-a8b0-8fa4e8ae59ab}</Project>
<Name>TGS.Interface</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Content Include="tgs.ico" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
-71
View File
@@ -1,71 +0,0 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
<section name="TGS.ControlPanel.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false" />
<section name="TGS.ControlPanel.Properties.Settings1" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false" />
<section name="TGStationServer3.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false" />
</sectionGroup>
</configSections>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
</startup>
<userSettings>
<TGS.ControlPanel.Properties.Settings>
<setting name="LastPageIndex" serializeAs="String">
<value>0</value>
</setting>
<setting name="LastConfigPageIndex" serializeAs="String">
<value>0</value>
</setting>
<setting name="UpgradeRequired" serializeAs="String">
<value>True</value>
</setting>
<setting name="LastChatProvider" serializeAs="String">
<value>0</value>
</setting>
<setting name="RemoteDefault" serializeAs="String">
<value>False</value>
</setting>
<setting name="GitHubAPIKey" serializeAs="String">
<value />
</setting>
<setting name="GitHubAPIKeyEntropy" serializeAs="String">
<value />
</setting>
</TGS.ControlPanel.Properties.Settings>
<TGS.ControlPanel.Properties.Settings1>
<setting name="LastPageIndex" serializeAs="String">
<value>0</value>
</setting>
</TGS.ControlPanel.Properties.Settings1>
<TGStationServer3.Properties.Settings>
<setting name="RepoURL" serializeAs="String">
<value>https://github.com/tgstation/tgstation.git</value>
</setting>
<setting name="ProjectName" serializeAs="String">
<value>tgstation</value>
</setting>
<setting name="ServerPort" serializeAs="String">
<value>2337</value>
</setting>
<setting name="RepoBranch" serializeAs="String">
<value>master</value>
</setting>
<setting name="PushChangelogToGit" serializeAs="String">
<value>False</value>
</setting>
<setting name="ByondPath" serializeAs="String">
<value>C:\Program Files (x86)\BYOND\bin</value>
</setting>
<setting name="CommitterName" serializeAs="String">
<value>tgstation-server</value>
</setting>
<setting name="CommitterEmail" serializeAs="String">
<value>tgstation-server@users.noreply.github.com</value>
</setting>
</TGStationServer3.Properties.Settings>
</userSettings>
</configuration>
-100
View File
@@ -1,100 +0,0 @@
using System;
using System.Windows.Forms;
using TGS.Interface;
using TGS.Interface.Components;
namespace TGS.ControlPanel
{
partial class ControlPanel
{
string lastReadError = null;
void InitBYONDPage()
{
var BYOND = Instance.Byond;
var CV = BYOND.GetVersion(ByondVersion.Installed);
if (CV == null)
CV = BYOND.GetVersion(ByondVersion.Staged);
if (CV != null)
{
var splits = CV.Split('.');
if(splits.Length == 2)
{
try
{
var Major = Convert.ToInt32(splits[0]);
var Minor = Convert.ToInt32(splits[1]);
MajorVersionNumeric.Value = Major;
MinorVersionNumeric.Value = Minor;
}
catch { }
}
}
var latestVer = BYOND.GetVersion(ByondVersion.Latest);
LatestVersionLabel.Text = latestVer;
try
{
var splits = latestVer.Split('.');
var maj = Convert.ToInt32(splits[0]);
MinorVersionNumeric.Value = Convert.ToInt32(splits[1]);
MajorVersionNumeric.Value = maj;
}
catch { }
}
private void UpdateButton_Click(object sender, EventArgs e)
{
UpdateBYONDButtons();
if (!Instance.Byond.UpdateToVersion((int)MajorVersionNumeric.Value, (int)MinorVersionNumeric.Value))
MessageBox.Show("Unable to begin update, there is another operation in progress.");
}
private void BYONDRefreshButton_Click(object sender, EventArgs e)
{
UpdateBYONDButtons();
}
void UpdateBYONDButtons()
{
var BYOND = Instance.Byond;
VersionLabel.Text = BYOND.GetVersion(ByondVersion.Installed) ?? "Not Installed";
StagedVersionTitle.Visible = false;
StagedVersionLabel.Visible = false;
switch (BYOND.CurrentStatus())
{
case ByondStatus.Idle:
case ByondStatus.Starting:
StatusLabel.Text = "Idle";
UpdateButton.Enabled = true;
break;
case ByondStatus.Downloading:
StatusLabel.Text = "Downloading...";
UpdateButton.Enabled = false;
break;
case ByondStatus.Staging:
StatusLabel.Text = "Staging...";
UpdateButton.Enabled = false;
break;
case ByondStatus.Staged:
StagedVersionTitle.Visible = true;
StagedVersionLabel.Visible = true;
StagedVersionLabel.Text = BYOND.GetVersion(ByondVersion.Staged) ?? "Unknown";
StatusLabel.Text = "Staged and waiting for BYOND to shutdown...";
UpdateButton.Enabled = true;
break;
case ByondStatus.Updating:
StatusLabel.Text = "Applying update...";
UpdateButton.Enabled = false;
break;
}
var error = Instance.Byond.GetError();
if (error != lastReadError)
{
lastReadError = error;
if (error != null)
MessageBox.Show("An error occurred: " + lastReadError);
}
}
}
}
-218
View File
@@ -1,218 +0,0 @@
using System;
using System.Collections.Generic;
using System.Windows.Forms;
using TGS.Interface;
using TGS.Interface.Components;
namespace TGS.ControlPanel
{
partial class ControlPanel
{
bool updatingChat = false;
ChatProvider ModifyingProvider
{
get { return (ChatProvider)Properties.Settings.Default.LastChatProvider; }
set { Properties.Settings.Default.LastChatProvider = (int)value; }
}
void LoadChatPage()
{
updatingChat = true;
var Chat = Instance.Chat;
var PI = Chat.ProviderInfos()[(int)ModifyingProvider];
ChatAdminsTextBox.Visible = true;
IRCModesComboBox.Visible = false;
switch (ModifyingProvider)
{
case ChatProvider.Discord:
var DPI = new DiscordSetupInfo(PI);
DiscordProviderSwitch.Select();
AuthField1.Text = DPI.BotToken; //it's invisible so whatever
AuthField1Title.Text = "Bot Token:";
AuthField2.Visible = false;
AuthField2Title.Visible = false;
ChatServerText.Visible = false;
ChatPortSelector.Visible = false;
ChatServerTitle.Visible = false;
ChatPortTitle.Visible = false;
ChatNicknameText.Visible = false;
ChatNicknameTitle.Visible = false;
ChatAdminsTitle.Text = String.Format("Admin {0} IDs:", DPI.AdminsAreSpecial ? "Role" : "User");
ChannelsTitle.Text = "Broadcast/Listening Channel IDs:";
AdminModeNormal.Text = "User IDs";
AdminModeSpecial.Text = "Role IDs";
break;
case ChatProvider.IRC:
var IRC = new IRCSetupInfo(PI);
IRCProviderSwitch.Select();
AuthField1.Text = IRC.AuthTarget;
AuthField2.Text = IRC.AuthMessage;
AuthField2.Visible = true;
AuthField2Title.Visible = true;
AuthField1Title.Text = "Auth Target:";
AuthField2Title.Text = "Auth Message:";
ChatServerText.Visible = true;
ChatPortSelector.Visible = true;
ChatServerTitle.Visible = true;
ChatPortTitle.Visible = true;
ChatServerText.Text = IRC.URL;
ChatPortSelector.Value = IRC.Port;
ChatNicknameText.Visible = true;
ChatNicknameTitle.Visible = true;
ChatNicknameText.Text = IRC.Nickname;
ChatAdminsTitle.Text = String.Format("Admin {0}:", IRC.AdminsAreSpecial ? "Req Mode" : "Nicknames");
ChannelsTitle.Text = "Broadcast/Listening Channels:";
AdminModeNormal.Text = "Nicknames";
AdminModeSpecial.Text = "Channel Mode";
if (IRC.AdminsAreSpecial)
{
ChatAdminsTextBox.Visible = false;
IRCModesComboBox.Visible = true;
IRCModesComboBox.SelectedIndex = (int)IRC.AuthLevel;
}
break;
default:
Properties.Settings.Default.LastChatProvider = (int)ChatProvider.IRC;
LoadChatPage();
return;
}
AdminModeNormal.Checked = !PI.AdminsAreSpecial;
AdminModeSpecial.Checked = PI.AdminsAreSpecial;
ChatEnabledCheckbox.Checked = PI.Enabled;
if (!PI.Enabled)
ChatStatusLabel.Text = "Disabled";
else if (Chat.Connected(ModifyingProvider))
ChatStatusLabel.Text = "Connected";
else
ChatStatusLabel.Text = "Disconnected";
ChatReconnectButton.Enabled = PI.Enabled;
AssignListToTextbox(PI.AdminList, ChatAdminsTextBox);
AssignListToTextbox(PI.WatchdogChannels, WDChannelsTextbox);
AssignListToTextbox(PI.AdminChannels, AdminChannelsTextbox);
AssignListToTextbox(PI.DevChannels, DevChannelsTextbox);
AssignListToTextbox(PI.GameChannels, GameChannelsTextbox);
updatingChat = false;
}
static void AssignListToTextbox(IList<string> a, TextBox b)
{
b.Text = "";
foreach (var I in a)
b.Text += I + Environment.NewLine;
}
private void ChatRefreshButton_Click(object sender, EventArgs e)
{
LoadChatPage();
}
private void ChatReconnectButton_Click(object sender, EventArgs e)
{
Instance.Chat.Reconnect(ModifyingProvider);
LoadChatPage();
}
static string[] SplitByLine(TextBox t)
{
var channels = t.Text.Split('\n');
var finalChannels = new List<string>();
foreach (var I in channels)
{
var trimmed = I.Trim();
if(trimmed != "")
finalChannels.Add(trimmed);
}
return finalChannels.ToArray();
}
private void DiscordProviderSwitch_CheckedChanged(object sender, EventArgs e)
{
if (!updatingChat && DiscordProviderSwitch.Checked)
{
ModifyingProvider = ChatProvider.Discord;
LoadChatPage();
}
}
private void IRCProviderSwitch_CheckedChanged(object sender, EventArgs e)
{
if (!updatingChat && IRCProviderSwitch.Checked)
{
ModifyingProvider = ChatProvider.IRC;
LoadChatPage();
}
}
void SetAdminsAreSpecial(bool value)
{
var Chat = Instance.Chat;
var PI = Chat.ProviderInfos()[(int)ModifyingProvider];
PI.AdminsAreSpecial = value;
var res = Chat.SetProviderInfo(PI);
if (res != null)
MessageBox.Show(res);
LoadChatPage();
}
private void AdminModeNormal_CheckedChanged(object sender, EventArgs e)
{
if (!updatingChat && AdminModeNormal.Checked)
SetAdminsAreSpecial(false);
}
private void AdminModeSpecial_CheckedChanged(object sender, EventArgs e)
{
if (!updatingChat && AdminModeSpecial.Checked)
SetAdminsAreSpecial(true);
}
private void ChatApplyButton_Click(object sender, EventArgs e)
{
string res = null;
ChatSetupInfo wip = null;
switch (ModifyingProvider)
{
case ChatProvider.Discord:
wip = new DiscordSetupInfo()
{
BotToken = AuthField1.Text
};
break;
case ChatProvider.IRC:
wip = new IRCSetupInfo()
{
AuthMessage = AuthField2.Text,
AuthTarget = AuthField1.Text,
Nickname = ChatNicknameText.Text,
URL = ChatServerText.Text,
Port = (ushort)ChatPortSelector.Value,
AuthLevel = (IRCMode)IRCModesComboBox.SelectedIndex,
};
break;
default:
res = "You really shouldn't be able to read this.";
break;
}
if (res == null)
{
wip.AdminChannels = new List<string>(AdminChannelsTextbox.Text.Split(Environment.NewLine.ToCharArray()));
wip.WatchdogChannels = new List<string>(WDChannelsTextbox.Text.Split(Environment.NewLine.ToCharArray()));
wip.DevChannels = new List<string>(DevChannelsTextbox.Text.Split(Environment.NewLine.ToCharArray()));
wip.GameChannels = new List<string>(GameChannelsTextbox.Text.Split(Environment.NewLine.ToCharArray()));
wip.AdminList = new List<string>(ChatAdminsTextBox.Text.Split(Environment.NewLine.ToCharArray()));
wip.Enabled = ChatEnabledCheckbox.Checked;
wip.AdminsAreSpecial = AdminModeSpecial.Checked;
res = Instance.Chat.SetProviderInfo(wip);
}
if (res != null)
MessageBox.Show(res);
LoadChatPage();
}
}
}
File diff suppressed because it is too large Load Diff
@@ -1,116 +0,0 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using TGS.Interface;
namespace TGS.ControlPanel
{
/// <summary>
/// The main <see cref="ControlPanel"/> form
/// </summary>
sealed partial class ControlPanel : CountedForm
{
/// <summary>
/// List of instances being used by open control panels
/// </summary>
public static IDictionary<string, ControlPanel> InstancesInUse { get; private set; } = new Dictionary<string, ControlPanel>();
/// <summary>
/// The <see cref="IInstance"/> for this <see cref="ControlPanel"/>
/// </summary>
readonly IInstance Instance;
/// <summary>
/// Constructs a <see cref="ControlPanel"/>
/// </summary>
/// <param name="server">The <see cref="IServer"/> to use</param>
/// <param name="instance">The <see cref="IInstance"/> to use</param>
public ControlPanel(IServer server, IInstance instance)
{
InitializeComponent();
FormClosed += ControlPanel_FormClosed;
Panels.SelectedIndexChanged += Panels_SelectedIndexChanged;
Panels.SelectedIndex += Math.Min(Properties.Settings.Default.LastPageIndex, Panels.TabCount - 1);
Instance = instance;
InstancesInUse.Add(Instance.Metadata.Name, this);
Text = String.Format("TGS {0} Instance: {1}", server.Version, Instance.Metadata.Name);
InitRepoPage();
InitBYONDPage();
InitServerPage();
UpdateSelectedPanel();
}
/// <summary>
/// Called when the <see cref="ControlPanel"/> is closed
/// </summary>
/// <param name="sender">The sender of the event</param>
/// <param name="e">The <see cref="EventArgs"/></param>
void ControlPanel_FormClosed(object sender, FormClosedEventArgs e)
{
InstancesInUse.Remove(Instance.Metadata.Name);
}
/// <summary>
/// Called from <see cref="Dispose(bool)"/>
/// </summary>
void Cleanup()
{
InstancesInUse.Remove(Instance.Metadata.Name);
}
private void Main_Resize(object sender, EventArgs e)
{
Panels.Location = new Point(10, 10);
Panels.Width = ClientSize.Width - 20;
Panels.Height = ClientSize.Height - 20;
}
/// <summary>
/// Updates the content of <see cref="TabControl.SelectedTab"/> of <see cref="Panels"/>
/// </summary>
void UpdateSelectedPanel()
{
switch (Panels.SelectedIndex)
{
case 0: //repo
PopulateRepoFields();
break;
case 1: //byond
UpdateBYONDButtons();
break;
case 2: //scp
LoadServerPage();
break;
case 3: //chat
LoadChatPage();
break;
case 4: //static
InitStaticPage();
break;
}
Properties.Settings.Default.LastPageIndex = Panels.SelectedIndex;
}
void Panels_SelectedIndexChanged(object sender, EventArgs e)
{
UpdateSelectedPanel();
}
bool CheckAdminWithWarning()
{
if (Instance.Administration != null)
{
MessageBox.Show("Only system administrators may use this command!");
return false;
}
return true;
}
}
}
File diff suppressed because it is too large Load Diff
-352
View File
@@ -1,352 +0,0 @@
using System;
using System.ComponentModel;
using System.Windows.Forms;
using System.Threading;
using TGS.Interface;
using TGS.Interface.Components;
namespace TGS.ControlPanel
{
partial class ControlPanel
{
enum RepoAction {
Clone,
Checkout,
Update,
Merge,
Reset,
Test,
Wait,
GenCL,
}
RepoAction action;
string CloneRepoURL;
string CheckoutBranch;
int TestPR;
string repoError;
private void InitRepoPage()
{
RepoBGW.ProgressChanged += RepoBGW_ProgressChanged;
RepoBGW.RunWorkerCompleted += RepoBGW_RunWorkerCompleted;
RepoBGW.DoWork += RepoBGW_DoWork;
BackupTagsList.MouseDoubleClick += BackupTagsList_MouseDoubleClick;
}
private void BackupTagsList_MouseDoubleClick(object sender, MouseEventArgs e)
{
int index = BackupTagsList.IndexFromPoint(e.Location);
if (index != ListBox.NoMatches)
{
var indexText = (string)BackupTagsList.Items[index];
if (indexText == "None" || indexText == "Unknown")
return;
var tagname = indexText.Split(':')[0];
var spaceSplits = indexText.Split(' ');
var sha = spaceSplits[spaceSplits.Length - 1];
if (MessageBox.Show(String.Format("Checkout tag {0} ({1})?", tagname, sha), "Restore Backup", MessageBoxButtons.YesNo) != DialogResult.Yes)
return;
CheckoutBranch = tagname;
DoAsyncOp(RepoAction.Checkout, "Checking out " + tagname + "...");
}
}
private void RepoBGW_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
RepoProgressBar.Value = 100;
RepoProgressBar.Style = ProgressBarStyle.Blocks;
RepoPanel.UseWaitCursor = false;
PopulateRepoFields();
}
private void RepoRefreshButton_Click(object sender, EventArgs e)
{
PopulateRepoFields();
}
private void PopulateRepoFields()
{
if (repoError != null)
MessageBox.Show("An error occured: " + repoError);
if (RepoBusyCheck())
return;
var Repo = Instance.Repository;
RepoProgressBar.Style = ProgressBarStyle.Marquee;
RepoProgressBar.Visible = false;
RemoteNameTitle.Visible = true;
RepoRemoteTextBox.Visible = true;
BranchNameTitle.Visible = true;
RepoBranchTextBox.Visible = true;
RepoRefreshButton.Visible = true;
SyncCommitsCheckBox.Visible = true;
SyncCommitsCheckBox.Checked = Repo.PushTestmergeCommits();
if (!Repo.Exists())
{
//repo unavailable
RepoRemoteTextBox.Text = "git://github.com/tgstation/tgstation.git";
RepoBranchTextBox.Text = "master";
RepoProgressBarLabel.Text = "Unable to locate repository";
CloneRepositoryButton.Visible = true;
}
else
{
RepoProgressBarLabel.Visible = false;
CurrentRevisionLabel.Visible = true;
CurrentRevisionTitle.Visible = true;
IdentityLabel.Visible = true;
MergePRButton.Visible = true;
TestMergeListLabel.Visible = true;
TestMergeListTitle.Visible = true;
UpdateRepoButton.Visible = true;
BackupTagsList.Visible = true;
HardReset.Visible = true;
RepoApplyButton.Visible = true;
TestmergeSelector.Visible = true;
RepoGenChangelogButton.Visible = true;
RecloneButton.Visible = true;
ResetRemote.Visible = true;
TGSJsonUpdate.Visible = true;
CurrentRevisionLabel.Text = Repo.GetHead(false, out string error) ?? "Unknown";
RepoRemoteTextBox.Text = Repo.GetRemote(out error) ?? "Unknown";
RepoBranchTextBox.Text = Repo.GetBranch(out error) ?? "Unknown";
var Backups = Repo.ListBackups(out error);
BackupTagsList.Items.Clear();
if (Backups != null)
{
if (Backups.Count == 0)
BackupTagsList.Items.Add("None");
else
foreach (var I in Backups)
BackupTagsList.Items.Add(I.Key + ": " + I.Value);
}
else
BackupTagsList.Items.Add("Unknown");
var PRs = Repo.MergedPullRequests(out error);
TestMergeListLabel.Items.Clear();
if (PRs != null)
if (PRs.Count == 0)
TestMergeListLabel.Items.Add("None");
else
foreach (var I in PRs)
TestMergeListLabel.Items.Add(String.Format("#{0}: {2} by {3} at commit {1}\r\n", I.Number, I.Sha, I.Title, I.Author));
else
TestMergeListLabel.Items.Add("Unknown");
}
}
bool RepoBusyCheck()
{
if (Instance.Repository.OperationInProgress())
{
DoAsyncOp(RepoAction.Wait, "Waiting for repository to finish another action...");
return true;
}
return false;
}
private void RepoBGW_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
var val = e.ProgressPercentage;
if (val < 0)
{
RepoProgressBar.Style = ProgressBarStyle.Marquee;
return;
}
RepoProgressBar.Style = ProgressBarStyle.Blocks;
RepoProgressBar.Value = val;
}
private void RepoBGW_DoWork(object sender, DoWorkEventArgs e)
{
//Only for clones
var Repo = Instance.Repository;
switch (action) {
case RepoAction.Clone:
repoError = Repo.Setup(CloneRepoURL, CheckoutBranch);
break;
case RepoAction.Checkout:
repoError = Repo.Checkout(CheckoutBranch);
break;
case RepoAction.Merge:
repoError = Repo.Update(false);
break;
case RepoAction.Reset:
repoError = Repo.Reset(true);
break;
case RepoAction.Test:
repoError = Repo.MergePullRequest(TestPR);
break;
case RepoAction.Update:
repoError = Repo.Update(true);
break;
case RepoAction.Wait:
break;
case RepoAction.GenCL:
var result = Repo.GenerateChangelog(out repoError);
if(repoError != null)
repoError += ": " + result;
break;
default:
//reeee
return;
}
do
{
Thread.Sleep(1000);
RepoBGW.ReportProgress(Repo.CheckoutProgress());
} while (Repo.OperationInProgress());
}
private void CloneRepositoryButton_Click(object sender, EventArgs e)
{
CloneRepo();
}
void CloneRepo()
{
CloneRepoURL = RepoRemoteTextBox.Text;
CheckoutBranch = RepoBranchTextBox.Text;
DoAsyncOp(RepoAction.Clone, String.Format("Cloning {0} branch of {1}...", CheckoutBranch, CloneRepoURL));
}
private void RecloneButton_Click(object sender, EventArgs e)
{
var DialogResult = MessageBox.Show("This will re-clone the repository, backup, and reset the Static configuration folders. Continue?", "Confim", MessageBoxButtons.YesNo);
if (DialogResult == DialogResult.No)
return;
CloneRepo();
}
void DoAsyncOp(RepoAction ra, string message)
{
if (RepoBGW.IsBusy || (ra != RepoAction.Wait && RepoBusyCheck()))
return;
SyncCommitsCheckBox.Visible = false;
CurrentRevisionLabel.Visible = false;
CurrentRevisionTitle.Visible = false;
TestMergeListLabel.Visible = false;
TestMergeListTitle.Visible = false;
RepoApplyButton.Visible = false;
UpdateRepoButton.Visible = false;
MergePRButton.Visible = false;
CloneRepositoryButton.Visible = false;
RemoteNameTitle.Visible = false;
RepoRemoteTextBox.Visible = false;
BranchNameTitle.Visible = false;
RepoBranchTextBox.Visible = false;
RepoProgressBar.Visible = true;
HardReset.Visible = false;
IdentityLabel.Visible = false;
TestmergeSelector.Visible = false;
RepoGenChangelogButton.Visible = false;
RecloneButton.Visible = false;
ResetRemote.Visible = false;
BackupTagsList.Visible = false;
RepoRefreshButton.Visible = false;
TGSJsonUpdate.Visible = false;
RepoPanel.UseWaitCursor = true;
RepoProgressBar.Value = 0;
RepoProgressBar.Style = ProgressBarStyle.Marquee;
RepoProgressBarLabel.Text = message;
RepoProgressBarLabel.Visible = true;
action = ra;
repoError = null;
RepoBGW.RunWorkerAsync();
}
private void RepoApplyButton_Click(object sender, EventArgs e)
{
var Repo = Instance.Repository;
if (RepoBusyCheck())
return;
var remote = Repo.GetRemote(out string error);
if (remote == null) {
MessageBox.Show("Error: " + error);
return;
}
var Reclone = remote != RepoRemoteTextBox.Text;
if (Reclone)
{
var DialogResult = MessageBox.Show("Changing the remote URL requires a re-cloning of the repository. Continue?", "Confim", MessageBoxButtons.YesNo);
if (DialogResult == DialogResult.No)
return;
}
if (!Reclone)
{
Repo.SetPushTestmergeCommits(SyncCommitsCheckBox.Checked);
var branch = Repo.GetBranch(out error);
if(branch == null)
{
MessageBox.Show("Error: " + error);
return;
}
CheckoutBranch = RepoBranchTextBox.Text;
if(branch != CheckoutBranch)
DoAsyncOp(RepoAction.Checkout, String.Format("Checking out {0}...", CheckoutBranch));
}
else
CloneRepositoryButton_Click(null, null);
}
private void UpdateRepoButton_Click(object sender, EventArgs e)
{
DoAsyncOp(RepoAction.Merge, "Merging origin branch...");
}
private void HardReset_Click(object sender, EventArgs e)
{
DoAsyncOp(RepoAction.Reset, "Resetting to origin branch...");
}
private void ResetRemote_Click(object sender, EventArgs e)
{
DoAsyncOp(RepoAction.Update, "Updating and resetting to remote branch...");
}
private void TestMergeButton_Click(object sender, EventArgs e)
{
if (TestmergeSelector.Value == 0)
{
MessageBox.Show("Invalid PR number!");
return;
}
TestPR = (int)TestmergeSelector.Value;
DoAsyncOp(RepoAction.Test, String.Format("Merging latest commit of PR #{0}...", TestPR));
}
private void RepoGenChangelogButton_Click(object sender, System.EventArgs e)
{
DoAsyncOp(RepoAction.GenCL, "Generating changelog...");
}
private void TGSJsonUpdate_Click(object sender, EventArgs e)
{
if (MessageBox.Show("This will update the cached TGS3.json to the current repository version, potentially redefining symlinks. Proceed?", "Json Update", MessageBoxButtons.YesNo) != DialogResult.Yes)
return;
var res = Instance.Repository.UpdateTGS3Json();
if (res != null)
MessageBox.Show(res);
}
}
}
-501
View File
@@ -1,501 +0,0 @@
using Octokit;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using System.Windows.Forms;
using TGS.Interface;
using TGS.Interface.Components;
namespace TGS.ControlPanel
{
partial class ControlPanel
{
bool updatingFields = false;
/// <summary>
/// <see cref="GitHubClient"/> used for checking the merged state of <see cref="PullRequest"/>s
/// </summary>
GitHubClient ghclient;
void InitServerPage()
{
projectNameText.LostFocus += ProjectNameText_LostFocus;
projectNameText.KeyDown += ProjectNameText_KeyDown;
ServerStartBGW.RunWorkerCompleted += ServerStartBGW_RunWorkerCompleted;
ghclient = new GitHubClient(new ProductHeaderValue(Assembly.GetExecutingAssembly().GetName().Name));
var config = Properties.Settings.Default;
if (!String.IsNullOrWhiteSpace(config.GitHubAPIKey))
ghclient.Credentials = new Credentials(Helpers.DecryptData(config.GitHubAPIKey, config.GitHubAPIKeyEntropy));
}
private void ServerStartBGW_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Result != null)
MessageBox.Show((string)e.Result);
LoadServerPage();
}
private void ProjectNameText_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
UpdateProjectName();
}
private void CompileCancelButton_Click(object sender, EventArgs e)
{
var res = Instance.Compiler.Cancel();
if (res != null)
MessageBox.Show(res);
LoadServerPage();
}
void LoadServerPage()
{
var RepoExists = Instance.Repository.Exists();
compileButton.Visible = RepoExists;
AutoUpdateCheckbox.Visible = RepoExists;
initializeButton.Visible = RepoExists;
AutostartCheckbox.Visible = RepoExists;
WebclientCheckBox.Visible = RepoExists;
PortSelector.Visible = RepoExists;
projectNameText.Visible = RepoExists;
CompilerStatusLabel.Visible = RepoExists;
CompileCancelButton.Visible = RepoExists;
CompilerLabel.Visible = RepoExists;
ProjectPathLabel.Visible = RepoExists;
ServerGStopButton.Visible = RepoExists;
ServerStartButton.Visible = RepoExists;
ServerGRestartButton.Visible = RepoExists;
ServerRestartButton.Visible = RepoExists;
PortLabel.Visible = RepoExists;
ServerStopButton.Visible = RepoExists;
TestMergeManagerButton.Visible = RepoExists;
UpdateServerButton.Visible = RepoExists;
RemoveAllTestMergesButton.Visible = RepoExists;
WorldAnnounceField.Visible = RepoExists;
WorldAnnounceButton.Visible = RepoExists;
WorldAnnounceLabel.Visible = RepoExists;
SyncCommitsCheckBox.Visible = RepoExists;
if (updatingFields)
return;
var DM = Instance.Compiler;
var DD = Instance.DreamDaemon;
var Config = Instance.Config;
var Repo = Instance.Repository;
try
{
updatingFields = true;
ServerPathLabel.Text = "Server Path: " + Instance.ServerDirectory();
SecuritySelector.SelectedIndex = (int)DD.SecurityLevel();
if (!RepoExists)
return;
var interval = Repo.AutoUpdateInterval();
var interval_not_zero = interval != 0;
AutoUpdateCheckbox.Checked = interval_not_zero;
AutoUpdateInterval.Visible = interval_not_zero;
AutoUpdateMLabel.Visible = interval_not_zero;
if (interval_not_zero)
AutoUpdateInterval.Value = interval;
var DaeStat = DD.DaemonStatus();
var Online = DaeStat == DreamDaemonStatus.Online;
ServerStartButton.Enabled = !Online;
ServerGStopButton.Enabled = Online;
ServerGRestartButton.Enabled = Online;
ServerStopButton.Enabled = Online;
ServerRestartButton.Enabled = Online;
switch (DaeStat)
{
case DreamDaemonStatus.HardRebooting:
ServerStatusLabel.Text = "REBOOTING";
break;
case DreamDaemonStatus.Offline:
ServerStatusLabel.Text = "OFFLINE";
break;
case DreamDaemonStatus.Online:
ServerStatusLabel.Text = "ONLINE";
var pc = DD.PlayerCount();
if (pc != -1)
ServerStatusLabel.Text += " (" + pc + " players)";
break;
}
ServerGStopButton.Checked = DD.ShutdownInProgress();
AutostartCheckbox.Checked = DD.Autostart();
WebclientCheckBox.Checked = DD.Webclient();
if (!PortSelector.Focused)
PortSelector.Value = DD.Port();
if (!projectNameText.Focused)
projectNameText.Text = DM.ProjectName();
UpdateServerButton.Enabled = false;
TestMergeManagerButton.Enabled = false;
RemoveAllTestMergesButton.Enabled = false;
switch (DM.GetStatus())
{
case CompilerStatus.Compiling:
CompilerStatusLabel.Text = "Compiling...";
compileButton.Enabled = false;
initializeButton.Enabled = false;
CompileCancelButton.Enabled = true;
break;
case CompilerStatus.Initializing:
CompilerStatusLabel.Text = "Initializing...";
compileButton.Enabled = false;
initializeButton.Enabled = false;
CompileCancelButton.Enabled = false;
break;
case CompilerStatus.Initialized:
CompilerStatusLabel.Text = "Idle";
initializeButton.Enabled = true;
compileButton.Enabled = true;
CompileCancelButton.Enabled = false;
UpdateServerButton.Enabled = true;
TestMergeManagerButton.Enabled = true;
RemoveAllTestMergesButton.Enabled = true;
break;
case CompilerStatus.Uninitialized:
CompilerStatusLabel.Text = "Uninitialized";
compileButton.Enabled = false;
initializeButton.Enabled = true;
CompileCancelButton.Enabled = false;
break;
default:
CompilerStatusLabel.Text = "Unknown!";
initializeButton.Enabled = true;
compileButton.Enabled = true;
CompileCancelButton.Enabled = true;
break;
}
var error = DM.CompileError();
if (error != null)
MessageBox.Show("Error: " + error);
}
finally
{
updatingFields = false;
}
}
private void ProjectNameText_LostFocus(object sender, EventArgs e)
{
UpdateProjectName();
}
void UpdateProjectName()
{
if (!updatingFields)
Instance.Compiler.SetProjectName(projectNameText.Text);
}
private void PortSelector_ValueChanged(object sender, EventArgs e)
{
if (!updatingFields)
Instance.DreamDaemon.SetPort((ushort)PortSelector.Value);
}
private void ServerPageRefreshButton_Click(object sender, EventArgs e)
{
LoadServerPage();
}
private void InitializeButton_Click(object sender, EventArgs e)
{
if (!Instance.Compiler.Initialize())
MessageBox.Show("Unable to start initialization!");
LoadServerPage();
}
private void CompileButton_Click(object sender, EventArgs e)
{
if (!Instance.Compiler.Compile())
MessageBox.Show("Unable to start compilation!");
LoadServerPage();
}
private void AutostartCheckbox_CheckedChanged(object sender, System.EventArgs e)
{
if (!updatingFields)
Instance.DreamDaemon.SetAutostart(AutostartCheckbox.Checked);
}
private void ServerStartButton_Click(object sender, System.EventArgs e)
{
if (!ServerStartBGW.IsBusy)
ServerStartBGW.RunWorkerAsync();
}
private void ServerStartBGW_DoWork(object sender, DoWorkEventArgs e)
{
try
{
e.Result = Instance.DreamDaemon.Start();
}
catch (Exception ex)
{
e.Result = ex.ToString();
}
}
private void ServerStopButton_Click(object sender, EventArgs e)
{
var DialogResult = MessageBox.Show("This will immediately shut down the server. Continue?", "Confim", MessageBoxButtons.YesNo);
if (DialogResult == DialogResult.No)
return;
var res = Instance.DreamDaemon.Stop();
if (res != null)
MessageBox.Show(res);
}
private void ServerRestartButton_Click(object sender, EventArgs e)
{
var DialogResult = MessageBox.Show("This will immediately restart the server. Continue?", "Confim", MessageBoxButtons.YesNo);
if (DialogResult == DialogResult.No)
return;
var res = Instance.DreamDaemon.Restart();
if (res != null)
MessageBox.Show(res);
}
private void ServerGStopButton_Checked(object sender, EventArgs e)
{
if (updatingFields)
return;
var DialogResult = MessageBox.Show("This will shut down the server when the current round ends. Continue?", "Confim", MessageBoxButtons.YesNo);
if (DialogResult == DialogResult.No)
return;
Instance.DreamDaemon.RequestStop();
LoadServerPage();
}
private void ServerGRestartButton_Click(object sender, EventArgs e)
{
var DialogResult = MessageBox.Show("This will restart the server when the current round ends. Continue?", "Confim", MessageBoxButtons.YesNo);
if (DialogResult == DialogResult.No)
return;
Instance.DreamDaemon.RequestRestart();
}
/// <summary>
/// Launches the <see cref="TestMergeManager"/>
/// </summary>
/// <param name="sender">The sender of the event</param>
/// <param name="e">The <see cref="EventArgs"/></param>
void TestMergeManagerButton_Click(object sender, System.EventArgs e)
{
using (var TMM = new TestMergeManager(Instance, ghclient))
TMM.ShowDialog();
LoadServerPage();
}
/// <summary>
/// Calls <see cref="UpdateServer"/>
/// </summary>
/// <param name="sender">The sender of the event</param>
/// <param name="e">The <see cref="EventArgs"/></param>
void UpdateServerButton_Click(object sender, EventArgs e)
{
UpdateServer();
}
/// <summary>
/// Calls <see cref="ITGRepository.Update(bool)"/> with a <see langword="true"/> parameter, re-merging any current <see cref="PullRequest"/>s at their current commit, calls <see cref="ITGRepository.GenerateChangelog(out string)"/> and <see cref="ITGRepository.SynchronizePush"/>, and starts the <see cref="ITGCompiler.Compile(bool)"/> prompting the user with any errors that may occur. Merged <see cref="PullRequest"/>s are not remerged
/// </summary>
async void UpdateServer()
{
try
{
UseWaitCursor = true;
Enabled = false;
try
{
string res = null;
var repo = Instance.Repository;
var pulls = await Task.Run(() => repo.MergedPullRequests(out res));
if (pulls == null)
{
MessageBox.Show(res);
return;
}
List<Task<PullRequest>> pullsRequests = null;
if (Program.GetRepositoryRemote(repo, out string remoteOwner, out string remoteName))
{
//find out which of the PRs have been merged
pullsRequests = new List<Task<PullRequest>>();
foreach (var I in pulls)
pullsRequests.Add(ghclient.PullRequest.Get(remoteOwner, remoteName, I.Number));
}
res = await Task.Run(() => repo.Update(true));
if (res != null)
{
MessageBox.Show(res, "Error updating repository");
return;
}
await Task.Run(() => repo.GenerateChangelog(out res));
if (res != null)
MessageBox.Show(res, "Error generating changelog");
res = await Task.Run(() => repo.SynchronizePush());
if (res != null)
MessageBox.Show(res, "Error synchronizing commits");
if (pullsRequests != null)
Task.WaitAll(pullsRequests.ToArray());
foreach (var I in pullsRequests)
if (I.Result.Merged)
pulls.RemoveAll(x => x.Number == I.Result.Number);
var mergeResults = await Task.Run(() => repo.MergePullRequests(pulls, true));
var compileStartResult = await Task.Run(() => Instance.Compiler.Compile(true));
//Show any errors
for (var I = 0; I < mergeResults.Count(); ++I)
{
var err = mergeResults.ElementAt(I);
if (err != null)
MessageBox.Show(err, String.Format("Error re-merging PR #{0}", pulls[I].Number));
}
if (!compileStartResult)
MessageBox.Show(res, "Error starting compile!");
}
finally
{
UseWaitCursor = false;
Enabled = true;
}
}
catch (ForbiddenException)
{
if (ghclient.Credentials.AuthenticationType == AuthenticationType.Anonymous)
{
if (Program.RateLimitPrompt(ghclient))
UpdateServer();
return;
}
else
throw;
}
LoadServerPage();
}
/// <summary>
/// Calls <see cref="ITGRepository.Reset(bool)"/> with a <see langword="true"/> parameter, calls <see cref="ITGRepository.GenerateChangelog(out string)"/>, and starts the <see cref="ITGCompiler.Compile(bool)"/> prompting the user with any errors that may occur.
/// </summary>
/// <param name="sender">The sender of the event</param>
/// <param name="e">The <see cref="EventArgs"/></param>
async void RemoveAllTestMergesButton_Click(object sender, EventArgs e)
{
if (MessageBox.Show("Are you sure you want to remove all test merges?", "Confirm", MessageBoxButtons.YesNo) != DialogResult.Yes)
return;
try
{
UseWaitCursor = true;
Enabled = false;
try
{
var repo = Instance.Repository;
var res = await Task.Run(() => repo.Reset(true));
if (res != null)
{
MessageBox.Show(res, "Error resetting repository");
return;
}
await Task.Run(() => repo.GenerateChangelog(out res));
if (res != null)
MessageBox.Show(res, "Error generating changelog");
await Task.Run(() => Instance.Compiler.Compile(false));
if (res != null)
MessageBox.Show(res, "Error starting compile!");
}
finally
{
UseWaitCursor = false;
Enabled = true;
}
}
catch (ForbiddenException)
{
if (ghclient.Credentials.AuthenticationType == AuthenticationType.Anonymous)
{
if (Program.RateLimitPrompt(ghclient))
UpdateServer();
return;
}
else
throw;
}
LoadServerPage();
}
private void SecuritySelector_SelectedIndexChanged(object sender, EventArgs e)
{
if (!updatingFields)
if (!Instance.DreamDaemon.SetSecurityLevel((DreamDaemonSecurity)SecuritySelector.SelectedIndex))
MessageBox.Show("Security change will be applied after next server reboot.");
}
private void WorldAnnounceButton_Click(object sender, EventArgs e)
{
var msg = WorldAnnounceField.Text;
if (!String.IsNullOrWhiteSpace(msg))
{
var res = Instance.DreamDaemon.WorldAnnounce(msg);
if (res != null)
{
MessageBox.Show(res);
return;
}
}
WorldAnnounceField.Text = "";
}
private void WebclientCheckBox_CheckedChanged(object sender, EventArgs e)
{
if (!updatingFields)
Instance.DreamDaemon.SetWebclient(WebclientCheckBox.Checked);
}
private void AutoUpdateInterval_ValueChanged(object sender, EventArgs e)
{
if (!updatingFields)
Instance.Repository.SetAutoUpdateInterval((ulong)AutoUpdateInterval.Value);
}
private void AutoUpdateCheckbox_CheckedChanged(object sender, EventArgs e)
{
if (updatingFields)
return;
var on = AutoUpdateCheckbox.Visible && AutoUpdateCheckbox.Checked;
AutoUpdateInterval.Visible = on;
AutoUpdateMLabel.Visible = on;
if (!on)
Instance.Repository.SetAutoUpdateInterval(0);
else
Instance.Repository.SetAutoUpdateInterval((ulong)AutoUpdateInterval.Value);
}
}
}
-356
View File
@@ -1,356 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Windows.Forms;
using TGS.Interface.Components;
namespace TGS.ControlPanel
{
partial class ControlPanel
{
IDictionary<int, string> IndexesToPaths = new Dictionary<int, string>();
string originalCurrentFileContent;
IList<string> EnumeratedPaths = new List<string>() { "" };
bool enumerating = false;
/// <summary>
/// Whether or not the static page has been initialized yet
/// </summary>
bool initializedStaticPage = false;
enum EnumResult
{
Enumerated,
Unauthorized,
NotEnumerated,
Error,
}
void InitStaticPage()
{
if (initializedStaticPage)
return;
if(Instance.Administration == null)
RecreateStaticButton.Visible = false;
BuildFileList();
initializedStaticPage = true;
}
void BuildFileList()
{
enumerating = true;
IndexesToPaths.Clear();
StaticFileListBox.Items.Clear();
IndexesToPaths.Add(StaticFileListBox.Items.Add("/"), "/");
if (EnumeratePath("", Instance.Config, 1) == EnumResult.Unauthorized)
{
StaticFileListBox.Items[0] += " (UNAUTHORIZED)";
IndexesToPaths[0] = null;
}
StaticFileListBox.SelectedIndex = 0;
enumerating = false;
}
EnumResult EnumeratePath(string path, ITGConfig config, int level)
{
if (!EnumeratedPaths.Contains(path))
return EnumResult.NotEnumerated;
var Enum = config.ListStaticDirectory(path, out string error, out bool unauthorized);
if(Enum == null)
{
if (unauthorized)
return EnumResult.Unauthorized;
else
{
MessageBox.Show(String.Format("Could not enumerate static path \"{0}\" error: {1}", path, error));
return EnumResult.Error;
}
}
foreach(var I in Enum)
{
if(I[0] == '/')
{
var dir = I.Remove(0, 1);
var index = StaticFileListBox.Items.Add(DSNTimes(level) + dir + '/');
var fullpath = path + '/' + dir;
IndexesToPaths.Add(index, fullpath);
switch(EnumeratePath(fullpath, config, level + 1))
{
case EnumResult.Unauthorized:
StaticFileListBox.Items[index] += " (UNAUTHORIZED)";
IndexesToPaths[index] = null;
break;
case EnumResult.NotEnumerated:
StaticFileListBox.Items[index] += " (...)";
break;
case EnumResult.Error:
StaticFileListBox.Items[index] += " (ERROR)";
IndexesToPaths[index] = null;
break;
}
continue;
}
IndexesToPaths.Add(StaticFileListBox.Items.Add(DSNTimes(level) + I), Path.Combine(path, I));
}
return EnumResult.Enumerated;
}
string DSNTimes(int n)
{
var res = "";
for (var I = 0; I < n; ++I)
res += " ";
return res;
}
private void StaticFilesRefreshButton_Click(object sender, EventArgs e)
{
BuildFileList();
UpdateEditText();
}
private void StaticFileUploadButton_Click(object sender, EventArgs e)
{
if (StaticFileEditTextbox.Text != "Directory")
{
MessageBox.Show("Please select a directory to upload the file to.");
return;
}
var ofd = new OpenFileDialog()
{
CheckFileExists = true,
CheckPathExists = true,
DefaultExt = ".txt",
Multiselect = false,
Title = "Static File Upload",
ValidateNames = true,
Filter = "All files (*.*)|*.*",
AddExtension = false,
SupportMultiDottedExtensions = true,
};
if (ofd.ShowDialog() != DialogResult.OK)
return;
var fileToUpload = ofd.FileName;
var FileName = Path.Combine(IndexesToPaths[StaticFileListBox.SelectedIndex], Path.GetFileName(fileToUpload));
string fileContents = null;
string error = null;
try
{
fileContents = File.ReadAllText(fileToUpload);
}
catch (Exception ex)
{
error = ex.ToString();
}
if (error == null)
try
{
error = Instance.Config.WriteText(FileName, fileContents, null, out bool unauthorized);
}
catch (Exception ex)
{
error = "Failed to read file, most likely due to it being too large. The transfer limit is much higher on non-remote connections. " + ex.ToString();
}
if (error != null)
MessageBox.Show("An error occurred: " + error);
BuildFileList();
}
private void StaticFileDownloadButton_Click(object sender, EventArgs e)
{
if (StaticFileEditTextbox.ReadOnly)
{
MessageBox.Show("Cannot download this file!");
return;
}
var remotePath = IndexesToPaths[StaticFileListBox.SelectedIndex];
if (remotePath == null)
return;
string text, error;
try
{
text = Instance.Config.ReadText(remotePath, false, out error, out bool unauthorized);
}
catch (Exception ex)
{
text = null;
error = "Failed to read file, most likely due to it being too large. The transfer limit is much higher on non-remote connections. " + ex.ToString();
}
if (text != null)
{
var ofd = new SaveFileDialog()
{
CheckFileExists = false,
CheckPathExists = true,
DefaultExt = ".txt",
Title = "Static File Download",
ValidateNames = true,
Filter = "All files (*.*)|*.*",
AddExtension = false,
CreatePrompt = false,
OverwritePrompt = true,
SupportMultiDottedExtensions = true,
};
if (ofd.ShowDialog() != DialogResult.OK)
return;
try
{
File.WriteAllText(ofd.FileName, text);
return;
}
catch (Exception ex)
{
error = ex.ToString();
}
}
MessageBox.Show("An error occurred: " + error);
}
private void StaticFileDeleteButton_Click(object sender, EventArgs e)
{
if (MessageBox.Show("Are you sure you want to delete " + ((string)StaticFileListBox.SelectedItem).Trim() + "?", "Confirm", MessageBoxButtons.YesNo) != DialogResult.Yes)
return;
var res = Instance.Config.DeleteFile(IndexesToPaths[StaticFileListBox.SelectedIndex], out bool unauthorized);
if (res != null)
MessageBox.Show(res);
BuildFileList();
}
private void StaticFileCreateButton_Click(object sender, EventArgs e)
{
if(StaticFileEditTextbox.Text != "Directory")
{
MessageBox.Show("Please select a directory to create the file in.");
return;
}
var FileName = Program.TextPrompt("Static File/Directory Creation", "Enter the name of the file/directory:");
if (FileName == null)
return;
var resu = MessageBox.Show("Is this the name of a directory?", "Directory", MessageBoxButtons.YesNoCancel);
if (resu == DialogResult.Cancel)
return;
var FullFileName = Path.Combine(IndexesToPaths[StaticFileListBox.SelectedIndex], FileName);
if (resu == DialogResult.Yes)
FullFileName = Path.Combine(FullFileName, "__TGS3_CP_DIRECTORY_CREATOR__");
var config = Instance.Config;
var res = config.WriteText(FullFileName, "", null, out bool unauthorized);
if (res != null)
MessageBox.Show(res);
if (resu == DialogResult.Yes)
{
FullFileName = Path.Combine(FullFileName, "__TGS3_CP_DIRECTORY_CREATOR__");
config.DeleteFile(FullFileName, out unauthorized); //don't care about this
}
BuildFileList();
}
private void StaticFileSaveButton_Click(object sender, EventArgs e)
{
var index = StaticFileListBox.SelectedIndex;
string res;
bool unauthorized;
try
{
res = Instance.Config.WriteText(IndexesToPaths[index], StaticFileEditTextbox.Text, originalCurrentFileContent, out unauthorized);
if (res == null)
originalCurrentFileContent = StaticFileEditTextbox.Text;
}
catch (Exception ex)
{
unauthorized = false;
res = "Failed to write file, most likely due to it being too large. The transfer limit is much higher on non-remote connections. " + ex.ToString();
}
if (res != null)
{
MessageBox.Show("Error: " + res);
var title = (string)StaticFileListBox.Items[index];
if (unauthorized && !title.Contains(" (UNAUTHORIZED)"))
StaticFileListBox.Items[index] = title + " (UNAUTHORIZED)";
}
UpdateEditText();
}
private void StaticFileListBox_SelectedIndexChanged(object sender, EventArgs e)
{
UpdateEditText();
}
void UpdateEditText()
{
if (enumerating)
return;
var newIndex = StaticFileListBox.SelectedIndex;
if (newIndex == -1)
return;
var path = IndexesToPaths[newIndex];
var title = (string)StaticFileListBox.Items[newIndex];
var authed_title = title.Replace(" (UNAUTHORIZED)", "").Replace(" (...)", "");
if (authed_title[authed_title.Length - 1] == '/')
{
StaticFileEditTextbox.ReadOnly = true;
StaticFileEditTextbox.Text = "Directory";
if (path != null && path != "/")
{
EnumeratedPaths.Add(path);
BuildFileList();
enumerating = true;
StaticFileListBox.SelectedIndex = newIndex;
enumerating = false;
return;
}
}
else
{
string entry, error;
bool unauthorized;
try
{
entry = Instance.Config.ReadText(path, false, out error, out unauthorized);
}
catch(Exception e)
{
entry = null;
unauthorized = false;
error = "Failed to read file, most likely due to it being too large. The transfer limit is much higher on non-remote connections. " + e.ToString();
}
if (entry == null)
{
StaticFileEditTextbox.ReadOnly = true;
StaticFileEditTextbox.Text = "ERROR: " + error;
if (unauthorized && !title.Contains(" (UNAUTHORIZED)"))
StaticFileListBox.Items[newIndex] = title + " (UNAUTHORIZED)";
}
else
{
StaticFileEditTextbox.ReadOnly = false;
originalCurrentFileContent = entry;
StaticFileEditTextbox.Text = entry.Replace("\r\n", "\n").Replace("\r", "\n").Replace("\n", Environment.NewLine);
}
}
}
private void RecreateStaticButton_Click(object sender, EventArgs e)
{
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 (!CheckAdminWithWarning())
{
RecreateStaticButton.Visible = false;
return;
}
var res = Instance.Administration.RecreateStaticFolder();
if (res != null)
MessageBox.Show(res);
BuildFileList();
}
}
}
-38
View File
@@ -1,38 +0,0 @@
using System.Windows.Forms;
namespace TGS.ControlPanel
{
/// <summary>
/// Calls <see cref="Application.Exit()"/> when all <see cref="CountedForm"/>s are <see cref="Form.Close"/>d
/// </summary>
#if !DEBUG
abstract
#endif
class CountedForm : ServerOpForm
{
/// <summary>
/// The current number of active <see cref="CountedForm"/>s
/// </summary>
static uint FormCount;
/// <summary>
/// Construct a <see cref="CountedForm"/>. Increments <see cref="FormCount"/>
/// </summary>
public CountedForm()
{
FormClosed += CountedForm_FormClosed;
++FormCount;
}
/// <summary>
/// Decrements <see cref="FormCount"/>. Calls <see cref="Application.Exit()"/> if it reaches 0
/// </summary>
/// <param name="sender">The sender of the event</param>
/// <param name="e">The <see cref="FormClosedEventArgs"/></param>
private void CountedForm_FormClosed(object sender, FormClosedEventArgs e)
{
if (--FormCount == 0)
Application.Exit();
}
}
}
-176
View File
@@ -1,176 +0,0 @@
namespace TGS.ControlPanel
{
partial class GitHubLoginPrompt
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.PasswordLabel = new System.Windows.Forms.Label();
this.UsernameLabel = new System.Windows.Forms.Label();
this.PasswordTextBox = new System.Windows.Forms.TextBox();
this.UsernameTextBox = new System.Windows.Forms.TextBox();
this.DividerLabel = new System.Windows.Forms.Label();
this.APIKeyLabel = new System.Windows.Forms.Label();
this.APIKeyTextBox = new System.Windows.Forms.TextBox();
this.OrLabel = new System.Windows.Forms.Label();
this.LoginButton = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// PasswordLabel
//
this.PasswordLabel.AutoSize = true;
this.PasswordLabel.Font = new System.Drawing.Font("Verdana", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.PasswordLabel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242)))));
this.PasswordLabel.Location = new System.Drawing.Point(12, 41);
this.PasswordLabel.Name = "PasswordLabel";
this.PasswordLabel.Size = new System.Drawing.Size(92, 18);
this.PasswordLabel.TabIndex = 25;
this.PasswordLabel.Text = "Password:";
this.PasswordLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
//
// UsernameLabel
//
this.UsernameLabel.AutoSize = true;
this.UsernameLabel.Font = new System.Drawing.Font("Verdana", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.UsernameLabel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242)))));
this.UsernameLabel.Location = new System.Drawing.Point(12, 9);
this.UsernameLabel.Name = "UsernameLabel";
this.UsernameLabel.Size = new System.Drawing.Size(97, 18);
this.UsernameLabel.TabIndex = 24;
this.UsernameLabel.Text = "Username:";
this.UsernameLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
//
// PasswordTextBox
//
this.PasswordTextBox.Location = new System.Drawing.Point(132, 41);
this.PasswordTextBox.Name = "PasswordTextBox";
this.PasswordTextBox.Size = new System.Drawing.Size(233, 20);
this.PasswordTextBox.TabIndex = 23;
this.PasswordTextBox.UseSystemPasswordChar = true;
//
// UsernameTextBox
//
this.UsernameTextBox.Location = new System.Drawing.Point(132, 9);
this.UsernameTextBox.Name = "UsernameTextBox";
this.UsernameTextBox.Size = new System.Drawing.Size(233, 20);
this.UsernameTextBox.TabIndex = 22;
//
// DividerLabel
//
this.DividerLabel.AutoSize = true;
this.DividerLabel.Font = new System.Drawing.Font("Verdana", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.DividerLabel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242)))));
this.DividerLabel.Location = new System.Drawing.Point(-24, 59);
this.DividerLabel.Name = "DividerLabel";
this.DividerLabel.Size = new System.Drawing.Size(468, 18);
this.DividerLabel.TabIndex = 26;
this.DividerLabel.Text = "______________________________________________";
this.DividerLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
//
// APIKeyLabel
//
this.APIKeyLabel.AutoSize = true;
this.APIKeyLabel.Font = new System.Drawing.Font("Verdana", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.APIKeyLabel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242)))));
this.APIKeyLabel.Location = new System.Drawing.Point(12, 95);
this.APIKeyLabel.Name = "APIKeyLabel";
this.APIKeyLabel.Size = new System.Drawing.Size(78, 18);
this.APIKeyLabel.TabIndex = 28;
this.APIKeyLabel.Text = "API Key:";
this.APIKeyLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
//
// APIKeyTextBox
//
this.APIKeyTextBox.Location = new System.Drawing.Point(132, 95);
this.APIKeyTextBox.Name = "APIKeyTextBox";
this.APIKeyTextBox.Size = new System.Drawing.Size(233, 20);
this.APIKeyTextBox.TabIndex = 27;
this.APIKeyTextBox.UseSystemPasswordChar = true;
//
// OrLabel
//
this.OrLabel.AutoSize = true;
this.OrLabel.Font = new System.Drawing.Font("Verdana", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.OrLabel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242)))));
this.OrLabel.Location = new System.Drawing.Point(157, 64);
this.OrLabel.Name = "OrLabel";
this.OrLabel.Size = new System.Drawing.Size(32, 18);
this.OrLabel.TabIndex = 29;
this.OrLabel.Text = "OR";
this.OrLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
//
// LoginButton
//
this.LoginButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.LoginButton.Location = new System.Drawing.Point(145, 133);
this.LoginButton.Name = "LoginButton";
this.LoginButton.Size = new System.Drawing.Size(88, 25);
this.LoginButton.TabIndex = 30;
this.LoginButton.Text = InitalLoginButtonText;
this.LoginButton.UseVisualStyleBackColor = true;
this.LoginButton.Click += new System.EventHandler(this.LoginButton_Click);
//
// GitHubLoginPrompt
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(39)))), ((int)(((byte)(40)))), ((int)(((byte)(34)))));
this.ClientSize = new System.Drawing.Size(376, 170);
this.Controls.Add(this.LoginButton);
this.Controls.Add(this.OrLabel);
this.Controls.Add(this.APIKeyLabel);
this.Controls.Add(this.APIKeyTextBox);
this.Controls.Add(this.DividerLabel);
this.Controls.Add(this.PasswordLabel);
this.Controls.Add(this.UsernameLabel);
this.Controls.Add(this.PasswordTextBox);
this.Controls.Add(this.UsernameTextBox);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "GitHubLoginPrompt";
this.ShowIcon = false;
this.ShowInTaskbar = false;
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "Login To GitHub";
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Label PasswordLabel;
private System.Windows.Forms.Label UsernameLabel;
private System.Windows.Forms.TextBox PasswordTextBox;
private System.Windows.Forms.TextBox UsernameTextBox;
private System.Windows.Forms.Label DividerLabel;
private System.Windows.Forms.Label APIKeyLabel;
private System.Windows.Forms.TextBox APIKeyTextBox;
private System.Windows.Forms.Label OrLabel;
private System.Windows.Forms.Button LoginButton;
}
}
-103
View File
@@ -1,103 +0,0 @@
using Octokit;
using System;
using System.Threading.Tasks;
using System.Windows.Forms;
using TGS.Interface;
namespace TGS.ControlPanel
{
/// <summary>
/// Used for recieving a GitHub API key for use in <see cref="Credentials"/>
/// </summary>
sealed partial class GitHubLoginPrompt : Form
{
/// <summary>
/// The text to appear on <see cref="LoginButton"/>
/// </summary>
const string InitalLoginButtonText = "Login";
/// <summary>
/// The <see cref="GitHubClient"/> to use for OAuth requests
/// </summary>
readonly GitHubClient client;
/// <summary>
/// Construct a <see cref="GitHubLoginPrompt"/>
/// </summary>
/// <param name="c">The <see cref="GitHubClient"/> to use for requests</param>
public GitHubLoginPrompt(GitHubClient c)
{
InitializeComponent();
AcceptButton = LoginButton;
DialogResult = DialogResult.Cancel;
client = c;
}
/// <summary>
/// Calls <see cref="GetAPIKey"/>. On success, encrypts it's return value, saves it in <see cref="Properties.Settings.GitHubAPIKey"/> and <see cref="Properties.Settings.GitHubAPIKeyEntropy"/>, and closes the <see cref="GitHubLoginPrompt"/>. On failure, shows a <see cref="MessageBox"/> and exits
/// </summary>
/// <param name="sender">The sender of the event</param>
/// <param name="e">The <see cref="EventArgs"/></param>
async void LoginButton_Click(object sender, EventArgs e)
{
LoginButton.Text = "Logging in...";
Enabled = false;
UseWaitCursor = true;
var APIKey = await GetAPIKey();
if(APIKey == null)
{
MessageBox.Show("Authentication failure!");
Enabled = true;
UseWaitCursor = false;
LoginButton.Text = "Login";
return;
}
if (String.IsNullOrWhiteSpace(APIKeyTextBox.Text))
//They used username authentication, let them know we made a token
MessageBox.Show("A personal access token has been created on your account for use with the Control Panel");
//Encrypt it and let's be on our way
var Config = Properties.Settings.Default;
Config.GitHubAPIKey = Helpers.EncryptData(APIKey, out string entropy);
Config.GitHubAPIKeyEntropy = entropy;
DialogResult = DialogResult.OK;
Close();
}
/// <summary>
/// Retrieves an API key from GitHub based on login information or from the <see cref="APIKeyTextBox"/> and verifies it
/// </summary>
/// <returns>The GitHub API key on success, <see langword="null"/> on failure</returns>
async Task<string> GetAPIKey()
{
string APIKey;
if (String.IsNullOrWhiteSpace(APIKeyTextBox.Text))
{
try
{
client.Credentials = new Credentials(UsernameTextBox.Text, PasswordTextBox.Text);
var token = await client.Authorization.Create(new NewAuthorization { Note = "TGS.ControlPanel token to bypass rate limiting" });
APIKey = token.Token;
}
catch (AuthorizationException)
{
return null;
}
}
else
APIKey = APIKeyTextBox.Text;
//validate it by pinging a random repository
client.Credentials = new Credentials(APIKey);
try
{
await client.Repository.Get("Dextraspace", "Test");
}
catch (AuthorizationException)
{
return null;
}
return APIKey;
}
}
}
-120
View File
@@ -1,120 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>
-164
View File
@@ -1,164 +0,0 @@
namespace TGS.ControlPanel
{
partial class InstanceSelector
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(InstanceSelector));
this.InstanceListBox = new System.Windows.Forms.ListBox();
this.CreateInstanceButton = new System.Windows.Forms.Button();
this.ImportInstanceButton = new System.Windows.Forms.Button();
this.RenameInstanceButton = new System.Windows.Forms.Button();
this.DetachInstanceButton = new System.Windows.Forms.Button();
this.RefreshButton = new System.Windows.Forms.Button();
this.ConnectButton = new System.Windows.Forms.Button();
this.EnabledCheckBox = new System.Windows.Forms.CheckBox();
this.SuspendLayout();
//
// InstanceListBox
//
this.InstanceListBox.Anchor = System.Windows.Forms.AnchorStyles.Left;
this.InstanceListBox.FormattingEnabled = true;
this.InstanceListBox.Location = new System.Drawing.Point(13, 13);
this.InstanceListBox.Name = "InstanceListBox";
this.InstanceListBox.Size = new System.Drawing.Size(339, 238);
this.InstanceListBox.TabIndex = 0;
this.InstanceListBox.SelectedIndexChanged += new System.EventHandler(this.InstanceListBox_SelectedIndexChanged);
//
// CreateInstanceButton
//
this.CreateInstanceButton.Anchor = System.Windows.Forms.AnchorStyles.Right;
this.CreateInstanceButton.Location = new System.Drawing.Point(358, 15);
this.CreateInstanceButton.Name = "CreateInstanceButton";
this.CreateInstanceButton.Size = new System.Drawing.Size(148, 25);
this.CreateInstanceButton.TabIndex = 14;
this.CreateInstanceButton.Text = "Create Instance";
this.CreateInstanceButton.UseVisualStyleBackColor = true;
this.CreateInstanceButton.Click += new System.EventHandler(this.CreateInstanceButton_Click);
//
// ImportInstanceButton
//
this.ImportInstanceButton.Anchor = System.Windows.Forms.AnchorStyles.Right;
this.ImportInstanceButton.Location = new System.Drawing.Point(358, 45);
this.ImportInstanceButton.Name = "ImportInstanceButton";
this.ImportInstanceButton.Size = new System.Drawing.Size(148, 25);
this.ImportInstanceButton.TabIndex = 15;
this.ImportInstanceButton.Text = "Import Instance";
this.ImportInstanceButton.UseVisualStyleBackColor = true;
this.ImportInstanceButton.Click += new System.EventHandler(this.ImportInstanceButton_Click);
//
// RenameInstanceButton
//
this.RenameInstanceButton.Anchor = System.Windows.Forms.AnchorStyles.Right;
this.RenameInstanceButton.Location = new System.Drawing.Point(358, 76);
this.RenameInstanceButton.Name = "RenameInstanceButton";
this.RenameInstanceButton.Size = new System.Drawing.Size(148, 25);
this.RenameInstanceButton.TabIndex = 16;
this.RenameInstanceButton.Text = "Rename Instance";
this.RenameInstanceButton.UseVisualStyleBackColor = true;
this.RenameInstanceButton.Click += new System.EventHandler(this.RenameInstanceButton_Click);
//
// DetachInstanceButton
//
this.DetachInstanceButton.Anchor = System.Windows.Forms.AnchorStyles.Right;
this.DetachInstanceButton.Location = new System.Drawing.Point(358, 107);
this.DetachInstanceButton.Name = "DetachInstanceButton";
this.DetachInstanceButton.Size = new System.Drawing.Size(148, 25);
this.DetachInstanceButton.TabIndex = 17;
this.DetachInstanceButton.Text = "Detach Instance";
this.DetachInstanceButton.UseVisualStyleBackColor = true;
this.DetachInstanceButton.Click += new System.EventHandler(this.DetachInstanceButton_Click);
//
// RefreshButton
//
this.RefreshButton.Anchor = System.Windows.Forms.AnchorStyles.Right;
this.RefreshButton.Location = new System.Drawing.Point(358, 195);
this.RefreshButton.Name = "RefreshButton";
this.RefreshButton.Size = new System.Drawing.Size(148, 25);
this.RefreshButton.TabIndex = 19;
this.RefreshButton.Text = "Refresh";
this.RefreshButton.UseVisualStyleBackColor = true;
this.RefreshButton.Click += new System.EventHandler(this.RefreshButton_Click);
//
// ConnectButton
//
this.ConnectButton.Anchor = System.Windows.Forms.AnchorStyles.Right;
this.ConnectButton.Location = new System.Drawing.Point(358, 226);
this.ConnectButton.Name = "ConnectButton";
this.ConnectButton.Size = new System.Drawing.Size(148, 25);
this.ConnectButton.TabIndex = 20;
this.ConnectButton.Text = "Connect";
this.ConnectButton.UseVisualStyleBackColor = true;
this.ConnectButton.Click += new System.EventHandler(this.ConnectButton_Click);
//
// EnabledCheckBox
//
this.EnabledCheckBox.AutoSize = true;
this.EnabledCheckBox.ForeColor = System.Drawing.Color.White;
this.EnabledCheckBox.Location = new System.Drawing.Point(396, 149);
this.EnabledCheckBox.Name = "EnabledCheckBox";
this.EnabledCheckBox.Size = new System.Drawing.Size(65, 17);
this.EnabledCheckBox.TabIndex = 21;
this.EnabledCheckBox.Text = "Enabled";
this.EnabledCheckBox.UseVisualStyleBackColor = true;
this.EnabledCheckBox.CheckedChanged += new System.EventHandler(this.EnabledCheckBox_CheckedChanged);
//
// InstanceSelector
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(39)))), ((int)(((byte)(40)))), ((int)(((byte)(34)))));
this.ClientSize = new System.Drawing.Size(518, 261);
this.Controls.Add(this.EnabledCheckBox);
this.Controls.Add(this.ConnectButton);
this.Controls.Add(this.RefreshButton);
this.Controls.Add(this.DetachInstanceButton);
this.Controls.Add(this.RenameInstanceButton);
this.Controls.Add(this.ImportInstanceButton);
this.Controls.Add(this.CreateInstanceButton);
this.Controls.Add(this.InstanceListBox);
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.Name = "InstanceSelector";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "Server Instances";
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.ListBox InstanceListBox;
private System.Windows.Forms.Button CreateInstanceButton;
private System.Windows.Forms.Button ImportInstanceButton;
private System.Windows.Forms.Button RenameInstanceButton;
private System.Windows.Forms.Button DetachInstanceButton;
private System.Windows.Forms.Button RefreshButton;
private System.Windows.Forms.Button ConnectButton;
private System.Windows.Forms.CheckBox EnabledCheckBox;
}
}
-230
View File
@@ -1,230 +0,0 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Windows.Forms;
using TGS.Interface;
namespace TGS.ControlPanel
{
/// <summary>
/// Form used for managing <see cref="IInstance"/>s
/// </summary>
sealed partial class InstanceSelector : CountedForm
{
/// <summary>
/// The <see cref="IServer"/> we build instance connections from
/// </summary>
readonly IServer server;
/// <summary>
/// Used for modifying <see cref="EnabledCheckBox"/> without invoking its side effects
/// </summary>
bool UpdatingEnabledCheckbox = false;
/// <summary>
/// Construct an <see cref="InstanceSelector"/>
/// </summary>
/// <param name="_server">The value of <see cref="server"/></param>
public InstanceSelector(IServer _server)
{
InitializeComponent();
InstanceListBox.MouseDoubleClick += InstanceListBox_MouseDoubleClick;
server = _server;
RefreshInstances();
}
/// <summary>
/// Connects to a <see cref="IInstance"/> if it is double clicked in <see cref="InstanceListBox"/>
/// </summary>
/// <param name="sender">The sender of the event</param>
/// <param name="e">The <see cref="MouseEventArgs"/></param>
void InstanceListBox_MouseDoubleClick(object sender, MouseEventArgs e)
{
TryConnectToIndexInstance(InstanceListBox.IndexFromPoint(e.Location));
}
/// <summary>
/// Returns the <see cref="InstanceMetadata"/> associated with <see cref="InstanceListBox"/>'s current selected index
/// </summary>
/// <returns>The <see cref="InstanceMetadata"/> associated with <see cref="InstanceListBox"/>'s current selected index if it exists, <see langword="null"/> otherwise</returns>
InstanceMetadata GetSelectedInstanceMetadata()
{
var index = (IInstance)InstanceListBox.SelectedItem;
return index?.Metadata;
}
/// <summary>
/// Loads the <see cref="InstanceListBox"/> using <see cref="Interface.Components.ITGLanding.ListInstances"/>
/// </summary>
void RefreshInstances()
{
InstanceListBox.Items.Clear();
server.RebuildInstanceList();
foreach(var I in server.Instances)
InstanceListBox.Items.Add(I);
var HasServerAdmin = server.InstanceManager != null;
CreateInstanceButton.Enabled = HasServerAdmin;
ImportInstanceButton.Enabled = HasServerAdmin;
RenameInstanceButton.Enabled = HasServerAdmin;
DetachInstanceButton.Enabled = HasServerAdmin;
EnabledCheckBox.Enabled = HasServerAdmin;
if(InstanceListBox.Items.Count > 0)
InstanceListBox.SelectedIndex = 0;
}
/// <summary>
/// Tries to start a <see cref="ControlPanel"/> for a given <see cref="InstanceListBox"/> <paramref name="index"/>
/// </summary>
/// <param name="index">The <see cref="ListBox.SelectedIndex"/> of <see cref="InstanceListBox"/> to connect to</param>
void TryConnectToIndexInstance(int index)
{
if (index == ListBox.NoMatches)
return;
var instance = (IInstance)InstanceListBox.Items[index];
if (ControlPanel.InstancesInUse.TryGetValue(instance.Metadata.Name, out ControlPanel activeCP))
{
activeCP.BringToFront();
return;
}
new ControlPanel(server, instance).Show();
}
/// <summary>
/// Prompts the user for parameters to <see cref="Interface.Components.ITGInstanceManager.DetachInstance(string)"/>
/// </summary>
/// <param name="sender">The sender of the event</param>
/// <param name="e">The <see cref="EventArgs"/></param>
async void DetachInstanceButton_Click(object sender, EventArgs e)
{
var imd = GetSelectedInstanceMetadata();
if (imd == null)
return;
if (MessageBox.Show(String.Format("This will dissociate the server instance at \"{0}\"! Are you sure?", imd.Path), "Instance Detach", MessageBoxButtons.YesNo) != DialogResult.Yes)
return;
string res = null;
await WrapServerOp(() => res = server.InstanceManager.DetachInstance(imd.Name));
if (res != null)
MessageBox.Show(res);
RefreshInstances();
}
/// <summary>
/// Calls <see cref="RefreshInstances"/>
/// </summary>
/// <param name="sender">The sender of the event</param>
/// <param name="e">The <see cref="EventArgs"/></param>
void RefreshButton_Click(object sender, EventArgs e)
{
RefreshInstances();
}
/// <summary>
/// Prompts the user for parameters to <see cref="Interface.Components.ITGInstanceManager.RenameInstance(string, string)"/>
/// </summary>
/// <param name="sender">The sender of the event</param>
/// <param name="e">The <see cref="EventArgs"/></param>
async void RenameInstanceButton_Click(object sender, EventArgs e)
{
var imd = GetSelectedInstanceMetadata();
if (imd == null)
return;
var new_name = Program.TextPrompt("Instance Rename", "Enter a new name for the instance:");
if (new_name == null)
return;
if (imd.Enabled && MessageBox.Show(String.Format("This will temporarily offline the server instance! Are you sure?", imd.Path), "Instance Restart", MessageBoxButtons.YesNo) != DialogResult.Yes)
return;
string res = null;
await WrapServerOp(() => res = server.InstanceManager.RenameInstance(imd.Name, new_name));
if (res != null)
MessageBox.Show(res);
RefreshInstances();
}
/// <summary>
/// Prompts the user for parameters to <see cref="Interface.Components.ITGInstanceManager.ImportInstance(string)"/>
/// </summary>
/// <param name="sender">The sender of the event</param>
/// <param name="e">The <see cref="EventArgs"/></param>
async void ImportInstanceButton_Click(object sender, EventArgs e)
{
var instance_path = Program.TextPrompt("Instance Import", "Enter the full path to the instance:");
if (instance_path == null)
return;
string res = null;
await WrapServerOp(() => res = server.InstanceManager.ImportInstance(instance_path));
if (res != null)
MessageBox.Show(res);
RefreshInstances();
}
/// <summary>
/// Prompts the user for parameters to <see cref="Interface.Components.ITGInstanceManager.CreateInstance(string, string)"/>
/// </summary>
/// <param name="sender">The sender of the event</param>
/// <param name="e">The <see cref="EventArgs"/></param>
async void CreateInstanceButton_Click(object sender, EventArgs e)
{
var instance_name = Program.TextPrompt("Instance Creation", "Enter the name of the instance:");
if (instance_name == null)
return;
var instance_path = Program.TextPrompt("Instance Creation", "Enter the full path to the instance:");
if (instance_path == null)
return;
string res = null;
await WrapServerOp(() => res = server.InstanceManager.CreateInstance(instance_name, instance_path));
if (res != null)
MessageBox.Show(res);
RefreshInstances();
}
/// <summary>
/// Attempts to connect the user to an <see cref="IInstance"/> based on the <see cref="ListBox.SelectedIndex"/> of <see cref="InstanceListBox"/>
/// </summary>
/// <param name="sender">The sender of the event</param>
/// <param name="e">The <see cref="EventArgs"/></param>
void ConnectButton_Click(object sender, EventArgs e)
{
TryConnectToIndexInstance(InstanceListBox.SelectedIndex);
}
/// <summary>
/// Prompts the user if they want to call <see cref="Interface.Components.ITGInstanceManager.SetInstanceEnabled(string, bool)"/> to either online or offline an <see cref="IInstance"/> based on its current state
/// </summary>
/// <param name="sender">The sender of the event</param>
/// <param name="e">The <see cref="EventArgs"/></param>
async void EnabledCheckBox_CheckedChanged(object sender, EventArgs e)
{
if (UpdatingEnabledCheckbox)
return;
var enabling = EnabledCheckBox.Checked;
try
{
if (MessageBox.Show(String.Format("Are you sure you want to {0} this instance?", enabling ? "online" : "offline"), "Instance Status Change", MessageBoxButtons.YesNo) != DialogResult.Yes)
return;
string res = null;
await WrapServerOp(() => res = server.InstanceManager.SetInstanceEnabled(GetSelectedInstanceMetadata().Name, enabling));
if (res != null)
MessageBox.Show(res);
}
finally
{
RefreshInstances();
}
}
/// <summary>
/// Update <see cref="EnabledCheckBox"/> based on the selected <see cref="IInstance"/>'s <see cref="InstanceMetadata.Enabled"/> property
/// </summary>
/// <param name="sender">The sender of the event</param>
/// <param name="e">The <see cref="EventArgs"/></param>
void InstanceListBox_SelectedIndexChanged(object sender, EventArgs e)
{
if (InstanceListBox.SelectedIndex != ListBox.NoMatches)
{
UpdatingEnabledCheckbox = true;
EnabledCheckBox.Checked = GetSelectedInstanceMetadata().Enabled;
UpdatingEnabledCheckbox = false;
}
}
}
}
File diff suppressed because it is too large Load Diff
-245
View File
@@ -1,245 +0,0 @@
namespace TGS.ControlPanel
{
partial class Login
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Login));
this.LocalLoginButton = new System.Windows.Forms.Button();
this.CurrentRevisionTitle = new System.Windows.Forms.Label();
this.UsernameTextBox = new System.Windows.Forms.TextBox();
this.PasswordTextBox = new System.Windows.Forms.TextBox();
this.RemoteLoginButton = new System.Windows.Forms.Button();
this.AddressLabel = new System.Windows.Forms.Label();
this.label2 = new System.Windows.Forms.Label();
this.label3 = new System.Windows.Forms.Label();
this.label4 = new System.Windows.Forms.Label();
this.PortSelector = new System.Windows.Forms.NumericUpDown();
this.SavePasswordCheckBox = new System.Windows.Forms.CheckBox();
this.IPComboBox = new System.Windows.Forms.ComboBox();
this.DeleteLoginButton = new System.Windows.Forms.Button();
((System.ComponentModel.ISupportInitialize)(this.PortSelector)).BeginInit();
this.SuspendLayout();
//
// LocalLoginButton
//
this.LocalLoginButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.LocalLoginButton.Location = new System.Drawing.Point(102, 12);
this.LocalLoginButton.Name = "LocalLoginButton";
this.LocalLoginButton.Size = new System.Drawing.Size(157, 25);
this.LocalLoginButton.TabIndex = 1;
this.LocalLoginButton.Text = "Connect to Local Service";
this.LocalLoginButton.UseVisualStyleBackColor = true;
this.LocalLoginButton.Click += new System.EventHandler(this.LocalLoginButton_Click);
//
// CurrentRevisionTitle
//
this.CurrentRevisionTitle.AutoSize = true;
this.CurrentRevisionTitle.Font = new System.Drawing.Font("Verdana", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.CurrentRevisionTitle.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242)))));
this.CurrentRevisionTitle.Location = new System.Drawing.Point(116, 69);
this.CurrentRevisionTitle.Name = "CurrentRevisionTitle";
this.CurrentRevisionTitle.Size = new System.Drawing.Size(128, 18);
this.CurrentRevisionTitle.TabIndex = 14;
this.CurrentRevisionTitle.Text = "Remote Login:";
this.CurrentRevisionTitle.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
//
// UsernameTextBox
//
this.UsernameTextBox.Location = new System.Drawing.Point(129, 132);
this.UsernameTextBox.Name = "UsernameTextBox";
this.UsernameTextBox.Size = new System.Drawing.Size(233, 20);
this.UsernameTextBox.TabIndex = 16;
//
// PasswordTextBox
//
this.PasswordTextBox.Location = new System.Drawing.Point(129, 164);
this.PasswordTextBox.Name = "PasswordTextBox";
this.PasswordTextBox.Size = new System.Drawing.Size(233, 20);
this.PasswordTextBox.TabIndex = 17;
this.PasswordTextBox.UseSystemPasswordChar = true;
//
// RemoteLoginButton
//
this.RemoteLoginButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.RemoteLoginButton.Location = new System.Drawing.Point(102, 200);
this.RemoteLoginButton.Name = "RemoteLoginButton";
this.RemoteLoginButton.Size = new System.Drawing.Size(157, 25);
this.RemoteLoginButton.TabIndex = 18;
this.RemoteLoginButton.Text = "Connect to Remote Service";
this.RemoteLoginButton.UseVisualStyleBackColor = true;
this.RemoteLoginButton.Click += new System.EventHandler(this.RemoteLoginButton_Click);
//
// AddressLabel
//
this.AddressLabel.AutoSize = true;
this.AddressLabel.Font = new System.Drawing.Font("Verdana", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.AddressLabel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242)))));
this.AddressLabel.Location = new System.Drawing.Point(9, 99);
this.AddressLabel.Name = "AddressLabel";
this.AddressLabel.Size = new System.Drawing.Size(80, 18);
this.AddressLabel.TabIndex = 19;
this.AddressLabel.Text = "Address:";
this.AddressLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
//
// label2
//
this.label2.AutoSize = true;
this.label2.Font = new System.Drawing.Font("Verdana", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.label2.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242)))));
this.label2.Location = new System.Drawing.Point(9, 132);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(97, 18);
this.label2.TabIndex = 20;
this.label2.Text = "Username:";
this.label2.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
//
// label3
//
this.label3.AutoSize = true;
this.label3.Font = new System.Drawing.Font("Verdana", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.label3.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242)))));
this.label3.Location = new System.Drawing.Point(9, 164);
this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(92, 18);
this.label3.TabIndex = 21;
this.label3.Text = "Password:";
this.label3.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
//
// label4
//
this.label4.AutoSize = true;
this.label4.Font = new System.Drawing.Font("Verdana", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.label4.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242)))));
this.label4.Location = new System.Drawing.Point(-19, 40);
this.label4.Name = "label4";
this.label4.Size = new System.Drawing.Size(468, 18);
this.label4.TabIndex = 22;
this.label4.Text = "______________________________________________";
this.label4.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
//
// PortSelector
//
this.PortSelector.Location = new System.Drawing.Point(282, 99);
this.PortSelector.Maximum = new decimal(new int[] {
65535,
0,
0,
0});
this.PortSelector.Minimum = new decimal(new int[] {
1,
0,
0,
0});
this.PortSelector.Name = "PortSelector";
this.PortSelector.Size = new System.Drawing.Size(80, 20);
this.PortSelector.TabIndex = 16;
this.PortSelector.Value = new decimal(new int[] {
38607,
0,
0,
0});
//
// SavePasswordCheckBox
//
this.SavePasswordCheckBox.AutoSize = true;
this.SavePasswordCheckBox.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242)))));
this.SavePasswordCheckBox.Location = new System.Drawing.Point(265, 205);
this.SavePasswordCheckBox.Name = "SavePasswordCheckBox";
this.SavePasswordCheckBox.Size = new System.Drawing.Size(100, 17);
this.SavePasswordCheckBox.TabIndex = 23;
this.SavePasswordCheckBox.Text = "Save Password";
this.SavePasswordCheckBox.UseVisualStyleBackColor = true;
this.SavePasswordCheckBox.CheckedChanged += new System.EventHandler(this.SavePasswordCheckBox_CheckedChanged);
//
// IPComboBox
//
this.IPComboBox.FormattingEnabled = true;
this.IPComboBox.Location = new System.Drawing.Point(130, 98);
this.IPComboBox.Name = "IPComboBox";
this.IPComboBox.Size = new System.Drawing.Size(146, 21);
this.IPComboBox.TabIndex = 24;
//
// DeleteLoginButton
//
this.DeleteLoginButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.DeleteLoginButton.Location = new System.Drawing.Point(105, 98);
this.DeleteLoginButton.Name = "DeleteLoginButton";
this.DeleteLoginButton.Size = new System.Drawing.Size(19, 19);
this.DeleteLoginButton.TabIndex = 25;
this.DeleteLoginButton.Text = "x";
this.DeleteLoginButton.UseVisualStyleBackColor = true;
this.DeleteLoginButton.Click += new System.EventHandler(this.DeleteLoginButton_Click);
//
// Login
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(39)))), ((int)(((byte)(40)))), ((int)(((byte)(34)))));
this.ClientSize = new System.Drawing.Size(374, 237);
this.Controls.Add(this.DeleteLoginButton);
this.Controls.Add(this.IPComboBox);
this.Controls.Add(this.SavePasswordCheckBox);
this.Controls.Add(this.PortSelector);
this.Controls.Add(this.label4);
this.Controls.Add(this.label3);
this.Controls.Add(this.label2);
this.Controls.Add(this.AddressLabel);
this.Controls.Add(this.RemoteLoginButton);
this.Controls.Add(this.PasswordTextBox);
this.Controls.Add(this.UsernameTextBox);
this.Controls.Add(this.CurrentRevisionTitle);
this.Controls.Add(this.LocalLoginButton);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.MaximizeBox = false;
this.Name = "Login";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "Login";
((System.ComponentModel.ISupportInitialize)(this.PortSelector)).EndInit();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Button LocalLoginButton;
private System.Windows.Forms.Label CurrentRevisionTitle;
private System.Windows.Forms.TextBox UsernameTextBox;
private System.Windows.Forms.TextBox PasswordTextBox;
private System.Windows.Forms.Button RemoteLoginButton;
private System.Windows.Forms.Label AddressLabel;
private System.Windows.Forms.Label label2;
private System.Windows.Forms.Label label3;
private System.Windows.Forms.Label label4;
private System.Windows.Forms.NumericUpDown PortSelector;
private System.Windows.Forms.CheckBox SavePasswordCheckBox;
private System.Windows.Forms.ComboBox IPComboBox;
private System.Windows.Forms.Button DeleteLoginButton;
}
}
-190
View File
@@ -1,190 +0,0 @@
using System;
using System.Collections.Specialized;
using System.Windows.Forms;
using TGS.Interface;
namespace TGS.ControlPanel
{
sealed partial class Login : Form
{
/// <summary>
/// Currently selected saved <see cref="RemoteLoginInfo"/>
/// </summary>
RemoteLoginInfo currentLoginInfo;
bool updatingFields;
/// <summary>
/// Construct a <see cref="Login"/>
/// </summary>
public Login()
{
InitializeComponent();
var Config = Properties.Settings.Default;
AcceptButton = RemoteLoginButton;
if(Config.RemoteDefault)
RemoteLoginButton.TabIndex = 0; //make this the first thing selected when loading
IPComboBox.SelectedIndexChanged += IPComboBox_SelectedIndexChanged;
var loginInfo = Config.RemoteLoginInfo;
if (loginInfo != null)
{
foreach(var I in loginInfo)
IPComboBox.Items.Add(new RemoteLoginInfo(I));
if (IPComboBox.Items.Count > 0)
IPComboBox.SelectedIndex = 0;
}
IPComboBox.TextChanged += (a, b) => ClearFields(2);
PortSelector.ValueChanged += (a, b) => ClearFields(3);
UsernameTextBox.TextChanged += (a, b) => ClearFields(4);
}
void ClearFields(int start = 1)
{
if (updatingFields || currentLoginInfo == null)
return;
updatingFields = true;
switch (start) {
case 1:
IPComboBox.Text = "";
goto case 2; //reason number #3 why you shouldn't use c#
case 2:
PortSelector.Value = 38607;
goto case 3;
case 3:
UsernameTextBox.Text = "";
goto case 4;
case 4:
if (currentLoginInfo?.HasPassword ?? false) //reason number #749 why you shouldn't use c#
PasswordTextBox.Text = "";
break;
}
currentLoginInfo = null;
updatingFields = false;
}
void IPComboBox_SelectedIndexChanged(object sender, EventArgs e)
{
RemoteLoginInfo newLoginInfo = IPComboBox.SelectedItem as RemoteLoginInfo;
//new thing
if (newLoginInfo == null)
{
ClearFields();
currentLoginInfo = newLoginInfo;
return;
}
updatingFields = true;
currentLoginInfo = newLoginInfo;
IPComboBox.Text = currentLoginInfo.IP;
PortSelector.Value = currentLoginInfo.Port;
UsernameTextBox.Text = currentLoginInfo.Username;
SavePasswordCheckBox.Checked = currentLoginInfo.HasPassword;
if (currentLoginInfo.HasPassword)
PasswordTextBox.Text = "************";
else
PasswordTextBox.Text = "";
updatingFields = false;
}
void RemoteLoginButton_Click(object sender, EventArgs e)
{
if (String.IsNullOrWhiteSpace(PasswordTextBox.Text) || String.IsNullOrWhiteSpace(UsernameTextBox.Text) || String.IsNullOrWhiteSpace(IPComboBox.Text) || PortSelector.Value == 0)
return;
RemoteLoginInfo loginInfo;
if (currentLoginInfo == null)
{
loginInfo = new RemoteLoginInfo(IPComboBox.Text, (ushort)PortSelector.Value, UsernameTextBox.Text.Trim(), PasswordTextBox.Text);
}
else
{
loginInfo = (RemoteLoginInfo)IPComboBox.SelectedItem;
if (!loginInfo.HasPassword)
loginInfo.Password = PasswordTextBox.Text;
}
var I = new Client(loginInfo);
var Config = Properties.Settings.Default;
//This needs to be read here because V&C Closing us will corrupt the data
var savePassword = SavePasswordCheckBox.Checked;
if (VerifyAndConnect(I))
{
Config.RemoteDefault = true;
if (!savePassword)
loginInfo.Password = null;
Config.RemoteLoginInfo = new StringCollection { loginInfo.ToJSON() };
foreach (RemoteLoginInfo info in IPComboBox.Items)
if (!info.Equals(loginInfo))
Config.RemoteLoginInfo.Add(info.ToJSON());
}
}
void LocalLoginButton_Click(object sender, EventArgs e)
{
Properties.Settings.Default.RemoteDefault = false;
VerifyAndConnect(new Client());
}
/// <summary>
/// Attempts a connection on a given <see cref="IClient"/>
/// </summary>
/// <param name="I">The <see cref="IClient"/> to attempt a connection on</param>
/// <returns><see langword="true"/> if the connection was made and authenticated, <see langword="false"/> otherwise</returns>
bool VerifyAndConnect(IClient I)
{
try
{
var res = I.ConnectionStatus(out string error);
if (!res.HasFlag(ConnectivityLevel.Connected))
{
MessageBox.Show("Unable to connect to service! Error: " + error);
return false;
}
if (!res.HasFlag(ConnectivityLevel.Authenticated))
{
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 false;
}
if (I.VersionMismatch(out error) && MessageBox.Show(error, "Warning", MessageBoxButtons.OKCancel) == DialogResult.Cancel)
return true;
Close();
Program.ServerInterface = I;
return true;
}
catch
{
I.Dispose();
throw;
}
}
void SavePasswordCheckBox_CheckedChanged(object sender, EventArgs e)
{
if (!updatingFields && !SavePasswordCheckBox.Checked && currentLoginInfo != null)
{
currentLoginInfo = null;
PasswordTextBox.Text = "";
}
}
/// <summary>
/// Removes the selected item from <see cref="IPComboBox"/>
/// </summary>
/// <param name="sender">The sender of the event</param>
/// <param name="e">The <see cref="EventArgs"/></param>
void DeleteLoginButton_Click(object sender, EventArgs e)
{
//make sure we're trying to delete a real item
if (IPComboBox.SelectedItem as RemoteLoginInfo == null)
return;
IPComboBox.Items.RemoveAt(IPComboBox.SelectedIndex);
}
}
}
File diff suppressed because it is too large Load Diff
-135
View File
@@ -1,135 +0,0 @@
using System;
using System.Windows.Forms;
using TGS.Interface;
using TGS.Interface.Components;
namespace TGS.ControlPanel
{
static class Program
{
public static IClient ServerInterface;
[STAThread]
static void Main(string[] args)
{
try
{
if (Properties.Settings.Default.UpgradeRequired)
{
Properties.Settings.Default.Upgrade();
Properties.Settings.Default.UpgradeRequired = false;
Properties.Settings.Default.Save();
}
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Interface.Client.SetBadCertificateHandler(BadCertificateHandler);
Application.Run(new Login());
if(ServerInterface != null)
{
new InstanceSelector(ServerInterface.Server).Show();
Application.Run();
}
}
catch (Exception e)
{
if (ServerInterface != null)
ServerInterface.Dispose();
ServiceDisconnectException(e);
}
finally
{
Properties.Settings.Default.Save();
}
}
static bool SSLErrorPromptResult = false;
static bool BadCertificateHandler(string message)
{
if (!SSLErrorPromptResult)
{
var result = MessageBox.Show(message + " IT IS HIGHLY RECCOMENDED YOU DO NOT PROCEED! Continue?", "SSL Error", MessageBoxButtons.YesNo) == DialogResult.Yes;
SSLErrorPromptResult = result;
return result;
}
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());
}
public static string TextPrompt(string caption, string text)
{
Form prompt = new Form()
{
Width = 500,
Height = 150,
FormBorderStyle = FormBorderStyle.FixedDialog,
Text = caption,
StartPosition = FormStartPosition.CenterScreen,
MaximizeBox = false,
MinimizeBox = false,
};
Label textLabel = new Label() { Left = 50, Top = 20, Text = text, AutoSize = true };
TextBox textBox = new TextBox() { Left = 50, Top = 50, Width = 400 };
Button confirmation = new Button() { Text = "Ok", Left = 350, Width = 100, Top = 70, DialogResult = DialogResult.OK };
confirmation.Click += (sender, e) => { prompt.Close(); };
prompt.Controls.Add(textBox);
prompt.Controls.Add(confirmation);
prompt.Controls.Add(textLabel);
prompt.AcceptButton = confirmation;
return prompt.ShowDialog() == DialogResult.OK ? textBox.Text : null;
}
/// <summary>
/// Prompts the user to open the <see cref="GitHubLoginPrompt"/> explaining the API rate limit
/// </summary>
/// <param name="client">The <see cref="Octokit.GitHubClient"/> to use</param>
/// <returns><see langword="true"/> if the <see cref="GitHubLoginPrompt"/> ran and returned a <see cref="DialogResult.OK"/>, <see langword="false"/> otherwise</returns>
public static bool RateLimitPrompt(Octokit.GitHubClient client)
{
if (MessageBox.Show("You seem to have hit the rate limit of 60 requests per hour of the GitHub API for anonymous requests. Would you like to enter credentials to bypass this?", "Rate limited", MessageBoxButtons.YesNo) != DialogResult.Yes)
return false;
using (var D = new GitHubLoginPrompt(client))
return D.ShowDialog() == DialogResult.OK;
}
/// <summary>
/// Gets the <paramref name="owner"/> and <paramref name="name"/> of a given <paramref name="repo"/>'s remote. Shows an error if the target remote isn't GitHub
/// </summary>
/// <param name="repo">The <see cref="ITGRepository"/> to get the remote of</param>
/// <param name="owner">The owner of the remote <see cref="Octokit.Repository"/></param>
/// <param name="name">The remote <see cref="Octokit.Repository.Name"/></param>
/// <returns><see langword="true"/> if <paramref name="repo"/>'s remote was a valid GitHub <see cref="Octokit.Repository"/>, <see langword="false"/> otherwise and the user was prompted</returns>
public static bool GetRepositoryRemote(ITGRepository repo, out string owner, out string name)
{
string remote = null;
remote = repo.GetRemote(out string error);
if (remote == null)
{
MessageBox.Show(String.Format("Error retrieving remote repository: {0}", error));
owner = null;
name = null;
return false;
}
if (!remote.Contains("github.com"))
{
MessageBox.Show("Pull request support is only available for GitHub based repositories!", "Error");
owner = null;
name = null;
return false;
}
//Assume standard gh format: [(git)|(https)]://github.com/owner/repo(.git)[0-1]
//Yes use .git twice in case it was weird
var toRemove = new string[] { ".git", "/", ".git" };
foreach (string item in toRemove)
if (remote.EndsWith(item))
remote = remote.Substring(0, remote.LastIndexOf(item));
var splits = remote.Split('/');
name = splits[splits.Length - 1];
owner = splits[splits.Length - 2].Split('.')[0];
return true;
}
}
}
@@ -1,16 +0,0 @@
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("TGStation Server Control Panel")]
[assembly: AssemblyDescription("Control panel for the TG Station Server Service")]
// 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("394e7643-6b8c-416f-ab18-95ac12648cdc")]
-121
View File
@@ -1,121 +0,0 @@
//------------------------------------------------------------------------------
// <auto-generated>
// 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.
// </auto-generated>
//------------------------------------------------------------------------------
namespace TGS.ControlPanel.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.3.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;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("0")]
public int LastPageIndex {
get {
return ((int)(this["LastPageIndex"]));
}
set {
this["LastPageIndex"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("0")]
public int LastConfigPageIndex {
get {
return ((int)(this["LastConfigPageIndex"]));
}
set {
this["LastConfigPageIndex"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("True")]
public bool UpgradeRequired {
get {
return ((bool)(this["UpgradeRequired"]));
}
set {
this["UpgradeRequired"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("0")]
public int LastChatProvider {
get {
return ((int)(this["LastChatProvider"]));
}
set {
this["LastChatProvider"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
public global::System.Collections.Specialized.StringCollection RemoteLoginInfo {
get {
return ((global::System.Collections.Specialized.StringCollection)(this["RemoteLoginInfo"]));
}
set {
this["RemoteLoginInfo"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("False")]
public bool RemoteDefault {
get {
return ((bool)(this["RemoteDefault"]));
}
set {
this["RemoteDefault"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("")]
public string GitHubAPIKey {
get {
return ((string)(this["GitHubAPIKey"]));
}
set {
this["GitHubAPIKey"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("")]
public string GitHubAPIKeyEntropy {
get {
return ((string)(this["GitHubAPIKeyEntropy"]));
}
set {
this["GitHubAPIKeyEntropy"] = value;
}
}
}
}
@@ -1,30 +0,0 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="TGS.ControlPanel.Properties" GeneratedClassName="Settings">
<Profiles />
<Settings>
<Setting Name="LastPageIndex" Type="System.Int32" Scope="User">
<Value Profile="(Default)">0</Value>
</Setting>
<Setting Name="LastConfigPageIndex" Type="System.Int32" Scope="User">
<Value Profile="(Default)">0</Value>
</Setting>
<Setting Name="UpgradeRequired" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">True</Value>
</Setting>
<Setting Name="LastChatProvider" Type="System.Int32" Scope="User">
<Value Profile="(Default)">0</Value>
</Setting>
<Setting Name="RemoteLoginInfo" Type="System.Collections.Specialized.StringCollection" Scope="User">
<Value Profile="(Default)" />
</Setting>
<Setting Name="RemoteDefault" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
<Setting Name="GitHubAPIKey" Type="System.String" Scope="User">
<Value Profile="(Default)" />
</Setting>
<Setting Name="GitHubAPIKeyEntropy" Type="System.String" Scope="User">
<Value Profile="(Default)" />
</Setting>
</Settings>
</SettingsFile>
-35
View File
@@ -1,35 +0,0 @@
using System;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace TGS.ControlPanel
{
/// <summary>
/// Used to provide an ATP function for calls into an <see cref="TGS.Interface.IClient"/>
/// </summary>
#if !DEBUG
abstract
#endif
class ServerOpForm : Form
{
/// <summary>
/// Used to wrap <see cref="TGS.Interface.IClient"/> calls in a non-blocking fashion while disabling the <see cref="Form"/> and enabling the wait cursor
/// </summary>
/// <param name="action">The <see cref="TGS.Interface.IClient"/> operation to wrap</param>
/// <returns>A <see cref="Task"/> wrapping <paramref name="action"/></returns>
protected Task WrapServerOp(Action action)
{
Enabled = false;
UseWaitCursor = true;
try
{
return Task.Run(action);
}
finally
{
Enabled = true;
UseWaitCursor = false;
}
}
}
}
-151
View File
@@ -1,151 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{394E7643-6B8C-416F-AB18-95AC12648CDC}</ProjectGuid>
<OutputType>WinExe</OutputType>
<RootNamespace>TGS.ControlPanel</RootNamespace>
<AssemblyName>TGControlPanel</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup>
<ApplicationIcon>tgs.ico</ApplicationIcon>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|AnyCPU'">
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<DocumentationFile>bin\x86\Release\TGS.ControlPanel.xml</DocumentationFile>
<Optimize>true</Optimize>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<DebugType>pdbonly</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup>
<SignAssembly>false</SignAssembly>
</PropertyGroup>
<ItemGroup>
<Reference Include="Octokit, Version=0.29.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Octokit.0.29.0\lib\net45\Octokit.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms">
<HintPath>C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5.2\System.Windows.Forms.dll</HintPath>
</Reference>
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="ControlPanel\ByondPage.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="ControlPanel\ChatPage.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="CountedForm.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="GitHubLoginPrompt.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="GitHubLoginPrompt.Designer.cs">
<DependentUpon>GitHubLoginPrompt.cs</DependentUpon>
</Compile>
<Compile Include="InstanceSelector.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="InstanceSelector.Designer.cs">
<DependentUpon>InstanceSelector.cs</DependentUpon>
</Compile>
<Compile Include="Login.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Login.Designer.cs">
<DependentUpon>Login.cs</DependentUpon>
</Compile>
<Compile Include="ControlPanel\ControlPanel.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="ControlPanel\ControlPanel.Designer.cs">
<DependentUpon>ControlPanel.cs</DependentUpon>
</Compile>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Properties\Settings.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
<DependentUpon>Settings.settings</DependentUpon>
</Compile>
<Compile Include="ControlPanel\RepoPage.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="ControlPanel\ServerPage.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="..\AssemblyInfo.global.cs" />
<Compile Include="ControlPanel\StaticPage.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="TestMergeManager.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="TestMergeManager.Designer.cs">
<DependentUpon>TestMergeManager.cs</DependentUpon>
</Compile>
<Compile Include="ServerOpForm.cs">
<SubType>Form</SubType>
</Compile>
<EmbeddedResource Include="GitHubLoginPrompt.resx">
<DependentUpon>GitHubLoginPrompt.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="InstanceSelector.resx">
<DependentUpon>InstanceSelector.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Login.resx">
<DependentUpon>Login.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="ControlPanel\ControlPanel.resx">
<DependentUpon>ControlPanel.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="TestMergeManager.resx">
<DependentUpon>TestMergeManager.cs</DependentUpon>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
<None Include="packages.config" />
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\TGS.Interface\TGS.Interface.csproj">
<Project>{ac4e7e8b-f83a-481c-a8b0-8fa4e8ae59ab}</Project>
<Name>TGS.Interface</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Content Include="tgs.ico" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
-222
View File
@@ -1,222 +0,0 @@
namespace TGS.ControlPanel
{
partial class TestMergeManager
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(TestMergeManager));
this.PullRequestListBox = new System.Windows.Forms.CheckedListBox();
this.ApplyButton = new System.Windows.Forms.Button();
this.UpdateToRemoteRadioButton = new System.Windows.Forms.RadioButton();
this.UpdateToOriginRadioButton = new System.Windows.Forms.RadioButton();
this.NoUpdateRadioButton = new System.Windows.Forms.RadioButton();
this.RefreshButton = new System.Windows.Forms.Button();
this.ApplyingPullRequestsLabel = new System.Windows.Forms.Label();
this.ApplyingPullRequestsProgressBar = new System.Windows.Forms.ProgressBar();
this.AddPRButton = new System.Windows.Forms.Button();
this.AddPRNumericUpDown = new System.Windows.Forms.NumericUpDown();
((System.ComponentModel.ISupportInitialize)(this.AddPRNumericUpDown)).BeginInit();
this.SuspendLayout();
//
// PullRequestListBox
//
this.PullRequestListBox.CheckOnClick = true;
this.PullRequestListBox.FormattingEnabled = true;
this.PullRequestListBox.Location = new System.Drawing.Point(13, 13);
this.PullRequestListBox.Name = "PullRequestListBox";
this.PullRequestListBox.Size = new System.Drawing.Size(588, 199);
this.PullRequestListBox.TabIndex = 0;
this.PullRequestListBox.ThreeDCheckBoxes = true;
this.PullRequestListBox.UseWaitCursor = true;
//
// ApplyButton
//
this.ApplyButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.ApplyButton.Location = new System.Drawing.Point(544, 226);
this.ApplyButton.Name = "ApplyButton";
this.ApplyButton.Size = new System.Drawing.Size(57, 23);
this.ApplyButton.TabIndex = 2;
this.ApplyButton.Text = "Apply";
this.ApplyButton.UseVisualStyleBackColor = true;
this.ApplyButton.UseWaitCursor = true;
this.ApplyButton.Click += new System.EventHandler(this.ApplyButton_Click);
//
// UpdateToRemoteRadioButton
//
this.UpdateToRemoteRadioButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.UpdateToRemoteRadioButton.AutoSize = true;
this.UpdateToRemoteRadioButton.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242)))));
this.UpdateToRemoteRadioButton.Location = new System.Drawing.Point(13, 229);
this.UpdateToRemoteRadioButton.Name = "UpdateToRemoteRadioButton";
this.UpdateToRemoteRadioButton.Size = new System.Drawing.Size(116, 17);
this.UpdateToRemoteRadioButton.TabIndex = 3;
this.UpdateToRemoteRadioButton.TabStop = true;
this.UpdateToRemoteRadioButton.Text = "Update To Remote";
this.UpdateToRemoteRadioButton.UseVisualStyleBackColor = true;
this.UpdateToRemoteRadioButton.UseWaitCursor = true;
//
// UpdateToOriginRadioButton
//
this.UpdateToOriginRadioButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.UpdateToOriginRadioButton.AutoSize = true;
this.UpdateToOriginRadioButton.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242)))));
this.UpdateToOriginRadioButton.Location = new System.Drawing.Point(135, 229);
this.UpdateToOriginRadioButton.Name = "UpdateToOriginRadioButton";
this.UpdateToOriginRadioButton.Size = new System.Drawing.Size(106, 17);
this.UpdateToOriginRadioButton.TabIndex = 4;
this.UpdateToOriginRadioButton.TabStop = true;
this.UpdateToOriginRadioButton.Text = "Update To Origin";
this.UpdateToOriginRadioButton.UseVisualStyleBackColor = true;
this.UpdateToOriginRadioButton.UseWaitCursor = true;
//
// NoUpdateRadioButton
//
this.NoUpdateRadioButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.NoUpdateRadioButton.AutoSize = true;
this.NoUpdateRadioButton.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242)))));
this.NoUpdateRadioButton.Location = new System.Drawing.Point(247, 229);
this.NoUpdateRadioButton.Name = "NoUpdateRadioButton";
this.NoUpdateRadioButton.Size = new System.Drawing.Size(77, 17);
this.NoUpdateRadioButton.TabIndex = 5;
this.NoUpdateRadioButton.TabStop = true;
this.NoUpdateRadioButton.Text = "No Update";
this.NoUpdateRadioButton.UseVisualStyleBackColor = true;
this.NoUpdateRadioButton.UseWaitCursor = true;
//
// RefreshButton
//
this.RefreshButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.RefreshButton.Location = new System.Drawing.Point(481, 226);
this.RefreshButton.Name = "RefreshButton";
this.RefreshButton.Size = new System.Drawing.Size(57, 23);
this.RefreshButton.TabIndex = 6;
this.RefreshButton.Text = "Refresh";
this.RefreshButton.UseVisualStyleBackColor = true;
this.RefreshButton.UseWaitCursor = true;
this.RefreshButton.Click += new System.EventHandler(this.RefreshButton_Click);
//
// ApplyingPullRequestsLabel
//
this.ApplyingPullRequestsLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
this.ApplyingPullRequestsLabel.AutoSize = true;
this.ApplyingPullRequestsLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.ApplyingPullRequestsLabel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242)))));
this.ApplyingPullRequestsLabel.Location = new System.Drawing.Point(224, 92);
this.ApplyingPullRequestsLabel.Name = "ApplyingPullRequestsLabel";
this.ApplyingPullRequestsLabel.Size = new System.Drawing.Size(156, 16);
this.ApplyingPullRequestsLabel.TabIndex = 8;
this.ApplyingPullRequestsLabel.Text = "Applying Pull Requests...";
this.ApplyingPullRequestsLabel.UseWaitCursor = true;
this.ApplyingPullRequestsLabel.Visible = false;
//
// ApplyingPullRequestsProgressBar
//
this.ApplyingPullRequestsProgressBar.Location = new System.Drawing.Point(48, 112);
this.ApplyingPullRequestsProgressBar.Name = "ApplyingPullRequestsProgressBar";
this.ApplyingPullRequestsProgressBar.Size = new System.Drawing.Size(513, 23);
this.ApplyingPullRequestsProgressBar.Style = System.Windows.Forms.ProgressBarStyle.Marquee;
this.ApplyingPullRequestsProgressBar.TabIndex = 9;
this.ApplyingPullRequestsProgressBar.UseWaitCursor = true;
this.ApplyingPullRequestsProgressBar.Visible = false;
//
// AddPRButton
//
this.AddPRButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.AddPRButton.Location = new System.Drawing.Point(418, 226);
this.AddPRButton.Name = "AddPRButton";
this.AddPRButton.Size = new System.Drawing.Size(57, 23);
this.AddPRButton.TabIndex = 10;
this.AddPRButton.Text = "Add PR";
this.AddPRButton.UseVisualStyleBackColor = true;
this.AddPRButton.UseWaitCursor = true;
this.AddPRButton.Click += new System.EventHandler(this.AddPRButton_Click);
//
// AddPRNumericUpDown
//
this.AddPRNumericUpDown.Location = new System.Drawing.Point(331, 229);
this.AddPRNumericUpDown.Maximum = new decimal(new int[] {
10000000,
0,
0,
0});
this.AddPRNumericUpDown.Minimum = new decimal(new int[] {
1,
0,
0,
0});
this.AddPRNumericUpDown.Name = "AddPRNumericUpDown";
this.AddPRNumericUpDown.Size = new System.Drawing.Size(81, 20);
this.AddPRNumericUpDown.TabIndex = 11;
this.AddPRNumericUpDown.UseWaitCursor = true;
this.AddPRNumericUpDown.Value = new decimal(new int[] {
1,
0,
0,
0});
//
// TestMergeManager
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(39)))), ((int)(((byte)(40)))), ((int)(((byte)(34)))));
this.ClientSize = new System.Drawing.Size(613, 261);
this.Controls.Add(this.AddPRNumericUpDown);
this.Controls.Add(this.AddPRButton);
this.Controls.Add(this.ApplyingPullRequestsProgressBar);
this.Controls.Add(this.ApplyingPullRequestsLabel);
this.Controls.Add(this.RefreshButton);
this.Controls.Add(this.NoUpdateRadioButton);
this.Controls.Add(this.UpdateToOriginRadioButton);
this.Controls.Add(this.UpdateToRemoteRadioButton);
this.Controls.Add(this.ApplyButton);
this.Controls.Add(this.PullRequestListBox);
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.MaximizeBox = false;
this.Name = "TestMergeManager";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen;
this.Text = "Test Merge Manager";
this.UseWaitCursor = true;
((System.ComponentModel.ISupportInitialize)(this.AddPRNumericUpDown)).EndInit();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.CheckedListBox PullRequestListBox;
private System.Windows.Forms.Button ApplyButton;
private System.Windows.Forms.RadioButton UpdateToRemoteRadioButton;
private System.Windows.Forms.RadioButton UpdateToOriginRadioButton;
private System.Windows.Forms.RadioButton NoUpdateRadioButton;
private System.Windows.Forms.Button RefreshButton;
private System.Windows.Forms.Label ApplyingPullRequestsLabel;
private System.Windows.Forms.ProgressBar ApplyingPullRequestsProgressBar;
private System.Windows.Forms.Button AddPRButton;
private System.Windows.Forms.NumericUpDown AddPRNumericUpDown;
}
}
-384
View File
@@ -1,384 +0,0 @@
using Octokit;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
using TGS.Interface;
using TGS.Interface.Components;
namespace TGS.ControlPanel
{
sealed partial class TestMergeManager : ServerOpForm
{
/// <summary>
/// Error message format used when <see cref="ITGRepository.MergedPullRequests(out string)"/> fails
/// </summary>
const string MergedPullsError = "Error retrieving currently merged pull requests: {0}";
/// <summary>
/// The <see cref="IInstance"/>to handle pull requests for
/// </summary>
readonly IInstance currentInterface;
/// <summary>
/// The <see cref="GitHubClient"/> to use to read PR lists
/// </summary>
readonly GitHubClient client;
/// <summary>
/// The owner of the target <see cref="Repository"/>
/// </summary>
string repoOwner;
/// <summary>
/// The name of the target <see cref="Repository"/>
/// </summary>
string repoName;
/// <summary>
/// Construct a <see cref="TestMergeManager"/>
/// </summary>
/// <param name="interfaceToUse">The <see cref="IInstance"/> to manage pull requests for</param>
/// <param name="clientToUse">The <see cref="GitHubClient"/> to use for getting pull request information</param>
public TestMergeManager(IInstance interfaceToUse, GitHubClient clientToUse)
{
InitializeComponent();
DialogResult = DialogResult.Cancel;
UpdateToRemoteRadioButton.Checked = true;
currentInterface = interfaceToUse;
client = clientToUse;
Load += PullRequestManager_Load;
PullRequestListBox.ItemCheck += PullRequestListBox_ItemCheck;
}
/// <summary>
/// Called when an item in <see cref="PullRequestListBox"/> is checked or unchecked. Unchecks opposing PRs
/// </summary>
/// <param name="sender">The sender of the event</param>
/// <param name="e">The <see cref="ItemCheckEventArgs"/></param>
void PullRequestListBox_ItemCheck(object sender, ItemCheckEventArgs e)
{
if (e.NewValue != CheckState.Checked)
return;
//let's uncheck the opposing PR# if this is one of the outdated/updated testmerge
var item = (string)PullRequestListBox.Items[e.Index];
var prefix = item.Split(' ')[0];
for (var I = 0; I < PullRequestListBox.Items.Count; ++I)
{
var S = (string)PullRequestListBox.Items[I];
if (S.Split(' ')[0] == prefix && S != item)
{
PullRequestListBox.SetItemChecked(I, false);
break;
}
}
}
/// <summary>
/// Populate <see cref="PullRequestListBox"/> with <see cref="PullRequestInfo"/> from github and check off ones that are currently merged. Prompts the user to login to GitHub if they hit the rate limit
/// </summary>
async void LoadPullRequests()
{
try
{
Enabled = false;
UseWaitCursor = true;
try
{
PullRequestListBox.Items.Clear();
var repo = currentInterface.Repository;
string error = null;
List<PullRequestInfo> pulls = null;
//get started on this while we're processing here
var pullsRequest = Task.Run(() => pulls = repo.MergedPullRequests(out error));
//Search for open PRs
Enabled = false;
UseWaitCursor = true;
SearchIssuesResult result;
try
{
result = await client.Search.SearchIssues(new SearchIssuesRequest
{
Repos = new RepositoryCollection { { repoOwner, repoName } },
State = ItemState.Open,
Type = IssueTypeQualifier.PullRequest
});
}
finally
{
Enabled = true;
UseWaitCursor = false;
}
//now we need to know what's merged
await pullsRequest;
if (pulls == null)
MessageBox.Show(String.Format(MergedPullsError, error));
//insert the open pull requests, checking already merged once
foreach (var I in result.Items)
if (!pulls.Any(x => x.Number == I.Number))
InsertPullRequest(I, false, CheckState.Unchecked);
//insert remaining merged pulls
foreach (var I in pulls)
{
var pr = await client.PullRequest.Get(repoOwner, repoName, I.Number);
var outdated = pr.Head.Sha != I.Sha;
var mergedOrOutdated = pr.Merged || outdated;
InsertPullRequest(await client.Issue.Get(repoOwner, repoName, I.Number), true, mergedOrOutdated ? CheckState.Indeterminate : CheckState.Checked, String.Format("{1}{0}", mergedOrOutdated ? String.Format(" - {0}", I.Sha) : String.Empty, pr.Merged ? " - MERGED ON REMOTE: " : (outdated ? " - OUTDATED: " : String.Empty)));
}
}
finally
{
Enabled = true;
UseWaitCursor = false;
}
}
catch (ForbiddenException)
{
if (client.Credentials.AuthenticationType == AuthenticationType.Anonymous) //assume request limit hit
{
if(Program.RateLimitPrompt(client))
LoadPullRequests();
}
else
throw;
}
}
/// <summary>
/// Format an entry for <paramref name="issue"/> and insert it into <see cref="PullRequestListBox"/>
/// </summary>
/// <param name="issue">The <see cref="Issue"/> to format, must contain a <see cref="PullRequest"/></param>
/// <param name="prioritize">If this or <paramref name="checkState"/> is mpt <see cref="CheckState.Unchecked"/>, <paramref name="issue"/> will be inserted at the top of <see cref="PullRequestListBox"/> as opposed to the bottom</param>
/// <param name="checkState">The <see cref="CheckState"/> of the item</param>
/// <param name="append">An optional <see cref="string"/> to append to the entry before it's inserted</param>
void InsertPullRequest(Issue issue, bool prioritize, CheckState checkState, string append = null)
{
bool needsTesting = issue.Labels.Any(x => x.Name.ToLower().Contains("test"));
prioritize |= needsTesting;
var itemString = String.Format("#{0} - {1}{2}{3}", issue.Number, issue.Title, needsTesting ? " - TESTING REQUESTED" : String.Empty, append ?? String.Empty);
InsertItem(itemString, prioritize, checkState);
}
/// <summary>
/// Insert an <paramref name="itemString"/> into <see cref="PullRequestListBox"/>
/// </summary>
/// <param name="itemString">The <see cref="string"/> to insert</param>
/// <param name="prioritize">If this or <paramref name="checkState"/> is not <see cref="CheckState.Unchecked"/>, <paramref name="itemString"/> will be inserted at the top of <see cref="PullRequestListBox"/> as opposed to the bottom</param>
/// <param name="checkState">The <see cref="CheckState"/> of the item</param>
void InsertItem(string itemString, bool prioritize, CheckState checkState)
{
prioritize = prioritize || checkState != CheckState.Unchecked;
if (prioritize)
{
PullRequestListBox.Items.Insert(0, itemString);
PullRequestListBox.SetItemCheckState(0, checkState);
}
else
PullRequestListBox.Items.Add(itemString, checkState);
}
/// <summary>
/// Called when the <see cref="TestMergeManager"/> is loaded. Sets <see cref="repoOwner"/> and <see cref="repoName"/>
/// </summary>
/// <param name="sender">The sender of the event</param>
/// <param name="e">The <see cref="EventArgs"/></param>
void PullRequestManager_Load(object sender, EventArgs e)
{
Enabled = false;
var repo = currentInterface.Repository;
if(!Program.GetRepositoryRemote(repo, out repoOwner, out repoName))
{
Close();
return;
}
LoadPullRequests();
}
/// <summary>
/// Calls <see cref="ITGRepository.GenerateChangelog(out string)"/> and shows the user an error prompt if it fails
/// </summary>
/// <param name="repo">The <see cref="ITGRepository"/> to call <see cref="ITGRepository.GenerateChangelog(out string)"/> on</param>
async void GenerateChangelog(ITGRepository repo)
{
string error = null;
await WrapServerOp(() => repo.GenerateChangelog(out error));
if (error != null)
MessageBox.Show(String.Format("Error generating changelog: {0}", error));
}
/// <summary>
/// Called when the <see cref="ApplyButton"/> is clicked. Calls <see cref="ITGRepository.Update(bool)"/> if necessary, merge pull requests, and call <see cref="ITGCompiler.Compile(bool)"/>. Closes the <see cref="TestMergeManager"/> if appropriate
/// </summary>
/// <param name="sender">The sender of the event</param>
/// <param name="e">The <see cref="EventArgs"/></param>
async void ApplyButton_Click(object sender, EventArgs e)
{
Enabled = false;
UseWaitCursor = true;
ApplyingPullRequestsLabel.Visible = true;
ApplyingPullRequestsProgressBar.Visible = true;
PullRequestListBox.Visible = false;
try
{
//so first collect a list of pulls that are checked
var pulls = new List<PullRequestInfo>();
foreach (int I in PullRequestListBox.CheckedIndices)
{
var S = (string)PullRequestListBox.Items[I];
string mergedSha = null;
var splits = S.Split(' ');
var mergedOnRemote = S.Contains(" - MERGED ON REMOTE: ");
if (mergedOnRemote && UpdateToRemoteRadioButton.Checked)
continue;
if ((S.Contains(" - OUTDATED: " ) || mergedOnRemote) && PullRequestListBox.GetItemCheckState(I) == CheckState.Indeterminate)
mergedSha = splits[splits.Length - 1];
var key = Convert.ToInt32((splits[0].Substring(1)));
try
{
pulls.Add(new PullRequestInfo(key, mergedSha));
}
catch
{
MessageBox.Show(String.Format("Checked both keep and update option for #{0}", key), "Error");
return;
}
}
var repo = currentInterface.Repository;
string error = null;
//Do standard repo updates
if (UpdateToRemoteRadioButton.Checked)
await WrapServerOp(() => error = repo.Update(true));
else if (UpdateToOriginRadioButton.Checked)
await WrapServerOp(() => error = repo.Reset(true));
if (error != null)
{
MessageBox.Show(String.Format("Error updating repository: {0}", error));
return;
}
if (UpdateToRemoteRadioButton.Checked)
{
GenerateChangelog(repo);
error = await Task.Run(() => repo.SynchronizePush());
}
//Merge the PRs, collect errors
var errors = await Task.Run(() => repo.MergePullRequests(pulls, false));
if (errors != null)
{
//Show any errors
for (var I = 0; I < errors.Count(); ++I)
{
var err = errors.ElementAt(I);
if (err != null)
MessageBox.Show(err, String.Format("Error merging PR #{0}", pulls[I].Number));
}
return;
}
if (pulls.Count > 0)
//regen the changelog
GenerateChangelog(repo);
//Start the compile
var compileStarted = await Task.Run(() => currentInterface.Compiler.Compile(pulls.Count == 1));
if (error != null)
MessageBox.Show(String.Format("Error sychronizing repo: {0}", error));
if (!compileStarted)
MessageBox.Show("Could not start compilation!");
else
MessageBox.Show("Test merges updated and compilation started!");
}
finally
{
Enabled = true;
UseWaitCursor = false;
ApplyingPullRequestsLabel.Visible = false;
ApplyingPullRequestsProgressBar.Visible = false;
PullRequestListBox.Visible = true;
}
}
/// <summary>
/// Called when the <see cref="RefreshButton"/> is clicked
/// </summary>
/// <param name="sender">The sender of the event</param>
/// <param name="e">The <see cref="EventArgs"/></param>
void RefreshButton_Click(object sender, EventArgs e)
{
LoadPullRequests();
}
/// <summary>
/// Called when the <see cref="AddPRButton"/> is clicked. Adds the PR with the number in <see cref="AddPRNumericUpDown"/> to <see cref="PullRequestListBox"/> or shows an error prompt if it doesn't/already exists
/// </summary>
/// <param name="sender">The sender of the event</param>
/// <param name="e">The <see cref="EventArgs"/></param>
async void AddPRButton_Click(object sender, EventArgs e)
{
Enabled = false;
UseWaitCursor = true;
try
{
IList<PullRequestInfo> pulls = null;
string error = null;
var mergedPullsTask = WrapServerOp(() => pulls = currentInterface.Repository.MergedPullRequests(out error));
int PRNumber;
try
{
PRNumber = Convert.ToInt32(AddPRNumericUpDown.Value);
}
catch
{
MessageBox.Show("Invalid PR number!");
return;
}
var found = false;
var asString = PRNumber.ToString();
foreach (var I in PullRequestListBox.Items)
if (((string)I).Split(' ')[0].Substring(1) == asString)
{
found = true;
break;
}
if (found)
{
MessageBox.Show("That PR is already in the list!");
return;
}
await mergedPullsTask;
if (pulls == null)
MessageBox.Show(String.Format(MergedPullsError, error));
//get the PR in question
var PR = await client.Issue.Get(repoName, repoOwner, PRNumber);
if(PR == null ||PR.PullRequest == null)
{
MessageBox.Show("That doesn't seem to be a valid PR!");
return;
}
InsertPullRequest(PR, true, CheckState.Unchecked);
}
finally
{
UseWaitCursor = false;
Enabled = true;
}
}
}
}
File diff suppressed because it is too large Load Diff
-4
View File
@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Octokit" version="0.29.0" targetFramework="net452" />
</packages>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 120 KiB

-4
View File
@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Weavers>
<Costura />
</Weavers>
-227
View File
@@ -1,227 +0,0 @@
using System;
using System.Diagnostics;
using System.Reflection;
namespace TGS.Installer.UI
{
partial class Main
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
CleanTempDir();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Main));
this.ProgressBar = new System.Windows.Forms.ProgressBar();
this.DeskShortcutsCheckbox = new System.Windows.Forms.CheckBox();
this.StartShortcutsCheckbox = new System.Windows.Forms.CheckBox();
this.InstallingLabel = new System.Windows.Forms.Label();
this.PathTextBox = new System.Windows.Forms.TextBox();
this.SelectPathButton = new System.Windows.Forms.Button();
this.label1 = new System.Windows.Forms.Label();
this.VersionLabel = new System.Windows.Forms.Label();
this.InstallButton = new System.Windows.Forms.Button();
this.ShowLogCheckbox = new System.Windows.Forms.CheckBox();
this.TargetVersionLabel = new System.Windows.Forms.Label();
this.InstallCancelButton = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// ProgressBar
//
this.ProgressBar.Location = new System.Drawing.Point(11, 119);
this.ProgressBar.Name = "ProgressBar";
this.ProgressBar.Size = new System.Drawing.Size(386, 22);
this.ProgressBar.TabIndex = 0;
//
// DeskShortcutsCheckbox
//
this.DeskShortcutsCheckbox.AutoSize = true;
this.DeskShortcutsCheckbox.Checked = true;
this.DeskShortcutsCheckbox.CheckState = System.Windows.Forms.CheckState.Checked;
this.DeskShortcutsCheckbox.Font = new System.Drawing.Font("Verdana", 12F);
this.DeskShortcutsCheckbox.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242)))));
this.DeskShortcutsCheckbox.Location = new System.Drawing.Point(12, 61);
this.DeskShortcutsCheckbox.Name = "DeskShortcutsCheckbox";
this.DeskShortcutsCheckbox.Size = new System.Drawing.Size(214, 22);
this.DeskShortcutsCheckbox.TabIndex = 2;
this.DeskShortcutsCheckbox.Text = "Add Desktop Shortcuts";
this.DeskShortcutsCheckbox.UseVisualStyleBackColor = true;
//
// StartShortcutsCheckbox
//
this.StartShortcutsCheckbox.AutoSize = true;
this.StartShortcutsCheckbox.Checked = true;
this.StartShortcutsCheckbox.CheckState = System.Windows.Forms.CheckState.Checked;
this.StartShortcutsCheckbox.Font = new System.Drawing.Font("Verdana", 12F);
this.StartShortcutsCheckbox.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242)))));
this.StartShortcutsCheckbox.Location = new System.Drawing.Point(289, 61);
this.StartShortcutsCheckbox.Name = "StartShortcutsCheckbox";
this.StartShortcutsCheckbox.Size = new System.Drawing.Size(236, 22);
this.StartShortcutsCheckbox.TabIndex = 3;
this.StartShortcutsCheckbox.Text = "Add Start Menu Shortcuts";
this.StartShortcutsCheckbox.UseVisualStyleBackColor = true;
//
// InstallingLabel
//
this.InstallingLabel.AutoSize = true;
this.InstallingLabel.Font = new System.Drawing.Font("Verdana", 12F);
this.InstallingLabel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242)))));
this.InstallingLabel.Location = new System.Drawing.Point(8, 37);
this.InstallingLabel.Name = "InstallingLabel";
this.InstallingLabel.Size = new System.Drawing.Size(52, 18);
this.InstallingLabel.TabIndex = 1;
this.InstallingLabel.Text = "Path:";
//
// PathTextBox
//
this.PathTextBox.Location = new System.Drawing.Point(66, 35);
this.PathTextBox.Name = "PathTextBox";
this.PathTextBox.ReadOnly = true;
this.PathTextBox.Size = new System.Drawing.Size(413, 20);
this.PathTextBox.TabIndex = 4;
//
// SelectPathButton
//
this.SelectPathButton.Location = new System.Drawing.Point(485, 35);
this.SelectPathButton.Name = "SelectPathButton";
this.SelectPathButton.Size = new System.Drawing.Size(40, 20);
this.SelectPathButton.TabIndex = 6;
this.SelectPathButton.Text = "...";
this.SelectPathButton.UseVisualStyleBackColor = true;
this.SelectPathButton.Click += new System.EventHandler(this.SelectPathButton_Click);
//
// label1
//
this.label1.AutoSize = true;
this.label1.Font = new System.Drawing.Font("Verdana", 12F);
this.label1.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242)))));
this.label1.Location = new System.Drawing.Point(8, 9);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(155, 18);
this.label1.TabIndex = 7;
this.label1.Text = "Detected Version:";
//
// VersionLabel
//
this.VersionLabel.AutoSize = true;
this.VersionLabel.Font = new System.Drawing.Font("Verdana", 12F);
this.VersionLabel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242)))));
this.VersionLabel.Location = new System.Drawing.Point(169, 9);
this.VersionLabel.Name = "VersionLabel";
this.VersionLabel.Size = new System.Drawing.Size(228, 18);
this.VersionLabel.TabIndex = 8;
this.VersionLabel.Text = "Unknown (No service running)";
//
// InstallButton
//
this.InstallButton.Location = new System.Drawing.Point(400, 89);
this.InstallButton.Name = "InstallButton";
this.InstallButton.Size = new System.Drawing.Size(125, 22);
this.InstallButton.TabIndex = 9;
this.InstallButton.Text = "Install";
this.InstallButton.UseVisualStyleBackColor = true;
this.InstallButton.Click += new System.EventHandler(this.InstallButton_Click);
//
// ShowLogCheckbox
//
this.ShowLogCheckbox.AutoSize = true;
this.ShowLogCheckbox.Font = new System.Drawing.Font("Verdana", 12F);
this.ShowLogCheckbox.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242)))));
this.ShowLogCheckbox.Location = new System.Drawing.Point(289, 90);
this.ShowLogCheckbox.Name = "ShowLogCheckbox";
this.ShowLogCheckbox.Size = new System.Drawing.Size(105, 22);
this.ShowLogCheckbox.TabIndex = 10;
this.ShowLogCheckbox.Text = "Show Log";
this.ShowLogCheckbox.UseVisualStyleBackColor = true;
//
// TargetVersionLabel
//
this.TargetVersionLabel.AutoSize = true;
this.TargetVersionLabel.Font = new System.Drawing.Font("Verdana", 12F);
this.TargetVersionLabel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242)))));
this.TargetVersionLabel.Location = new System.Drawing.Point(8, 90);
this.TargetVersionLabel.Name = "TargetVersionLabel";
this.TargetVersionLabel.Size = new System.Drawing.Size(132, 18);
this.TargetVersionLabel.TabIndex = 11;
this.TargetVersionLabel.Text = "Target Version: v" + FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location).FileVersion;
//
// InstallCancelButton
//
this.InstallCancelButton.Enabled = false;
this.InstallCancelButton.Location = new System.Drawing.Point(400, 119);
this.InstallCancelButton.Name = "InstallCancelButton";
this.InstallCancelButton.Size = new System.Drawing.Size(125, 22);
this.InstallCancelButton.TabIndex = 12;
this.InstallCancelButton.Text = "Cancel";
this.InstallCancelButton.UseVisualStyleBackColor = true;
this.InstallCancelButton.Click += new System.EventHandler(this.CancelButton_Click);
//
// Main
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(39)))), ((int)(((byte)(40)))), ((int)(((byte)(34)))));
this.ClientSize = new System.Drawing.Size(537, 153);
this.FormClosing += Main_FormClosing;
this.Controls.Add(this.InstallCancelButton);
this.Controls.Add(this.TargetVersionLabel);
this.Controls.Add(this.ShowLogCheckbox);
this.Controls.Add(this.InstallButton);
this.Controls.Add(this.VersionLabel);
this.Controls.Add(this.label1);
this.Controls.Add(this.SelectPathButton);
this.Controls.Add(this.PathTextBox);
this.Controls.Add(this.StartShortcutsCheckbox);
this.Controls.Add(this.DeskShortcutsCheckbox);
this.Controls.Add(this.InstallingLabel);
this.Controls.Add(this.ProgressBar);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "Main";
this.Text = "/tg/station Server Installer";
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.ProgressBar ProgressBar;
private System.Windows.Forms.CheckBox DeskShortcutsCheckbox;
private System.Windows.Forms.CheckBox StartShortcutsCheckbox;
private System.Windows.Forms.Label InstallingLabel;
private System.Windows.Forms.TextBox PathTextBox;
private System.Windows.Forms.Button SelectPathButton;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Label VersionLabel;
private System.Windows.Forms.Button InstallButton;
private System.Windows.Forms.CheckBox ShowLogCheckbox;
private System.Windows.Forms.Label TargetVersionLabel;
private System.Windows.Forms.Button InstallCancelButton;
}
}
-461
View File
@@ -1,461 +0,0 @@
using Microsoft.Deployment.WindowsInstaller;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.ServiceProcess;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using TGS.Interface;
using TGS.Interface.Components;
using TGS.Server;
namespace TGS.Installer.UI
{
partial class Main : Form
{
const string DefaultInstallDir = "TG Station Server"; //keep this in sync with the msi installer
string tempDir;
bool installing = false;
bool cancelled = false;
bool pathIsDefault = true;
/// <summary>
/// If we should attempt to make a <see cref="ServerConfig"/> for the new install
/// </summary>
bool attemptNetSettingsMigration = false;
IClient Interface;
/// <summary>
/// Construct an installer form
/// </summary>
public Main()
{
InitializeComponent();
SetupTempDir();
CheckForExistingVersion();
PathTextBox.Text = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), DefaultInstallDir);
}
void SetupTempDir()
{
tempDir = Path.Combine(Path.GetTempPath(), "TGS3InstallerTempDir");
try
{
if (File.Exists(tempDir))
File.Delete(tempDir);
else if (Directory.Exists(tempDir))
Directory.Delete(tempDir, true);
}
catch { }
if (File.Exists(tempDir) || Directory.Exists(tempDir))
{
tempDir = Path.GetTempFileName();
File.Delete(tempDir); //we want a dir not a file
}
try
{
Directory.CreateDirectory(tempDir);
}
catch
{
tempDir = null;
}
}
void CleanTempDir() {
if(tempDir != null)
try
{
Directory.Delete(tempDir, true);
}
catch { }
}
void CheckForExistingVersion() {
Interface = new Client();
var verifiedConnection = Interface.ConnectionStatus().HasFlag(ConnectivityLevel.Administrator);
try
{
var realVersion = Interface.Server.Version;
var isV0 = realVersion < new Version(3, 1, 0, 0);
if (isV0) //OH GOD!!!!
MessageBox.Show("Upgrading from version 3.0 may trigger a bug that can delete /config and /data. IT IS STRONGLY RECCOMMENDED THAT YOU BACKUP THESE FOLDERS BEFORE UPDATING!", "Warning");
var isUnderV2 = isV0 || realVersion < new Version(3, 2, 0, 0);
if (isUnderV2)
//Friendly reminger
MessageBox.Show("Upgrading to service version 3.2 will break the 3.1 DMAPI. It is recommended you update your game to the 3.2 API before updating the servive to avoid having to trigger hard restarts.", "Note");
attemptNetSettingsMigration = isUnderV2;
}
catch
{
if (verifiedConnection)
VersionLabel.Text = "< v3.0.85.0 (Missing ITGService.Version())";
}
}
bool ConfirmDangerousUpgrade()
{
return MessageBox.Show("Unable connect to service! Existing DreamDaemon instances will be terminated. Continue?", "Warning", MessageBoxButtons.YesNo) == DialogResult.Yes;
}
bool TellServiceWereComingForThem()
{
var connectionVerified = Interface.ConnectionStatus().HasFlag(ConnectivityLevel.Administrator);
try
{
Interface.Server.Management.PrepareForUpdate();
Thread.Sleep(3000); //chat messages
return true;
}
catch
{
return ConfirmDangerousUpgrade();
}
}
private void Main_FormClosing(object sender, FormClosingEventArgs e)
{
e.Cancel = installing; //not allowed, nununu
}
enum PKillType
{
Killed,
Aborted,
NoneFound,
}
PKillType PromptKillProcesses(string name)
{
var processes = new List<Process>(Process.GetProcessesByName(name));
processes.RemoveAll(x => x.HasExited);
if(processes.Count > 0)
{
if (MessageBox.Show(String.Format("Found {1} running instance{2} of {0}.exe! Shall I terminate {3}?", name, processes.Count, processes.Count > 1 ? "s" : "", processes.Count > 1 ? "them" : "it"), "Warning", MessageBoxButtons.YesNo) != DialogResult.Yes)
return PKillType.Aborted;
foreach (var P in processes)
{
if (!P.HasExited)
{
P.Kill();
P.WaitForExit();
}
P.Dispose();
}
return PKillType.Killed;
}
return PKillType.NoneFound;
}
static string TextPrompt(string caption, string text)
{
Form prompt = new Form()
{
Width = 500,
Height = 150,
FormBorderStyle = FormBorderStyle.FixedDialog,
Text = caption,
StartPosition = FormStartPosition.CenterScreen,
MaximizeBox = false,
MinimizeBox = false,
};
Label textLabel = new Label() { Left = 50, Top = 20, Text = text, AutoSize = true };
TextBox textBox = new TextBox() { Left = 50, Top = 50, Width = 400 };
Button confirmation = new Button() { Text = "Ok", Left = 350, Width = 100, Top = 70, DialogResult = DialogResult.OK };
confirmation.Click += (sender, e) => { prompt.Close(); };
prompt.Controls.Add(textBox);
prompt.Controls.Add(confirmation);
prompt.Controls.Add(textLabel);
prompt.AcceptButton = confirmation;
return prompt.ShowDialog() == DialogResult.OK ? textBox.Text : null;
}
bool HandleNoConfigMigration(ServerConfig sc)
{
var path = @"C:\TGSTATION-SERVER-3";
if (Directory.Exists(path)) {
sc.InstancePaths.Add(path);
new InstanceConfig(path).Save();
return MessageBox.Show(String.Format("Unable to migrate settings, default config created at {0}. You will need to reconfigure your server instance. Continue with installation?", path), "Migration Error", MessageBoxButtons.YesNo) == DialogResult.Yes;
}
return MessageBox.Show("All config migrations have failed. You will need to fully recreate your server. Continue with installation?", "Migration Failure", MessageBoxButtons.YesNo) == DialogResult.Yes;
}
/// <summary>
/// Migrate to the new <see cref="ServerConfig"/> since the <see cref="Server.Server"/> won't know about it until it's upgraded
/// </summary>
bool AttemptMigrationOfNetSettings()
{
if (!attemptNetSettingsMigration)
return true;
var sc = new ServerConfig();
try
{
sc.PythonPath = Interface.Server.Management.PythonPath();
}
catch { }
try
{
sc.RemoteAccessPort = Interface.Server.Management.RemoteAccessPort();
}
catch { }
try
{
foreach (var I in Interface.Server.Instances)
sc.InstancePaths.Add(I.Metadata.Path);
}
catch { }
try
{
if (sc.InstancePaths.Count > 0) //nice suprise, this is uneeded
return true;
//stopping the service here is MANDATORY to get the correct reattach values
using (var controller = new ServiceController("TG Station Server"))
{
controller.Stop();
controller.WaitForStatus(ServiceControllerStatus.Stopped);
//we need to find the old user.config
//check wow64 first
var path = @"C:\Windows\SysWOW64\config\systemprofile\AppData\Local\TGServerService";
if (!Directory.Exists(path))
{
//ok... check the System32 path
path = @"C:\Windows\System32\config\systemprofile\AppData\Local\TGServerService";
if (!Directory.Exists(path))
{
//well, i'm out of ideas, just use the default location
var res = HandleNoConfigMigration(sc);
if(!res)
{
controller.Start();
return false;
}
}
}
//now who knows wtf windows calls the damn folder
//take our best guess based on last modified time
DirectoryInfo lastModified = null;
foreach (var D in new DirectoryInfo(path).GetDirectories())
if (lastModified == null || D.LastWriteTime > lastModified.LastWriteTime)
lastModified = D;
if (lastModified == null)
{
//well, i'm out of ideas, just use the default location
var res = HandleNoConfigMigration(sc);
if (!res)
{
controller.Start();
return false;
}
}
var next = lastModified;
lastModified = null;
foreach (var D in next.GetDirectories())
if (lastModified == null || D.LastWriteTime > lastModified.LastWriteTime)
lastModified = D;
if (lastModified == null)
{
//well, i'm out of ideas, just use the default location
var res = HandleNoConfigMigration(sc);
if (!res)
{
controller.Start();
return false;
}
}
path = Path.Combine(tempDir, "user.config");
var netConfigPath = Path.Combine(lastModified.FullName, "user.config");
File.Copy(netConfigPath, path, true);
new InstanceConfig(tempDir).Save();
var instanceConfigPath = Path.Combine(tempDir, "Instance.json");
if (MessageBox.Show(String.Format("The 3.2 settings migration is a manual process, please open \"{0}\" with your favorite text editor and copy the values under TGServerService.Properties.Settings to the relevent fields at \"{1}\". If something in the original config appears wrong to you, correct it in the new config, but do not modify the \"Version\", \"Enabled\", or \"Name\" fields at all. See field mappings here: https://github.com/tgstation/tgstation-server/blob/a372b22fd3367dd60ee0cbebd9210f4b072c952d/TGServerService/DeprecatedInstanceConfig.cs#L23-L39", path, instanceConfigPath), "Manual Migration Required", MessageBoxButtons.OKCancel) != DialogResult.OK)
{
controller.Start();
return false;
}
var name = TextPrompt("Set Instance Directory", String.Format("Please enter the ServerDirectory entry from the original config here.{0}Use backslashes and uppercase letters. Leave this blank if it is not present.", Environment.NewLine));
if (name == null)
{
controller.Start();
return false;
}
if (String.IsNullOrWhiteSpace(name))
name = @"C:\TGSTATION-SERVER-3";
sc.InstancePaths.Add(name);
if (MessageBox.Show(String.Format("Please confirm you have finished copying settings from \"{0}\" to \"{1}\"!", path, instanceConfigPath), "Last Confirmation", MessageBoxButtons.OKCancel) != DialogResult.OK)
{
controller.Start();
return false;
}
//validate it for good measure
try
{
InstanceConfig.Load(tempDir);
}
catch (Exception e)
{
MessageBox.Show(String.Format("JSON Validation Error: {0}", e.Message));
controller.Start();
return false;
}
File.Copy(instanceConfigPath, Path.Combine(name, "Instance.json"), true);
return true;
}
}
finally
{
Directory.CreateDirectory(Server.Server.MigrationConfigDirectory);
sc.Save(Server.Server.MigrationConfigDirectory);
}
}
async void DoInstall()
{
string logfile = null;
try
{
while (true)
{
var res = PromptKillProcesses("TGCommandLine");
if (res == PKillType.Aborted)
return;
else if (res == PKillType.Killed)
continue;
res = PromptKillProcesses("TGControlPanel");
if (res == PKillType.Aborted)
return;
else if (res == PKillType.Killed)
continue;
break;
}
if (!TellServiceWereComingForThem())
return;
if (!AttemptMigrationOfNetSettings())
return;
var args = new List<string>();
if (!pathIsDefault)
args.Add(String.Format("INSTALLFOLDER=\"{0}\"", PathTextBox.Text));
if (StartShortcutsCheckbox.Checked)
args.Add("INSTALLSHORTCUTSTART=1");
if (DeskShortcutsCheckbox.Checked)
args.Add("INSTALLSHORTCUTDESK=1");
args.Add("REBOOT=R");
SelectPathButton.Enabled = false;
PathTextBox.Enabled = false;
DeskShortcutsCheckbox.Enabled = false;
StartShortcutsCheckbox.Enabled = false;
InstallButton.Enabled = false;
ShowLogCheckbox.Enabled = false;
InstallButton.Text = "Installing...";
var msipath = Path.Combine(tempDir, "TGS.Installer.msi");
File.WriteAllBytes(msipath, Properties.Resources.TGSInstaller);
File.WriteAllBytes(Path.Combine(tempDir, "cab1.cab"), Properties.Resources.cab1);
ProgressBar.Style = ProgressBarStyle.Marquee;
InstallCancelButton.Enabled = true;
if (ShowLogCheckbox.Checked)
{
logfile = Path.Combine(tempDir, "tgsinstall.log");
Microsoft.Deployment.WindowsInstaller.Installer.EnableLog(InstallLogModes.Verbose | InstallLogModes.PropertyDump, logfile);
}
var cl = String.Join(" ", args);
// TODO: Uncomment this when OnUIUpdate is more robust
// Microsoft.Deployment.WindowsInstaller.Installer.SetInternalUI(InstallUIOptions.Silent);
Microsoft.Deployment.WindowsInstaller.Installer.SetExternalUI(OnUIUpdate, InstallLogModes.Progress);
await Task.Run(() => Microsoft.Deployment.WindowsInstaller.Installer.InstallProduct(msipath, cl));
if (cancelled)
{
MessageBox.Show("Operation cancelled!");
return;
}
}
catch (Exception ex)
{
MessageBox.Show("Error: " + ex.ToString());
return;
}
finally
{
InstallCancelButton.Enabled = false;
ShowLogCheckbox.Enabled = true;
SelectPathButton.Enabled = true;
PathTextBox.Enabled = true;
DeskShortcutsCheckbox.Enabled = true;
StartShortcutsCheckbox.Enabled = true;
ProgressBar.Style = ProgressBarStyle.Blocks;
InstallButton.Enabled = true;
InstallButton.Text = "Install";
installing = false;
cancelled = false;
if (ShowLogCheckbox.Checked && logfile != null)
try
{
Process.Start(logfile).WaitForInputIdle();
}
catch { }
}
MessageBox.Show("Success!");
Application.Exit();
}
MessageResult OnUIUpdate(InstallMessage messageType, string message, MessageButtons buttons, MessageIcon icon, MessageDefaultButton defaultButton)
{
if (cancelled && installing)
{
installing = false;
return MessageResult.Cancel;
}
return MessageResult.OK;
}
private void InstallButton_Click(object sender, EventArgs e)
{
DoInstall();
}
private void SelectPathButton_Click(object sender, EventArgs e)
{
var fbd = new FolderBrowserDialog()
{
Description = "Select where you would like to install the service executables. This isn't where an actual server instance is created.",
ShowNewFolderButton = true
};
if (fbd.ShowDialog() != DialogResult.OK)
return;
pathIsDefault = false;
PathTextBox.Text = fbd.SelectedPath + Path.DirectorySeparatorChar + DefaultInstallDir;
}
private void CancelButton_Click(object sender, EventArgs e)
{
cancelled = true;
InstallCancelButton.Enabled = false;
}
}
}
File diff suppressed because it is too large Load Diff
-19
View File
@@ -1,19 +0,0 @@
using System;
using System.Windows.Forms;
namespace TGS.Installer.UI
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Main());
}
}
}
@@ -1,16 +0,0 @@
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("TGStation Server Installer")]
[assembly: AssemblyDescription("Installation manager for /tg/station Server")]
// 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("8956d4c3-bfb9-448e-bf5f-ee7e6f9996f9")]
-83
View File
@@ -1,83 +0,0 @@
//------------------------------------------------------------------------------
// <auto-generated>
// 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.
// </auto-generated>
//------------------------------------------------------------------------------
namespace TGS.Installer.UI.Properties {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// 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", "15.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() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[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("TGS.Installer.UI.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] cab1 {
get {
object obj = ResourceManager.GetObject("cab1", resourceCulture);
return ((byte[])(obj));
}
}
/// <summary>
/// Looks up a localized resource of type System.Byte[].
/// </summary>
internal static byte[] TGSInstaller {
get {
object obj = ResourceManager.GetObject("TGSInstaller", resourceCulture);
return ((byte[])(obj));
}
}
}
}
-116
View File
@@ -1,116 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\packages\WiX.3.11.0\build\wix.props" Condition="Exists('..\packages\WiX.3.11.0\build\wix.props')" />
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{8956D4C3-BFB9-448E-BF5F-EE7E6F9996F9}</ProjectGuid>
<OutputType>WinExe</OutputType>
<RootNamespace>TGS.Installer.UI</RootNamespace>
<AssemblyName>TG Station Server Installer</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup>
<ApplicationManifest>app.manifest</ApplicationManifest>
</PropertyGroup>
<PropertyGroup>
<ApplicationIcon>tgs.ico</ApplicationIcon>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|AnyCPU'">
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<DocumentationFile>bin\x86\Release\TG Station Server Installer.xml</DocumentationFile>
<Optimize>true</Optimize>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<DebugType>pdbonly</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>true</Prefer32Bit>
</PropertyGroup>
<PropertyGroup>
<SignAssembly>false</SignAssembly>
</PropertyGroup>
<ItemGroup>
<Reference Include="Costura, Version=1.6.2.0, Culture=neutral, PublicKeyToken=9919ef960d84173d, processorArchitecture=MSIL">
<HintPath>..\packages\Costura.Fody.1.6.2\lib\dotnet\Costura.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Microsoft.Deployment.WindowsInstaller">
<HintPath>..\packages\WiX.3.11.0\tools\Microsoft.Deployment.WindowsInstaller.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Drawing" />
<Reference Include="System.ServiceProcess" />
<Reference Include="System.Windows.Forms" />
</ItemGroup>
<ItemGroup>
<Compile Include="Main.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Main.Designer.cs">
<DependentUpon>Main.cs</DependentUpon>
</Compile>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Include="..\AssemblyInfo.global.cs" />
<EmbeddedResource Include="Main.resx">
<DependentUpon>Main.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
<SubType>Designer</SubType>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
<None Include="app.manifest" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<None Include="FodyWeavers.xml" />
<Content Include="tgs.ico" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\TGS.Interface\TGS.Interface.csproj">
<Project>{ac4e7e8b-f83a-481c-a8b0-8fa4e8ae59ab}</Project>
<Name>TGS.Interface</Name>
</ProjectReference>
<ProjectReference Include="..\TGS.Server\TGS.Server.csproj">
<Project>{f32eda25-0855-411c-af5e-f0d042917e2d}</Project>
<Name>TGS.Server</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="..\packages\Fody.2.0.0\build\dotnet\Fody.targets" Condition="Exists('..\packages\Fody.2.0.0\build\dotnet\Fody.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\Fody.2.0.0\build\dotnet\Fody.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Fody.2.0.0\build\dotnet\Fody.targets'))" />
<Error Condition="!Exists('..\packages\Costura.Fody.1.6.2\build\dotnet\Costura.Fody.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Costura.Fody.1.6.2\build\dotnet\Costura.Fody.targets'))" />
<Error Condition="!Exists('..\packages\WiX.3.11.0\build\wix.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\WiX.3.11.0\build\wix.props'))" />
</Target>
<Import Project="..\packages\Costura.Fody.1.6.2\build\dotnet\Costura.Fody.targets" Condition="Exists('..\packages\Costura.Fody.1.6.2\build\dotnet\Costura.Fody.targets')" />
</Project>
-76
View File
@@ -1,76 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
<security>
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
<!-- UAC Manifest Options
If you want to change the Windows User Account Control level replace the
requestedExecutionLevel node with one of the following.
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
<requestedExecutionLevel level="highestAvailable" uiAccess="false" />
Specifying requestedExecutionLevel element will disable file and registry virtualization.
Remove this element if your application requires this virtualization for backwards
compatibility.
-->
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
</requestedPrivileges>
</security>
</trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- A list of the Windows versions that this application has been tested on and is
is designed to work with. Uncomment the appropriate elements and Windows will
automatically selected the most compatible environment. -->
<!-- Windows Vista -->
<!--<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />-->
<!-- Windows 7 -->
<!--<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />-->
<!-- Windows 8 -->
<!--<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />-->
<!-- Windows 8.1 -->
<!--<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />-->
<!-- Windows 10 -->
<!--<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />-->
</application>
</compatibility>
<!-- Indicates that the application is DPI-aware and will not be automatically scaled by Windows at higher
DPIs. Windows Presentation Foundation (WPF) applications are automatically DPI-aware and do not need
to opt in. Windows Forms applications targeting .NET Framework 4.6 that opt into this setting, should
also set the 'EnableWindowsFormsHighDpiAutoResizing' setting to 'true' in their app.config. -->
<!--
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
</windowsSettings>
</application>
-->
<!-- Enable themes for Windows common controls and dialogs (Windows XP and later) -->
<!--
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="*"
publicKeyToken="6595b64144ccf1df"
language="*"
/>
</dependentAssembly>
</dependency>
-->
</assembly>
-6
View File
@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Costura.Fody" version="1.6.2" targetFramework="net452" developmentDependency="true" />
<package id="Fody" version="2.0.0" targetFramework="net452" developmentDependency="true" />
<package id="WiX" version="3.11.0" targetFramework="net452" developmentDependency="true" />
</packages>
Binary file not shown.

Before

Width:  |  Height:  |  Size: 120 KiB

-155
View File
@@ -1,155 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product Id="*" Name="TG Station Server" Language="1033" Version="!(bind.FileVersion.ServiceExecutable)" Manufacturer="/tg/station 13" UpgradeCode="663badae-ddca-4aa9-8f3f-3b7b20332eac">
<Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
<MediaTemplate />
<Feature Id="ProductFeature" Title="TGS.Installer" Level="1">
<ComponentGroupRef Id="ProductComponents" />
<ComponentGroupRef Id="StartMenuShortcuts" />
<ComponentGroupRef Id="DesktopShortcuts" />
<ComponentGroupRef Id="Gitx86Components" />
<ComponentGroupRef Id="Gitx64Components" />
</Feature>
<Icon Id="tgs.ico" SourceFile="..\tgs.ico"/>
<Property Id="ARPPRODUCTICON" Value="tgs.ico" />
<Property Id="INSTALLSHORTCUTDESK" Value="0" />
<Property Id="INSTALLSHORTCUTSTART" Value="0" />
<InstallExecuteSequence>
<RemoveShortcuts>Installed AND NOT UPGRADINGPRODUCTCODE</RemoveShortcuts>
</InstallExecuteSequence>
</Product>
<Fragment>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="INSTALLFOLDER" Name="TG Station Server">
<Directory Id ="LibFolder" Name="lib">
<Directory Id ="Win32Folder" Name="win32">
<Directory Id ="x86Folder" Name="x86" />
<Directory Id ="x64Folder" Name="x64" />
</Directory>
</Directory>
</Directory>
</Directory>
<Directory Id="DesktopFolder" Name="Desktop" />
<Directory Id="ProgramMenuFolder" Name="StartMenuDir">
<Directory Id="ApplicationProgramsFolder" Name="TG Station Server"/>
</Directory>
</Directory>
</Fragment>
<Fragment>
<ComponentGroup Id="StartMenuShortcuts" Directory="ApplicationProgramsFolder">
<Component Id="StartMenuShortcut" Guid="*">
<Condition>INSTALLSHORTCUTSTART = 1</Condition>
<Shortcut Id="StartMenuShortcutCL"
Name="TG Command Line"
Target="[!TGCommandLine.exe]"
WorkingDirectory="APPLICATIONROOTDIRECTORY"/>
<Shortcut Id="StartMenuShortcutCP"
Name="TG Control Panel"
Target="[!TGControlPanel.exe]"
WorkingDirectory="APPLICATIONROOTDIRECTORY"/>
<Shortcut Id="UninstallProduct"
Name="Uninstall TG Station Server"
Target="[SystemFolder]msiexec.exe"
Arguments="/x [ProductCode]"
Description="Uninstalls TG Station Server" />
<RemoveFolder Id="CleanUpSMShortCuts" Directory="ApplicationProgramsFolder" On="uninstall"/>
<RegistryValue Root="HKCU" Key="Software\TGStation\Server" Name="StartMenuShortcuts" Type="integer" Value="1" KeyPath="yes"/>
</Component>
</ComponentGroup>
<ComponentGroup Id="DesktopShortcuts" Directory="DesktopFolder">
<Component Id="DesktopShortcut" Guid="*">
<Condition>INSTALLSHORTCUTDESK = 1</Condition>
<Shortcut Id="DesktopShortcutCL"
Name="TG Command Line"
Target="[!TGCommandLine.exe]"
WorkingDirectory="APPLICATIONROOTDIRECTORY"/>
<Shortcut Id="DesktopShortcutCP"
Name="TG Control Panel"
Target="[!TGControlPanel.exe]"
WorkingDirectory="APPLICATIONROOTDIRECTORY"/>
<RemoveFolder Id="CleanUpDKShortCuts" Directory="ApplicationProgramsFolder" On="uninstall"/>
<RegistryValue Root="HKCU" Key="Software\TGStation\Server" Name="DesktopShortcuts" Type="integer" Value="1" KeyPath="yes"/>
</Component>
</ComponentGroup>
<ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
<Component Id="TGS.CommandLine" Guid="*">
<File Source="$(var.TGS.CommandLine.TargetPath)" />
<Environment Id="PATH" Name="PATH" Value="[INSTALLFOLDER]" Permanent="no" Part="last" Action="set" System="yes" />
</Component>
<Component Id="TGS.Server" Guid="*">
<File Source="$(var.TGS.Server.TargetPath)" />
</Component>
<Component Id="TGS.Server.Service" Guid="*">
<File Source="$(var.TGS.Server.Service.TargetPath)" Id="ServiceExecutable" />
<ServiceInstall Id="ServiceInstaller" Name="TG Station Server" Type="ownProcess" EraseDescription="no" ErrorControl="normal" Start="auto" Vital="yes" />
<ServiceControl Id="StartService" Start="install" Stop="both" Remove="uninstall" Name="TG Station Server" Wait="yes" />
</Component>
<Component Id="DiscordNetCore" Guid="*">
<File Source="$(var.TGS.Server.TargetDir)/Discord.Net.Core.dll" />
</Component>
<Component Id="DiscordNetRest" Guid="*">
<File Source="$(var.TGS.Server.TargetDir)/Discord.Net.Rest.dll" />
</Component>
<Component Id="DiscordNetWebSocket" Guid="*">
<File Source="$(var.TGS.Server.TargetDir)/Discord.Net.WebSocket.dll" />
</Component>
<Component Id="LibGit2Sharp" Guid="*">
<File Source="$(var.TGS.Server.TargetDir)/LibGit2Sharp.dll" />
</Component>
<Component Id="Octokit" Guid="*">
<File Source="$(var.TGS.ControlPanel.TargetDir)/Octokit.dll" />
</Component>
<Component Id="MeebeySmartIrc4net" Guid="*">
<File Source="$(var.TGS.Server.TargetDir)/Meebey.SmartIrc4net.dll" />
</Component>
<Component Id="NewtonsoftJson" Guid="*">
<File Source="$(var.TGS.Server.TargetDir)/Newtonsoft.Json.dll" />
</Component>
<Component Id="SystemCollectionsImmutable" Guid="*">
<File Source="$(var.TGS.Server.TargetDir)/System.Collections.Immutable.dll" />
</Component>
<Component Id="SystemInteractiveAsync" Guid="*">
<File Source="$(var.TGS.Server.TargetDir)/System.Interactive.Async.dll" />
</Component>
<Component Id="TGS.ControlPanel" Guid="*">
<File Source="$(var.TGS.ControlPanel.TargetPath)" KeyPath="yes" />
</Component>
<Component Id="TGS.Interface" Guid="*">
<File Source="$(var.TGS.Interface.TargetPath)" />
</Component>
<Component Id="TGS.Interface.Bridge" Guid="*">
<File Source="$(var.TGS.Interface.Bridge.TargetPath)" />
</Component>
</ComponentGroup>
<ComponentGroup Id="Gitx86Components" Directory="x86Folder">
<Component Id="LibGit2Sharpx86" Guid="*">
<File Source="$(var.TGS.Server.TargetDir)lib\win32\x86\git2-ssh-baa87df.dll" Id="LibGit2Sharpx86dll"/>
</Component>
<Component Id="LibGit2Sharpx86SSH" Guid="*">
<File Source="$(var.TGS.Server.TargetDir)lib\win32\x86\libssh2.dll" Id="LibGit2Sharpx86SSHdll"/>
</Component>
<Component Id="LibGit2Sharpx86Z" Guid="*">
<File Source="$(var.TGS.Server.TargetDir)lib\win32\x86\zlib.dll" Id="LibGit2Sharpx86Zdll"/>
</Component>
</ComponentGroup>
<ComponentGroup Id="Gitx64Components" Directory="x64Folder">
<Component Id="LibGit2Sharpx64" Guid="*">
<File Source="$(var.TGS.Server.TargetDir)lib\win32\x64\git2-ssh-baa87df.dll" Id="LibGit2Sharpx64dll" />
</Component>
<Component Id="LibGit2Sharpx64SSH" Guid="*">
<File Source="$(var.TGS.Server.TargetDir)lib\win32\x64\libssh2.dll" Id="LibGit2Sharpx64SSHdll"/>
</Component>
<Component Id="LibGit2Sharpx64Z" Guid="*">
<File Source="$(var.TGS.Server.TargetDir)lib\win32\x64\zlib.dll" Id="LibGit2Sharpx64Zdll"/>
</Component>
</ComponentGroup>
</Fragment>
</Wix>
-110
View File
@@ -1,110 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" InitialTargets="EnsureWixToolsetInstalled" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\packages\WiX.3.11.0\build\wix.props" Condition="Exists('..\packages\WiX.3.11.0\build\wix.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
<ProductVersion>3.10</ProductVersion>
<ProjectGuid>154435f6-0890-42d4-9aec-b743d4fbc1cb</ProjectGuid>
<SchemaVersion>2.0</SchemaVersion>
<OutputName>TGServiceInstaller</OutputName>
<OutputType>Package</OutputType>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<OutputPath>bin\$(Configuration)\</OutputPath>
<IntermediateOutputPath>obj\$(Configuration)\</IntermediateOutputPath>
<DefineConstants>Debug</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<OutputPath>bin\$(Configuration)\</OutputPath>
<IntermediateOutputPath>obj\$(Configuration)\</IntermediateOutputPath>
<SuppressPdbOutput>True</SuppressPdbOutput>
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<Compile Include="Product.wxs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\TGS.ControlPanel\TGS.ControlPanel.csproj">
<Name>TGS.ControlPanel</Name>
<Project>{394e7643-6b8c-416f-ab18-95ac12648cdc}</Project>
<Private>True</Private>
<DoNotHarvest>True</DoNotHarvest>
<RefProjectOutputGroups>Binaries;Content;Satellites</RefProjectOutputGroups>
<RefTargetDir>INSTALLFOLDER</RefTargetDir>
</ProjectReference>
<ProjectReference Include="..\TGS.CommandLine\TGS.CommandLine.csproj">
<Name>TGS.CommandLine</Name>
<Project>{89191f69-b18e-4b59-b72e-e12f9b6811a0}</Project>
<Private>True</Private>
<DoNotHarvest>True</DoNotHarvest>
<RefProjectOutputGroups>Binaries;Content;Satellites</RefProjectOutputGroups>
<RefTargetDir>INSTALLFOLDER</RefTargetDir>
</ProjectReference>
<ProjectReference Include="..\TGS.Interface.Bridge\TGS.Interface.Bridge.csproj">
<Name>TGS.Interface.Bridge</Name>
<Project>{9a01ef03-8eae-45cb-8b87-4a17bd904557}</Project>
<Private>True</Private>
<DoNotHarvest>True</DoNotHarvest>
<RefProjectOutputGroups>Binaries;Content;Satellites</RefProjectOutputGroups>
<RefTargetDir>INSTALLFOLDER</RefTargetDir>
</ProjectReference>
<ProjectReference Include="..\TGS.Server.Service\TGS.Server.Service.csproj">
<Name>TGS.Server.Service</Name>
<Project>{3f81e398-b223-4006-b40c-c2800714ce29}</Project>
<Private>True</Private>
<DoNotHarvest>True</DoNotHarvest>
<RefProjectOutputGroups>Binaries;Content;Satellites</RefProjectOutputGroups>
<RefTargetDir>INSTALLFOLDER</RefTargetDir>
</ProjectReference>
<ProjectReference Include="..\TGS.Server\TGS.Server.csproj">
<Name>TGS.Server</Name>
<Project>{f32eda25-0855-411c-af5e-f0d042917e2d}</Project>
<Private>True</Private>
<DoNotHarvest>True</DoNotHarvest>
<RefProjectOutputGroups>Binaries;Content;Satellites</RefProjectOutputGroups>
<RefTargetDir>INSTALLFOLDER</RefTargetDir>
</ProjectReference>
<ProjectReference Include="..\TGS.Interface\TGS.Interface.csproj">
<Name>TGS.Interface</Name>
<Project>{ac4e7e8b-f83a-481c-a8b0-8fa4e8ae59ab}</Project>
<Private>True</Private>
<DoNotHarvest>True</DoNotHarvest>
<RefProjectOutputGroups>Binaries;Content;Satellites</RefProjectOutputGroups>
<RefTargetDir>INSTALLFOLDER</RefTargetDir>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Content Include="packages.config" />
</ItemGroup>
<Import Project="$(WixTargetsPath)" Condition=" '$(WixTargetsPath)' != '' " />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\WiX\v3.x\Wix.targets" Condition=" '$(WixTargetsPath)' == '' AND Exists('$(MSBuildExtensionsPath32)\Microsoft\WiX\v3.x\Wix.targets') " />
<Target Name="EnsureWixToolsetInstalled" Condition=" '$(WixTargetsImported)' != 'true' ">
<Error Text="The WiX Toolset v3 build tools must be installed to build this project. To download the WiX Toolset, see http://wixtoolset.org/releases/" />
</Target>
<Target Name="AfterResolveReferences">
<Exec Command="$(PreBuildEventCommand)" />
</Target>
<PropertyGroup>
<PreBuildEventCommand>powershell -Command "&amp; \"$(SolutionDir)Tools/SignBasics.ps1\""</PreBuildEventCommand>
</PropertyGroup>
<PropertyGroup>
<PostBuildEvent>powershell -Command "&amp; \"$(SolutionDir)Tools/SignMSI.ps1\""</PostBuildEvent>
</PropertyGroup>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\WiX.3.11.0\build\wix.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\WiX.3.11.0\build\wix.props'))" />
</Target>
<!--
To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Wix.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>
-4
View File
@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="WiX" version="3.11.0" developmentDependency="true" />
</packages>
-35
View File
@@ -1,35 +0,0 @@
using RGiesecke.DllExport;
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace TGS.Interface.Bridge
{
/// <summary>
/// Holds the proc that DD calls to access <see cref="ITGInterop"/>
/// </summary>
public static class DreamDaemonBridge
{
/// <summary>
/// The proc that DD calls to access <see cref="ITGInterop"/>
/// </summary>
/// <param name="argc">The number of arguments passed</param>
/// <param name="args">The arguments passed</param>
/// <returns>0</returns>
[DllExport("DDEntryPoint", CallingConvention = CallingConvention.Cdecl)]
public static int DDEntryPoint(int argc, [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPStr, SizeParamIndex = 0)]string[] args)
{
try
{
var parsedArgs = new List<string>();
parsedArgs.AddRange(args);
var instance = parsedArgs[0];
parsedArgs.RemoveAt(0);
using (var I = new Client())
I.Server.GetInstance(instance).Interop.InteropMessage(String.Join(" ", parsedArgs));
}
catch { }
return 0;
}
}
}
-4
View File
@@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Weavers>
<Costura />
</Weavers>
@@ -1,16 +0,0 @@
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("TGStation Server Service DreamDaemon Bridge")]
[assembly: AssemblyDescription("Used by DreamDaemon to call into the TGStation Server Service")]
// 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("9a01ef03-8eae-45cb-8b87-4a17bd904557")]
@@ -1,78 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{9A01EF03-8EAE-45CB-8B87-4A17BD904557}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>TGS.Interface.Bridge</RootNamespace>
<AssemblyName>TGDreamDaemonBridge</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<PlatformTarget>x86</PlatformTarget>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\x86\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<OutputPath>bin\x86\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup>
<SignAssembly>false</SignAssembly>
</PropertyGroup>
<ItemGroup>
<Compile Include="DreamDaemonBridge.cs" />
<Compile Include="..\AssemblyInfo.global.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<Reference Include="Costura, Version=1.6.2.0, Culture=neutral, PublicKeyToken=9919ef960d84173d, processorArchitecture=MSIL">
<HintPath>..\packages\Costura.Fody.1.6.2\lib\dotnet\Costura.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="RGiesecke.DllExport.Metadata, Version=1.0.0.0, Culture=neutral, PublicKeyToken=8f52d83c1a22df51, processorArchitecture=MSIL">
<HintPath>..\packages\UnmanagedExports.1.2.7\lib\net\RGiesecke.DllExport.Metadata.dll</HintPath>
<Private>False</Private>
</Reference>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<None Include="FodyWeavers.xml" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\TGS.Interface\TGS.Interface.csproj">
<Project>{ac4e7e8b-f83a-481c-a8b0-8fa4e8ae59ab}</Project>
<Name>TGS.Interface</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="../packages/UnmanagedExports.1.2.7/tools/RGiesecke.DllExport.targets" Condition="Exists('../packages/UnmanagedExports.1.2.7/tools/RGiesecke.DllExport.targets')" />
<Import Project="..\packages\Fody.2.0.0\build\netstandard1.4\Fody.targets" Condition="Exists('..\packages\Fody.2.0.0\build\netstandard1.4\Fody.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\Fody.2.0.0\build\netstandard1.4\Fody.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Fody.2.0.0\build\netstandard1.4\Fody.targets'))" />
<Error Condition="!Exists('..\packages\Costura.Fody.1.6.2\build\dotnet\Costura.Fody.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Costura.Fody.1.6.2\build\dotnet\Costura.Fody.targets'))" />
</Target>
<Import Project="..\packages\Costura.Fody.1.6.2\build\dotnet\Costura.Fody.targets" Condition="Exists('..\packages\Costura.Fody.1.6.2\build\dotnet\Costura.Fody.targets')" />
</Project>
-6
View File
@@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Costura.Fody" version="1.6.2" targetFramework="net452" developmentDependency="true" />
<package id="Fody" version="2.0.0" targetFramework="net452" developmentDependency="true" requireReinstallation="true" />
<package id="UnmanagedExports" version="1.2.7" targetFramework="net452" developmentDependency="true" />
</packages>
@@ -1,97 +0,0 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Description;
namespace TGS.Interface
{
/// <summary>
/// Used to attach windows credential headers to SOAP messages
/// </summary>
sealed class AuthenticationHeaderApplicator : IEndpointBehavior, IClientMessageInspector
{
/// <summary>
/// The credentials to attach
/// </summary>
readonly RemoteLoginInfo remoteLoginInfo;
/// <summary>
/// Construct a <see cref="AuthenticationHeaderApplicator"/>
/// </summary>
/// <param name="loginInfo">The <see cref="RemoteLoginInfo"/> to use</param>
public AuthenticationHeaderApplicator(RemoteLoginInfo loginInfo)
{
remoteLoginInfo = loginInfo;
}
/// <summary>
/// Add <see langword="this"/> to the message inspectors for the channel
/// </summary>
/// <param name="endpoint">The <see cref="ServiceEndpoint"/></param>
/// <param name="clientRuntime">The <see cref="ClientRuntime"/></param>
public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
var t = Type.GetType("Mono.Runtime");
ICollection<IClientMessageInspector> inspectors;
if (t != null)
{
var prop = clientRuntime.GetType()
.GetTypeInfo()
.GetDeclaredProperty("MessageInspectors");
inspectors = (ICollection<IClientMessageInspector>)prop
.GetValue(clientRuntime);
}
else
inspectors = clientRuntime.ClientMessageInspectors;
inspectors.Add(this);
}
/// <summary>
/// Attach <see cref="RemoteLoginInfo.Username"/> and <see cref="RemoteLoginInfo.Password"/> to the <paramref name="request"/>
/// </summary>
/// <param name="request">The outgoing request</param>
/// <param name="channel">The <see cref="IClientChannel"/></param>
/// <returns></returns>
public object BeforeSendRequest(ref Message request, IClientChannel channel)
{
request.Headers.Add(MessageHeader.CreateHeader("Username", "http://tempuri.org", remoteLoginInfo.Username));
request.Headers.Add(MessageHeader.CreateHeader("Password", "http://tempuri.org", remoteLoginInfo.Password));
return null;
}
/// <summary>
/// Unused implementation of <see cref="IEndpointBehavior"/>
/// </summary>
public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
//intentionally left blank
}
/// <summary>
/// Unused implementation of <see cref="IClientMessageInspector"/>
/// </summary>
public void AfterReceiveReply(ref Message reply, object correlationState)
{
//intentionally left blank
}
/// <summary>
/// Unused implementation of <see cref="IEndpointBehavior"/>
/// </summary>
public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
//intentionally left blank
}
/// <summary>
/// Unused implementation of <see cref="IEndpointBehavior"/>
/// </summary>
public void Validate(ServiceEndpoint endpoint)
{
//intentionally left blank
}
}
}
-344
View File
@@ -1,344 +0,0 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
namespace TGS.Interface
{
/// <summary>
/// For setting up authentication no matter the chat provider
/// </summary>
[DataContract]
[KnownType(typeof(IRCSetupInfo))]
[KnownType(typeof(DiscordSetupInfo))]
public class ChatSetupInfo
{
const int AdminListIndex = 0;
const int AdminModeIndex = 1;
const int AdminChannelIndex = 2;
const int DevChannelIndex = 3;
const int WDChannelIndex = 4;
const int GameChannelIndex = 5;
const int ProviderIndex = 6;
const int EnabledIndex = 7;
/// <summary>
/// Starting index of <see cref="DataFields"/> which child classes should use to write their custom data to
/// </summary>
protected const int BaseIndex = 8;
/// <summary>
/// Set to <see langword="true"/> if a child constructor should use the baseInfo parameter of <see cref="ChatSetupInfo.ChatSetupInfo(ChatProvider, ChatSetupInfo, int)"/> to initialize it's property fields, <see langword="false"/> otherwise
/// </summary>
protected readonly bool InitializeFields;
/// <summary>
/// Raw access to the underlying data
/// </summary>
[DataMember]
public IList<string> DataFields { get; protected set; }
/// <summary>
/// Constructs a <see cref="ChatSetupInfo"/> from optional <paramref name="baseInfo"/>
/// </summary>
/// <param name="provider">The <see cref="ChatProvider"/> that this <see cref="ChatSetupInfo"/> is for</param>
/// <param name="baseInfo">Optional past data</param>
/// <param name="numFields">The number of fields in this chat provider</param>
protected internal ChatSetupInfo(ChatProvider provider, ChatSetupInfo baseInfo, int numFields)
{
numFields += BaseIndex;
InitializeFields = baseInfo == null || baseInfo.DataFields.Count != numFields;
if (InitializeFields)
{
DataFields = new List<string>(numFields);
for (var I = 0; I < numFields; ++I)
DataFields.Add(null);
AdminList = new List<string>();
AdminChannels = new List<string>();
DevChannels = new List<string>();
GameChannels = new List<string>();
WatchdogChannels = new List<string>();
AdminsAreSpecial = false;
Enabled = false;
}
else
DataFields = baseInfo.DataFields;
Provider = provider;
Specialize(true); //to check we have a valid provider
}
/// <summary>
/// Recreates <see langword="this"/> as the correct child <see cref="ChatSetupInfo"/>
/// </summary>
/// <param name="checkOnly">If <see langword="true"/>, <see langword="null"/> is returned provided <see cref="Provider"/> is a valid <see cref="ChatProvider"/></param>
/// <returns>A new <see cref="ChatSetupInfo"/> based on the <see cref="Provider"/> type</returns>
ChatSetupInfo Specialize(bool checkOnly)
{
switch (Provider)
{
case ChatProvider.IRC:
if (!checkOnly)
return new IRCSetupInfo(this);
break;
case ChatProvider.Discord:
if (!checkOnly)
return new DiscordSetupInfo(this);
break;
default:
throw new Exception("Invalid provider!");
}
return null;
}
/// <summary>
/// Properly formats a <paramref name="channel"/> name for the <see cref="ChatProvider"/>
/// </summary>
/// <param name="channel">The <see cref="string"/> to format</param>
/// <returns>The formatted <see cref="string"/></returns>
protected virtual string SanitizeChannelName(string channel)
{
return Specialize(false).SanitizeChannelName(channel);
}
/// <summary>
/// Sanitizes a list of <paramref name="channelnames"/>
/// </summary>
/// <param name="channelnames">A <see cref="List{T}"/> of strings</param>
void SanitizeChannelNames(IList<string> channelnames)
{
for (var I = 0; I < channelnames.Count; ++I)
if (String.IsNullOrWhiteSpace(channelnames[I]))
{
channelnames.RemoveAt(I);
--I;
}
else
channelnames[I] = SanitizeChannelName(channelnames[I].Trim());
}
/// <summary>
/// Constructs a <see cref="ChatSetupInfo"/> from a data list
/// </summary>
/// <param name="DeserializedData">The data</param>
public ChatSetupInfo(IList<string> DeserializedData)
{
DataFields = DeserializedData;
Specialize(false); //ensure provider type is valid
}
/// <summary>
/// The list of admin entries
/// </summary>
public List<string> AdminList
{
get { return JsonConvert.DeserializeObject<List<string>>(DataFields[AdminListIndex]); }
set { DataFields[AdminListIndex] = JsonConvert.SerializeObject(value); }
}
/// <summary>
/// If AdminList corresponds to a Provider specific recognization method
/// </summary>
public bool AdminsAreSpecial
{
get { return Convert.ToBoolean(DataFields[AdminModeIndex]); }
set { DataFields[AdminModeIndex] = Convert.ToString(value); }
}
/// <summary>
/// The channels from which admin commands/messages can be sent/received
/// </summary>
public List<string> AdminChannels
{
get { return JsonConvert.DeserializeObject<List<string>>(DataFields[AdminChannelIndex]); }
set
{
SanitizeChannelNames(value);
DataFields[AdminChannelIndex] = JsonConvert.SerializeObject(value);
}
}
/// <summary>
/// The channels to which repo and compile messages are sent
/// </summary>
public List<string> DevChannels
{
get { return JsonConvert.DeserializeObject<List<string>>(DataFields[DevChannelIndex]); }
set
{
SanitizeChannelNames(value);
DataFields[DevChannelIndex] = JsonConvert.SerializeObject(value);
}
}
/// <summary>
/// The channels to which watchdog messages are sent
/// </summary>
public List<string> WatchdogChannels
{
get { return JsonConvert.DeserializeObject<List<string>>(DataFields[WDChannelIndex]); }
set
{
SanitizeChannelNames(value);
DataFields[WDChannelIndex] = JsonConvert.SerializeObject(value);
}
}
/// <summary>
/// The channels to which game messages are sent
/// </summary>
public List<string> GameChannels
{
get { return JsonConvert.DeserializeObject<List<string>>(DataFields[GameChannelIndex]); }
set
{
SanitizeChannelNames(value);
DataFields[GameChannelIndex] = JsonConvert.SerializeObject(value);
}
}
/// <summary>
/// If this chat provider is enabled
/// </summary>
public bool Enabled
{
get { return Convert.ToBoolean(DataFields[EnabledIndex]); }
set { DataFields[EnabledIndex] = Convert.ToString(value); }
}
/// <summary>
/// The type of provider
/// </summary>
public ChatProvider Provider
{
get { return (ChatProvider)Convert.ToInt32(DataFields[ProviderIndex]); }
set { DataFields[ProviderIndex] = Convert.ToString((int)value); }
}
}
/// <summary>
/// Chat provider for IRC. Admin entries should be user nicknames in normal mode or required channel flags in special mode
/// </summary>
[DataContract]
public sealed class IRCSetupInfo : ChatSetupInfo
{
const int URLIndex = 0;
const int PortIndex = 1;
const int NickIndex = 2;
const int AuthTargetIndex = 3;
const int AuthMessageIndex = 4;
const int AuthLevelIndex = 5;
const int FieldsLen = 6;
/// <summary>
/// Construct IRC setup info from optional generic info. Defaults to TGS3 on rizons IRC server
/// </summary>
/// <param name="baseInfo">Optional generic info</param>
public IRCSetupInfo(ChatSetupInfo baseInfo = null) : base(ChatProvider.IRC, baseInfo, FieldsLen)
{
if (!InitializeFields)
return;
Nickname = "TGS3";
URL = "irc.rizon.net";
Port = 6667;
AuthTarget = "";
AuthMessage = "";
AdminsAreSpecial = true;
AuthLevel = IRCMode.Op;
}
/// <inheritdoc />
protected override string SanitizeChannelName(string working)
{
if (working[0] != '#')
return "#" + working;
return working;
}
/// <summary>
/// The port of the IRC server
/// </summary>
public ushort Port
{
get { return Convert.ToUInt16(DataFields[BaseIndex + PortIndex]); }
set { DataFields[BaseIndex + PortIndex] = value.ToString(); }
}
/// <summary>
/// The URL of the IRC server
/// </summary>
public string URL
{
get { return DataFields[BaseIndex + URLIndex]; }
set { DataFields[BaseIndex + URLIndex] = value; }
}
/// <summary>
/// The nickname of the IRC bot
/// </summary>
public string Nickname
{
get { return DataFields[BaseIndex + NickIndex]; }
set { DataFields[BaseIndex + NickIndex] = value; }
}
/// <summary>
/// The target for sending authentication messages
/// </summary>
public string AuthTarget
{
get { return DataFields[BaseIndex + AuthTargetIndex]; }
set { DataFields[BaseIndex + AuthTargetIndex] = value; }
}
/// <summary>
/// The authentication message
/// </summary>
public string AuthMessage
{
get { return DataFields[BaseIndex + AuthMessageIndex]; }
set { DataFields[BaseIndex + AuthMessageIndex] = value; }
}
/// <summary>
/// The minimum mode required to use admin bot commands when in special auth mode
/// </summary>
public IRCMode AuthLevel
{
get { return (IRCMode)Convert.ToInt32(DataFields[BaseIndex + AuthLevelIndex]); }
set { DataFields[BaseIndex + AuthLevelIndex] = Convert.ToString((int)value); }
}
}
/// <summary>
/// Chat provider for Discord. Admin entires should be user ids in normal mode or group ids in special mode
/// </summary>
[DataContract]
public sealed class DiscordSetupInfo : ChatSetupInfo
{
const int BotTokenIndex = 0;
const int FieldsLen = 1;
/// <summary>
/// Construct Discord setup info from optional generic info. Default is not a valid discord bot tokent
/// </summary>
/// <param name="baseInfo">Optional generic info</param>
public DiscordSetupInfo(ChatSetupInfo baseInfo = null) : base(ChatProvider.Discord, baseInfo, FieldsLen)
{
if (!InitializeFields)
return;
BotToken = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; //needless to say, this is fake
}
/// <inheritdoc />
protected override string SanitizeChannelName(string working)
{
working = working.Replace("<", "").Replace(">", "").Replace("&", ""); //filter out some stuff that can come in the copypasta
try
{
Convert.ToUInt64(working);
}
catch
{
throw new Exception("Invalid Discord channel ID!");
}
return working;
}
/// <summary>
/// The Discord bot token to use. See https://discordapp.com/developers/applications/me for registering bot accounts
/// </summary>
public string BotToken
{
get { return DataFields[BaseIndex + BotTokenIndex]; }
set { DataFields[BaseIndex + BotTokenIndex] = value; }
}
}
}
-318
View File
@@ -1,318 +0,0 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Net;
using System.Net.Security;
using System.Reflection;
using System.Security.Principal;
using System.ServiceModel;
using System.ServiceModel.Description;
using TGS.Interface.Components;
namespace TGS.Interface
{
/// <inheritdoc />
sealed public class Client : IClient
{
/// <summary>
/// Version of the <see cref="Client"/>
/// </summary>
public static readonly Version Version = Assembly.GetExecutingAssembly().GetName().Version;
/// <inheritdoc />
public IServer Server => server;
/// <inheritdoc />
public Version ServerVersion { get
{
lock (this)
if (_serverVersion == null)
{
string rawVersion;
//check ITGSService first for compatiblity reasons
try
{
rawVersion = GetComponent<ITGSService>(null).Version();
}
catch
{
rawVersion = GetComponent<ITGLanding>(null).Version();
}
var splits = rawVersion.Split(' ');
_serverVersion = new Version(splits[splits.Length - 1].Substring(1));
}
return _serverVersion;
} }
/// <inheritdoc />
public string InstanceName { get; private set; }
/// <inheritdoc />
public RemoteLoginInfo LoginInfo { get { return _loginInfo; } }
/// <summary>
/// Backing field for <see cref="Server"/>
/// </summary>
readonly IServer server;
/// <summary>
/// Backing field for <see cref="LoginInfo"/>
/// </summary>
readonly RemoteLoginInfo _loginInfo;
/// <summary>
/// The <see cref="ServerVersion"/>
/// </summary>
Version _serverVersion;
/// <summary>
/// Associated list of open <see cref="ChannelFactory"/>s keyed by <see langword="interface"/> type name. A <see cref="ChannelFactory"/> in this list may close or fault at any time. <see langword="this"/> must be locked before being accessed
/// </summary>
IDictionary<string, ChannelFactory> ChannelFactoryCache;
/// <summary>
/// Sets the function called when a remote login fails due to the server having an invalid SSL cert
/// </summary>
/// <param name="handler">The <see cref="Func{T, TResult}"/> to be called when a remote login is attempted while the server posesses a bad certificate. Passed a <see cref="string"/> of error information about the and should return <see langword="true"/> if it the connection should be made anyway</param>
public static void SetBadCertificateHandler(Func<string, bool> handler)
{
ServicePointManager.ServerCertificateValidationCallback = (sender, cert, chain, error) =>
{
string ErrorMessage;
switch (error)
{
case SslPolicyErrors.None:
return true;
case SslPolicyErrors.RemoteCertificateChainErrors:
ErrorMessage = "There are certificate chain errors.";
break;
case SslPolicyErrors.RemoteCertificateNameMismatch:
ErrorMessage = "The certificate name does not match.";
break;
case SslPolicyErrors.RemoteCertificateNotAvailable:
ErrorMessage = "The certificate doesn't exist in the trust store.";
break;
default:
ErrorMessage = "An unknown error occurred.";
break;
}
ErrorMessage = String.Format("The server's certificate failed to verify! Error: {0} Cert: {1}", ErrorMessage, cert.ToString());
return handler(ErrorMessage);
};
}
/// <summary>
/// Construct an <see cref="Client"/> for a local connection
/// </summary>
public Client()
{
ChannelFactoryCache = new Dictionary<string, ChannelFactory>();
server = new Server(this);
}
/// <summary>
/// Construct an <see cref="Client"/> for a remote connection
/// </summary>
/// <param name="loginInfo">The <see cref="RemoteLoginInfo"/> for a remote connection</param>
public Client(RemoteLoginInfo loginInfo) : this()
{
if (!loginInfo.HasPassword)
throw new InvalidOperationException("password must be set on loginInfo!");
_loginInfo = loginInfo;
}
/// <inheritdoc />
public bool IsRemoteConnection { get { return LoginInfo != null; } }
/// <summary>
/// Closes all <see cref="ChannelFactory"/>s stored in <see cref="ChannelFactoryCache"/> and <see langword="nulls"/> it
/// </summary>
public void Dispose()
{
lock (this)
{
if (ChannelFactoryCache == null)
return;
foreach (var I in ChannelFactoryCache)
{
var cf = I.Value;
try
{
cf.Close();
}
catch
{
cf.Abort();
}
}
ChannelFactoryCache = null;
}
}
/// <inheritdoc />
public bool VersionMismatch(out string errorMessage)
{
if (ServerVersion.Major != Version.Major || ServerVersion.Minor != Version.Minor || ServerVersion.Build != Version.Build) //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.", Version, ServerVersion);
return true;
}
errorMessage = null;
return false;
}
/// <summary>
/// Returns the requested <see cref="Client"/> component <see langword="interface"/> for the instance <see cref="InstanceName"/>. This does not guarantee a successful connection. <see cref="ChannelFactory{TChannel}"/>s created this way are recycled for minimum latency and bandwidth usage
/// </summary>
/// <typeparam name="T">The component <see langword="interface"/> to retrieve</typeparam>
/// <param name="instanceName">The name of the <see cref="IInstance"/> to use</param>
/// <returns>The correct component <see langword="interface"/></returns>
internal T GetComponent<T>(string instanceName)
{
var actualToT = typeof(T);
var tot = actualToT.Name;
if (instanceName != null)
tot = instanceName + tot;
ChannelFactory<T> cf;
lock (this)
{
if (ChannelFactoryCache == null)
throw new ObjectDisposedException(GetType().Name);
if (ChannelFactoryCache.ContainsKey(tot))
try
{
cf = ((ChannelFactory<T>)ChannelFactoryCache[tot]);
if (cf.State != CommunicationState.Opened)
throw new Exception();
return cf.CreateChannel();
}
catch
{
ChannelFactoryCache[tot].Abort();
ChannelFactoryCache.Remove(tot);
}
cf = CreateChannel<T>(instanceName);
ChannelFactoryCache[tot] = cf;
}
return cf.CreateChannel();
}
/// <summary>
/// Directly creates a <see cref="ChannelFactory{TChannel}"/> for <typeparamref name="T"/> without caching. This should be eventually closed by the caller
/// </summary>
/// <typeparam name="T">The component <see langword="interface"/> of the channel to be created</typeparam>
/// <param name="instanceName">The instance to connect to, if any</param>
/// <returns>The correct <see cref="ChannelFactory{TChannel}"/></returns>
ChannelFactory<T> CreateChannel<T>(string instanceName)
{
var accessPath = instanceName == null ? Definitions.MasterInterfaceName : String.Format("{0}/{1}", Definitions.InstanceInterfaceName, instanceName);
if (!IsRemoteConnection)
return CreateLocalChannel<T>(instanceName, accessPath);
return CreateRemoteChannel<T>(instanceName, accessPath);
}
//NOTE: This needs to be kept seperate from CreateRemoteChannel because not all our client implementations have NetNamedPipeBinding and attempting to call this function on those platforms will throw an exception
/// <summary>
/// Directly creates a <see cref="ChannelFactory{TChannel}"/> for on a local connection <typeparamref name="T"/> without caching. This should be eventually closed by the caller
/// </summary>
/// <typeparam name="T">The component <see langword="interface"/> of the channel to be created</typeparam>
/// <param name="instanceName">The instance to connect to, if any</param>
/// <param name="accessPath">The URL of the interface to connect to</param>
/// <returns>The correct <see cref="ChannelFactory{TChannel}"/></returns>
ChannelFactory<T> CreateLocalChannel<T>(string instanceName, string accessPath)
{
var interfaceName = typeof(T).Name;
var res2 = new ChannelFactory<T>(
new NetNamedPipeBinding { SendTimeout = new TimeSpan(0, 0, 30), MaxReceivedMessageSize = Definitions.TransferLimitLocal }, new EndpointAddress(String.Format("net.pipe://localhost/{0}/{1}", accessPath, interfaceName))); //10 megs
res2.Credentials.Windows.AllowedImpersonationLevel = TokenImpersonationLevel.Impersonation;
return res2;
}
/// <summary>
/// Directly creates a <see cref="ChannelFactory{TChannel}"/> for on a remote connection <typeparamref name="T"/> without caching. This should be eventually closed by the caller
/// </summary>
/// <typeparam name="T">The component <see langword="interface"/> of the channel to be created</typeparam>
/// <param name="instanceName">The instance to connect to, if any</param>
/// <param name="accessPath">The URL of the interface to connect to</param>
/// <returns>The correct <see cref="ChannelFactory{TChannel}"/></returns>
ChannelFactory<T> CreateRemoteChannel<T>(string instanceName, string accessPath)
{
//tls memes
ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12;
//okay we're going over
var binding = new BasicHttpsBinding()
{
SendTimeout = new TimeSpan(0, 0, 40),
MaxReceivedMessageSize = Definitions.TransferLimitRemote
};
var interfaceName = typeof(T).Name;
var requireAuth = interfaceName != typeof(ITGConnectivity).Name;
var url = String.Format("https://{0}:{1}/{2}/{3}", LoginInfo.IP, LoginInfo.Port, accessPath, interfaceName);
var address = new EndpointAddress(url);
var res = new ChannelFactory<T>(binding, address);
if (requireAuth)
{
var applicator = new AuthenticationHeaderApplicator(LoginInfo);
var t = Type.GetType("Mono.Runtime");
KeyedCollection<Type, IEndpointBehavior> behaviours;
if (t != null)
{
var prop = res.Endpoint.GetType()
.GetTypeInfo()
.GetDeclaredProperty("Behaviors");
behaviours = (KeyedCollection<Type, IEndpointBehavior>)prop
.GetValue(res.Endpoint);
}
else
behaviours = res.Endpoint.EndpointBehaviors;
behaviours.Add(applicator);
}
return res;
}
/// <inheritdoc />
public ConnectivityLevel ConnectionStatus()
{
return ConnectionStatus(out string unused);
}
/// <inheritdoc />
public ConnectivityLevel ConnectionStatus(out string error)
{
try
{
GetComponent<ITGConnectivity>(null).VerifyConnection();
}
catch (Exception e)
{
error = e.ToString();
return ConnectivityLevel.None;
}
try
{
GetComponent<ITGLanding>(null).Version();
}
catch(Exception e)
{
error = e.ToString();
return ConnectivityLevel.Connected;
}
try
{
GetComponent<ITGSService>(null).Version();
error = null;
return ConnectivityLevel.Administrator;
}
catch(Exception e)
{
error = e.ToString();
return ConnectivityLevel.Authenticated;
}
}
}
}
-92
View File
@@ -1,92 +0,0 @@
using System;
using System.Collections.Generic;
using System.Threading;
namespace TGS.Interface
{
/// <summary>
/// Helper for creating a text <see cref="Command"/> tree
/// </summary>
public abstract class Command
{
/// <summary>
/// Exit codes for <see cref="Command"/>s
/// </summary>
public enum ExitCode : int
{
/// <summary>
/// The <see cref="Command"/> ran successfully
/// </summary>
Normal = 0,
/// <summary>
/// The connection to the service was interrupted during the <see cref="Command"/>
/// </summary>
ConnectionError = 1,
/// <summary>
/// Invalid parameters for <see cref="Command"/>
/// </summary>
BadCommand = 2,
/// <summary>
/// The command failed due to conditions on the service
/// </summary>
ServerError = 3,
}
/// <summary>
/// Proc that will show a message to the <see cref="Command"/> invoker. Do not call directly, use <see cref="OutputProc(string)"/> instead
/// </summary>
public static ThreadLocal<Action<string>> OutputProcVar = new ThreadLocal<Action<string>>();
/// <summary>
/// Write output to the <see cref="Command"/> invoker
/// </summary>
/// <param name="message">The output to display</param>
protected static void OutputProc(string message)
{
OutputProcVar.Value(message);
}
/// <summary>
/// The text that invokes this <see cref="Command"/>. Set in constructor
/// </summary>
public string Keyword { get; protected set; }
/// <summary>
/// The number of parameters this <see cref="Command"/> requires. Set in Constructor
/// </summary>
public int RequiredParameters { get; protected set; }
/// <summary>
/// Caller of <see cref="Run(IList{string})"/>, can be used to modify the root behaviour of the <see cref="Command"/>
/// </summary>
/// <param name="parameters">List of parameters passed to the <see cref="Command"/></param>
/// <returns>An <see cref="ExitCode"/> describing the execution of the <see cref="Command"/></returns>
public virtual ExitCode DoRun(IList<string> parameters)
{
return Run(parameters);
}
/// <summary>
/// Override to do the actions of the <see cref="Command"/>
/// </summary>
/// <param name="parameters">List of <see cref="string"/> parameters passed to the <see cref="Command"/>. Guaranteed to have at least <see cref="RequiredParameters"/> non-empty/whitespace entries</param>
/// <returns>An <see cref="ExitCode"/> describing the execution of the <see cref="Command"/></returns>
protected abstract ExitCode Run(IList<string> parameters);
/// <summary>
/// Prints usage text of the <see cref="Command"/> to the invoker
/// </summary>
public virtual void PrintHelp()
{
var argstr = GetArgumentString();
OutputProc(String.Format("{0} {1}- {2}", Keyword, argstr.Length > 0 ? argstr + " " : "", GetHelpText()));
}
/// <summary>
/// Override to add argument text to the <see cref="Command"/>
/// Format is &lt;required&gt; &lt;arguments&gt; [optional] [arguments]
/// </summary>
/// <returns>Formatted argument text for the <see cref="Command"/></returns>
public virtual string GetArgumentString()
{
return "";
}
/// <summary>
/// Override to add usage text to the <see cref="Command"/>
/// </summary>
/// <returns>Formatted usage text for the <see cref="Command"/></returns>
public abstract string GetHelpText();
}
}
@@ -1,33 +0,0 @@
using System.ServiceModel;
namespace TGS.Interface.Components
{
/// <summary>
/// Manage the group that is used to access the service, can only be used by an administrator
/// </summary>
[ServiceContract]
public interface ITGAdministration
{
/// <summary>
/// Returns the name of the windows group allowed to use the service other than administrator
/// </summary>
/// <returns>The name of the windows group allowed to use the service other than administrator, "ADMIN" if it's unset, <see langword="null"/> on failure</returns>
[OperationContract]
string GetCurrentAuthorizedGroup();
/// <summary>
/// Searches the windows machine for the group named <paramref name="groupName"/>, sets it as the authorized group if it's found
/// </summary>
/// <param name="groupName">The name of the windows group to search for or null to clear the setting</param>
/// <returns>The name of the windows group that is now authorized to use the service on success, <see langword="null"/> on failure, "ADMIN" on clearing</returns>
[OperationContract]
string SetAuthorizedGroup(string groupName);
/// <summary>
/// Renames the current static folder to a backup name and recreates is from the current repo using TGS3.json
/// </summary>
/// <returns><see langword="null"/> on success, error message on failure</returns>
[OperationContract]
string RecreateStaticFolder();
}
}
-43
View File
@@ -1,43 +0,0 @@
using System.ServiceModel;
namespace TGS.Interface.Components
{
/// <summary>
/// For managing the BYOND installation the server runs
/// </summary>
[ServiceContract]
public interface ITGByond
{
/// <summary>
/// Gets the current status of any BYOND updates
/// </summary>
/// <returns>The current status of the byond updater</returns>
[OperationContract]
ByondStatus CurrentStatus();
/// <summary>
/// updates the used byond version to that of version <paramref name="major"/>.<paramref name="minor"/>. The change won't take place until DD reboots. Calls <see cref="ITGDreamDaemon.RequestRestart"/> if blocked by a running DD instance. Runs asyncronously, use <see cref="CurrentStatus"/> to check progress
/// </summary>
/// <param name="major">Major BYOND version. E.g. 511</param>
/// <param name="minor">Minor BYOND version. E.g. 1381</param>
/// <returns><see langword="true"/> if the update started, <see langword="false"/> if another operation was in progress or DreamDaemon is running</returns>
[OperationContract]
bool UpdateToVersion(int major, int minor);
/// <summary>
/// Check the last update error. Checking this will clear the value
/// </summary>
/// <returns>The last update error, if any. <see langword="null"/> otherwise.</returns>
[OperationContract]
string GetError();
/// <summary>
/// Get the currently installed version as a string formatted as Major.Minor
/// </summary>
/// <param name="type">The type of version to retrieve</param>
/// <returns><see langword="null"/> if no version is detected, the version string otherwise</returns>
[OperationContract]
string GetVersion(ByondVersion type);
}
}
-41
View File
@@ -1,41 +0,0 @@
using System.Collections.Generic;
using System.ServiceModel;
namespace TGS.Interface.Components
{
/// <summary>
/// Interface for handling chat bot
/// </summary>
[ServiceContract]
public interface ITGChat
{
/// <summary>
/// Sets a chat provider <paramref name="info"/>
/// </summary>
/// <param name="info">The info to set</param>
[OperationContract]
string SetProviderInfo(ChatSetupInfo info);
/// <summary>
/// Returns <see cref="ChatSetupInfo"/> for all <see cref="ChatProvider"/>s
/// </summary>
/// <returns>A list of all <see cref="ChatSetupInfo"/>s</returns>
[OperationContract]
IList<ChatSetupInfo> ProviderInfos();
/// <summary>
/// Checks connection status
/// </summary>
/// <param name="providerType">The type of provider to check if connected</param>
/// <returns><see langword="true"/> if connected, <see langword="false"/> otherwise</returns>
[OperationContract]
bool Connected(ChatProvider providerType);
/// <summary>
/// Reconnect a specific <paramref name="providerType"/> to it's chat service
/// </summary>
/// <param name="providerType">The type of provider to reconnect</param>
/// <returns><see langword="null"/> on success, error message <see cref="string"/> on failure</returns>
[OperationContract]
string Reconnect(ChatProvider providerType);
}
}
-61
View File
@@ -1,61 +0,0 @@
using System.ServiceModel;
namespace TGS.Interface.Components
{
/// <summary>
/// For managing the Game A/B/Live folders, compiling, and hotswapping them
/// </summary>
[ServiceContract]
public interface ITGCompiler
{
/// <summary>
/// Sets up the symlinks for hotswapping game code. This will reset everything in the Game folder. Requires the repository to be set up and locks it once the compilation stage starts. Runs asyncronously from this call
/// </summary>
/// <returns><see langword="true"/> if the operation began, <see langword="false"/> if it could not start</returns>
[OperationContract]
bool Initialize();
/// <summary>
/// Does all the necessary actions to take the revision currently in the repository and compile it to be run on the next server reboot. Requires BYOND to be set up and the <see cref="GetStatus"/> to return <see cref="CompilerStatus.Initialized"/>. Runs asyncronously
/// </summary>
/// <param name="silent">If <see langword="true"/> no message for compilation start will be printed</param>
/// <returns><see langword="true"/> if the operation began, <see langword="false"/> if it could not start</returns>
[OperationContract]
bool Compile(bool silent = false);
/// <summary>
/// Cancels the current compilation
/// </summary>
/// <returns><see langword="null"/> on success, error message on failure</returns>
[OperationContract]
string Cancel();
/// <summary>
/// Returns the current compiler status
/// </summary>
/// <returns>The current compiler status</returns>
[OperationContract]
CompilerStatus GetStatus();
/// <summary>
/// Returns the error message of the last operation. Reading this will clear the stored value
/// </summary>
/// <returns>the error message of the last operation if it failed or <see langword="null"/> if it succeeded</returns>
[OperationContract]
string CompileError();
/// <summary>
/// Returns the relative path of the dme the compiler will look for without the .dme part
/// </summary>
/// <returns>The relative path of the dme the compiler will look for without the .dme part</returns>
[OperationContract]
string ProjectName();
/// <summary>
/// Sets the relative path of the dme the compiler will look for without the .dme part
/// </summary>
/// <param name="projectName">The relative path of the dme the compiler will look for without the .dme part</param>
[OperationContract]
void SetProjectName(string projectName);
}
}
-58
View File
@@ -1,58 +0,0 @@
using System.Collections.Generic;
using System.ServiceModel;
namespace TGS.Interface.Components
{
/// <summary>
/// For modifying the in game config
/// Most if not all of these will not apply until the next server reboot
/// </summary>
[ServiceContract]
public interface ITGConfig
{
/// <summary>
/// Returns the file contents of the specified server directory
/// Subdirectories will be prefixed with '/'
/// </summary>
/// <param name="subpath">Subdirectory to enumerate, enumerates the root directory if null</param>
/// <param name="error">null on success, error message on failure</param>
/// <param name="unauthorized">This will be true if error is set to a message that indicates the current user does not have access to the specified file</param>
/// <returns>A list of files in the enumerated static directory on success, null on failure</returns>
[OperationContract]
IList<string> ListStaticDirectory(string subpath, out string error, out bool unauthorized);
/// <summary>
/// Read from a static file
/// </summary>
/// <param name="staticRelativePath">The path from the Static dir. E.g. config/config.txt</param>
/// <param name="repo">if true, the file will be read from the repository instead of the static dir</param>
/// <param name="error">null on success, error message on failure</param>
/// <param name="unauthorized">This will be true if error is set to a message that indicates the current user does not have access to the specified file</param>
/// <returns>The full text of the file on success, null on failure</returns>
/// <exception cref="CommunicationException">Along with implied disconnect exceptions, if the file exceeds transfer limits</exception>
[OperationContract]
string ReadText(string staticRelativePath, bool repo, out string error, out bool unauthorized);
/// <summary>
/// Write to a static file
/// </summary>
/// <param name="staticRelativePath">The path from the Static dir. E.g. config/config.txt</param>
/// <param name="data">The full text of the config file</param>
/// <param name="originalData">The original data that the client knows about. Set to null to skip checks</param>
/// <param name="unauthorized">This will be true if error is set to a message that indicates the current user does not have access to the specified file</param>
/// <returns>null on success, error message on failure</returns>
/// <exception cref="CommunicationException">Along with implied disconnect exceptions, if the file exceeds transfer limits</exception>
[OperationContract]
string WriteText(string staticRelativePath, string data, string originalData, out bool unauthorized);
/// <summary>
/// Deletes the target static file
/// </summary>
/// <param name="staticRelativePath">The path from the Static dir. E.g. config/config.txt</param>
/// <param name="unauthorized">This will be true if error is set to a message that indicates the current user does not have access to the specified file</param>
/// <returns>null on success, error message on failure</returns>
[OperationContract]
string DeleteFile(string staticRelativePath, out bool unauthorized);
}
}
-17
View File
@@ -1,17 +0,0 @@
using System.ServiceModel;
namespace TGS.Interface.Components
{
/// <summary>
/// Used for testing connections to the service without authentication
/// </summary>
[ServiceContract]
public interface ITGConnectivity
{
/// <summary>
/// Does nothing on the server end, but if the call completes, you can be sure you are connected. WCF won't throw until you try until you actually use the API
/// </summary>
[OperationContract]
void VerifyConnection();
}
}
-146
View File
@@ -1,146 +0,0 @@
using System.ServiceModel;
namespace TGS.Interface.Components
{
/// <summary>
/// Interface for managing the actual BYOND game server
/// </summary>
[ServiceContract]
public interface ITGDreamDaemon
{
/// <summary>
/// Gets the status of DreamDaemon
/// </summary>
/// <returns>The appropriate <see cref="DreamDaemonStatus"/></returns>
[OperationContract]
DreamDaemonStatus DaemonStatus();
/// <summary>
/// Returns a human readable string of the current server status
/// </summary>
/// <param name="includeMetaInfo">If <see langword="true"/>, the status will include the server's current visibility and security levels</param>
/// <returns>A human readable <see cref="string"/> of the current server status</returns>
[OperationContract]
string StatusString(bool includeMetaInfo);
/// <summary>
/// Check if a call to <see cref="Start"/> will fail. Of course, be aware of race conditions with other interfaces
/// </summary>
/// <returns>The error that would occur, <see langword="null"/> otherwise</returns>
[OperationContract]
string CanStart();
/// <summary>
/// Starts the server if it isn't running
/// </summary>
/// <returns><see langword="null"/> on success or error message on failure</returns>
[OperationContract]
string Start();
/// <summary>
/// Immediately kills the server
/// </summary>
/// <returns><see langword="null"/> on success or error message on failure</returns>
[OperationContract]
string Stop();
/// <summary>
/// Immediately kills and restarts the server
/// </summary>
/// <returns><see langword="null"/> on success or error message on failure</returns>
[OperationContract]
string Restart();
/// <summary>
/// Restart the server after the currently running world reboots. Has no effect if the server isn't running
/// </summary>
[OperationContract]
void RequestRestart();
/// <summary>
/// Stop the server after the currently running world reboots. Has no effect if the server isn't running
/// </summary>
[OperationContract]
void RequestStop();
/// <summary>
/// Get the configured (not necessarily running) security level
/// </summary>
/// <returns>The configured (not necessarily running) <see cref="DreamDaemonSecurity"/></returns>
[OperationContract]
DreamDaemonSecurity SecurityLevel();
/// <summary>
/// Sets the security level of the server. Requires server reboot to apply. Calls <see cref="RequestRestart"/>. Note that anything higher than Trusted will disable interop from DD
/// </summary>
/// <param name="level">The new security level</param>
/// <returns><see langword="true"/> if the change was immediately applied, <see langword="false"/> otherwise and a call to <see cref="RequestRestart"/> was made</returns>
[OperationContract]
bool SetSecurityLevel(DreamDaemonSecurity level);
/// <summary>
/// Get the configured port. Not necessarily the running port if it has since changed
/// </summary>
/// <returns>The configured port</returns>
[OperationContract]
ushort Port();
/// <summary>
/// Set the port to host DD on. Requires reboot to apply. Calls <see cref="RequestRestart"/>.
/// </summary>
/// <param name="new_port">The new port</param>
[OperationContract]
void SetPort(ushort new_port);
/// <summary>
/// Check if the watchdog will start when the service starts
/// </summary>
/// <returns><see langword="true"/> if autostart is enabled, <see langword="false"/> otherwise</returns>
[OperationContract]
bool Autostart();
/// <summary>
/// Set the autostart config
/// </summary>
/// <param name="on"><see langword="true"/> to start the watchdog with the service, <see langword="false"/> to disable that functionality</param>
[OperationContract]
void SetAutostart(bool on);
/// <summary>
/// Check if the BYOND webclient is currently enabled for the server
/// </summary>
/// <returns><see langword="true"/> if the webclient is enabled, <see langword="false"/> otherwise</returns>
[OperationContract]
bool Webclient();
/// <summary>
/// Set the webclient config. Calls <see cref="RequestRestart"/>
/// </summary>
/// <param name="on"><see langword="true"/> to enable the byond webclient for the server, <see langword="false"/> otherwise</param>
[OperationContract]
void SetWebclient(bool on);
/// <summary>
/// Checks if a server stop has been requested
/// </summary>
/// <returns><see langword="true"/> if <see cref="RequestStop"/> has been called since the last server start, <see langword="false"/> otherwise</returns>
[OperationContract]
bool ShutdownInProgress();
/// <summary>
/// Sends a message to everyone on the server
/// </summary>
/// <param name="msg">The message to send</param>
/// <returns><see langword="null"/> on success, error message on failure</returns>
[OperationContract]
string WorldAnnounce(string msg);
/// <summary>
/// Returns the number of connected players. Requires game to use API version >= 3.1.0.1
/// </summary>
/// <returns>The number of connected players or -1 on error</returns>
[OperationContract]
int PlayerCount();
}
}
-25
View File
@@ -1,25 +0,0 @@
using System.ServiceModel;
namespace TGS.Interface.Components
{
/// <summary>
/// Metadata for a server instance
/// </summary>
[ServiceContract]
public interface ITGInstance
{
/// <summary>
/// Return the directory of the server on the host machine
/// </summary>
/// <returns>The path to the directory on success, null on failure</returns>
[OperationContract]
string ServerDirectory();
/// <summary>
/// Retrieve's the service's version
/// </summary>
/// <returns>The service's version</returns>
[OperationContract]
string Version();
}
}
@@ -1,62 +0,0 @@
using System.ServiceModel;
namespace TGS.Interface.Components
{
/// <summary>
/// Used for managing <see cref="ITGInstance"/>s
/// </summary>
[ServiceContract]
public interface ITGInstanceManager
{
/// <summary>
/// Creates a new <see cref="ITGInstance"/>
/// </summary>
/// <param name="Name">The name of the instance</param>
/// <param name="path">The path to the instance</param>
/// <returns><see langword="null"/> on success, error message on failure</returns>
[OperationContract]
string CreateInstance(string Name, string path);
/// <summary>
/// Registers an existing server instance
/// </summary>
/// <param name="path">The path to the instance</param>
/// <returns><see langword="null"/> on success, error message on failure</returns>
[OperationContract]
string ImportInstance(string path);
/// <summary>
/// Checks if an instance is online
/// </summary>
/// <param name="Name">The name of the instance</param>
/// <returns><see langword="true"/> if the Instance exists and is online, <see langword="false"/> otherwise</returns>
[OperationContract]
bool InstanceEnabled(string Name);
/// <summary>
/// Sets an instance's enabled status
/// </summary>
/// <param name="Name">The instance whom's status should be changed</param>
/// <param name="enabled"><see langword="true"/> to enable the instance, <see langword="false"/> to disable it</param>
/// <returns><see langword="null"/> on success, error message on failure</returns>
[OperationContract]
string SetInstanceEnabled(string Name, bool enabled);
/// <summary>
/// Renames an instance, this will restart the instance if it is enabled
/// </summary>
/// <param name="name">The current name of the instance</param>
/// <param name="new_name">The new name of the instance</param>
/// <returns><see langword="null"/> on success, error message on failure</returns>
[OperationContract]
string RenameInstance(string name, string new_name);
/// <summary>
/// Disables and unregisters an instance, allowing the folder and data to be manipulated manually
/// </summary>
/// <param name="name">The instance to detach</param>
/// <returns><see langword="null"/> on success, error message on failure</returns>
[OperationContract]
string DetachInstance(string name);
}
}
-19
View File
@@ -1,19 +0,0 @@
using System.ServiceModel;
namespace TGS.Interface.Components
{
/// <summary>
/// Used by DreamDaemon to access the interop API with call()(). Restrictions are in place so that only a DreamDaemon instance launched by the service can use this API
/// </summary>
[ServiceContract]
public interface ITGInterop
{
/// <summary>
/// Called from /world/ExportService(command)
/// </summary>
/// <param name="command">The command to run</param>
/// <returns><see langword="true"/> on success, <see langword="false"/> on failure</returns>
[OperationContract]
bool InteropMessage(string command);
}
}
-26
View File
@@ -1,26 +0,0 @@
using System.Collections.Generic;
using System.ServiceModel;
namespace TGS.Interface.Components
{
/// <summary>
/// Used for general authentication and listing <see cref="ITGInstance"/>s
/// </summary>
[ServiceContract]
public interface ITGLanding
{
/// <summary>
/// Retrieve's the service's version
/// </summary>
/// <returns>The service's version</returns>
[OperationContract]
string Version();
/// <summary>
/// List instances that the caller can access
/// </summary>
/// <returns>A <see cref="IDictionary{TKey, TValue}"/> of instance names relating to their paths</returns>
[OperationContract]
IList<InstanceMetadata> ListInstances();
}
}
-204
View File
@@ -1,204 +0,0 @@
using System.Collections.Generic;
using System.ServiceModel;
namespace TGS.Interface.Components
{
/// <summary>
/// Interface for managing the code repository
/// </summary>
[ServiceContract]
public interface ITGRepository
{
/// <summary>
/// If the repo is currently undergoing an operation
/// </summary>
/// <returns><see langword="true"/> if the repo is busy, <see langword="false"/> otherwise</returns>
[OperationContract]
bool OperationInProgress();
/// <summary>
/// Gets the progress of repository operations, not all operations are supported
/// </summary>
/// <returns>A value between 0 and 100 inclusive representing the progress of the current operation or -1 if the operation cannot be monitored</returns>
[OperationContract]
int CheckoutProgress();
/// <summary>
/// Check if the repository is valid, if not <see cref="Setup(string, string)"/> must be called
/// </summary>
/// <returns><see langword="true"/> if the repository is valid, <see langword="false"/> otherwise</returns>
[OperationContract]
bool Exists();
/// <summary>
/// Deletes whatever may be left over and clones the repo at <paramref name="remote"/> and checks out <paramref name="branch"/>. Will move config and data dirs to a backup location if they exist. Runs asyncronously
/// </summary>
/// <param name="remote">The address of the repo to clone. If ssh protocol is used, private_key.txt and public_key.txt must exist in the server RepoKey directory.</param>
/// <param name="branch">The branch of the repo to checkout</param>
/// <returns><see langword="null"/> on success, error message on failure</returns>
[OperationContract]
string Setup(string remote, string branch = "master");
/// <summary>
/// Gets the sha of the current HEAD
/// </summary>
/// <param name="useTracked">If set to true and HEAD is currently a branch, will instead return the sha of the tracked remote branch if it exists</param>
/// <param name="error"><see langword="null"/> on success, error message on failure</param>
/// <returns>The sha of the current HEAD on success, <see langword="null"/> on failure</returns>
[OperationContract]
string GetHead(bool useTracked, out string error);
/// <summary>
/// Gets the name of the current branch
/// </summary>
/// <param name="error"><see langword="null"/> on success, error message on failure</param>
/// <returns>The name of the current branch on success, <see langword="null"/> on failure</returns>
[OperationContract]
string GetBranch(out string error);
/// <summary>
/// Gets the url of the current origin
/// </summary>
/// <param name="error"><see langword="null"/> on success, error message on failure</param>
/// <returns>The url of the current origin on success, <see langword="null"/> on failure</returns>
[OperationContract]
string GetRemote(out string error);
/// <summary>
/// Hard checks out the passed object name
/// </summary>
/// <param name="objectName">The branch, commit, or tag to checkout</param>
/// <returns><see langword="null"/> on success, error message on failure</returns>
[OperationContract]
string Checkout(string objectName);
/// <summary>
/// Fetches the origin and merges it into the current branch
/// </summary>
/// <param name="reset">If <see langword="true"/>, the operation will perform a hard reset instead of a merge</param>
/// <returns><see langword="null"/> on success, error message on failure</returns>
[OperationContract]
string Update(bool reset);
/// <summary>
/// Runs git reset --hard
/// </summary>
/// <param name="tracked">Changes command to git reset --hard origin/branch_name if <see langword="true"/></param>
/// <returns><see langword="null"/> on success, error message on failure</returns>
[OperationContract]
string Reset(bool tracked);
/// <summary>
/// Merges the target pull request into the current branch if the remote is a github repository
/// </summary>
/// <param name="PRnumber">The github pull request number in the remote repository</param>
/// <param name="atSHA">The SHA of the pull request to merge</param>
/// <param name="silent">Suppresses chat messages if <see langword="true"/></param>
/// <returns><see langword="null"/> on success, error message on failure</returns>
[OperationContract]
string MergePullRequest(int PRnumber, string atSHA = null, bool silent = false);
/// <summary>
/// Merges the target pull requests into the current branch if the remote is a github repository
/// </summary>
/// <param name="pullRequestInfos"><see cref="IEnumerable{T}"/> of <see cref="PullRequestInfo"/>s containing pull request numbers and shas in the remote repository</param>
/// <param name="silent">Suppresses chat messages if <see langword="true"/></param>
/// <returns><see langword="null"/> on success, list of error messages if any fail failure. Those with indexes of those that succeed will have null entries</returns>
[OperationContract]
IEnumerable<string> MergePullRequests(IEnumerable<PullRequestInfo> pullRequestInfos, bool silent = false);
/// <summary>
/// Get the currently merged pull requests. Note that switching branches will delete this list and switching back won't restore it
/// </summary>
/// <param name="error"><see langword="null"/> on success, error message on failure</param>
/// <returns>A <see cref="IList{T}"/> of <see cref="PullRequestInfo"/></returns>
[OperationContract]
List<PullRequestInfo> MergedPullRequests(out string error);
/// <summary>
/// Gets the name of the configured git committer
/// </summary>
/// <returns>The name of the configured git committer</returns>
[OperationContract]
string GetCommitterName();
/// <summary>
/// Sets the name of the configured git committer
/// </summary>
/// <param name="newName">The name to set</param>
[OperationContract]
void SetCommitterName(string newName);
/// <summary>
/// Gets the email of the configured git committer
/// </summary>
/// <returns>The email of the configured git committer</returns>
[OperationContract]
string GetCommitterEmail();
/// <summary>
/// Sets the email of the configured git committer
/// </summary>
/// <param name="newEmail">The email to set</param>
[OperationContract]
void SetCommitterEmail(string newEmail);
/// <summary>
/// Updates the html changelog
/// </summary>
/// <param name="error"><see langword="null"/> on success, error on failure</param>
/// <returns>The output of the python script</returns>
[OperationContract]
string GenerateChangelog(out string error);
/// <summary>
/// Pushes the paths listed in TGS3.json to the currentl git remote. No other commit differences may exist for this function to succeed
/// </summary>
/// <returns><see langword="null"/> on success, error on failure</returns>
[OperationContract]
string SynchronizePush();
/// <summary>
/// List the tagged commits of the repo at which compiles took place
/// </summary>
/// <param name="error"><see langword="null"/> on success, error message on failure</param>
/// <returns>A <see cref="IDictionary{TKey, TValue}"/> of tag name -> commit on success, <see langword="null"/> on failure</returns>
[OperationContract]
IDictionary<string, string> ListBackups(out string error);
/// <summary>
/// Updates the cached TGS3.json to the repo's version. Compiles will not succeed if these two to not match
/// </summary>
/// <returns><see langword="null"/> on success, error message on failure</returns>
[OperationContract]
string UpdateTGS3Json();
/// <summary>
/// (De)Activate and set the interval for the automatic server updater
/// </summary>
/// <param name="newInterval">Interval to check for updates in minutes, disables if 0</param>
[OperationContract]
void SetAutoUpdateInterval(ulong newInterval);
/// <summary>
/// Get the current autoupdate interval
/// </summary>
/// <returns>The current auto update interval or 0 if it's disabled</returns>
[OperationContract]
ulong AutoUpdateInterval();
/// <summary>
/// Check if we push a temporary branch to the remote when we make testmerge commits
/// </summary>
/// <returns><see langword="true"/> if we publish testmerge commits to the remote, <see langword="false"/> otherwise</returns>
[OperationContract]
bool PushTestmergeCommits();
/// <summary>
/// Set if we push a temporary branch to the remote when we make testmerge commits
/// </summary>
/// <param name="newValue"><see langword="true"/> if we testmerge commits should be published to the remote, <see langword="false"/> otherwise</param>
[OperationContract]
void SetPushTestmergeCommits(bool newValue);
}
}
-57
View File
@@ -1,57 +0,0 @@
using System.Collections.Generic;
using System.ServiceModel;
namespace TGS.Interface.Components
{
/// <summary>
/// Interface for managing the service
/// </summary>
[ServiceContract]
public interface ITGSService
{
/// <summary>
/// Next stop of the service will not close DD and sets a flag for it to reattach once it restarts
/// </summary>
[OperationContract]
void PrepareForUpdate();
/// <summary>
/// Get the port used for remote operation
/// </summary>
/// <returns>The port used for remote operation</returns>
[OperationContract]
ushort RemoteAccessPort();
/// <summary>
/// Set the port used for remote operation
/// Requires a service restart to take effect
/// </summary>
/// <param name="port">The new port to use for remote operation</param>
/// <returns>null on success, error message on failure</returns>
[OperationContract]
string SetRemoteAccessPort(ushort port);
/// <summary>
/// Retrieve's the service's version
/// </summary>
/// <returns>The service's version</returns>
[OperationContract]
string Version();
/// <summary>
/// Sets the path to the python 2.7 installation
/// </summary>
/// <param name="path">The new path</param>
/// <returns><see langword="true"/> if the path exists, <see langword="false"/> otherwise</returns>
[OperationContract]
bool SetPythonPath(string path);
/// <summary>
/// Gets the path to the python 2.7 installation
/// </summary>
/// <returns>The path to the python 2.7 installation</returns>
[OperationContract]
string PythonPath();
}
}
-32
View File
@@ -1,32 +0,0 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using TGS.Interface.Components;
namespace TGS.Interface
{
/// <summary>
/// Contains constants for the interface
/// </summary>
public static class Definitions
{
/// <summary>
/// The maximum message size to and from a local server
/// </summary>
public const long TransferLimitLocal = Int32.MaxValue; //2GB can't go higher
/// <summary>
/// The maximum message size to and from a remote server
/// </summary>
public const long TransferLimitRemote = 10485760; //10 MB
/// <summary>
/// Base name of communication URLs
/// </summary>
public const string MasterInterfaceName = "TGStationServerService";
/// <summary>
/// Base name of instance URLs
/// </summary>
public const string InstanceInterfaceName = MasterInterfaceName + "/Instance";
}
}
-178
View File
@@ -1,178 +0,0 @@
using System;
namespace TGS.Interface
{
/// <summary>
/// Description of the connectivity level to an <see cref="Components.ITGInstance"/> or the <see cref="Components.ITGSService"/>
/// </summary>
[Flags]
public enum ConnectivityLevel
{
/// <summary>
/// The connection could not be made, either a communication error occurred or the specified <see cref="Components.ITGInstance"/> does not exist
/// </summary>
None = 0,
/// <summary>
/// The connection could be made
/// </summary>
Connected = 1,
/// <summary>
/// The connected user is authenticated
/// </summary>
Authenticated = 2 | Connected,
/// <summary>
/// The connected user is an administrator
/// </summary>
Administrator = 4 | Authenticated,
}
/// <summary>
/// The status of a BYOND update job
/// </summary>
public enum ByondStatus
{
/// <summary>
/// No byond update in progress
/// </summary>
Idle,
/// <summary>
/// Preparing to update
/// </summary>
Starting,
/// <summary>
/// Revision is downloading
/// </summary>
Downloading,
/// <summary>
/// Revision is deflating
/// </summary>
Staging,
/// <summary>
/// Revision is ready and waiting for DreamDaemon reboot
/// </summary>
Staged,
/// <summary>
/// Revision is being applied
/// </summary>
Updating,
/// <summary>
/// Running game code is being recompiled under staged update
/// </summary>
CompilingStaged,
}
/// <summary>
/// Type of byond version
/// </summary>
public enum ByondVersion
{
/// <summary>
/// The highest version from http://www.byond.com/download/build/LATEST/
/// </summary>
Latest,
/// <summary>
/// The version in the staging directory
/// </summary>
Staged,
/// <summary>
/// The installed version
/// </summary>
Installed,
}
/// <summary>
/// The type of chat provider
/// </summary>
public enum ChatProvider : int
{
/// <summary>
/// IRC chat provider
/// </summary>
IRC = 0,
/// <summary>
/// Discord chat provider
/// </summary>
Discord = 1,
}
/// <summary>
/// Supported irc permission modes
/// </summary>
public enum IRCMode : int
{
/// <summary>
/// +
/// </summary>
Voice,
/// <summary>
/// %
/// </summary>
Halfop,
/// <summary>
/// @
/// </summary>
Op,
/// <summary>
/// ~
/// </summary>
Owner,
}
/// <summary>
/// The status of the compiler
/// </summary>
public enum CompilerStatus
{
/// <summary>
/// Game folder is broken or does not exist
/// </summary>
Uninitialized,
/// <summary>
/// Game folder is being created
/// </summary>
Initializing,
/// <summary>
/// Game folder is setup, does not imply the dmb is compiled
/// </summary>
Initialized,
/// <summary>
/// Game is being compiled
/// </summary>
Compiling,
}
/// <summary>
/// The status of the DD instance
/// </summary>
public enum DreamDaemonStatus
{
/// <summary>
/// Server is not running
/// </summary>
Offline,
/// <summary>
/// Server is being rebooted
/// </summary>
HardRebooting,
/// <summary>
/// Server is running
/// </summary>
Online,
}
/// <summary>
/// DreamDaemon's security level
/// </summary>
public enum DreamDaemonSecurity
{
/// <summary>
/// Server is unrestricted in terms of file access and shell commands
/// </summary>
Trusted = 0,
/// <summary>
/// Server will not be able to run shell commands or access files outside it's working directory
/// </summary>
Safe,
/// <summary>
/// Server will not be able to run shell commands or access anything but temporary files
/// </summary>
Ultrasafe
}
}
-49
View File
@@ -1,49 +0,0 @@
using System;
using System.Security.Cryptography;
using System.Text;
namespace TGS.Interface
{
/// <summary>
/// Helper functions used across the server suite
/// </summary>
public static class Helpers
{
/// <summary>
/// Takes some <paramref name="cleartext"/> and returns an encrypted version along with the <paramref name="entropy"/> required to decrypt it
/// </summary>
/// <param name="cleartext">The <see cref="string"/> to encrypt</param>
/// <param name="entropy">The entropy required the decrypt the ciphertext</param>
/// <returns>Ciphertext for the <paramref name="cleartext"/></returns>
public static string EncryptData(string cleartext, out string entropy)
{
// Generate additional entropy (will be used as the Initialization vector)
byte[] bentropy = new byte[20];
using (var rng = new RNGCryptoServiceProvider())
rng.GetBytes(bentropy);
byte[] ciphertext = ProtectedData.Protect(Encoding.UTF8.GetBytes(cleartext), bentropy, DataProtectionScope.CurrentUser);
entropy = Convert.ToBase64String(bentropy, 0, bentropy.Length);
return Convert.ToBase64String(ciphertext, 0, ciphertext.Length);
}
/// <summary>
/// Takes ciphertext and entropy from <see cref="EncryptData(string, out string)"/> and returns the cleartext. Note that this only works if the OS user of the program is the same one that called <see cref="EncryptData(string, out string)"/>
/// </summary>
/// <param name="ciphertext">A return value from a previous call to <see cref="EncryptData(string, out string)"/></param>
/// <param name="entropy">The entropy parameter from the previous call to <see cref="EncryptData(string, out string)"/> that returned <paramref name="ciphertext"/></param>
/// <returns>The decrypted <see cref="string"/> on sucess or <see langword="null"/> on failure</returns>
public static string DecryptData(string ciphertext, string entropy)
{
try
{
return Encoding.UTF8.GetString(ProtectedData.Unprotect(Convert.FromBase64String(ciphertext), Convert.FromBase64String(entropy), DataProtectionScope.CurrentUser));
}
catch
{
return null;
}
}
}
}
-44
View File
@@ -1,44 +0,0 @@
using System;
namespace TGS.Interface
{
/// <summary>
/// Main <see langword="interface"/> for communicating the <see cref="Components.ITGSService"/>
/// </summary>
public interface IClient : IDisposable
{
/// <summary>
/// The <see cref="IServer"/> the <see cref="IClient"/> connects to
/// </summary>
IServer Server { get; }
/// <summary>
/// The <see cref="RemoteLoginInfo"/> for the <see cref="IClient"/>. Is <see langword="null"/> for local connections
/// </summary>
RemoteLoginInfo LoginInfo { get; }
/// <summary>
/// Checks if the <see cref="IClient"/> is setup for a remote connection
/// </summary>
bool IsRemoteConnection { get; }
/// <summary>
/// Returns <see langword="true"/> if the <see cref="IClient"/> interface being used to connect to a service does not have the same release version as the service
/// </summary>
/// <param name="errorMessage">An error message to display to the user should this function return <see langword="true"/></param>
/// <returns><see langword="true"/> if the <see cref="IClient"/> interface being used to connect to a service does not have the same release version as the service</returns>
bool VersionMismatch(out string errorMessage);
/// <summary>
/// See <see cref="ConnectionStatus(out string)"/> without the error argument
/// </summary>
ConnectivityLevel ConnectionStatus();
/// <summary>
/// Used to test if the <see cref="Components.ITGSService"/> is avaiable on the target machine. Note that state can change at any time and any call into the may throw an exception because of communcation errors
/// </summary>
/// <param name="error">String of the error that prevented an elevated connectivity level</param>
/// <returns>The apporopriate <see cref="ConnectivityLevel"/></returns>
ConnectivityLevel ConnectionStatus(out string error);
}
}
-55
View File
@@ -1,55 +0,0 @@
using TGS.Interface.Components;
namespace TGS.Interface
{
/// <summary>
/// Wrapper for <see cref="ITGInstance"/> components
/// </summary>
public interface IInstance : ITGInstance
{
/// <summary>
/// Get the <see cref="InstanceMetadata"/> for the <see cref="IInstance"/>
/// </summary>
InstanceMetadata Metadata { get; }
/// <summary>
/// The <see cref="ITGAdministration"/> component. Will be <see langword="null"/> if the connected user is not an administrator of the <see cref="IInstance"/>
/// </summary>
ITGAdministration Administration { get; }
/// <summary>
/// The <see cref="ITGByond"/> component
/// </summary>
ITGByond Byond { get; }
/// <summary>
/// The <see cref="ITGChat"/> component
/// </summary>
ITGChat Chat { get; }
/// <summary>
/// The <see cref="ITGCompiler"/> component
/// </summary>
ITGCompiler Compiler { get; }
/// <summary>
/// The <see cref="ITGConfig"/> component
/// </summary>
ITGConfig Config { get; }
/// <summary>
/// The <see cref="ITGDreamDaemon"/> component
/// </summary>
ITGDreamDaemon DreamDaemon { get; }
/// <summary>
/// The <see cref="ITGInterop"/> component
/// </summary>
ITGInterop Interop { get; }
/// <summary>
/// The <see cref="ITGRepository"/> component
/// </summary>
ITGRepository Repository { get; }
}
}
-44
View File
@@ -1,44 +0,0 @@
using System;
using System.Collections.Generic;
using TGS.Interface.Components;
namespace TGS.Interface
{
/// <summary>
/// Wrapper representing a <see cref="ITGSService"/>
/// </summary>
public interface IServer
{
/// <summary>
/// The <see cref="System.Version"/> of the <see cref="IServer"/>
/// </summary>
Version Version { get; }
/// <summary>
/// Get the <see cref="IInstance"/>s the <see cref="IServer"/> contains that the current user can access and connect to
/// </summary>
IEnumerable<IInstance> Instances { get; }
/// <summary>
/// The <see cref="ITGInstanceManager"/> component. Will be <see langword="null"/> if the connected user is not an administrator of the <see cref="IServer"/>
/// </summary>
ITGInstanceManager InstanceManager { get; }
/// <summary>
/// The <see cref="ITGSService"/> component. Will be <see langword="null"/> if the connected user is not an administrator of the <see cref="IServer"/>
/// </summary>
ITGSService Management { get; }
/// <summary>
/// Gets the specified <see cref="IInstance"/> without connectivity checks
/// </summary>
/// <param name="name">The name of the <see cref="IInstance"/> to get</param>
/// <returns>The <see cref="IInstance"/> named <paramref name="name"/> on success, <see langword="null"/> on failure</returns>
IInstance GetInstance(string name);
/// <summary>
/// Rebuilds the internal cached <see cref="IInstance"/> list
/// </summary>
void RebuildInstanceList();
}
}
-119
View File
@@ -1,119 +0,0 @@
using System;
using System.Linq;
using TGS.Interface.Components;
namespace TGS.Interface
{
/// <inheritdoc />
sealed class Instance : IInstance
{
/// <inheritdoc />
public InstanceMetadata Metadata
{
get
{
if (!metadata.Enabled)
//metadata needs populating
metadata = serverInterface.GetComponent<ITGLanding>(null).ListInstances().Where(x => x.Name == metadata.Name).First();
return metadata;
}
}
/// <summary>
/// Whether or not the current user is known to be an administrator of the <see cref="IInstance"/>
/// </summary>
bool UserIsAdministrator
{
get
{
lock (this)
{
if (isAdministrator)
return true;
try
{
serverInterface.GetComponent<ITGAdministration>(metadata.Name).GetCurrentAuthorizedGroup();
isAdministrator = true;
return true;
}
catch
{
return false;
}
}
}
}
/// <summary>
/// The backing <see cref="Client"/>
/// </summary>
readonly Client serverInterface;
/// <summary>
/// The name of the <see cref="IInstance"/>
/// </summary>
InstanceMetadata metadata;
/// <summary>
/// Backing field for <see cref="UserIsAdministrator"/>
/// </summary>
bool isAdministrator;
/// <summary>
/// Construct an <see cref="Instance"/>
/// </summary>
/// <param name="_serverInterface">The <see cref="Client"/> to use</param>
/// <param name="_metadata">The <see cref="InstanceMetadata"/> for the <see cref="IInstance"/></param>
public Instance(Client _serverInterface, InstanceMetadata _metadata)
{
serverInterface = _serverInterface;
metadata = _metadata;
if (metadata.Enabled)
//run a connectivity check
serverInterface.GetComponent<ITGConnectivity>(metadata.Name).VerifyConnection();
}
/// <inheritdoc />
public ITGAdministration Administration => UserIsAdministrator ? serverInterface.GetComponent<ITGAdministration>(metadata.Name) : null;
/// <inheritdoc />
public ITGByond Byond => serverInterface.GetComponent<ITGByond>(metadata.Name);
/// <inheritdoc />
public ITGChat Chat => serverInterface.GetComponent<ITGChat>(metadata.Name);
/// <inheritdoc />
public ITGCompiler Compiler => serverInterface.GetComponent<ITGCompiler>(metadata.Name);
/// <inheritdoc />
public ITGConfig Config => serverInterface.GetComponent<ITGConfig>(metadata.Name);
/// <inheritdoc />
public ITGDreamDaemon DreamDaemon => serverInterface.GetComponent<ITGDreamDaemon>(metadata.Name);
/// <inheritdoc />
public ITGInterop Interop => serverInterface.GetComponent<ITGInterop>(metadata.Name);
/// <inheritdoc />
public ITGRepository Repository => serverInterface.GetComponent<ITGRepository>(metadata.Name);
/// <inheritdoc />
public string ServerDirectory()
{
return serverInterface.GetComponent<ITGInstance>(metadata.Name).ServerDirectory();
}
/// <inheritdoc />
public string Version()
{
return serverInterface.GetComponent<ITGInstance>(metadata.Name).Version();
}
/// <summary>
/// Get a string representation of the <see cref="Instance"/>
/// </summary>
/// <returns>A string representation of the <see cref="Instance"/></returns>
public override string ToString()
{
return String.Format("{0}: {1} - {2} - {3}", metadata.LoggingID, metadata.Name, metadata.Path, metadata.Enabled ? "ONLINE" : "OFFLINE");
}
}
}
-33
View File
@@ -1,33 +0,0 @@
using System.Runtime.Serialization;
namespace TGS.Interface
{
/// <summary>
/// Metadata about an <see cref="Components.ITGInstance"/>
/// </summary>
//Namespace required for compatibility reasons
[DataContract(Namespace = "http://schemas.datacontract.org/2004/07/TGServiceInterface")]
public sealed class InstanceMetadata
{
/// <summary>
/// The name of the <see cref="Components.ITGInstance"/>
/// </summary>
[DataMember]
public string Name { get; set; }
/// <summary>
/// The path of the <see cref="Components.ITGInstance"/>
/// </summary>
[DataMember]
public string Path { get; set; }
/// <summary>
/// Whether or not the <see cref="Components.ITGInstance"/> is enabled
/// </summary>
[DataMember]
public bool Enabled { get; set; }
/// <summary>
/// The logging ID of the <see cref="Components.ITGInstance"/>. Will be 0 if <see cref="Enabled"/> is <see langword="false"/>
/// </summary>
[DataMember]
public byte LoggingID { get; set; }
}
}
-16
View File
@@ -1,16 +0,0 @@
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("TGStation Server Service Interface")]
[assembly: AssemblyDescription("Used by user programs to access the TGStation Server Service")]
// 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("ac4e7e8b-f83a-481c-a8b0-8fa4e8ae59ab")]
-58
View File
@@ -1,58 +0,0 @@
using System.Runtime.Serialization;
namespace TGS.Interface
{
/// <summary>
/// Information about a pull request
/// </summary>
[DataContract]
public sealed class PullRequestInfo
{
/// <summary>
/// The PR number
/// </summary>
[DataMember]
public int Number { get; private set; }
/// <summary>
/// The PR's author
/// </summary>
[DataMember]
public string Author { get; private set; }
/// <summary>
/// The PR's title
/// </summary>
[DataMember]
public string Title { get; private set; }
/// <summary>
/// The commit the PR was merged locally at
/// </summary>
[DataMember]
public string Sha { get; private set; }
/// <summary>
/// Construct a <see cref="PullRequestInfo"/>
/// </summary>
/// <param name="number">The PR number</param>
/// <param name="author">The PR's author</param>
/// <param name="title">The PR's title</param>
/// <param name="sha">The commit the PR was merged locally at</param>
public PullRequestInfo(int number, string author, string title, string sha)
{
Number = number;
Author = author;
Title = title;
Sha = sha;
}
/// <summary>
/// Construct a <see cref="PullRequestInfo"/> for a call to <see cref="Components.ITGRepository.MergedPullRequests"/>
/// </summary>
/// <param name="number">The PR number</param>
/// <param name="sha">The optional commit to merge the PR at</param>
public PullRequestInfo(int number, string sha = null)
{
Number = number;
Sha = sha;
}
}
}
-132
View File
@@ -1,132 +0,0 @@
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
namespace TGS.Interface
{
/// <summary>
/// Information representing a remote server connection
/// </summary>
public sealed class RemoteLoginInfo : IEquatable<RemoteLoginInfo>
{
/// <summary>
/// Used for <see cref="Password"/> serialization
/// </summary>
const string EntropyFormatter = "{0}Entropy";
/// <summary>
/// The IP address or URL of the target server
/// </summary>
public string IP { get { return _ip; } }
/// <summary>
/// The port to connect to the target server
/// </summary>
public ushort Port { get { return _port; } }
/// <summary>
/// A Windows username for the target server
/// </summary>
public string Username { get { return _username; } }
/// <summary>
/// The Windows password for <see cref="Username"/>
/// </summary>
public string Password { internal get; set; }
/// <summary>
/// Check if the <see cref="RemoteLoginInfo"/> has been initialized with a <see cref="Password"/>
/// </summary>
[JsonIgnore]
public bool HasPassword { get { return !String.IsNullOrWhiteSpace(Password); } }
/// <summary>
/// Backing field for <see cref="IP"/>
/// </summary>
readonly string _ip;
/// <summary>
/// Backing field for <see cref="Port"/>
/// </summary>
readonly ushort _port;
/// <summary>
/// Backing field for <see cref="Username"/>
/// </summary>
readonly string _username;
/// <summary>
/// Construct a <see cref="RemoteLoginInfo"/>
/// </summary>
/// <param name="ip">The value for <see cref="IP"/></param>
/// <param name="port">The value for <see cref="Port"/></param>
/// <param name="username">The value for <see cref="Username"/></param>
/// <param name="password">The value for <see cref="Password"/></param>
public RemoteLoginInfo(string ip, ushort port, string username, string password)
{
if (String.IsNullOrWhiteSpace(ip))
throw new InvalidOperationException("ip must be set!");
_ip = ip;
if (port == 0)
throw new InvalidOperationException("port may not be 0!");
_port = port;
if (String.IsNullOrWhiteSpace(username))
throw new InvalidOperationException("username must be set!");
_username = username;
Password = password;
}
/// <summary>
/// Construct a <see cref="RemoteLoginInfo"/> from JSON
/// </summary>
/// <param name="json">The result of a call to <see cref="ToJSON"/></param>
public RemoteLoginInfo(string json)
{
var dic = JsonConvert.DeserializeObject<IDictionary<string, object>>(json);
var ip = (string)dic[nameof(IP)];
if (String.IsNullOrWhiteSpace(ip))
throw new InvalidOperationException("ip must be set!");
_ip = ip;
var port = (ushort)(long)dic[nameof(Port)];
if (port == 0)
throw new InvalidOperationException("port may not be 0!");
_port = port;
var username = (string)dic[nameof(Username)];
if (String.IsNullOrWhiteSpace(username))
throw new InvalidOperationException("username must be set!");
_username = username;
if(dic.ContainsKey(nameof(Password)))
Password = Helpers.DecryptData((string)dic[nameof(Password)], (string)dic[String.Format(EntropyFormatter, nameof(Password))]);
}
/// <summary>
/// Returns <see cref="IP"/>
/// </summary>
/// <returns><see cref="IP"/></returns>
public override string ToString()
{
return IP;
}
/// <summary>
/// Checks if another <see cref="RemoteLoginInfo"/> matches <see langword="this"/> one
/// </summary>
/// <param name="other">Another <see cref="RemoteLoginInfo"/></param>
/// <returns><see langword="true"/> if <see langword="this"/> and <paramref name="other"/> have the same <see cref="IP"/>, <see cref="Port"/>, and <see cref="Username"/></returns>
public bool Equals(RemoteLoginInfo other)
{
return other != null && IP == other.IP && Port == other.Port && Username == other.Username;
}
/// <summary>
/// Returns a JSON representation of the <see cref="RemoteLoginInfo"/> with the <see cref="Password"/> encrypted
/// </summary>
/// <returns>A JSON representation of the <see cref="RemoteLoginInfo"/></returns>
public string ToJSON()
{
//serialize it to a dic first so we can store the entropy
var raw = JsonConvert.SerializeObject(this);
var dic = JsonConvert.DeserializeObject<IDictionary<string, object>>(raw);
if (HasPassword)
{
dic.Add(nameof(Password), Helpers.EncryptData(Password, out string entropy));
dic.Add(String.Format(EntropyFormatter, nameof(Password)), entropy);
}
return JsonConvert.SerializeObject(dic);
}
}
}
-105
View File
@@ -1,105 +0,0 @@
using System;
using System.Collections.Generic;
namespace TGS.Interface
{
/// <summary>
/// Helper for creating commands that contain sub commands
/// </summary>
public abstract class RootCommand : Command
{
/// <summary>
/// <see cref="Command"/>s further down the tree from this one. Set in Constructor
/// </summary>
public Command[] Children { get; protected set; } = { };
/// <summary>
/// If set to <see langword="true"/> a multiline, detailed list of <see cref="Command"/>s will be printed. Otherwise a singleline list of <see cref="Command"/>s will be printed
/// </summary>
public static bool PrintHelpList = false;
/// <summary>
/// Forward parameters to commands further down the tree
/// </summary>
/// <param name="parameters">List of parameters passed to the <see cref="RootCommand"/></param>
/// <returns>The result of a sub <see cref="Command"/> or an appropriate <see cref="Command.ExitCode"/> if the <see cref="RootCommand"/> handled it</returns>
protected override ExitCode Run(IList<string> parameters)
{
if (parameters.Count > 0)
{
var LocalKeyword = parameters[0].Trim().ToLower();
parameters.RemoveAt(0);
switch (LocalKeyword)
{
case "help":
case "?":
PrintHelp();
return ExitCode.Normal;
default:
foreach (var c in Children)
if (c.Keyword == LocalKeyword)
{
if (parameters.Count > 0)
{
var possibleHelp = parameters[0].ToLower();
if (possibleHelp == "help" || possibleHelp == "?")
{
c.PrintHelp();
return ExitCode.Normal;
}
}
if (parameters.Count < c.RequiredParameters)
{
OutputProc("Not enough parameters!");
return ExitCode.BadCommand;
}
return c.DoRun(parameters);
}
parameters.Insert(0, LocalKeyword);
break;
}
}
OutputProc(String.Format("Invalid command! Type '{0}?' or '{0}help' for available commands.", Keyword != null ? Keyword + " " : ""));
return ExitCode.BadCommand;
}
/// <inheritdoc />
public override void PrintHelp()
{
var Final = new List<string>();
if (PrintHelpList)
{
foreach (var c in Children)
Final.Add(c.Keyword);
OutputProc("Available commands (type '?' or 'help' after command for more info): " + String.Join(", ", Final));
}
else
{
var Prefixes = new List<string>();
var Postfixes = new List<string>();
int MaxPrefixLen = 0;
foreach (var c in Children)
{
var ns = c.Keyword + " " + c.GetArgumentString();
MaxPrefixLen = Math.Max(MaxPrefixLen, ns.Length);
Prefixes.Add(ns);
Postfixes.Add(c.GetHelpText());
}
for (var I = 0; I < Prefixes.Count; ++I)
{
var lp = Prefixes[I];
for (; lp.Length < MaxPrefixLen + 1; lp += " ") ;
Final.Add(lp + "- " + Postfixes[I]);
}
Final.Sort();
Final.ForEach(OutputProc);
}
}
/// <inheritdoc />
public override string GetHelpText()
{
throw new NotImplementedException();
}
}
}
-103
View File
@@ -1,103 +0,0 @@
using System;
using System.Collections.Generic;
using TGS.Interface.Components;
namespace TGS.Interface
{
/// <inheritdoc />
sealed class Server : IServer
{
/// <inheritdoc />
public Version Version => serverInterface.ServerVersion;
/// <inheritdoc />
public IEnumerable<IInstance> Instances { get
{
lock (this)
if (knownInstances == null)
RebuildInstanceList();
foreach (var I in knownInstances)
{
IInstance nextInstance;
try
{
nextInstance = new Instance(serverInterface, I);
}
catch
{
continue;
}
yield return nextInstance;
}
}
}
/// <inheritdoc />
public ITGInstanceManager InstanceManager => UserIsAdministrator ? serverInterface.GetComponent<ITGInstanceManager>(null) : null;
/// <inheritdoc />
public ITGSService Management => UserIsAdministrator ? serverInterface.GetComponent<ITGSService>(null) : null;
/// <summary>
/// If the connected user is an administrator of the <see cref="IServer"/>
/// </summary>
bool UserIsAdministrator
{
get
{
lock (this)
{
if (isAdministrator)
return true;
try
{
serverInterface.GetComponent<ITGSService>(null).Version();
isAdministrator = true;
return true;
}
catch
{
return false;
}
}
}
}
/// <summary>
/// The backing <see cref="Client"/>
/// </summary>
readonly Client serverInterface;
/// <summary>
/// Result of a call to <see cref="ITGLanding.ListInstances"/>
/// </summary>
IList<InstanceMetadata> knownInstances;
/// <summary>
/// Backing field for <see cref="UserIsAdministrator"/>
/// </summary>
bool isAdministrator;
/// <summary>
/// Construct an <see cref="Server"/>
/// </summary>
/// <param name="_serverInterface">The <see cref="Client"/> to use</param>
public Server(Client _serverInterface)
{
serverInterface = _serverInterface;
}
/// <inheritdoc />
public IInstance GetInstance(string name)
{
return new Instance(serverInterface, new InstanceMetadata { Name = name, Enabled = false });
}
/// <inheritdoc />
public void RebuildInstanceList()
{
lock (this)
knownInstances = serverInterface.GetComponent<ITGLanding>(null).ListInstances();
}
}
}
-92
View File
@@ -1,92 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{AC4E7E8B-F83A-481C-A8B0-8FA4E8AE59AB}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>TGS.Interface</RootNamespace>
<AssemblyName>TGServiceInterface</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup>
<ApplicationIcon>tgs.ico</ApplicationIcon>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'">
<DebugSymbols>true</DebugSymbols>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|AnyCPU'">
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<DocumentationFile>bin\x86\Release\TGS.Interface.xml</DocumentationFile>
<Optimize>true</Optimize>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<DebugType>pdbonly</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<RegisterForComInterop>false</RegisterForComInterop>
</PropertyGroup>
<PropertyGroup>
<SignAssembly>false</SignAssembly>
</PropertyGroup>
<ItemGroup>
<Reference Include="Newtonsoft.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Runtime.Serialization" />
<Reference Include="System.Security" />
<Reference Include="System.ServiceModel" />
</ItemGroup>
<ItemGroup>
<Compile Include="AuthenticationHeaderApplicator.cs" />
<Compile Include="Components\Administration.cs" />
<Compile Include="Components\Byond.cs" />
<Compile Include="ChatSetupInfo.cs" />
<Compile Include="Command.cs" />
<Compile Include="Components\Compiler.cs" />
<Compile Include="Components\Config.cs" />
<Compile Include="Components\Connectivity.cs" />
<Compile Include="Components\DreamDaemon.cs" />
<Compile Include="Components\Chat.cs" />
<Compile Include="Components\Instance.cs" />
<Compile Include="Components\InstanceManager.cs" />
<Compile Include="Components\Landing.cs" />
<Compile Include="Definitions.cs" />
<Compile Include="Enumerations.cs" />
<Compile Include="Helpers.cs" />
<Compile Include="IInstance.cs" />
<Compile Include="InstanceMetadata.cs" />
<Compile Include="Instance.cs" />
<Compile Include="IServer.cs" />
<Compile Include="IClient.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Components\Repository.cs" />
<Compile Include="PullRequestInfo.cs" />
<Compile Include="RemoteLoginInfo.cs" />
<Compile Include="RootCommand.cs" />
<Compile Include="Client.cs" />
<Compile Include="..\AssemblyInfo.global.cs" />
<Compile Include="Components\Service.cs" />
<Compile Include="Components\Interop.cs" />
<Compile Include="Server.cs" />
</ItemGroup>
<ItemGroup>
<Content Include="tgs.ico" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
<None Include="TGS.Interface.nuspec" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

Some files were not shown because too many files have changed in this diff Show More