diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000000..e3cd01e65a --- /dev/null +++ b/.travis.yml @@ -0,0 +1,17 @@ +language: generic +sudo: false + +env: + global: + - BYOND_MAJOR="511" + - BYOND_MINOR="1385" + +cache: + directories: + - $HOME/BYOND-${BYOND_MAJOR}.${BYOND_MINOR} + +install: + - install_byond.sh + +build: + - build_byond.sh diff --git a/DMAPI/server_tools.dm b/DMAPI/server_tools.dm index a25ac1cec6..4c4c27583f 100644 --- a/DMAPI/server_tools.dm +++ b/DMAPI/server_tools.dm @@ -1,12 +1,13 @@ // /tg/station 13 server tools API v3.1 //CONFIGURATION -//Comment this out once you've filled the below +//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) @@ -22,7 +23,7 @@ //Required hooks: //Put this somewhere in /world/New() that is always run -#define SERVER_TOOLS_ON_NEW ListServiceCustomCommands(TRUE) +#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) @@ -30,12 +31,16 @@ //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 other function in this list will succeed if this is FALSE -#define SERVER_TOOLS_PRESENT (world.RunningService() != null) -//Gets the current version of the server tools, only supported in versions >= 3.0.91.0 -#define SERVER_TOOLS_VERSION world.ServiceVersion() +//No function below this succeed if this is FALSE +#define SERVER_TOOLS_PRESENT (RunningService() != null) +//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) this will release any open .dll handles, purge all memory leaks, and 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 @@ -54,7 +59,7 @@ //IMPLEMENTATION -#define SERVICE_API_VERSION "3.1.0.0" +#define SERVICE_API_VERSION_STRING "3.1.0.0" #define REBOOT_MODE_NORMAL 0 #define REBOOT_MODE_HARD 1 @@ -70,12 +75,12 @@ #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_VERSION "api_ver" #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" @@ -85,6 +90,7 @@ #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 diff --git a/DMAPI/st_interface.dm b/DMAPI/st_interface.dm index c21fe28fdc..570798eead 100644 --- a/DMAPI/st_interface.dm +++ b/DMAPI/st_interface.dm @@ -1,21 +1,35 @@ SERVER_TOOLS_DEFINE_AND_SET_GLOBAL(reboot_mode, REBOOT_MODE_NORMAL) +SERVER_TOOLS_DEFINE_AND_SET_GLOBAL(server_tools_api_compatible, FALSE) /proc/GetTestMerges() - if(world.RunningService() && fexists(SERVICE_PR_TEST_JSON)) + if(RunningService() && fexists(SERVICE_PR_TEST_JSON)) . = json_decode(file2text(SERVICE_PR_TEST_JSON)) if(.) return return list() -/world/proc/RunningService() - return params[SERVICE_WORLD_PARAM] +/world/proc/ServiceInit() + if(!RunningService(TRUE)) + return + ListCustomCommands() + ExportService("[SERVICE_REQUEST_API_VERSION] [SERVICE_API_VERSION]", TRUE) + +/proc/RunningService(skip_compat_check = FALSE) + return (skip_compat_check || SERVER_TOOLS_READ_GLOBAL(server_tools_api_compatible)) && world.params[SERVICE_WORLD_PARAM] /proc/ServiceVersion() - if(world.RunningService()) + if(RunningService(TRUE)) return world.params[SERVICE_VERSION_PARAM] -/world/proc/ExportService(command) +/proc/ServiceAPIVersion() + return SERVICE_API_VERSION_STRING + +/world/proc/ExportService(command, skip_compat_check = FALSE) . = FALSE + if(!RunningService(skip_compat_check)) + return + if(skip_compat_check && !fexists(SERVICE_INTERFACE_DLL)) + CRASH("Service parameter present but no interface DLL detected. This is symptomatic of running a service less than version 3.1! Please upgrade.") call(SERVICE_INTERFACE_DLL, SERVICE_INTERFACE_FUNCTION)(command) //trust no retval return TRUE @@ -57,6 +71,9 @@ SERVER_TOOLS_DEFINE_AND_SET_GLOBAL(reboot_mode, REBOOT_MODE_NORMAL) return "No command!" switch(command) + if(SERVICE_CMD_API_COMPATIBLE) + SERVER_TOOLS_WRITE_GLOBAL(server_tools_api_compatible, TRUE) + return "SUCCESS" if(SERVICE_CMD_HARD_REBOOT) if(SERVER_TOOLS_READ_GLOBAL(reboot_mode) != REBOOT_MODE_HARD) SERVER_TOOLS_WRITE_GLOBAL(reboot_mode, REBOOT_MODE_HARD) @@ -73,8 +90,6 @@ SERVER_TOOLS_DEFINE_AND_SET_GLOBAL(reboot_mode, REBOOT_MODE_NORMAL) return "No message set!" SERVER_TOOLS_WORLD_ANNOUNCE(msg) return "SUCCESS" - if(SERVICE_CMD_API_VERSION) - return SERVICE_API_VERSION if(SERVICE_CMD_LIST_CUSTOM) return json_encode(ListServiceCustomCommands(FALSE)) else diff --git a/DMAPITravisTester.dm b/DMAPITravisTester.dm new file mode 100644 index 0000000000..fa7df9f483 --- /dev/null +++ b/DMAPITravisTester.dm @@ -0,0 +1,39 @@ +//Fill out a dummy API +#define SERVER_TOOLS_DEFINE_AND_SET_GLOBAL(Name, Value) var/##Name = ##Value +#define SERVER_TOOLS_READ_GLOBAL(Name) global.##Name +#define SERVER_TOOLS_WRITE_GLOBAL(Name, Value) global.##Name = ##Value +#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) + +/world/New() + SERVER_TOOLS_ON_NEW + world.log << "Service API Version: [SERVER_TOOLS_API_VERSION]" + WaitForAPICompatResponse() + +/world/Topic(T, Addr, Master, Keys) + SERVER_TOOLS_ON_TOPIC + +/world/Reboot(reason) + SERVER_TOOLS_ON_REBOOT + SERVER_TOOLS_REBOOT_BYOND + +/proc/message_admins(event) + world << event + world.log << event + +/proc/WaitForAPICompatResponse() + set waitfor = FALSE + sleep(50) + if(!SERVER_TOOLS_PRESENT) + world.log << "No running service detected" + return + world.log << "Running service version: [SERVER_TOOLS_VERSION]" + var/list/prs = SERVER_TOOLS_PR_LIST + for(var/I in prs) + var/data = prs[I] + world.log << "Testmerge: #[I]: [data["title"]] by [data["author"]] at commit [data["commit"]]" + var/checks_complete = "Server tools API checks complete! Rebooting..." + SERVER_TOOLS_CHAT_BROADCAST(checks_complete) + SERVER_TOOLS_RELAY_BROADCAST(checks_complete) + world.Reboot() diff --git a/DMAPITravisTester.dme b/DMAPITravisTester.dme new file mode 100644 index 0000000000..721607b654 --- /dev/null +++ b/DMAPITravisTester.dme @@ -0,0 +1,20 @@ +// DM Environment file for DMAPITravisTester.dme. +// All manual changes should be made outside the BEGIN_ and END_ blocks. +// New source code should be placed in .dm files: choose File/New --> Code File. + +// BEGIN_INTERNALS +// END_INTERNALS + +// BEGIN_FILE_DIR +#define FILE_DIR . +// END_FILE_DIR + +// BEGIN_PREFERENCES +// END_PREFERENCES + +// BEGIN_INCLUDE +#include "DMAPITravisTester.dm" +#include "DMAPI\server_tools.dm" +#include "DMAPI\st_commands.dm" +#include "DMAPI\st_interface.dm" +// END_INCLUDE diff --git a/TGServerService/Interop.cs b/TGServerService/Interop.cs index 8e0949391a..5cf968e842 100644 --- a/TGServerService/Interop.cs +++ b/TGServerService/Interop.cs @@ -3,7 +3,6 @@ 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; @@ -20,19 +19,20 @@ namespace TGServerService //range of supported api versions const string MinAPIVersion = "3.1.0.0"; - const string MaxAPIVersion = "3.1.0.0"; + const string MaxAPIVersion = "3.1.0.99"; //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 SCGetAPIVer = "api_ver"; //Returns the server's API version + 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"; @@ -88,6 +88,21 @@ namespace TGServerService } } break; + case SRAPIVersion: + try + { + var theirs = new Version(splits[1]); + var minimum = new Version(MinAPIVersion); + var maximum = new Version(MaxAPIVersion); + if (theirs < minimum || theirs > maximum) + throw new Exception(); + SendCommand(SCAPICompat); + } + catch + { + TGServerService.WriteWarning("API version of the game ({0}) is incompatible with the current supported API versions (Min: {1}. Max: {2})", TGServerService.EventID.APIVersionMismatch); + } + break; } } diff --git a/TGServerService/ServerService.cs b/TGServerService/ServerService.cs index d32c8bbb5a..3241c4c295 100644 --- a/TGServerService/ServerService.cs +++ b/TGServerService/ServerService.cs @@ -79,6 +79,7 @@ namespace TGServerService PreactionEvent = 6800, PreactionFail = 6900, InteropCallException = 7000, + APIVersionMismatch = 7100, } static TGServerService ActiveService; //So everyone else can write to our eventlog diff --git a/build_byond.sh b/build_byond.sh new file mode 100755 index 0000000000..392ff04378 --- /dev/null +++ b/build_byond.sh @@ -0,0 +1,18 @@ +#!/bin/bash + +#nb: must be bash to support shopt globstar +set -e +source $HOME/BYOND-${BYOND_MAJOR}.${BYOND_MINOR}/byond/bin/byondsetup +if hash DreamMaker 2>/dev/null +then + DreamMaker $dmepath.mdme 2>&1 | tee result.log + retval=$? + if ! grep '\- 0 errors, 0 warnings' result.log + then + retval=1 #hard fail, due to warnings or errors + fi +else + echo "Couldn't find the DreamMaker executable, aborting." + retval=2 +fi +exit $retval \ No newline at end of file diff --git a/install_byond.sh b/install_byond.sh new file mode 100755 index 0000000000..b2da430247 --- /dev/null +++ b/install_byond.sh @@ -0,0 +1,21 @@ +#!/bin/bash +set -e + +if [ -d "$HOME/BYOND-${BYOND_MAJOR}.${BYOND_MINOR}/byond/bin" ]; +then + echo "Using cached directory." + exit 0 +else + echo "Setting up BYOND." + mkdir -p "$HOME/BYOND-${BYOND_MAJOR}.${BYOND_MINOR}" + cd "$HOME/BYOND-${BYOND_MAJOR}.${BYOND_MINOR}" + curl "http://www.byond.com/download/build/${BYOND_MAJOR}/${BYOND_MAJOR}.${BYOND_MINOR}_byond_linux.zip" -o byond.zip + unzip byond.zip + cd byond + make here + cd ~/ + exit 0 +fi + +#some variable not set correctly, panic +exit 1