diff --git a/byond-extools.dll b/byond-extools.dll index bd6b34c48e..cd27969ce1 100644 Binary files a/byond-extools.dll and b/byond-extools.dll differ diff --git a/code/__DEFINES/_extools.dm b/code/__DEFINES/_extools.dm new file mode 100644 index 0000000000..4513243aae --- /dev/null +++ b/code/__DEFINES/_extools.dm @@ -0,0 +1 @@ +#define EXTOOLS (world.system_type == MS_WINDOWS ? "byond-extools.dll" : "libbyond-extools.so") diff --git a/code/__DEFINES/atmospherics.dm b/code/__DEFINES/atmospherics.dm index 621af2c811..dad2a38afd 100644 --- a/code/__DEFINES/atmospherics.dm +++ b/code/__DEFINES/atmospherics.dm @@ -281,8 +281,6 @@ GLOBAL_LIST_INIT(atmos_adjacent_savings, list(0,0)) #define CALCULATE_ADJACENT_TURFS(T) SSadjacent_air.queue[T] = 1 #endif -#define EXTOOLS (world.system_type == MS_WINDOWS ? "byond-extools.dll" : "libbyond-extools.so") - GLOBAL_VAR(atmos_extools_initialized) // this must be an uninitialized (null) one or init_monstermos will be called twice because reasons #define ATMOS_EXTOOLS_CHECK if(!GLOB.atmos_extools_initialized){\ GLOB.atmos_extools_initialized=TRUE;\ diff --git a/code/__HELPERS/_extools_api.dm b/code/__HELPERS/_extools_api.dm new file mode 100644 index 0000000000..8c00ec643e --- /dev/null +++ b/code/__HELPERS/_extools_api.dm @@ -0,0 +1,89 @@ +/* + TFFI - Threaded FFI + All DLL calls are automatically threaded off. + Black magic is used to suspend (sleep) the currently executing proc, allowing non-blocking FFI. + You may call a DLL function and sleep until it returns, pass a callback to be called with the result, + or call resolve() on the /datum/promise to receive the return value at any time. + Example: + var/x = call_wait("sample.dll", "do_work", "arg1", "arg2", "arg3") + - Calls the do_work function from sample.dll with 3 arguments. The proc sleeps until do_work returns. + var/datum/promise/P = call_async("sample.dll", "do_work", "arg1") + ... do something else ... + var/result = P.resolve() + - Calls do_work with 1 argument. Returns a promise object. Runs some other code before calling P.resolve() to obtain the result. + /proc/print_result(result) + world << result + call_cb("sample.dll", "do_work", /proc/print_result, "arg1", "arg2") + - Calls do_work with 2 arguments. The callback is invoked with the result as the single argument. Execution resumes immediately. +*/ + +/proc/tffi_initialize() + return call(EXTOOLS, "tffi_initialize")() + +var/fallback_alerted = FALSE +var/next_promise_id = 0 + +/datum/promise + var/completed = FALSE + var/result = "" + var/callback_context = GLOBAL_PROC + var/callback_proc = null + var/__id = 0 + +/datum/promise/New() + __id = next_promise_id++ //please don't create more than 10^38 promises in a single tick + +//This proc's bytecode is overwritten to allow suspending and resuming on demand. +//None of the code here should run. +/datum/promise/proc/__internal_resolve(ref, id) + if(!fallback_alerted && world.system_type != UNIX) // the rewriting is currently broken on Linux. + world << "TFFI: __internal_resolve has not been rewritten, the TFFI DLL was not loaded correctly." + world.log << "TFFI: __internal_resolve has not been rewritten, the TFFI DLL was not loaded correctly." + fallback_alerted = TRUE + while(!completed) + sleep(1) + //It might be better to just fail and notify the user that something went wrong. + +/datum/promise/proc/__resolve_callback() + __internal_resolve("\ref[src]", __id) + if(callback_context == GLOBAL_PROC) + call(callback_proc)(result) + else + call(callback_context, callback_proc)(result) + +/datum/promise/proc/resolve() + __internal_resolve("\ref[src]", __id) + return result + +/proc/call_async() + var/list/arguments = args.Copy() + var/datum/promise/P = new + arguments.Insert(1, "\ref[P]") + call(EXTOOLS, "call_async")(arglist(arguments)) + return P + +/proc/call_cb() + var/list/arguments = args.Copy() + var/context = arguments[3] + var/callback = arguments[4] + arguments.Cut(3, 5) + var/datum/promise/P = new + P.callback_context = context + P.callback_proc = callback + arguments.Insert(1, "\ref[P]") + call(EXTOOLS, "call_async")(arglist(arguments)) + spawn(0) + P.__resolve_callback() + +/proc/call_wait() + return call_async(arglist(args)).resolve() + +/* + Logging -- same exact interface as rust_g logging, but threaded and doesn't go through call()(). +*/ + +#define EXTOOLS_LOGGING // rust_g is used as a fallback if this is undefined + +/proc/extools_log_write() + +/proc/extools_finalize_logging() diff --git a/code/__HELPERS/_logging.dm b/code/__HELPERS/_logging.dm index 62e6c4daf9..888c20003f 100644 --- a/code/__HELPERS/_logging.dm +++ b/code/__HELPERS/_logging.dm @@ -4,10 +4,15 @@ #define SEND_SOUND(target, sound) DIRECT_OUTPUT(target, sound) #define SEND_TEXT(target, text) DIRECT_OUTPUT(target, text) #define WRITE_FILE(file, text) DIRECT_OUTPUT(file, text) +#ifdef EXTOOLS_LOGGING +// proc hooked, so we can just put in standard TRUE and FALSE +#define WRITE_LOG(log, text) extools_log_write(log,text,TRUE) +#define WRITE_LOG_NO_FORMAT(log, text) extools_log_write(log,text,FALSE) +#else //This is an external call, "true" and "false" are how rust parses out booleans #define WRITE_LOG(log, text) rustg_log_write(log, text, "true") #define WRITE_LOG_NO_FORMAT(log, text) rustg_log_write(log, text, "false") - +#endif //print a warning message to world.log #define WARNING(MSG) warning("[MSG] in [__FILE__] at line [__LINE__] src: [UNLINT(src)] usr: [usr].") /proc/warning(msg) @@ -216,7 +221,11 @@ /* Close open log handles. This should be called as late as possible, and no logging should hapen after. */ /proc/shutdown_logging() +#ifdef EXTOOLS_LOGGING + extools_finalize_logging() +#else rustg_log_close_all() +#endif /* Helper procs for building detailed log lines */ diff --git a/code/game/world.dm b/code/game/world.dm index 2d174c86e5..0306b42275 100644 --- a/code/game/world.dm +++ b/code/game/world.dm @@ -11,6 +11,11 @@ GLOBAL_LIST(topic_status_cache) /world/New() if (fexists(EXTOOLS)) call(EXTOOLS, "maptick_initialize")() + #ifdef EXTOOLS_LOGGING + call(EXTOOLS, "init_logging")() + else + CRASH("[EXTOOLS] does not exist!") + #endif enable_debugger() #ifdef REFERENCE_TRACKING enable_reference_tracking() diff --git a/libbyond-extools.so b/libbyond-extools.so index 8e17f952f2..184babef2c 100644 Binary files a/libbyond-extools.so and b/libbyond-extools.so differ diff --git a/tgstation.dme b/tgstation.dme index e742498527..f0ca748803 100644 --- a/tgstation.dme +++ b/tgstation.dme @@ -17,6 +17,7 @@ #include "_maps\_basemap.dm" #include "code\_compile_options.dm" #include "code\world.dm" +#include "code\__DEFINES\_extools.dm" #include "code\__DEFINES\_globals.dm" #include "code\__DEFINES\_protect.dm" #include "code\__DEFINES\_tick.dm" @@ -147,6 +148,7 @@ #include "code\__DEFINES\storage\_storage.dm" #include "code\__DEFINES\storage\volumetrics.dm" #include "code\__HELPERS\_cit_helpers.dm" +#include "code\__HELPERS\_extools_api.dm" #include "code\__HELPERS\_lists.dm" #include "code\__HELPERS\_logging.dm" #include "code\__HELPERS\_string_lists.dm"