iw5-mod/src/module/scheduler.cpp

179 lines
3.8 KiB
C++
Raw Normal View History

2019-09-24 05:46:47 -04:00
#include <std_include.hpp>
2022-03-23 09:48:13 -04:00
#include <loader/module_loader.hpp>
#include "game/game.hpp"
2022-03-23 09:48:13 -04:00
#include <utils/hook.hpp>
#include <utils/thread.hpp>
#include <utils/concurrency.hpp>
2022-03-23 09:48:13 -04:00
#include "scheduler.hpp"
2019-09-24 05:46:47 -04:00
namespace
{
constexpr bool cond_continue = false;
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];
}
2019-09-24 05:46:47 -04:00
void scheduler::execute(const pipeline type)
2019-09-24 05:46:47 -04:00
{
assert(type >= 0 && type < pipeline::count);
pipelines[type].execute();
2019-09-24 05:46:47 -04:00
}
void scheduler::r_end_frame_stub()
2019-09-24 05:46:47 -04:00
{
utils::hook::invoke<void>(SELECT_VALUE(0x4193D0, 0x67F840));
execute(pipeline::renderer);
2019-09-24 05:46:47 -04:00
}
2022-04-25 11:07:29 -04:00
void scheduler::g_glass_update_stub()
2019-09-24 05:46:47 -04:00
{
utils::hook::invoke<void>(SELECT_VALUE(0x4E3730, 0x505BB0));
execute(pipeline::server);
2019-09-24 05:46:47 -04:00
}
void scheduler::main_frame_stub()
2019-09-24 05:46:47 -04:00
{
utils::hook::invoke<void>(SELECT_VALUE(0x458600, 0x556470));
execute(pipeline::main);
2019-09-24 05:46:47 -04:00
}
void scheduler::schedule(const std::function<bool()>& callback, const pipeline type,
const std::chrono::milliseconds delay)
2019-09-24 05:46:47 -04:00
{
assert(type >= 0 && type < pipeline::count);
task task;
task.handler = callback;
task.interval = delay;
task.last_call = std::chrono::high_resolution_clock::now();
pipelines[type].add(std::move(task));
2019-09-24 05:46:47 -04:00
}
void scheduler::loop(const std::function<void()>& callback, const pipeline type,
const std::chrono::milliseconds delay)
2019-09-24 05:46:47 -04:00
{
schedule([callback]()
2019-09-24 05:46:47 -04:00
{
callback();
return cond_continue;
}, type, delay);
2019-09-24 05:46:47 -04:00
}
void scheduler::once(const std::function<void()>& callback, const pipeline type,
const std::chrono::milliseconds delay)
2019-09-24 05:46:47 -04:00
{
schedule([callback]
2019-09-24 05:46:47 -04:00
{
callback();
return cond_end;
}, type, delay);
2019-09-24 05:46:47 -04:00
}
void scheduler::post_start()
2019-09-24 05:46:47 -04:00
{
thread = utils::thread::create_named_thread("Async Scheduler", []()
2019-09-24 05:46:47 -04:00
{
while (!kill)
{
execute(pipeline::async);
std::this_thread::sleep_for(10ms);
}
});
2019-09-24 05:46:47 -04:00
}
void scheduler::post_load()
{
utils::hook(SELECT_VALUE(0x44C7DB, 0x55688E), main_frame_stub, HOOK_CALL).install()->quick();
utils::hook(SELECT_VALUE(0x57F7F8, 0x4978E2), r_end_frame_stub, HOOK_CALL).install()->quick();
2022-04-25 11:07:29 -04:00
// Hook a function inside G_RunFrame. Fixes TLS issues
utils::hook(SELECT_VALUE(0x52EFBC, 0x50CEC6), g_glass_update_stub, HOOK_CALL).install()->quick();
2019-09-24 05:46:47 -04:00
}
void scheduler::pre_destroy()
{
kill = true;
if (thread.joinable())
{
thread.join();
}
2019-09-24 05:46:47 -04:00
}
REGISTER_MODULE(scheduler);