]> git.lizzy.rs Git - minetest.git/commitdiff
Implement player attribute backend (#4155)
authorLoïc Blot <nerzhul@users.noreply.github.com>
Fri, 27 Jan 2017 07:59:30 +0000 (08:59 +0100)
committerGitHub <noreply@github.com>
Fri, 27 Jan 2017 07:59:30 +0000 (08:59 +0100)
* This backend permit mods to store extra players attributes to a common interface.
* Add the obj:set_attribute(attr, value) Lua call
* Add the obj:get_attribute(attr) Lua call

Examples:
* player:set_attribute("home:home", "10,25,-78")
* player:get_attribute("default:mana")

Attributes are saved as a json in the player file in extended_attributes
key

They are saved only if a modification on the attributes occurs and loaded
when emergePlayer is called (they are attached to PlayerSAO).

doc/lua_api.txt
src/content_sao.cpp
src/content_sao.h
src/remoteplayer.cpp
src/remoteplayer.h
src/script/lua_api/l_object.cpp
src/script/lua_api/l_object.h
src/server.h
src/serverenvironment.cpp

index 62a7b81f7d08b78b0ccd6be3b6f3d84803031bad..ee7d57c2f936cd7ab22c682f929ff947e2b82bf6 100644 (file)
@@ -2899,6 +2899,8 @@ This is basically a reference to a C++ `ServerActiveObject`
         * `0`: player is drowning,
         * `1`-`10`: remaining number of bubbles
         * `11`: bubbles bar is not shown
+* `set_attribute(attribute, value)`: sets an extra attribute with value on player
+* `get_attribute(attribute)`: returns value for extra attribute. Returns nil if no attribute found.
 * `set_inventory_formspec(formspec)`
     * Redefine player's inventory form
     * Should usually be called in on_joinplayer
index bb62aea7d8622e8c7e48f33861a9173286b0dcda..35133490ed553c905cbab6eb5e00a9d72e88a0e1 100644 (file)
@@ -791,6 +791,7 @@ PlayerSAO::PlayerSAO(ServerEnvironment *env_, u16 peer_id_, bool is_singleplayer
        m_pitch(0),
        m_fov(0),
        m_wanted_range(0),
+       m_extended_attributes_modified(false),
        // public
        m_physics_override_speed(1),
        m_physics_override_jump(1),
index c3674fa2dfa38e17e7309227b2305420837c6f13..bbf244742c5b56bcd7928779bfa37827717be780 100644 (file)
@@ -180,6 +180,7 @@ class LagPool
        }
 };
 
+typedef UNORDERED_MAP<std::string, std::string> PlayerAttributes;
 class RemotePlayer;
 
 class PlayerSAO : public UnitSAO
@@ -249,6 +250,39 @@ class PlayerSAO : public UnitSAO
        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 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
        */
@@ -343,6 +377,9 @@ class PlayerSAO : public UnitSAO
        f32 m_pitch;
        f32 m_fov;
        s16 m_wanted_range;
+
+       PlayerAttributes m_extra_attributes;
+       bool m_extended_attributes_modified;
 public:
        float m_physics_override_speed;
        float m_physics_override_jump;
index 18bfa1030894fd2dd6e98a83b69f1281d6a23e7c..6853ad6d9f5a04e032a51b9812cb555c940fdf84 100644 (file)
@@ -19,13 +19,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 */
 
 #include "remoteplayer.h"
+#include <json/json.h>
 #include "content_sao.h"
 #include "filesys.h"
 #include "gamedef.h"
 #include "porting.h"  // strlcpy
+#include "server.h"
 #include "settings.h"
 
-
 /*
        RemotePlayer
 */
@@ -112,9 +113,23 @@ void RemotePlayer::save(std::string savedir, IGameDef *gamedef)
        }
 
        infostream << "Didn't find free file for player " << m_name << std::endl;
-       return;
 }
 
