Implement player attribute backend (#4155)

* 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).
This commit is contained in:
Loïc Blot 2017-01-27 08:59:30 +01:00 committed by GitHub
parent 2a89531071
commit b7a98e9850
9 changed files with 128 additions and 6 deletions

@ -2899,6 +2899,8 @@ This is basically a reference to a C++ `ServerActiveObject`
* `0`: player is drowning, * `0`: player is drowning,
* `1`-`10`: remaining number of bubbles * `1`-`10`: remaining number of bubbles
* `11`: bubbles bar is not shown * `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)` * `set_inventory_formspec(formspec)`
* Redefine player's inventory form * Redefine player's inventory form
* Should usually be called in on_joinplayer * Should usually be called in on_joinplayer

@ -791,6 +791,7 @@ PlayerSAO::PlayerSAO(ServerEnvironment *env_, u16 peer_id_, bool is_singleplayer
m_pitch(0), m_pitch(0),
m_fov(0), m_fov(0),
m_wanted_range(0), m_wanted_range(0),
m_extended_attributes_modified(false),
// public // public
m_physics_override_speed(1), m_physics_override_speed(1),
m_physics_override_jump(1), m_physics_override_jump(1),

@ -180,6 +180,7 @@ public:
} }
}; };
typedef UNORDERED_MAP<std::string, std::string> PlayerAttributes;
class RemotePlayer; class RemotePlayer;
class PlayerSAO : public UnitSAO class PlayerSAO : public UnitSAO
@ -249,6 +250,39 @@ public:
int getWieldIndex() const; int getWieldIndex() const;
void setWieldIndex(int i); 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 PlayerSAO-specific
*/ */
@ -343,6 +377,9 @@ private:
f32 m_pitch; f32 m_pitch;
f32 m_fov; f32 m_fov;
s16 m_wanted_range; s16 m_wanted_range;
PlayerAttributes m_extra_attributes;
bool m_extended_attributes_modified;
public: public:
float m_physics_override_speed; float m_physics_override_speed;
float m_physics_override_jump; float m_physics_override_jump;

@ -19,13 +19,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
*/ */
#include "remoteplayer.h" #include "remoteplayer.h"
#include <json/json.h>
#include "content_sao.h" #include "content_sao.h"
#include "filesys.h" #include "filesys.h"
#include "gamedef.h" #include "gamedef.h"
#include "porting.h" // strlcpy #include "porting.h" // strlcpy
#include "server.h"
#include "settings.h" #include "settings.h"
/* /*
RemotePlayer 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; 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, void RemotePlayer::deSerialize(std::istream &is, const std::string &playername,
PlayerSAO *sao) PlayerSAO *sao)
{ {
@ -150,6 +165,20 @@ void RemotePlayer::deSerialize(std::istream &is, const std::string &playername,
try { try {
sao->setBreath(args.getS32("breath"), false); sao->setBreath(args.getS32("breath"), false);
} catch (SettingNotFoundException &e) {} } 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); inventory.deSerialize(is);
@ -175,7 +204,6 @@ void RemotePlayer::serialize(std::ostream &os)
Settings args; Settings args;
args.setS32("version", 1); args.setS32("version", 1);
args.set("name", m_name); args.set("name", m_name);
//args.set("password", m_password);
// This should not happen // This should not happen
assert(m_sao); assert(m_sao);
@ -185,6 +213,10 @@ void RemotePlayer::serialize(std::ostream &os)
args.setFloat("yaw", m_sao->getYaw()); args.setFloat("yaw", m_sao->getYaw());
args.setS32("breath", m_sao->getBreath()); args.setS32("breath", m_sao->getBreath());
std::string extended_attrs = "";
serializeExtraAttributes(extended_attrs);
args.set("extended_attributes", extended_attrs);
args.writeLines(os); args.writeLines(os);
os<<"PlayerArgsEnd\n"; os<<"PlayerArgsEnd\n";

@ -25,11 +25,13 @@ with this program; if not, write to the Free Software Foundation, Inc.,
class PlayerSAO; class PlayerSAO;
enum RemotePlayerChatResult { enum RemotePlayerChatResult
{
RPLAYER_CHATRESULT_OK, RPLAYER_CHATRESULT_OK,
RPLAYER_CHATRESULT_FLOODING, RPLAYER_CHATRESULT_FLOODING,
RPLAYER_CHATRESULT_KICK, RPLAYER_CHATRESULT_KICK,
}; };
/* /*
Player on the server Player on the server
*/ */
@ -135,6 +137,7 @@ private:
deSerialize stops reading exactly at the right point. deSerialize stops reading exactly at the right point.
*/ */
void serialize(std::ostream &os); void serialize(std::ostream &os);
void serializeExtraAttributes(std::string &output);
PlayerSAO *m_sao; PlayerSAO *m_sao;
bool m_dirty; bool m_dirty;

@ -1181,6 +1181,45 @@ int ObjectRef::l_get_breath(lua_State *L)
return 1; 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) // set_inventory_formspec(self, formspec)
int ObjectRef::l_set_inventory_formspec(lua_State *L) 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, set_look_pitch),
luamethod(ObjectRef, get_breath), luamethod(ObjectRef, get_breath),
luamethod(ObjectRef, set_breath), luamethod(ObjectRef, set_breath),
luamethod(ObjectRef, get_attribute),
luamethod(ObjectRef, set_attribute),
luamethod(ObjectRef, set_inventory_formspec), luamethod(ObjectRef, set_inventory_formspec),
luamethod(ObjectRef, get_inventory_formspec), luamethod(ObjectRef, get_inventory_formspec),
luamethod(ObjectRef, get_player_control), luamethod(ObjectRef, get_player_control),

@ -226,6 +226,12 @@ private:
// get_breath(self, breath) // get_breath(self, breath)
static int l_get_breath(lua_State *L); 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) // set_inventory_formspec(self, formspec)
static int l_set_inventory_formspec(lua_State *L); static int l_set_inventory_formspec(lua_State *L);

@ -576,7 +576,6 @@ private:
float m_time_of_day_send_timer; float m_time_of_day_send_timer;
// Uptime of server in seconds // Uptime of server in seconds
MutexedVariable<double> m_uptime; MutexedVariable<double> m_uptime;
/* /*
Client interface Client interface
*/ */

@ -500,7 +500,8 @@ void ServerEnvironment::saveLoadedPlayers()
for (std::vector<RemotePlayer *>::iterator it = m_players.begin(); for (std::vector<RemotePlayer *>::iterator it = m_players.begin();
it != m_players.end(); it != m_players.end();
++it) { ++it) {
if ((*it)->checkModified()) { if ((*it)->checkModified() ||
((*it)->getPlayerSAO() && (*it)->getPlayerSAO()->extendedAttributesModified())) {
(*it)->save(players_path, m_server); (*it)->save(players_path, m_server);
} }
} }