1
0
Fork 0
mirror of https://github.com/luanti-org/luanti.git synced 2025-06-27 16:36:03 +00:00

Implement helpful __tostring for all userdata-based classes

This commit is contained in:
Lars Mueller 2025-04-27 17:27:55 +02:00 committed by Lars Müller
parent 9ad23e4384
commit 747857bffa
22 changed files with 103 additions and 66 deletions

View file

@ -330,7 +330,7 @@ void LuaAreaStore::Register(lua_State *L)
{"__gc", gc_object}, {"__gc", gc_object},
{0, 0} {0, 0}
}; };
registerClass(L, className, methods, metamethods); registerClass<LuaAreaStore>(L, methods, metamethods);
// Can be created from Lua (AreaStore()) // Can be created from Lua (AreaStore())
lua_register(L, className, create_object); lua_register(L, className, create_object);

View file

@ -90,29 +90,6 @@ bool ModApiBase::registerFunction(lua_State *L, const char *name,
return true; return true;
} }
void ModApiBase::registerClass(lua_State *L, const char *name,
const luaL_Reg *methods,
const luaL_Reg *metamethods)
{
luaL_newmetatable(L, name);
luaL_register(L, NULL, metamethods);
int metatable = lua_gettop(L);
lua_newtable(L);
luaL_register(L, NULL, methods);
int methodtable = lua_gettop(L);
lua_pushvalue(L, methodtable);
lua_setfield(L, metatable, "__index");
// Protect the real metatable.
lua_pushvalue(L, methodtable);
lua_setfield(L, metatable, "__metatable");
// Pop methodtable and metatable.
lua_pop(L, 2);
}
int ModApiBase::l_deprecated_function(lua_State *L, const char *good, const char *bad, lua_CFunction func) int ModApiBase::l_deprecated_function(lua_State *L, const char *good, const char *bad, lua_CFunction func)
{ {
thread_local std::vector<u64> deprecated_logged; thread_local std::vector<u64> deprecated_logged;

View file

@ -60,9 +60,37 @@ public:
lua_CFunction func, lua_CFunction func,
int top); int top);
static void registerClass(lua_State *L, const char *name, template<typename T>
static void registerClass(lua_State *L,
const luaL_Reg *methods, const luaL_Reg *methods,
const luaL_Reg *metamethods); const luaL_Reg *metamethods)
{
luaL_newmetatable(L, T::className);
luaL_register(L, NULL, metamethods);
int metatable = lua_gettop(L);
lua_newtable(L);
luaL_register(L, NULL, methods);
int methodtable = lua_gettop(L);
lua_pushvalue(L, methodtable);
lua_setfield(L, metatable, "__index");
lua_getfield(L, metatable, "__tostring");
bool default_tostring = lua_isnil(L, -1);
lua_pop(L, 1);
if (default_tostring) {
lua_pushcfunction(L, ModApiBase::defaultToString<T>);
lua_setfield(L, metatable, "__tostring");
}
// Protect the real metatable.
lua_pushvalue(L, methodtable);
lua_setfield(L, metatable, "__metatable");
// Pop methodtable and metatable.
lua_pop(L, 2);
}
template<typename T> template<typename T>
static inline T *checkObject(lua_State *L, int narg) static inline T *checkObject(lua_State *L, int narg)
@ -84,4 +112,14 @@ public:
* @return value from `func` * @return value from `func`
*/ */
static int l_deprecated_function(lua_State *L, const char *good, const char *bad, lua_CFunction func); static int l_deprecated_function(lua_State *L, const char *good, const char *bad, lua_CFunction func);
private:
template<typename T>
static int defaultToString(lua_State *L)
{
auto *t = checkObject<T>(L, 1);
lua_pushfstring(L, "%s: %p", T::className, t);
return 1;
}
}; };

View file

@ -175,7 +175,7 @@ void LuaCamera::Register(lua_State *L)
{"__gc", gc_object}, {"__gc", gc_object},
{0, 0} {0, 0}
}; };
registerClass(L, className, methods, metamethods); registerClass<LuaCamera>(L, methods, metamethods);
} }
const char LuaCamera::className[] = "Camera"; const char LuaCamera::className[] = "Camera";

