Scripting progress
This commit is contained in:
parent
e2d5c93407
commit
3498944714
@ -7,6 +7,8 @@ namespace game
|
|||||||
{
|
{
|
||||||
Cmd_AddCommand_t Cmd_AddCommand;
|
Cmd_AddCommand_t Cmd_AddCommand;
|
||||||
|
|
||||||
|
Com_Error_t Com_Error;
|
||||||
|
|
||||||
Conbuf_AppendText_t Conbuf_AppendText;
|
Conbuf_AppendText_t Conbuf_AppendText;
|
||||||
|
|
||||||
DB_LoadXAssets_t DB_LoadXAssets;
|
DB_LoadXAssets_t DB_LoadXAssets;
|
||||||
@ -26,6 +28,8 @@ namespace game
|
|||||||
short* scrVarGlob;
|
short* scrVarGlob;
|
||||||
char** scrMemTreePub;
|
char** scrMemTreePub;
|
||||||
|
|
||||||
|
unsigned int* levelEntityId;
|
||||||
|
|
||||||
void AddRefToValue(VariableValue* value)
|
void AddRefToValue(VariableValue* value)
|
||||||
{
|
{
|
||||||
if (value->type == SCRIPT_OBJECT)
|
if (value->type == SCRIPT_OBJECT)
|
||||||
@ -92,6 +96,8 @@ namespace game
|
|||||||
|
|
||||||
native::Cmd_AddCommand = native::Cmd_AddCommand_t(SELECT_VALUE(0x558820, 0x545DF0, 0));
|
native::Cmd_AddCommand = native::Cmd_AddCommand_t(SELECT_VALUE(0x558820, 0x545DF0, 0));
|
||||||
|
|
||||||
|
native::Com_Error = native::Com_Error_t(SELECT_VALUE(0x425540, 0x555450, 0x4D93F0));
|
||||||
|
|
||||||
native::Conbuf_AppendText = native::Conbuf_AppendText_t(SELECT_VALUE(0x4C84E0, 0x5CF610, 0x53C790));
|
native::Conbuf_AppendText = native::Conbuf_AppendText_t(SELECT_VALUE(0x4C84E0, 0x5CF610, 0x53C790));
|
||||||
|
|
||||||
native::DB_LoadXAssets = native::DB_LoadXAssets_t(SELECT_VALUE(0x48A8E0, 0x4CD020, 0x44F770));
|
native::DB_LoadXAssets = native::DB_LoadXAssets_t(SELECT_VALUE(0x48A8E0, 0x4CD020, 0x44F770));
|
||||||
@ -110,5 +116,7 @@ namespace game
|
|||||||
|
|
||||||
native::scrVarGlob = reinterpret_cast<short*>(SELECT_VALUE(0x19AFC80, 0x1E72180, 0x1D3C800));
|
native::scrVarGlob = reinterpret_cast<short*>(SELECT_VALUE(0x19AFC80, 0x1E72180, 0x1D3C800));
|
||||||
native::scrMemTreePub = reinterpret_cast<char**>(SELECT_VALUE(0x196FB00, 0x1E32000, 0x1C152A4));
|
native::scrMemTreePub = reinterpret_cast<char**>(SELECT_VALUE(0x196FB00, 0x1E32000, 0x1C152A4));
|
||||||
|
|
||||||
|
native::levelEntityId = reinterpret_cast<unsigned int*>(SELECT_VALUE(0x1BCBCA4, 0x208E1A4, 0x1CD873C));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,9 @@ namespace game
|
|||||||
typedef void (*Cmd_AddCommand_t)(const char* cmdName, void (*function)(), cmd_function_t* allocedCmd);
|
typedef void (*Cmd_AddCommand_t)(const char* cmdName, void (*function)(), cmd_function_t* allocedCmd);
|
||||||
extern Cmd_AddCommand_t Cmd_AddCommand;
|
extern Cmd_AddCommand_t Cmd_AddCommand;
|
||||||
|
|
||||||
|
typedef void (*Com_Error_t)(int code, const char *fmt, ...);
|
||||||
|
extern Com_Error_t Com_Error;
|
||||||
|
|
||||||
typedef void (*Conbuf_AppendText_t)(const char* message);
|
typedef void (*Conbuf_AppendText_t)(const char* message);
|
||||||
extern Conbuf_AppendText_t Conbuf_AppendText;
|
extern Conbuf_AppendText_t Conbuf_AppendText;
|
||||||
|
|
||||||
@ -37,6 +40,8 @@ namespace game
|
|||||||
extern short* scrVarGlob;
|
extern short* scrVarGlob;
|
||||||
extern char** scrMemTreePub;
|
extern char** scrMemTreePub;
|
||||||
|
|
||||||
|
extern unsigned int* levelEntityId;
|
||||||
|
|
||||||
void AddRefToValue(VariableValue* value);
|
void AddRefToValue(VariableValue* value);
|
||||||
|
|
||||||
scr_entref_t Scr_GetEntityIdRef(unsigned int id);
|
scr_entref_t Scr_GetEntityIdRef(unsigned int id);
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
#include "loader/module_loader.hpp"
|
#include "loader/module_loader.hpp"
|
||||||
#include "notification.hpp"
|
#include "notification.hpp"
|
||||||
#include "utils/hook.hpp"
|
#include "utils/hook.hpp"
|
||||||
|
#include "utils/string.hpp"
|
||||||
|
#include "scheduler.hpp"
|
||||||
|
|
||||||
std::mutex notification::mutex_;
|
std::mutex notification::mutex_;
|
||||||
std::vector<std::function<void(notification::event*)>> notification::callbacks_;
|
std::vector<std::function<void(notification::event*)>> notification::callbacks_;
|
||||||
@ -45,7 +47,7 @@ void notification::dispatch(event* event)
|
|||||||
copy = callbacks_;
|
copy = callbacks_;
|
||||||
}
|
}
|
||||||
|
|
||||||
for(const auto& callback : copy)
|
for (const auto& callback : copy)
|
||||||
{
|
{
|
||||||
callback(event);
|
callback(event);
|
||||||
}
|
}
|
||||||
@ -54,18 +56,29 @@ void notification::dispatch(event* event)
|
|||||||
void notification::vm_notify_stub(const unsigned int notify_id, const unsigned short type,
|
void notification::vm_notify_stub(const unsigned int notify_id, const unsigned short type,
|
||||||
game::native::VariableValue* stack)
|
game::native::VariableValue* stack)
|
||||||
{
|
{
|
||||||
event e;
|
|
||||||
e.name = game::native::SL_ConvertToString(type);
|
|
||||||
e.entity = game::native::Scr_GetEntityIdRef(notify_id);
|
|
||||||
|
|
||||||
for (auto value = stack; value->type != game::native::SCRIPT_END; --value)
|
|
||||||
{
|
|
||||||
e.arguments.emplace_back(*value);
|
|
||||||
}
|
|
||||||
|
|
||||||
dispatch(&e);
|
|
||||||
|
|
||||||
game::native::VM_Notify(notify_id, type, stack);
|
game::native::VM_Notify(notify_id, type, stack);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
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 %X: %s\n", e.entity_id, *game::native::levelEntityId, e.name.data());
|
||||||
|
|
||||||
|
for (auto value = stack; value->type != game::native::SCRIPT_END; --value)
|
||||||
|
{
|
||||||
|
e.arguments.emplace_back(*value);
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch(&e);
|
||||||
|
}
|
||||||
|
catch (std::exception& e)
|
||||||
|
{
|
||||||
|
scheduler::error(e.what(), 5);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
REGISTER_MODULE(notification)
|
REGISTER_MODULE(notification)
|
||||||
|
@ -9,7 +9,7 @@ public:
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
std::string name;
|
std::string name;
|
||||||
game::native::scr_entref_t entity;
|
unsigned int entity_id;
|
||||||
//std::vector<scripting::variable> arguments;
|
//std::vector<scripting::variable> arguments;
|
||||||
std::vector<game::native::VariableValue> arguments;
|
std::vector<game::native::VariableValue> arguments;
|
||||||
};
|
};
|
||||||
|
@ -1,7 +1,11 @@
|
|||||||
#include <std_include.hpp>
|
#include <std_include.hpp>
|
||||||
#include "scheduler.hpp"
|
#include "scheduler.hpp"
|
||||||
|
#include "utils/string.hpp"
|
||||||
|
#include "game/structs.hpp"
|
||||||
|
#include "game/game.hpp"
|
||||||
|
|
||||||
std::mutex scheduler::mutex_;
|
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::callbacks_;
|
||||||
std::vector<std::function<void()>> scheduler::single_callbacks_;
|
std::vector<std::function<void()>> scheduler::single_callbacks_;
|
||||||
|
|
||||||
@ -17,7 +21,23 @@ void scheduler::once(const std::function<void()>& callback)
|
|||||||
single_callbacks_.push_back(callback);
|
single_callbacks_.push_back(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
void scheduler::execute()
|
void scheduler::error(const std::string& message, int level)
|
||||||
|
{
|
||||||
|
std::lock_guard _(mutex_);
|
||||||
|
errors_.emplace(message, level);
|
||||||
|
}
|
||||||
|
|
||||||
|
__declspec(naked) void scheduler::execute()
|
||||||
|
{
|
||||||
|
__asm
|
||||||
|
{
|
||||||
|
call execute_error
|
||||||
|
call execute_safe
|
||||||
|
retn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void scheduler::execute_safe()
|
||||||
{
|
{
|
||||||
std::vector<std::function<void()>> callbacks_copy;
|
std::vector<std::function<void()>> callbacks_copy;
|
||||||
std::vector<std::function<void()>> single_callbacks_copy;
|
std::vector<std::function<void()>> single_callbacks_copy;
|
||||||
@ -40,6 +60,35 @@ void scheduler::execute()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void scheduler::execute_error()
|
||||||
|
{
|
||||||
|
const char* message;
|
||||||
|
int level;
|
||||||
|
|
||||||
|
if(get_next_error(&message, &level) && message)
|
||||||
|
{
|
||||||
|
game::native::Com_Error(level, "%s", message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool scheduler::get_next_error(const char** error_message, int* error_level)
|
||||||
|
{
|
||||||
|
std::lock_guard _(mutex_);
|
||||||
|
if(errors_.empty())
|
||||||
|
{
|
||||||
|
*error_message = nullptr;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto error = errors_.front();
|
||||||
|
errors_.pop();
|
||||||
|
|
||||||
|
*error_level = error.second;
|
||||||
|
*error_message = utils::string::va("%s", error.first.data());
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void scheduler::pre_destroy()
|
void scheduler::pre_destroy()
|
||||||
{
|
{
|
||||||
std::lock_guard _(mutex_);
|
std::lock_guard _(mutex_);
|
||||||
|
@ -8,10 +8,17 @@ public:
|
|||||||
static void once(const std::function<void()>& callback);
|
static void once(const std::function<void()>& callback);
|
||||||
static void execute();
|
static void execute();
|
||||||
|
|
||||||
|
static void error(const std::string& message, int level);
|
||||||
|
|
||||||
void pre_destroy() override;
|
void pre_destroy() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static std::mutex mutex_;
|
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()>> callbacks_;
|
||||||
static std::vector<std::function<void()>> single_callbacks_;
|
static std::vector<std::function<void()>> single_callbacks_;
|
||||||
|
|
||||||
|
static void execute_safe();
|
||||||
|
static void execute_error();
|
||||||
|
static bool get_next_error(const char** error_message, int* error_level);
|
||||||
};
|
};
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
#include "notification.hpp"
|
#include "notification.hpp"
|
||||||
#include "utils/io.hpp"
|
#include "utils/io.hpp"
|
||||||
#include "utils/string.hpp"
|
#include "utils/string.hpp"
|
||||||
|
#include "scheduler.hpp"
|
||||||
|
|
||||||
utils::hook scripting::start_hook_;
|
utils::hook scripting::start_hook_;
|
||||||
utils::hook scripting::stop_hook_;
|
utils::hook scripting::stop_hook_;
|
||||||
@ -10,6 +11,35 @@ std::mutex scripting::mutex_;
|
|||||||
std::vector<std::function<void()>> scripting::start_callbacks_;
|
std::vector<std::function<void()>> scripting::start_callbacks_;
|
||||||
std::vector<std::function<void()>> scripting::stop_callbacks_;
|
std::vector<std::function<void()>> scripting::stop_callbacks_;
|
||||||
|
|
||||||
|
scripting::entity::entity(scripting* environment, const unsigned int entity_id) : environment_(environment),
|
||||||
|
entity_id_(entity_id)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void scripting::entity::on_notify(const std::string& event,
|
||||||
|
const std::function<void(const std::vector<chaiscript::Boxed_Value>&)>& callback,
|
||||||
|
const bool is_volatile)
|
||||||
|
const
|
||||||
|
{
|
||||||
|
event_listener listener;
|
||||||
|
listener.event = event;
|
||||||
|
listener.callback = callback;
|
||||||
|
listener.entity_id = this->entity_id_;
|
||||||
|
listener.is_volatile = is_volatile;
|
||||||
|
|
||||||
|
this->environment_->add_event_listener(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int scripting::entity::get_entity_id() const
|
||||||
|
{
|
||||||
|
return this->entity_id_;
|
||||||
|
}
|
||||||
|
|
||||||
|
game::native::scr_entref_t scripting::entity::get_entity_reference() const
|
||||||
|
{
|
||||||
|
return game::native::Scr_GetEntityIdRef(this->get_entity_id());
|
||||||
|
}
|
||||||
|
|
||||||
scripting::variable::variable(game::native::VariableValue value) : value_(value)
|
scripting::variable::variable(game::native::VariableValue value) : value_(value)
|
||||||
{
|
{
|
||||||
game::native::AddRefToValue(&value);
|
game::native::AddRefToValue(&value);
|
||||||
@ -27,7 +57,17 @@ scripting::variable::operator game::native::VariableValue() const
|
|||||||
|
|
||||||
void scripting::post_start()
|
void scripting::post_start()
|
||||||
{
|
{
|
||||||
on_start(std::bind(&scripting::initialize, this));
|
on_start([this]()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
this->initialize();
|
||||||
|
}
|
||||||
|
catch (std::exception& e)
|
||||||
|
{
|
||||||
|
scheduler::error(e.what(), 5);
|
||||||
|
}
|
||||||
|
});
|
||||||
on_stop([this]()
|
on_stop([this]()
|
||||||
{
|
{
|
||||||
this->chai_ = {};
|
this->chai_ = {};
|
||||||
@ -56,21 +96,29 @@ void scripting::pre_destroy()
|
|||||||
stop_callbacks_.clear();
|
stop_callbacks_.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void scripting::add_event_listener(const event_listener& listener)
|
||||||
|
{
|
||||||
|
this->event_listeners_.add(listener);
|
||||||
|
}
|
||||||
|
|
||||||
void scripting::initialize()
|
void scripting::initialize()
|
||||||
{
|
{
|
||||||
this->chai_ = std::make_unique<chaiscript::ChaiScript>();
|
this->chai_ = std::make_unique<chaiscript::ChaiScript>();
|
||||||
|
this->chai_->add(chaiscript::user_type<entity>(), "entity");
|
||||||
|
this->chai_->add(chaiscript::fun(&entity::on_notify), "onNotify");
|
||||||
|
|
||||||
this->chai_->add(chaiscript::fun([](const std::string& string)
|
this->chai_->add(chaiscript::fun([](const std::string& string)
|
||||||
{
|
{
|
||||||
printf("%s\n", string.data());
|
printf("%s\n", string.data());
|
||||||
}), "print");
|
}), "print");
|
||||||
|
|
||||||
this->chai_->add(chaiscript::fun(
|
this->chai_->add(chaiscript::fun([](const std::string& string)
|
||||||
[this](const std::function<void(const std::string&,
|
{
|
||||||
const std::vector<chaiscript::Boxed_Value>&)>& callback)
|
MessageBoxA(nullptr, string.data(), nullptr, 0);
|
||||||
{
|
}), "alert");
|
||||||
std::lock_guard _(mutex_);
|
|
||||||
this->callbacks_.push_back(callback);
|
const auto level_id = *game::native::levelEntityId;
|
||||||
}), "onNotify");
|
this->chai_->add_global(chaiscript::var(entity(this, level_id)), "level");
|
||||||
|
|
||||||
this->load_scripts();
|
this->load_scripts();
|
||||||
|
|
||||||
@ -80,28 +128,26 @@ void scripting::initialize()
|
|||||||
|
|
||||||
for (const auto& argument : event->arguments)
|
for (const auto& argument : event->arguments)
|
||||||
{
|
{
|
||||||
arguments.push_back(make_boxed(argument));
|
arguments.push_back(this->make_boxed(argument));
|
||||||
}
|
}
|
||||||
|
|
||||||
decltype(this->callbacks_) copy;
|
for (auto listener = this->event_listeners_.begin(); listener.is_valid(); ++listener)
|
||||||
{
|
|
||||||
std::lock_guard _(mutex_);
|
|
||||||
copy = this->callbacks_;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& callback : copy)
|
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
callback(event->name, arguments);
|
if (listener->event == event->name && listener->entity_id == event->entity_id)
|
||||||
|
{
|
||||||
|
if (listener->is_volatile)
|
||||||
|
{
|
||||||
|
this->event_listeners_.remove(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
listener->callback(arguments);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (chaiscript::exception::eval_error &e)
|
catch (chaiscript::exception::eval_error& e)
|
||||||
{
|
{
|
||||||
printf("Failed to handle event: %s\n", e.pretty_print().data());
|
throw std::runtime_error(e.pretty_print());
|
||||||
}
|
|
||||||
catch (std::exception& e)
|
|
||||||
{
|
|
||||||
printf("Failed to handle event: %s\n", e.what());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -119,13 +165,9 @@ void scripting::load_scripts() const
|
|||||||
{
|
{
|
||||||
this->chai_->eval_file(script);
|
this->chai_->eval_file(script);
|
||||||
}
|
}
|
||||||
catch (chaiscript::exception::eval_error &e)
|
catch (chaiscript::exception::eval_error& e)
|
||||||
{
|
{
|
||||||
printf("Failed to load script %s: %s\n", script.data(), e.pretty_print().data());
|
throw std::runtime_error(e.pretty_print());
|
||||||
}
|
|
||||||
catch (std::exception& e)
|
|
||||||
{
|
|
||||||
printf("Failed to load script %s: %s\n", script.data(), e.what());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -146,6 +188,10 @@ chaiscript::Boxed_Value scripting::make_boxed(const game::native::VariableValue
|
|||||||
{
|
{
|
||||||
return chaiscript::var(value.u.intValue);
|
return chaiscript::var(value.u.intValue);
|
||||||
}
|
}
|
||||||
|
else if (value.type == game::native::SCRIPT_OBJECT)
|
||||||
|
{
|
||||||
|
return chaiscript::var(entity(this, value.u.entityId));
|
||||||
|
}
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,28 @@
|
|||||||
#include "loader/module_loader.hpp"
|
#include "loader/module_loader.hpp"
|
||||||
#include "game/game.hpp"
|
#include "game/game.hpp"
|
||||||
#include "utils/hook.hpp"
|
#include "utils/hook.hpp"
|
||||||
|
#include "utils/chain.hpp"
|
||||||
|
|
||||||
class scripting final : public module
|
class scripting final : public module
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
class entity final
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
entity(scripting* environment, unsigned int entity_id);
|
||||||
|
|
||||||
|
void on_notify(const std::string& event,
|
||||||
|
const std::function<void(const std::vector<chaiscript::Boxed_Value>&)>& callback,
|
||||||
|
bool is_volatile) const;
|
||||||
|
|
||||||
|
unsigned int get_entity_id() const;
|
||||||
|
game::native::scr_entref_t get_entity_reference() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
scripting* environment_;
|
||||||
|
unsigned int entity_id_;
|
||||||
|
};
|
||||||
|
|
||||||
class variable final
|
class variable final
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -18,6 +36,15 @@ public:
|
|||||||
game::native::VariableValue value_;
|
game::native::VariableValue value_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class event_listener final
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
std::string event = {};
|
||||||
|
unsigned int entity_id = 0;
|
||||||
|
std::function<void(const std::vector<chaiscript::Boxed_Value>&)> callback = {};
|
||||||
|
bool is_volatile = false;
|
||||||
|
};
|
||||||
|
|
||||||
void post_start() override;
|
void post_start() override;
|
||||||
void post_load() override;
|
void post_load() override;
|
||||||
void pre_destroy() override;
|
void pre_destroy() override;
|
||||||
@ -27,12 +54,14 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<chaiscript::ChaiScript> chai_;
|
std::unique_ptr<chaiscript::ChaiScript> chai_;
|
||||||
std::vector<std::function<void(const std::string&, const std::vector<chaiscript::Boxed_Value>&)>> callbacks_;
|
utils::chain<event_listener> event_listeners_;
|
||||||
|
|
||||||
|
void add_event_listener(const event_listener& listener);
|
||||||
|
|
||||||
void initialize();
|
void initialize();
|
||||||
void load_scripts() const;
|
void load_scripts() const;
|
||||||
|
|
||||||
static chaiscript::Boxed_Value make_boxed(game::native::VariableValue value);
|
chaiscript::Boxed_Value make_boxed(game::native::VariableValue value);
|
||||||
|
|
||||||
static utils::hook start_hook_;
|
static utils::hook start_hook_;
|
||||||
static utils::hook stop_hook_;
|
static utils::hook stop_hook_;
|
||||||
|
147
src/utils/chain.hpp
Normal file
147
src/utils/chain.hpp
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
namespace utils
|
||||||
|
{
|
||||||
|
template <typename T>
|
||||||
|
class chain
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
class entry
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
std::shared_ptr<T> object_;
|
||||||
|
std::shared_ptr<entry> next_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool has_next()
|
||||||
|
{
|
||||||
|
return (this->next_.use_count() > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_valid()
|
||||||
|
{
|
||||||
|
return (this->object_.use_count() > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void set(T object)
|
||||||
|
{
|
||||||
|
this->object_ = std::shared_ptr<T>(new T());
|
||||||
|
*this->object_.get() = object;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<T> get()
|
||||||
|
{
|
||||||
|
return this->object_;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry get_next()
|
||||||
|
{
|
||||||
|
if (this->has_next())
|
||||||
|
{
|
||||||
|
return *(this->next_.get());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return entry();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<entry> get_next_entry()
|
||||||
|
{
|
||||||
|
return this->next_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_next_entry(std::shared_ptr<entry> entry)
|
||||||
|
{
|
||||||
|
this->next_ = entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
T *operator->()
|
||||||
|
{
|
||||||
|
return (this->object_.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
entry& operator++ ()
|
||||||
|
{
|
||||||
|
*this = this->get_next();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry operator++ (int)
|
||||||
|
{
|
||||||
|
entry result = *this;
|
||||||
|
this->operator++();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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::shared_ptr<entry>(new entry);
|
||||||
|
*current_object.get() = this->object_;
|
||||||
|
|
||||||
|
// Add it to the chain
|
||||||
|
this->object_ = entry();
|
||||||
|
this->object_.set_next_entry(current_object);
|
||||||
|
}
|
||||||
|
|
||||||
|
this->object_.set(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove(std::shared_ptr<T> object)
|
||||||
|
{
|
||||||
|
std::lock_guard _(this->mutex_);
|
||||||
|
|
||||||
|
if (!this->empty())
|
||||||
|
{
|
||||||
|
if (this->object_.get().get() == object.get())
|
||||||
|
{
|
||||||
|
this->object_ = this->object_.get_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())
|
||||||
|
{
|
||||||
|
*entry.get_next_entry().get() = next.get_next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove(entry entry)
|
||||||
|
{
|
||||||
|
if (entry.is_valid())
|
||||||
|
{
|
||||||
|
this->remove(entry.get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool empty()
|
||||||
|
{
|
||||||
|
return !this->object_.is_valid();
|
||||||
|
}
|
||||||
|
|
||||||
|
entry begin()
|
||||||
|
{
|
||||||
|
return this->object_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear()
|
||||||
|
{
|
||||||
|
this->object_ = entry();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user