summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThorbjørn Lindeijer <thorbjorn@lindeijer.nl>2013-09-19 15:43:18 +0200
committerThorbjørn Lindeijer <thorbjorn@lindeijer.nl>2013-09-19 16:54:32 +0200
commitcae44328f1f3ca9f7f13f9252d279231e3c8ef2a (patch)
tree5b31fecb2b3a76578d151fe7b6595eb8cb57deb4
parentdf8e8de0e0850d54992ae4fc8727cbe17527ccb5 (diff)
downloadmanaserv-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.cpp5
-rw-r--r--src/game-server/entity.h20
-rw-r--r--src/game-server/idmanager.h75
-rw-r--r--src/scripting/lua.cpp2
-rw-r--r--src/scripting/luautil.cpp111
-rw-r--r--src/scripting/luautil.h51
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;