View file

@ -124,7 +124,7 @@ void LuaRaycast::Register(lua_State *L)
{"__gc", gc_object}, {"__gc", gc_object},
{0, 0} {0, 0}
}; };
registerClass(L, className, methods, metamethods); registerClass<LuaRaycast>(L, methods, metamethods);
lua_register(L, className, create_object); lua_register(L, className, create_object);
} }

View file

@ -410,7 +410,7 @@ void InvRef::Register(lua_State *L)
{"__gc", gc_object}, {"__gc", gc_object},
{0, 0} {0, 0}
}; };
registerClass(L, className, methods, metamethods); registerClass<InvRef>(L, methods, metamethods);
// Cannot be created from Lua // Cannot be created from Lua
//lua_register(L, className, create_object); //lua_register(L, className, create_object);

View file

@ -524,7 +524,7 @@ void LuaItemStack::Register(lua_State *L)
{"__eq", l_equals}, {"__eq", l_equals},
{0, 0} {0, 0}
}; };
registerClass(L, className, methods, metamethods); registerClass<LuaItemStack>(L, methods, metamethods);
// Can be created from Lua (ItemStack(itemstack or itemstring or table or nil)) // Can be created from Lua (ItemStack(itemstack or itemstring or table or nil))
lua_register(L, className, create_object); lua_register(L, className, create_object);

View file

@ -81,7 +81,7 @@ void ItemStackMetaRef::create(lua_State *L, LuaItemStack *istack)
void ItemStackMetaRef::Register(lua_State *L) void ItemStackMetaRef::Register(lua_State *L)
{ {
registerMetadataClass(L, className, methods); registerMetadataClass<ItemStackMetaRef>(L, methods);
} }
const char ItemStackMetaRef::className[] = "ItemStackMetaRef"; const char ItemStackMetaRef::className[] = "ItemStackMetaRef";

View file

@ -464,7 +464,7 @@ void LuaLocalPlayer::Register(lua_State *L)
{"__gc", gc_object}, {"__gc", gc_object},
{0, 0} {0, 0}
}; };
registerClass(L, className, methods, metamethods); registerClass<LuaLocalPlayer>(L, methods, metamethods);
} }
const char LuaLocalPlayer::className[] = "LocalPlayer"; const char LuaLocalPlayer::className[] = "LocalPlayer";

View file

@ -315,20 +315,3 @@ int MetaDataRef::l_equals(lua_State *L)
lua_pushboolean(L, *data1 == *data2); lua_pushboolean(L, *data1 == *data2);
return 1; return 1;
} }
void MetaDataRef::registerMetadataClass(lua_State *L, const char *name,
const luaL_Reg *methods)
{
const luaL_Reg metamethods[] = {
{"__eq", l_equals},
{"__gc", gc_object},
{0, 0}
};
registerClass(L, name, methods, metamethods);
// Set metadata_class in the metatable for MetaDataRef::checkAnyMetadata.
luaL_getmetatable(L, name);
lua_pushstring(L, name);
lua_setfield(L, -2, "metadata_class");
lua_pop(L, 1);
}

View file

@ -29,7 +29,22 @@ protected:
virtual void handleToTable(lua_State *L, IMetadata *meta); virtual void handleToTable(lua_State *L, IMetadata *meta);
virtual bool handleFromTable(lua_State *L, int table, IMetadata *meta); virtual bool handleFromTable(lua_State *L, int table, IMetadata *meta);
static void registerMetadataClass(lua_State *L, const char *name, const luaL_Reg *methods); template<class T>
static void registerMetadataClass(lua_State *L, const luaL_Reg *methods)
{
const luaL_Reg metamethods[] = {
{"__eq", l_equals},
{"__gc", gc_object},
{0, 0}
};
registerClass<T>(L, methods, metamethods);
// Set metadata_class in the metatable for MetaDataRef::checkAnyMetadata.
luaL_getmetatable(L, T::className);
lua_pushstring(L, T::className);
lua_setfield(L, -2, "metadata_class");
lua_pop(L, 1);
}
// Exported functions // Exported functions

