diff --git a/src/game/scripting/context.cpp b/src/game/scripting/context.cpp index 17bd822..4e2a27f 100644 --- a/src/game/scripting/context.cpp +++ b/src/game/scripting/context.cpp @@ -5,7 +5,7 @@ namespace game { namespace scripting { - context::context() : executer_(this), parameters_(this), event_handler_(this) + context::context() : executer_(this), scheduler_(this), parameters_(this), event_handler_(this) { context_initializer::initialize(this); } @@ -15,6 +15,11 @@ namespace game return &this->executer_; } + scheduler* context::get_scheduler() + { + return &this->scheduler_; + } + parameters* context::get_parameters() { return &this->parameters_; diff --git a/src/game/scripting/context.hpp b/src/game/scripting/context.hpp index 7e3e99e..ee46c19 100644 --- a/src/game/scripting/context.hpp +++ b/src/game/scripting/context.hpp @@ -1,5 +1,6 @@ #pragma once #include "executer.hpp" +#include "scheduler.hpp" #include "parameters.hpp" #include "event_handler.hpp" @@ -13,12 +14,14 @@ namespace game context(); executer* get_executer(); + scheduler* get_scheduler(); parameters* get_parameters(); event_handler* get_event_handler(); chaiscript::ChaiScript* get_chai(); private: executer executer_; + scheduler scheduler_; parameters parameters_; event_handler event_handler_; chaiscript::ChaiScript chai_; diff --git a/src/game/scripting/event_handler.cpp b/src/game/scripting/event_handler.cpp index 554440b..b5b9690 100644 --- a/src/game/scripting/event_handler.cpp +++ b/src/game/scripting/event_handler.cpp @@ -9,6 +9,11 @@ namespace game { } + void event_handler::run_frame() + { + + } + void event_handler::dispatch(event* event) { try diff --git a/src/game/scripting/event_handler.hpp b/src/game/scripting/event_handler.hpp index 13d1dd0..1da7432 100644 --- a/src/game/scripting/event_handler.hpp +++ b/src/game/scripting/event_handler.hpp @@ -31,6 +31,7 @@ namespace game public: explicit event_handler(context* context); + void run_frame(); void dispatch(event* event); void add_event_listener(const event_listener& listener); diff --git a/src/game/scripting/scheduler.cpp b/src/game/scripting/scheduler.cpp new file mode 100644 index 0000000..3ab272c --- /dev/null +++ b/src/game/scripting/scheduler.cpp @@ -0,0 +1,17 @@ +#include "std_include.hpp" +#include "context.hpp" + +namespace game +{ + namespace scripting + { + scheduler::scheduler(context* context) : context_(context) + { + } + + void scheduler::run_frame() + { + + } + } +} diff --git a/src/game/scripting/scheduler.hpp b/src/game/scripting/scheduler.hpp new file mode 100644 index 0000000..bc662d8 --- /dev/null +++ b/src/game/scripting/scheduler.hpp @@ -0,0 +1,20 @@ +#pragma once + +namespace game +{ + namespace scripting + { + class context; + + class scheduler final + { + public: + explicit scheduler(context* context); + + void run_frame(); + + private: + context* context_; + }; + } +} diff --git a/src/module/scripting.cpp b/src/module/scripting.cpp index 968cf2e..442f189 100644 --- a/src/module/scripting.cpp +++ b/src/module/scripting.cpp @@ -1,131 +1,151 @@ #include +#include "loader/module_loader.hpp" #include "utils/hook.hpp" #include "utils/io.hpp" - +#include "game/scripting/context.hpp" #include "scheduler.hpp" -#include "scripting.hpp" + +class scripting final : public module +{ +public: + void post_load() override + { + start_hook_.initialize(SELECT_VALUE(0x50C575, 0x50D4F2, 0x48A026), &start_execution_stub, HOOK_CALL) // + ->install() // + ->quick(); + + stop_hook_.initialize(SELECT_VALUE(0x528B04, 0x569E46, 0x4F03FA), &stop_execution_stub, HOOK_CALL) // + ->install() // + ->quick(); + + 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(); + + if (game::is_sp()) + { + utils::hook(0x610970, &vm_notify_stub, HOOK_JUMP).install()->quick(); + } + + scheduler::on_frame(std::bind(&scripting::run_frame, this)); + } + + void pre_destroy() override + { + this->scripts_.clear(); + } + +private: + std::vector> scripts_; + + void load_scripts() + { + const auto scripts = utils::io::list_files("open-iw5/scripts/"); + + for (const auto& script : scripts) + { + if (script.substr(script.find_last_of('.') + 1) == "chai") + { + try + { + auto context = std::make_unique(); + 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()); + } + } + } + } + + void start_execution() + { + try + { + this->load_scripts(); + } + catch (std::exception& e) + { + propagate_error(e); + } + } + + void stop_execution() + { + this->scripts_.clear(); + } + + void run_frame() + { + for (const auto& script : this->scripts_) + { + script->get_scheduler()->run_frame(); + } + } + + void dispatch(game::scripting::event* event) + { + for (const auto& script : this->scripts_) + { + script->get_event_handler()->dispatch(event); + } + } + + static utils::hook start_hook_; + static utils::hook stop_hook_; + + static void propagate_error(const std::exception& e) + { + printf("\n******* Script execution error *******\n"); + printf("%s\n", e.what()); + printf("**************************************\n\n"); + + scheduler::error("Script execution error\n(see console for actual details)\n", 5); + } + + static void start_execution_stub() + { + module_loader::get()->start_execution(); + reinterpret_cast(start_hook_.get_original())(); + } + + static void stop_execution_stub() + { + module_loader::get()->stop_execution(); + reinterpret_cast(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 + + //printf("%X: %s\n", e.entity_id, e.name.data()); + + for (auto value = stack; value->type != game::native::SCRIPT_END; --value) + { + e.arguments.emplace_back(*value); + } + + module_loader::get()->dispatch(&e); + } + catch (std::exception& e) + { + propagate_error(e); + } + + game::native::VM_Notify(notify_id, type, stack); + } +}; utils::hook scripting::start_hook_; utils::hook scripting::stop_hook_; -void scripting::post_load() -{ - start_hook_.initialize(SELECT_VALUE(0x50C575, 0x50D4F2, 0x48A026), &start_execution_stub, HOOK_CALL) // - ->install() // - ->quick(); - - stop_hook_.initialize(SELECT_VALUE(0x528B04, 0x569E46, 0x4F03FA), &stop_execution_stub, HOOK_CALL) // - ->install() // - ->quick(); - - 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(); - - if (game::is_sp()) - { - utils::hook(0x610970, &vm_notify_stub, HOOK_JUMP).install()->quick(); - } -} - -void scripting::pre_destroy() -{ - this->scripts_.clear(); -} - -void scripting::load_scripts() -{ - const auto scripts = utils::io::list_files("open-iw5/scripts/"); - - for (const auto& script : scripts) - { - if (script.substr(script.find_last_of('.') + 1) == "chai") - { - try - { - auto context = std::make_unique(); - 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()); - } - } - } -} - -void scripting::start_execution() -{ - try - { - this->load_scripts(); - } - catch (std::exception& e) - { - propagate_error(e); - } -} - -void scripting::stop_execution() -{ - this->scripts_.clear(); -} - -void scripting::dispatch(game::scripting::event* event) -{ - for (const auto& script : this->scripts_) - { - script->get_event_handler()->dispatch(event); - } -} - -void scripting::propagate_error(const std::exception& e) -{ - printf("\n******* Script execution error *******\n"); - printf("%s\n", e.what()); - printf("**************************************\n\n"); - - scheduler::error("Script execution error\n(see console for actual details)\n", 5); -} - -void scripting::start_execution_stub() -{ - module_loader::get()->start_execution(); - reinterpret_cast(start_hook_.get_original())(); -} - -void scripting::stop_execution_stub() -{ - module_loader::get()->stop_execution(); - reinterpret_cast(stop_hook_.get_original())(); -} - -void scripting::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 - - //printf("%X: %s\n", e.entity_id, e.name.data()); - - for (auto value = stack; value->type != game::native::SCRIPT_END; --value) - { - e.arguments.emplace_back(*value); - } - - module_loader::get()->dispatch(&e); - } - catch (std::exception& e) - { - propagate_error(e); - } - - game::native::VM_Notify(notify_id, type, stack); -} - REGISTER_MODULE(scripting) diff --git a/src/module/scripting.hpp b/src/module/scripting.hpp deleted file mode 100644 index 14bbe8b..0000000 --- a/src/module/scripting.hpp +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once -#include "loader/module_loader.hpp" -#include "utils/hook.hpp" -#include "game/scripting/context.hpp" - -class scripting final : public module -{ -public: - void post_load() override; - void pre_destroy() override; - - static void propagate_error(const std::exception& e); - -private: - std::vector> scripts_; - - void load_scripts(); - - void start_execution(); - void stop_execution(); - - void dispatch(game::scripting::event* event); - - static utils::hook start_hook_; - static utils::hook stop_hook_; - - static void start_execution_stub(); - static void stop_execution_stub(); - static void vm_notify_stub(const unsigned int notify_id, const unsigned short type, - game::native::VariableValue* stack); -};