From 53573eb79512c101f3249e944b38a966926ab406 Mon Sep 17 00:00:00 2001 From: madeline Date: Thu, 23 Apr 2026 04:49:21 -0700 Subject: [PATCH] inter mod communication --- docs/modding.md | 38 +++++++++++++++++++++++++++++++++++++- include/dusk/mod_api.h | 3 +++ src/dusk/mod_loader.cpp | 19 +++++++++++++++++++ 3 files changed, 59 insertions(+), 1 deletion(-) diff --git a/docs/modding.md b/docs/modding.md index bae5a2d84c..406a632229 100644 --- a/docs/modding.md +++ b/docs/modding.md @@ -16,7 +16,8 @@ Mods are shared libraries packaged into a `.dusk` zip archive. The loader scans - [Post-hooks](#post-hooks) - [Replace hooks](#replace-hooks) - [Reading and writing arguments](#reading-and-writing-arguments) -9. [Full Example](#full-example) +9. [Inter-Mod Communication](#inter-mod-communication) +10. [Full Example](#full-example) --- @@ -98,6 +99,8 @@ The `api` pointer is valid for the lifetime of the mod. When using `hook.hpp`, c | `register_tab_content` | Add a panel to the mod manager's per-mod tab | | `register_menu_item` | Add an item to the quick-access menu | | `hook_dispatch_pre` / `hook_dispatch_post` | Called by the trampoline, do not call directly | +| `service_publish` | Register a named pointer in the global service registry | +| `service_get` | Look up a named pointer registered by another mod | --- @@ -248,6 +251,39 @@ dusk::hookAddPre<&daEnemy_c::takeDamage>(on_takeDamage_pre); For reference parameters (e.g. `const cXyz& pos`), use `argRef` to get a direct reference. +--- + +## Inter-Mod Communication + +Mods can expose a public API to each other through a global service registry. The convention for names is `"mod_name/service_name"`. + +**Mod A — publishing:** + +```cpp +struct MyModAPI { + void (*do_thing)(int value); +}; + +static void my_do_thing(int value) { ... } +static MyModAPI g_api = { my_do_thing }; + +extern "C" void mod_init(DuskModAPI* api) { + api->service_publish("my_mod/api", &g_api); +} +``` + +**Mod B — consuming:** + +```cpp +#include "my_mod_api.h" + +static MyModAPI* g_my_mod = nullptr; + +extern "C" void mod_init(DuskModAPI* api) { + g_my_mod = static_cast(api->service_get("my_mod/api")); +} +``` + --- ## Full Example diff --git a/include/dusk/mod_api.h b/include/dusk/mod_api.h index ba80f7f4b6..6d7d75414d 100644 --- a/include/dusk/mod_api.h +++ b/include/dusk/mod_api.h @@ -38,6 +38,9 @@ typedef struct DuskModAPI { bool (*hook_dispatch_pre) (void* fn_addr, void* args); void (*hook_dispatch_post)(void* fn_addr, void* args); + + void (*service_publish)(const char* name, void* ptr); + void* (*service_get) (const char* name); } DuskModAPI; void mod_init(DuskModAPI* api); diff --git a/src/dusk/mod_loader.cpp b/src/dusk/mod_loader.cpp index d81163b4ee..6094e2cb0c 100644 --- a/src/dusk/mod_loader.cpp +++ b/src/dusk/mod_loader.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include "imgui.h" #include "miniz.h" @@ -59,6 +60,7 @@ static constexpr const char* k_libExt = ".so"; #endif static dusk::LoadedMod* g_currentMod = nullptr; +static std::unordered_map g_services; namespace dusk { void* g_dusk_hook_current_mod = nullptr; @@ -134,6 +136,20 @@ static void cb_register_tab_content(void (*draw_fn)(void*), void* userdata) { g_currentMod->tab_content.push_back({draw_fn, userdata}); } +static void cb_service_publish(const char* name, void* ptr) { + if (name) { + g_services[name] = ptr; + } +} + +static void* cb_service_get(const char* name) { + if (!name) { + return nullptr; + } + auto it = g_services.find(name); + return it != g_services.end() ? it->second : nullptr; +} + static void cb_register_menu_item(void (*draw_fn)(void*), void* userdata) { if (g_currentMod && draw_fn) g_currentMod->menu_items.push_back({draw_fn, userdata}); @@ -174,6 +190,8 @@ void ModLoader::buildAPI(LoadedMod& mod) { mod.api.hook_replace = api_hook_replace; mod.api.hook_dispatch_pre = hookDispatchPre; mod.api.hook_dispatch_post = hookDispatchPost; + mod.api.service_publish = cb_service_publish; + mod.api.service_get = cb_service_get; } void ModLoader::tryLoadDusk(const std::filesystem::path& modPath) { @@ -365,6 +383,7 @@ void ModLoader::shutdown() { } } m_mods.clear(); + g_services.clear(); DuskLog.info("ModLoader: all mods unloaded"); }