View file

@ -160,7 +160,7 @@ void LuaMinimap::Register(lua_State *L)
{"__gc", gc_object}, {"__gc", gc_object},
{0, 0} {0, 0}
}; };
registerClass(L, className, methods, metamethods); registerClass<LuaMinimap>(L, methods, metamethods);
} }
const char LuaMinimap::className[] = "Minimap"; const char LuaMinimap::className[] = "Minimap";

View file

@ -77,7 +77,7 @@ void ModChannelRef::Register(lua_State *L)
{"__gc", gc_object}, {"__gc", gc_object},
{0, 0} {0, 0}
}; };
registerClass(L, className, methods, metamethods); registerClass<ModChannelRef>(L, methods, metamethods);
} }
void ModChannelRef::create(lua_State *L, const std::string &channel) void ModChannelRef::create(lua_State *L, const std::string &channel)

View file

@ -186,7 +186,7 @@ const char NodeMetaRef::className[] = "NodeMetaRef";
void NodeMetaRef::Register(lua_State *L) void NodeMetaRef::Register(lua_State *L)
{ {
registerMetadataClass(L, className, methodsServer); registerMetadataClass<NodeMetaRef>(L, methodsServer);
} }
@ -211,7 +211,7 @@ const luaL_Reg NodeMetaRef::methodsServer[] = {
void NodeMetaRef::RegisterClient(lua_State *L) void NodeMetaRef::RegisterClient(lua_State *L)
{ {
registerMetadataClass(L, className, methodsClient); registerMetadataClass<NodeMetaRef>(L, methodsClient);
} }

View file

@ -84,7 +84,7 @@ void NodeTimerRef::Register(lua_State *L)
{"__gc", gc_object}, {"__gc", gc_object},
{0, 0} {0, 0}
}; };
registerClass(L, className, methods, metamethods); registerClass<NodeTimerRef>(L, methods, metamethods);
// Cannot be created from Lua // Cannot be created from Lua
//lua_register(L, className, create_object); //lua_register(L, className, create_object);

View file

@ -101,7 +101,7 @@ void LuaValueNoise::Register(lua_State *L)
{"__gc", gc_object}, {"__gc", gc_object},
{0, 0} {0, 0}
}; };
registerClass(L, className, methods, metamethods); registerClass<LuaValueNoise>(L, methods, metamethods);
lua_register(L, className, create_object); lua_register(L, className, create_object);
@ -360,7 +360,7 @@ void LuaValueNoiseMap::Register(lua_State *L)
{"__gc", gc_object}, {"__gc", gc_object},
{0, 0} {0, 0}
}; };
registerClass(L, className, methods, metamethods); registerClass<LuaValueNoiseMap>(L, methods, metamethods);
lua_register(L, className, create_object); lua_register(L, className, create_object);
@ -449,7 +449,7 @@ void LuaPseudoRandom::Register(lua_State *L)
{"__gc", gc_object}, {"__gc", gc_object},
{0, 0} {0, 0}
}; };
registerClass(L, className, methods, metamethods); registerClass<LuaPseudoRandom>(L, methods, metamethods);
lua_register(L, className, create_object); lua_register(L, className, create_object);
} }
@ -562,7 +562,7 @@ void LuaPcgRandom::Register(lua_State *L)
{"__gc", gc_object}, {"__gc", gc_object},
{0, 0} {0, 0}
}; };
registerClass(L, className, methods, metamethods); registerClass<LuaPcgRandom>(L, methods, metamethods);
lua_register(L, className, create_object); lua_register(L, className, create_object);
} }
@ -652,7 +652,7 @@ void LuaSecureRandom::Register(lua_State *L)
{"__gc", gc_object}, {"__gc", gc_object},
{0, 0} {0, 0}
}; };
registerClass(L, className, methods, metamethods); registerClass<LuaSecureRandom>(L, methods, metamethods);
lua_register(L, className, create_object); lua_register(L, className, create_object);
} }

View file