+void RemotePlayer::serializeExtraAttributes(std::string &output)
+{
+       assert(m_sao);
+       Json::Value json_root;
+       const PlayerAttributes &attrs = m_sao->getExtendedAttributes();
+       for (PlayerAttributes::const_iterator it = attrs.begin(); it != attrs.end(); ++it) {
+               json_root[(*it).first] = (*it).second;
+       }
+
+       Json::FastWriter writer;
+       output = writer.write(json_root);
+       m_sao->setExtendedAttributeModified(false);
+}
+
+
 void RemotePlayer::deSerialize(std::istream &is, const std::string &playername,
                PlayerSAO *sao)
 {
@@ -150,6 +165,20 @@ void RemotePlayer::deSerialize(std::istream &is, const std::string &playername,
                try {
                        sao->setBreath(args.getS32("breath"), false);
                } catch (SettingNotFoundException &e) {}
+
+               try {
+                       std::string extended_attributes = args.get("extended_attributes");
+                       Json::Reader reader;
+                       Json::Value attr_root;
+                       reader.parse(extended_attributes, attr_root);
+
+                       const Json::Value::Members attr_list = attr_root.getMemberNames();
+                       for (Json::Value::Members::const_iterator it = attr_list.begin();
+                                it != attr_list.end(); ++it) {
+                               Json::Value attr_value = attr_root[*it];
+                               sao->setExtendedAttribute(*it, attr_value.asString());
+                       }
+               } catch (SettingNotFoundException &e) {}
        }
 
        inventory.deSerialize(is);
@@ -175,7 +204,6 @@ void RemotePlayer::serialize(std::ostream &os)
        Settings args;
        args.setS32("version", 1);
        args.set("name", m_name);
-       //args.set("password", m_password);
 
        // This should not happen
        assert(m_sao);
@@ -185,6 +213,10 @@ void RemotePlayer::serialize(std::ostream &os)
        args.setFloat("yaw", m_sao->getYaw());
        args.setS32("breath", m_sao->getBreath());
 
+       std::string extended_attrs = "";
+       serializeExtraAttributes(extended_attrs);
+       args.set("extended_attributes", extended_attrs);
+
        args.writeLines(os);
 
        os<<"PlayerArgsEnd\n";
index 61b5a23defc2bdd5d0d5fb72b40ea231b3ba5540..f44fb9332640ba892da3ca1b4d32ed37f6575912 100644 (file)
@@ -25,11 +25,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 
 class PlayerSAO;
 
-enum RemotePlayerChatResult {
+enum RemotePlayerChatResult
+{
        RPLAYER_CHATRESULT_OK,
        RPLAYER_CHATRESULT_FLOODING,
        RPLAYER_CHATRESULT_KICK,
 };
+
 /*
        Player on the server
 */
@@ -135,6 +137,7 @@ class RemotePlayer : public Player
                deSerialize stops reading exactly at the right point.
        */
        void serialize(std::ostream &os);
+       void serializeExtraAttributes(std::string &output);
 
        PlayerSAO *m_sao;
        bool m_dirty;
index be4451704f7c67fe06f03dcec7153cda0a8f81fd..9352812ab19218f7c63c9f4f6b8c22c2e1ba91c2 100644 (file)
@@ -1181,6 +1181,45 @@ int ObjectRef::l_get_breath(lua_State *L)
        return 1;
 }
 
+// set_attribute(self, attribute, value)
+int ObjectRef::l_set_attribute(lua_State *L)
+{
+       ObjectRef *ref = checkobject(L, 1);
+       PlayerSAO* co = getplayersao(ref);
+       if (co == NULL) {
+               return 0;
+       }
+
+       std::string attr = luaL_checkstring(L, 2);
+       std::string value = luaL_checkstring(L, 3);
+
+       if (co->getType() == ACTIVEOBJECT_TYPE_PLAYER) {
+               co->setExtendedAttribute(attr, value);
+       }
+       return 1;
+}
+
+// get_attribute(self, attribute)
+int ObjectRef::l_get_attribute(lua_State *L)
+{
+       ObjectRef *ref = checkobject(L, 1);
+       PlayerSAO* co = getplayersao(ref);
+       if (co == NULL) {
+               return 0;
+       }
+
+       std::string attr = luaL_checkstring(L, 2);
+
+       std::string value = "";
+       if (co->getExtendedAttribute(attr, &value)) {
+               lua_pushstring(L, value.c_str());
+               return 1;
+       }
+
+       return 0;
+}
+
+
 // set_inventory_formspec(self, formspec)
 int ObjectRef::l_set_inventory_formspec(lua_State *L)
 {
@@ -1839,6 +1878,8 @@ const luaL_reg ObjectRef::methods[] = {
        luamethod(ObjectRef, set_look_pitch),
        luamethod(ObjectRef, get_breath),
        luamethod(ObjectRef, set_breath),
+       luamethod(ObjectRef, get_attribute),
+       luamethod(ObjectRef, set_attribute),
        luamethod(ObjectRef, set_inventory_formspec),
        luamethod(ObjectRef, get_inventory_formspec),
        luamethod(ObjectRef, get_player_control),
index 96d0abae80ec9552c927151e9c78f0443b4fa2c2..2c9aa559a87d43602f01a95c2b9128212d022bee 100644 (file)
@@ -226,6 +226,12 @@ class ObjectRef : public ModApiBase {
        // get_breath(self, breath)
        static int l_get_breath(lua_State *L);
 
+       // set_attribute(self, attribute, value)
+       static int l_set_attribute(lua_State *L);
+
+       // get_attribute(self, attribute)
+       static int l_get_attribute(lua_State *L);
+
        // set_inventory_formspec(self, formspec)
        static int l_set_inventory_formspec(lua_State *L);
 
index e5121bdc30c2a0f7dd0f432f45c5daed0e043351..8f553ce385a417033ace0e9e7d0ccffafc815b92 100644 (file)
@@ -576,7 +576,6 @@ class Server : public con::PeerHandler, public MapEventReceiver,
        float m_time_of_day_send_timer;
        // Uptime of server in seconds
        MutexedVariable<double> m_uptime;
-
        /*
         Client interface
         */
index 01dc3ff10c25a087b6e8158ae9308ab6714f3021..7a5cfafd6826c9ab0203a077f54e591e47e15679 100644 (file)
@@ -500,7 +500,8 @@ void ServerEnvironment::saveLoadedPlayers()
        for (std::vector<RemotePlayer *>::iterator it = m_players.begin();
                it != m_players.end();
                ++it) {
-               if ((*it)->checkModified()) {
+               if ((*it)->checkModified() ||
+                       ((*it)->getPlayerSAO() && (*it)->getPlayerSAO()->extendedAttributesModified())) {
                        (*it)->save(players_path, m_server);
                }
        }