From 747857bffa73df6dbec5820c12879366fcade444 Mon Sep 17 00:00:00 2001 From: Lars Mueller Date: Sun, 27 Apr 2025 17:27:55 +0200 Subject: [PATCH] Implement helpful `__tostring` for all userdata-based classes --- src/script/lua_api/l_areastore.cpp | 2 +- src/script/lua_api/l_base.cpp | 23 -------------- src/script/lua_api/l_base.h | 42 ++++++++++++++++++++++++-- src/script/lua_api/l_camera.cpp | 2 +- src/script/lua_api/l_env.cpp | 2 +- src/script/lua_api/l_inventory.cpp | 2 +- src/script/lua_api/l_item.cpp | 2 +- src/script/lua_api/l_itemstackmeta.cpp | 2 +- src/script/lua_api/l_localplayer.cpp | 2 +- src/script/lua_api/l_metadata.cpp | 17 ----------- src/script/lua_api/l_metadata.h | 17 ++++++++++- src/script/lua_api/l_minimap.cpp | 2 +- src/script/lua_api/l_modchannels.cpp | 2 +- src/script/lua_api/l_nodemeta.cpp | 4 +-- src/script/lua_api/l_nodetimer.cpp | 2 +- src/script/lua_api/l_noise.cpp | 10 +++--- src/script/lua_api/l_object.cpp | 24 +++++++++++++-- src/script/lua_api/l_object.h | 4 +++ src/script/lua_api/l_playermeta.cpp | 2 +- src/script/lua_api/l_settings.cpp | 2 +- src/script/lua_api/l_storage.cpp | 2 +- src/script/lua_api/l_vmanip.cpp | 2 +- 22 files changed, 103 insertions(+), 66 deletions(-) diff --git a/src/script/lua_api/l_areastore.cpp b/src/script/lua_api/l_areastore.cpp index dd5edfcc4..fb5a52a5c 100644 --- a/src/script/lua_api/l_areastore.cpp +++ b/src/script/lua_api/l_areastore.cpp @@ -330,7 +330,7 @@ void LuaAreaStore::Register(lua_State *L) {"__gc", gc_object}, {0, 0} }; - registerClass(L, className, methods, metamethods); + registerClass(L, methods, metamethods); // Can be created from Lua (AreaStore()) lua_register(L, className, create_object); diff --git a/src/script/lua_api/l_base.cpp b/src/script/lua_api/l_base.cpp index c25c80724..c95acc733 100644 --- a/src/script/lua_api/l_base.cpp +++ b/src/script/lua_api/l_base.cpp @@ -90,29 +90,6 @@ bool ModApiBase::registerFunction(lua_State *L, const char *name, 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) { thread_local std::vector deprecated_logged; diff --git a/src/script/lua_api/l_base.h b/src/script/lua_api/l_base.h index d0448009a..138b4cf24 100644 --- a/src/script/lua_api/l_base.h +++ b/src/script/lua_api/l_base.h @@ -60,9 +60,37 @@ public: lua_CFunction func, int top); - static void registerClass(lua_State *L, const char *name, + template + static void registerClass(lua_State *L, 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); + 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 static inline T *checkObject(lua_State *L, int narg) @@ -84,4 +112,14 @@ public: * @return value from `func` */ static int l_deprecated_function(lua_State *L, const char *good, const char *bad, lua_CFunction func); + +private: + + template + static int defaultToString(lua_State *L) + { + auto *t = checkObject(L, 1); + lua_pushfstring(L, "%s: %p", T::className, t); + return 1; + } }; diff --git a/src/script/lua_api/l_camera.cpp b/src/script/lua_api/l_camera.cpp index a00f92fac..08917b993 100644 --- a/src/script/lua_api/l_camera.cpp +++ b/src/script/lua_api/l_camera.cpp @@ -175,7 +175,7 @@ void LuaCamera::Register(lua_State *L) {"__gc", gc_object}, {0, 0} }; - registerClass(L, className, methods, metamethods); + registerClass(L, methods, metamethods); } const char LuaCamera::className[] = "Camera"; diff --git a/src/script/lua_api/l_env.cpp b/src/script/lua_api/l_env.cpp index 195e002ec..aafde7540 100644 --- a/src/script/lua_api/l_env.cpp +++ b/src/script/lua_api/l_env.cpp @@ -124,7 +124,7 @@ void LuaRaycast::Register(lua_State *L) {"__gc", gc_object}, {0, 0} }; - registerClass(L, className, methods, metamethods); + registerClass(L, methods, metamethods); lua_register(L, className, create_object); } diff --git a/src/script/lua_api/l_inventory.cpp b/src/script/lua_api/l_inventory.cpp index 10bebb85e..20f3bc470 100644 --- a/src/script/lua_api/l_inventory.cpp +++ b/src/script/lua_api/l_inventory.cpp @@ -410,7 +410,7 @@ void InvRef::Register(lua_State *L) {"__gc", gc_object}, {0, 0} }; - registerClass(L, className, methods, metamethods); + registerClass(L, methods, metamethods); // Cannot be created from Lua //lua_register(L, className, create_object); diff --git a/src/script/lua_api/l_item.cpp b/src/script/lua_api/l_item.cpp index 04182041e..bae8aa2d2 100644 --- a/src/script/lua_api/l_item.cpp +++ b/src/script/lua_api/l_item.cpp @@ -524,7 +524,7 @@ void LuaItemStack::Register(lua_State *L) {"__eq", l_equals}, {0, 0} }; - registerClass(L, className, methods, metamethods); + registerClass(L, methods, metamethods); // Can be created from Lua (ItemStack(itemstack or itemstring or table or nil)) lua_register(L, className, create_object); diff --git a/src/script/lua_api/l_itemstackmeta.cpp b/src/script/lua_api/l_itemstackmeta.cpp index d6716bd66..250c84b7f 100644 --- a/src/script/lua_api/l_itemstackmeta.cpp +++ b/src/script/lua_api/l_itemstackmeta.cpp @@ -81,7 +81,7 @@ void ItemStackMetaRef::create(lua_State *L, LuaItemStack *istack) void ItemStackMetaRef::Register(lua_State *L) { - registerMetadataClass(L, className, methods); + registerMetadataClass(L, methods); } const char ItemStackMetaRef::className[] = "ItemStackMetaRef"; diff --git a/src/script/lua_api/l_localplayer.cpp b/src/script/lua_api/l_localplayer.cpp index 49900f145..0e0ceda2f 100644 --- a/src/script/lua_api/l_localplayer.cpp +++ b/src/script/lua_api/l_localplayer.cpp @@ -464,7 +464,7 @@ void LuaLocalPlayer::Register(lua_State *L) {"__gc", gc_object}, {0, 0} }; - registerClass(L, className, methods, metamethods); + registerClass(L, methods, metamethods); } const char LuaLocalPlayer::className[] = "LocalPlayer"; diff --git a/src/script/lua_api/l_metadata.cpp b/src/script/lua_api/l_metadata.cpp index 53a87c874..1dc72484a 100644 --- a/src/script/lua_api/l_metadata.cpp +++ b/src/script/lua_api/l_metadata.cpp @@ -315,20 +315,3 @@ int MetaDataRef::l_equals(lua_State *L) lua_pushboolean(L, *data1 == *data2); 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); -} diff --git a/src/script/lua_api/l_metadata.h b/src/script/lua_api/l_metadata.h index a961e0ae3..8accd7148 100644 --- a/src/script/lua_api/l_metadata.h +++ b/src/script/lua_api/l_metadata.h @@ -29,7 +29,22 @@ protected: virtual void handleToTable(lua_State *L, 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 + static void registerMetadataClass(lua_State *L, const luaL_Reg *methods) + { + const luaL_Reg metamethods[] = { + {"__eq", l_equals}, + {"__gc", gc_object}, + {0, 0} + }; + registerClass(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 diff --git a/src/script/lua_api/l_minimap.cpp b/src/script/lua_api/l_minimap.cpp index b79c23627..e040e5a10 100644 --- a/src/script/lua_api/l_minimap.cpp +++ b/src/script/lua_api/l_minimap.cpp @@ -160,7 +160,7 @@ void LuaMinimap::Register(lua_State *L) {"__gc", gc_object}, {0, 0} }; - registerClass(L, className, methods, metamethods); + registerClass(L, methods, metamethods); } const char LuaMinimap::className[] = "Minimap"; diff --git a/src/script/lua_api/l_modchannels.cpp b/src/script/lua_api/l_modchannels.cpp index d3dd7495f..1a2f46bb8 100644 --- a/src/script/lua_api/l_modchannels.cpp +++ b/src/script/lua_api/l_modchannels.cpp @@ -77,7 +77,7 @@ void ModChannelRef::Register(lua_State *L) {"__gc", gc_object}, {0, 0} }; - registerClass(L, className, methods, metamethods); + registerClass(L, methods, metamethods); } void ModChannelRef::create(lua_State *L, const std::string &channel) diff --git a/src/script/lua_api/l_nodemeta.cpp b/src/script/lua_api/l_nodemeta.cpp index 40285bfe7..11362a1e5 100644 --- a/src/script/lua_api/l_nodemeta.cpp +++ b/src/script/lua_api/l_nodemeta.cpp @@ -186,7 +186,7 @@ const char NodeMetaRef::className[] = "NodeMetaRef"; void NodeMetaRef::Register(lua_State *L) { - registerMetadataClass(L, className, methodsServer); + registerMetadataClass(L, methodsServer); } @@ -211,7 +211,7 @@ const luaL_Reg NodeMetaRef::methodsServer[] = { void NodeMetaRef::RegisterClient(lua_State *L) { - registerMetadataClass(L, className, methodsClient); + registerMetadataClass(L, methodsClient); } diff --git a/src/script/lua_api/l_nodetimer.cpp b/src/script/lua_api/l_nodetimer.cpp index 7a4319e8f..999ede129 100644 --- a/src/script/lua_api/l_nodetimer.cpp +++ b/src/script/lua_api/l_nodetimer.cpp @@ -84,7 +84,7 @@ void NodeTimerRef::Register(lua_State *L) {"__gc", gc_object}, {0, 0} }; - registerClass(L, className, methods, metamethods); + registerClass(L, methods, metamethods); // Cannot be created from Lua //lua_register(L, className, create_object); diff --git a/src/script/lua_api/l_noise.cpp b/src/script/lua_api/l_noise.cpp index 5ad57835b..73cac5b38 100644 --- a/src/script/lua_api/l_noise.cpp +++ b/src/script/lua_api/l_noise.cpp @@ -101,7 +101,7 @@ void LuaValueNoise::Register(lua_State *L) {"__gc", gc_object}, {0, 0} }; - registerClass(L, className, methods, metamethods); + registerClass(L, methods, metamethods); lua_register(L, className, create_object); @@ -360,7 +360,7 @@ void LuaValueNoiseMap::Register(lua_State *L) {"__gc", gc_object}, {0, 0} }; - registerClass(L, className, methods, metamethods); + registerClass(L, methods, metamethods); lua_register(L, className, create_object); @@ -449,7 +449,7 @@ void LuaPseudoRandom::Register(lua_State *L) {"__gc", gc_object}, {0, 0} }; - registerClass(L, className, methods, metamethods); + registerClass(L, methods, metamethods); lua_register(L, className, create_object); } @@ -562,7 +562,7 @@ void LuaPcgRandom::Register(lua_State *L) {"__gc", gc_object}, {0, 0} }; - registerClass(L, className, methods, metamethods); + registerClass(L, methods, metamethods); lua_register(L, className, create_object); } @@ -652,7 +652,7 @@ void LuaSecureRandom::Register(lua_State *L) {"__gc", gc_object}, {0, 0} }; - registerClass(L, className, methods, metamethods); + registerClass(L, methods, metamethods); lua_register(L, className, create_object); } diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index 816f42857..19c513dd3 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -69,8 +69,27 @@ RemotePlayer *ObjectRef::getplayer(ObjectRef *ref) // Exported functions +int ObjectRef::mt_tostring(lua_State *L) +{ + auto *ref = checkObject(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 -int ObjectRef::gc_object(lua_State *L) { +int ObjectRef::gc_object(lua_State *L) +{ ObjectRef *obj = *(ObjectRef **)(lua_touserdata(L, 1)); delete obj; return 0; @@ -2806,9 +2825,10 @@ void ObjectRef::Register(lua_State *L) { static const luaL_Reg metamethods[] = { {"__gc", gc_object}, + {"__tostring", mt_tostring}, {0, 0} }; - registerClass(L, className, methods, metamethods); + registerClass(L, methods, metamethods); } const char ObjectRef::className[] = "ObjectRef"; diff --git a/src/script/lua_api/l_object.h b/src/script/lua_api/l_object.h index 43415f5ef..f97d7d2da 100644 --- a/src/script/lua_api/l_object.h +++ b/src/script/lua_api/l_object.h @@ -6,6 +6,7 @@ #include "lua_api/l_base.h" #include "irrlichttypes.h" +#include class ServerActiveObject; class LuaEntitySAO; @@ -48,6 +49,9 @@ private: // Exported functions + // __tostring metamethod + static int mt_tostring(lua_State *L); + // garbage collector static int gc_object(lua_State *L); diff --git a/src/script/lua_api/l_playermeta.cpp b/src/script/lua_api/l_playermeta.cpp index e33df3e9f..8c33ace21 100644 --- a/src/script/lua_api/l_playermeta.cpp +++ b/src/script/lua_api/l_playermeta.cpp @@ -44,7 +44,7 @@ void PlayerMetaRef::create(lua_State *L, ServerEnvironment *env, std::string_vie void PlayerMetaRef::Register(lua_State *L) { - registerMetadataClass(L, className, methods); + registerMetadataClass(L, methods); } const char PlayerMetaRef::className[] = "PlayerMetaRef"; diff --git a/src/script/lua_api/l_settings.cpp b/src/script/lua_api/l_settings.cpp index 089b8a01e..121670dbe 100644 --- a/src/script/lua_api/l_settings.cpp +++ b/src/script/lua_api/l_settings.cpp @@ -362,7 +362,7 @@ void LuaSettings::Register(lua_State* L) {"__gc", gc_object}, {0, 0} }; - registerClass(L, className, methods, metamethods); + registerClass(L, methods, metamethods); // Can be created from Lua (Settings(filename)) lua_register(L, className, create_object); diff --git a/src/script/lua_api/l_storage.cpp b/src/script/lua_api/l_storage.cpp index d03aeed78..96f346857 100644 --- a/src/script/lua_api/l_storage.cpp +++ b/src/script/lua_api/l_storage.cpp @@ -36,7 +36,7 @@ void StorageRef::create(lua_State *L, const std::string &mod_name, ModStorageDat void StorageRef::Register(lua_State *L) { - registerMetadataClass(L, className, methods); + registerMetadataClass(L, methods); } IMetadata* StorageRef::getmeta(bool auto_create) diff --git a/src/script/lua_api/l_vmanip.cpp b/src/script/lua_api/l_vmanip.cpp index 464cd9fd3..5815d3bf3 100644 --- a/src/script/lua_api/l_vmanip.cpp +++ b/src/script/lua_api/l_vmanip.cpp @@ -425,7 +425,7 @@ void LuaVoxelManip::Register(lua_State *L) {"__gc", gc_object}, {0, 0} }; - registerClass(L, className, methods, metamethods); + registerClass(L, methods, metamethods); // Can be created from Lua (VoxelManip()) lua_register(L, className, create_object);