Added base scheduler

This commit is contained in:
ineedbots 2021-06-29 13:06:33 -06:00
parent ff04c81ddd
commit fbeca33cfb
9 changed files with 559 additions and 1 deletions

View File

@ -3,12 +3,162 @@
namespace Components namespace Components
{ {
bool Scheduler::AsyncTerminate;
std::thread Scheduler::AsyncThread;
bool Scheduler::ReadyPassed = false;
Utils::Signal<Scheduler::Callback> Scheduler::ReadySignal;
Utils::Signal<Scheduler::Callback> Scheduler::ShutdownSignal;
Utils::Signal<Scheduler::Callback> Scheduler::FrameSignal;
Utils::Signal<Scheduler::Callback> Scheduler::FrameOnceSignal;
std::vector<Scheduler::DelayedSlot> Scheduler::DelayedSlots;
Utils::Signal<Scheduler::Callback> Scheduler::AsyncFrameSignal;
Utils::Signal<Scheduler::Callback> Scheduler::AsyncFrameOnceSignal;
void Scheduler::Once(Utils::Slot<Scheduler::Callback> callback, bool clientOnly)
{
if (clientOnly && Game::IsDedicated()) return;
Scheduler::FrameOnceSignal.connect(callback);
}
void Scheduler::OnShutdown(Utils::Slot<Scheduler::Callback> callback)
{
Scheduler::ShutdownSignal.connect(callback);
}
void Scheduler::OnFrame(Utils::Slot<Scheduler::Callback> callback, bool clientOnly)
{
if (clientOnly && Game::IsDedicated()) return;
Scheduler::FrameSignal.connect(callback);
}
void Scheduler::OnReady(Utils::Slot<Scheduler::Callback> callback, bool clientOnly)
{
if (clientOnly && Game::IsDedicated()) return;
if (Scheduler::ReadyPassed) Scheduler::Once(callback);
else Scheduler::ReadySignal.connect(callback);
}
void Scheduler::ReadyHandler()
{
if (!false)
{
Scheduler::Once(Scheduler::ReadyHandler);
}
else
{
Scheduler::ReadyPassed = true;
Scheduler::ReadySignal();
Scheduler::ReadySignal.clear();
}
}
void Scheduler::FrameHandler()
{
Scheduler::DelaySignal();
Scheduler::FrameSignal();
Utils::Signal<Scheduler::Callback> copy(Scheduler::FrameOnceSignal);
Scheduler::FrameOnceSignal.clear();
copy();
}
void Scheduler::OnDelay(Utils::Slot<Scheduler::Callback> callback, std::chrono::nanoseconds delay, bool clientOnly)
{
if (clientOnly && Game::IsDedicated()) return;
Scheduler::DelayedSlot slot;
slot.callback = callback;
slot.delay = delay;
Scheduler::DelayedSlots.push_back(slot);
}
void Scheduler::DelaySignal()
{
Utils::Signal<Scheduler::Callback> signal;
for (auto i = Scheduler::DelayedSlots.begin(); i != Scheduler::DelayedSlots.end();)
{
if (i->interval.elapsed(i->delay))
{
signal.connect(i->callback);
i = Scheduler::DelayedSlots.erase(i);
continue;
}
++i;
}
signal();
}
void Scheduler::ShutdownStub(int num)
{
Scheduler::ShutdownSignal();
//Utils::Hook::Call<void(int)>(0x46B370)(num);
}
void Scheduler::OnFrameAsync(Utils::Slot<Scheduler::Callback> callback)
{
Scheduler::AsyncFrameSignal.connect(callback);
}
void Scheduler::OnceAsync(Utils::Slot<Scheduler::Callback> callback)
{
Scheduler::AsyncFrameOnceSignal.connect(callback);
}
Scheduler::Scheduler() Scheduler::Scheduler()
{ {
//MessageBoxA(nullptr, Utils::String::VA("%d", Game::svs), "DEBUG", 0); Scheduler::ReadyPassed = false;
Scheduler::Once(Scheduler::ReadyHandler);
// hook frames,
//Utils::Hook(0x4D697A, Scheduler::ShutdownStub, HOOK_CALL).install()->quick();
if (!Loader::IsPerformingUnitTests())
{
Scheduler::AsyncTerminate = false;
Scheduler::AsyncThread = std::thread([]()
{
while (!Scheduler::AsyncTerminate)
{
Scheduler::AsyncFrameSignal();
Utils::Signal<Scheduler::Callback> copy(Scheduler::AsyncFrameOnceSignal);
Scheduler::AsyncFrameOnceSignal.clear();
copy();
std::this_thread::sleep_for(16ms);
}
});
}
} }
Scheduler::~Scheduler() Scheduler::~Scheduler()
{ {
Scheduler::ReadySignal.clear();
Scheduler::ShutdownSignal.clear();
Scheduler::FrameSignal.clear();
Scheduler::FrameOnceSignal.clear();
Scheduler::DelayedSlots.clear();
Scheduler::AsyncFrameSignal.clear();
Scheduler::AsyncFrameOnceSignal.clear();
Scheduler::ReadyPassed = false;
}
void Scheduler::preDestroy()
{
Scheduler::AsyncTerminate = true;
if (Scheduler::AsyncThread.joinable())
{
Scheduler::AsyncThread.join();
}
} }
} }

