/* * Sample C++ Plugin — fully unloadable, no runtime baggage. * * Exports the standard Overlord plugin ABI: * PluginOnLoad, PluginOnEvent, PluginOnUnload, PluginSetCallback, * PluginGetRuntime (returns "cpp" — tells the host this DLL can be freed) * * Build (Windows / MSVC): * cl /LD /EHsc /O2 plugin.cpp /Fe:sample-cpp-windows-amd64.dll * — or with MinGW — * x86_64-w64-mingw32-g++ -shared -O2 -o sample-cpp-windows-amd64.dll plugin.cpp * * Build (Linux): * g++ -shared -fPIC -O2 -o sample-cpp-linux-amd64.so plugin.cpp */ #include #include #include #include #include #include /* ------------------------------------------------------------------ */ /* Platform helpers */ /* ------------------------------------------------------------------ */ #ifdef _WIN32 # define EXPORT extern "C" __declspec(dllexport) # define WIN32_LEAN_AND_MEAN # include BOOL WINAPI DllMain(HINSTANCE hDll, DWORD reason, LPVOID reserved) { (void)hDll; (void)reserved; return TRUE; } #else # define EXPORT extern "C" __attribute__((visibility("default"))) #endif /* ------------------------------------------------------------------ */ /* Host callback */ /* ------------------------------------------------------------------ */ #ifdef _WIN32 typedef void (__stdcall *host_callback_t)( const char *event, uintptr_t eventLen, const char *payload, uintptr_t payloadLen); #else typedef void (*host_callback_t)( uintptr_t ctx, const char *event, int eventLen, const char *payload, int payloadLen); #endif static host_callback_t g_callback = nullptr; #ifndef _WIN32 static uintptr_t g_callback_ctx = 0; #endif static void send_event(const char *event, const char *payload) { if (!g_callback) return; int elen = event ? static_cast(strlen(event)) : 0; int plen = payload ? static_cast(strlen(payload)) : 0; #ifdef _WIN32 g_callback(event, static_cast(elen), payload, static_cast(plen)); #else g_callback(g_callback_ctx, event, elen, payload, plen); #endif } /* ------------------------------------------------------------------ */ /* Plugin state — demonstrates C++ features (std::string, maps, mutex) */ /* ------------------------------------------------------------------ */ static std::mutex g_mu; static std::string g_client_id; static std::unordered_map g_event_counts; /* Extract a JSON string value by key (simple, no full JSON parser) */ static std::string json_extract(const char *json, int len, const char *key) { if (!json || len <= 0) return ""; std::string haystack(json, static_cast(len)); std::string needle = std::string("\"") + key + "\":\""; auto pos = haystack.find(needle); if (pos == std::string::npos) { needle = std::string("\"") + key + "\": \""; pos = haystack.find(needle); } if (pos == std::string::npos) return ""; pos += needle.size(); auto end = haystack.find('"', pos); if (end == std::string::npos) return ""; return haystack.substr(pos, end - pos); } /* ------------------------------------------------------------------ */ /* Exported ABI */ /* ------------------------------------------------------------------ */ EXPORT const char *PluginGetRuntime() { return "cpp"; } #ifdef _WIN32 EXPORT void PluginSetCallback(uint64_t cb) { g_callback = reinterpret_cast(static_cast(cb)); } EXPORT int PluginOnLoad(const char *hostInfo, int hostInfoLen, uint64_t cb) { g_callback = reinterpret_cast(static_cast(cb)); #else EXPORT int PluginOnLoad(const char *hostInfo, int hostInfoLen, uintptr_t cb, uintptr_t ctx) { g_callback = reinterpret_cast(cb); g_callback_ctx = ctx; #endif { std::lock_guard lk(g_mu); g_client_id = json_extract(hostInfo, hostInfoLen, "clientId"); g_event_counts.clear(); } fprintf(stderr, "[sample-cpp] loaded, clientId=%s\n", g_client_id.c_str()); send_event("ready", "{\"message\":\"sample-cpp plugin ready\"}"); return 0; } EXPORT int PluginOnEvent(const char *event, int eventLen, const char *payload, int payloadLen) { std::string ev(event, static_cast(eventLen)); { std::lock_guard lk(g_mu); g_event_counts[ev]++; } if (ev == "ping") { send_event("pong", nullptr); return 0; } if (ev == "ui_message") { std::string pl(payload ? payload : "", payload ? static_cast(payloadLen) : 0u); fprintf(stderr, "[sample-cpp] got ui_message: %s\n", pl.c_str()); std::string resp = "{\"message\":\"echo from C++: " + pl + "\"}"; send_event("echo", resp.c_str()); return 0; } if (ev == "stats") { std::lock_guard lk(g_mu); std::string json = "{"; bool first = true; for (auto &kv : g_event_counts) { if (!first) json += ","; json += "\"" + kv.first + "\":" + std::to_string(kv.second); first = false; } json += "}"; send_event("stats_reply", json.c_str()); return 0; } fprintf(stderr, "[sample-cpp] unhandled event: %s\n", ev.c_str()); return 0; } EXPORT void PluginOnUnload() { fprintf(stderr, "[sample-cpp] unloading\n"); g_callback = nullptr; #ifndef _WIN32 g_callback_ctx = 0; #endif std::lock_guard lk(g_mu); g_client_id.clear(); g_event_counts.clear(); }