diff --git a/byond-extools.dll b/byond-extools.dll
index 65be0856e095..dc90fd1ecc4f 100644
Binary files a/byond-extools.dll and b/byond-extools.dll differ
diff --git a/byond-extools.pdb b/byond-extools.pdb
index be8b64d780b0..42a16cd58fc1 100644
Binary files a/byond-extools.pdb and b/byond-extools.pdb differ
diff --git a/code/__DEFINES/_globals.dm b/code/__DEFINES/_globals.dm
index 8c0c99eefda5..873ed7eaea77 100644
--- a/code/__DEFINES/_globals.dm
+++ b/code/__DEFINES/_globals.dm
@@ -57,4 +57,4 @@
#define GLOBAL_LIST(X) GLOBAL_RAW(/list/##X); GLOBAL_UNMANAGED(X)
//Create an typed null global
-#define GLOBAL_DATUM(X, Typepath) GLOBAL_RAW(Typepath/##X); GLOBAL_UNMANAGED(X)
+#define GLOBAL_DATUM(X, Typepath) GLOBAL_RAW(Typepath/##X); GLOBAL_UNMANAGED(X)
\ No newline at end of file
diff --git a/code/__DEFINES/extools.dm b/code/__DEFINES/extools.dm
new file mode 100644
index 000000000000..cb2bd56ee8d9
--- /dev/null
+++ b/code/__DEFINES/extools.dm
@@ -0,0 +1,143 @@
+#define EXTOOLS_SUCCESS "SUCCESS"
+#define EXTOOLS_FAILED "FAIL"
+
+/*
+ Core - Provides necessary functionality for other modules.
+ Initializing any other modules also initializes this so it shouldn't be necessary to call this.
+*/
+
+/proc/extools_initialize()
+ return call(EXTOOLS, "core_initialize")() == EXTOOLS_SUCCESS
+
+/*
+ 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.
+*/
+
+#if 0
+
+/proc/tffi_initialize()
+ return call(EXTOOLS, "tffi_initialize")() == EXTOOLS_SUCCESS
+
+GLOBAL_VAR_INIT(fallback_alerted, FALSE)
+GLOBAL_VAR_INIT(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()
+
+#endif
+
+/*
+ Extended Profiling - High precision in-depth performance profiling.
+ Turning on extended profiling for a proc will cause each execution of it to generate a file in the ./profiles directory
+ containing a breakdown of time spent executing the proc and each sub-proc it calls. Import the file into https://www.speedscope.app/ to
+ view a good visual representation.
+ Be aware that sleeping counts as stopping and restarting the execution of the proc, which will generate multiple files, one between each sleep.
+ For large procs the profiles may become unusably large. Optimizations pending.
+ Example:
+ start_profiling(/datum/explosion/New)
+ - Enables profiling for /datum/explosion/New(), which will produce a detailed breakdown of each explosion that occurs afterwards.
+ stop_profiling(/datum/explosion/New)
+ - Disables profiling for explosions. Any currently running profiles will stop when the proc finishes executing or enters a sleep.
+*/
+
+/proc/profiling_initialize()
+ return call(EXTOOLS, "extended_profiling_initialize")() == EXTOOLS_SUCCESS
+
+/proc/start_profiling(procpath)
+ call(EXTOOLS, "enable_extended_profiling")("[procpath]")
+
+/proc/stop_profiling(procpath)
+ call(EXTOOLS, "disable_extended_profiling")("[procpath]")
+
+/*
+ Debug Server - High and low level debugging of DM code.
+ Calling debugger_initialize will start a debug server that allows connections from frontends,
+ such as SpaceManiac's VSCode extension for line-by-line debugging (and more), or Steamport's
+ Somnium for bytecode inspection.
+ Call with pause = TRUE to wait until the debugger connected and immediately break on the next instruction after the call.
+*/
+
+/proc/debugger_initialize(pause = FALSE)
+ return call(EXTOOLS, "debug_initialize")(pause ? "pause" : "") == EXTOOLS_SUCCESS
+
+/*
+ Misc
+*/
+
+//Programatically enable and disable the built-in byond profiler. Useful if you want to, for example, profile subsystem initializations.
+/proc/enable_profiling()
+ return call(EXTOOLS, "enable_profiling")() == EXTOOLS_SUCCESS
+
+/proc/disable_profiling()
+ return call(EXTOOLS, "disable_profiling")() == EXTOOLS_SUCCESS
+
+// Will dump the server's in-depth memory profile into the file specified.
+/proc/dump_memory_profile(file_name)
+ return call(EXTOOLS, "dump_memory_usage")(file_name) == EXTOOLS_SUCCESS
diff --git a/code/__HELPERS/files.dm b/code/__HELPERS/files.dm
index d3cb7b7c9258..18ba7eacc6a5 100644
--- a/code/__HELPERS/files.dm
+++ b/code/__HELPERS/files.dm
@@ -3,7 +3,7 @@
for(var/file in args)
src << browse_rsc(file)
-/client/proc/browse_files(root="data/logs/", max_iterations=10, list/valid_extensions=list("txt","log","htm", "html"))
+/client/proc/browse_files(root="data/logs/", max_iterations=10, list/valid_extensions=list("txt","log","htm", "html", "json"))
if(IsAdminAdvancedProcCall())
log_admin_private("BROWSEFILES: Admin proc call blocked")
message_admins("BROWSEFILES: Admin proc call blocked")
diff --git a/code/_compile_options.dm b/code/_compile_options.dm
index d126453c807f..7dfc3773a0d3 100644
--- a/code/_compile_options.dm
+++ b/code/_compile_options.dm
@@ -56,4 +56,4 @@
#define TESTING
#endif
-#define EXTOOLS (world.system_type == MS_WINDOWS ? "byond-extools.dll" : "libbyond-extools.so")
+#define EXTOOLS (world.system_type == MS_WINDOWS ? "byond-extools.dll" : "libbyond-extools.so")
\ No newline at end of file
diff --git a/code/modules/admin/admin_verbs.dm b/code/modules/admin/admin_verbs.dm
index e7a919f32332..e15dcd79a5fd 100644
--- a/code/modules/admin/admin_verbs.dm
+++ b/code/modules/admin/admin_verbs.dm
@@ -147,7 +147,8 @@ GLOBAL_PROTECT(admin_verbs_server)
/client/proc/adminchangemap,
/client/proc/panicbunker,
/client/proc/toggle_hub,
- /client/proc/mentor_memo, /* YOGS - something stupid about "Mentor memos" */
+ /client/proc/mentor_memo, // YOGS - something stupid about "Mentor memos"
+ ///client/proc/dump_memory_usage,
/client/proc/release_queue // Yogs -- Adds some queue-manipulation verbs
)
GLOBAL_LIST_INIT(admin_verbs_debug, world.AVerbsDebug())
@@ -778,3 +779,30 @@ GLOBAL_PROTECT(admin_verbs_hideable)
log_admin("[key_name(usr)] has [AI_Interact ? "activated" : "deactivated"] Admin AI Interact")
message_admins("[key_name_admin(usr)] has [AI_Interact ? "activated" : "deactivated"] their AI interaction")
+
+/*/client/proc/dump_memory_usage()
+ set name = "Dump Server Memory Usage"
+ set category = "Server"
+
+ if(!check_rights(R_SERVER))
+ return
+
+ if(alert(usr, "This will dump memory usage and potentially lag the server. Proceed?", "Alert", "Yes", "No") != "Yes")
+ return
+
+ var/fname = "[GLOB.round_id ? GLOB.round_id : "NULL"]-[time2text(world.timeofday, "MM-DD-hhmm")].json"
+
+ to_chat(world, "Performing a memory dump!")
+ log_admin("[key_name_admin(usr)] has initiated a memory dump into \"[fname]\".")
+ message_admins("[key_name_admin(usr)] has initiated a memory dump into \"[fname]\".")
+
+ sleep(20)
+
+ if(!dump_memory_profile("data/logs/memory/[fname]"))
+ to_chat(usr, "Dumping memory failed at dll call.")
+ return
+
+ if(!fexists("data/logs/memory/[fname]"))
+ to_chat(usr, "File creation failed. Please check to see if the data/logs/memory folder actually exists.")
+ else
+ to_chat(usr, "Memory dump completed.")*/
diff --git a/libbyond-extools.so b/libbyond-extools.so
index 0f3247dbb1e2..8de3f23fa9fe 100755
Binary files a/libbyond-extools.so and b/libbyond-extools.so differ
diff --git a/yogstation.dme b/yogstation.dme
index 3b73ccce1344..460293a400f8 100644
--- a/yogstation.dme
+++ b/yogstation.dme
@@ -43,6 +43,7 @@
#include "code\__DEFINES\economy.dm"
#include "code\__DEFINES\events.dm"
#include "code\__DEFINES\exports.dm"
+#include "code\__DEFINES\extools.dm"
#include "code\__DEFINES\fantasy_affixes.dm"
#include "code\__DEFINES\fastdmm2.dm"
#include "code\__DEFINES\flags.dm"