View File

@ -5,7 +5,50 @@ namespace Components
class Scheduler : public Component class Scheduler : public Component
{ {
public: public:
typedef void(Callback)();
Scheduler(); Scheduler();
~Scheduler(); ~Scheduler();
void preDestroy() override;
static void OnShutdown(Utils::Slot<Callback> callback);
static void OnFrame(Utils::Slot<Callback> callback, bool clientOnly = false);
static void OnReady(Utils::Slot<Callback> callback, bool clientOnly = false);
static void Once(Utils::Slot<Callback> callback, bool clientOnly = false);
static void OnDelay(Utils::Slot<Callback> callback, std::chrono::nanoseconds delay, bool clientOnly = false);
static void OnFrameAsync(Utils::Slot<Callback> callback);
static void OnceAsync(Utils::Slot<Callback> callback);
static void FrameHandler();
private:
class DelayedSlot
{
public:
std::chrono::nanoseconds delay;
Utils::Time::Interval interval;
Utils::Slot<Callback> callback;
};
static bool AsyncTerminate;
static std::thread AsyncThread;
static Utils::Signal<Callback> FrameSignal;
static Utils::Signal<Callback> FrameOnceSignal;
static std::vector<DelayedSlot> DelayedSlots;
static bool ReadyPassed;
static Utils::Signal<Callback> ReadySignal;
static Utils::Signal<Callback> ShutdownSignal;
static Utils::Signal<Callback> AsyncFrameSignal;
static Utils::Signal<Callback> AsyncFrameOnceSignal;
static void ReadyHandler();
static void DelaySignal();
static void ShutdownStub(int num);
}; };
} }

View File

