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