feat: added native scheduler
This commit is contained in:
22
src/detail/scheduler.hpp
Normal file
22
src/detail/scheduler.hpp
Normal file
@@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
#include <functional>
|
||||
|
||||
namespace fxn
|
||||
{
|
||||
class NativeScheduler
|
||||
{
|
||||
private:
|
||||
struct Impl;
|
||||
Impl* m_Impl;
|
||||
|
||||
private:
|
||||
NativeScheduler();
|
||||
~NativeScheduler();
|
||||
|
||||
public:
|
||||
static NativeScheduler& GetInstance();
|
||||
|
||||
public:
|
||||
void Schedule(const std::function<void()>& task);
|
||||
};
|
||||
}
|
||||
415
src/impl/scheduler.cpp
Normal file
415
src/impl/scheduler.cpp
Normal file
@@ -0,0 +1,415 @@
|
||||
#include <mutex>
|
||||
|
||||
#include <detail/scheduler.hpp>
|
||||
#include <detail/event.hpp>
|
||||
#include <detail/fxn.hpp>
|
||||
|
||||
#include <fxn/utility.hpp>
|
||||
|
||||
#include <blackbase/xorstr.hpp>
|
||||
#include <blackbase/library/library.hpp>
|
||||
#include <blackbase/pattern/matcher.hpp>
|
||||
|
||||
#define RESOURCE_MANAGER_PATTERN xorstr_("48 8B 0D ? ? ? ? 48 85 C9 74 06 48 8B 01 FF")
|
||||
#define RESOURCE_MANAGER_MODULE xorstr_("citizen-resources-gta.dll")
|
||||
|
||||
namespace fxn
|
||||
{
|
||||
namespace
|
||||
{
|
||||
struct scrThreadContext
|
||||
{
|
||||
unsigned int ThreadId;
|
||||
unsigned int ThreadHash;
|
||||
};
|
||||
|
||||
typedef void* GtaThread;
|
||||
|
||||
struct CfxThread
|
||||
{
|
||||
private:
|
||||
GtaThread m_GtaThread;
|
||||
|
||||
public:
|
||||
GtaThread GetGtaThread()
|
||||
{
|
||||
return m_GtaThread;
|
||||
}
|
||||
|
||||
public:
|
||||
virtual ~CfxThread() = default;
|
||||
virtual void Reset() = 0;
|
||||
virtual void DoRun() = 0;
|
||||
virtual void Kill() = 0;
|
||||
|
||||
public:
|
||||
CfxThread()
|
||||
{
|
||||
auto library = blackbase::Library::FindByName(xorstr_("rage-scripting-five.dll"));
|
||||
if (!library.has_value())
|
||||
{
|
||||
throw std::runtime_error(xorstr_("rage-scripting-five.dll not found"));
|
||||
}
|
||||
|
||||
auto constructor = library->GetExport(xorstr_("??0CfxThread@@QEAA@XZ"));
|
||||
if (!constructor.has_value())
|
||||
{
|
||||
throw std::runtime_error(xorstr_("CfxThread constructor not found"));
|
||||
}
|
||||
|
||||
using ctor_t = void(*)(CfxThread*);
|
||||
ctor_t ctor = reinterpret_cast<ctor_t>(constructor->GetAddress());
|
||||
ctor(this);
|
||||
}
|
||||
|
||||
public:
|
||||
void Initialize()
|
||||
{
|
||||
using attach_t = void(*)(CfxThread*);
|
||||
|
||||
static auto attach = []() -> attach_t
|
||||
{
|
||||
auto library = blackbase::Library::FindByName(xorstr_("rage-scripting-five.dll"));
|
||||
if (!library.has_value())
|
||||
{
|
||||
throw std::runtime_error(xorstr_("rage-scripting-five.dll not found"));
|
||||
}
|
||||
|
||||
auto attachExport = library->GetExport(xorstr_("?AttachScriptHandler@CfxThread@@QEAAXXZ"));
|
||||
if (!attachExport.has_value())
|
||||
{
|
||||
throw std::runtime_error(xorstr_("CfxThread::AttachScriptHandler not found"));
|
||||
}
|
||||
|
||||
return reinterpret_cast<attach_t>(attachExport->GetAddress());
|
||||
}();
|
||||
|
||||
if (attach)
|
||||
{
|
||||
attach(this);
|
||||
}
|
||||
}
|
||||
|
||||
void Shutdown()
|
||||
{
|
||||
using detach_t = void(*)(CfxThread*);
|
||||
|
||||
static auto detach = []() -> detach_t
|
||||
{
|
||||
auto library = blackbase::Library::FindByName(xorstr_("rage-scripting-five.dll"));
|
||||
if (!library.has_value())
|
||||
{
|
||||
throw std::runtime_error(xorstr_("rage-scripting-five.dll not found"));
|
||||
}
|
||||
|
||||
auto detachExport = library->GetExport(xorstr_("?DetachScriptHandler@CfxThread@@QEAAXXZ"));
|
||||
if (!detachExport.has_value())
|
||||
{
|
||||
throw std::runtime_error(xorstr_("CfxThread::DetachScriptHandler not found"));
|
||||
}
|
||||
|
||||
return reinterpret_cast<detach_t>(detachExport->GetAddress());
|
||||
}();
|
||||
|
||||
if (detach)
|
||||
{
|
||||
detach(this);
|
||||
}
|
||||
}
|
||||
|
||||
scrThreadContext* GetContext()
|
||||
{
|
||||
using get_context_t = scrThreadContext* (*)(GtaThread);
|
||||
|
||||
static auto get_context = []() -> get_context_t
|
||||
{
|
||||
auto library = blackbase::Library::FindByName(xorstr_("rage-scripting-five.dll"));
|
||||
if (!library.has_value())
|
||||
{
|
||||
throw std::runtime_error(xorstr_("rage-scripting-five.dll not found"));
|
||||
}
|
||||
|
||||
auto getContextExport = library->GetExport(xorstr_("?GetContext@scrThread@rage@@QEAAPEAUscrThreadContext@2@XZ"));
|
||||
if (!getContextExport.has_value())
|
||||
{
|
||||
throw std::runtime_error(xorstr_("GtaThread::GetThreadContext not found"));
|
||||
}
|
||||
|
||||
return reinterpret_cast<get_context_t>(getContextExport->GetAddress());
|
||||
}();
|
||||
|
||||
if (get_context)
|
||||
{
|
||||
return get_context(m_GtaThread);
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void SetScriptName(const char* name)
|
||||
{
|
||||
using set_script_name_t = void(*)(GtaThread, const char*);
|
||||
|
||||
static auto set_script_name = []() -> set_script_name_t
|
||||
{
|
||||
auto library = blackbase::Library::FindByName(xorstr_("rage-scripting-five.dll"));
|
||||
if (!library.has_value())
|
||||
{
|
||||
throw std::runtime_error(xorstr_("rage-scripting-five.dll not found"));
|
||||
}
|
||||
|
||||
auto setScriptNameExport = library->GetExport(xorstr_("?SetScriptName@GtaThread@@QEAAXPEBD@Z"));
|
||||
if (!setScriptNameExport.has_value())
|
||||
{
|
||||
throw std::runtime_error(xorstr_("GtaThread::SetScriptName not found"));
|
||||
}
|
||||
|
||||
return reinterpret_cast<set_script_name_t>(setScriptNameExport->GetAddress());
|
||||
}();
|
||||
|
||||
if (set_script_name)
|
||||
{
|
||||
set_script_name(m_GtaThread, name);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct FxnThread : public CfxThread
|
||||
{
|
||||
public:
|
||||
virtual void Reset() override {}
|
||||
virtual void DoRun() override {}
|
||||
virtual void Kill() override {}
|
||||
|
||||
public:
|
||||
FxnThread() : CfxThread()
|
||||
{
|
||||
auto context = GetContext();
|
||||
context->ThreadId = HashString(xorstr_("FxnThread"));
|
||||
context->ThreadHash = HashString(xorstr_("FxnThread"));
|
||||
|
||||
SetScriptName(xorstr_("FxnThread"));
|
||||
Initialize();
|
||||
}
|
||||
|
||||
~FxnThread() override
|
||||
{
|
||||
Shutdown();
|
||||
}
|
||||
};
|
||||
|
||||
using get_active_thread_t = GtaThread (*)();
|
||||
static get_active_thread_t g_GetActiveThread = nullptr;
|
||||
|
||||
using set_active_thread_t = void (*)(GtaThread);
|
||||
static set_active_thread_t g_SetActiveThread = nullptr;
|
||||
|
||||
struct NativeScope
|
||||
{
|
||||
private:
|
||||
GtaThread m_PreviousThread;
|
||||
|
||||
public:
|
||||
void InitializeFunctions()
|
||||
{
|
||||
if (g_GetActiveThread && g_SetActiveThread)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
auto library = blackbase::Library::FindByName(xorstr_("rage-scripting-five.dll"));
|
||||
if (!library.has_value())
|
||||
{
|
||||
throw std::runtime_error(xorstr_("rage-scripting-five.dll not found"));
|
||||
}
|
||||
|
||||
auto getActiveThreadExport = library->GetExport(xorstr_("?GetActiveThread@scrEngine@rage@@SAPEAVscrThread@2@XZ"));
|
||||
if (!getActiveThreadExport.has_value())
|
||||
{
|
||||
throw std::runtime_error(xorstr_("GtaThread::GetActiveThread not found"));
|
||||
}
|
||||
|
||||
auto setActiveThreadExport = library->GetExport(xorstr_("?SetActiveThread@scrEngine@rage@@SAXPEAVscrThread@2@@Z"));
|
||||
if (!setActiveThreadExport.has_value())
|
||||
{
|
||||
throw std::runtime_error(xorstr_("GtaThread::SetActiveThread not found"));
|
||||
}
|
||||
|
||||
g_GetActiveThread = reinterpret_cast<get_active_thread_t>(getActiveThreadExport->GetAddress());
|
||||
g_SetActiveThread = reinterpret_cast<set_active_thread_t>(setActiveThreadExport->GetAddress());
|
||||
}
|
||||
|
||||
public:
|
||||
NativeScope(FxnThread* fxnThread)
|
||||
{
|
||||
InitializeFunctions();
|
||||
|
||||
m_PreviousThread = g_GetActiveThread();
|
||||
g_SetActiveThread(fxnThread->GetGtaThread());
|
||||
}
|
||||
|
||||
~NativeScope()
|
||||
{
|
||||
g_SetActiveThread(m_PreviousThread);
|
||||
}
|
||||
};
|
||||
|
||||
void* GetResourceManager()
|
||||
{
|
||||
blackbase::Pattern p(RESOURCE_MANAGER_PATTERN);
|
||||
blackbase::Matcher m(RESOURCE_MANAGER_MODULE);
|
||||
|
||||
auto match = m.FindFirst(p);
|
||||
if (!match.has_value())
|
||||
{
|
||||
throw std::runtime_error(xorstr_("Resource Manager pattern not found"));
|
||||
}
|
||||
|
||||
struct RefContainer
|
||||
{
|
||||
void* ptr;
|
||||
};
|
||||
|
||||
auto ref = match->ResolveRelative().As<RefContainer*>();
|
||||
return ref->ptr;
|
||||
}
|
||||
|
||||
fx::fwEvent<>* GetOnTickEvent()
|
||||
{
|
||||
auto resourceManager = GetResourceManager();
|
||||
if (!resourceManager)
|
||||
{
|
||||
throw std::runtime_error(xorstr_("Resource Manager is null"));
|
||||
}
|
||||
|
||||
return reinterpret_cast<fx::fwEvent<>*>(
|
||||
reinterpret_cast<std::uintptr_t>(resourceManager) + 0x20
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
struct NativeScheduler::Impl
|
||||
{
|
||||
std::size_t m_EventCookie = -1;
|
||||
FxnThread* m_FxnThread = nullptr;
|
||||
std::vector<std::function<void()>> m_Tasks;
|
||||
std::mutex m_Mutex;
|
||||
|
||||
Impl()
|
||||
{
|
||||
auto event = GetOnTickEvent();
|
||||
if (!event)
|
||||
{
|
||||
throw std::runtime_error(xorstr_("OnTick event not found"));
|
||||
}
|
||||
|
||||
m_EventCookie = event->Connect([this]()
|
||||
{
|
||||
this->Initialize();
|
||||
|
||||
if (!m_FxnThread)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<std::function<void()>> tasksCopy;
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_Mutex);
|
||||
tasksCopy = std::move(m_Tasks);
|
||||
m_Tasks.clear();
|
||||
}
|
||||
|
||||
NativeScope scope(m_FxnThread);
|
||||
for (const auto& task : tasksCopy)
|
||||
{
|
||||
try
|
||||
{
|
||||
task();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
// Handle exceptions from tasks
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
~Impl()
|
||||
{
|
||||
auto event = GetOnTickEvent();
|
||||
if (event)
|
||||
{
|
||||
event->Disconnect(m_EventCookie);
|
||||
}
|
||||
|
||||
Shutdown();
|
||||
}
|
||||
|
||||
void Schedule(const std::function<void()>& task)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_Mutex);
|
||||
m_Tasks.push_back(task);
|
||||
}
|
||||
|
||||
private:
|
||||
void Initialize()
|
||||
{
|
||||
if (m_FxnThread)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_FxnThread = new FxnThread();
|
||||
|
||||
NativeScope scope(m_FxnThread);
|
||||
|
||||
auto& manager = FxnManager::GetInstance();
|
||||
|
||||
auto handler = manager.GetNativeHandler(0xDB2434E51017FFCC);
|
||||
if (!handler)
|
||||
{
|
||||
throw std::runtime_error(xorstr_("Native handler for 0xDB2434E51017FFCC not found"));
|
||||
}
|
||||
|
||||
fxn::NativeContext context { 0xDB2434E51017FFCC };
|
||||
context.PushArgument<int>(32);
|
||||
context.PushArgument<bool>(false);
|
||||
context.PushArgument<int>(-1);
|
||||
|
||||
handler(&context);
|
||||
}
|
||||
|
||||
void Shutdown()
|
||||
{
|
||||
if (m_FxnThread)
|
||||
{
|
||||
delete m_FxnThread;
|
||||
m_FxnThread = nullptr;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
NativeScheduler& NativeScheduler::GetInstance()
|
||||
{
|
||||
static NativeScheduler instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
NativeScheduler::NativeScheduler()
|
||||
: m_Impl(new Impl())
|
||||
{
|
||||
}
|
||||
|
||||
NativeScheduler::~NativeScheduler()
|
||||
{
|
||||
delete m_Impl;
|
||||
}
|
||||
|
||||
void NativeScheduler::Schedule(const std::function<void()>& task)
|
||||
{
|
||||
m_Impl->Schedule(task);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user