diff --git a/src/game/scripting/context_initializer.cpp b/src/game/scripting/context_initializer.cpp index 3c57eec..e6a4a95 100644 --- a/src/game/scripting/context_initializer.cpp +++ b/src/game/scripting/context_initializer.cpp @@ -43,7 +43,7 @@ namespace game listener.is_volatile = false; listener.callback = callback; - context->get_event_handler()->add_event_listener(listener); + return context->get_event_handler()->add_event_listener(listener); }), "onNotify"); chai->add(chaiscript::fun([context](const std::string& event, @@ -57,7 +57,7 @@ namespace game listener.is_volatile = is_volatile; listener.callback = callback; - context->get_event_handler()->add_event_listener(listener); + return context->get_event_handler()->add_event_listener(listener); }), "onNotify"); // Notification diff --git a/src/game/scripting/entity.cpp b/src/game/scripting/entity.cpp index ae8ead3..ef21002 100644 --- a/src/game/scripting/entity.cpp +++ b/src/game/scripting/entity.cpp @@ -32,7 +32,7 @@ namespace game } } - void entity::on_notify(const std::string& event, + event_listener_handle entity::on_notify(const std::string& event, const std::function&)>& callback, const bool is_volatile) @@ -44,7 +44,7 @@ namespace game listener.entity_id = this->entity_id_; listener.is_volatile = is_volatile; - this->context_->get_event_handler()->add_event_listener(listener); + return this->context_->get_event_handler()->add_event_listener(listener); } unsigned int entity::get_entity_id() const diff --git a/src/game/scripting/entity.hpp b/src/game/scripting/entity.hpp index b9771b5..a8fa8a0 100644 --- a/src/game/scripting/entity.hpp +++ b/src/game/scripting/entity.hpp @@ -6,6 +6,7 @@ namespace game namespace scripting { class context; + class event_listener_handle; class entity final { @@ -15,7 +16,7 @@ namespace game entity(context* context, unsigned int entity_id); ~entity(); - void on_notify(const std::string& event, + event_listener_handle on_notify(const std::string& event, const std::function&)>& callback, bool is_volatile) const; diff --git a/src/game/scripting/event_handler.cpp b/src/game/scripting/event_handler.cpp index b5b9690..561442a 100644 --- a/src/game/scripting/event_handler.cpp +++ b/src/game/scripting/event_handler.cpp @@ -7,11 +7,21 @@ namespace game { event_handler::event_handler(context* context) : context_(context) { - } + const auto chai = this->context_->get_chai(); - void event_handler::run_frame() - { + chai->add(chaiscript::user_type(), "event_listener_handle"); + chai->add(chaiscript::constructor(), "event_listener_handle"); + chai->add(chaiscript::constructor(), "event_listener_handle"); + chai->add(chaiscript::fun([](event_listener_handle& lhs, const event_listener_handle& rhs) -> event_listener_handle& + { + return lhs = rhs; + }), "="); + + chai->add(chaiscript::fun([this](const event_listener_handle& handle) + { + this->remove(handle); + }), "clear"); } void event_handler::dispatch(event* event) @@ -37,7 +47,7 @@ namespace game void event_handler::dispatch_to_specific_listeners(event* event, const std::vector& arguments) { - for (auto listener = this->event_listeners_.begin(); listener.is_valid(); ++listener) + for (const auto& listener : this->event_listeners_) { if (listener->event == event->name && listener->entity_id == event->entity_id) { @@ -54,7 +64,7 @@ namespace game void event_handler::dispatch_to_generic_listeners(event* event, const std::vector& arguments) { - for (auto listener = this->generic_event_listeners_.begin(); listener.is_valid(); ++listener) + for (const auto& listener : this->generic_event_listeners_) { if (listener->event == event->name) { @@ -68,14 +78,39 @@ namespace game } } - void event_handler::add_event_listener(const event_listener& listener) + event_listener_handle event_handler::add_event_listener(event_listener listener) { + listener.id = ++this->current_listener_id_; this->event_listeners_.add(listener); + return { listener.id }; } - void event_handler::add_event_listener(const generic_event_listener& listener) + event_listener_handle event_handler::add_event_listener(generic_event_listener listener) { + listener.id = ++this->current_listener_id_; this->generic_event_listeners_.add(listener); + return { listener.id }; + } + + void event_handler::remove(const event_listener_handle& handle) + { + for (const auto task : this->event_listeners_) + { + if (task->id == handle.id) + { + this->event_listeners_.remove(task); + return; + } + } + + for (const auto task : this->generic_event_listeners_) + { + if (task->id == handle.id) + { + this->generic_event_listeners_.remove(task); + return; + } + } } } } diff --git a/src/game/scripting/event_handler.hpp b/src/game/scripting/event_handler.hpp index 1da7432..4377fe7 100644 --- a/src/game/scripting/event_handler.hpp +++ b/src/game/scripting/event_handler.hpp @@ -1,5 +1,5 @@ #pragma once -#include "utils/chain.hpp" +#include "utils/concurrent_list.hpp" #include "entity.hpp" #include "event.hpp" @@ -9,7 +9,13 @@ namespace game { class context; - class event_listener final + class event_listener_handle + { + public: + unsigned long long id = 0; + }; + + class event_listener final : public event_listener_handle { public: std::string event = {}; @@ -18,7 +24,7 @@ namespace game bool is_volatile = false; }; - class generic_event_listener final + class generic_event_listener final : public event_listener_handle { public: std::string event = {}; @@ -31,20 +37,22 @@ namespace game public: explicit event_handler(context* context); - void run_frame(); void dispatch(event* event); - void add_event_listener(const event_listener& listener); - void add_event_listener(const generic_event_listener& listener); + event_listener_handle add_event_listener(event_listener listener); + event_listener_handle add_event_listener(generic_event_listener listener); private: context* context_; + std::atomic_int64_t current_listener_id_ = 0; + +utils::concurrent_list event_listeners_; + utils::concurrent_list generic_event_listeners_; void dispatch_to_specific_listeners(event* event, const std::vector& arguments); void dispatch_to_generic_listeners(event* event, const std::vector& arguments); - utils::chain event_listeners_; - utils::chain generic_event_listeners_; + void remove(const event_listener_handle& handle); }; } } diff --git a/src/game/scripting/scheduler.cpp b/src/game/scripting/scheduler.cpp index ec0f1da..b9b7513 100644 --- a/src/game/scripting/scheduler.cpp +++ b/src/game/scripting/scheduler.cpp @@ -40,7 +40,7 @@ namespace game void scheduler::run_frame() { - for (auto task = this->tasks_.begin(); task.is_valid(); ++task) + for (const auto& task : this->tasks_) { const auto now = std::chrono::steady_clock::now(); if ((now - task->last_execution) > task->delay) @@ -79,7 +79,7 @@ namespace game void scheduler::remove(const task_handle& handle) { - for (auto task = this->tasks_.begin(); task.is_valid(); ++task) + for (const auto task : this->tasks_) { if(task->id == handle.id) { diff --git a/src/game/scripting/scheduler.hpp b/src/game/scripting/scheduler.hpp index 5141345..660e6ff 100644 --- a/src/game/scripting/scheduler.hpp +++ b/src/game/scripting/scheduler.hpp @@ -1,5 +1,5 @@ #pragma once -#include "utils/chain.hpp" +#include "utils/concurrent_list.hpp" namespace game { @@ -32,7 +32,7 @@ namespace game private: context* context_; - utils::chain tasks_; + utils::concurrent_list tasks_; std::atomic_int64_t current_task_id_ = 0; task_handle add(const std::function& callback, long long milliseconds, bool is_volatile); diff --git a/src/module/scheduler.cpp b/src/module/scheduler.cpp index fafcc52..d361e46 100644 --- a/src/module/scheduler.cpp +++ b/src/module/scheduler.cpp @@ -6,19 +6,19 @@ std::mutex scheduler::mutex_; std::queue> scheduler::errors_; -std::vector> scheduler::callbacks_; -std::vector> scheduler::single_callbacks_; +utils::concurrent_list> scheduler::callbacks_; +utils::concurrent_list> scheduler::single_callbacks_; void scheduler::on_frame(const std::function& callback) { std::lock_guard _(mutex_); - callbacks_.push_back(callback); + callbacks_.add(callback); } void scheduler::once(const std::function& callback) { std::lock_guard _(mutex_); - single_callbacks_.push_back(callback); + single_callbacks_.add(callback); } void scheduler::error(const std::string& message, int level) @@ -39,24 +39,14 @@ __declspec(naked) void scheduler::execute() void scheduler::execute_safe() { - std::vector> callbacks_copy; - std::vector> single_callbacks_copy; - + for (const auto& callback : callbacks_) { - std::lock_guard _(mutex_); - callbacks_copy = callbacks_; - single_callbacks_copy = single_callbacks_; - single_callbacks_.clear(); + (*callback)(); } - for (const auto& callback : callbacks_copy) + for (const auto& callback : single_callbacks_) { - callback(); - } - - for (const auto& callback : single_callbacks_copy) - { - callback(); + (*callback)(); } } diff --git a/src/module/scheduler.hpp b/src/module/scheduler.hpp index 3e592be..001c6ac 100644 --- a/src/module/scheduler.hpp +++ b/src/module/scheduler.hpp @@ -1,5 +1,6 @@ #pragma once #include "loader/module_loader.hpp" +#include "utils/concurrent_list.hpp" class scheduler final : public module { @@ -15,8 +16,8 @@ public: private: static std::mutex mutex_; static std::queue> errors_; - static std::vector> callbacks_; - static std::vector> single_callbacks_; + static utils::concurrent_list> callbacks_; + static utils::concurrent_list> single_callbacks_; static void execute_safe(); static void execute_error(); diff --git a/src/utils/chain.hpp b/src/utils/concurrent_list.hpp similarity index 59% rename from src/utils/chain.hpp rename to src/utils/concurrent_list.hpp index 8804c9d..61dfef0 100644 --- a/src/utils/chain.hpp +++ b/src/utils/concurrent_list.hpp @@ -3,30 +3,26 @@ namespace utils { template - class chain final + class concurrent_list final { public: class entry final { - private: - std::shared_ptr object_; - std::shared_ptr next_; - public: + bool has_next() { - return (this->next_.use_count() > 0); + return this->next_.operator bool(); } bool is_valid() { - return (this->object_.use_count() > 0); + return this->object_.operator bool(); } - - void set(T object) + + void set(const std::shared_ptr& object) { - this->object_ = std::make_shared(); - *this->object_.get() = object; + this->object_ = object; } std::shared_ptr get() @@ -38,7 +34,7 @@ namespace utils { if (this->has_next()) { - return *(this->next_.get()); + return *this->next_; } else { @@ -56,67 +52,85 @@ namespace utils this->next_ = entry; } - T *operator->() + T* operator->() { - return (this->object_.get()); + return this->object_.get(); } - entry& operator++ () + std::shared_ptr operator*() + { + return this->object_; + } + + explicit operator bool() + { + return this->is_valid(); + } + + bool operator==(const entry& other) + { + return this->object_.get() == other.object_.get(); + } + + bool operator!=(const entry& other) + { + return !(*this == other); + } + + entry& operator++() { *this = this->get_next(); return *this; } - entry operator++ (int) + entry operator++(int) { entry result = *this; this->operator++(); return result; } + + private: + std::shared_ptr object_; + std::shared_ptr next_; }; - private: - std::mutex mutex_; - entry object_; - - public: void add(const T& object) { std::lock_guard _(this->mutex_); - + if (!this->empty()) { - // Create new chain entry std::shared_ptr current_object = std::make_shared(); - *current_object.get() = this->object_; + current_object->set(this->object_.get()); - // Add it to the chain this->object_ = entry(); this->object_.set_next_entry(current_object); } - this->object_.set(object); + const auto obj_ptr = std::make_shared(object); + this->object_.set(obj_ptr); } - void remove(std::shared_ptr object) + void remove(const std::shared_ptr& object) { std::lock_guard _(this->mutex_); - + if (!this->empty()) { - if (this->object_.get().get() == object.get()) + if (this->object_.get() == object) { this->object_ = this->object_.get_next(); } - else if(this->object_.has_next()) + else if (this->object_.has_next()) { for (auto entry = this->object_; entry.is_valid(); ++entry) { auto next = entry.get_next(); - if (next.is_valid() && next.get().get() == object.get()) + if (next.is_valid() && next.get() == object) { - *entry.get_next_entry().get() = next.get_next(); + entry.set_next_entry(next.get_next_entry()); } } } @@ -133,7 +147,7 @@ namespace utils bool empty() { - return !this->object_.is_valid(); + return !this->object_; } entry begin() @@ -141,9 +155,18 @@ namespace utils return this->object_; } + entry end() + { + return entry(); + } + void clear() { this->object_ = entry(); } + + private: + std::mutex mutex_; + entry object_; }; }