iw5-mod/src/module/scripting.cpp

152 lines
3.4 KiB
C++
Raw Normal View History

2019-01-13 05:16:19 -05:00
#include <std_include.hpp>
2019-01-20 12:29:13 -05:00
#include "loader/module_loader.hpp"
#include "utils/hook.hpp"
2019-01-13 15:28:05 -05:00
#include "utils/io.hpp"
2019-01-20 12:29:13 -05:00
#include "game/scripting/context.hpp"
2019-01-16 10:19:21 -05:00
#include "scheduler.hpp"
2019-01-13 13:03:46 -05:00
2019-01-20 12:29:13 -05:00
class scripting final : public module
2019-01-13 13:03:46 -05:00
{
2019-01-20 12:29:13 -05:00
public:
void post_load() override
{
start_hook_.initialize(SELECT_VALUE(0x50C575, 0x50D4F2, 0x48A026), &start_execution_stub, HOOK_CALL) //
->install() //
->quick();
2019-01-20 12:29:13 -05:00
stop_hook_.initialize(SELECT_VALUE(0x528B04, 0x569E46, 0x4F03FA), &stop_execution_stub, HOOK_CALL) //
->install() //
->quick();
2019-01-13 13:03:46 -05:00
2019-01-20 12:29:13 -05:00
utils::hook(SELECT_VALUE(0x6109F3, 0x56B637, 0x4EDFF7), &vm_notify_stub, HOOK_CALL).install()->quick();
utils::hook(SELECT_VALUE(0x6128BE, 0x56D541, 0x4EFAF9), &vm_notify_stub, HOOK_CALL).install()->quick();
2019-01-20 12:29:13 -05:00
if (game::is_sp())
{
utils::hook(0x610970, &vm_notify_stub, HOOK_JUMP).install()->quick();
}
scheduler::on_frame(std::bind(&scripting::run_frame, this));
}
2019-01-13 15:28:05 -05:00
2019-01-20 12:29:13 -05:00
void pre_destroy() override
{
this->scripts_.clear();
}
2019-01-13 13:03:46 -05:00
2019-01-20 12:29:13 -05:00
private:
std::vector<std::unique_ptr<game::scripting::context>> scripts_;
2019-01-13 15:28:05 -05:00
2019-01-20 12:29:13 -05:00
void load_scripts()
2019-01-13 15:28:05 -05:00
{
2019-01-20 12:29:13 -05:00
const auto scripts = utils::io::list_files("open-iw5/scripts/");
for (const auto& script : scripts)
2019-01-13 15:28:05 -05:00
{
2019-01-20 12:29:13 -05:00
if (script.substr(script.find_last_of('.') + 1) == "chai")
2019-01-13 16:59:01 -05:00
{
2019-01-20 12:29:13 -05:00
try
{
auto context = std::make_unique<game::scripting::context>();
context->get_chai()->eval_file(script);
this->scripts_.push_back(std::move(context));
}
catch (chaiscript::exception::eval_error& e)
{
throw std::runtime_error(e.pretty_print());
}
2019-01-13 15:53:52 -05:00
}
2019-01-13 15:28:05 -05:00
}
}
2019-01-13 13:03:46 -05:00
2019-01-20 12:29:13 -05:00
void start_execution()
{
2019-01-20 12:29:13 -05:00
try
{
this->load_scripts();
}
catch (std::exception& e)
{
propagate_error(e);
}
}
2019-01-20 12:29:13 -05:00
void stop_execution()
{
2019-01-20 12:29:13 -05:00
this->scripts_.clear();
}
2019-01-20 12:29:13 -05:00
void run_frame()
{
for (const auto& script : this->scripts_)
{
script->get_scheduler()->run_frame();
}
}
2019-01-13 13:03:46 -05:00
2019-01-20 12:29:13 -05:00
void dispatch(game::scripting::event* event)
{
2019-01-20 12:29:13 -05:00
for (const auto& script : this->scripts_)
{
script->get_event_handler()->dispatch(event);
}
}
2019-01-13 13:03:46 -05:00
2019-01-20 12:29:13 -05:00
static utils::hook start_hook_;
static utils::hook stop_hook_;
2019-01-16 16:44:50 -05:00
2019-01-20 12:29:13 -05:00
static void propagate_error(const std::exception& e)
{
printf("\n******* Script execution error *******\n");
printf("%s\n", e.what());
printf("**************************************\n\n");
2019-01-16 16:44:50 -05:00
2019-01-20 12:29:13 -05:00
scheduler::error("Script execution error\n(see console for actual details)\n", 5);
}
2019-01-13 13:03:46 -05:00
2019-01-20 12:29:13 -05:00
static void start_execution_stub()
{
module_loader::get<scripting>()->start_execution();
reinterpret_cast<void(*)()>(start_hook_.get_original())();
}
2019-01-13 13:03:46 -05:00
2019-01-20 12:29:13 -05:00
static void stop_execution_stub()
2019-01-13 13:03:46 -05:00
{
2019-01-20 12:29:13 -05:00
module_loader::get<scripting>()->stop_execution();
reinterpret_cast<void(*)()>(stop_hook_.get_original())();
}
static void vm_notify_stub(const unsigned int notify_id, const unsigned short type,
game::native::VariableValue* stack)
{
try
{
game::scripting::event e;
e.name = game::native::SL_ConvertToString(type);
e.entity_id = notify_id;
if (e.name == "touch") return; // Skip that for now
2019-01-20 12:29:13 -05:00
//printf("%X: %s\n", e.entity_id, e.name.data());
2019-01-20 12:29:13 -05:00
for (auto value = stack; value->type != game::native::SCRIPT_END; --value)
{
e.arguments.emplace_back(*value);
}
2019-01-13 06:07:19 -05:00
2019-01-20 12:29:13 -05:00
module_loader::get<scripting>()->dispatch(&e);
}
catch (std::exception& e)
{
2019-01-20 12:29:13 -05:00
propagate_error(e);
}
2019-01-20 12:29:13 -05:00
game::native::VM_Notify(notify_id, type, stack);
2019-01-13 13:03:46 -05:00
}
2019-01-20 12:29:13 -05:00
};
2019-01-20 12:29:13 -05:00
utils::hook scripting::start_hook_;
utils::hook scripting::stop_hook_;
2019-01-13 06:07:19 -05:00
2019-01-13 05:16:19 -05:00
REGISTER_MODULE(scripting)