aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build/android/jni/Android.mk1
-rw-r--r--doc/lua_api.txt16
-rw-r--r--games/minimal/mods/test/init.lua34
-rw-r--r--src/content_sao.h49
-rw-r--r--src/database/database-postgresql.cpp6
-rw-r--r--src/database/database-sqlite3.cpp6
-rw-r--r--src/itemstackmetadata.cpp20
-rw-r--r--src/itemstackmetadata.h2
-rw-r--r--src/metadata.cpp14
-rw-r--r--src/metadata.h7
-rw-r--r--src/remoteplayer.cpp8
-rw-r--r--src/script/lua_api/CMakeLists.txt1
-rw-r--r--src/script/lua_api/l_itemstackmeta.cpp2
-rw-r--r--src/script/lua_api/l_itemstackmeta.h2
-rw-r--r--src/script/lua_api/l_metadata.cpp1
-rw-r--r--src/script/lua_api/l_metadata.h3
-rw-r--r--src/script/lua_api/l_object.cpp27
-rw-r--r--src/script/lua_api/l_object.h3
-rw-r--r--src/script/lua_api/l_playermeta.cpp121
-rw-r--r--src/script/lua_api/l_playermeta.h57
-rw-r--r--src/script/scripting_server.cpp2
-rw-r--r--src/serverenvironment.cpp2
22 files changed, 314 insertions, 70 deletions
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<std::string, std::string> PlayerAttributes;
class RemotePlayer;
class PlayerSAO : public UnitSAO
@@ -270,49 +269,6 @@ public:
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 <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"
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 <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
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 <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
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 <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
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 <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
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 <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
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 <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
index 000000000..697424318
--- /dev/null
+++ b/src/script/lua_api/l_playermeta.h
@@ -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);
+};
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) {