/* * The Mana Server * Copyright (C) 2007-2010 The Mana World Development Team * * 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 . */ #ifndef SCRIPTING_LUAUTIL_H #define SCRIPTING_LUAUTIL_H #include "scripting/script.h" extern "C" { #include #include } #include #include #include #include #include #include "game-server/abilitymanager.h" class CharacterComponent; class Entity; class ItemClass; class MapComposite; class MapObject; class MonsterClass; class StatusEffect; void raiseWarning(lua_State *s, const char *format, ...); /** * A generic userdata cache based on a native Lua table with weak values. * * Caching the created userdata instances has two main advantages: * * - It limits memory consumption and is a little faster. Creating these values * is relatively slow (compared to light userdata). * * - It makes sure that two userdata objects that refer to the same C++ object * compare as equal (because they will be the same userdata object). */ class UserDataCache { public: /** * Attempts to retrieve a userdata associated with the given object from * the cache and pushes it on the stack when available. * * Returns whether a userdata was pushed. */ static bool retrieve(lua_State *s, void *object); /** * 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. */ static void insert(lua_State *s, void *object); private: static char mRegistryKey; }; /** * A helper class for pushing and checking custom Lua userdata types. */ template class LuaUserData { public: /** * Creates a metatable to be used for the userdata associated with the * type. Then, registers the \a members with a library named \a typeName, * and sets the '__index' member of the metatable to this library. */ static void registerType(lua_State *s, const char *typeName, const luaL_Reg *members) { mTypeName = typeName; luaL_newmetatable(s, mTypeName); // 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, mTypeName); // metatable, "__index", {} lua_rawset(s, -3); // metatable lua_pop(s, 1); // -empty- } /** * Pushes a userdata reference to the given object on the stack. Either by * creating one, or reusing an existing one. * * When a null-pointer is passed for \a object, the value 'nil' is pushed. */ static void push(lua_State *s, T *object) { if (!object) { lua_pushnil(s); } else if (!UserDataCache::retrieve(s, object)) { void *userData = lua_newuserdata(s, sizeof(T*)); * static_cast(userData) = object; #if LUA_VERSION_NUM < 502 luaL_newmetatable(s, mTypeName); lua_setmetatable(s, -2); #else luaL_setmetatable(s, mTypeName); #endif UserDataCache::insert(s, object); } } /** * Returns the argument at position \a narg when it is of the right type, * and raises a Lua error otherwise. */ static T *check(lua_State *L, int narg) { void *userData = luaL_checkudata(L, narg, mTypeName); return *(static_cast(userData)); } private: static const char *mTypeName; }; template const char * LuaUserData::mTypeName; typedef LuaUserData LuaEntity; typedef LuaUserData LuaItemClass; typedef LuaUserData LuaMapObject; typedef LuaUserData LuaMonsterClass; typedef LuaUserData LuaStatusEffect; typedef LuaUserData LuaAbilityInfo; Script * getScript(lua_State *s); ItemClass * getItemClass(lua_State *s, int p); MonsterClass * getMonsterClass(lua_State *s, int p); Entity * checkActor(lua_State *s, int p); Entity * checkBeing(lua_State *s, int p); Entity * checkCharacter(lua_State *s, int p); ItemClass * checkItemClass(lua_State *s, int p); Entity * checkMonster(lua_State *s, int p); MonsterClass * checkMonsterClass(lua_State *s, int p); Entity * checkNpc(lua_State *s, int p); int checkSkill(lua_State *s, int p); AbilityManager::AbilityInfo *checkAbility(lua_State *s, int p); MapComposite * checkCurrentMap(lua_State *s, Script *script = 0); Script::Thread* checkCurrentThread(lua_State *s, Script *script = 0); /* Polymorphic wrapper for pushing variables. Useful for templates.*/ inline void push(lua_State *s, int val) { lua_pushinteger(s, val); } inline void push(lua_State *s, const std::string &val) { lua_pushlstring(s, val.c_str(), val.length()); } inline void push(lua_State *s, Entity *val) { LuaEntity::push(s, val); } inline void push(lua_State *s, double val) { lua_pushnumber(s, val); } inline void push(lua_State *s, MapObject *val) { LuaMapObject::push(s, val); } /* Pushes an STL LIST */ template void pushSTLContainer(lua_State *s, const std::list &container) { int len = container.size(); lua_createtable(s, len, 0); int table = lua_gettop(s); typename std::list::const_iterator i; i = container.begin(); for (int key = 1; key <= len; key++) { push(s, *i); lua_rawseti(s, table, key); ++i; } } /* Pushes an STL VECTOR */ template void pushSTLContainer(lua_State *s, const std::vector &container) { int len = container.size(); lua_createtable(s, len, 0); int table = lua_gettop(s); for (int key = 0; key < len; key++) { push(s, container.at(key)); lua_rawseti(s, table, key + 1); } } /* Pushes an STL MAP */ template void pushSTLContainer(lua_State *s, const std::map &container) { int len = container.size(); lua_createtable(s, 0, len); int table = lua_gettop(s); typename std::map::const_iterator i; i = container.begin(); for (int key = 1; key <= len; key++) { push(s, i->first); push(s, i->second); lua_settable(s, table); ++i; } } /* Pushes an STL SET */ template void pushSTLContainer(lua_State *s, const std::set &container) { int len = container.size(); lua_createtable(s, len, 0); int table = lua_gettop(s); typename std::set::const_iterator i; i = container.begin(); for (int key = 1; key <= len; key++) { push(s, *i); lua_rawseti(s, table, key); ++i; } } #endif