@ -33,6 +33,12 @@ namespace Game
Player_GetMethod_t* Player_GetMethod; Player_GetMethod_t* Player_GetMethod;
GScr_LoadGameTypeScript_t* GScr_LoadGameTypeScript; GScr_LoadGameTypeScript_t* GScr_LoadGameTypeScript;
G_LoadStructs_t* G_LoadStructs; G_LoadStructs_t* G_LoadStructs;
Sys_Milliseconds_t* Sys_Milliseconds;
bool IsDedicated()
{
return false;
}
void Init(GAMEEXE) void Init(GAMEEXE)
{ {
@ -41,6 +47,7 @@ namespace Game
Scr_LoadScript = ASSIGN(Scr_LoadScript_t*, 0x474D80); Scr_LoadScript = ASSIGN(Scr_LoadScript_t*, 0x474D80);
GScr_LoadGameTypeScript = ASSIGN(GScr_LoadGameTypeScript_t*, 0x503F90); GScr_LoadGameTypeScript = ASSIGN(GScr_LoadGameTypeScript_t*, 0x503F90);
G_LoadStructs = ASSIGN(G_LoadStructs_t*, 0x5118A0); G_LoadStructs = ASSIGN(G_LoadStructs_t*, 0x5118A0);
Sys_Milliseconds = ASSIGN(Sys_Milliseconds_t*, 0x435200);
cls = ASSIGN(clientStatic_t*, 0x68A408); cls = ASSIGN(clientStatic_t*, 0x68A408);

View File

@ -6,6 +6,7 @@
namespace Game namespace Game
{ {
void Init(GAMEEXE); void Init(GAMEEXE);
bool IsDedicated();
extern clientStatic_t* cls; extern clientStatic_t* cls;
extern serverStatic_t* svs; extern serverStatic_t* svs;
@ -42,6 +43,9 @@ namespace Game
typedef void (G_LoadStructs_t)(); typedef void (G_LoadStructs_t)();
extern G_LoadStructs_t* G_LoadStructs; extern G_LoadStructs_t* G_LoadStructs;
typedef int (Sys_Milliseconds_t)();
extern Sys_Milliseconds_t* Sys_Milliseconds;
extern unsigned int Scr_GetFunctionHandle(char*, const char*); extern unsigned int Scr_GetFunctionHandle(char*, const char*);
extern __int16 Scr_ExecThread(int); extern __int16 Scr_ExecThread(int);
extern void RemoveRefToObject(int); extern void RemoveRefToObject(int);

View File

@ -90,6 +90,8 @@ enum GAMEEXE
#include "Utils/Hooking.hpp" #include "Utils/Hooking.hpp"
#include "Utils/Memory.hpp" #include "Utils/Memory.hpp"
#include "Utils/String.hpp" #include "Utils/String.hpp"
#include "Utils/Time.hpp"
#include "Utils/Utils.hpp"
#include "Game/Structs.hpp" #include "Game/Structs.hpp"
#include "Game/Game.hpp" #include "Game/Game.hpp"

42
src/Utils/Time.cpp Normal file
View File

@ -0,0 +1,42 @@
#include "STDInclude.hpp"
namespace Utils
{
namespace Time
{
void Interval::update()
{
this->lastPoint = std::chrono::high_resolution_clock::now();
}
bool Interval::elapsed(std::chrono::nanoseconds nsecs)
{
return ((std::chrono::high_resolution_clock::now() - this->lastPoint) >= nsecs);
}
Point::Point() : lastPoint(Game::Sys_Milliseconds())
{
}
void Point::update()
{
this->lastPoint = Game::Sys_Milliseconds();
}
int Point::diff(Point point)
{
return point.lastPoint - this->lastPoint;
}
bool Point::after(Point point)
{
return this->diff(point) < 0;
}
bool Point::elapsed(int milliseconds)
{
return (Game::Sys_Milliseconds() - this->lastPoint) >= milliseconds;
}
}
}

33
src/Utils/Time.hpp Normal file
View File

@ -0,0 +1,33 @@
#pragma once
namespace Utils
{
namespace Time
{
class Interval
{
protected:
std::chrono::high_resolution_clock::time_point lastPoint;
public:
Interval() : lastPoint(std::chrono::high_resolution_clock::now()) {}
void update();
bool elapsed(std::chrono::nanoseconds nsecs);
};
class Point
{
public:
Point();
void update();
int diff(Point point);
bool after(Point point);
bool elapsed(int milliseconds);
private:
int lastPoint;
};
}
}

148
src/Utils/Utils.cpp Normal file
View File

@ -0,0 +1,148 @@
#include "STDInclude.hpp"
namespace Utils
{
std::string GetMimeType(const std::string& url)
{
wchar_t* mimeType = nullptr;
FindMimeFromData(nullptr, std::wstring(url.begin(), url.end()).data(), nullptr, 0, nullptr, 0, &mimeType, 0);
if (mimeType)
{
std::wstring wMimeType(mimeType);
return std::string(wMimeType.begin(), wMimeType.end());
}
return "application/octet-stream";
}
std::string ParseChallenge(const std::string& data)
{
auto pos = data.find_first_of("\n ");
if (pos == std::string::npos) return data;
return data.substr(0, pos).data();
}
void OutputDebugLastError()
{
DWORD errorMessageID = ::GetLastError();
OutputDebugStringA(Utils::String::VA("Last error code: 0x%08X (%s)\n", errorMessageID, GetLastWindowsError().data()));
}
std::string GetLastWindowsError()
{
DWORD errorMessageID = ::GetLastError();
LPSTR messageBuffer = nullptr;
size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), reinterpret_cast<LPSTR>(&messageBuffer), 0, nullptr);
std::string message(messageBuffer, size);
LocalFree(messageBuffer);
return message;
}
bool IsWineEnvironment()
{
HMODULE hntdll = GetModuleHandleA("ntdll.dll");
if (!hntdll) return false;
return (GetProcAddress(hntdll, "wine_get_version") != nullptr);
}
unsigned long GetParentProcessId()
{
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (hSnapshot == INVALID_HANDLE_VALUE) return 0;
Utils::Memory::Allocator allocator;
allocator.reference(hSnapshot, [](void* handle) { CloseHandle(handle); });
PROCESSENTRY32 pe32;
ZeroMemory(&pe32, sizeof(pe32));
pe32.dwSize = sizeof(pe32);
DWORD pid = GetCurrentProcessId();
while (Process32Next(hSnapshot, &pe32))
{
if (pe32.th32ProcessID == pid)
{
return pe32.th32ParentProcessID;
}
}
return 0;
}
size_t GetModuleSize(HMODULE module)
{
PIMAGE_DOS_HEADER header = PIMAGE_DOS_HEADER(module);
PIMAGE_NT_HEADERS ntHeader = PIMAGE_NT_HEADERS(DWORD(module) + header->e_lfanew);
return ntHeader->OptionalHeader.SizeOfImage;
}
void* GetThreadStartAddress(HANDLE hThread)
{
HMODULE ntdll = Utils::GetNTDLL();
if (!ntdll) return nullptr;
static uint8_t ntQueryInformationThread[] = { 0xB1, 0x8B, 0xAE, 0x8A, 0x9A, 0x8D, 0x86, 0xB6, 0x91, 0x99, 0x90, 0x8D, 0x92, 0x9E, 0x8B, 0x96, 0x90, 0x91, 0xAB, 0x97, 0x8D, 0x9A, 0x9E, 0x9B }; // NtQueryInformationThread
NtQueryInformationThread_t NtQueryInformationThread = NtQueryInformationThread_t(GetProcAddress(ntdll, Utils::String::XOR(std::string(reinterpret_cast<char*>(ntQueryInformationThread), sizeof ntQueryInformationThread), -1).data()));
if (!NtQueryInformationThread) return nullptr;
HANDLE dupHandle, currentProcess = GetCurrentProcess();
if (!DuplicateHandle(currentProcess, hThread, currentProcess, &dupHandle, THREAD_QUERY_INFORMATION, FALSE, 0))
{
SetLastError(ERROR_ACCESS_DENIED);
return nullptr;
}
void* address = nullptr;
NTSTATUS status = NtQueryInformationThread(dupHandle, ThreadQuerySetWin32StartAddress, &address, sizeof(address), nullptr);
CloseHandle(dupHandle);
if (status != 0) return nullptr;
return address;
}
void SetEnvironment()
{
wchar_t exeName[512];
GetModuleFileName(GetModuleHandle(nullptr), exeName, sizeof(exeName) / 2);
wchar_t* exeBaseName = wcsrchr(exeName, L'\\');
exeBaseName[0] = L'\0';
SetCurrentDirectory(exeName);
}
HMODULE GetNTDLL()
{
static uint8_t ntdll[] = { 0x91, 0x8B, 0x9B, 0x93, 0x93, 0xD1, 0x9B, 0x93, 0x93 }; // ntdll.dll
return GetModuleHandleA(Utils::String::XOR(std::string(reinterpret_cast<char*>(ntdll), sizeof ntdll), -1).data());
}
void SafeShellExecute(HWND hwnd, LPCSTR lpOperation, LPCSTR lpFile, LPCSTR lpParameters, LPCSTR lpDirectory, INT nShowCmd)
{
[=]()
{
__try
{
ShellExecuteA(hwnd, lpOperation, lpFile, lpParameters, lpDirectory, nShowCmd);
}
__finally
{}
}();
std::this_thread::yield();
}
void OpenUrl(const std::string& url)
{
SafeShellExecute(nullptr, "open", url.data(), nullptr, nullptr, SW_SHOWNORMAL);
}
bool HasIntercection(unsigned int base1, unsigned int len1, unsigned int base2, unsigned int len2)
{
return !(base1 + len1 <= base2 || base2 + len2 <= base1);
}
}

129
src/Utils/Utils.hpp Normal file
View File

@ -0,0 +1,129 @@
#pragma once
typedef LONG NTSTATUS;
typedef NTSTATUS(NTAPI *NtCreateThreadEx_t)(PHANDLE hThread, ACCESS_MASK DesiredAccess, LPVOID ObjectAttributes, HANDLE ProcessHandle, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, BOOL CreateSuspended, DWORD StackZeroBits, DWORD SizeOfStackCommit, DWORD SizeOfStackReserve, LPVOID lpBytesBuffer);
typedef NTSTATUS(NTAPI* NtQueryInformationThread_t)(HANDLE ThreadHandle, LONG ThreadInformationClass, PVOID ThreadInformation, ULONG ThreadInformationLength, PULONG ReturnLength);
#define ThreadQuerySetWin32StartAddress 9
namespace Utils
{
std::string GetMimeType(const std::string& url);
std::string ParseChallenge(const std::string& data);
void OutputDebugLastError();
std::string GetLastWindowsError();
bool IsWineEnvironment();
unsigned long GetParentProcessId();
size_t GetModuleSize(HMODULE);
void* GetThreadStartAddress(HANDLE hThread);
HMODULE GetNTDLL();
void SetEnvironment();
void OpenUrl(const std::string& url);
bool HasIntercection(unsigned int base1, unsigned int len1, unsigned int base2, unsigned int len2);
template <typename T> inline void RotLeft(T& object, size_t bits)
{
bits %= sizeof(T) * 8;
T sign = 1;
sign = sign << (sizeof(T) * 8 - 1);
bool negative = (object & sign) != 0;
object &= ~sign;
object = (object << bits) | (object >> (sizeof(T) * 8 - bits));
object |= T(negative) << ((sizeof(T) * 8 - 1 + bits) % (sizeof(T) * 8));
}
template <typename T> inline void RotRight(T& object, size_t bits)
{
bits %= (sizeof(T) * 8);
RotLeft<T>(object, ((sizeof(T) * 8) - bits));
}
template <typename T> inline void Merge(std::vector<T>* target, T* source, size_t length)
{
if (source)
{
for (size_t i = 0; i < length; ++i)
{
target->push_back(source[i]);
}
}
}
template <typename T> inline void Merge(std::vector<T>* target, std::vector<T> source)
{
for (auto &entry : source)
{
target->push_back(entry);
}
}
template <typename T> using Slot = std::function<T>;
template <typename T>
class Signal
{
public:
Signal()
{
std::lock_guard<std::recursive_mutex> _(this->mutex);
this->slots.clear();
}
Signal(Signal& obj) : Signal()
{
std::lock_guard<std::recursive_mutex> _(this->mutex);
std::lock_guard<std::recursive_mutex> __(obj.mutex);
Utils::Merge(&this->slots, obj.getSlots());
}
void connect(Slot<T> slot)
{
std::lock_guard<std::recursive_mutex> _(this->mutex);
if (slot)
{
this->slots.push_back(slot);
}
}
void clear()
{
std::lock_guard<std::recursive_mutex> _(this->mutex);
this->slots.clear();
}
std::vector<Slot<T>>& getSlots()
{
return this->slots;
}
template <class ...Args>
void operator()(Args&&... args) const
{
std::lock_guard<std::recursive_mutex> _(this->mutex);
std::vector<Slot<T>> copiedSlots;
Utils::Merge(&copiedSlots, this->slots);
for (auto& slot : copiedSlots)
{
if (slot)
{
slot(std::forward<Args>(args)...);
}
}
}
private:
mutable std::recursive_mutex mutex;
std::vector<Slot<T>> slots;
};
}