Support clearing event listeners

This commit is contained in:
momo5502 2019-01-20 22:26:18 +01:00
parent 180af9d404
commit a6b87fbe7f
10 changed files with 135 additions and 77 deletions

View File

@ -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

View File

@ -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<void(const std::vector<chaiscript::Boxed_Value>&)>&
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

View File

@ -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<void(const std::vector<chaiscript::Boxed_Value>&)>& callback,
bool is_volatile) const;

View File

@ -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>(), "event_listener_handle");
chai->add(chaiscript::constructor<event_listener_handle()>(), "event_listener_handle");
chai->add(chaiscript::constructor<event_listener_handle(const event_listener_handle&)>(), "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<chaiscript::Boxed_Value>& 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<chaiscript::Boxed_Value>& 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;
}
}
}
}
}

View File

@ -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_listener> event_listeners_;
utils::concurrent_list<generic_event_listener> generic_event_listeners_;
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);
utils::chain<event_listener> event_listeners_;
utils::chain<generic_event_listener> generic_event_listeners_;
void remove(const event_listener_handle& handle);
};
}
}

View File

@ -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)
{

View File

@ -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<task> tasks_;
utils::concurrent_list<task> tasks_;
std::atomic_int64_t current_task_id_ = 0;
task_handle add(const std::function<void()>& callback, long long milliseconds, bool is_volatile);

View File

@ -6,19 +6,19 @@
std::mutex scheduler::mutex_;
std::queue<std::pair<std::string, int>> scheduler::errors_;
std::vector<std::function<void()>> scheduler::callbacks_;
std::vector<std::function<void()>> scheduler::single_callbacks_;
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_);
callbacks_.push_back(callback);
callbacks_.add(callback);
}
void scheduler::once(const std::function<void()>& 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<std::function<void()>> callbacks_copy;
std::vector<std::function<void()>> 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)();
}
}

View File

@ -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<std::pair<std::string, int>> errors_;
static std::vector<std::function<void()>> callbacks_;
static std::vector<std::function<void()>> single_callbacks_;
static utils::concurrent_list<std::function<void()>> callbacks_;
static utils::concurrent_list<std::function<void()>> single_callbacks_;
static void execute_safe();
static void execute_error();

View File

@ -3,30 +3,26 @@
namespace utils
{
template <typename T>
class chain final
class concurrent_list final
{
public:
class entry final
{
private:
std::shared_ptr<T> object_;
std::shared_ptr<entry> 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<T>& object)
{
this->object_ = std::make_shared<T>();
*this->object_.get() = object;
this->object_ = object;
}
std::shared_ptr<T> 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<T> 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<T> object_;
std::shared_ptr<entry> 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<entry> current_object = std::make_shared<entry>();
*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<T>(object);
this->object_.set(obj_ptr);
}
void remove(std::shared_ptr<T> object)
void remove(const std::shared_ptr<T>& 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_;
};
}