/* * Copyright (C) 2010 - 2015 Eluna Lua Engine * This program is free software licensed under GPL version 3 * Please see the included DOCS/LICENSE.md for more information */ #ifndef _BINDING_MAP_H #define _BINDING_MAP_H #include #include "Common.h" #include "ElunaUtility.h" extern "C" { #include "lua.h" #include "lauxlib.h" }; /* * A set of bindings from keys of type `K` to Lua references. */ template class BindingMap : public ElunaUtil::RWLockable { private: lua_State* L; uint64 maxBindingID; struct Binding { uint64 id; lua_State* L; uint32 remainingShots; int functionReference; Binding(lua_State* L, uint64 id, int functionReference, uint32 remainingShots) : id(id), L(L), remainingShots(remainingShots), functionReference(functionReference) { } ~Binding() { luaL_unref(L, LUA_REGISTRYINDEX, functionReference); } }; typedef std::vector< std::unique_ptr > BindingList; std::unordered_map bindings; /* * This table is for fast removal of bindings by ID. * * Instead of having to look through (potentially) every BindingList to find * the Binding with the right ID, this allows you to go directly to the * BindingList that might have the Binding with that ID. * * However, you must be careful not to store pointers to BindingLists * that no longer exist (see `void Clear(const K& key)` implementation). */ std::unordered_map id_lookup_table; public: BindingMap(lua_State* L) : L(L), maxBindingID(0) { } /* * Insert a new binding from `key` to `ref`, which lasts for `shots`-many pushes. * * If `shots` is 0, it will never automatically expire, but can still be * removed with `Clear` or `Remove`. */ uint64 Insert(const K& key, int ref, uint32 shots) { WriteGuard guard(GetLock()); uint64 id = (++maxBindingID); BindingList& list = bindings[key]; list.push_back(std::unique_ptr(new Binding(L, id, ref, shots))); id_lookup_table[id] = &list; return id; } /* * Clear all bindings for `key`. */ void Clear(const K& key) { WriteGuard guard(GetLock()); if (bindings.empty()) return; auto iter = bindings.find(key); if (iter == bindings.end()) return; BindingList& list = iter->second; // Remove all pointers to `list` from `id_lookup_table`. for (auto i = list.begin(); i != list.end(); ++i) { std::unique_ptr& binding = *i; id_lookup_table.erase(binding->id); } bindings.erase(key); } /* * Clear all bindings for all keys. */ void Clear() { WriteGuard guard(GetLock()); if (bindings.empty()) return; id_lookup_table.clear(); bindings.clear(); } /* * Remove a specific binding identified by `id`. * * If `id` in invalid, nothing is removed. */ void Remove(uint64 id) { WriteGuard guard(GetLock()); auto iter = id_lookup_table.find(id); if (iter == id_lookup_table.end()) return; BindingList* list = iter->second; auto i = list->begin(); for (; i != list->end(); ++i) { std::unique_ptr& binding = *i; if (binding->id == id) break; } if (i != list->end()) list->erase(i); // Unconditionally erase the ID in the lookup table because // it was either already invalid, or it's no longer valid. id_lookup_table.erase(id); } /* * Check whether `key` has any bindings. */ bool HasBindingsFor(const K& key) { ReadGuard guard(GetLock()); if (bindings.empty()) return false; auto result = bindings.find(key); if (result == bindings.end()) return false; BindingList& list = result->second; return !list.empty(); } /* * Push all Lua references for `key` onto the stack. */ void PushRefsFor(const K& key) { WriteGuard guard(GetLock()); if (bindings.empty()) return; auto result = bindings.find(key); if (result == bindings.end()) return; BindingList& list = result->second; for (auto i = list.begin(); i != list.end();) { std::unique_ptr& binding = (*i); auto i_prev = (i++); lua_rawgeti(L, LUA_REGISTRYINDEX, binding->functionReference); if (binding->remainingShots > 0) { binding->remainingShots -= 1; if (binding->remainingShots == 0) { id_lookup_table.erase(binding->id); list.erase(i_prev); } } } } }; /* * A `BindingMap` key type for simple event ID bindings * (ServerEvents, GuildEvents, etc.). */ template struct EventKey { T event_id; EventKey(T event_id) : event_id(event_id) {} }; /* * A `BindingMap` key type for event ID/Object entry ID bindings * (CreatureEvents, GameObjectEvents, etc.). */ template struct EntryKey : public EventKey { uint32 entry; EntryKey(T event_type, uint32 entry) : EventKey(event_type), entry(entry) {} }; /* * A `BindingMap` key type for event ID/unique Object bindings * (currently just CreatureEvents). */ template struct UniqueObjectKey : public EventKey { uint64 guid; uint32 instance_id; UniqueObjectKey(T event_type, uint64 guid, uint32 instance_id) : EventKey(event_type), guid(guid), instance_id(instance_id) {} }; /* * Implementations of various std functions on the above key types, * so that they can be used within an unordered_map. */ namespace std { template struct equal_to < EventKey > { bool operator()(EventKey const& lhs, EventKey const& rhs) const { return lhs.event_id == rhs.event_id; } }; template struct equal_to < EntryKey > { bool operator()(EntryKey const& lhs, EntryKey const& rhs) const { return lhs.event_id == rhs.event_id && lhs.entry == rhs.entry; } }; template struct equal_to < UniqueObjectKey > { bool operator()(UniqueObjectKey const& lhs, UniqueObjectKey const& rhs) const { return lhs.event_id == rhs.event_id && lhs.guid == rhs.guid && lhs.instance_id == rhs.instance_id; } }; template struct hash < EventKey > { typedef EventKey argument_type; typedef std::size_t result_type; result_type operator()(argument_type const& k) const { result_type const h1(std::hash()(k.event_id)); return h1; } }; template struct hash < EntryKey > { typedef EntryKey argument_type; typedef std::size_t result_type; result_type operator()(argument_type const& k) const { result_type const h1(std::hash()(k.event_id)); result_type const h2(std::hash()(k.entry)); return h1 ^ (h2 << 8); // `event_id` probably won't exceed 2^8. } }; template struct hash < UniqueObjectKey > { typedef UniqueObjectKey argument_type; typedef std::size_t result_type; result_type operator()(argument_type const& k) const { result_type const h1(std::hash()(k.event_id)); result_type const h2(std::hash()(k.instance_id)); result_type const h3(std::hash()(k.guid)); return h1 ^ (h2 << 8) ^ (h3 << 24); // `instance_id` probably won't exceed 2^16. } }; } #endif // _BINDING_MAP_H