Adds player counting

This commit is contained in:
Cyberboss
2017-10-02 11:03:22 -04:00
parent d154a4c65d
commit 0a1a8d5a29
10 changed files with 1630 additions and 1588 deletions
+1
View File
@@ -5,3 +5,4 @@
#define SERVER_TOOLS_WORLD_ANNOUNCE(message) world << ##message
#define SERVER_TOOLS_LOG(message) world.log << ##message
#define SERVER_TOOLS_NOTIFY_ADMINS(event) message_admins(event)
#define SERVER_TOOLS_CLIENT_COUNT clients.len
+126 -123
View File
@@ -1,123 +1,126 @@
// /tg/station 13 server tools API v3.1
//CONFIGURATION
//use this define if you want to do configuration outside of this file
#ifndef SERVER_TOOLS_EXTERNAL_CONFIGURATION
//Comment this out once you've filled in the below
#error /tg/station server tools interface unconfigured
//Required interfaces (fill in with your codebase equivalent):
//create a global variable named `Name` and set it to `Value`
//These globals must not be modifiable from anywhere outside of the server tools
#define SERVER_TOOLS_DEFINE_AND_SET_GLOBAL(Name, Value)
//Read the value in the global variable `Name`
#define SERVER_TOOLS_READ_GLOBAL(Name)
//Set the value in the global variable `Name` to `Value`
#define SERVER_TOOLS_WRITE_GLOBAL(Name, Value)
//display an announcement `message` from the server to all players
#define SERVER_TOOLS_WORLD_ANNOUNCE(message)
//Write a string `message` to a server log
#define SERVER_TOOLS_LOG(message)
//Notify current in-game administrators of a string `event`
#define SERVER_TOOLS_NOTIFY_ADMINS(event)
#endif
//Required hooks:
//Put this somewhere in /world/New() that is always run
#define SERVER_TOOLS_ON_NEW ServiceInit()
//Put this somewhere in /world/Topic(T, Addr, Master, Keys) that is always run before T is modified
#define SERVER_TOOLS_ON_TOPIC var/service_topic_return = ServiceCommand(params2list(T)); if(service_topic_return) return service_topic_return
//Put at the beginning of world/Reboot(reason)
#define SERVER_TOOLS_ON_REBOOT ServiceReboot()
//Optional callable functions:
//Returns the string version of the API
#define SERVER_TOOLS_API_VERSION ServiceAPIVersion()
//Returns TRUE if the world was launched under the server tools and the API matches, FALSE otherwise
//No function below this succeed if this is FALSE
#define SERVER_TOOLS_PRESENT RunningService()
//Gets the current version of the service running the server
#define SERVER_TOOLS_VERSION ServiceVersion()
//Forces a hard reboot of BYOND by ending the process
//unlike del(world) clients will try to reconnect
//If the service has not requested a shutdown, the world will reboot shortly after
#define SERVER_TOOLS_REBOOT_BYOND world.ServiceEndProcess()
/*
Gets the list of any testmerged github pull requests
"[PR Number]" => list(
"title" -> PR title
"commit" -> Full hash of commit merged
"author" -> Github username of the author of the PR
)
*/
#define SERVER_TOOLS_PR_LIST GetTestMerges()
//Sends a message to connected game chats
#define SERVER_TOOLS_CHAT_BROADCAST(message) world.ChatBroadcast(message)
//Sends a message to connected admin chats
#define SERVER_TOOLS_RELAY_BROADCAST(message) world.AdminBroadcast(message)
//IMPLEMENTATION
#define SERVICE_API_VERSION_STRING "3.1.0.0"
#define REBOOT_MODE_NORMAL 0
#define REBOOT_MODE_HARD 1
#define REBOOT_MODE_SHUTDOWN 2
#define SERVICE_WORLD_PARAM "server_service"
#define SERVICE_VERSION_PARAM "server_service_version"
#define SERVICE_PR_TEST_JSON "prtestjob.json"
#define SERVICE_INTERFACE_DLL "TGServiceInterface.dll"
#define SERVICE_INTERFACE_FUNCTION "DDEntryPoint"
#define SERVICE_CMD_HARD_REBOOT "hard_reboot"
#define SERVICE_CMD_GRACEFUL_SHUTDOWN "graceful_shutdown"
#define SERVICE_CMD_WORLD_ANNOUNCE "world_announce"
#define SERVICE_CMD_LIST_CUSTOM "list_custom_commands"
#define SERVICE_CMD_PARAM_KEY "serviceCommsKey"
#define SERVICE_CMD_PARAM_COMMAND "command"
#define SERVICE_CMD_PARAM_SENDER "sender"
#define SERVICE_CMD_PARAM_CUSTOM "custom"
#define SERVICE_CMD_API_COMPATIBLE "api_compat"
#define SERVICE_JSON_PARAM_HELPTEXT "help_text"
#define SERVICE_JSON_PARAM_ADMINONLY "admin_only"
#define SERVICE_JSON_PARAM_REQUIREDPARAMETERS "required_parameters"
#define SERVICE_REQUEST_KILL_PROCESS "killme"
#define SERVICE_REQUEST_IRC_BROADCAST "irc"
#define SERVICE_REQUEST_IRC_ADMIN_CHANNEL_MESSAGE "send2irc"
#define SERVICE_REQUEST_WORLD_REBOOT "worldreboot"
#define SERVICE_REQUEST_API_VERSION "api_ver"
/*
The MIT License
Copyright (c) 2011 Dominic Tarr
Permission is hereby granted, free of charge,
to any person obtaining a copy of this software and
associated documentation files (the "Software"), to
deal in the Software without restriction, including
without limitation the rights to use, copy, modify,
merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom
the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
// /tg/station 13 server tools API v3.1.0.1
//CONFIGURATION
//use this define if you want to do configuration outside of this file
#ifndef SERVER_TOOLS_EXTERNAL_CONFIGURATION
//Comment this out once you've filled in the below
#error /tg/station server tools interface unconfigured
//Required interfaces (fill in with your codebase equivalent):
//create a global variable named `Name` and set it to `Value`
//These globals must not be modifiable from anywhere outside of the server tools
#define SERVER_TOOLS_DEFINE_AND_SET_GLOBAL(Name, Value)
//Read the value in the global variable `Name`
#define SERVER_TOOLS_READ_GLOBAL(Name)
//Set the value in the global variable `Name` to `Value`
#define SERVER_TOOLS_WRITE_GLOBAL(Name, Value)
//display an announcement `message` from the server to all players
#define SERVER_TOOLS_WORLD_ANNOUNCE(message)
//Write a string `message` to a server log
#define SERVER_TOOLS_LOG(message)
//Notify current in-game administrators of a string `event`
#define SERVER_TOOLS_NOTIFY_ADMINS(event)
//The current amount of connected clients
#define SERVER_TOOLS_CLIENT_COUNT
#endif
//Required hooks:
//Put this somewhere in /world/New() that is always run
#define SERVER_TOOLS_ON_NEW ServiceInit()
//Put this somewhere in /world/Topic(T, Addr, Master, Keys) that is always run before T is modified
#define SERVER_TOOLS_ON_TOPIC var/service_topic_return = ServiceCommand(params2list(T)); if(service_topic_return) return service_topic_return
//Put at the beginning of world/Reboot(reason)
#define SERVER_TOOLS_ON_REBOOT ServiceReboot()
//Optional callable functions:
//Returns the string version of the API
#define SERVER_TOOLS_API_VERSION ServiceAPIVersion()
//Returns TRUE if the world was launched under the server tools and the API matches, FALSE otherwise
//No function below this succeed if this is FALSE
#define SERVER_TOOLS_PRESENT RunningService()
//Gets the current version of the service running the server
#define SERVER_TOOLS_VERSION ServiceVersion()
//Forces a hard reboot of BYOND by ending the process
//unlike del(world) clients will try to reconnect
//If the service has not requested a shutdown, the world will reboot shortly after
#define SERVER_TOOLS_REBOOT_BYOND world.ServiceEndProcess()
/*
Gets the list of any testmerged github pull requests
"[PR Number]" => list(
"title" -> PR title
"commit" -> Full hash of commit merged
"author" -> Github username of the author of the PR
)
*/
#define SERVER_TOOLS_PR_LIST GetTestMerges()
//Sends a message to connected game chats
#define SERVER_TOOLS_CHAT_BROADCAST(message) world.ChatBroadcast(message)
//Sends a message to connected admin chats
#define SERVER_TOOLS_RELAY_BROADCAST(message) world.AdminBroadcast(message)
//IMPLEMENTATION
#define SERVICE_API_VERSION_STRING "3.1.0.1"
#define REBOOT_MODE_NORMAL 0
#define REBOOT_MODE_HARD 1
#define REBOOT_MODE_SHUTDOWN 2
#define SERVICE_WORLD_PARAM "server_service"
#define SERVICE_VERSION_PARAM "server_service_version"
#define SERVICE_PR_TEST_JSON "prtestjob.json"
#define SERVICE_INTERFACE_DLL "TGServiceInterface.dll"
#define SERVICE_INTERFACE_FUNCTION "DDEntryPoint"
#define SERVICE_CMD_HARD_REBOOT "hard_reboot"
#define SERVICE_CMD_GRACEFUL_SHUTDOWN "graceful_shutdown"
#define SERVICE_CMD_WORLD_ANNOUNCE "world_announce"
#define SERVICE_CMD_LIST_CUSTOM "list_custom_commands"
#define SERVICE_CMD_API_COMPATIBLE "api_compat"
#define SERVICE_CMD_PLAYER_COUNT "client_count"
#define SERVICE_CMD_PARAM_KEY "serviceCommsKey"
#define SERVICE_CMD_PARAM_COMMAND "command"
#define SERVICE_CMD_PARAM_SENDER "sender"
#define SERVICE_CMD_PARAM_CUSTOM "custom"
#define SERVICE_JSON_PARAM_HELPTEXT "help_text"
#define SERVICE_JSON_PARAM_ADMINONLY "admin_only"
#define SERVICE_JSON_PARAM_REQUIREDPARAMETERS "required_parameters"
#define SERVICE_REQUEST_KILL_PROCESS "killme"
#define SERVICE_REQUEST_IRC_BROADCAST "irc"
#define SERVICE_REQUEST_IRC_ADMIN_CHANNEL_MESSAGE "send2irc"
#define SERVICE_REQUEST_WORLD_REBOOT "worldreboot"
#define SERVICE_REQUEST_API_VERSION "api_ver"
/*
The MIT License
Copyright (c) 2011 Dominic Tarr
Permission is hereby granted, free of charge,
to any person obtaining a copy of this software and
associated documentation files (the "Software"), to
deal in the Software without restriction, including
without limitation the rights to use, copy, modify,
merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom
the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice
shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
+2
View File
@@ -89,6 +89,8 @@ SERVER_TOOLS_DEFINE_AND_SET_GLOBAL(server_tools_api_compatible, FALSE)
return "No message set!"
SERVER_TOOLS_WORLD_ANNOUNCE(msg)
return "SUCCESS"
if(SERVICE_CMD_PLAYER_COUNT)
return "[SERVER_TOOLS_CLIENT_COUNT]"
if(SERVICE_CMD_LIST_CUSTOM)
return json_encode(ListServiceCustomCommands(FALSE))
else
+314 -311
View File
@@ -1,311 +1,314 @@
using System;
using System.Collections.Generic;
using TGServiceInterface;
namespace TGCommandLine
{
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 : Command
{
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 = Server.GetComponent<ITGDreamDaemon>().WorldAnnounce(String.Join(" ", parameters));
OutputProc(res ?? "Success!");
return res == null ? ExitCode.Normal : ExitCode.ServerError;
}
}
class DDStartCommand : Command
{
public DDStartCommand()
{
Keyword = "start";
}
public override string GetHelpText()
{
return "Starts the server and watchdog";
}
protected override ExitCode Run(IList<string> parameters)
{
var res = Server.GetComponent<ITGDreamDaemon>().Start();
OutputProc(res ?? "Success!");
return res == null ? ExitCode.Normal : ExitCode.ServerError;
}
}
class DDStopCommand : Command
{
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 = Server.GetComponent<ITGDreamDaemon>();
if (parameters.Count > 0 && parameters[0].ToLower() == "--graceful")
{
if (DD.DaemonStatus() != TGDreamDaemonStatus.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 : Command
{
public DDRestartCommand()
{
Keyword = "restart";
}
public override string GetArgumentString()
{
return "[--graceful]";
}
protected override ExitCode Run(IList<string> parameters)
{
var DD = Server.GetComponent<ITGDreamDaemon>();
if (parameters.Count > 0 && parameters[0].ToLower() == "--graceful")
{
if (DD.DaemonStatus() != TGDreamDaemonStatus.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 : Command
{
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 = Server.GetComponent<ITGDreamDaemon>();
OutputProc(DD.StatusString(true));
if (DD.ShutdownInProgress())
OutputProc("The server will shutdown once the current round completes.");
return ExitCode.Normal;
}
}
class DDAutostartCommand : Command
{
public DDAutostartCommand()
{
Keyword = "autostart";
RequiredParameters = 1;
}
protected override ExitCode Run(IList<string> parameters)
{
var DD = Server.GetComponent<ITGDreamDaemon>();
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 : Command
{
public DDWebclientCommand()
{
Keyword = "webclient";
RequiredParameters = 1;
}
protected override ExitCode Run(IList<string> parameters)
{
var DD = Server.GetComponent<ITGDreamDaemon>();
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 : Command
{
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;
}
Server.GetComponent<ITGDreamDaemon>().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 : Command
{
public DDSecurityCommand()
{
Keyword = "set-security";
RequiredParameters = 1;
}
protected override ExitCode Run(IList<string> parameters)
{
TGDreamDaemonSecurity sec;
switch (parameters[0].ToLower())
{
case "safe":
sec = TGDreamDaemonSecurity.Safe;
break;
case "ultra":
case "ultrasafe":
sec = TGDreamDaemonSecurity.Ultrasafe;
break;
case "trust":
case "trusted":
sec = TGDreamDaemonSecurity.Trusted;
break;
default:
OutputProc("Invalid security word!");
return ExitCode.BadCommand;
}
Server.GetComponent<ITGDreamDaemon>().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";
}
}
}
using System;
using System.Collections.Generic;
using TGServiceInterface;
namespace TGCommandLine
{
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 : Command
{
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 = Server.GetComponent<ITGDreamDaemon>().WorldAnnounce(String.Join(" ", parameters));
OutputProc(res ?? "Success!");
return res == null ? ExitCode.Normal : ExitCode.ServerError;
}
}
class DDStartCommand : Command
{
public DDStartCommand()
{
Keyword = "start";
}
public override string GetHelpText()
{
return "Starts the server and watchdog";
}
protected override ExitCode Run(IList<string> parameters)
{
var res = Server.GetComponent<ITGDreamDaemon>().Start();
OutputProc(res ?? "Success!");
return res == null ? ExitCode.Normal : ExitCode.ServerError;
}
}
class DDStopCommand : Command
{
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 = Server.GetComponent<ITGDreamDaemon>();
if (parameters.Count > 0 && parameters[0].ToLower() == "--graceful")
{
if (DD.DaemonStatus() != TGDreamDaemonStatus.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 : Command
{
public DDRestartCommand()
{
Keyword = "restart";
}
public override string GetArgumentString()
{
return "[--graceful]";
}
protected override ExitCode Run(IList<string> parameters)
{
var DD = Server.GetComponent<ITGDreamDaemon>();
if (parameters.Count > 0 && parameters[0].ToLower() == "--graceful")
{
if (DD.DaemonStatus() != TGDreamDaemonStatus.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 : Command
{
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 = Server.GetComponent<ITGDreamDaemon>();
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 : Command
{
public DDAutostartCommand()
{
Keyword = "autostart";
RequiredParameters = 1;
}
protected override ExitCode Run(IList<string> parameters)
{
var DD = Server.GetComponent<ITGDreamDaemon>();
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 : Command
{
public DDWebclientCommand()
{
Keyword = "webclient";
RequiredParameters = 1;
}
protected override ExitCode Run(IList<string> parameters)
{
var DD = Server.GetComponent<ITGDreamDaemon>();
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 : Command
{
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;
}
Server.GetComponent<ITGDreamDaemon>().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 : Command
{
public DDSecurityCommand()
{
Keyword = "set-security";
RequiredParameters = 1;
}
protected override ExitCode Run(IList<string> parameters)
{
TGDreamDaemonSecurity sec;
switch (parameters[0].ToLower())
{
case "safe":
sec = TGDreamDaemonSecurity.Safe;
break;
case "ultra":
case "ultrasafe":
sec = TGDreamDaemonSecurity.Ultrasafe;
break;
case "trust":
case "trusted":
sec = TGDreamDaemonSecurity.Trusted;
break;
default:
OutputProc("Invalid security word!");
return ExitCode.BadCommand;
}
Server.GetComponent<ITGDreamDaemon>().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";
}
}
}
+220 -220
View File
@@ -1,220 +1,220 @@
using System;
using System.Collections.Generic;
using System.Linq;
using TGServiceInterface;
namespace TGCommandLine
{
class Program
{
static 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);
Server.SetRemoteLoginInformation(address, port, username, password);
break;
}
}
if (badConnectionString)
{
Console.WriteLine("Remote connection usage: <-c/--connect> username:password@address:port");
return ExitCode.BadCommand;
}
var res = Server.VerifyConnection();
if (res != null)
{
Console.WriteLine("Unable to connect to service: " + res);
Console.WriteLine("Remote connection usage: <-c/--connect> username:password@address:port");
return ExitCode.ConnectionError;
}
if (!Server.Authenticate())
{
Console.WriteLine("Authentication error: Username/password/windows identity is not authorized!");
return ExitCode.ConnectionError;
}
try
{
return new CLICommand().DoRun(argsAsList);
}
catch (Exception e)
{
Console.WriteLine("Error: " + e.ToString());
return ExitCode.ConnectionError;
};
}
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 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;
}
static int Main(string[] args)
{
Command.OutputProcVar.Value = Console.WriteLine;
if (args.Length != 0)
{
Server.SetBadCertificateHandler((message) => {
foreach (var I in args)
if (I.ToLower() == "--disable-ssl-verification") //im just not even going to document this because i hate it so much
return true;
return false;
});
//allow self signed certs in debug mode
return (int)RunCommandLine(new List<string>(args));
}
Server.SetBadCertificateHandler(BadCertificateInteractive);
Console.WriteLine("Type 'remote' to connect to a remote service");
//interactive mode
while (true)
{
Console.Write("Enter command: ");
var NextCommand = Console.ReadLine();
switch (NextCommand.ToLower())
{
case "remote":
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();
Server.SetRemoteLoginInformation(address, port, username, password);
var res = Server.VerifyConnection();
if (res != null)
{
Console.WriteLine("Unable to connect: " + res);
Server.MakeLocalConnection();
}
else if (!Server.Authenticate())
{
Console.WriteLine("Authentication error: Username/password/windows identity is not authorized! Returning to local mode...");
Server.MakeLocalConnection();
}
else
{
Console.WriteLine("Connected remotely");
Console.WriteLine("Type 'disconnect' to return to local mode");
}
break;
case "disconnect":
Server.MakeLocalConnection();
Console.WriteLine("Switch to local mode");
break;
case "quit":
case "exit":
return (int)ExitCode.Normal;
#if DEBUG
case "debug-upgrade":
Server.GetComponent<ITGSService>().PrepareForUpdate();
return (int)ExitCode.Normal;
#endif
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;
}
}
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using TGServiceInterface;
namespace TGCommandLine
{
class Program
{
static 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);
Server.SetRemoteLoginInformation(address, port, username, password);
break;
}
}
if (badConnectionString)
{
Console.WriteLine("Remote connection usage: <-c/--connect> username:password@address:port");
return ExitCode.BadCommand;
}
var res = Server.VerifyConnection();
if (res != null)
{
Console.WriteLine("Unable to connect to service: " + res);
Console.WriteLine("Remote connection usage: <-c/--connect> username:password@address:port");
return ExitCode.ConnectionError;
}
if (!Server.Authenticate())
{
Console.WriteLine("Authentication error: Username/password/windows identity is not authorized!");
return ExitCode.ConnectionError;
}
try
{
return new CLICommand().DoRun(argsAsList);
}
catch (Exception e)
{
Console.WriteLine("Error: " + e.ToString());
return ExitCode.ConnectionError;
};
}
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 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;
}
static int Main(string[] args)
{
Command.OutputProcVar.Value = Console.WriteLine;
if (args.Length != 0)
{
Server.SetBadCertificateHandler((message) => {
foreach (var I in args)
if (I.ToLower() == "--disable-ssl-verification") //im just not even going to document this because i hate it so much
return true;
return false;
});
//allow self signed certs in debug mode
return (int)RunCommandLine(new List<string>(args));
}
Server.SetBadCertificateHandler(BadCertificateInteractive);
Console.WriteLine("Type 'remote' to connect to a remote service");
//interactive mode
while (true)
{
Console.Write("Enter command: ");
var NextCommand = Console.ReadLine();
switch (NextCommand.ToLower())
{
case "remote":
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();
Server.SetRemoteLoginInformation(address, port, username, password);
var res = Server.VerifyConnection();
if (res != null)
{
Console.WriteLine("Unable to connect: " + res);
Server.MakeLocalConnection();
}
else if (!Server.Authenticate())
{
Console.WriteLine("Authentication error: Username/password/windows identity is not authorized! Returning to local mode...");
Server.MakeLocalConnection();
}
else
{
Console.WriteLine("Connected remotely");
Console.WriteLine("Type 'disconnect' to return to local mode");
}
break;
case "disconnect":
Server.MakeLocalConnection();
Console.WriteLine("Switch to local mode");
break;
case "quit":
case "exit":
return (int)ExitCode.Normal;
#if DEBUG
case "debug-upgrade":
Server.GetComponent<ITGSService>().PrepareForUpdate();
return (int)ExitCode.Normal;
#endif
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;
}
}
}
}
}
+45 -45
View File
@@ -1,45 +1,45 @@
using System;
using System.Drawing;
using System.Windows.Forms;
namespace TGControlPanel
{
public partial class Main : Form
{
public Main()
{
InitializeComponent();
Panels.SelectedIndexChanged += Panels_SelectedIndexChanged;
Panels.SelectedIndex += Math.Min(Properties.Settings.Default.LastPageIndex, Panels.TabCount - 1);
InitRepoPage();
InitBYONDPage();
InitServerPage();
LoadChatPage();
InitStaticPage();
}
private void Main_Resize(object sender, EventArgs e)
{
Panels.Location = new Point(10, 10);
Panels.Width = ClientSize.Width - 20;
Panels.Height = ClientSize.Height - 20;
}
private void Panels_SelectedIndexChanged(object sender, EventArgs e)
{
switch (Panels.SelectedIndex)
{
case 1: //byond
UpdateBYONDButtons();
break;
case 2: //scp
LoadServerPage();
break;
case 3: //chat
LoadChatPage();
break;
}
Properties.Settings.Default.LastPageIndex = Panels.SelectedIndex;
}
}
}
using System;
using System.Drawing;
using System.Windows.Forms;
namespace TGControlPanel
{
public partial class Main : Form
{
public Main()
{
InitializeComponent();
Panels.SelectedIndexChanged += Panels_SelectedIndexChanged;
Panels.SelectedIndex += Math.Min(Properties.Settings.Default.LastPageIndex, Panels.TabCount - 1);
InitRepoPage();
InitBYONDPage();
InitServerPage();
LoadChatPage();
InitStaticPage();
}
private void Main_Resize(object sender, EventArgs e)
{
Panels.Location = new Point(10, 10);
Panels.Width = ClientSize.Width - 20;
Panels.Height = ClientSize.Height - 20;
}
private void Panels_SelectedIndexChanged(object sender, EventArgs e)
{
switch (Panels.SelectedIndex)
{
case 1: //byond
UpdateBYONDButtons();
break;
case 2: //scp
LoadServerPage();
break;
case 3: //chat
LoadChatPage();
break;
}
Properties.Settings.Default.LastPageIndex = Panels.SelectedIndex;
}
}
}
+477 -474
View File
@@ -1,474 +1,477 @@
using System;
using System.ComponentModel;
using System.Windows.Forms;
using TGServiceInterface;
namespace TGControlPanel
{
partial class Main
{
enum FullUpdateAction
{
UpdateHard,
UpdateMerge,
UpdateHardTestmerge,
Reset,
Testmerge,
}
FullUpdateAction fuAction;
ushort testmergePR;
string updateError;
bool updatingFields = false;
void InitServerPage()
{
LoadServerPage();
if (!Server.AuthenticateAdmin())
{
ServerPathTextbox.Enabled = false;
ServerPathTextbox.ReadOnly = true;
}
FullUpdateWorker.RunWorkerCompleted += FullUpdateWorker_RunWorkerCompleted;
ServerPathTextbox.LostFocus += ServerPathTextbox_LostFocus;
ServerPathTextbox.KeyDown += ServerPathTextbox_KeyDown;
projectNameText.LostFocus += ProjectNameText_LostFocus;
projectNameText.KeyDown += ProjectNameText_KeyDown;
ServerStartBGW.RunWorkerCompleted += ServerStartBGW_RunWorkerCompleted;
}
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 ServerPathTextbox_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
UpdateServerPath();
}
private void FullUpdateWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (updateError != null)
MessageBox.Show(updateError);
UpdateHardButton.Enabled = true;
UpdateMergeButton.Enabled = true;
TestmergeButton.Enabled = true;
UpdateTestmergeButton.Enabled = true;
LoadServerPage();
}
private void CompileCancelButton_Click(object sender, EventArgs e)
{
var res = Server.GetComponent<ITGCompiler>().Cancel();
if (res != null)
MessageBox.Show(res);
LoadServerPage();
}
private void ServerPathTextbox_LostFocus(object sender, EventArgs e)
{
UpdateServerPath();
}
void UpdateServerPath()
{
if (!Program.CheckAdminWithWarning())
{
ServerPathTextbox.Enabled = false;
ServerPathTextbox.ReadOnly = true;
return;
}
if (updatingFields || ServerPathTextbox.Text.Trim() == Server.GetComponent<ITGConfig>().ServerDirectory())
return;
var DialogResult = MessageBox.Show("This will move the entire server installation.", "Confim", MessageBoxButtons.YesNo);
if (DialogResult != DialogResult.Yes)
return;
if (!Program.CheckAdminWithWarning())
{
ServerPathTextbox.Enabled = false;
ServerPathTextbox.ReadOnly = true;
return;
}
MessageBox.Show(Server.GetComponent<ITGAdministration>().MoveServer(ServerPathTextbox.Text) ?? "Success!");
}
void LoadServerPage()
{
var RepoExists = Server.GetComponent<ITGRepository>().Exists();
compileButton.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;
ServerPRLabel.Visible = RepoExists;
ServerGStopButton.Visible = RepoExists;
ServerStartButton.Visible = RepoExists;
ServerGRestartButton.Visible = RepoExists;
ServerRestartButton.Visible = RepoExists;
PortLabel.Visible = RepoExists;
ServerStopButton.Visible = RepoExists;
TestmergeButton.Visible = RepoExists;
ServerTestmergeInput.Visible = RepoExists;
UpdateHardButton.Visible = RepoExists;
UpdateMergeButton.Visible = RepoExists;
UpdateTestmergeButton.Visible = RepoExists;
ResetTestmerge.Visible = RepoExists;
WorldAnnounceField.Visible = RepoExists;
WorldAnnounceButton.Visible = RepoExists;
WorldAnnounceLabel.Visible = RepoExists;
var DM = Server.GetComponent<ITGCompiler>();
var DD = Server.GetComponent<ITGDreamDaemon>();
var Config = Server.GetComponent<ITGConfig>();
if (updatingFields)
return;
try
{
updatingFields = true;
if (!ServerPathTextbox.Focused)
ServerPathTextbox.Text = Config.ServerDirectory();
SecuritySelector.SelectedIndex = (int)DD.SecurityLevel();
if (!RepoExists)
return;
var DaeStat = DD.DaemonStatus();
var Online = DaeStat == TGDreamDaemonStatus.Online;
ServerStartButton.Enabled = !Online;
ServerGStopButton.Enabled = Online;
ServerGRestartButton.Enabled = Online;
ServerStopButton.Enabled = Online;
ServerRestartButton.Enabled = Online;
switch (DaeStat)
{
case TGDreamDaemonStatus.HardRebooting:
ServerStatusLabel.Text = "REBOOTING";
break;
case TGDreamDaemonStatus.Offline:
ServerStatusLabel.Text = "OFFLINE";
break;
case TGDreamDaemonStatus.Online:
ServerStatusLabel.Text = "ONLINE";
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();
switch (DM.GetStatus())
{
case TGCompilerStatus.Compiling:
CompilerStatusLabel.Text = "Compiling...";
compileButton.Enabled = false;
initializeButton.Enabled = false;
CompileCancelButton.Enabled = true;
break;
case TGCompilerStatus.Initializing:
CompilerStatusLabel.Text = "Initializing...";
compileButton.Enabled = false;
initializeButton.Enabled = false;
CompileCancelButton.Enabled = false;
break;
case TGCompilerStatus.Initialized:
CompilerStatusLabel.Text = "Idle";
initializeButton.Enabled = true;
compileButton.Enabled = true;
CompileCancelButton.Enabled = false;
break;
case TGCompilerStatus.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)
Server.GetComponent<ITGCompiler>().SetProjectName(projectNameText.Text);
}
private void PortSelector_ValueChanged(object sender, EventArgs e)
{
if (!updatingFields)
Server.GetComponent<ITGDreamDaemon>().SetPort((ushort)PortSelector.Value);
}
private void RunServerUpdate(FullUpdateAction fua, ushort tm = 0)
{
if (FullUpdateWorker.IsBusy)
return;
testmergePR = tm;
fuAction = fua;
initializeButton.Enabled = false;
compileButton.Enabled = false;
UpdateHardButton.Enabled = false;
UpdateMergeButton.Enabled = false;
TestmergeButton.Enabled = false;
UpdateTestmergeButton.Enabled = false;
switch (fuAction)
{
case FullUpdateAction.Testmerge:
CompilerStatusLabel.Text = String.Format("Testmerging pull request #{0}...", testmergePR);
break;
case FullUpdateAction.UpdateHard:
CompilerStatusLabel.Text = String.Format("Updating Server (RESET)...");
break;
case FullUpdateAction.UpdateMerge:
CompilerStatusLabel.Text = String.Format("Updating Server (MERGE)...");
break;
case FullUpdateAction.UpdateHardTestmerge:
CompilerStatusLabel.Text = String.Format("Updating and testmerging pull request #{0}...", testmergePR);
break;
}
FullUpdateWorker.RunWorkerAsync();
}
private void ServerPageRefreshButton_Click(object sender, EventArgs e)
{
LoadServerPage();
}
private void InitializeButton_Click(object sender, EventArgs e)
{
if (!Server.GetComponent<ITGCompiler>().Initialize())
MessageBox.Show("Unable to start initialization!");
LoadServerPage();
}
private void CompileButton_Click(object sender, EventArgs e)
{
if (!Server.GetComponent<ITGCompiler>().Compile())
MessageBox.Show("Unable to start compilation!");
LoadServerPage();
}
private void AutostartCheckbox_CheckedChanged(object sender, System.EventArgs e)
{
if (!updatingFields)
Server.GetComponent<ITGDreamDaemon>().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 = Server.GetComponent<ITGDreamDaemon>().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 = Server.GetComponent<ITGDreamDaemon>().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 = Server.GetComponent<ITGDreamDaemon>().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;
Server.GetComponent<ITGDreamDaemon>().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;
Server.GetComponent<ITGDreamDaemon>().RequestRestart();
}
private void FullUpdateWorker_DoWork(object sender, DoWorkEventArgs e)
{
try
{
var Repo = Server.GetComponent<ITGRepository>();
var DM = Server.GetComponent<ITGCompiler>();
switch (fuAction)
{
case FullUpdateAction.Testmerge:
updateError = Repo.MergePullRequest(testmergePR);
if (updateError == null)
{
Repo.GenerateChangelog(out updateError);
updateError = DM.Compile(true) ? updateError : "Compilation failed!";
}
break;
case FullUpdateAction.UpdateHard:
updateError = Repo.Update(true);
if (updateError == null)
{
Repo.GenerateChangelog(out updateError);
if (updateError == null)
updateError = Repo.PushChangelog();
updateError = DM.Compile(true) ? updateError : "Compilation failed!";
}
break;
case FullUpdateAction.UpdateHardTestmerge:
updateError = Repo.Update(true);
if (updateError == null)
{
Repo.GenerateChangelog(out updateError);
if (updateError == null)
updateError = Repo.PushChangelog();
updateError = Repo.MergePullRequest(testmergePR);
if (updateError == null)
{
Repo.GenerateChangelog(out updateError);
updateError = DM.Compile(true) ? updateError : "Compilation failed!";
}
}
break;
case FullUpdateAction.UpdateMerge:
updateError = Repo.Update(false);
if (updateError == null)
{
Repo.GenerateChangelog(out updateError);
if (updateError == null)
Repo.PushChangelog(); //not an error 99% of the time if this fails, just a dirty tree
updateError = DM.Compile(true) ? updateError : "Compilation failed!";
}
break;
case FullUpdateAction.Reset:
updateError = Repo.Reset(true);
if (updateError == null)
{
Repo.GenerateChangelog(out updateError);
updateError = DM.Compile(true) ? updateError : "Compilation failed!";
}
break;
}
}
catch (Exception ex)
{
Program.ServiceDisconnectException(ex);
}
}
private void ResetTestmerge_Click(object sender, EventArgs e)
{
RunServerUpdate(FullUpdateAction.Reset);
}
private void UpdateHardButton_Click(object sender, System.EventArgs e)
{
RunServerUpdate(FullUpdateAction.UpdateHard);
}
private void UpdateTestmergeButton_Click(object sender, System.EventArgs e)
{
RunServerUpdate(FullUpdateAction.UpdateHardTestmerge, (ushort)ServerTestmergeInput.Value);
}
private void UpdateMergeButton_Click(object sender, System.EventArgs e)
{
RunServerUpdate(FullUpdateAction.UpdateMerge);
}
private void TestmergeButton_Click(object sender, System.EventArgs e)
{
RunServerUpdate(FullUpdateAction.Testmerge, (ushort)ServerTestmergeInput.Value);
}
private void SecuritySelector_SelectedIndexChanged(object sender, EventArgs e)
{
if (!updatingFields)
if (!Server.GetComponent<ITGDreamDaemon>().SetSecurityLevel((TGDreamDaemonSecurity)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 = Server.GetComponent<ITGDreamDaemon>().WorldAnnounce(msg);
if (res != null)
{
MessageBox.Show(res);
return;
}
}
WorldAnnounceField.Text = "";
}
private void WebclientCheckBox_CheckedChanged(object sender, EventArgs e)
{
if (!updatingFields)
Server.GetComponent<ITGDreamDaemon>().SetWebclient(WebclientCheckBox.Checked);
}
}
}
using System;
using System.ComponentModel;
using System.Windows.Forms;
using TGServiceInterface;
namespace TGControlPanel
{
partial class Main
{
enum FullUpdateAction
{
UpdateHard,
UpdateMerge,
UpdateHardTestmerge,
Reset,
Testmerge,
}
FullUpdateAction fuAction;
ushort testmergePR;
string updateError;
bool updatingFields = false;
void InitServerPage()
{
LoadServerPage();
if (!Server.AuthenticateAdmin())
{
ServerPathTextbox.Enabled = false;
ServerPathTextbox.ReadOnly = true;
}
FullUpdateWorker.RunWorkerCompleted += FullUpdateWorker_RunWorkerCompleted;
ServerPathTextbox.LostFocus += ServerPathTextbox_LostFocus;
ServerPathTextbox.KeyDown += ServerPathTextbox_KeyDown;
projectNameText.LostFocus += ProjectNameText_LostFocus;
projectNameText.KeyDown += ProjectNameText_KeyDown;
ServerStartBGW.RunWorkerCompleted += ServerStartBGW_RunWorkerCompleted;
}
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 ServerPathTextbox_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
UpdateServerPath();
}
private void FullUpdateWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (updateError != null)
MessageBox.Show(updateError);
UpdateHardButton.Enabled = true;
UpdateMergeButton.Enabled = true;
TestmergeButton.Enabled = true;
UpdateTestmergeButton.Enabled = true;
LoadServerPage();
}
private void CompileCancelButton_Click(object sender, EventArgs e)
{
var res = Server.GetComponent<ITGCompiler>().Cancel();
if (res != null)
MessageBox.Show(res);
LoadServerPage();
}
private void ServerPathTextbox_LostFocus(object sender, EventArgs e)
{
UpdateServerPath();
}
void UpdateServerPath()
{
if (!Program.CheckAdminWithWarning())
{
ServerPathTextbox.Enabled = false;
ServerPathTextbox.ReadOnly = true;
return;
}
if (updatingFields || ServerPathTextbox.Text.Trim() == Server.GetComponent<ITGConfig>().ServerDirectory())
return;
var DialogResult = MessageBox.Show("This will move the entire server installation.", "Confim", MessageBoxButtons.YesNo);
if (DialogResult != DialogResult.Yes)
return;
if (!Program.CheckAdminWithWarning())
{
ServerPathTextbox.Enabled = false;
ServerPathTextbox.ReadOnly = true;
return;
}
MessageBox.Show(Server.GetComponent<ITGAdministration>().MoveServer(ServerPathTextbox.Text) ?? "Success!");
}
void LoadServerPage()
{
var RepoExists = Server.GetComponent<ITGRepository>().Exists();
compileButton.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;
ServerPRLabel.Visible = RepoExists;
ServerGStopButton.Visible = RepoExists;
ServerStartButton.Visible = RepoExists;
ServerGRestartButton.Visible = RepoExists;
ServerRestartButton.Visible = RepoExists;
PortLabel.Visible = RepoExists;
ServerStopButton.Visible = RepoExists;
TestmergeButton.Visible = RepoExists;
ServerTestmergeInput.Visible = RepoExists;
UpdateHardButton.Visible = RepoExists;
UpdateMergeButton.Visible = RepoExists;
UpdateTestmergeButton.Visible = RepoExists;
ResetTestmerge.Visible = RepoExists;
WorldAnnounceField.Visible = RepoExists;
WorldAnnounceButton.Visible = RepoExists;
WorldAnnounceLabel.Visible = RepoExists;
var DM = Server.GetComponent<ITGCompiler>();
var DD = Server.GetComponent<ITGDreamDaemon>();
var Config = Server.GetComponent<ITGConfig>();
if (updatingFields)
return;
try
{
updatingFields = true;
if (!ServerPathTextbox.Focused)
ServerPathTextbox.Text = Config.ServerDirectory();
SecuritySelector.SelectedIndex = (int)DD.SecurityLevel();
if (!RepoExists)
return;
var DaeStat = DD.DaemonStatus();
var Online = DaeStat == TGDreamDaemonStatus.Online;
ServerStartButton.Enabled = !Online;
ServerGStopButton.Enabled = Online;
ServerGRestartButton.Enabled = Online;
ServerStopButton.Enabled = Online;
ServerRestartButton.Enabled = Online;
switch (DaeStat)
{
case TGDreamDaemonStatus.HardRebooting:
ServerStatusLabel.Text = "REBOOTING";
break;
case TGDreamDaemonStatus.Offline:
ServerStatusLabel.Text = "OFFLINE";
break;
case TGDreamDaemonStatus.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();
switch (DM.GetStatus())
{
case TGCompilerStatus.Compiling:
CompilerStatusLabel.Text = "Compiling...";
compileButton.Enabled = false;
initializeButton.Enabled = false;
CompileCancelButton.Enabled = true;
break;
case TGCompilerStatus.Initializing:
CompilerStatusLabel.Text = "Initializing...";
compileButton.Enabled = false;
initializeButton.Enabled = false;
CompileCancelButton.Enabled = false;
break;
case TGCompilerStatus.Initialized:
CompilerStatusLabel.Text = "Idle";
initializeButton.Enabled = true;
compileButton.Enabled = true;
CompileCancelButton.Enabled = false;
break;
case TGCompilerStatus.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)
Server.GetComponent<ITGCompiler>().SetProjectName(projectNameText.Text);
}
private void PortSelector_ValueChanged(object sender, EventArgs e)
{
if (!updatingFields)
Server.GetComponent<ITGDreamDaemon>().SetPort((ushort)PortSelector.Value);
}
private void RunServerUpdate(FullUpdateAction fua, ushort tm = 0)
{
if (FullUpdateWorker.IsBusy)
return;
testmergePR = tm;
fuAction = fua;
initializeButton.Enabled = false;
compileButton.Enabled = false;
UpdateHardButton.Enabled = false;
UpdateMergeButton.Enabled = false;
TestmergeButton.Enabled = false;
UpdateTestmergeButton.Enabled = false;
switch (fuAction)
{
case FullUpdateAction.Testmerge:
CompilerStatusLabel.Text = String.Format("Testmerging pull request #{0}...", testmergePR);
break;
case FullUpdateAction.UpdateHard:
CompilerStatusLabel.Text = String.Format("Updating Server (RESET)...");
break;
case FullUpdateAction.UpdateMerge:
CompilerStatusLabel.Text = String.Format("Updating Server (MERGE)...");
break;
case FullUpdateAction.UpdateHardTestmerge:
CompilerStatusLabel.Text = String.Format("Updating and testmerging pull request #{0}...", testmergePR);
break;
}
FullUpdateWorker.RunWorkerAsync();
}
private void ServerPageRefreshButton_Click(object sender, EventArgs e)
{
LoadServerPage();
}
private void InitializeButton_Click(object sender, EventArgs e)
{
if (!Server.GetComponent<ITGCompiler>().Initialize())
MessageBox.Show("Unable to start initialization!");
LoadServerPage();
}
private void CompileButton_Click(object sender, EventArgs e)
{
if (!Server.GetComponent<ITGCompiler>().Compile())
MessageBox.Show("Unable to start compilation!");
LoadServerPage();
}
private void AutostartCheckbox_CheckedChanged(object sender, System.EventArgs e)
{
if (!updatingFields)
Server.GetComponent<ITGDreamDaemon>().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 = Server.GetComponent<ITGDreamDaemon>().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 = Server.GetComponent<ITGDreamDaemon>().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 = Server.GetComponent<ITGDreamDaemon>().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;
Server.GetComponent<ITGDreamDaemon>().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;
Server.GetComponent<ITGDreamDaemon>().RequestRestart();
}
private void FullUpdateWorker_DoWork(object sender, DoWorkEventArgs e)
{
try
{
var Repo = Server.GetComponent<ITGRepository>();
var DM = Server.GetComponent<ITGCompiler>();
switch (fuAction)
{
case FullUpdateAction.Testmerge:
updateError = Repo.MergePullRequest(testmergePR);
if (updateError == null)
{
Repo.GenerateChangelog(out updateError);
updateError = DM.Compile(true) ? updateError : "Compilation failed!";
}
break;
case FullUpdateAction.UpdateHard:
updateError = Repo.Update(true);
if (updateError == null)
{
Repo.GenerateChangelog(out updateError);
if (updateError == null)
updateError = Repo.PushChangelog();
updateError = DM.Compile(true) ? updateError : "Compilation failed!";
}
break;
case FullUpdateAction.UpdateHardTestmerge:
updateError = Repo.Update(true);
if (updateError == null)
{
Repo.GenerateChangelog(out updateError);
if (updateError == null)
updateError = Repo.PushChangelog();
updateError = Repo.MergePullRequest(testmergePR);
if (updateError == null)
{
Repo.GenerateChangelog(out updateError);
updateError = DM.Compile(true) ? updateError : "Compilation failed!";
}
}
break;
case FullUpdateAction.UpdateMerge:
updateError = Repo.Update(false);
if (updateError == null)
{
Repo.GenerateChangelog(out updateError);
if (updateError == null)
Repo.PushChangelog(); //not an error 99% of the time if this fails, just a dirty tree
updateError = DM.Compile(true) ? updateError : "Compilation failed!";
}
break;
case FullUpdateAction.Reset:
updateError = Repo.Reset(true);
if (updateError == null)
{
Repo.GenerateChangelog(out updateError);
updateError = DM.Compile(true) ? updateError : "Compilation failed!";
}
break;
}
}
catch (Exception ex)
{
Program.ServiceDisconnectException(ex);
}
}
private void ResetTestmerge_Click(object sender, EventArgs e)
{
RunServerUpdate(FullUpdateAction.Reset);
}
private void UpdateHardButton_Click(object sender, System.EventArgs e)
{
RunServerUpdate(FullUpdateAction.UpdateHard);
}
private void UpdateTestmergeButton_Click(object sender, System.EventArgs e)
{
RunServerUpdate(FullUpdateAction.UpdateHardTestmerge, (ushort)ServerTestmergeInput.Value);
}
private void UpdateMergeButton_Click(object sender, System.EventArgs e)
{
RunServerUpdate(FullUpdateAction.UpdateMerge);
}
private void TestmergeButton_Click(object sender, System.EventArgs e)
{
RunServerUpdate(FullUpdateAction.Testmerge, (ushort)ServerTestmergeInput.Value);
}
private void SecuritySelector_SelectedIndexChanged(object sender, EventArgs e)
{
if (!updatingFields)
if (!Server.GetComponent<ITGDreamDaemon>().SetSecurityLevel((TGDreamDaemonSecurity)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 = Server.GetComponent<ITGDreamDaemon>().WorldAnnounce(msg);
if (res != null)
{
MessageBox.Show(res);
return;
}
}
WorldAnnounceField.Text = "";
}
private void WebclientCheckBox_CheckedChanged(object sender, EventArgs e)
{
if (!updatingFields)
Server.GetComponent<ITGDreamDaemon>().SetWebclient(WebclientCheckBox.Checked);
}
}
}
+246 -233
View File
@@ -1,233 +1,246 @@
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Web.Script.Serialization;
using System.Web.Security;
using TGServiceInterface;
namespace TGServerService
{
//handles talking between the world and us
partial class TGStationServer : ITGInterop
{
object topicLock = new object();
const int CommsKeyLen = 64;
string serviceCommsKey; //regenerated every DD restart
//range of supported api versions
readonly Version MinAPIVersion = new Version("3.1.0.0");
readonly Version MaxAPIVersion = new Version("3.1.0.99");
Version GameAPIVersion;
//See code/modules/server_tools/server_tools.dm for command switch
const string SCHardReboot = "hard_reboot"; //requests that dreamdaemon restarts when the round ends
const string SCGracefulShutdown = "graceful_shutdown"; //requests that dreamdaemon stops when the round ends
const string SCWorldAnnounce = "world_announce"; //sends param 'message' to the world
const string SCListCustomCommands = "list_custom_commands"; //Get a list of commands supported by the server
const string SCAPICompat = "api_compat"; //Tells the server we understand each other
const string SRKillProcess = "killme";
const string SRIRCBroadcast = "irc";
const string SRIRCAdminChannelMessage = "send2irc";
const string SRWorldReboot = "worldreboot";
const string SRAPIVersion = "api_ver";
const string CCPHelpText = "help_text";
const string CCPAdminOnly = "admin_only";
const string CCPRequiredParameters = "required_parameters";
List<Command> ServerChatCommands;
void LoadServerChatCommands()
{
if (DaemonStatus() != TGDreamDaemonStatus.Online)
return;
var json = SendCommand(SCListCustomCommands);
if (String.IsNullOrWhiteSpace(json))
return;
List<Command> tmp = new List<Command>();
try
{
foreach(var I in new JavaScriptSerializer().Deserialize<IDictionary<string, IDictionary<string, object>>>(json))
tmp.Add(new ServerChatCommand(I.Key, (string)I.Value[CCPHelpText], ((int)I.Value[CCPAdminOnly]) == 1, (int)I.Value[CCPRequiredParameters]));
ServerChatCommands = tmp;
}
catch { }
}
//raw command string sent here via world.ExportService
void HandleCommand(string cmd)
{
var splits = new List<string>(cmd.Split(' '));
cmd = splits[0];
splits.RemoveAt(0);
bool APIValid;
lock (topicLock)
{
APIValid = CheckAPIVersionConstraints();
}
if (!APIValid && cmd != SRAPIVersion)
return; //SPEAK THE LANGUAGE!!!
switch (cmd)
{
case SRIRCBroadcast:
SendMessage("GAME: " + String.Join(" ", splits), ChatMessageType.GameInfo);
break;
case SRKillProcess:
KillMe();
break;
case SRIRCAdminChannelMessage:
SendMessage("RELAY: " + String.Join(" ", splits), ChatMessageType.AdminInfo);
break;
case SRWorldReboot:
TGServerService.WriteInfo("World Rebooted", TGServerService.EventID.WorldReboot);
ServerChatCommands = null;
ChatConnectivityCheck();
lock (CompilerLock)
{
if (UpdateStaged)
{
UpdateStaged = false;
lock (topicLock)
{
GameAPIVersion = null; //needs updating
}
TGServerService.WriteInfo("Staged update applied", TGServerService.EventID.ServerUpdateApplied);
}
}
break;
case SRAPIVersion:
lock (topicLock)
{
try
{
GameAPIVersion = new Version(splits[0]);
if (!CheckAPIVersionConstraints())
throw new Exception();
}
catch
{
TGServerService.WriteWarning(String.Format("API version of the game ({0}) is incompatible with the current supported API versions (Min: {1}. Max: {2}). Interop disabled.", splits.Count > 1 ? splits[1] : "NULL", MinAPIVersion, MaxAPIVersion), TGServerService.EventID.APIVersionMismatch);
GameAPIVersion = null;
break;
}
}
//This needs to be done asyncronously otherwise DD won't be able to process it, because it's waiting for THIS THREAD to return
ThreadPool.QueueUserWorkItem(_ => SendCommand(SCAPICompat));
break;
}
}
public string SendCommand(string cmd)
{
lock (watchdogLock)
{
if (currentStatus != TGDreamDaemonStatus.Online)
return "Error: Server Offline!";
return SendTopic(String.Format("serviceCommsKey={0};command={1}", serviceCommsKey, cmd), currentPort);
}
}
//requires topiclock
bool CheckAPIVersionConstraints()
{
return !(GameAPIVersion == null || GameAPIVersion < MinAPIVersion || GameAPIVersion > MaxAPIVersion);
}
//Fuckery to diddle byond with the right packet to accept our girth
string SendTopic(string topicdata, ushort port)
{
lock (topicLock) {
if (!CheckAPIVersionConstraints())
return "Incompatible API!";
using (var topicSender = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp) { SendTimeout = 5000, ReceiveTimeout = 5000 })
{
try
{
topicSender.Connect(IPAddress.Loopback, port);
StringBuilder stringPacket = new StringBuilder();
stringPacket.Append((char)'\x00', 8);
stringPacket.Append('?' + topicdata);
stringPacket.Append((char)'\x00');
string fullString = stringPacket.ToString();
var packet = Encoding.ASCII.GetBytes(fullString);
packet[1] = 0x83;
var FinalLength = packet.Length - 4;
if (FinalLength > UInt16.MaxValue)
return "Error: Topic too long";
var lengthBytes = BitConverter.GetBytes((ushort)FinalLength);
packet[2] = lengthBytes[1]; //fucking endianess
packet[3] = lengthBytes[0];
topicSender.Send(packet);
string returnedString = "NULL";
try
{
var returnedData = new byte[UInt16.MaxValue];
topicSender.Receive(returnedData);
var raw_string = Encoding.ASCII.GetString(returnedData).TrimEnd(new char[] { (char)0 }).Trim();
if (raw_string.Length > 6)
returnedString = raw_string.Substring(5, raw_string.Length - 5).Trim();
}
catch
{
returnedString = "Topic recieve error!";
}
finally
{
topicSender.Shutdown(SocketShutdown.Both);
}
return returnedString;
}
catch
{
return "Topic delivery failed!";
}
}
}
}
//Every time we make a new DD process we generate a new comms key for security
//It's in world.params['server_service']
void GenCommsKey()
{
var charsToRemove = new string[] { "!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "_", "-", "+", "=", "[", "{", "]", "}", ";", ":", "<", ">", "|", ".", "/", "?" };
serviceCommsKey = String.Empty;
do {
var tmp = Membership.GeneratePassword(CommsKeyLen, 0);
foreach (var c in charsToRemove)
tmp = tmp.Replace(c, String.Empty);
serviceCommsKey += tmp;
} while (serviceCommsKey.Length < CommsKeyLen);
serviceCommsKey = serviceCommsKey.Substring(0, CommsKeyLen);
TGServerService.WriteInfo("Service Comms Key set to: " + serviceCommsKey, TGServerService.EventID.CommsKeySet);
}
/// <inheritdoc />
public bool InteropMessage(string command)
{
try
{
HandleCommand(command);
return true;
}
catch(Exception e)
{
TGServerService.WriteWarning(String.Format("Handle command for \"{0}\" failed: {1}", command, e.ToString()), TGServerService.EventID.InteropCallException);
return false;
}
}
}
}
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Web.Script.Serialization;
using System.Web.Security;
using TGServiceInterface;
namespace TGServerService
{
//handles talking between the world and us
partial class TGStationServer : ITGInterop
{
object topicLock = new object();
const int CommsKeyLen = 64;
string serviceCommsKey; //regenerated every DD restart
//range of supported api versions
readonly Version MinAPIVersion = new Version("3.1.0.0");
readonly Version MaxAPIVersion = new Version("3.1.0.99");
Version GameAPIVersion;
//See code/modules/server_tools/server_tools.dm for command switch
const string SCHardReboot = "hard_reboot"; //requests that dreamdaemon restarts when the round ends
const string SCGracefulShutdown = "graceful_shutdown"; //requests that dreamdaemon stops when the round ends
const string SCWorldAnnounce = "world_announce"; //sends param 'message' to the world
const string SCListCustomCommands = "list_custom_commands"; //Get a list of commands supported by the server
const string SCAPICompat = "api_compat"; //Tells the server we understand each other
const string SCPlayerCount = "client_count"; //Gets the number of connected clients
const string SRKillProcess = "killme";
const string SRIRCBroadcast = "irc";
const string SRIRCAdminChannelMessage = "send2irc";
const string SRWorldReboot = "worldreboot";
const string SRAPIVersion = "api_ver";
const string CCPHelpText = "help_text";
const string CCPAdminOnly = "admin_only";
const string CCPRequiredParameters = "required_parameters";
List<Command> ServerChatCommands;
void LoadServerChatCommands()
{
if (DaemonStatus() != TGDreamDaemonStatus.Online)
return;
var json = SendCommand(SCListCustomCommands);
if (String.IsNullOrWhiteSpace(json))
return;
List<Command> tmp = new List<Command>();
try
{
foreach(var I in new JavaScriptSerializer().Deserialize<IDictionary<string, IDictionary<string, object>>>(json))
tmp.Add(new ServerChatCommand(I.Key, (string)I.Value[CCPHelpText], ((int)I.Value[CCPAdminOnly]) == 1, (int)I.Value[CCPRequiredParameters]));
ServerChatCommands = tmp;
}
catch { }
}
//raw command string sent here via world.ExportService
void HandleCommand(string cmd)
{
var splits = new List<string>(cmd.Split(' '));
cmd = splits[0];
splits.RemoveAt(0);
bool APIValid;
lock (topicLock)
{
APIValid = CheckAPIVersionConstraints();
}
if (!APIValid && cmd != SRAPIVersion)
return; //SPEAK THE LANGUAGE!!!
switch (cmd)
{
case SRIRCBroadcast:
SendMessage("GAME: " + String.Join(" ", splits), ChatMessageType.GameInfo);
break;
case SRKillProcess:
KillMe();
break;
case SRIRCAdminChannelMessage:
SendMessage("RELAY: " + String.Join(" ", splits), ChatMessageType.AdminInfo);
break;
case SRWorldReboot:
TGServerService.WriteInfo("World Rebooted", TGServerService.EventID.WorldReboot);
ServerChatCommands = null;
ChatConnectivityCheck();
lock (CompilerLock)
{
if (UpdateStaged)
{
UpdateStaged = false;
lock (topicLock)
{
GameAPIVersion = null; //needs updating
}
TGServerService.WriteInfo("Staged update applied", TGServerService.EventID.ServerUpdateApplied);
}
}
break;
case SRAPIVersion:
lock (topicLock)
{
try
{
GameAPIVersion = new Version(splits[0]);
if (!CheckAPIVersionConstraints())
throw new Exception();
}
catch
{
TGServerService.WriteWarning(String.Format("API version of the game ({0}) is incompatible with the current supported API versions (Min: {1}. Max: {2}). Interop disabled.", splits.Count > 1 ? splits[1] : "NULL", MinAPIVersion, MaxAPIVersion), TGServerService.EventID.APIVersionMismatch);
GameAPIVersion = null;
break;
}
}
//This needs to be done asyncronously otherwise DD won't be able to process it, because it's waiting for THIS THREAD to return
ThreadPool.QueueUserWorkItem(_ => SendCommand(SCAPICompat));
break;
}
}
public string SendCommand(string cmd)
{
lock (watchdogLock)
{
if (currentStatus != TGDreamDaemonStatus.Online)
return "Error: Server Offline!";
return SendTopic(String.Format("serviceCommsKey={0};command={1}", serviceCommsKey, cmd), currentPort);
}
}
public int PlayerCount()
{
try
{
return Convert.ToInt32(SendCommand(SCPlayerCount));
}
catch
{
return -1;
}
}
//requires topiclock
bool CheckAPIVersionConstraints()
{
return !(GameAPIVersion == null || GameAPIVersion < MinAPIVersion || GameAPIVersion > MaxAPIVersion);
}
//Fuckery to diddle byond with the right packet to accept our girth
string SendTopic(string topicdata, ushort port)
{
lock (topicLock) {
if (!CheckAPIVersionConstraints())
return "Incompatible API!";
using (var topicSender = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp) { SendTimeout = 5000, ReceiveTimeout = 5000 })
{
try
{
topicSender.Connect(IPAddress.Loopback, port);
StringBuilder stringPacket = new StringBuilder();
stringPacket.Append((char)'\x00', 8);
stringPacket.Append('?' + topicdata);
stringPacket.Append((char)'\x00');
string fullString = stringPacket.ToString();
var packet = Encoding.ASCII.GetBytes(fullString);
packet[1] = 0x83;
var FinalLength = packet.Length - 4;
if (FinalLength > UInt16.MaxValue)
return "Error: Topic too long";
var lengthBytes = BitConverter.GetBytes((ushort)FinalLength);
packet[2] = lengthBytes[1]; //fucking endianess
packet[3] = lengthBytes[0];
topicSender.Send(packet);
string returnedString = "NULL";
try
{
var returnedData = new byte[UInt16.MaxValue];
topicSender.Receive(returnedData);
var raw_string = Encoding.ASCII.GetString(returnedData).TrimEnd(new char[] { (char)0 }).Trim();
if (raw_string.Length > 6)
returnedString = raw_string.Substring(5, raw_string.Length - 5).Trim();
}
catch
{
returnedString = "Topic recieve error!";
}
finally
{
topicSender.Shutdown(SocketShutdown.Both);
}
return returnedString;
}
catch
{
return "Topic delivery failed!";
}
}
}
}
//Every time we make a new DD process we generate a new comms key for security
//It's in world.params['server_service']
void GenCommsKey()
{
var charsToRemove = new string[] { "!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "_", "-", "+", "=", "[", "{", "]", "}", ";", ":", "<", ">", "|", ".", "/", "?" };
serviceCommsKey = String.Empty;
do {
var tmp = Membership.GeneratePassword(CommsKeyLen, 0);
foreach (var c in charsToRemove)
tmp = tmp.Replace(c, String.Empty);
serviceCommsKey += tmp;
} while (serviceCommsKey.Length < CommsKeyLen);
serviceCommsKey = serviceCommsKey.Substring(0, CommsKeyLen);
TGServerService.WriteInfo("Service Comms Key set to: " + serviceCommsKey, TGServerService.EventID.CommsKeySet);
}
/// <inheritdoc />
public bool InteropMessage(string command)
{
try
{
HandleCommand(command);
return true;
}
catch(Exception e)
{
TGServerService.WriteWarning(String.Format("Handle command for \"{0}\" failed: {1}", command, e.ToString()), TGServerService.EventID.InteropCallException);
return false;
}
}
}
}
+189 -182
View File
@@ -1,182 +1,189 @@
using System.ServiceModel;
namespace TGServiceInterface
{
/// <summary>
/// The status of the DD instance
/// </summary>
public enum TGDreamDaemonStatus
{
/// <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 TGDreamDaemonSecurity
{
/// <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
}
/// <summary>
/// Interface for managing the actual BYOND game server
/// </summary>
[ServiceContract]
public interface ITGDreamDaemon
{
/// <summary>
/// Gets the status of DreamDaemon
/// </summary>
/// <returns>The appropriate TGDreamDaemonStatus</returns>
[OperationContract]
TGDreamDaemonStatus DaemonStatus();
/// <summary>
/// Returns a human readable string of the current server status
/// </summary>
/// <param name="includeMetaInfo">If true, the status will include the server's current visibility and security levels</param>
/// <returns>A human readable string of the current server status</returns>
[OperationContract]
string StatusString(bool includeMetaInfo);
/// <summary>
/// Check if a call to Start will fail
/// Of course, be aware of race conditions with other control panels
/// </summary>
/// <returns>returns the error that would occur, null otherwise</returns>
[OperationContract]
string CanStart();
/// <summary>
/// Starts the server if it isn't running
/// </summary>
/// <returns>null on success or error message on failure</returns>
[OperationContract]
string Start();
/// <summary>
/// Immediately kills the server
/// </summary>
/// <returns>null on success or error message on failure</returns>
[OperationContract]
string Stop();
/// <summary>
/// Immediately kills and restarts the server
/// </summary>
/// <returns>null on success or error message on failure</returns>
[OperationContract]
string Restart();
/// <summary>
/// Restart the server after the currently running round ends
/// Has no effect if the server isn't running
/// </summary>
[OperationContract]
void RequestRestart();
/// <summary>
/// Stop the server after the currently running round ends
/// Has no effect if the server isn't running
/// </summary>
[OperationContract]
void RequestStop();
/// <summary>
/// Get the configured (not running) security level
/// </summary>
/// <returns>The configured (not running) security level</returns>
[OperationContract]
TGDreamDaemonSecurity SecurityLevel();
/// <summary>
/// Sets the security level of the server. Requires reboot to apply
/// Implies a call to RequestRestart()
/// note that anything higher than Trusted will disable interop from DD
/// </summary>
/// <param name="level">The new security level</param>
/// <returns>True if the change was immediately applied, false if a graceful restart was queued</returns>
[OperationContract]
bool SetSecurityLevel(TGDreamDaemonSecurity 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
/// Implies a call to 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>true if autostart is enabled, false otherwise</returns>
[OperationContract]
bool Autostart();
/// <summary>
/// Set the autostart config
/// </summary>
/// <param name="on">true to start the watchdog with the service, false otherwise</param>
[OperationContract]
void SetAutostart(bool on);
/// <summary>
/// Check if the byond webclient is currently enabled for the server
/// </summary>
/// <returns>true if the webclient is enabled, false otherwise</returns>
[OperationContract]
bool Webclient();
/// <summary>
/// Set the webclient config. Calls <see cref="RequestRestart"/>
/// </summary>
/// <param name="on">true to enable the byond webclient for the server, false otherwise</param>
[OperationContract]
void SetWebclient(bool on);
/// <summary>
/// Checks if a server stop has bee requested
/// </summary>
/// <returns>true if RequestStop has been called since the last server start, 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>null on success, error message on failure</returns>
[OperationContract]
string WorldAnnounce(string msg);
}
}
using System.ServiceModel;
namespace TGServiceInterface
{
/// <summary>
/// The status of the DD instance
/// </summary>
public enum TGDreamDaemonStatus
{
/// <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 TGDreamDaemonSecurity
{
/// <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
}
/// <summary>
/// Interface for managing the actual BYOND game server
/// </summary>
[ServiceContract]
public interface ITGDreamDaemon
{
/// <summary>
/// Gets the status of DreamDaemon
/// </summary>
/// <returns>The appropriate TGDreamDaemonStatus</returns>
[OperationContract]
TGDreamDaemonStatus DaemonStatus();
/// <summary>
/// Returns a human readable string of the current server status
/// </summary>
/// <param name="includeMetaInfo">If true, the status will include the server's current visibility and security levels</param>
/// <returns>A human readable string of the current server status</returns>
[OperationContract]
string StatusString(bool includeMetaInfo);
/// <summary>
/// Check if a call to Start will fail
/// Of course, be aware of race conditions with other control panels
/// </summary>
/// <returns>returns the error that would occur, null otherwise</returns>
[OperationContract]
string CanStart();
/// <summary>
/// Starts the server if it isn't running
/// </summary>
/// <returns>null on success or error message on failure</returns>
[OperationContract]
string Start();
/// <summary>
/// Immediately kills the server
/// </summary>
/// <returns>null on success or error message on failure</returns>
[OperationContract]
string Stop();
/// <summary>
/// Immediately kills and restarts the server
/// </summary>
/// <returns>null on success or error message on failure</returns>
[OperationContract]
string Restart();
/// <summary>
/// Restart the server after the currently running round ends
/// Has no effect if the server isn't running
/// </summary>
[OperationContract]
void RequestRestart();
/// <summary>
/// Stop the server after the currently running round ends
/// Has no effect if the server isn't running
/// </summary>
[OperationContract]
void RequestStop();
/// <summary>
/// Get the configured (not running) security level
/// </summary>
/// <returns>The configured (not running) security level</returns>
[OperationContract]
TGDreamDaemonSecurity SecurityLevel();
/// <summary>
/// Sets the security level of the server. Requires reboot to apply
/// Implies a call to RequestRestart()
/// note that anything higher than Trusted will disable interop from DD
/// </summary>
/// <param name="level">The new security level</param>
/// <returns>True if the change was immediately applied, false if a graceful restart was queued</returns>
[OperationContract]
bool SetSecurityLevel(TGDreamDaemonSecurity 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
/// Implies a call to 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>true if autostart is enabled, false otherwise</returns>
[OperationContract]
bool Autostart();
/// <summary>
/// Set the autostart config
/// </summary>
/// <param name="on">true to start the watchdog with the service, false otherwise</param>
[OperationContract]
void SetAutostart(bool on);
/// <summary>
/// Check if the byond webclient is currently enabled for the server
/// </summary>
/// <returns>true if the webclient is enabled, false otherwise</returns>
[OperationContract]
bool Webclient();
/// <summary>
/// Set the webclient config. Calls <see cref="RequestRestart"/>
/// </summary>
/// <param name="on">true to enable the byond webclient for the server, false otherwise</param>
[OperationContract]
void SetWebclient(bool on);
/// <summary>
/// Checks if a server stop has bee requested
/// </summary>
/// <returns>true if RequestStop has been called since the last server start, 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>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();
}
}
+10
View File
@@ -34,3 +34,13 @@
SERVER_TOOLS_CHAT_BROADCAST(checks_complete)
SERVER_TOOLS_RELAY_BROADCAST(checks_complete)
world.Reboot()
var/list/clients = list()
/client/New()
clients += src
return ..()
/client/Del()
clients -= src
return ..()