@ -69,8 +69,27 @@ RemotePlayer *ObjectRef::getplayer(ObjectRef *ref)
// Exported functions // Exported functions
int ObjectRef::mt_tostring(lua_State *L)
{
auto *ref = checkObject<ObjectRef>(L, 1);
if (getobject(ref)) {
if (auto *player = getplayer(ref)) {
lua_pushfstring(L, "ObjectRef (player): %s", player->getName().c_str());
} else if (auto *entitysao = getluaobject(ref)) {
lua_pushfstring(L, "ObjectRef (entity): %s (id: %d)",
entitysao->getName().c_str(), entitysao->getId());
} else {
lua_pushfstring(L, "ObjectRef (?): %p", ref);
}
} else {
lua_pushfstring(L, "ObjectRef (invalid): %p", ref);
}
return 1;
}
// garbage collector // garbage collector
int ObjectRef::gc_object(lua_State *L) { int ObjectRef::gc_object(lua_State *L)
{
ObjectRef *obj = *(ObjectRef **)(lua_touserdata(L, 1)); ObjectRef *obj = *(ObjectRef **)(lua_touserdata(L, 1));
delete obj; delete obj;
return 0; return 0;
@ -2806,9 +2825,10 @@ void ObjectRef::Register(lua_State *L)
{ {
static const luaL_Reg metamethods[] = { static const luaL_Reg metamethods[] = {
{"__gc", gc_object}, {"__gc", gc_object},
{"__tostring", mt_tostring},
{0, 0} {0, 0}
}; };
registerClass(L, className, methods, metamethods); registerClass<ObjectRef>(L, methods, metamethods);
} }
const char ObjectRef::className[] = "ObjectRef"; const char ObjectRef::className[] = "ObjectRef";

View file

@ -6,6 +6,7 @@
#include "lua_api/l_base.h" #include "lua_api/l_base.h"
#include "irrlichttypes.h" #include "irrlichttypes.h"
#include <lua.h>
class ServerActiveObject; class ServerActiveObject;
class LuaEntitySAO; class LuaEntitySAO;
@ -48,6 +49,9 @@ private:
// Exported functions // Exported functions
// __tostring metamethod
static int mt_tostring(lua_State *L);
// garbage collector // garbage collector
static int gc_object(lua_State *L); static int gc_object(lua_State *L);

View file

@ -44,7 +44,7 @@ void PlayerMetaRef::create(lua_State *L, ServerEnvironment *env, std::string_vie
void PlayerMetaRef::Register(lua_State *L) void PlayerMetaRef::Register(lua_State *L)
{ {
registerMetadataClass(L, className, methods); registerMetadataClass<PlayerMetaRef>(L, methods);
} }
const char PlayerMetaRef::className[] = "PlayerMetaRef"; const char PlayerMetaRef::className[] = "PlayerMetaRef";

View file

@ -362,7 +362,7 @@ void LuaSettings::Register(lua_State* L)
{"__gc", gc_object}, {"__gc", gc_object},
{0, 0} {0, 0}
}; };
registerClass(L, className, methods, metamethods); registerClass<LuaSettings>(L, methods, metamethods);
// Can be created from Lua (Settings(filename)) // Can be created from Lua (Settings(filename))
lua_register(L, className, create_object); lua_register(L, className, create_object);

View file

@ -36,7 +36,7 @@ void StorageRef::create(lua_State *L, const std::string &mod_name, ModStorageDat
void StorageRef::Register(lua_State *L) void StorageRef::Register(lua_State *L)
{ {
registerMetadataClass(L, className, methods); registerMetadataClass<StorageRef>(L, methods);
} }
IMetadata* StorageRef::getmeta(bool auto_create) IMetadata* StorageRef::getmeta(bool auto_create)

View file

@ -425,7 +425,7 @@ void LuaVoxelManip::Register(lua_State *L)
{"__gc", gc_object}, {"__gc", gc_object},
{0, 0} {0, 0}
}; };
registerClass(L, className, methods, metamethods); registerClass<LuaVoxelManip>(L, methods, metamethods);
// Can be created from Lua (VoxelManip()) // Can be created from Lua (VoxelManip())
lua_register(L, className, create_object); lua_register(L, className, create_object);