From 99b3cad1743fd66863c72ce4e9f3145a34075a3c Mon Sep 17 00:00:00 2001 From: caxanga334 <10157643+caxanga334@users.noreply.github.com> Date: Sun, 23 Nov 2025 11:09:38 -0300 Subject: [PATCH 1/7] Squashed commit of the following: commit c7efce2d6722b08e2ea719ce1a39f80747502f77 Author: caxanga334 <10157643+caxanga334@users.noreply.github.com> Date: Sat Nov 22 22:35:24 2025 -0300 Fix Error commit e8acf9f505aa26b007c5837d4075f649099f43c3 Author: caxanga334 <10157643+caxanga334@users.noreply.github.com> Date: Sat Nov 22 22:23:57 2025 -0300 Fix Possible Memory Leak commit 51e718922726c074c0e7a45f9d13c6a51e4b97eb Author: caxanga334 <10157643+caxanga334@users.noreply.github.com> Date: Sat Nov 22 22:13:55 2025 -0300 Add Upload Forward --- buildbot/PackageScript | 10 ++++++- extension/extension.cpp | 9 ++++++ extension/extension.h | 5 ++++ extension/forwards.cpp | 59 +++++++++++++++++++++++++++++++++++++++ extension/forwards.h | 11 ++++++++ extension/smsdk_config.h | 2 +- scripting/accelerator.inc | 37 ++++++++++++++++++++++++ 7 files changed, 131 insertions(+), 2 deletions(-) create mode 100644 extension/forwards.cpp create mode 100644 extension/forwards.h create mode 100644 scripting/accelerator.inc diff --git a/buildbot/PackageScript b/buildbot/PackageScript index 8b428e3..6b05a9e 100644 --- a/buildbot/PackageScript +++ b/buildbot/PackageScript @@ -9,7 +9,8 @@ builder.SetBuildFolder('package') folder_list = [ 'addons/sourcemod/extensions', 'addons/sourcemod/gamedata', - 'addons/sourcemod/configs' + 'addons/sourcemod/configs', + 'addons/sourcemod/scripting/include', ] # Are we build a x86_64 extension ? @@ -42,6 +43,13 @@ def CopyDirContent(src, dest): if item.is_file(): builder.AddCopy(item.path, dest_entry) +# Include files +CopyFiles('scripting', 'addons/sourcemod/scripting/include', + [ + 'accelerator.inc', + ] +) + # Copy binaries. for task in Accelerator.extension: if task.target.arch == 'x86_64': diff --git a/extension/extension.cpp b/extension/extension.cpp index 4c85c6d..b36d5e3 100644 --- a/extension/extension.cpp +++ b/extension/extension.cpp @@ -27,6 +27,7 @@ #include #include "MemoryDownloader.h" +#include "forwards.h" #if defined _LINUX #include "client/linux/handler/exception_handler.h" @@ -464,6 +465,7 @@ class UploadThread: public IThread count++; g_pSM->LogError(myself, "Accelerator uploaded crash dump: %s", response); if (log) fprintf(log, "Uploaded crash dump: %s\n", response); + extforwards::CallForward(count, response); } else { failed++; g_pSM->LogError(myself, "Accelerator failed to upload crash dump: %s", response); @@ -1321,6 +1323,7 @@ bool Accelerator::SDK_OnLoad(char *error, size_t maxlength, bool late) void Accelerator::SDK_OnUnload() { + extforwards::Shutdown(); plsys->RemovePluginsListener(this); #if defined _LINUX @@ -1336,6 +1339,12 @@ void Accelerator::SDK_OnUnload() delete handler; } +void Accelerator::SDK_OnAllLoaded() +{ + extforwards::Init(); + sharesys->RegisterLibrary(myself, "accelerator"); +} + void Accelerator::OnCoreMapStart(edict_t *pEdictList, int edictCount, int clientMax) { strncpy(crashMap, gamehelpers->GetCurrentMap(), sizeof(crashMap) - 1); diff --git a/extension/extension.h b/extension/extension.h index d9495ef..a05cdc8 100644 --- a/extension/extension.h +++ b/extension/extension.h @@ -49,6 +49,11 @@ public: // SDKExtension */ virtual void SDK_OnUnload(); + /** + * @brief This is called once all known extensions have been loaded. + */ + virtual void SDK_OnAllLoaded(); + /** * @brief Called on server activation before plugins receive the OnServerLoad forward. * diff --git a/extension/forwards.cpp b/extension/forwards.cpp new file mode 100644 index 0000000..b8c8d9d --- /dev/null +++ b/extension/forwards.cpp @@ -0,0 +1,59 @@ +#include +#include +#include "extension.h" +#include "forwards.h" + +static SourceMod::IForward* s_postuploadfoward = nullptr; + +struct CrashPawnData +{ + int index; + std::string response; +}; + +static void OnCrashUploadedCallback(void* data) +{ + CrashPawnData* crashdata = reinterpret_cast(data); + + if (s_postuploadfoward) + { + s_postuploadfoward->PushCell(crashdata->index); + s_postuploadfoward->PushString(crashdata->response.c_str()); + s_postuploadfoward->PushCell(static_cast(crashdata->response.size())); + s_postuploadfoward->Execute(nullptr); + } + + delete crashdata; +} + +void extforwards::Init() +{ + std::array types = { + SourceMod::ParamType::Param_Cell, + SourceMod::ParamType::Param_String, + SourceMod::ParamType::Param_Cell, + }; + + s_postuploadfoward = forwards->CreateForward("Accelerator_OnCrashUploaded", SourceMod::ExecType::ET_Ignore, 3, types.data()); +} + +void extforwards::Shutdown() +{ + if (s_postuploadfoward) + { + forwards->ReleaseForward(s_postuploadfoward); + s_postuploadfoward = nullptr; + } +} + +void extforwards::CallForward(int crashIndex, const char* response) +{ + CrashPawnData* data = new CrashPawnData; + + data->index = crashIndex; + data->response.assign(response); + + // Accelerator uploads from a secondary thread, calling a forward directly will crash + // this will call the forward from the main thread. + smutils->AddFrameAction(OnCrashUploadedCallback, reinterpret_cast(data)); +} diff --git a/extension/forwards.h b/extension/forwards.h new file mode 100644 index 0000000..ae9bae2 --- /dev/null +++ b/extension/forwards.h @@ -0,0 +1,11 @@ +#ifndef _INCLUDE_FORWARDS_H_ +#define _INCLUDE_FORWARDS_H_ + +namespace extforwards +{ + void Init(); + void Shutdown(); + void CallForward(int crashIndex, const char* response); +} + +#endif // !_INCLUDE_FORWARDS_H_ diff --git a/extension/smsdk_config.h b/extension/smsdk_config.h index 7d7c58e..e034460 100644 --- a/extension/smsdk_config.h +++ b/extension/smsdk_config.h @@ -58,7 +58,7 @@ //#define SMEXT_CONF_METAMOD /** Enable interfaces you want to use here by uncommenting lines */ -//#define SMEXT_ENABLE_FORWARDSYS +#define SMEXT_ENABLE_FORWARDSYS //#define SMEXT_ENABLE_HANDLESYS //#define SMEXT_ENABLE_PLAYERHELPERS //#define SMEXT_ENABLE_DBMANAGER diff --git a/scripting/accelerator.inc b/scripting/accelerator.inc new file mode 100644 index 0000000..1e1424f --- /dev/null +++ b/scripting/accelerator.inc @@ -0,0 +1,37 @@ +/** + * Accelerator's SourcePawn include file + */ + +#if defined _accelerator_included + #endinput +#endif +#define _accelerator_included + + +/** + * Called a crash dump is uploaded. + * + * @param index Index of the crash. + * @param response HTTP response in plain text. + * @param responsesize HTTP response char buffer size. + */ +forward void Accelerator_OnCrashUploaded(int index, const char[] response, int responsesize); + +/** + * Do not edit below this line! + */ +public Extension __ext_accelerator = +{ + name = "accelerator", + file = "accelerator.ext", +#if defined AUTOLOAD_EXTENSIONS + autoload = 1, +#else + autoload = 0, +#endif +#if defined REQUIRE_EXTENSIONS + required = 1, +#else + required = 0, +#endif +}; \ No newline at end of file From f4d60b32755cd029969f6d6e859c70458c015f9f Mon Sep 17 00:00:00 2001 From: caxanga334 <10157643+caxanga334@users.noreply.github.com> Date: Sun, 23 Nov 2025 11:16:14 -0300 Subject: [PATCH 2/7] Update accelerator.inc --- scripting/accelerator.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripting/accelerator.inc b/scripting/accelerator.inc index 1e1424f..0ac5662 100644 --- a/scripting/accelerator.inc +++ b/scripting/accelerator.inc @@ -9,7 +9,7 @@ /** - * Called a crash dump is uploaded. + * Called when a crash dump is uploaded. * * @param index Index of the crash. * @param response HTTP response in plain text. From d0cb652e0d83a1366fe0618d26c94db551d49b2e Mon Sep 17 00:00:00 2001 From: caxanga334 <10157643+caxanga334@users.noreply.github.com> Date: Sun, 23 Nov 2025 11:24:02 -0300 Subject: [PATCH 3/7] Fix: Missing forwards.cpp in AMBuilder --- extension/AMBuilder | 1 + 1 file changed, 1 insertion(+) diff --git a/extension/AMBuilder b/extension/AMBuilder index 1e6f25f..4ff05ee 100644 --- a/extension/AMBuilder +++ b/extension/AMBuilder @@ -7,6 +7,7 @@ project = builder.LibraryProject('accelerator.ext') project.sources = [ 'extension.cpp', 'MemoryDownloader.cpp', + 'forwards.cpp', os.path.join(Accelerator.sm_root, 'public', 'smsdk_ext.cpp') ] From 1a6223ad829d35a54e749a97d5d96f97dbaa9f08 Mon Sep 17 00:00:00 2001 From: caxanga334 <10157643+caxanga334@users.noreply.github.com> Date: Mon, 24 Nov 2025 11:12:05 -0300 Subject: [PATCH 4/7] Refactor API Forward notifies plugins when the extension is done uploading. Plugins can fetch data from uploaded crashes via natives. Added example plugin. --- buildbot/PackageScript | 10 +++- extension/AMBuilder | 1 + extension/extension.cpp | 58 +++++++++++++++++- extension/extension.h | 63 ++++++++++++++++++++ extension/forwards.cpp | 49 ++++------------ extension/forwards.h | 6 +- extension/natives.cpp | 54 +++++++++++++++++ extension/natives.h | 10 ++++ scripting/accelerator.inc | 37 ------------ scripting/accelerator_example.sp | 97 +++++++++++++++++++++++++++++++ scripting/include/accelerator.inc | 67 +++++++++++++++++++++ 11 files changed, 375 insertions(+), 77 deletions(-) create mode 100644 extension/natives.cpp create mode 100644 extension/natives.h delete mode 100644 scripting/accelerator.inc create mode 100644 scripting/accelerator_example.sp create mode 100644 scripting/include/accelerator.inc diff --git a/buildbot/PackageScript b/buildbot/PackageScript index 6b05a9e..4894e48 100644 --- a/buildbot/PackageScript +++ b/buildbot/PackageScript @@ -10,6 +10,7 @@ folder_list = [ 'addons/sourcemod/extensions', 'addons/sourcemod/gamedata', 'addons/sourcemod/configs', + 'addons/sourcemod/scripting', 'addons/sourcemod/scripting/include', ] @@ -44,12 +45,19 @@ def CopyDirContent(src, dest): builder.AddCopy(item.path, dest_entry) # Include files -CopyFiles('scripting', 'addons/sourcemod/scripting/include', +CopyFiles('scripting/include', 'addons/sourcemod/scripting/include', [ 'accelerator.inc', ] ) +# Example files +CopyFiles('scripting', 'addons/sourcemod/scripting', + [ + 'accelerator_example.sp', + ] +) + # Copy binaries. for task in Accelerator.extension: if task.target.arch == 'x86_64': diff --git a/extension/AMBuilder b/extension/AMBuilder index 4ff05ee..0d6126d 100644 --- a/extension/AMBuilder +++ b/extension/AMBuilder @@ -8,6 +8,7 @@ project.sources = [ 'extension.cpp', 'MemoryDownloader.cpp', 'forwards.cpp', + 'natives.cpp', os.path.join(Accelerator.sm_root, 'public', 'smsdk_ext.cpp') ] diff --git a/extension/extension.cpp b/extension/extension.cpp index b36d5e3..e520f5d 100644 --- a/extension/extension.cpp +++ b/extension/extension.cpp @@ -28,6 +28,7 @@ #include #include "MemoryDownloader.h" #include "forwards.h" +#include "natives.h" #if defined _LINUX #include "client/linux/handler/exception_handler.h" @@ -465,7 +466,8 @@ class UploadThread: public IThread count++; g_pSM->LogError(myself, "Accelerator uploaded crash dump: %s", response); if (log) fprintf(log, "Uploaded crash dump: %s\n", response); - extforwards::CallForward(count, response); + UploadedCrash crash{ response }; + g_accelerator.StoreUploadedCrash(crash); } else { failed++; g_pSM->LogError(myself, "Accelerator failed to upload crash dump: %s", response); @@ -497,6 +499,7 @@ class UploadThread: public IThread log = nullptr; } + g_accelerator.MarkAsDoneUploading(); rootconsole->ConsolePrint("Accelerator upload thread finished. (%d skipped, %d uploaded, %d failed)", skip, count, failed); } @@ -1082,6 +1085,25 @@ class UploadThread: public IThread } } uploadThread; +class SourcePawnNotifyThread : public IThread +{ +public: + + void RunThread(IThreadHandle* pHandle) { + for (;;) { + // Wait until OnMapStart is called once, this should be enough delay to make sure plugins are loaded. + if (g_accelerator.IsMapStarted() && g_accelerator.IsDoneUploading()) { + extforwards::CallOnDoneUploadingForward(); + break; + } + } + } + + void OnTerminate(IThreadHandle* pHandle, bool cancel) { + } + +} spNotifyThread; + class VFuncEmptyClass {}; const char *GetCmdLine() @@ -1119,6 +1141,11 @@ const char *GetCmdLine() return (const char *)(reinterpret_cast(cmdline)->*u.mfpnew)(); } +Accelerator::Accelerator() : + m_doneuploading(false), m_maphasstarted(false) +{ +} + bool Accelerator::SDK_OnLoad(char *error, size_t maxlength, bool late) { sharesys->AddDependency(myself, "webternet.ext", true, true); @@ -1144,6 +1171,7 @@ bool Accelerator::SDK_OnLoad(char *error, size_t maxlength, bool late) strncpy(crashGameDirectory, g_pSM->GetGameFolderName(), sizeof(crashGameDirectory) - 1); threader->MakeThread(&uploadThread); + threader->MakeThread(&spNotifyThread); // This thread waits for accelator to be done uploading and for the first OnMapStart call, then fires a SourceMod forward do { char gameconfigError[256]; @@ -1343,11 +1371,17 @@ void Accelerator::SDK_OnAllLoaded() { extforwards::Init(); sharesys->RegisterLibrary(myself, "accelerator"); + + natives::SetupNatives(m_natives); + m_natives.push_back({ nullptr, nullptr }); // SM requires this to signal the end of the native info array + + sharesys->AddNatives(myself, m_natives.data()); } void Accelerator::OnCoreMapStart(edict_t *pEdictList, int edictCount, int clientMax) { strncpy(crashMap, gamehelpers->GetCurrentMap(), sizeof(crashMap) - 1); + m_maphasstarted.store(true); } /* 010 Editor Template @@ -1508,3 +1542,25 @@ void Accelerator::OnPluginUnloaded(IPlugin *plugin) SerializePluginContexts(); } + +void Accelerator::StoreUploadedCrash(UploadedCrash& crash) +{ + std::lock_guard lock(m_uploadedcrashes_mutex); + m_uploadedcrashes.push_back(std::move(crash)); +} + +const UploadedCrash* Accelerator::GetUploadedCrash(int element) const +{ + std::lock_guard lock(m_uploadedcrashes_mutex); + if (element < 0 || element >= static_cast(m_uploadedcrashes.size())) { + return nullptr; + } + + return &m_uploadedcrashes[element]; +} + +cell_t Accelerator::GetUploadedCrashCount() const +{ + std::lock_guard lock(m_uploadedcrashes_mutex); + return static_cast(m_uploadedcrashes.size()); +} diff --git a/extension/extension.h b/extension/extension.h index a05cdc8..0412a7c 100644 --- a/extension/extension.h +++ b/extension/extension.h @@ -26,14 +26,36 @@ * @brief Accelerator extension code header. */ +#include +#include +#include #include "smsdk_ext.h" +/** + * @brief Represents a crash that has been successfully uploaded to Accelerator's backend + */ +class UploadedCrash +{ +public: + UploadedCrash(const char* response) : + m_httpresponse(response) + { + } + + const std::string& GetHTTPResponse() const { return m_httpresponse; } + +private: + std::string m_httpresponse; // HTTP response from the crash upload in free form text. +}; + /** * @brief Sample implementation of the SDK Extension. */ class Accelerator : public SDKExtension, IPluginsListener { public: // SDKExtension + Accelerator(); + /** * @brief This is called after the initial loading sequence has been processed. * @@ -73,6 +95,47 @@ public: // IPluginsListener * @brief Called when a plugin is unloaded (only if fully loaded). */ virtual void OnPluginUnloaded(IPlugin *plugin); + + /** + * @brief Stores an uploaded crash into the vector. + * @param crash Uploaded crash instance to store. + */ + void StoreUploadedCrash(UploadedCrash& crash); + /** + * @brief Retrieves an uploaded crash from the given element. + * @param element Vector element (index) to read. + * @return Upload crash if found or NULL if out of bounds. + */ + const UploadedCrash* GetUploadedCrash(int element) const; + /** + * @brief Returns the number of uploaded crashes (for SourcePawn plugins). + * @return Number of uploaded crashes. + */ + cell_t GetUploadedCrashCount() const; + /** + * @brief Signals the extension that uploading is done. + */ + void MarkAsDoneUploading() { m_doneuploading.store(true); } + /** + * @brief Is Accelerator done uploading crashes. + * @return Returns true if yes, false otherwise. + */ + bool IsDoneUploading() const { return m_doneuploading.load(); } + /** + * @brief Has the 'OnMapStart' function called at least once. + * @return True if yes, false otherwise. + */ + bool IsMapStarted() const { return m_maphasstarted.load(); } + +private: + std::vector m_uploadedcrashes; // Vector of uploaded crashes + std::vector m_natives; // Vector of SourcePawn natives + mutable std::mutex m_uploadedcrashes_mutex; // mutex for accessing the m_uploadedcrashes vector + std::atomic_bool m_doneuploading; // Signals that Accelerator is done uploading crashes. + std::atomic_bool m_maphasstarted; // Signals that OnMapStart has been called at least once. }; +// Expose the extension singleton. +extern Accelerator g_accelerator; + #endif // _INCLUDE_SOURCEMOD_EXTENSION_PROPER_H_ diff --git a/extension/forwards.cpp b/extension/forwards.cpp index b8c8d9d..f73222d 100644 --- a/extension/forwards.cpp +++ b/extension/forwards.cpp @@ -3,57 +3,32 @@ #include "extension.h" #include "forwards.h" -static SourceMod::IForward* s_postuploadfoward = nullptr; -struct CrashPawnData -{ - int index; - std::string response; -}; +static SourceMod::IForward* s_ondoneuploadingforward = nullptr; -static void OnCrashUploadedCallback(void* data) +static void OnDoneUploadingCallback(void* data) { - CrashPawnData* crashdata = reinterpret_cast(data); - - if (s_postuploadfoward) - { - s_postuploadfoward->PushCell(crashdata->index); - s_postuploadfoward->PushString(crashdata->response.c_str()); - s_postuploadfoward->PushCell(static_cast(crashdata->response.size())); - s_postuploadfoward->Execute(nullptr); + if (s_ondoneuploadingforward) { + s_ondoneuploadingforward->Execute(); } - - delete crashdata; } + void extforwards::Init() { - std::array types = { - SourceMod::ParamType::Param_Cell, - SourceMod::ParamType::Param_String, - SourceMod::ParamType::Param_Cell, - }; - - s_postuploadfoward = forwards->CreateForward("Accelerator_OnCrashUploaded", SourceMod::ExecType::ET_Ignore, 3, types.data()); + s_ondoneuploadingforward = forwards->CreateForward("Accelerator_OnDoneUploadingCrashes", SourceMod::ExecType::ET_Ignore, 0, nullptr); } void extforwards::Shutdown() { - if (s_postuploadfoward) - { - forwards->ReleaseForward(s_postuploadfoward); - s_postuploadfoward = nullptr; + if (s_ondoneuploadingforward) { + forwards->ReleaseForward(s_ondoneuploadingforward); + s_ondoneuploadingforward = nullptr; } } -void extforwards::CallForward(int crashIndex, const char* response) +void extforwards::CallOnDoneUploadingForward() { - CrashPawnData* data = new CrashPawnData; - - data->index = crashIndex; - data->response.assign(response); - - // Accelerator uploads from a secondary thread, calling a forward directly will crash - // this will call the forward from the main thread. - smutils->AddFrameAction(OnCrashUploadedCallback, reinterpret_cast(data)); + smutils->AddFrameAction(OnDoneUploadingCallback, nullptr); } + diff --git a/extension/forwards.h b/extension/forwards.h index ae9bae2..80321a6 100644 --- a/extension/forwards.h +++ b/extension/forwards.h @@ -3,9 +3,13 @@ namespace extforwards { + // Initialize the sourcepawn forwards. void Init(); + // Shutdown the sourcepawn forwards. void Shutdown(); - void CallForward(int crashIndex, const char* response); + void CallOnDoneUploadingForward(); } + + #endif // !_INCLUDE_FORWARDS_H_ diff --git a/extension/natives.cpp b/extension/natives.cpp new file mode 100644 index 0000000..067a216 --- /dev/null +++ b/extension/natives.cpp @@ -0,0 +1,54 @@ +#include +#include +#include "extension.h" +#include "natives.h" + +static cell_t Native_GetUploadedCrashCount(IPluginContext* context, const cell_t* params) +{ + return g_accelerator.GetUploadedCrashCount(); +} + +static cell_t Native_IsDoneUploadingCrashes(IPluginContext* context, const cell_t* params) +{ + if (g_accelerator.IsDoneUploading()) { + return 1; + } + + return 0; +} + +static cell_t Native_GetCrashHTTPResponse(IPluginContext* context, const cell_t* params) +{ + if (!g_accelerator.IsDoneUploading()) { + context->ReportError("Wait until accelerator is done uploading crashes before accessing crash information!"); + return 0; + } + + int element = static_cast(params[1]); + const UploadedCrash* crash = g_accelerator.GetUploadedCrash(element); + + if (!crash) { + context->ReportError("Crash index %i is invalid!", element); + return 0; + } + + char* buffer; + context->LocalToString(params[2], &buffer); + + size_t maxsize = static_cast(params[3]); + + ke::SafeStrcpy(buffer, maxsize, crash->GetHTTPResponse().c_str()); + + return 0; +} + +void natives::SetupNatives(std::vector& vec) +{ + sp_nativeinfo_t list[] = { + {"Accelerator_GetUploadedCrashCount", Native_GetUploadedCrashCount}, + {"Accelerator_IsDoneUploadingCrashes", Native_IsDoneUploadingCrashes}, + {"Accelerator_GetCrashHTTPResponse", Native_GetCrashHTTPResponse}, + }; + + vec.insert(vec.end(), std::begin(list), std::end(list)); +} diff --git a/extension/natives.h b/extension/natives.h new file mode 100644 index 0000000..de0cfb3 --- /dev/null +++ b/extension/natives.h @@ -0,0 +1,10 @@ +#ifndef _INCLUDE_NATIVES_H_ +#define _INCLUDE_NATIVES_H_ + + +namespace natives +{ + void SetupNatives(std::vector& vec); +} + +#endif // !_INCLUDE_NATIVES_H_ diff --git a/scripting/accelerator.inc b/scripting/accelerator.inc deleted file mode 100644 index 0ac5662..0000000 --- a/scripting/accelerator.inc +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Accelerator's SourcePawn include file - */ - -#if defined _accelerator_included - #endinput -#endif -#define _accelerator_included - - -/** - * Called when a crash dump is uploaded. - * - * @param index Index of the crash. - * @param response HTTP response in plain text. - * @param responsesize HTTP response char buffer size. - */ -forward void Accelerator_OnCrashUploaded(int index, const char[] response, int responsesize); - -/** - * Do not edit below this line! - */ -public Extension __ext_accelerator = -{ - name = "accelerator", - file = "accelerator.ext", -#if defined AUTOLOAD_EXTENSIONS - autoload = 1, -#else - autoload = 0, -#endif -#if defined REQUIRE_EXTENSIONS - required = 1, -#else - required = 0, -#endif -}; \ No newline at end of file diff --git a/scripting/accelerator_example.sp b/scripting/accelerator_example.sp new file mode 100644 index 0000000..3042645 --- /dev/null +++ b/scripting/accelerator_example.sp @@ -0,0 +1,97 @@ +#include +#include + +#pragma newdecls required +#pragma semicolon 1 + +bool g_bLoggedCrashes = false; +Handle g_Timer = null; + +public Plugin myinfo = +{ + name = "Accelerator Example", + author = "caxanga334", + description = "Example Accelerator natives plugin.", + version = "1.0.0", + url = "https://github.com/asherkin/accelerator" +}; + + +public void OnPluginStart() +{ + RegAdminCmd("sm_listcrashes", Command_ListCrashes, ADMFLAG_RCON, "List all uploaded crash dumps."); +} + +public void OnMapStart() +{ + if (!g_bLoggedCrashes) + { + if (g_Timer == null) + { + g_Timer = CreateTimer(0.1, Timer_LogCrashes, .flags = TIMER_REPEAT); + } + } +} + +// This is called when the extension is done uploading crashes +public void Accelerator_OnDoneUploadingCrashes() +{ + LogMessage("Accelerator is done uploading crashes!"); +} + +Action Command_ListCrashes(int client, int args) +{ + if (!Accelerator_IsDoneUploadingCrashes()) + { + ReplyToCommand(client, "Accelerator is still uploading crashes, please wait!"); + return Plugin_Handled; + } + + int max = Accelerator_GetUploadedCrashCount(); + + if (max == 0) + { + ReplyToCommand(client, "No crashes were uploaded!"); + return Plugin_Handled; + } + + char buffer[512]; + + for (int i = 0; i < max; i++) + { + Accelerator_GetCrashHTTPResponse(i, buffer, sizeof(buffer)); + ReplyToCommand(client, "Crash #%i: HTTP reponse: \"%s\".", i, buffer); + } + + return Plugin_Handled; +} + +Action Timer_LogCrashes(Handle timer) +{ + if (!Accelerator_IsDoneUploadingCrashes()) + { + return Plugin_Continue; + } + + int max = Accelerator_GetUploadedCrashCount(); + + if (max == 0) + { + LogMessage("No crashes were uploaded!"); + g_Timer = null; + g_bLoggedCrashes = true; + return Plugin_Stop; + } + + char buffer[512]; + + for (int i = 0; i < max; i++) + { + Accelerator_GetCrashHTTPResponse(i, buffer, sizeof(buffer)); + LogMessage("Crash #%i: HTTP reponse: \"%s\".", i, buffer); + } + + g_Timer = null; + g_bLoggedCrashes = true; + return Plugin_Stop; +} \ No newline at end of file diff --git a/scripting/include/accelerator.inc b/scripting/include/accelerator.inc new file mode 100644 index 0000000..6394b29 --- /dev/null +++ b/scripting/include/accelerator.inc @@ -0,0 +1,67 @@ +/** + * Accelerator's SourcePawn include file + */ + +#if defined _accelerator_included + #endinput +#endif +#define _accelerator_included + + +/** + * Called when Accelerator is done uploading crash dumps. + */ +forward void Accelerator_OnDoneUploadingCrashes(); + +/** + * Returns the number of crashes uploaded. + * + * @return Number of crashes uploaded. + */ +native int Accelerator_GetUploadedCrashCount(); + +/** + * Returns if Accelerator is done uploading crashes. + * + * @return True if no more crashes will be uploaded, false if there are still crashes pending upload. + */ +native bool Accelerator_IsDoneUploadingCrashes(); + +/** + * Gets a crash's HTTP response from Accelerator's backend server. + * + * @note The response from the server can be anything and there is no guarantee a crash ID will be present. + * @param index The crash index, starts at 0 and goes up to Accelerator_GetUploadedCrashCount() - 1; + * @param buffer Buffer to store the crash HTTP response. + * @param size Size of the buffer parameter. + * @error Native called before Accelerator_IsDoneUploadingCrashes() returns true or invalid crash index passed. + */ +native void Accelerator_GetCrashHTTPResponse(int index, char[] buffer, int size); + +/** + * Do not edit below this line! + */ +public Extension __ext_accelerator = +{ + name = "accelerator", + file = "accelerator.ext", +#if defined AUTOLOAD_EXTENSIONS + autoload = 1, +#else + autoload = 0, +#endif +#if defined REQUIRE_EXTENSIONS + required = 1, +#else + required = 0, +#endif +}; + +#if !defined REQUIRE_EXTENSIONS +public __ext_accelerator_SetNTVOptional() +{ + MarkNativeAsOptional("Accelerator_GetUploadedCrashCount"); + MarkNativeAsOptional("Accelerator_IsDoneUploadingCrashes"); + MarkNativeAsOptional("Accelerator_GetCrashHTTPResponse"); +} +#endif \ No newline at end of file From d06a0fddc6a29785b395f9a79ed1d046e722005b Mon Sep 17 00:00:00 2001 From: caxanga334 <10157643+caxanga334@users.noreply.github.com> Date: Mon, 24 Nov 2025 11:54:46 -0300 Subject: [PATCH 5/7] Implement Requested Changes --- extension/forwards.cpp | 2 ++ extension/forwards.h | 1 + extension/natives.cpp | 2 +- extension/natives.h | 3 ++- scripting/accelerator_example.sp | 24 ++++++++++++++++++++++-- 5 files changed, 28 insertions(+), 4 deletions(-) diff --git a/extension/forwards.cpp b/extension/forwards.cpp index f73222d..a1fbe3b 100644 --- a/extension/forwards.cpp +++ b/extension/forwards.cpp @@ -29,6 +29,8 @@ void extforwards::Shutdown() void extforwards::CallOnDoneUploadingForward() { + // It's obligatory to use AddFrameAction here because SourcePawn forwards can only be called from the server's main thread. + // AddFrameAction will do this for us and is also thread safe. smutils->AddFrameAction(OnDoneUploadingCallback, nullptr); } diff --git a/extension/forwards.h b/extension/forwards.h index 80321a6..4f0ea1e 100644 --- a/extension/forwards.h +++ b/extension/forwards.h @@ -7,6 +7,7 @@ namespace extforwards void Init(); // Shutdown the sourcepawn forwards. void Shutdown(); + // Calls the on done uploadind forward. (thread safe) void CallOnDoneUploadingForward(); } diff --git a/extension/natives.cpp b/extension/natives.cpp index 067a216..f64c193 100644 --- a/extension/natives.cpp +++ b/extension/natives.cpp @@ -42,7 +42,7 @@ static cell_t Native_GetCrashHTTPResponse(IPluginContext* context, const cell_t* return 0; } -void natives::SetupNatives(std::vector& vec) +void natives::Setup(std::vector& vec) { sp_nativeinfo_t list[] = { {"Accelerator_GetUploadedCrashCount", Native_GetUploadedCrashCount}, diff --git a/extension/natives.h b/extension/natives.h index de0cfb3..f3b78b3 100644 --- a/extension/natives.h +++ b/extension/natives.h @@ -4,7 +4,8 @@ namespace natives { - void SetupNatives(std::vector& vec); + // Called during the load process to setup the extension natives + void Setup(std::vector& vec); } #endif // !_INCLUDE_NATIVES_H_ diff --git a/scripting/accelerator_example.sp b/scripting/accelerator_example.sp index 3042645..9752c93 100644 --- a/scripting/accelerator_example.sp +++ b/scripting/accelerator_example.sp @@ -24,10 +24,12 @@ public void OnPluginStart() public void OnMapStart() { - if (!g_bLoggedCrashes) + // Example of how to fetch crashes automatically. This doesn't have to be on a OnMapStart callback. + if (!g_bLoggedCrashes) // only log crashes once { - if (g_Timer == null) + if (g_Timer == null) // avoid creating multiple timers since this is OnMapStart { + // Create a repeating timer that will query Accelerator and log crashes when it's done uploading. g_Timer = CreateTimer(0.1, Timer_LogCrashes, .flags = TIMER_REPEAT); } } @@ -37,8 +39,25 @@ public void OnMapStart() public void Accelerator_OnDoneUploadingCrashes() { LogMessage("Accelerator is done uploading crashes!"); + + int max = Accelerator_GetUploadedCrashCount(); + + if (max == 0) + { + LogMessage("No crashes were uploaded!"); + return; + } + + char buffer[512]; + + for (int i = 0; i < max; i++) + { + Accelerator_GetCrashHTTPResponse(i, buffer, sizeof(buffer)); + LogMessage("Crash #%i: HTTP reponse: \"%s\".", i, buffer); + } } +// Admin command to list crashes Action Command_ListCrashes(int client, int args) { if (!Accelerator_IsDoneUploadingCrashes()) @@ -68,6 +87,7 @@ Action Command_ListCrashes(int client, int args) Action Timer_LogCrashes(Handle timer) { + // Wait until accelerator is done. if (!Accelerator_IsDoneUploadingCrashes()) { return Plugin_Continue; From 68ba00ff46017758a9a12dcd400673635a911d84 Mon Sep 17 00:00:00 2001 From: caxanga334 <10157643+caxanga334@users.noreply.github.com> Date: Mon, 24 Nov 2025 11:56:40 -0300 Subject: [PATCH 6/7] Fix Error --- extension/extension.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extension/extension.cpp b/extension/extension.cpp index e520f5d..3786d85 100644 --- a/extension/extension.cpp +++ b/extension/extension.cpp @@ -1372,7 +1372,7 @@ void Accelerator::SDK_OnAllLoaded() extforwards::Init(); sharesys->RegisterLibrary(myself, "accelerator"); - natives::SetupNatives(m_natives); + natives::Setup(m_natives); m_natives.push_back({ nullptr, nullptr }); // SM requires this to signal the end of the native info array sharesys->AddNatives(myself, m_natives.data()); From e567441f7afb97a7dec52c9c8ec67dddb692921f Mon Sep 17 00:00:00 2001 From: caxanga334 <10157643+caxanga334@users.noreply.github.com> Date: Mon, 24 Nov 2025 16:43:14 -0300 Subject: [PATCH 7/7] Update Example Plugin Removed timer example. Added example of how to handle plugin late loads. --- scripting/accelerator_example.sp | 47 ++------------------------------ 1 file changed, 3 insertions(+), 44 deletions(-) diff --git a/scripting/accelerator_example.sp b/scripting/accelerator_example.sp index 9752c93..cb10aa8 100644 --- a/scripting/accelerator_example.sp +++ b/scripting/accelerator_example.sp @@ -4,9 +4,6 @@ #pragma newdecls required #pragma semicolon 1 -bool g_bLoggedCrashes = false; -Handle g_Timer = null; - public Plugin myinfo = { name = "Accelerator Example", @@ -20,18 +17,11 @@ public Plugin myinfo = public void OnPluginStart() { RegAdminCmd("sm_listcrashes", Command_ListCrashes, ADMFLAG_RCON, "List all uploaded crash dumps."); -} -public void OnMapStart() -{ - // Example of how to fetch crashes automatically. This doesn't have to be on a OnMapStart callback. - if (!g_bLoggedCrashes) // only log crashes once + // Call the forward manually. (For late loading plugins) + if (Accelerator_IsDoneUploadingCrashes()) { - if (g_Timer == null) // avoid creating multiple timers since this is OnMapStart - { - // Create a repeating timer that will query Accelerator and log crashes when it's done uploading. - g_Timer = CreateTimer(0.1, Timer_LogCrashes, .flags = TIMER_REPEAT); - } + Accelerator_OnDoneUploadingCrashes(); } } @@ -84,34 +74,3 @@ Action Command_ListCrashes(int client, int args) return Plugin_Handled; } - -Action Timer_LogCrashes(Handle timer) -{ - // Wait until accelerator is done. - if (!Accelerator_IsDoneUploadingCrashes()) - { - return Plugin_Continue; - } - - int max = Accelerator_GetUploadedCrashCount(); - - if (max == 0) - { - LogMessage("No crashes were uploaded!"); - g_Timer = null; - g_bLoggedCrashes = true; - return Plugin_Stop; - } - - char buffer[512]; - - for (int i = 0; i < max; i++) - { - Accelerator_GetCrashHTTPResponse(i, buffer, sizeof(buffer)); - LogMessage("Crash #%i: HTTP reponse: \"%s\".", i, buffer); - } - - g_Timer = null; - g_bLoggedCrashes = true; - return Plugin_Stop; -} \ No newline at end of file