mirror of
https://github.com/asherkin/accelerator.git
synced 2025-12-06 18:08:30 +00:00
Merge e567441f7a into 3ed1266798
This commit is contained in:
commit
d158ec0b46
@ -9,7 +9,9 @@ builder.SetBuildFolder('package')
|
||||
folder_list = [
|
||||
'addons/sourcemod/extensions',
|
||||
'addons/sourcemod/gamedata',
|
||||
'addons/sourcemod/configs'
|
||||
'addons/sourcemod/configs',
|
||||
'addons/sourcemod/scripting',
|
||||
'addons/sourcemod/scripting/include',
|
||||
]
|
||||
|
||||
# Are we build a x86_64 extension ?
|
||||
@ -42,6 +44,20 @@ def CopyDirContent(src, dest):
|
||||
if item.is_file():
|
||||
builder.AddCopy(item.path, dest_entry)
|
||||
|
||||
# Include files
|
||||
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':
|
||||
|
||||
@ -7,6 +7,8 @@ project = builder.LibraryProject('accelerator.ext')
|
||||
project.sources = [
|
||||
'extension.cpp',
|
||||
'MemoryDownloader.cpp',
|
||||
'forwards.cpp',
|
||||
'natives.cpp',
|
||||
os.path.join(Accelerator.sm_root, 'public', 'smsdk_ext.cpp')
|
||||
]
|
||||
|
||||
|
||||
@ -27,6 +27,8 @@
|
||||
|
||||
#include <IWebternet.h>
|
||||
#include "MemoryDownloader.h"
|
||||
#include "forwards.h"
|
||||
#include "natives.h"
|
||||
|
||||
#if defined _LINUX
|
||||
#include "client/linux/handler/exception_handler.h"
|
||||
@ -464,6 +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);
|
||||
UploadedCrash crash{ response };
|
||||
g_accelerator.StoreUploadedCrash(crash);
|
||||
} else {
|
||||
failed++;
|
||||
g_pSM->LogError(myself, "Accelerator failed to upload crash dump: %s", response);
|
||||
@ -495,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);
|
||||
}
|
||||
|
||||
@ -1080,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()
|
||||
@ -1117,6 +1141,11 @@ const char *GetCmdLine()
|
||||
return (const char *)(reinterpret_cast<VFuncEmptyClass*>(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);
|
||||
@ -1142,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];
|
||||
@ -1321,6 +1351,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,9 +1367,21 @@ void Accelerator::SDK_OnUnload()
|
||||
delete handler;
|
||||
}
|
||||
|
||||
void Accelerator::SDK_OnAllLoaded()
|
||||
{
|
||||
extforwards::Init();
|
||||
sharesys->RegisterLibrary(myself, "accelerator");
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
void Accelerator::OnCoreMapStart(edict_t *pEdictList, int edictCount, int clientMax)
|
||||
{
|
||||
strncpy(crashMap, gamehelpers->GetCurrentMap(), sizeof(crashMap) - 1);
|
||||
m_maphasstarted.store(true);
|
||||
}
|
||||
|
||||
/* 010 Editor Template
|
||||
@ -1499,3 +1542,25 @@ void Accelerator::OnPluginUnloaded(IPlugin *plugin)
|
||||
|
||||
SerializePluginContexts();
|
||||
}
|
||||
|
||||
void Accelerator::StoreUploadedCrash(UploadedCrash& crash)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_uploadedcrashes_mutex);
|
||||
m_uploadedcrashes.push_back(std::move(crash));
|
||||
}
|
||||
|
||||
const UploadedCrash* Accelerator::GetUploadedCrash(int element) const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_uploadedcrashes_mutex);
|
||||
if (element < 0 || element >= static_cast<int>(m_uploadedcrashes.size())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return &m_uploadedcrashes[element];
|
||||
}
|
||||
|
||||
cell_t Accelerator::GetUploadedCrashCount() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_uploadedcrashes_mutex);
|
||||
return static_cast<cell_t>(m_uploadedcrashes.size());
|
||||
}
|
||||
|
||||
@ -26,14 +26,36 @@
|
||||
* @brief Accelerator extension code header.
|
||||
*/
|
||||
|
||||
#include <atomic>
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
#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.
|
||||
*
|
||||
@ -49,6 +71,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.
|
||||
*
|
||||
@ -68,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<UploadedCrash> m_uploadedcrashes; // Vector of uploaded crashes
|
||||
std::vector<sp_nativeinfo_t> 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_
|
||||
|
||||
36
extension/forwards.cpp
Normal file
36
extension/forwards.cpp
Normal file
@ -0,0 +1,36 @@
|
||||
#include <array>
|
||||
#include <string>
|
||||
#include "extension.h"
|
||||
#include "forwards.h"
|
||||
|
||||
|
||||
static SourceMod::IForward* s_ondoneuploadingforward = nullptr;
|
||||
|
||||
static void OnDoneUploadingCallback(void* data)
|
||||
{
|
||||
if (s_ondoneuploadingforward) {
|
||||
s_ondoneuploadingforward->Execute();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void extforwards::Init()
|
||||
{
|
||||
s_ondoneuploadingforward = forwards->CreateForward("Accelerator_OnDoneUploadingCrashes", SourceMod::ExecType::ET_Ignore, 0, nullptr);
|
||||
}
|
||||
|
||||
void extforwards::Shutdown()
|
||||
{
|
||||
if (s_ondoneuploadingforward) {
|
||||
forwards->ReleaseForward(s_ondoneuploadingforward);
|
||||
s_ondoneuploadingforward = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
16
extension/forwards.h
Normal file
16
extension/forwards.h
Normal file
@ -0,0 +1,16 @@
|
||||
#ifndef _INCLUDE_FORWARDS_H_
|
||||
#define _INCLUDE_FORWARDS_H_
|
||||
|
||||
namespace extforwards
|
||||
{
|
||||
// Initialize the sourcepawn forwards.
|
||||
void Init();
|
||||
// Shutdown the sourcepawn forwards.
|
||||
void Shutdown();
|
||||
// Calls the on done uploadind forward. (thread safe)
|
||||
void CallOnDoneUploadingForward();
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endif // !_INCLUDE_FORWARDS_H_
|
||||
54
extension/natives.cpp
Normal file
54
extension/natives.cpp
Normal file
@ -0,0 +1,54 @@
|
||||
#include <algorithm>
|
||||
#include <amtl/am-string.h>
|
||||
#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<int>(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<size_t>(params[3]);
|
||||
|
||||
ke::SafeStrcpy(buffer, maxsize, crash->GetHTTPResponse().c_str());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void natives::Setup(std::vector<sp_nativeinfo_t>& 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));
|
||||
}
|
||||
11
extension/natives.h
Normal file
11
extension/natives.h
Normal file
@ -0,0 +1,11 @@
|
||||
#ifndef _INCLUDE_NATIVES_H_
|
||||
#define _INCLUDE_NATIVES_H_
|
||||
|
||||
|
||||
namespace natives
|
||||
{
|
||||
// Called during the load process to setup the extension natives
|
||||
void Setup(std::vector<sp_nativeinfo_t>& vec);
|
||||
}
|
||||
|
||||
#endif // !_INCLUDE_NATIVES_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
|
||||
|
||||
76
scripting/accelerator_example.sp
Normal file
76
scripting/accelerator_example.sp
Normal file
@ -0,0 +1,76 @@
|
||||
#include <sourcemod>
|
||||
#include <accelerator>
|
||||
|
||||
#pragma newdecls required
|
||||
#pragma semicolon 1
|
||||
|
||||
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.");
|
||||
|
||||
// Call the forward manually. (For late loading plugins)
|
||||
if (Accelerator_IsDoneUploadingCrashes())
|
||||
{
|
||||
Accelerator_OnDoneUploadingCrashes();
|
||||
}
|
||||
}
|
||||
|
||||
// This is called when the extension is done uploading crashes
|
||||
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())
|
||||
{
|
||||
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;
|
||||
}
|
||||
67
scripting/include/accelerator.inc
Normal file
67
scripting/include/accelerator.inc
Normal file
@ -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
|
||||
Loading…
Reference in New Issue
Block a user