diff options
author | Thorbjørn Lindeijer <thorbjorn@lindeijer.nl> | 2013-09-19 15:43:18 +0200 |
---|---|---|
committer | Thorbjørn Lindeijer <thorbjorn@lindeijer.nl> | 2013-09-19 16:54:32 +0200 |
commit | cae44328f1f3ca9f7f13f9252d279231e3c8ef2a (patch) | |
tree | 5b31fecb2b3a76578d151fe7b6595eb8cb57deb4 | |
parent | df8e8de0e0850d54992ae4fc8727cbe17527ccb5 (diff) | |
download | manaserv-cae44328f1f3ca9f7f13f9252d279231e3c8ef2a.tar.gz manaserv-cae44328f1f3ca9f7f13f9252d279231e3c8ef2a.tar.bz2 manaserv-cae44328f1f3ca9f7f13f9252d279231e3c8ef2a.tar.xz manaserv-cae44328f1f3ca9f7f13f9252d279231e3c8ef2a.zip |
Have Lua use entity IDs instead of direct pointers
Allows to report access to removed entities instead of crashing.
-rw-r--r-- | src/game-server/entity.cpp | 5 | ||||
-rw-r--r-- | src/game-server/entity.h | 20 | ||||
-rw-r--r-- | src/game-server/idmanager.h | 75 | ||||
-rw-r--r-- | src/scripting/lua.cpp | 2 | ||||
-rw-r--r-- | src/scripting/luautil.cpp | 111 | ||||
-rw-r--r-- | src/scripting/luautil.h | 51 |
6 files changed, 225 insertions, 39 deletions
diff --git a/src/game-server/entity.cpp b/src/game-server/entity.cpp index b40ac442..15afc29d 100644 --- a/src/game-server/entity.cpp +++ b/src/game-server/entity.cpp @@ -21,7 +21,10 @@ #include "game-server/entity.h" +IdManager<Entity> Entity::mIdManager; + Entity::Entity(EntityType type, MapComposite *map) : + mId(mIdManager.allocate(this)), mMap(map), mType(type) { @@ -33,6 +36,8 @@ Entity::~Entity() { for (int i = 0; i < ComponentTypeCount; ++i) delete mComponents[i]; + + mIdManager.free(mId); } /** diff --git a/src/game-server/entity.h b/src/game-server/entity.h index bb76e440..b67b8063 100644 --- a/src/game-server/entity.h +++ b/src/game-server/entity.h @@ -25,6 +25,7 @@ #include "common/manaserv_protocol.h" #include "game-server/component.h" +#include "game-server/idmanager.h" #include <sigc++/signal.h> #include <sigc++/trackable.h> @@ -48,6 +49,7 @@ class Entity : public sigc::trackable virtual ~Entity(); + unsigned getId() const; EntityType getType() const; template <class T> void addComponent(T *component); @@ -71,13 +73,31 @@ class Entity : public sigc::trackable private: Component *getComponent(ComponentType type) const; + unsigned mId; MapComposite *mMap; /**< Map the entity is on */ EntityType mType; /**< Type of this entity. */ Component *mComponents[ComponentTypeCount]; + + static IdManager<Entity> mIdManager; + + friend Entity *findEntity(unsigned id); }; /** + * Looks up an entity by its ID, returns null if there is no such entity. + */ +inline Entity *findEntity(unsigned id) +{ + return Entity::mIdManager.find(id); +} + +inline unsigned Entity::getId() const +{ + return mId; +} + +/** * Gets type of this entity. * * @return the type of this entity. diff --git a/src/game-server/idmanager.h b/src/game-server/idmanager.h new file mode 100644 index 00000000..e9f9cdfc --- /dev/null +++ b/src/game-server/idmanager.h @@ -0,0 +1,75 @@ +/* + * The Mana Server + * Copyright (C) 2013 The Mana Developers + * + * This file is part of The Mana Server. + * + * The Mana Server is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * any later version. + * + * The Mana Server is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with The Mana Server. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef IDMANAGER_H +#define IDMANAGER_H + +#include <unordered_map> + +/** + * A very simplistic ID manager. + * + * Does not have any error handling on the premise that other problems will + * occur before hitting UINT_MAX allocated IDs. + */ +template <typename Value> +class IdManager +{ +public: + IdManager() = default; + IdManager(const IdManager&) = delete; + + unsigned allocate(Value *t); + void free(unsigned id); + Value *find(unsigned id) const; + +private: + std::unordered_map<unsigned, Value*> mIdMap; + unsigned mLastId = 0; +}; + + +template <typename Value> +inline unsigned IdManager<Value>::allocate(Value *t) +{ + do { + ++mLastId; + } while (mIdMap.find(mLastId) != mIdMap.end()); + + mIdMap.insert(std::make_pair(mLastId, t)); + return mLastId; +} + +template <typename Value> +inline void IdManager<Value>::free(unsigned id) +{ + mIdMap.erase(id); +} + +template <typename Value> +inline Value *IdManager<Value>::find(unsigned id) const +{ + auto it = mIdMap.find(id); + if (it != mIdMap.end()) + return it->second; + return nullptr; +} + +#endif // IDMANAGER_H diff --git a/src/scripting/lua.cpp b/src/scripting/lua.cpp index a999744e..a84fe92d 100644 --- a/src/scripting/lua.cpp +++ b/src/scripting/lua.cpp @@ -3609,7 +3609,7 @@ LuaScript::LuaScript(): { nullptr, nullptr} }; - LuaEntity::registerType(mRootState, "Entity", members_Entity); + LuaEntity::registerType(mRootState, members_Entity); LuaItemClass::registerType(mRootState, "ItemClass", members_ItemClass); LuaMapObject::registerType(mRootState, "MapObject", members_MapObject); LuaMonsterClass::registerType(mRootState, "MonsterClass", members_MonsterClass); diff --git a/src/scripting/luautil.cpp b/src/scripting/luautil.cpp index 4607c8dd..1408eb63 100644 --- a/src/scripting/luautil.cpp +++ b/src/scripting/luautil.cpp @@ -42,17 +42,15 @@ void raiseWarning(lua_State *, const char *format, ...) vsprintf(message, format, args); va_end( args ); - LOG_WARN("Lua script error: "<< message); + LOG_WARN("Lua script error: " << message); } -char UserDataCache::mRegistryKey; - -bool UserDataCache::retrieve(lua_State *s, void *object) +bool UserDataCache::retrieve(lua_State *s) { // Retrieve the cache table - lua_pushlightuserdata(s, &mRegistryKey); // key - lua_rawget(s, LUA_REGISTRYINDEX); // Cache? + lua_pushlightuserdata(s, &mRegistryKey); // object_key, key + lua_rawget(s, LUA_REGISTRYINDEX); // object_key, Cache? if (lua_isnil(s, -1)) { @@ -60,47 +58,110 @@ bool UserDataCache::retrieve(lua_State *s, void *object) return false; } - lua_pushlightuserdata(s, object); // Cache, object - lua_rawget(s, -2); // Cache, UD? + lua_pushvalue(s, -2); // object_key, Cache, object_key + lua_rawget(s, -2); // object_key, Cache, UD? if (lua_isnil(s, -1)) { - lua_pop(s, 2); // ... + lua_pop(s, 2); // object_key return false; } - lua_replace(s, -2); // UD + lua_replace(s, -3); // UD, Cache + lua_pop(s, 1); // UD return true; } -void UserDataCache::insert(lua_State *s, void *object) +void UserDataCache::insert(lua_State *s) { // Retrieve the cache table - lua_pushlightuserdata(s, &mRegistryKey); // UD, key - lua_rawget(s, LUA_REGISTRYINDEX); // UD, Cache? + lua_pushlightuserdata(s, &mRegistryKey); // object_key, UD, key + lua_rawget(s, LUA_REGISTRYINDEX); // object_key, UD, Cache? // Create the cache when it doesn't exist yet if (lua_isnil(s, -1)) { - lua_pop(s, 1); // UD - lua_newtable(s); // UD, Cache + lua_pop(s, 1); // object_key, UD + lua_newtable(s); // object_key, UD, Cache // The metatable that makes the values in the table above weak - lua_newtable(s); // UD, Cache, {} + lua_createtable(s, 0, 1); // object_key, UD, Cache, {} lua_pushliteral(s, "__mode"); lua_pushliteral(s, "v"); - lua_rawset(s, -3); // UD, Cache, { __mode = "v" } - lua_setmetatable(s, -2); // UD, Cache + lua_rawset(s, -3); // object_key, UD, Cache, { __mode = "v" } + lua_setmetatable(s, -2); // object_key, UD, Cache - lua_pushlightuserdata(s, &mRegistryKey);// UD, Cache, key - lua_pushvalue(s, -2); // UD, Cache, key, Cache - lua_rawset(s, LUA_REGISTRYINDEX); // UD, Cache + lua_pushlightuserdata(s, &mRegistryKey);// object_key, UD, Cache, key + lua_pushvalue(s, -2); // object_key, UD, Cache, key, Cache + lua_rawset(s, LUA_REGISTRYINDEX); // object_key, UD, Cache } - lua_pushlightuserdata(s, object); // UD, Cache, object - lua_pushvalue(s, -3); // UD, Cache, object, UD - lua_rawset(s, -3); // UD, Cache { object = UD } - lua_pop(s, 1); // UD + lua_pushvalue(s, -3); // object_key, UD, Cache, object_key + lua_pushvalue(s, -3); // object_key, UD, Cache, object_key, UD + lua_rawset(s, -3); // object_key, UD, Cache { object_key = UD } + lua_pop(s, 1); // object_key, UD + lua_replace(s, -2); // UD +} + + +UserDataCache LuaUserData<Entity>::mUserDataCache; + +void LuaUserData<Entity>::registerType(lua_State *s, const luaL_Reg *members) +{ + luaL_newmetatable(s, "Entity"); // metatable + lua_pushliteral(s, "__index"); // metatable, "__index" + lua_createtable(s, 0, 0); // metatable, "__index", {} +#if LUA_VERSION_NUM < 502 + luaL_register(s, nullptr, members); +#else + luaL_setfuncs(s, members, 0); +#endif + + // Make the functions table available as a global + lua_pushvalue(s, -1); // metatable, "__index", {}, {} + lua_setglobal(s, "Entity"); // metatable, "__index", {} + + lua_rawset(s, -3); // metatable + lua_pop(s, 1); // -empty- +} + +void LuaUserData<Entity>::push(lua_State *s, Entity *entity) +{ + if (!entity) + { + lua_pushnil(s); + } + else + { +#if LUA_VERSION_NUM < 502 + lua_pushnumber(s, entity->getId()); +#else + lua_pushunsigned(s, entity->getId()); +#endif + + if (!mUserDataCache.retrieve(s)) + { + void *userData = lua_newuserdata(s, sizeof(unsigned)); + * static_cast<unsigned*>(userData) = entity->getId(); + +#if LUA_VERSION_NUM < 502 + luaL_newmetatable(s, "Entity"); + lua_setmetatable(s, -2); +#else + luaL_setmetatable(s, "Entity"); +#endif + + mUserDataCache.insert(s); + } + } +} + +Entity *LuaUserData<Entity>::check(lua_State *L, int narg) +{ + void *userData = luaL_checkudata(L, narg, "Entity"); + Entity *entity = findEntity(*(static_cast<unsigned*>(userData))); + luaL_argcheck(L, entity, narg, "invalid entity"); + return entity; } diff --git a/src/scripting/luautil.h b/src/scripting/luautil.h index fb4793a0..e38b01c8 100644 --- a/src/scripting/luautil.h +++ b/src/scripting/luautil.h @@ -62,21 +62,23 @@ class UserDataCache { public: /** - * Attempts to retrieve a userdata associated with the given object from - * the cache and pushes it on the stack when available. + * Attempts to retrieve a userdata associated with the key at the top of + * the stack from the cache and pushes it on the stack when available. + * When no userdata is found, the key is left on the stack. * * Returns whether a userdata was pushed. */ - static bool retrieve(lua_State *s, void *object); + bool retrieve(lua_State *s); /** * Inserts the userdata at the top of the stack in the cache using - * the given object as the key. Leaves the userdata on the stack. + * the key that is just below the top of the stack. Leaves the userdata on + * the stack. */ - static void insert(lua_State *s, void *object); + void insert(lua_State *s); private: - static char mRegistryKey; + char mRegistryKey; }; @@ -127,19 +129,24 @@ public: { lua_pushnil(s); } - else if (!UserDataCache::retrieve(s, object)) + else { - void *userData = lua_newuserdata(s, sizeof(T*)); - * static_cast<T**>(userData) = object; + lua_pushlightuserdata(s, object); + + if (!mUserDataCache.retrieve(s)) + { + void *userData = lua_newuserdata(s, sizeof(T*)); + * static_cast<T**>(userData) = object; #if LUA_VERSION_NUM < 502 - luaL_newmetatable(s, mTypeName); - lua_setmetatable(s, -2); + luaL_newmetatable(s, mTypeName); + lua_setmetatable(s, -2); #else - luaL_setmetatable(s, mTypeName); + luaL_setmetatable(s, mTypeName); #endif - UserDataCache::insert(s, object); + mUserDataCache.insert(s); + } } } @@ -155,9 +162,27 @@ public: private: static const char *mTypeName; + static UserDataCache mUserDataCache; }; template <typename T> const char * LuaUserData<T>::mTypeName; +template <typename T> UserDataCache LuaUserData<T>::mUserDataCache; + +/** + * Template specialization for entities, to allow their userdata to refer to + * the entity ID. + */ +template <> +class LuaUserData <Entity> +{ +public: + static void registerType(lua_State *s, const luaL_Reg *members); + static void push(lua_State *s, Entity *entity); + static Entity *check(lua_State *L, int narg); + +private: + static UserDataCache mUserDataCache; +}; typedef LuaUserData<Entity> LuaEntity; typedef LuaUserData<ItemClass> LuaItemClass; |