From bc1fc1d2457ffe3644be721a063e62f43f2469b9 Mon Sep 17 00:00:00 2001 From: Sandu Liviu Catalin Date: Sun, 5 Mar 2023 15:38:59 +0200 Subject: [PATCH] Allow external native plug-ins to interact wit the script. Very primitive mechanism but better than nothing. --- module/CMakeLists.txt | 16 ++++-- module/Core.cpp | 123 ++++++++++++++++++++++++++++++++++++++--- module/Core.hpp | 35 ++++++++++++ module/Core/Events.inc | 40 +++++++++++++- module/Exports.cpp | 73 ++++++++++++++++++++++++ module/Main.cpp | 4 +- module/Register.cpp | 5 -- module/SDK/sqmod.h | 75 +++++++++++++++++++++++++ 8 files changed, 349 insertions(+), 22 deletions(-) create mode 100644 module/Exports.cpp create mode 100644 module/SDK/sqmod.h diff --git a/module/CMakeLists.txt b/module/CMakeLists.txt index 89a207d2..2d77f0bf 100644 --- a/module/CMakeLists.txt +++ b/module/CMakeLists.txt @@ -1,5 +1,7 @@ # Create the Squirrel module add_library(SqModule MODULE SqBase.hpp Main.cpp + # SDK + SDK/sqmod.h # VCMP VCMP/vcmp.h VCMP/vcmp20.h @@ -114,6 +116,7 @@ add_library(SqModule MODULE SqBase.hpp Main.cpp Core.cpp Core.hpp Logger.cpp Logger.hpp Register.cpp + Exports.cpp ) # Various definitions required by the plug-in target_compile_definitions(SqModule PRIVATE SCRAT_USE_EXCEPTIONS=1) @@ -201,6 +204,7 @@ else(WIN32) endif(WIN32) # Include current directory in the search path target_include_directories(SqModule PRIVATE ${CMAKE_CURRENT_LIST_DIR}) +target_include_directories(SqModule PRIVATE ${CMAKE_CURRENT_LIST_DIR}/SDK) target_include_directories(SqModule PRIVATE ${CMAKE_CURRENT_LIST_DIR}/VCMP) target_include_directories(SqModule PRIVATE ${CMAKE_CURRENT_LIST_DIR}/Sqrat) # Include PCRE directory in the header search path @@ -222,7 +226,7 @@ if(WIN32 AND MINGW AND COPY_DEPENDENCIES) endif() # Make sure the deps folder exists file(MAKE_DIRECTORY "${PROJECT_SOURCE_DIR}/bin/deps") - # Copy dependencies into the plug-ins folder (only so it can be distributed with the DLL) + # Copy dependencies into the deps folder (only so it can be distributed with the DLL) file(COPY "${MINGW_BIN_PATH}/zlib1.dll" DESTINATION "${PROJECT_SOURCE_DIR}/bin/deps") file(COPY "${MINGW_BIN_PATH}/libpq.dll" DESTINATION "${PROJECT_SOURCE_DIR}/bin/deps") file(COPY "${MINGW_BIN_PATH}/libzstd.dll" DESTINATION "${PROJECT_SOURCE_DIR}/bin/deps") @@ -245,17 +249,17 @@ if(WIN32 AND MINGW AND COPY_DEPENDENCIES) file(COPY "${MINGW_BIN_PATH}/libp11-kit-0.dll" DESTINATION "${PROJECT_SOURCE_DIR}/bin/deps") file(COPY "${MINGW_BIN_PATH}/libbrotlidec.dll" DESTINATION "${PROJECT_SOURCE_DIR}/bin/deps") file(COPY "${MINGW_BIN_PATH}/libbrotlicommon.dll" DESTINATION "${PROJECT_SOURCE_DIR}/bin/deps") - file(COPY "${MINGW_BIN_PATH}/libunistring-2.dll" DESTINATION "${PROJECT_SOURCE_DIR}/bin/deps") + file(COPY "${MINGW_BIN_PATH}/libunistring-5.dll" DESTINATION "${PROJECT_SOURCE_DIR}/bin/deps") file(COPY "${MINGW_BIN_PATH}/libnghttp2-14.dll" DESTINATION "${PROJECT_SOURCE_DIR}/bin/deps") file(COPY "${MINGW_BIN_PATH}/libwinpthread-1.dll" DESTINATION "${PROJECT_SOURCE_DIR}/bin/deps") file(COPY "${MINGW_BIN_PATH}/libstdc++-6.dll" DESTINATION "${PROJECT_SOURCE_DIR}/bin/deps") if(CMAKE_SIZEOF_VOID_P EQUAL 8 AND NOT FORCE_32BIT_BIN) file(COPY "${MINGW_BIN_PATH}/libgcc_s_seh-1.dll" DESTINATION "${PROJECT_SOURCE_DIR}/bin/deps") - file(COPY "${MINGW_BIN_PATH}/libssl-1_1-x64.dll" DESTINATION "${PROJECT_SOURCE_DIR}/bin/deps") - file(COPY "${MINGW_BIN_PATH}/libcrypto-1_1-x64.dll" DESTINATION "${PROJECT_SOURCE_DIR}/bin/deps") + file(COPY "${MINGW_BIN_PATH}/libssl-3-x64.dll" DESTINATION "${PROJECT_SOURCE_DIR}/bin/deps") + file(COPY "${MINGW_BIN_PATH}/libcrypto-3-x64.dll" DESTINATION "${PROJECT_SOURCE_DIR}/bin/deps") else() - file(COPY "${MINGW_BIN_PATH}/libssl-1_1.dll" DESTINATION "${PROJECT_SOURCE_DIR}/bin/deps") - file(COPY "${MINGW_BIN_PATH}/libcrypto-1_1.dll" DESTINATION "${PROJECT_SOURCE_DIR}/bin/deps") + file(COPY "${MINGW_BIN_PATH}/libssl-3.dll" DESTINATION "${PROJECT_SOURCE_DIR}/bin/deps") + file(COPY "${MINGW_BIN_PATH}/libcrypto-3.dll" DESTINATION "${PROJECT_SOURCE_DIR}/bin/deps") endif() if(POCO_UNBUNDLED) file(COPY "${MINGW_BIN_PATH}/libexpat-1.dll" DESTINATION "${PROJECT_SOURCE_DIR}/bin/deps") diff --git a/module/Core.cpp b/module/Core.cpp index 583f3918..02056dfb 100644 --- a/module/Core.cpp +++ b/module/Core.cpp @@ -161,6 +161,7 @@ Core::Core() noexcept , m_Scripts() , m_PendingScripts() , m_Options() + , m_ExtCommands{nullptr, nullptr, nullptr, nullptr} , m_Blips() , m_Checkpoints() , m_KeyBinds() @@ -441,9 +442,9 @@ bool Core::Execute() m_LockPostLoadSignal = false; m_LockUnloadSignal = false; - //cLogDbg(m_Verbosity >= 1, "Signaling outside plug-ins to register their API"); + cLogDbg(m_Verbosity >= 1, "Signaling outside plug-ins that the API is being registered"); // Tell modules to do their monkey business - //_Func->SendPluginCommand(0xDEADBABE, ""); + _Func->SendPluginCommand(0xDEADBABE, ""); // Load pending scripts while we're in the bounds of the allowed recursiveness for (unsigned levels = 0; !m_PendingScripts.empty() && (levels < 8); ++levels) @@ -511,9 +512,9 @@ void Core::Terminate(bool shutdown) // Clear the callbacks ResetSignalPair(mOnUnload); - //cLogDbg(m_Verbosity >= 1, "Signaling outside plug-ins to release their resources"); + cLogDbg(m_Verbosity >= 1, "Signaling outside plug-ins to release their resources"); // Tell modules to do their monkey business - //_Func->SendPluginCommand(0xDEADC0DE, ""); + _Func->SendPluginCommand(0xDEADC0DE, ""); } cLogDbg(m_Verbosity >= 1, "Clearing the entity containers"); @@ -596,9 +597,9 @@ void Core::Terminate(bool shutdown) HSQUIRRELVM sq_vm = m_VM; m_VM = nullptr; - //cLogDbg(m_Verbosity >= 1, "Signaling outside plug-ins the virtual machine is closing"); + cLogDbg(m_Verbosity >= 1, "Signaling outside plug-ins the virtual machine is closing"); // Tell modules to do their monkey business - //_Func->SendPluginCommand(0xBAAAAAAD, ""); + _Func->SendPluginCommand(0xBAAAAAAD, ""); // Release any callbacks from the logger Logger::Get().Release(); cLogDbg(m_Verbosity >= 2, "Closing Virtual Machine"); @@ -629,9 +630,9 @@ void Core::Terminate(bool shutdown) // Destroy the VM context, if any delete ctx; - //cLogDbg(m_Verbosity >= 1, "Signaling outside plug-ins to release the virtual machine"); + cLogDbg(m_Verbosity >= 1, "Signaling outside plug-ins the virtual machine was closed"); // Tell modules to do their monkey business - //_Func->SendPluginCommand(0xDEADBEAF, ""); + _Func->SendPluginCommand(0xDEADBEAF, ""); } OutputMessage("Squirrel plug-in was successfully terminated"); @@ -904,6 +905,81 @@ String Core::FetchCodeLine(const SQChar * src, SQInteger line, bool trim) return script->FetchLine(line, trim); } +// ------------------------------------------------------------------------------------------------ +int32_t Core::RegisterExtCommand(ExtPluginCommand_t fn) +{ + ExtPluginCommand_t * slot = nullptr; + // Find a free slot or matching function pointer in the pool + for (size_t i = 0; i < m_ExtCommands.max_size(); ++i) + { + // Is this slot available and are we still looking for a slot? + if (m_ExtCommands[i] == nullptr && slot == nullptr) + { + slot = &m_ExtCommands[i]; // Found a slot + } + // We keep looking for duplicates even if we found the slot + else if (m_ExtCommands[i] == fn) + { + return 0; // Already registered + } + } + // Do we have a free slot? + if (slot != nullptr) + { + *slot = fn; // Use this slot + return 1; // Successfully registered + } + // No space in the pool + return -1; +} + +// ------------------------------------------------------------------------------------------------ +int32_t Core::UnregisterExtCommand(ExtPluginCommand_t fn) +{ + // Find the matching function pointer + for (size_t i = 0; i < m_ExtCommands.max_size(); ++i) + { + // Is this the same pointer? + if (m_ExtCommands[i] != nullptr && m_ExtCommands[i] == fn) + { + // Forget about it + m_ExtCommands[i] = nullptr; + return 1; // Successfully unregistered + } + } + // No space + return -1; +} + +// ------------------------------------------------------------------------------------------------ +int32_t Core::SendExtCommand(int32_t target, int32_t req, int32_t tag, const uint8_t * data, size_t size) +{ + int32_t count = 0; + // Send the command to all registered function pointers + for (size_t i = 0; i < m_ExtCommands.max_size(); ++i) + { + if (m_ExtCommands[i] != nullptr) + { + const int32_t r = m_ExtCommands[i](target, req, tag, data, size); + // Command processed + ++count; + // Command failed? + if (r < 0) + { + LogErr("External command failed (%i): target(%i), req(%i), tag(%i), data(%p), size (%zu)", + r, target, req, tag, data, size); + } + // Command consumed? + else if (r > 0) + { + break; // This function pointer requested exclusive access over this command + } + } + } + // Return how many function pointers received this command + return count; +} + // ------------------------------------------------------------------------------------------------ bool Core::DoScripts(Scripts::iterator itr, Scripts::iterator end) { @@ -2371,6 +2447,8 @@ void Core::InitEvents() InitSignalPair(mOnServerOption, m_Events, "ServerOption"); InitSignalPair(mOnScriptReload, m_Events, "ScriptReload"); InitSignalPair(mOnScriptLoaded, m_Events, "ScriptLoaded"); + InitSignalPair(mOnExtCommandReply, m_Events, "ExtCommandReply"); + InitSignalPair(mOnExtCommandEvent, m_Events, "ExtCommandEvent"); } // ------------------------------------------------------------------------------------------------ void Core::DropEvents() @@ -2516,6 +2594,8 @@ void Core::DropEvents() ResetSignalPair(mOnServerOption); ResetSignalPair(mOnScriptReload); ResetSignalPair(mOnScriptLoaded); + ResetSignalPair(mOnExtCommandReply); + ResetSignalPair(mOnExtCommandEvent); m_Events.Release(); } @@ -2861,6 +2941,31 @@ static LightObj & SqGetClientDataBuffer() return Core::Get().GetClientDataBuffer(); } +// ------------------------------------------------------------------------------------------------ +static SQInteger SqSendExtCommand(int32_t target, int32_t req, int32_t tag, SqBuffer & buffer) +{ + // Default to an empty/null buffer + const uint8_t * data = nullptr; + size_t size = 0; + // Does the buffer actually point to anything? + if (buffer.GetRef()) + { + data = buffer.GetRef()->Begin< uint8_t >(); + size = buffer.GetRef()->PositionAs< size_t >(); + } + // Forward the request + return Core::Get().SendExtCommand(target, req, tag, data, size); +} + +// ------------------------------------------------------------------------------------------------ +static SQInteger SqSendExtCommandStr(int32_t target, int32_t req, int32_t tag, StackStrF & str) +{ + // Forward the request + return Core::Get().SendExtCommand(target, req, tag, + reinterpret_cast< const uint8_t * >(str.mPtr), + str.mLen <= 0 ? 0 : static_cast< size_t >(str.mLen)); +} + // ================================================================================================ void Register_Core(HSQUIRRELVM vm) { @@ -2910,6 +3015,8 @@ void Register_Core(HSQUIRRELVM vm) .Func(_SC("DestroyPickup"), &SqDelPickup) .Func(_SC("DestroyVehicle"), &SqDelVehicle) .Func(_SC("ClientDataBuffer"), &SqGetClientDataBuffer) + .Func(_SC("SendExtCommand"), &SqSendExtCommand) + .FmtFunc(_SC("SendExtCommandStr"), &SqSendExtCommandStr) .Func(_SC("OnPreLoad"), &SqGetPreLoadEvent) .Func(_SC("OnPostLoad"), &SqGetPostLoadEvent) .Func(_SC("OnUnload"), &SqGetUnloadEvent) diff --git a/module/Core.hpp b/module/Core.hpp index 31dc3950..c036b4d3 100644 --- a/module/Core.hpp +++ b/module/Core.hpp @@ -9,6 +9,9 @@ // ------------------------------------------------------------------------------------------------ #include +// ------------------------------------------------------------------------------------------------ +#include "SDK/sqmod.h" + // ------------------------------------------------------------------------------------------------ namespace SqMod { @@ -56,6 +59,8 @@ public: typedef std::vector< ScriptSrc > Scripts; // List of loaded scripts. // -------------------------------------------------------------------------------------------- typedef std::unordered_map< String, String > Options; // List of custom options. + // -------------------------------------------------------------------------------------------- + typedef std::array< ExtPluginCommand_t, 4 > ExtCommands; // 4 external command parsers should be enough. private: @@ -68,6 +73,7 @@ private: Scripts m_Scripts; // Loaded scripts objects. Scripts m_PendingScripts; // Pending scripts objects. Options m_Options; // Custom configuration options. + ExtCommands m_ExtCommands; // External command parsers pointers. // -------------------------------------------------------------------------------------------- Blips m_Blips; // Blips pool. @@ -399,6 +405,23 @@ public: */ SQMOD_NODISCARD String FetchCodeLine(const SQChar * src, SQInteger line, bool trim = true); + /* -------------------------------------------------------------------------------------------- + * Register a pointer to a function used to processes commands from script. + * Returns -1 it failed (no free slot), 0 if it was already registered and 1 if it succeeded. + */ + int32_t RegisterExtCommand(ExtPluginCommand_t fn); + + /* -------------------------------------------------------------------------------------------- + * Remove a pointer to a function used to processes commands from script. + * Returns -1 it failed (no free slot) and 1 if it succeeded. + */ + int32_t UnregisterExtCommand(ExtPluginCommand_t fn); + + /* -------------------------------------------------------------------------------------------- + * Send a command to all functions currently registered to receive them. + */ + int32_t SendExtCommand(int32_t target, int32_t req, int32_t tag, const uint8_t * data, size_t size); + protected: /* -------------------------------------------------------------------------------------------- @@ -710,6 +733,16 @@ public: */ void EmitClientScriptData(int32_t player_id, const uint8_t * data, size_t size); + /* -------------------------------------------------------------------------------------------- + * Send a response to the script that may have resulted from a previous command. + */ + void EmitExtCommandReply(int32_t sender, int32_t tag, const uint8_t * data, size_t size); + + /* -------------------------------------------------------------------------------------------- + * Forward an event to the script from an external plug-in. + */ + void EmitExtCommandEvent(int32_t sender, int32_t tag, const uint8_t * data, size_t size); + public: /* -------------------------------------------------------------------------------------------- @@ -864,6 +897,8 @@ public: SignalPair mOnServerOption{}; SignalPair mOnScriptReload{}; SignalPair mOnScriptLoaded{}; + SignalPair mOnExtCommandReply{}; + SignalPair mOnExtCommandEvent{}; }; /* ------------------------------------------------------------------------------------------------ diff --git a/module/Core/Events.inc b/module/Core/Events.inc index d461dacb..99a12617 100644 --- a/module/Core/Events.inc +++ b/module/Core/Events.inc @@ -347,7 +347,7 @@ void Core::EmitIncomingConnection(char * player_name, size_t name_buffer_size, c // Release any stored buffer information m_IncomingNameBuffer = nullptr; m_IncomingNameCapacity = 0; - // We catched the exception so we can release the assigned buffer + // We caught the exception so we can release the assigned buffer throw; // re-throw it } // Release any stored buffer information @@ -2246,6 +2246,44 @@ void Core::EmitClientScriptData(int32_t player_id, const uint8_t * data, size_t m_ClientData.Release(); } +// ------------------------------------------------------------------------------------------------ +void Core::EmitExtCommandReply(int32_t sender, int32_t tag, const uint8_t * data, size_t size) +{ + SQMOD_CO_EV_TRACEBACK("[TRACE<] Core::ExtCommandReply(%i, %i, %p, %zu)", sender, tag, data, size) + // Don't even bother if there's no one listening + if (!(mOnExtCommandReply.first->IsEmpty())) + { + // Allocate a buffer with the received size + Buffer b(static_cast< Buffer::SzType >(size)); + // Replicate the data to the allocated buffer + b.Write(0, reinterpret_cast< Buffer::ConstPtr >(data), static_cast< Buffer::SzType >(size)); + // Prepare an object for the obtained buffer + LightObj obj(SqTypeIdentity< SqBuffer >{}, m_VM, std::move(b)); + // Forward the event call + (*mOnExtCommandReply.first)(sender, tag, obj, size); + } + SQMOD_CO_EV_TRACEBACK("[TRACE>] Core::ExtCommandReply") +} + +// ------------------------------------------------------------------------------------------------ +void Core::EmitExtCommandEvent(int32_t sender, int32_t tag, const uint8_t * data, size_t size) +{ + SQMOD_CO_EV_TRACEBACK("[TRACE<] Core::ExtCommandEvent(%i, %i, %p, %zu)", sender, tag, data, size) + // Don't even bother if there's no one listening + if (!(mOnExtCommandEvent.first->IsEmpty())) + { + // Allocate a buffer with the received size + Buffer b(static_cast< Buffer::SzType >(size)); + // Replicate the data to the allocated buffer + b.Write(0, reinterpret_cast< Buffer::ConstPtr >(data), static_cast< Buffer::SzType >(size)); + // Prepare an object for the obtained buffer + LightObj obj(SqTypeIdentity< SqBuffer >{}, m_VM, std::move(b)); + // Forward the event call + (*mOnExtCommandEvent.first)(sender, tag, obj, size); + } + SQMOD_CO_EV_TRACEBACK("[TRACE>] Core::ExtCommandEvent") +} + #undef NULL_SQOBJ_ // don't need this anymore } // Namespace:: SqMod diff --git a/module/Exports.cpp b/module/Exports.cpp new file mode 100644 index 00000000..8f8331f3 --- /dev/null +++ b/module/Exports.cpp @@ -0,0 +1,73 @@ +// ------------------------------------------------------------------------------------------------ +#include "Core.hpp" + +// ------------------------------------------------------------------------------------------------ +#include + +// ------------------------------------------------------------------------------------------------ +#include + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + +// ------------------------------------------------------------------------------------------------ +static int32_t RegisterCommandFn(ExtPluginCommand_t fn) +{ + return Core::Get().RegisterExtCommand(fn); +} + +// ------------------------------------------------------------------------------------------------ +static int32_t UnregisterCommandFn(ExtPluginCommand_t fn) +{ + return Core::Get().UnregisterExtCommand(fn); +} + +// ------------------------------------------------------------------------------------------------ +static int32_t SendCommandFn(int32_t target, int32_t req, int32_t tag, const uint8_t * data, size_t size) +{ + return Core::Get().SendExtCommand(target, req, tag, data, size); +} + +// ------------------------------------------------------------------------------------------------ +static int32_t SendCommandReplyFn(int32_t sender, int32_t tag, const uint8_t * data, size_t size) +{ + // Mark the initialization as successful by default + const CoreState cs(SQMOD_SUCCESS); + // Forward the call to the script callbacks + Core::Get().EmitExtCommandReply(sender, tag, data, size); + // Return the last known plug-in state + return Core::Get().GetState(); +} + +// ------------------------------------------------------------------------------------------------ +static int32_t SendCommandEventFn(int32_t sender, int32_t tag, const uint8_t * data, size_t size) +{ + // Mark the initialization as successful by default + const CoreState cs(SQMOD_SUCCESS); + // Forward the call to the script callbacks + Core::Get().EmitExtCommandEvent(sender, tag, data, size); + // Return the last known plug-in state + return Core::Get().GetState(); +} + +// ------------------------------------------------------------------------------------------------ +static const SQ_MOD_EXPORTS g_SqModExports{ + sizeof(SQ_MOD_EXPORTS), + &RegisterCommandFn, + &UnregisterCommandFn, + &SendCommandFn, + &SendCommandReplyFn, + &SendCommandEventFn +}; + +// The server needs a pointer to a pointer, and a persistent one +static const SQ_MOD_EXPORTS * g_SqModExportsPtr = &g_SqModExports; + +// ------------------------------------------------------------------------------------------------ +void InitExports() +{ + // Tell the server about the pointer to the exports structure + _Func->ExportFunctions(_Info->pluginId, reinterpret_cast< const void ** >(&g_SqModExportsPtr), sizeof(HSQ_MOD_EXPORTS)); +} + +} // Namespace:: SqMod diff --git a/module/Main.cpp b/module/Main.cpp index 62ebb967..2bfe4420 100644 --- a/module/Main.cpp +++ b/module/Main.cpp @@ -17,7 +17,7 @@ namespace SqMod { static bool g_Reload = false; // ------------------------------------------------------------------------------------------------ -//extern void InitExports(); +extern void InitExports(); extern void InitializeNet(); extern void InitializePocoDataConnectors(); extern void ProcessRoutines(); @@ -1083,7 +1083,7 @@ SQMOD_API_EXPORT unsigned int VcmpPluginInit(PluginFuncs * funcs, PluginCallback _Clbk->OnEntityStreamingChange = OnEntityStreamingChange; #endif // Attempt to initialize the plug-in exports - //InitExports(); + InitExports(); // Dummy spacing puts(""); // Initialization was successful diff --git a/module/Register.cpp b/module/Register.cpp index 74d52bbe..685d0677 100644 --- a/module/Register.cpp +++ b/module/Register.cpp @@ -1,6 +1,3 @@ -#ifndef _REGISTER_HPP_ -#define _REGISTER_HPP_ - // ------------------------------------------------------------------------------------------------ #include @@ -144,5 +141,3 @@ bool RegisterAPI(HSQUIRRELVM vm) } } // Namespace:: SqMod - -#endif // _REGISTER_HPP_ diff --git a/module/SDK/sqmod.h b/module/SDK/sqmod.h new file mode 100644 index 00000000..a082b495 --- /dev/null +++ b/module/SDK/sqmod.h @@ -0,0 +1,75 @@ +#if !defined(_SQ_MOD_API_H_) +#define _SQ_MOD_API_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + + #ifndef SQMOD_API_EXPORT + #if defined(_MSC_VER) + #define SQMOD_API_EXPORT extern "C" __declspec(dllexport) + #elif defined(__GNUC__) + #define SQMOD_API_EXPORT extern "C" + #else + #define SQMOD_API_EXPORT extern "C" + #endif + #endif + + #define SQMOD_HOST_NAME "SqModHost" + #define SQMOD_INITIALIZE_CMD 0xDABBAD00 + #define SQMOD_LOAD_CMD 0xDEADBABE // API is being registered + #define SQMOD_TERMINATE_CMD 0xDEADC0DE // release your resources + #define SQMOD_CLOSING_CMD 0xBAAAAAAD // virtual machine is closing + #define SQMOD_RELEASED_CMD 0xDEADBEAF // virtual machine was closed + #define SQMOD_API_VER 1 + + // -------------------------------------------------------------------------------------------- + typedef int32_t(*ExtPluginCommand_t)(int32_t, int32_t, int32_t, const uint8_t *, size_t); + + /* -------------------------------------------------------------------------------------------- + * The structure exported by the host plug-in to import the module and squirrel API. + */ + typedef struct + { + uint32_t StructSize; + /* Register a pointer to a function used to processes commands from script. + This is like the functionality offered by SendPluginCommand but offers interaction from + script side without interfering with other native plugins. And also offers a few extra + methods of identification to provide back and forth communication. + It offers a bare minimum, primitive way of interacting with the script from native plug-ins. + return : -1 it failed (no free slot), 0 if it was already registered and 1 if it succeeded. + */ + int32_t (*RegisterCommand) (ExtPluginCommand_t fn); + /* Remove a pointer to a function used to processes commands from script. + return : -1 it failed (no free slot) and 1 if it succeeded. + */ + int32_t (*UnregisterCommand) (ExtPluginCommand_t fn); + /* Send a command to all functions currently registered to receive them. This is mostly used by the script. + target - ideally a unique value that can be used to identify the intended audience for the command. + req : ideally a unique value that can be used to identify the requested information from the command. + tag : ideally a unique value that can be used to identify a later response if one is generated. + data : binary data that represents the command payload. the command is free to interpret it however it wants. + size : size of the binary data. most likely in bytes but the command is free to interpret it however it wants. + */ + int32_t (*SendCommand) (int32_t target, int32_t req, int32_t tag, const uint8_t * data, size_t size); + /* Send a response to the script that may have resulted from a previous command. This is mostly by the native plug-ins. + sender : ideally a unique value that can be used to identify the intended who generated the response. + tag : ideally a unique value that can be used to identify what the generated response might contain/provide. + data : binary data that represents the command payload. the command is free to interpret it however it wants. + size : size of the binary data. most likely in bytes but the command is free to interpret it however it wants. + */ + int32_t (*SendCommandReply) (int32_t sender, int32_t tag, const uint8_t * data, size_t size); + /* Forward an event to the script from an external plug-in. This is mostly by the native plug-ins. + Similar to SendCommandReply but may not have been the result of a previous command. + */ + int32_t (*SendCommandEvent) (int32_t sender, int32_t tag, const uint8_t * data, size_t size); + } sq_mod_exports, SQ_MOD_EXPORTS, *HSQ_MOD_EXPORTS; + +#ifdef __cplusplus +} /*extern "C"*/ +#endif // __cplusplus + +#endif /*_SQ_MOD_API_H_*/