/* * 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 extern "C" { #include #include } #include #include #include #include #include class Being; class Character; class ItemClass; class MapObject; class Monster; class MonsterClass; class NPC; class StatusEffect; class Thing; // Report script errors and interrupt the script. void raiseScriptError(lua_State *s, const char *format, ...); 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_pushstring(s, "__index"); // metatable, "__index" luaL_register(s, typeName, members); // 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; luaL_newmetatable(s, mTypeName); lua_setmetatable(s, -2); 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 LuaItemClass; typedef LuaUserData LuaMapObject; typedef LuaUserData LuaMonsterClass; typedef LuaUserData LuaStatusEffect; Being * getBeing(lua_State *s, int p); Character * getCharacter(lua_State *s, int p); ItemClass * getItemClass(lua_State *s, int p); Monster * getMonster(lua_State *s, int p); MonsterClass * getMonsterClass(lua_State *s, int p); NPC * getNPC(lua_State *s, int p); Being * checkBeing(lua_State *s, int p); Character * checkCharacter(lua_State *s, int p); ItemClass * checkItemClass(lua_State *s, int p); Monster * checkMonster(lua_State *s, int p); MonsterClass * checkMonsterClass(lua_State *s, int p); NPC * checkNPC(lua_State *s, int p); /* Polymorphic wrapper for pushing variables. Useful for templates.*/ void push(lua_State *s, int val); void push(lua_State *s, const std::string &val); void push(lua_State *s, Thing *val); void push(lua_State *s, double 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, key); push(s, *i); lua_settable(s, table); 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, key+1); push(s, container.at(key)); lua_settable(s, table); } } /* 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, key); push(s, *i); lua_settable(s, table); i++; } } #endif