From 91615f9588420fd716978552fdacf1323b8df11c Mon Sep 17 00:00:00 2001 From: rubenwardy Date: Fri, 6 Apr 2018 09:52:29 +0100 Subject: [PATCH] Add player:get_meta(), deprecate player attributes (#7202) * Add player:get_meta(), deprecate player attributes --- build/android/jni/Android.mk | 1 + doc/lua_api.txt | 16 +++- games/minimal/mods/test/init.lua | 34 ++++++- src/content_sao.h | 49 +--------- src/database/database-postgresql.cpp | 6 +- src/database/database-sqlite3.cpp | 6 +- src/itemstackmetadata.cpp | 20 ++++ src/itemstackmetadata.h | 2 +- src/metadata.cpp | 14 +++ src/metadata.h | 7 +- src/remoteplayer.cpp | 8 +- src/script/lua_api/CMakeLists.txt | 1 + src/script/lua_api/l_itemstackmeta.cpp | 2 + src/script/lua_api/l_itemstackmeta.h | 2 + src/script/lua_api/l_metadata.cpp | 1 + src/script/lua_api/l_metadata.h | 3 +- src/script/lua_api/l_object.cpp | 27 ++++-- src/script/lua_api/l_object.h | 3 + src/script/lua_api/l_playermeta.cpp | 121 +++++++++++++++++++++++++ src/script/lua_api/l_playermeta.h | 57 ++++++++++++ src/script/scripting_server.cpp | 2 + src/serverenvironment.cpp | 2 +- 22 files changed, 314 insertions(+), 70 deletions(-) create mode 100644 src/script/lua_api/l_playermeta.cpp create mode 100644 src/script/lua_api/l_playermeta.h diff --git a/build/android/jni/Android.mk b/build/android/jni/Android.mk index d5e87bc1f..a4e55eaa9 100644 --- a/build/android/jni/Android.mk +++ b/build/android/jni/Android.mk @@ -347,6 +347,7 @@ LOCAL_SRC_FILES += \ jni/src/script/lua_api/l_nodetimer.cpp \ jni/src/script/lua_api/l_noise.cpp \ jni/src/script/lua_api/l_object.cpp \ + jni/src/script/lua_api/l_playermeta.cpp \ jni/src/script/lua_api/l_particles.cpp \ jni/src/script/lua_api/l_particles_local.cpp\ jni/src/script/lua_api/l_rollback.cpp \ diff --git a/doc/lua_api.txt b/doc/lua_api.txt index ea8e8b254..620828824 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -3902,7 +3902,7 @@ An interface to use mod channels on client and server * Message size is limited to 65535 characters by protocol. ### `MetaDataRef` -See `StorageRef`, `NodeMetaRef` and `ItemStackMetaRef`. +See `StorageRef`, `NodeMetaRef`, `ItemStackMetaRef`, and `PlayerMetaRef`. #### Methods * `set_string(name, value)` @@ -3952,6 +3952,15 @@ Can be obtained via `minetest.get_mod_storage()` during load time. #### Methods * All methods in MetaDataRef +### `PlayerMetaRef` +Player metadata. +Uses the same method of storage as the deprecated player attribute API, so +data there will also be in player meta. +Can be obtained using `player:get_meta()`. + +#### Methods +* All methods in MetaDataRef + ### `NodeTimerRef` Node Timers: a high resolution persistent per-node timer. Can be gotten via `minetest.get_node_timer(pos)`. @@ -4101,14 +4110,15 @@ This is basically a reference to a C++ `ServerActiveObject` * `0`: player is drowning * max: bubbles bar is not shown * See Object Properties for more information -* `set_attribute(attribute, value)`: +* `set_attribute(attribute, value)`: DEPRECATED, use get_meta() instead * Sets an extra attribute with value on player. * `value` must be a string, or a number which will be converted to a string. * If `value` is `nil`, remove attribute from player. -* `get_attribute(attribute)`: +* `get_attribute(attribute)`: DEPRECATED, use get_meta() instead * Returns value (a string) for extra attribute. * Returns `nil` if no attribute found. +* `get_meta()`: Returns a PlayerMetaRef. * `set_inventory_formspec(formspec)` * Redefine player's inventory form * Should usually be called in `on_joinplayer` diff --git a/games/minimal/mods/test/init.lua b/games/minimal/mods/test/init.lua index b08ddfa94..9b826bce6 100644 --- a/games/minimal/mods/test/init.lua +++ b/games/minimal/mods/test/init.lua @@ -13,7 +13,7 @@ assert(pseudo:next() == 13854) -- HP Change Reasons -- local expect = nil -minetest.register_on_joinplayer(function(player) +local function run_hpchangereason_tests(player) expect = { type = "set_hp", from = "mod" } player:set_hp(3) assert(expect == nil) @@ -25,8 +25,7 @@ minetest.register_on_joinplayer(function(player) expect = { df = 3458973454, type = "fall", from = "mod" } player:set_hp(10, { type = "fall", df = 3458973454 }) assert(expect == nil) -end) - +end minetest.register_on_player_hpchange(function(player, hp, reason) for key, value in pairs(reason) do assert(expect[key] == value) @@ -38,3 +37,32 @@ minetest.register_on_player_hpchange(function(player, hp, reason) expect = nil end) + + + +local function run_player_meta_tests(player) + local meta = player:get_meta() + meta:set_string("foo", "bar") + assert(meta:get_string("foo") == "bar") + + local meta2 = player:get_meta() + assert(meta2:get_string("foo") == "bar") + assert(meta:equals(meta2)) + assert(player:get_attribute("foo") == "bar") + + meta:set_string("bob", "dillan") + assert(meta:get_string("foo") == "bar") + assert(meta:get_string("bob") == "dillan") + assert(meta2:get_string("foo") == "bar") + assert(meta2:get_string("bob") == "dillan") + assert(meta:equals(meta2)) + assert(player:get_attribute("foo") == "bar") + assert(player:get_attribute("bob") == "dillan") +end + +local function run_player_tests(player) + run_hpchangereason_tests(player) + run_player_meta_tests(player) + minetest.chat_send_all("All tests pass!") +end +minetest.register_on_joinplayer(run_player_tests) diff --git a/src/content_sao.h b/src/content_sao.h index 3f75a7890..6583f31d6 100644 --- a/src/content_sao.h +++ b/src/content_sao.h @@ -197,7 +197,6 @@ public: } }; -typedef std::unordered_map PlayerAttributes; class RemotePlayer; class PlayerSAO : public UnitSAO @@ -269,49 +268,6 @@ public: int getWieldIndex() const; void setWieldIndex(int i); - /* - Modding interface - */ - inline void setExtendedAttribute(const std::string &attr, const std::string &value) - { - m_extra_attributes[attr] = value; - m_extended_attributes_modified = true; - } - - inline bool getExtendedAttribute(const std::string &attr, std::string *value) - { - if (m_extra_attributes.find(attr) == m_extra_attributes.end()) - return false; - - *value = m_extra_attributes[attr]; - return true; - } - - inline void removeExtendedAttribute(const std::string &attr) - { - PlayerAttributes::iterator it = m_extra_attributes.find(attr); - if (it == m_extra_attributes.end()) - return; - - m_extra_attributes.erase(it); - m_extended_attributes_modified = true; - } - - inline const PlayerAttributes &getExtendedAttributes() - { - return m_extra_attributes; - } - - inline bool extendedAttributesModified() const - { - return m_extended_attributes_modified; - } - - inline void setExtendedAttributeModified(bool v) - { - m_extended_attributes_modified = v; - } - /* PlayerSAO-specific */ @@ -375,6 +331,8 @@ public: v3f getEyePosition() const { return m_base_position + getEyeOffset(); } v3f getEyeOffset() const; + inline Metadata &getMeta() { return m_meta; } + private: std::string getPropertyPacket(); void unlinkPlayerSessionAndSave(); @@ -410,8 +368,7 @@ private: f32 m_fov = 0.0f; s16 m_wanted_range = 0.0f; - PlayerAttributes m_extra_attributes; - bool m_extended_attributes_modified = false; + Metadata m_meta; public: float m_physics_override_speed = 1.0f; float m_physics_override_jump = 1.0f; diff --git a/src/database/database-postgresql.cpp b/src/database/database-postgresql.cpp index 74651135a..2d9c3b49e 100644 --- a/src/database/database-postgresql.cpp +++ b/src/database/database-postgresql.cpp @@ -518,7 +518,7 @@ void PlayerDatabasePostgreSQL::savePlayer(RemotePlayer *player) } execPrepared("remove_player_metadata", 1, rmvalues); - const PlayerAttributes &attrs = sao->getExtendedAttributes(); + const StringMap &attrs = sao->getMeta().getStrings(); for (const auto &attr : attrs) { const char *meta_values[] = { player->getName(), @@ -527,6 +527,7 @@ void PlayerDatabasePostgreSQL::savePlayer(RemotePlayer *player) }; execPrepared("save_player_metadata", 3, meta_values); } + sao->getMeta().setModified(false); endSave(); } @@ -594,8 +595,9 @@ bool PlayerDatabasePostgreSQL::loadPlayer(RemotePlayer *player, PlayerSAO *sao) int numrows = PQntuples(results); for (int row = 0; row < numrows; row++) { - sao->setExtendedAttribute(PQgetvalue(results, row, 0),PQgetvalue(results, row, 1)); + sao->getMeta().setString(PQgetvalue(results, row, 0), PQgetvalue(results, row, 1)); } + sao->getMeta().setModified(false); PQclear(results); diff --git a/src/database/database-sqlite3.cpp b/src/database/database-sqlite3.cpp index 78c182f86..76935ada4 100644 --- a/src/database/database-sqlite3.cpp +++ b/src/database/database-sqlite3.cpp @@ -520,7 +520,7 @@ void PlayerDatabaseSQLite3::savePlayer(RemotePlayer *player) sqlite3_vrfy(sqlite3_step(m_stmt_player_metadata_remove), SQLITE_DONE); sqlite3_reset(m_stmt_player_metadata_remove); - const PlayerAttributes &attrs = sao->getExtendedAttributes(); + const StringMap &attrs = sao->getMeta().getStrings(); for (const auto &attr : attrs) { str_to_sqlite(m_stmt_player_metadata_add, 1, player->getName()); str_to_sqlite(m_stmt_player_metadata_add, 2, attr.first); @@ -528,6 +528,7 @@ void PlayerDatabaseSQLite3::savePlayer(RemotePlayer *player) sqlite3_vrfy(sqlite3_step(m_stmt_player_metadata_add), SQLITE_DONE); sqlite3_reset(m_stmt_player_metadata_add); } + sao->getMeta().setModified(false); endSave(); } @@ -578,8 +579,9 @@ bool PlayerDatabaseSQLite3::loadPlayer(RemotePlayer *player, PlayerSAO *sao) std::string attr = sqlite_to_string(m_stmt_player_metadata_load, 0); std::string value = sqlite_to_string(m_stmt_player_metadata_load, 1); - sao->setExtendedAttribute(attr, value); + sao->getMeta().setString(attr, value); } + sao->getMeta().setModified(false); sqlite3_reset(m_stmt_player_metadata_load); return true; } diff --git a/src/itemstackmetadata.cpp b/src/itemstackmetadata.cpp index 53c8bad83..4aa1a0903 100644 --- a/src/itemstackmetadata.cpp +++ b/src/itemstackmetadata.cpp @@ -1,3 +1,23 @@ +/* +Minetest +Copyright (C) 2017-8 rubenwardy + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + + #include "itemstackmetadata.h" #include "util/serialize.h" #include "util/strfnd.h" diff --git a/src/itemstackmetadata.h b/src/itemstackmetadata.h index 0e1977c8c..a7f134955 100644 --- a/src/itemstackmetadata.h +++ b/src/itemstackmetadata.h @@ -1,6 +1,6 @@ /* Minetest -Copyright (C) 2010-2013 rubenwardy +Copyright (C) 2017-8 rubenwardy This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by diff --git a/src/metadata.cpp b/src/metadata.cpp index 6ad65e007..453ac1c9a 100644 --- a/src/metadata.cpp +++ b/src/metadata.cpp @@ -27,6 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc., void Metadata::clear() { m_stringvars.clear(); + m_modified = true; } bool Metadata::empty() const @@ -68,6 +69,18 @@ const std::string &Metadata::getString(const std::string &name, u16 recursion) c return resolveString(it->second, recursion); } +bool Metadata::getStringToRef( + const std::string &name, std::string &str, u16 recursion) const +{ + StringMap::const_iterator it = m_stringvars.find(name); + if (it == m_stringvars.end()) { + return false; + } + + str = resolveString(it->second, recursion); + return true; +} + /** * Sets var to name key in the metadata storage * @@ -88,6 +101,7 @@ bool Metadata::setString(const std::string &name, const std::string &var) } m_stringvars[name] = var; + m_modified = true; return true; } diff --git a/src/metadata.h b/src/metadata.h index d95a0ed5d..5333f8a9d 100644 --- a/src/metadata.h +++ b/src/metadata.h @@ -26,6 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc., class Metadata { + bool m_modified = false; public: virtual ~Metadata() = default; @@ -45,14 +46,18 @@ public: size_t size() const; bool contains(const std::string &name) const; const std::string &getString(const std::string &name, u16 recursion = 0) const; + bool getStringToRef(const std::string &name, std::string &str, u16 recursion = 0) const; virtual bool setString(const std::string &name, const std::string &var); + inline bool removeString(const std::string &name) { return setString(name, ""); } const StringMap &getStrings() const { return m_stringvars; } // Add support for variable names in values const std::string &resolveString(const std::string &str, u16 recursion = 0) const; + + inline bool isModified() const { return m_modified; } + inline void setModified(bool v) { m_modified = v; } protected: StringMap m_stringvars; - }; diff --git a/src/remoteplayer.cpp b/src/remoteplayer.cpp index f8f31d928..d070eb7e4 100644 --- a/src/remoteplayer.cpp +++ b/src/remoteplayer.cpp @@ -72,14 +72,15 @@ void RemotePlayer::serializeExtraAttributes(std::string &output) { assert(m_sao); Json::Value json_root; - const PlayerAttributes &attrs = m_sao->getExtendedAttributes(); + + const StringMap &attrs = m_sao->getMeta().getStrings(); for (const auto &attr : attrs) { json_root[attr.first] = attr.second; } output = fastWriteJson(json_root); - m_sao->setExtendedAttributeModified(false); + m_sao->getMeta().setModified(false); } @@ -132,8 +133,9 @@ void RemotePlayer::deSerialize(std::istream &is, const std::string &playername, const Json::Value::Members attr_list = attr_root.getMemberNames(); for (const auto &it : attr_list) { Json::Value attr_value = attr_root[it]; - sao->setExtendedAttribute(it, attr_value.asString()); + sao->getMeta().setString(it, attr_value.asString()); } + sao->getMeta().setModified(false); } catch (SettingNotFoundException &e) {} } diff --git a/src/script/lua_api/CMakeLists.txt b/src/script/lua_api/CMakeLists.txt index a55ea6635..97c3786ec 100644 --- a/src/script/lua_api/CMakeLists.txt +++ b/src/script/lua_api/CMakeLists.txt @@ -15,6 +15,7 @@ set(common_SCRIPT_LUA_API_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/l_noise.cpp ${CMAKE_CURRENT_SOURCE_DIR}/l_object.cpp ${CMAKE_CURRENT_SOURCE_DIR}/l_particles.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/l_playermeta.cpp ${CMAKE_CURRENT_SOURCE_DIR}/l_rollback.cpp ${CMAKE_CURRENT_SOURCE_DIR}/l_server.cpp ${CMAKE_CURRENT_SOURCE_DIR}/l_settings.cpp diff --git a/src/script/lua_api/l_itemstackmeta.cpp b/src/script/lua_api/l_itemstackmeta.cpp index 3cbc3d0f7..0661ec25d 100644 --- a/src/script/lua_api/l_itemstackmeta.cpp +++ b/src/script/lua_api/l_itemstackmeta.cpp @@ -1,6 +1,8 @@ /* Minetest Copyright (C) 2013 celeron55, Perttu Ahola +Copyright (C) 2017-8 rubenwardy +Copyright (C) 2017 raymoo This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by diff --git a/src/script/lua_api/l_itemstackmeta.h b/src/script/lua_api/l_itemstackmeta.h index 46c40ab30..2f4c37fd9 100644 --- a/src/script/lua_api/l_itemstackmeta.h +++ b/src/script/lua_api/l_itemstackmeta.h @@ -1,6 +1,8 @@ /* Minetest Copyright (C) 2013 celeron55, Perttu Ahola +Copyright (C) 2017-8 rubenwardy +Copyright (C) 2017 raymoo This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by diff --git a/src/script/lua_api/l_metadata.cpp b/src/script/lua_api/l_metadata.cpp index e84d1d939..69f495c4a 100644 --- a/src/script/lua_api/l_metadata.cpp +++ b/src/script/lua_api/l_metadata.cpp @@ -1,6 +1,7 @@ /* Minetest Copyright (C) 2013 celeron55, Perttu Ahola +Copyright (C) 2017-8 rubenwardy This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by diff --git a/src/script/lua_api/l_metadata.h b/src/script/lua_api/l_metadata.h index e0e9696cb..3aaf62d4a 100644 --- a/src/script/lua_api/l_metadata.h +++ b/src/script/lua_api/l_metadata.h @@ -1,6 +1,7 @@ /* Minetest -Copyright (C) 2013 celeron55, Perttu Ahola +Copyright (C) 2013-8 celeron55, Perttu Ahola +Copyright (C) 2017-8 rubenwardy This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index 52bb0a784..0bef23541 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "lua_api/l_internal.h" #include "lua_api/l_inventory.h" #include "lua_api/l_item.h" +#include "lua_api/l_playermeta.h" #include "common/c_converter.h" #include "common/c_content.h" #include "log.h" @@ -1218,16 +1219,15 @@ int ObjectRef::l_set_attribute(lua_State *L) { ObjectRef *ref = checkobject(L, 1); PlayerSAO* co = getplayersao(ref); - if (co == NULL) { + if (co == NULL) return 0; - } std::string attr = luaL_checkstring(L, 2); if (lua_isnil(L, 3)) { - co->removeExtendedAttribute(attr); + co->getMeta().removeString(attr); } else { std::string value = luaL_checkstring(L, 3); - co->setExtendedAttribute(attr, value); + co->getMeta().setString(attr, value); } return 1; } @@ -1237,14 +1237,13 @@ int ObjectRef::l_get_attribute(lua_State *L) { ObjectRef *ref = checkobject(L, 1); PlayerSAO* co = getplayersao(ref); - if (co == NULL) { + if (co == NULL) return 0; - } std::string attr = luaL_checkstring(L, 2); std::string value; - if (co->getExtendedAttribute(attr, &value)) { + if (co->getMeta().getStringToRef(attr, value)) { lua_pushstring(L, value.c_str()); return 1; } @@ -1253,6 +1252,19 @@ int ObjectRef::l_get_attribute(lua_State *L) } +// get_meta(self, attribute) +int ObjectRef::l_get_meta(lua_State *L) +{ + ObjectRef *ref = checkobject(L, 1); + PlayerSAO *co = getplayersao(ref); + if (co == NULL) + return 0; + + PlayerMetaRef::create(L, &co->getMeta()); + return 1; +} + + // set_inventory_formspec(self, formspec) int ObjectRef::l_set_inventory_formspec(lua_State *L) { @@ -1884,6 +1896,7 @@ const luaL_Reg ObjectRef::methods[] = { luamethod(ObjectRef, set_breath), luamethod(ObjectRef, get_attribute), luamethod(ObjectRef, set_attribute), + luamethod(ObjectRef, get_meta), luamethod(ObjectRef, set_inventory_formspec), luamethod(ObjectRef, get_inventory_formspec), luamethod(ObjectRef, set_formspec_prepend), diff --git a/src/script/lua_api/l_object.h b/src/script/lua_api/l_object.h index 21c215c3f..76b8bc4a4 100644 --- a/src/script/lua_api/l_object.h +++ b/src/script/lua_api/l_object.h @@ -250,6 +250,9 @@ private: // get_attribute(self, attribute) static int l_get_attribute(lua_State *L); + // get_meta(self) + static int l_get_meta(lua_State *L); + // set_inventory_formspec(self, formspec) static int l_set_inventory_formspec(lua_State *L); diff --git a/src/script/lua_api/l_playermeta.cpp b/src/script/lua_api/l_playermeta.cpp new file mode 100644 index 000000000..b8f6aabb7 --- /dev/null +++ b/src/script/lua_api/l_playermeta.cpp @@ -0,0 +1,121 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola +Copyright (C) 2017-8 rubenwardy + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "lua_api/l_playermeta.h" +#include "lua_api/l_internal.h" +#include "common/c_content.h" + +/* + PlayerMetaRef +*/ +PlayerMetaRef *PlayerMetaRef::checkobject(lua_State *L, int narg) +{ + luaL_checktype(L, narg, LUA_TUSERDATA); + void *ud = luaL_checkudata(L, narg, className); + if (!ud) + luaL_typerror(L, narg, className); + + return *(PlayerMetaRef **)ud; // unbox pointer +} + +Metadata *PlayerMetaRef::getmeta(bool auto_create) +{ + return metadata; +} + +void PlayerMetaRef::clearMeta() +{ + metadata->clear(); +} + +void PlayerMetaRef::reportMetadataChange() +{ + // TODO +} + +// garbage collector +int PlayerMetaRef::gc_object(lua_State *L) +{ + PlayerMetaRef *o = *(PlayerMetaRef **)(lua_touserdata(L, 1)); + delete o; + return 0; +} + +// Creates an PlayerMetaRef and leaves it on top of stack +// Not callable from Lua; all references are created on the C side. +void PlayerMetaRef::create(lua_State *L, Metadata *metadata) +{ + PlayerMetaRef *o = new PlayerMetaRef(metadata); + *(void **)(lua_newuserdata(L, sizeof(void *))) = o; + luaL_getmetatable(L, className); + lua_setmetatable(L, -2); +} + +void PlayerMetaRef::Register(lua_State *L) +{ + lua_newtable(L); + int methodtable = lua_gettop(L); + luaL_newmetatable(L, className); + int metatable = lua_gettop(L); + + lua_pushliteral(L, "__metatable"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); // hide metatable from Lua getmetatable() + + lua_pushliteral(L, "metadata_class"); + lua_pushlstring(L, className, strlen(className)); + lua_settable(L, metatable); + + lua_pushliteral(L, "__index"); + lua_pushvalue(L, methodtable); + lua_settable(L, metatable); + + lua_pushliteral(L, "__gc"); + lua_pushcfunction(L, gc_object); + lua_settable(L, metatable); + + lua_pushliteral(L, "__eq"); + lua_pushcfunction(L, l_equals); + lua_settable(L, metatable); + + lua_pop(L, 1); // drop metatable + + luaL_openlib(L, 0, methods, 0); + lua_pop(L, 1); + + // Cannot be created from Lua + // lua_register(L, className, create_object); +} + +// clang-format off +const char PlayerMetaRef::className[] = "PlayerMetaRef"; +const luaL_Reg PlayerMetaRef::methods[] = { + luamethod(MetaDataRef, get_string), + luamethod(MetaDataRef, set_string), + luamethod(MetaDataRef, get_int), + luamethod(MetaDataRef, set_int), + luamethod(MetaDataRef, get_float), + luamethod(MetaDataRef, set_float), + luamethod(MetaDataRef, to_table), + luamethod(MetaDataRef, from_table), + luamethod(MetaDataRef, equals), + {0,0} +}; +// clang-format on diff --git a/src/script/lua_api/l_playermeta.h b/src/script/lua_api/l_playermeta.h new file mode 100644 index 000000000..697424318 --- /dev/null +++ b/src/script/lua_api/l_playermeta.h @@ -0,0 +1,57 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola +Copyright (C) 2017-8 rubenwardy + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program 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 Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#pragma once + +#include "lua_api/l_base.h" +#include "lua_api/l_metadata.h" +#include "irrlichttypes_bloated.h" +#include "inventory.h" +#include "metadata.h" + +class PlayerMetaRef : public MetaDataRef +{ +private: + Metadata *metadata = nullptr; + + static const char className[]; + static const luaL_Reg methods[]; + + static PlayerMetaRef *checkobject(lua_State *L, int narg); + + virtual Metadata *getmeta(bool auto_create); + + virtual void clearMeta(); + + virtual void reportMetadataChange(); + + // garbage collector + static int gc_object(lua_State *L); + +public: + PlayerMetaRef(Metadata *metadata) : metadata(metadata) {} + ~PlayerMetaRef() = default; + + // Creates an ItemStackMetaRef and leaves it on top of stack + // Not callable from Lua; all references are created on the C side. + static void create(lua_State *L, Metadata *metadata); + + static void Register(lua_State *L); +}; diff --git a/src/script/scripting_server.cpp b/src/script/scripting_server.cpp index 1eee24c61..93b28b61b 100644 --- a/src/script/scripting_server.cpp +++ b/src/script/scripting_server.cpp @@ -35,6 +35,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "lua_api/l_nodetimer.h" #include "lua_api/l_noise.h" #include "lua_api/l_object.h" +#include "lua_api/l_playermeta.h" #include "lua_api/l_particles.h" #include "lua_api/l_rollback.h" #include "lua_api/l_server.h" @@ -99,6 +100,7 @@ void ServerScripting::InitializeModApi(lua_State *L, int top) NodeMetaRef::Register(L); NodeTimerRef::Register(L); ObjectRef::Register(L); + PlayerMetaRef::Register(L); LuaSettings::Register(L); StorageRef::Register(L); ModChannelRef::Register(L); diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp index 25319699a..9835522b1 100644 --- a/src/serverenvironment.cpp +++ b/src/serverenvironment.cpp @@ -535,7 +535,7 @@ void ServerEnvironment::saveLoadedPlayers() for (RemotePlayer *player : m_players) { if (player->checkModified() || (player->getPlayerSAO() && - player->getPlayerSAO()->extendedAttributesModified())) { + player->getPlayerSAO()->getMeta().isModified())) { try { m_player_database->savePlayer(player); } catch (DatabaseException &e) {