Merge pull request #77 from diamante0018/master
Scheduler refactoring [Fix spawnbot]
This commit is contained in:
commit
feb5cf67c1
@ -17,7 +17,7 @@ namespace game
|
|||||||
typedef void (*Cmd_RemoveCommand_t)(const char* cmdName);
|
typedef void (*Cmd_RemoveCommand_t)(const char* cmdName);
|
||||||
extern Cmd_RemoveCommand_t Cmd_RemoveCommand;
|
extern Cmd_RemoveCommand_t Cmd_RemoveCommand;
|
||||||
|
|
||||||
typedef void (*Com_Error_t)(int code, const char* fmt, ...);
|
typedef void (*Com_Error_t)(errorParm_t code, const char* fmt, ...);
|
||||||
extern Com_Error_t Com_Error;
|
extern Com_Error_t Com_Error;
|
||||||
|
|
||||||
typedef void (*DB_LoadXAssets_t)(XZoneInfo* zoneInfo, unsigned int zoneCount, int sync);
|
typedef void (*DB_LoadXAssets_t)(XZoneInfo* zoneInfo, unsigned int zoneCount, int sync);
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#include <std_include.hpp>
|
#include <std_include.hpp>
|
||||||
|
|
||||||
#include "context.hpp"
|
#include "context.hpp"
|
||||||
|
|
||||||
namespace game::scripting
|
namespace game::scripting
|
||||||
@ -13,10 +14,10 @@ namespace game::scripting
|
|||||||
"_event_listener_handle");
|
"_event_listener_handle");
|
||||||
|
|
||||||
chai->add(chaiscript::fun(
|
chai->add(chaiscript::fun(
|
||||||
[](event_listener_handle& lhs, const event_listener_handle& rhs) -> event_listener_handle&
|
[](event_listener_handle& lhs, const event_listener_handle& rhs) -> event_listener_handle&
|
||||||
{
|
{
|
||||||
return lhs = rhs;
|
return lhs = rhs;
|
||||||
}), "=");
|
}), "=");
|
||||||
|
|
||||||
chai->add(chaiscript::fun([this](const event_listener_handle& handle)
|
chai->add(chaiscript::fun([this](const event_listener_handle& handle)
|
||||||
{
|
{
|
||||||
@ -45,77 +46,112 @@ namespace game::scripting
|
|||||||
}
|
}
|
||||||
|
|
||||||
void event_handler::dispatch_to_specific_listeners(event* event,
|
void event_handler::dispatch_to_specific_listeners(event* event,
|
||||||
const std::vector<chaiscript::Boxed_Value>& arguments)
|
const std::vector<chaiscript::Boxed_Value>& arguments)
|
||||||
{
|
{
|
||||||
for (auto listener : this->event_listeners_)
|
this->event_listeners_.access([&](task_list& tasks)
|
||||||
{
|
{
|
||||||
if (listener->event == event->name && listener->entity_id == event->entity_id)
|
for (auto listener = tasks.begin(); listener != tasks.end();)
|
||||||
{
|
{
|
||||||
if (listener->is_volatile)
|
if (listener->event == event->name && listener->entity_id == event->entity_id)
|
||||||
{
|
{
|
||||||
this->event_listeners_.remove(listener);
|
if (listener->is_volatile)
|
||||||
|
{
|
||||||
|
listener = tasks.erase(listener);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
listener->callback(arguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
listener->callback(arguments);
|
++listener;
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void event_handler::dispatch_to_generic_listeners(event* event,
|
void event_handler::dispatch_to_generic_listeners(event* event,
|
||||||
const std::vector<chaiscript::Boxed_Value>& arguments)
|
const std::vector<chaiscript::Boxed_Value>& arguments)
|
||||||
{
|
{
|
||||||
for (auto listener : this->generic_event_listeners_)
|
this->generic_event_listeners_.access([&](generic_task_list& tasks)
|
||||||
{
|
{
|
||||||
if (listener->event == event->name)
|
for (auto listener = tasks.begin(); listener != tasks.end();)
|
||||||
{
|
{
|
||||||
if (listener->is_volatile)
|
if (listener->event == event->name)
|
||||||
{
|
{
|
||||||
this->generic_event_listeners_.remove(listener);
|
if (listener->is_volatile)
|
||||||
|
{
|
||||||
|
listener = tasks.erase(listener);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
listener->callback(entity(this->context_, event->entity_id), arguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
listener->callback(entity(this->context_, event->entity_id), arguments);
|
++listener;
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
event_listener_handle event_handler::add_event_listener(event_listener listener)
|
event_listener_handle event_handler::add_event_listener(event_listener listener)
|
||||||
{
|
{
|
||||||
listener.id = ++this->current_listener_id_;
|
listener.id = ++this->current_listener_id_;
|
||||||
this->event_listeners_.add(listener);
|
this->event_listeners_.access([listener](task_list& tasks)
|
||||||
|
{
|
||||||
|
tasks.emplace_back(std::move(listener));
|
||||||
|
});
|
||||||
return {listener.id};
|
return {listener.id};
|
||||||
}
|
}
|
||||||
|
|
||||||
event_listener_handle event_handler::add_event_listener(generic_event_listener listener)
|
event_listener_handle event_handler::add_event_listener(generic_event_listener listener)
|
||||||
{
|
{
|
||||||
listener.id = ++this->current_listener_id_;
|
listener.id = ++this->current_listener_id_;
|
||||||
this->generic_event_listeners_.add(listener);
|
this->generic_event_listeners_.access([listener](generic_task_list& tasks)
|
||||||
|
{
|
||||||
|
tasks.emplace_back(std::move(listener));
|
||||||
|
});
|
||||||
return {listener.id};
|
return {listener.id};
|
||||||
}
|
}
|
||||||
|
|
||||||
void event_handler::clear()
|
void event_handler::clear()
|
||||||
{
|
{
|
||||||
this->event_listeners_.clear();
|
this->event_listeners_.access([](task_list& tasks)
|
||||||
this->generic_event_listeners_.clear();
|
{
|
||||||
|
tasks.clear();
|
||||||
|
});
|
||||||
|
|
||||||
|
this->generic_event_listeners_.access([](generic_task_list& tasks)
|
||||||
|
{
|
||||||
|
tasks.clear();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void event_handler::remove(const event_listener_handle& handle)
|
void event_handler::remove(const event_listener_handle& handle)
|
||||||
{
|
{
|
||||||
for (const auto task : this->event_listeners_)
|
this->event_listeners_.access([handle](task_list& tasks)
|
||||||
{
|
{
|
||||||
if (task->id == handle.id)
|
for (auto i = tasks.begin(); i != tasks.end();)
|
||||||
{
|
{
|
||||||
this->event_listeners_.remove(task);
|
if (i->id == handle.id)
|
||||||
return;
|
{
|
||||||
}
|
i = tasks.erase(i);
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (const auto task : this->generic_event_listeners_)
|
++i;
|
||||||
{
|
|
||||||
if (task->id == handle.id)
|
|
||||||
{
|
|
||||||
this->generic_event_listeners_.remove(task);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
|
this->generic_event_listeners_.access([handle](generic_task_list& tasks)
|
||||||
|
{
|
||||||
|
for (auto i = tasks.begin(); i != tasks.end();)
|
||||||
|
{
|
||||||
|
if (i->id == handle.id)
|
||||||
|
{
|
||||||
|
i = tasks.erase(i);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "utils/concurrent_list.hpp"
|
#include <utils/concurrency.hpp>
|
||||||
#include "entity.hpp"
|
#include "entity.hpp"
|
||||||
#include "event.hpp"
|
#include "event.hpp"
|
||||||
|
|
||||||
@ -46,8 +46,11 @@ namespace game::scripting
|
|||||||
context* context_;
|
context* context_;
|
||||||
std::atomic_int64_t current_listener_id_ = 0;
|
std::atomic_int64_t current_listener_id_ = 0;
|
||||||
|
|
||||||
utils::concurrent_list<event_listener> event_listeners_;
|
using task_list = std::vector<event_listener>;
|
||||||
utils::concurrent_list<generic_event_listener> generic_event_listeners_;
|
utils::concurrency::container<task_list> event_listeners_;
|
||||||
|
|
||||||
|
using generic_task_list = std::vector<generic_event_listener>;
|
||||||
|
utils::concurrency::container<generic_task_list> generic_event_listeners_;
|
||||||
|
|
||||||
void dispatch_to_specific_listeners(event* event, const std::vector<chaiscript::Boxed_Value>& arguments);
|
void dispatch_to_specific_listeners(event* event, const std::vector<chaiscript::Boxed_Value>& arguments);
|
||||||
void dispatch_to_generic_listeners(event* event, const std::vector<chaiscript::Boxed_Value>& arguments);
|
void dispatch_to_generic_listeners(event* event, const std::vector<chaiscript::Boxed_Value>& arguments);
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#include <std_include.hpp>
|
#include <std_include.hpp>
|
||||||
|
|
||||||
#include "context.hpp"
|
#include "context.hpp"
|
||||||
|
|
||||||
namespace game::scripting
|
namespace game::scripting
|
||||||
@ -17,16 +18,16 @@ namespace game::scripting
|
|||||||
}), "=");
|
}), "=");
|
||||||
|
|
||||||
chai->add(chaiscript::fun(
|
chai->add(chaiscript::fun(
|
||||||
[this](const std::function<void()>& callback, const long long milliseconds) -> task_handle
|
[this](const std::function<void()>& callback, const long long milliseconds) -> task_handle
|
||||||
{
|
{
|
||||||
return this->add(callback, milliseconds, true);
|
return this->add(callback, milliseconds, true);
|
||||||
}), "setTimeout");
|
}), "setTimeout");
|
||||||
|
|
||||||
chai->add(chaiscript::fun(
|
chai->add(chaiscript::fun(
|
||||||
[this](const std::function<void()>& callback, const long long milliseconds) -> task_handle
|
[this](const std::function<void()>& callback, const long long milliseconds) -> task_handle
|
||||||
{
|
{
|
||||||
return this->add(callback, milliseconds, false);
|
return this->add(callback, milliseconds, false);
|
||||||
}), "setInterval");
|
}), "setInterval");
|
||||||
|
|
||||||
const auto clear = [this](const task_handle& handle)
|
const auto clear = [this](const task_handle& handle)
|
||||||
{
|
{
|
||||||
@ -40,25 +41,40 @@ namespace game::scripting
|
|||||||
|
|
||||||
void scheduler::run_frame()
|
void scheduler::run_frame()
|
||||||
{
|
{
|
||||||
for (auto task : this->tasks_)
|
this->tasks_.access([&](task_list& tasks)
|
||||||
{
|
{
|
||||||
const auto now = std::chrono::steady_clock::now();
|
for (auto i = tasks.begin(); i != tasks.end();)
|
||||||
if ((now - task->last_execution) > task->delay)
|
|
||||||
{
|
{
|
||||||
task->last_execution = now;
|
const auto now = std::chrono::steady_clock::now();
|
||||||
if (task->is_volatile)
|
const auto diff = now - i->last_execution;
|
||||||
|
|
||||||
|
if (diff < i->delay)
|
||||||
{
|
{
|
||||||
this->tasks_.remove(task);
|
++i;
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
task->callback();
|
i->last_execution = now;
|
||||||
|
|
||||||
|
if (i->is_volatile)
|
||||||
|
{
|
||||||
|
i = tasks.erase(i);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
i->callback();
|
||||||
|
++i;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void scheduler::clear()
|
void scheduler::clear()
|
||||||
{
|
{
|
||||||
this->tasks_.clear();
|
this->tasks_.access([&](task_list& tasks)
|
||||||
|
{
|
||||||
|
tasks.clear();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
task_handle scheduler::add(const std::function<void()>& callback, const long long milliseconds,
|
task_handle scheduler::add(const std::function<void()>& callback, const long long milliseconds,
|
||||||
@ -77,20 +93,28 @@ namespace game::scripting
|
|||||||
task.last_execution = std::chrono::steady_clock::now();
|
task.last_execution = std::chrono::steady_clock::now();
|
||||||
task.id = ++this->current_task_id_;
|
task.id = ++this->current_task_id_;
|
||||||
|
|
||||||
this->tasks_.add(task);
|
this->tasks_.access([&task](task_list& tasks)
|
||||||
|
{
|
||||||
|
tasks.emplace_back(std::move(task));
|
||||||
|
});
|
||||||
|
|
||||||
return {task.id};
|
return {task.id};
|
||||||
}
|
}
|
||||||
|
|
||||||
void scheduler::remove(const task_handle& handle)
|
void scheduler::remove(const task_handle& handle)
|
||||||
{
|
{
|
||||||
for (auto task : this->tasks_)
|
this->tasks_.access([&](task_list& tasks)
|
||||||
{
|
{
|
||||||
if (task->id == handle.id)
|
for (auto i = tasks.begin(); i != tasks.end();)
|
||||||
{
|
{
|
||||||
this->tasks_.remove(task);
|
if (i->id == handle.id)
|
||||||
break;
|
{
|
||||||
|
i = tasks.erase(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
++i;
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "utils/concurrent_list.hpp"
|
#include <utils/concurrency.hpp>
|
||||||
|
|
||||||
namespace game::scripting
|
namespace game::scripting
|
||||||
{
|
{
|
||||||
@ -8,7 +8,7 @@ namespace game::scripting
|
|||||||
class task_handle
|
class task_handle
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
unsigned long long id = 0;
|
std::uint64_t id = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class task final : public task_handle
|
class task final : public task_handle
|
||||||
@ -31,7 +31,8 @@ namespace game::scripting
|
|||||||
private:
|
private:
|
||||||
context* context_;
|
context* context_;
|
||||||
|
|
||||||
utils::concurrent_list<task> tasks_;
|
using task_list = std::vector<task>;
|
||||||
|
utils::concurrency::container<task_list> tasks_;
|
||||||
std::atomic_int64_t current_task_id_ = 0;
|
std::atomic_int64_t current_task_id_ = 0;
|
||||||
|
|
||||||
task_handle add(const std::function<void()>& callback, long long milliseconds, bool is_volatile);
|
task_handle add(const std::function<void()>& callback, long long milliseconds, bool is_volatile);
|
||||||
|
@ -374,6 +374,18 @@ namespace game
|
|||||||
};
|
};
|
||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
|
|
||||||
|
enum errorParm_t
|
||||||
|
{
|
||||||
|
ERR_FATAL = 0x0,
|
||||||
|
ERR_DROP = 0x1,
|
||||||
|
ERR_SERVERDISCONNECT = 0x2,
|
||||||
|
ERR_DISCONNECT = 0x3,
|
||||||
|
ERR_SCRIPT = 0x4,
|
||||||
|
ERR_SCRIPT_DROP = 0x5,
|
||||||
|
ERR_LOCALIZATION = 0x6,
|
||||||
|
ERR_MAPLOADERRORSUMMARY = 0x7
|
||||||
|
};
|
||||||
|
|
||||||
enum svscmd_type
|
enum svscmd_type
|
||||||
{
|
{
|
||||||
SV_CMD_CAN_IGNORE,
|
SV_CMD_CAN_IGNORE,
|
||||||
|
@ -21,7 +21,8 @@ public:
|
|||||||
|
|
||||||
void post_start() override
|
void post_start() override
|
||||||
{
|
{
|
||||||
scheduler::on_frame(std::bind(&console::log_messages, this));
|
scheduler::loop(std::bind(&console::log_messages, this), scheduler::pipeline::main);
|
||||||
|
|
||||||
this->console_runner_ = std::thread(std::bind(&console::runner, this));
|
this->console_runner_ = std::thread(std::bind(&console::runner, this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -24,7 +24,7 @@ public:
|
|||||||
|
|
||||||
Discord_Initialize("531526691319971880", &handlers, 1, nullptr);
|
Discord_Initialize("531526691319971880", &handlers, 1, nullptr);
|
||||||
|
|
||||||
scheduler::on_frame(Discord_RunCallbacks);
|
scheduler::loop(Discord_RunCallbacks, scheduler::pipeline::main);
|
||||||
}
|
}
|
||||||
|
|
||||||
void pre_destroy() override
|
void pre_destroy() override
|
||||||
|
@ -1,103 +1,181 @@
|
|||||||
#include <std_include.hpp>
|
#include <std_include.hpp>
|
||||||
#include <loader/module_loader.hpp>
|
#include <loader/module_loader.hpp>
|
||||||
#include <utils/string.hpp>
|
|
||||||
#include <utils/hook.hpp>
|
#include <utils/hook.hpp>
|
||||||
|
#include <utils/thread.hpp>
|
||||||
|
#include <utils/concurrency.hpp>
|
||||||
|
|
||||||
#include "game/game.hpp"
|
#include "game/game.hpp"
|
||||||
#include "scheduler.hpp"
|
#include "scheduler.hpp"
|
||||||
|
|
||||||
std::mutex scheduler::mutex_;
|
namespace
|
||||||
std::queue<std::pair<std::string, int>> scheduler::errors_;
|
|
||||||
utils::concurrent_list<std::function<void()>> scheduler::callbacks_;
|
|
||||||
utils::concurrent_list<std::function<void()>> scheduler::single_callbacks_;
|
|
||||||
|
|
||||||
void scheduler::on_frame(const std::function<void()>& callback)
|
|
||||||
{
|
{
|
||||||
std::lock_guard _(mutex_);
|
constexpr bool cond_continue = false;
|
||||||
callbacks_.add(callback);
|
constexpr bool cond_end = true;
|
||||||
|
|
||||||
|
struct task
|
||||||
|
{
|
||||||
|
std::function<bool()> handler{};
|
||||||
|
std::chrono::milliseconds interval{};
|
||||||
|
std::chrono::high_resolution_clock::time_point last_call{};
|
||||||
|
};
|
||||||
|
|
||||||
|
using task_list = std::vector<task>;
|
||||||
|
|
||||||
|
class task_pipeline
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void add(task&& task)
|
||||||
|
{
|
||||||
|
new_callbacks_.access([&task](task_list& tasks)
|
||||||
|
{
|
||||||
|
tasks.emplace_back(std::move(task));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void execute()
|
||||||
|
{
|
||||||
|
callbacks_.access([&](task_list& tasks)
|
||||||
|
{
|
||||||
|
this->merge_callbacks();
|
||||||
|
|
||||||
|
for (auto i = tasks.begin(); i != tasks.end();)
|
||||||
|
{
|
||||||
|
const auto now = std::chrono::high_resolution_clock::now();
|
||||||
|
const auto diff = now - i->last_call;
|
||||||
|
|
||||||
|
if (diff < i->interval)
|
||||||
|
{
|
||||||
|
++i;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
i->last_call = now;
|
||||||
|
|
||||||
|
const auto res = i->handler();
|
||||||
|
if (res == cond_end)
|
||||||
|
{
|
||||||
|
i = tasks.erase(i);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
utils::concurrency::container<task_list> new_callbacks_;
|
||||||
|
utils::concurrency::container<task_list, std::recursive_mutex> callbacks_;
|
||||||
|
|
||||||
|
void merge_callbacks()
|
||||||
|
{
|
||||||
|
callbacks_.access([&](task_list& tasks)
|
||||||
|
{
|
||||||
|
new_callbacks_.access([&](task_list& new_tasks)
|
||||||
|
{
|
||||||
|
tasks.insert(tasks.end(), std::move_iterator<task_list::iterator>(new_tasks.begin()), std::move_iterator<task_list::iterator>(new_tasks.end()));
|
||||||
|
new_tasks = {};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
volatile bool kill = false;
|
||||||
|
std::thread thread;
|
||||||
|
task_pipeline pipelines[scheduler::pipeline::count];
|
||||||
}
|
}
|
||||||
|
|
||||||
void scheduler::once(const std::function<void()>& callback)
|
void scheduler::execute(const pipeline type)
|
||||||
{
|
{
|
||||||
std::lock_guard _(mutex_);
|
assert(type >= 0 && type < pipeline::count);
|
||||||
single_callbacks_.add(callback);
|
pipelines[type].execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
void scheduler::error(const std::string& message, int level)
|
void scheduler::r_end_frame_stub()
|
||||||
{
|
{
|
||||||
std::lock_guard _(mutex_);
|
reinterpret_cast<void(*)()>(SELECT_VALUE(0x4193D0, 0x67F840, 0x0))();
|
||||||
errors_.emplace(message, level);
|
execute(pipeline::renderer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void scheduler::frame_stub()
|
void scheduler::g_glass_update_stub()
|
||||||
|
{
|
||||||
|
reinterpret_cast<void(*)()>(SELECT_VALUE(0x4E3730, 0x505BB0, 0x481EA0))();
|
||||||
|
execute(pipeline::server);
|
||||||
|
}
|
||||||
|
|
||||||
|
void scheduler::main_frame_stub()
|
||||||
{
|
{
|
||||||
execute();
|
|
||||||
reinterpret_cast<void(*)()>(SELECT_VALUE(0x458600, 0x556470, 0x4DB070))();
|
reinterpret_cast<void(*)()>(SELECT_VALUE(0x458600, 0x556470, 0x4DB070))();
|
||||||
|
execute(pipeline::main);
|
||||||
}
|
}
|
||||||
|
|
||||||
__declspec(naked) void scheduler::execute()
|
void scheduler::schedule(const std::function<bool()>& callback, const pipeline type,
|
||||||
|
const std::chrono::milliseconds delay)
|
||||||
{
|
{
|
||||||
__asm
|
assert(type >= 0 && type < pipeline::count);
|
||||||
{
|
|
||||||
call execute_error
|
task task;
|
||||||
call execute_safe
|
task.handler = callback;
|
||||||
retn
|
task.interval = delay;
|
||||||
}
|
task.last_call = std::chrono::high_resolution_clock::now();
|
||||||
|
|
||||||
|
pipelines[type].add(std::move(task));
|
||||||
}
|
}
|
||||||
|
|
||||||
void scheduler::execute_safe()
|
void scheduler::loop(const std::function<void()>& callback, const pipeline type,
|
||||||
|
const std::chrono::milliseconds delay)
|
||||||
{
|
{
|
||||||
for (auto callback : callbacks_)
|
schedule([callback]()
|
||||||
{
|
{
|
||||||
(*callback)();
|
callback();
|
||||||
}
|
return cond_continue;
|
||||||
|
}, type, delay);
|
||||||
for (auto callback : single_callbacks_)
|
|
||||||
{
|
|
||||||
single_callbacks_.remove(callback);
|
|
||||||
(*callback)();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void scheduler::execute_error()
|
void scheduler::once(const std::function<void()>& callback, const pipeline type,
|
||||||
|
const std::chrono::milliseconds delay)
|
||||||
{
|
{
|
||||||
const char* message = nullptr;
|
schedule([callback]()
|
||||||
int level = 0;
|
|
||||||
|
|
||||||
if (get_next_error(&message, &level) && message)
|
|
||||||
{
|
{
|
||||||
game::native::Com_Error(level, "%s", message);
|
callback();
|
||||||
}
|
return cond_end;
|
||||||
|
}, type, delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool scheduler::get_next_error(const char** error_message, int* error_level)
|
void scheduler::post_start()
|
||||||
{
|
{
|
||||||
std::lock_guard _(mutex_);
|
thread = utils::thread::create_named_thread("Async Scheduler", []()
|
||||||
if (errors_.empty())
|
|
||||||
{
|
{
|
||||||
*error_message = nullptr;
|
while (!kill)
|
||||||
return false;
|
{
|
||||||
}
|
execute(pipeline::async);
|
||||||
|
std::this_thread::sleep_for(10ms);
|
||||||
const auto error = errors_.front();
|
}
|
||||||
errors_.pop();
|
});
|
||||||
|
|
||||||
*error_level = error.second;
|
|
||||||
*error_message = utils::string::va("%s", error.first.data());
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void scheduler::post_load()
|
void scheduler::post_load()
|
||||||
{
|
{
|
||||||
utils::hook(SELECT_VALUE(0x44C7DB, 0x55688E, 0x4DB324), frame_stub, HOOK_CALL).install()->quick();
|
utils::hook(SELECT_VALUE(0x44C7DB, 0x55688E, 0x4DB324), main_frame_stub, HOOK_CALL).install()->quick();
|
||||||
|
|
||||||
|
if (!game::is_dedi())
|
||||||
|
{
|
||||||
|
utils::hook(SELECT_VALUE(0x57F7F8, 0x4978E2, 0x0), r_end_frame_stub, HOOK_CALL).install()->quick();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hook a function inside G_RunFrame. Fixes TLS issues
|
||||||
|
utils::hook(SELECT_VALUE(0x52EFBC, 0x50CEC6, 0x48B277), g_glass_update_stub, HOOK_CALL).install()->quick();
|
||||||
}
|
}
|
||||||
|
|
||||||
void scheduler::pre_destroy()
|
void scheduler::pre_destroy()
|
||||||
{
|
{
|
||||||
std::lock_guard _(mutex_);
|
kill = true;
|
||||||
callbacks_.clear();
|
if (thread.joinable())
|
||||||
single_callbacks_.clear();
|
{
|
||||||
|
thread.join();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
REGISTER_MODULE(scheduler);
|
REGISTER_MODULE(scheduler);
|
||||||
|
@ -1,27 +1,40 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "utils/concurrent_list.hpp"
|
|
||||||
|
|
||||||
class scheduler final : public module
|
class scheduler final : public module
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static void on_frame(const std::function<void()>& callback);
|
enum pipeline
|
||||||
static void once(const std::function<void()>& callback);
|
{
|
||||||
|
// Asynchronuous pipeline, disconnected from the game
|
||||||
|
async = 0,
|
||||||
|
|
||||||
static void error(const std::string& message, int level);
|
// The game's rendering pipeline
|
||||||
|
renderer,
|
||||||
|
|
||||||
|
// The game's server thread
|
||||||
|
server,
|
||||||
|
|
||||||
|
// The game's main thread
|
||||||
|
main,
|
||||||
|
|
||||||
|
count,
|
||||||
|
};
|
||||||
|
|
||||||
|
void post_start() override;
|
||||||
void post_load() override;
|
void post_load() override;
|
||||||
void pre_destroy() override;
|
void pre_destroy() override;
|
||||||
|
|
||||||
|
static void schedule(const std::function<bool()>& callback, pipeline type = pipeline::async,
|
||||||
|
std::chrono::milliseconds delay = 0ms);
|
||||||
|
static void loop(const std::function<void()>& callback, pipeline type = pipeline::async,
|
||||||
|
std::chrono::milliseconds delay = 0ms);
|
||||||
|
static void once(const std::function<void()>& callback, pipeline type = pipeline::async,
|
||||||
|
std::chrono::milliseconds delay = 0ms);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static std::mutex mutex_;
|
static void execute(const pipeline type);
|
||||||
static std::queue<std::pair<std::string, int>> errors_;
|
|
||||||
static utils::concurrent_list<std::function<void()>> callbacks_;
|
|
||||||
static utils::concurrent_list<std::function<void()>> single_callbacks_;
|
|
||||||
|
|
||||||
static void frame_stub();
|
static void r_end_frame_stub();
|
||||||
|
static void g_glass_update_stub();
|
||||||
static void execute();
|
static void main_frame_stub();
|
||||||
static void execute_safe();
|
|
||||||
static void execute_error();
|
|
||||||
static bool get_next_error(const char** error_message, int* error_level);
|
|
||||||
};
|
};
|
||||||
|
@ -48,7 +48,7 @@ private:
|
|||||||
{
|
{
|
||||||
const auto script_dir = "open-iw5/scripts/"s;
|
const auto script_dir = "open-iw5/scripts/"s;
|
||||||
|
|
||||||
if(!utils::io::directory_exists(script_dir))
|
if (!utils::io::directory_exists(script_dir))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -115,7 +115,10 @@ private:
|
|||||||
printf("%s\n", e.what());
|
printf("%s\n", e.what());
|
||||||
printf("**************************************\n\n");
|
printf("**************************************\n\n");
|
||||||
|
|
||||||
scheduler::error("Script execution error\n(see console for actual details)\n", 5);
|
scheduler::once([]
|
||||||
|
{
|
||||||
|
game::native::Com_Error(game::native::errorParm_t::ERR_SCRIPT, "Script execution error\n(see console for actual details)\n");
|
||||||
|
}, scheduler::pipeline::main);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void start_execution_stub()
|
static void start_execution_stub()
|
||||||
|
@ -1,10 +1,12 @@
|
|||||||
#include <std_include.hpp>
|
#include <std_include.hpp>
|
||||||
#include <loader/module_loader.hpp>
|
#include <loader/module_loader.hpp>
|
||||||
#include <utils/hook.hpp>
|
#include <utils/hook.hpp>
|
||||||
|
#include <utils/string.hpp>
|
||||||
|
|
||||||
#include "game/game.hpp"
|
#include "game/game.hpp"
|
||||||
#include "test_clients.hpp"
|
#include "test_clients.hpp"
|
||||||
#include "command.hpp"
|
#include "command.hpp"
|
||||||
|
#include "scheduler.hpp"
|
||||||
|
|
||||||
bool test_clients::can_add()
|
bool test_clients::can_add()
|
||||||
{
|
{
|
||||||
@ -91,7 +93,7 @@ game::native::gentity_s* test_clients::sv_add_test_client()
|
|||||||
return client->gentity;
|
return client->gentity;
|
||||||
}
|
}
|
||||||
|
|
||||||
void test_clients::spawn()
|
void test_clients::gscr_add_test_client()
|
||||||
{
|
{
|
||||||
const auto* ent = test_clients::sv_add_test_client();
|
const auto* ent = test_clients::sv_add_test_client();
|
||||||
|
|
||||||
@ -101,6 +103,35 @@ void test_clients::spawn()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void test_clients::spawn(const int count)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < count; ++i)
|
||||||
|
{
|
||||||
|
scheduler::once([]()
|
||||||
|
{
|
||||||
|
auto* ent = sv_add_test_client();
|
||||||
|
if (ent == nullptr) return;
|
||||||
|
|
||||||
|
game::native::Scr_AddEntityNum(ent->s.number, 0);
|
||||||
|
scheduler::once([ent]()
|
||||||
|
{
|
||||||
|
game::native::Scr_AddString("autoassign");
|
||||||
|
game::native::Scr_AddString("team_marinesopfor");
|
||||||
|
game::native::Scr_Notify(ent, static_cast<std::uint16_t>(game::native::SL_GetString("menuresponse", 0)), 2);
|
||||||
|
|
||||||
|
scheduler::once([ent]()
|
||||||
|
{
|
||||||
|
game::native::Scr_AddString(utils::string::va("class%i", std::rand() % 5));
|
||||||
|
game::native::Scr_AddString("changeclass");
|
||||||
|
game::native::Scr_Notify(ent, static_cast<std::uint16_t>(game::native::SL_GetString("menuresponse", 0)), 2);
|
||||||
|
}, scheduler::pipeline::server, 2s);
|
||||||
|
|
||||||
|
}, scheduler::pipeline::server, 1s);
|
||||||
|
|
||||||
|
}, scheduler::pipeline::server, 2s * (i + 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void test_clients::scr_shutdown_system_mp_stub(unsigned char sys)
|
void test_clients::scr_shutdown_system_mp_stub(unsigned char sys)
|
||||||
{
|
{
|
||||||
game::native::SV_DropAllBots();
|
game::native::SV_DropAllBots();
|
||||||
@ -158,11 +189,15 @@ void test_clients::post_load()
|
|||||||
if (game::is_mp()) this->patch_mp();
|
if (game::is_mp()) this->patch_mp();
|
||||||
else return; // No sp/dedi bots for now :(
|
else return; // No sp/dedi bots for now :(
|
||||||
|
|
||||||
command::add("spawnBot", []()
|
command::add("spawnBot", [](const command::params& params)
|
||||||
{
|
{
|
||||||
// Because I am unable to expand the scheduler at the moment
|
if (params.size() < 2)
|
||||||
// we only get one bot at the time
|
{
|
||||||
test_clients::spawn();
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto count = std::atoi(params.get(1));
|
||||||
|
test_clients::spawn(count);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -180,7 +215,7 @@ void test_clients::patch_mp()
|
|||||||
utils::hook(0x576DCC, &test_clients::check_timeouts_stub_mp, HOOK_JUMP).install()->quick(); // SV_CheckTimeouts
|
utils::hook(0x576DCC, &test_clients::check_timeouts_stub_mp, HOOK_JUMP).install()->quick(); // SV_CheckTimeouts
|
||||||
|
|
||||||
// Replace nullsubbed gsc func "GScr_AddTestClient" with our spawn
|
// Replace nullsubbed gsc func "GScr_AddTestClient" with our spawn
|
||||||
utils::hook::set<void(*)()>(0x8AC8DC, test_clients::spawn);
|
utils::hook::set<void(*)()>(0x8AC8DC, test_clients::gscr_add_test_client);
|
||||||
}
|
}
|
||||||
|
|
||||||
REGISTER_MODULE(test_clients);
|
REGISTER_MODULE(test_clients);
|
||||||
|
@ -10,7 +10,8 @@ private:
|
|||||||
|
|
||||||
static bool can_add();
|
static bool can_add();
|
||||||
static game::native::gentity_s* sv_add_test_client();
|
static game::native::gentity_s* sv_add_test_client();
|
||||||
static void spawn();
|
static void gscr_add_test_client();
|
||||||
|
static void spawn(int count);
|
||||||
|
|
||||||
static void scr_shutdown_system_mp_stub(unsigned char sys);
|
static void scr_shutdown_system_mp_stub(unsigned char sys);
|
||||||
|
|
||||||
|
46
src/utils/concurrency.hpp
Normal file
46
src/utils/concurrency.hpp
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
namespace utils::concurrency
|
||||||
|
{
|
||||||
|
template <typename T, typename MutexType = std::mutex>
|
||||||
|
class container
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
template <typename R = void, typename F>
|
||||||
|
R access(F&& accessor) const
|
||||||
|
{
|
||||||
|
std::lock_guard<MutexType> _{mutex_};
|
||||||
|
return accessor(object_);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename R = void, typename F>
|
||||||
|
R access(F&& accessor)
|
||||||
|
{
|
||||||
|
std::lock_guard<MutexType> _{mutex_};
|
||||||
|
return accessor(object_);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename R = void, typename F>
|
||||||
|
R access_with_lock(F&& accessor) const
|
||||||
|
{
|
||||||
|
std::unique_lock<MutexType> lock{mutex_};
|
||||||
|
return accessor(object_, lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename R = void, typename F>
|
||||||
|
R access_with_lock(F&& accessor)
|
||||||
|
{
|
||||||
|
std::unique_lock<MutexType> lock{mutex_};
|
||||||
|
return accessor(object_, lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
T& get_raw() { return object_; }
|
||||||
|
const T& get_raw() const { return object_; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
mutable MutexType mutex_{};
|
||||||
|
T object_{};
|
||||||
|
};
|
||||||
|
}
|
@ -1,131 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
namespace utils
|
|
||||||
{
|
|
||||||
template <typename T>
|
|
||||||
class concurrent_list final
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
class element final
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
explicit element(std::recursive_mutex* mutex, std::shared_ptr<T> entry = {},
|
|
||||||
std::shared_ptr<element> next = {}) :
|
|
||||||
mutex_(mutex),
|
|
||||||
entry_(std::move(entry)),
|
|
||||||
next_(std::move(next))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void remove(const std::shared_ptr<T>& element)
|
|
||||||
{
|
|
||||||
std::lock_guard _(*this->mutex_);
|
|
||||||
if (!this->next_) return;
|
|
||||||
|
|
||||||
if (this->next_->entry_.get() == element.get())
|
|
||||||
{
|
|
||||||
this->next_ = this->next_->next_;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this->next_->remove(element);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<element> get_next() const
|
|
||||||
{
|
|
||||||
std::lock_guard _(*this->mutex_);
|
|
||||||
return this->next_;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<T> operator*() const
|
|
||||||
{
|
|
||||||
std::lock_guard _(*this->mutex_);
|
|
||||||
return this->entry_;
|
|
||||||
}
|
|
||||||
|
|
||||||
element& operator++()
|
|
||||||
{
|
|
||||||
std::lock_guard _(*this->mutex_);
|
|
||||||
*this = this->next_ ? *this->next_ : element(this->mutex_);
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
element operator++(int)
|
|
||||||
{
|
|
||||||
std::lock_guard _(*this->mutex_);
|
|
||||||
auto result = *this;
|
|
||||||
this->operator++();
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator==(const element& other)
|
|
||||||
{
|
|
||||||
std::lock_guard _(*this->mutex_);
|
|
||||||
return this->entry_.get() == other.entry_.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator!=(const element& other)
|
|
||||||
{
|
|
||||||
std::lock_guard _(*this->mutex_);
|
|
||||||
return !(*this == other);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::recursive_mutex* mutex_;
|
|
||||||
std::shared_ptr<T> entry_;
|
|
||||||
std::shared_ptr<element> next_;
|
|
||||||
};
|
|
||||||
|
|
||||||
element begin()
|
|
||||||
{
|
|
||||||
std::lock_guard _(this->mutex_);
|
|
||||||
return this->entry_ ? *this->entry_ : this->end();
|
|
||||||
}
|
|
||||||
|
|
||||||
element end()
|
|
||||||
{
|
|
||||||
std::lock_guard _(this->mutex_);
|
|
||||||
return element(&this->mutex_);
|
|
||||||
}
|
|
||||||
|
|
||||||
void remove(const element& entry)
|
|
||||||
{
|
|
||||||
std::lock_guard _(this->mutex_);
|
|
||||||
this->remove(*entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
void remove(const std::shared_ptr<T>& element)
|
|
||||||
{
|
|
||||||
std::lock_guard _(this->mutex_);
|
|
||||||
if (!this->entry_) return;
|
|
||||||
|
|
||||||
if ((**this->entry_).get() == element.get())
|
|
||||||
{
|
|
||||||
this->entry_ = this->entry_->get_next();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this->entry_->remove(element);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void add(const T& object)
|
|
||||||
{
|
|
||||||
std::lock_guard _(this->mutex_);
|
|
||||||
|
|
||||||
const auto object_ptr = std::make_shared<T>(object);
|
|
||||||
this->entry_ = std::make_shared<element>(&this->mutex_, object_ptr, this->entry_);
|
|
||||||
}
|
|
||||||
|
|
||||||
void clear()
|
|
||||||
{
|
|
||||||
std::lock_guard _(this->mutex_);
|
|
||||||
this->entry_ = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::recursive_mutex mutex_;
|
|
||||||
std::shared_ptr<element> entry_;
|
|
||||||
};
|
|
||||||
}
|
|
@ -36,6 +36,19 @@ namespace utils::string
|
|||||||
return text;
|
return text;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::wstring convert(const std::string& str)
|
||||||
|
{
|
||||||
|
std::wstring result;
|
||||||
|
result.reserve(str.size());
|
||||||
|
|
||||||
|
for (const auto& chr : str)
|
||||||
|
{
|
||||||
|
result.push_back(static_cast<wchar_t>(chr));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
std::string dump_hex(const std::string& data, const std::string& separator)
|
std::string dump_hex(const std::string& data, const std::string& separator)
|
||||||
{
|
{
|
||||||
std::string result;
|
std::string result;
|
||||||
|
@ -77,5 +77,7 @@ namespace utils::string
|
|||||||
std::string to_lower(std::string text);
|
std::string to_lower(std::string text);
|
||||||
std::string to_upper(std::string text);
|
std::string to_upper(std::string text);
|
||||||
|
|
||||||
|
std::wstring convert(const std::string& str);
|
||||||
|
|
||||||
std::string dump_hex(const std::string& data, const std::string& separator = " ");
|
std::string dump_hex(const std::string& data, const std::string& separator = " ");
|
||||||
}
|
}
|
||||||
|
130
src/utils/thread.cpp
Normal file
130
src/utils/thread.cpp
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
#include <std_include.hpp>
|
||||||
|
|
||||||
|
#include "thread.hpp"
|
||||||
|
#include "string.hpp"
|
||||||
|
#include "nt.hpp"
|
||||||
|
|
||||||
|
#include <TlHelp32.h>
|
||||||
|
|
||||||
|
#include <gsl/gsl>
|
||||||
|
|
||||||
|
namespace utils::thread
|
||||||
|
{
|
||||||
|
bool set_name(const HANDLE t, const std::string& name)
|
||||||
|
{
|
||||||
|
const nt::library kernel32("kernel32.dll");
|
||||||
|
if (!kernel32)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto set_description = kernel32.get_proc<HRESULT(WINAPI*)(HANDLE, PCWSTR)>("SetThreadDescription");
|
||||||
|
if (!set_description)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SUCCEEDED(set_description(t, string::convert(name).data()));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool set_name(const DWORD id, const std::string& name)
|
||||||
|
{
|
||||||
|
auto* const t = OpenThread(THREAD_SET_LIMITED_INFORMATION, FALSE, id);
|
||||||
|
if (!t) return false;
|
||||||
|
|
||||||
|
const auto _ = gsl::finally([t]()
|
||||||
|
{
|
||||||
|
CloseHandle(t);
|
||||||
|
});
|
||||||
|
|
||||||
|
return set_name(t, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool set_name(std::thread& t, const std::string& name)
|
||||||
|
{
|
||||||
|
return set_name(t.native_handle(), name);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool set_name(const std::string& name)
|
||||||
|
{
|
||||||
|
return set_name(GetCurrentThread(), name);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<DWORD> get_thread_ids()
|
||||||
|
{
|
||||||
|
auto* const h = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, GetCurrentProcessId());
|
||||||
|
if (h == INVALID_HANDLE_VALUE)
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto _ = gsl::finally([h]()
|
||||||
|
{
|
||||||
|
CloseHandle(h);
|
||||||
|
});
|
||||||
|
|
||||||
|
THREADENTRY32 entry{};
|
||||||
|
entry.dwSize = sizeof(entry);
|
||||||
|
if (!Thread32First(h, &entry))
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<DWORD> ids{};
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
const auto check_size = entry.dwSize < FIELD_OFFSET(THREADENTRY32, th32OwnerProcessID)
|
||||||
|
+ sizeof(entry.th32OwnerProcessID);
|
||||||
|
entry.dwSize = sizeof(entry);
|
||||||
|
|
||||||
|
if (check_size && entry.th32OwnerProcessID == GetCurrentProcessId())
|
||||||
|
{
|
||||||
|
ids.emplace_back(entry.th32ThreadID);
|
||||||
|
}
|
||||||
|
} while (Thread32Next(h, &entry));
|
||||||
|
|
||||||
|
return ids;
|
||||||
|
}
|
||||||
|
|
||||||
|
void for_each_thread(const std::function<void(HANDLE)>& callback)
|
||||||
|
{
|
||||||
|
const auto ids = get_thread_ids();
|
||||||
|
|
||||||
|
for (const auto& id : ids)
|
||||||
|
{
|
||||||
|
auto* const thread = OpenThread(THREAD_ALL_ACCESS, FALSE, id);
|
||||||
|
if (thread != nullptr)
|
||||||
|
{
|
||||||
|
const auto _ = gsl::finally([thread]()
|
||||||
|
{
|
||||||
|
CloseHandle(thread);
|
||||||
|
});
|
||||||
|
|
||||||
|
callback(thread);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void suspend_other_threads()
|
||||||
|
{
|
||||||
|
for_each_thread([](const HANDLE thread)
|
||||||
|
{
|
||||||
|
if (GetThreadId(thread) != GetCurrentThreadId())
|
||||||
|
{
|
||||||
|
SuspendThread(thread);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void resume_other_threads()
|
||||||
|
{
|
||||||
|
for_each_thread([](const HANDLE thread)
|
||||||
|
{
|
||||||
|
if (GetThreadId(thread) != GetCurrentThreadId())
|
||||||
|
{
|
||||||
|
ResumeThread(thread);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
24
src/utils/thread.hpp
Normal file
24
src/utils/thread.hpp
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
namespace utils::thread
|
||||||
|
{
|
||||||
|
bool set_name(HANDLE t, const std::string& name);
|
||||||
|
bool set_name(DWORD id, const std::string& name);
|
||||||
|
bool set_name(std::thread& t, const std::string& name);
|
||||||
|
bool set_name(const std::string& name);
|
||||||
|
|
||||||
|
template <typename ...Args>
|
||||||
|
std::thread create_named_thread(const std::string& name, Args&&... args)
|
||||||
|
{
|
||||||
|
auto t = std::thread(std::forward<Args>(args)...);
|
||||||
|
set_name(t, name);
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<DWORD> get_thread_ids();
|
||||||
|
void for_each_thread(const std::function<void(HANDLE)>& callback);
|
||||||
|
|
||||||
|
void suspend_other_threads();
|
||||||
|
void resume_other_threads();
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user