diff --git a/DLLSocket/DLLSocket.cbp b/DLLSocket/DLLSocket.cbp
new file mode 100644
index 00000000000..6415a5ebd67
--- /dev/null
+++ b/DLLSocket/DLLSocket.cbp
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/DLLSocket/compile.sh b/DLLSocket/compile.sh
new file mode 100755
index 00000000000..0ff309bdb9f
--- /dev/null
+++ b/DLLSocket/compile.sh
@@ -0,0 +1 @@
+g++ -static -shared -O3 -fPIC main.cpp -o DLLSocket.so
diff --git a/DLLSocket/main.cpp b/DLLSocket/main.cpp
new file mode 100644
index 00000000000..4516de19306
--- /dev/null
+++ b/DLLSocket/main.cpp
@@ -0,0 +1,133 @@
+// OS-specific networking includes
+// -------------------------------
+#ifdef __WIN32
+ #include
+ typedef int socklen_t;
+#else
+ extern "C" {
+ #include
+ #include
+ #include
+ #include
+ #include
+ #include
+ #include
+ #include
+ }
+
+ typedef int SOCKET;
+ typedef sockaddr_in SOCKADDR_IN;
+ typedef sockaddr SOCKADDR;
+ #define SOCKET_ERROR -1
+#endif
+
+// Socket used for all communications
+SOCKET sock;
+
+// Address of the remote server
+SOCKADDR_IN addr;
+
+// Buffer used to return dynamic strings to the caller
+#define BUFFER_SIZE 1024
+char return_buffer[BUFFER_SIZE];
+
+// exposed functions
+// ------------------------------
+
+const char* SUCCESS = "1\0"; // string representing success
+
+#ifdef __WIN32
+ #define DLL_EXPORT __declspec(dllexport)
+#else
+ #define DLL_EXPORT __attribute__ ((visibility ("default")))
+#endif
+
+// arg1: ip(in the xx.xx.xx.xx format)
+// arg2: port(a short)
+// return: NULL on failure, SUCCESS otherwise
+extern "C" DLL_EXPORT const char* establish_connection(int n, char *v[])
+{
+ // extract args
+ // ------------
+ if(n < 2) return 0;
+ const char* ip = v[0];
+ const char* port_s = v[1];
+ unsigned short port = atoi(port_s);
+
+ // set up network stuff
+ // --------------------
+ #ifdef __WIN32
+ WSADATA wsa;
+ WSAStartup(MAKEWORD(2,0),&wsa);
+ #endif
+ sock = socket(AF_INET,SOCK_DGRAM,0);
+
+ // make the socket non-blocking
+ // ----------------------------
+ #ifdef __WIN32
+ unsigned long iMode=1;
+ ioctlsocket(sock,FIONBIO,&iMode);
+ #else
+ fcntl(sock, F_SETFL, O_NONBLOCK);
+ #endif
+
+ // establish a connection to the server
+ // ------------------------------------
+ memset(&addr,0,sizeof(SOCKADDR_IN));
+ addr.sin_family=AF_INET;
+ addr.sin_port=htons(port);
+
+ // convert the string representation of the ip to a byte representation
+ addr.sin_addr.s_addr=inet_addr(ip);
+
+ return SUCCESS;
+}
+
+// arg1: string message to send
+// return: NULL on failure, SUCCESS otherwise
+extern "C" DLL_EXPORT const char* send_message(int n, char *v[])
+{
+ // extract the args
+ if(n < 1) return 0;
+ const char* msg = v[0];
+
+ // send the message
+ int rc = sendto(sock,msg,strlen(msg),0,(SOCKADDR*)&addr,sizeof(SOCKADDR));
+
+ // check for errors
+ if (rc != -1) {
+ return SUCCESS;
+ }
+ else {
+ return 0;
+ }
+}
+
+// no args
+// return: message if any received, NULL otherwise
+extern "C" DLL_EXPORT const char* recv_message(int n, char *v[])
+{
+ SOCKADDR_IN sender; // we will store the sender address here
+
+ socklen_t sender_byte_length = sizeof(sender);
+
+ // Try receiving messages until we receive one that's valid, or there are no more messages
+ while(1) {
+ int rc = recvfrom(sock, return_buffer, BUFFER_SIZE,0,(SOCKADDR*) &sender,&sender_byte_length);
+ if(rc > 0) {
+ // we could read something
+
+ if(sender.sin_addr.s_addr != addr.sin_addr.s_addr) {
+ continue; // not our connection, ignore and try again
+ } else {
+ return_buffer[rc] = '0'; // 0-terminate the string
+ return return_buffer;
+ }
+ }
+ else {
+ break; // no more messages, stop trying to receive
+ }
+ }
+
+ return 0;
+}
diff --git a/DLLSocket/server_controller.py b/DLLSocket/server_controller.py
new file mode 100644
index 00000000000..89303755cc9
--- /dev/null
+++ b/DLLSocket/server_controller.py
@@ -0,0 +1,43 @@
+import subprocess
+import socket
+import urlparse
+
+UDP_IP="127.0.0.1"
+UDP_PORT=8019
+
+sock = socket.socket( socket.AF_INET, # Internet
+ socket.SOCK_DGRAM ) # UDP
+sock.bind( (UDP_IP,UDP_PORT) )
+
+last_ticker_state = None
+
+def handle_message(data, addr):
+ global last_ticker_state
+
+ params = urlparse.parse_qs(data)
+ print(data)
+
+ try:
+ if params["type"][0] == "log" and str(params["log"][0]) and str(params["message"][0]):
+ open(params["log"][0],"a+").write(params["message"][0]+"\n")
+ except IOError:
+ pass
+ except KeyError:
+ pass
+
+ try:
+ if params["type"][0] == "ticker_state" and str(params["message"][0]):
+ last_ticker_state = str(params["message"][0])
+ except KeyError:
+ pass
+
+ try:
+ if params["type"][0] == "startup" and last_ticker_state:
+ open("crashlog.txt","a+").write("Server exited, last ticker state was: "+last_ticker_state+"\n")
+ except KeyError:
+ pass
+
+
+while True:
+ data, addr = sock.recvfrom( 1024 ) # buffer size is 1024 bytes
+ handle_message(data,addr)
\ No newline at end of file
diff --git a/baystation12.dme b/baystation12.dme
index 3d156afe557..a05fbb8d31e 100644
--- a/baystation12.dme
+++ b/baystation12.dme
@@ -206,6 +206,7 @@
#define FILE_DIR "icons/vending_icons"
#define FILE_DIR "interface"
#define FILE_DIR "maps"
+#define FILE_DIR "maps/backup"
#define FILE_DIR "maps/RandomZLevels"
#define FILE_DIR "sound"
#define FILE_DIR "sound/AI"
@@ -378,6 +379,7 @@
#include "code\game\shuttle_engines.dm"
#include "code\game\skincmd.dm"
#include "code\game\smoothwall.dm"
+#include "code\game\socket_talk.dm"
#include "code\game\sound.dm"
#include "code\game\specops_shuttle.dm"
#include "code\game\status.dm"
diff --git a/code/datums/configuration.dm b/code/datums/configuration.dm
index f0cfee61dfc..67dec25042e 100644
--- a/code/datums/configuration.dm
+++ b/code/datums/configuration.dm
@@ -25,6 +25,7 @@
var/vote_period = 60 // length of voting period (seconds, default 1 minute)
var/vote_no_default = 0 // vote does not default to nochange/norestart (tbi)
var/vote_no_dead = 0 // dead people can't vote (tbi)
+ var/socket_talk = 0 // use socket_talk to communicate with other processes
// var/enable_authentication = 0 // goon authentication
var/del_new_on_log = 1 // del's new players if they log before they spawn in
var/feature_object_spell_system = 0 //spawns a spellbook which gives object-type spells instead of verb-type spells for the wizard
@@ -309,6 +310,9 @@
if("ticklag")
Ticklag = text2num(value)
+ if("socket_talk")
+ socket_talk = text2num(value)
+
if("tickcomp")
Tickcomp = 1
diff --git a/code/game/cellautomata.dm b/code/game/cellautomata.dm
index 7034c022a2b..466eee1b3e3 100644
--- a/code/game/cellautomata.dm
+++ b/code/game/cellautomata.dm
@@ -137,6 +137,7 @@
src.update_status()
+ socket_talk = new /datum/socket_talk()
master_controller = new /datum/controller/game_controller()
spawn(-1)
master_controller.setup()
diff --git a/code/game/master_controller.dm b/code/game/master_controller.dm
index 6f43d698129..1fc6a160da1 100644
--- a/code/game/master_controller.dm
+++ b/code/game/master_controller.dm
@@ -22,17 +22,30 @@ datum/controller/game_controller
var/global/ticker_ready = 0
proc
+ keepalive()
setup()
setup_objects()
process()
set_debug_state(txt)
+ keepalive()
+ spawn while(1)
+ sleep(10)
+
+ // Notify the other process that we're still there
+ socket_talk.send_keepalive()
+
setup()
if(master_controller && (master_controller != src))
del(src)
return
//There can be only one master.
+ socket_talk = new /datum/socket_talk()
+
+ // notify the other process that we started up
+ socket_talk.send_raw("type=startup")
+
if(!air_master)
air_master = new /datum/controller/air_system()
air_master.setup()
@@ -73,6 +86,8 @@ datum/controller/game_controller
setupfactions()
+ spawn keepalive()
+
spawn
ticker.pregame()
@@ -109,7 +124,7 @@ datum/controller/game_controller
// This should describe what is currently being done by the master controller
// Useful for crashlogs and similar, because that way it's easy to tell what
// was going on when the server crashed.
-
+ socket_talk.send_raw("type=ticker_state&message=[txt]")
return
process()
@@ -141,6 +156,9 @@ datum/controller/game_controller
powernets_ready = 0
ticker_ready = 0
+ // Notify the other process that we're still there
+ socket_talk.send_keepalive()
+
// the fact that the air master is not in the master controller
// will make it very hard to find out whether it's responsible
// for crashes
diff --git a/code/game/socket_talk.dm b/code/game/socket_talk.dm
new file mode 100644
index 00000000000..b6024f3a191
--- /dev/null
+++ b/code/game/socket_talk.dm
@@ -0,0 +1,26 @@
+// Module used for fast interprocess communication between BYOND and other processes
+
+/datum/socket_talk
+ var
+ enabled = 0
+ New()
+ ..()
+ src.enabled = config.socket_talk
+
+ if(enabled)
+ call("DLLSocket.so","establish_connection")("127.0.0.1","8019")
+
+ proc
+ send_raw(message)
+ if(enabled)
+ return call("DLLSocket.so","send_message")(message)
+ receive_raw()
+ if(enabled)
+ return call("DLLSocket.so","recv_message")()
+ send_log(var/log, var/message)
+ return send_raw("type=log&log=[log]&message=[message]")
+ send_keepalive()
+ return send_raw("type=keepalive")
+
+
+var/global/datum/socket_talk/socket_talk
\ No newline at end of file
diff --git a/config/config.txt b/config/config.txt
index 5c47d1f156c..cfc5138a669 100644
--- a/config/config.txt
+++ b/config/config.txt
@@ -163,5 +163,8 @@ TICKLAG 0.9
## Defines if Tick Compensation is used. It results in a minor slowdown of movement of all mobs, but attempts to result in a level movement speed across all ticks. Recommended if tickrate is lowered.
TICKCOMP 0
+## Whether the server will talk to other processes through socket_talk
+SOCKET_TALK 0
+
## Uncomment to restrict non-admins using humanoid alien races
USEALIENWHITELIST