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