Add player:get_meta(), deprecate player attributes (#7202)
authorrubenwardy <rw@rubenwardy.com>
Fri, 6 Apr 2018 08:52:29 +0000 (09:52 +0100)
committerLoïc Blot <nerzhul@users.noreply.github.com>
Fri, 6 Apr 2018 08:52:29 +0000 (10:52 +0200)
* Add player:get_meta(), deprecate player attributes

22 files changed:
build/android/jni/Android.mk
doc/lua_api.txt
games/minimal/mods/test/init.lua
src/content_sao.h
src/database/database-postgresql.cpp
src/database/database-sqlite3.cpp
src/itemstackmetadata.cpp
src/itemstackmetadata.h
src/metadata.cpp
src/metadata.h
src/remoteplayer.cpp
src/script/lua_api/CMakeLists.txt
src/script/lua_api/l_itemstackmeta.cpp
src/script/lua_api/l_itemstackmeta.h
src/script/lua_api/l_metadata.cpp
src/script/lua_api/l_metadata.h
src/script/lua_api/l_object.cpp
src/script/lua_api/l_object.h
src/script/lua_api/l_playermeta.cpp [new file with mode: 0644]
src/script/lua_api/l_playermeta.h [new file with mode: 0644]
src/script/scripting_server.cpp
src/serverenvironment.cpp

index d5e87bc1f740af6db2003fc2da2e9e00a5e95f3d..a4e55eaa947263c4b3f8e6f2300f9044183722c0 100644 (file)
@@ -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     \
index ea8e8b254647a96e3345e6abf80087bcffbe0305..620828824032a75b95b55759868c3c39068a713a 100644 (file)
@@ -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`
index b08ddfa94e00063accded14801cb56ebfd4727b7..9b826bce6986ea9e7e8cf0d3d2a71c1be13b0499 100644 (file)
@@ -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)
index 3f75a789034d5a0a90a3cfba0493a09048dfed1e..6583f31d659c82a3b4ef04ddf465a1bb4818307f 100644 (file)
@@ -197,7 +197,6 @@ public:
        }
 };
 
-typedef std::unordered_map<std::string, std::string> 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;
index 74651135a84b26239f486377ba1ec23a21cb137f..2d9c3b49ef982c61c7b56e0b8d2e960ec1219f98 100644 (file)
@@ -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);
 
index 78c182f8685bb5a6e9141b85eaa35f0c5b7098dd..76935ada4648934672d98ba80f38ac02aab3c502 100644 (file)
@@ -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;
 }
index 53c8bad83e25680f4d0c5da866ca6963203d1fcc..4aa1a09030541e42ceb6800ac242f29799c16661 100644 (file)
@@ -1,3 +1,23 @@
+/*
+Minetest
+Copyright (C) 2017-8 rubenwardy <rw@rubenwardy.com>
+
+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"
index 0e1977c8c850590b1c312f9c1c3212cb85b9224f..a7f1349553ed71ff3b8ec81fdabd811f8d4902dd 100644 (file)
@@ -1,6 +1,6 @@
 /*
 Minetest
-Copyright (C) 2010-2013 rubenwardy <rubenwardy@gmail.com>
+Copyright (C) 2017-8 rubenwardy <rw@rubenwardy.com>
 
 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
index 6ad65e007843a0f9af81f09b19cd5b1cd71e4fee..453ac1c9adc905d7193415ce588f321a0fb80fa5 100644 (file)
@@ -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;
 }
 
index d95a0ed5dc7394caa713fa9347d37e223d27ecd4..5333f8a9d07470265bd57dcd5580b5bb6166ce8d 100644 (file)
@@ -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;
-
 };
index f8f31d928f6f49fb49c8332ee1e2988f9cf7951f..d070eb7e4cb40a01665d7d237219f9865aa0931a 100644 (file)
@@ -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) {}
        }
 
index a55ea66359ad02d471cd131e68d5c79a1d8ff427..97c3786ec84cbc63ffd8784c492de2bb0d52ba83 100644 (file)
@@ -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
index 3cbc3d0f74721559a2f3e636c98528a8d38bc565..0661ec25d5f70a1aac8acfd651ded48cc7ce4a7a 100644 (file)
@@ -1,6 +1,8 @@
 /*
 Minetest
 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+Copyright (C) 2017-8 rubenwardy <rw@rubenwardy.com>
+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
index 46c40ab30b835af2b35f1a8b09f5d20f27162fd0..2f4c37fd905a682dccc3e88328e149a5d6d8cb57 100644 (file)
@@ -1,6 +1,8 @@
 /*
 Minetest
 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+Copyright (C) 2017-8 rubenwardy <rw@rubenwardy.com>
+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
index e84d1d9394944ef24e3d56fe19e7fce9ffa69c9a..69f495c4aa32cd149241415642bd340fd568e80d 100644 (file)
@@ -1,6 +1,7 @@
 /*
 Minetest
 Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+Copyright (C) 2017-8 rubenwardy <rw@rubenwardy.com>
 
 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
index e0e9696cb6fb3eb72d05bd497bfa14e88ec676b3..3aaf62d4a2a4d7b6f7b32df676aef31ea0b1d290 100644 (file)
@@ -1,6 +1,7 @@
 /*
 Minetest
-Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+Copyright (C) 2013-8 celeron55, Perttu Ahola <celeron55@gmail.com>
+Copyright (C) 2017-8 rubenwardy <rw@rubenwardy.com>
 
 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
index 52bb0a784e7e0deade84a5a1eb401bbd9a79d1f5..0bef2354184b84766e34a3610a03f5144f78d80b 100644 (file)
@@ -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),
index 21c215c3f8f5ca8c343f45466f8a87c26b91bce5..76b8bc4a47800d42c0a877a944c89f7291917feb 100644 (file)
@@ -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 (file)
index 0000000..b8f6aab
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+Copyright (C) 2017-8 rubenwardy <rw@rubenwardy.com>
+
+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 (file)
index 0000000..6974243
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+Minetest
+Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
+Copyright (C) 2017-8 rubenwardy <rw@rubenwardy.com>
+
+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);
+};
index 1eee24c61a0f35ecd6076f3f7c6ce9d9b8245aa4..93b28b61b324f3b80d470ac2568399ad04615ceb 100644 (file)
@@ -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);
index 25319699adf40973bc00ae2752155435bc1d8c8b..9835522b1ec4de3033716c31f445f9198b5dc379 100644 (file)
@@ -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) {