From 7bbd716426bf989bf071e2322a9b797cc5f78acb Mon Sep 17 00:00:00 2001
From: Loic Blot <loic.blot@unix-experience.fr>
Date: Sat, 8 Oct 2016 17:56:38 +0200
Subject: [PATCH] RemotePlayer/LocalPlayer Player base class proper separation
 (code cleanup) (patch 3 of X)

* remove IGameDef from Player class, only LocalPlayer has it now
* move many attributes/functions only used by LocalPlayer from Player to LocalPlayer
* move many attributes/functions only used by RemotePlayer from Player to RemotePlayer
* make some functions const
* hudGetHotbarSelectedImage now returns const ref
* RemotePlayer getHotbarSelectedImage now returns const ref
* various code style fixes
---
 src/environment.cpp             |  22 +--
 src/environment.h               |   2 +-
 src/localplayer.cpp             |  24 ++-
 src/localplayer.h               |  24 ++-
 src/player.cpp                  | 176 ++++++++++-----------
 src/player.h                    | 270 +++++++++++++-------------------
 src/script/lua_api/l_object.cpp |   2 +-
 src/server.cpp                  |  22 +--
 src/server.h                    |   5 +-
 9 files changed, 255 insertions(+), 292 deletions(-)

diff --git a/src/environment.cpp b/src/environment.cpp
index bc246f66c..514aa918a 100644
--- a/src/environment.cpp
+++ b/src/environment.cpp
@@ -603,8 +603,9 @@ void ServerEnvironment::kickAllPlayers(AccessDeniedCode reason,
 {
 	for (std::vector<Player*>::iterator it = m_players.begin();
 			it != m_players.end(); ++it) {
-		((Server*)m_gamedef)->DenyAccessVerCompliant((*it)->peer_id,
-			(*it)->protocol_version, reason, str_reason, reconnect);
+		RemotePlayer *player = dynamic_cast<RemotePlayer *>(*it);
+		((Server*)m_gamedef)->DenyAccessVerCompliant(player->peer_id,
+				player->protocol_version, reason, str_reason, reconnect);
 	}
 }
 
@@ -618,7 +619,7 @@ void ServerEnvironment::saveLoadedPlayers()
 			++it) {
 		RemotePlayer *player = static_cast<RemotePlayer*>(*it);
 		if (player->checkModified()) {
-			player->save(players_path);
+			player->save(players_path, m_gamedef);
 		}
 	}
 }
@@ -628,7 +629,7 @@ void ServerEnvironment::savePlayer(RemotePlayer *player)
 	std::string players_path = m_path_world + DIR_DELIM "players";
 	fs::CreateDir(players_path);
 
-	player->save(players_path);
+	player->save(players_path, m_gamedef);
 }
 
 RemotePlayer *ServerEnvironment::loadPlayer(const std::string &playername)
@@ -640,7 +641,7 @@ RemotePlayer *ServerEnvironment::loadPlayer(const std::string &playername)
 
 	RemotePlayer *player = getPlayer(playername.c_str());
 	if (!player) {
-		player = new RemotePlayer(m_gamedef, "");
+		player = new RemotePlayer("", m_gamedef->idef());
 		newplayer = true;
 	}
 
@@ -2300,15 +2301,14 @@ LocalPlayer *ClientEnvironment::getPlayer(const char* name)
 	return dynamic_cast<LocalPlayer *>(Environment::getPlayer(name));
 }
 
-void ClientEnvironment::addPlayer(Player *player)
+void ClientEnvironment::addPlayer(LocalPlayer *player)
 {
 	DSTACK(FUNCTION_NAME);
 	/*
-		It is a failure if player is local and there already is a local
-		player
+		It is a failure if already is a local player
 	*/
-	FATAL_ERROR_IF(player->isLocal() && getLocalPlayer() != NULL,
-				   "Player is local but there is already a local player");
+	FATAL_ERROR_IF(getLocalPlayer() != NULL,
+			"Player is local but there is already a local player");
 
 	Environment::addPlayer(player);
 }
@@ -2563,7 +2563,7 @@ void ClientEnvironment::step(float dtime)
 	*/
 	for (std::vector<Player*>::iterator i = m_players.begin();
 			i != m_players.end(); ++i) {
-		LocalPlayer *player = dynamic_cast<LocalPlayer *>(*i);
+		Player *player = *i;
 		assert(player);
 
 		/*
diff --git a/src/environment.h b/src/environment.h
index 99066c367..7ed38ad5d 100644
--- a/src/environment.h
+++ b/src/environment.h
@@ -579,7 +579,7 @@ class ClientEnvironment : public Environment
 
 	void step(f32 dtime);
 
-	virtual void addPlayer(Player *player);
+	virtual void addPlayer(LocalPlayer *player);
 	LocalPlayer * getLocalPlayer();
 
 	/*
diff --git a/src/localplayer.cpp b/src/localplayer.cpp
index 732ca8acf..bc242a59d 100644
--- a/src/localplayer.cpp
+++ b/src/localplayer.cpp
@@ -26,16 +26,29 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "settings.h"
 #include "environment.h"
 #include "map.h"
-#include "util/numeric.h"
+#include "client.h"
 
 /*
 	LocalPlayer
 */
 
-LocalPlayer::LocalPlayer(IGameDef *gamedef, const char *name):
-	Player(gamedef, name),
+LocalPlayer::LocalPlayer(Client *gamedef, const char *name):
+	Player(name, gamedef->idef()),
 	parent(0),
+	got_teleported(false),
 	isAttached(false),
+	touching_ground(false),
+	in_liquid(false),
+	in_liquid_stable(false),
+	liquid_viscosity(0),
+	is_climbing(false),
+	swimming_vertical(false),
+	// Movement overrides are multipliers and must be 1 by default
+	physics_override_speed(1.0f),
+	physics_override_jump(1.0f),
+	physics_override_gravity(1.0f),
+	physics_override_sneak(true),
+	physics_override_sneak_glitch(true),
 	overridePosition(v3f(0,0,0)),
 	last_position(v3f(0,0,0)),
 	last_speed(v3f(0,0,0)),
@@ -47,6 +60,8 @@ LocalPlayer::LocalPlayer(IGameDef *gamedef, const char *name):
 	hotbar_image(""),
 	hotbar_selected_image(""),
 	light_color(255,255,255,255),
+	hurt_tilt_timer(0.0f),
+	hurt_tilt_strength(0.0f),
 	m_sneak_node(32767,32767,32767),
 	m_sneak_node_exists(false),
 	m_need_to_get_new_sneak_node(true),
@@ -54,7 +69,8 @@ LocalPlayer::LocalPlayer(IGameDef *gamedef, const char *name):
 	m_old_node_below(32767,32767,32767),
 	m_old_node_below_type("air"),
 	m_can_jump(false),
-	m_cao(NULL)
+	m_cao(NULL),
+	m_gamedef(gamedef)
 {
 	// Initialize hp to 0, so that no hearts will be shown if server
 	// doesn't support health points
diff --git a/src/localplayer.h b/src/localplayer.h
index 8897adc5e..182b51d4d 100644
--- a/src/localplayer.h
+++ b/src/localplayer.h
@@ -21,8 +21,10 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #define LOCALPLAYER_HEADER
 
 #include "player.h"
+#include "environment.h"
 #include <list>
 
+class Client;
 class Environment;
 class GenericCAO;
 class ClientActiveObject;
@@ -32,7 +34,7 @@ enum LocalPlayerAnimations {NO_ANIM, WALK_ANIM, DIG_ANIM, WD_ANIM};  // no local
 class LocalPlayer : public Player
 {
 public:
-	LocalPlayer(IGameDef *gamedef, const char *name);
+	LocalPlayer(Client *gamedef, const char *name);
 	virtual ~LocalPlayer();
 
 	bool isLocal() const
@@ -42,7 +44,23 @@ class LocalPlayer : public Player
 
 	ClientActiveObject *parent;
 
+	bool got_teleported;
 	bool isAttached;
+	bool touching_ground;
+	// This oscillates so that the player jumps a bit above the surface
+	bool in_liquid;
+	// This is more stable and defines the maximum speed of the player
+	bool in_liquid_stable;
+	// Gets the viscosity of water to calculate friction
+	u8 liquid_viscosity;
+	bool is_climbing;
+	bool swimming_vertical;
+
+	float physics_override_speed;
+	float physics_override_jump;
+	float physics_override_gravity;
+	bool physics_override_sneak;
+	bool physics_override_sneak_glitch;
 
 	v3f overridePosition;
 
@@ -71,6 +89,9 @@ class LocalPlayer : public Player
 
 	video::SColor light_color;
 
+	float hurt_tilt_timer;
+	float hurt_tilt_strength;
+
 	GenericCAO* getCAO() const {
 		return m_cao;
 	}
@@ -102,6 +123,7 @@ class LocalPlayer : public Player
 	bool m_can_jump;
 
 	GenericCAO* m_cao;
+	Client *m_gamedef;
 };
 
 #endif
diff --git a/src/player.cpp b/src/player.cpp
index fd72d63b6..c0d367134 100644
--- a/src/player.cpp
+++ b/src/player.cpp
@@ -32,31 +32,19 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "porting.h"  // strlcpy
 
 
-Player::Player(IGameDef *gamedef, const char *name):
-	got_teleported(false),
-	touching_ground(false),
-	in_liquid(false),
-	in_liquid_stable(false),
-	liquid_viscosity(0),
-	is_climbing(false),
-	swimming_vertical(false),
+Player::Player(const char *name, IItemDefManager *idef):
 	camera_barely_in_ceiling(false),
-	inventory(gamedef->idef()),
+	inventory(idef),
 	hp(PLAYER_MAX_HP),
-	hurt_tilt_timer(0),
-	hurt_tilt_strength(0),
-	protocol_version(0),
 	peer_id(PEER_ID_INEXISTENT),
 	keyPressed(0),
 // protected
-	m_gamedef(gamedef),
 	m_breath(PLAYER_MAX_BREATH),
 	m_pitch(0),
 	m_yaw(0),
 	m_speed(0,0,0),
 	m_position(0,0,0),
-	m_collisionbox(-BS*0.30,0.0,-BS*0.30,BS*0.30,BS*1.75,BS*0.30),
-	m_dirty(false)
+	m_collisionbox(-BS*0.30,0.0,-BS*0.30,BS*0.30,BS*1.75,BS*0.30)
 {
 	strlcpy(m_name, name, PLAYERNAME_SIZE);
 
@@ -92,13 +80,6 @@ Player::Player(IGameDef *gamedef, const char *name):
 	movement_gravity                = 9.81 * BS;
 	local_animation_speed           = 0.0;
 
-	// Movement overrides are multipliers and must be 1 by default
-	physics_override_speed        = 1;
-	physics_override_jump         = 1;
-	physics_override_gravity      = 1;
-	physics_override_sneak        = true;
-	physics_override_sneak_glitch = true;
-
 	hud_flags =
 		HUD_FLAG_HOTBAR_VISIBLE    | HUD_FLAG_HEALTHBAR_VISIBLE |
 		HUD_FLAG_CROSSHAIR_VISIBLE | HUD_FLAG_WIELDITEM_VISIBLE |
@@ -117,70 +98,6 @@ v3s16 Player::getLightPosition() const
 	return floatToInt(m_position + v3f(0,BS+BS/2,0), BS);
 }
 
-void Player::serialize(std::ostream &os)
-{
-	// Utilize a Settings object for storing values
-	Settings args;
-	args.setS32("version", 1);
-	args.set("name", m_name);
-	//args.set("password", m_password);
-	args.setFloat("pitch", m_pitch);
-	args.setFloat("yaw", m_yaw);
-	args.setV3F("position", m_position);
-	args.setS32("hp", hp);
-	args.setS32("breath", m_breath);
-
-	args.writeLines(os);
-
-	os<<"PlayerArgsEnd\n";
-
-	inventory.serialize(os);
-}
-
-void Player::deSerialize(std::istream &is, std::string playername)
-{
-	Settings args;
-
-	if (!args.parseConfigLines(is, "PlayerArgsEnd")) {
-		throw SerializationError("PlayerArgsEnd of player " +
-				playername + " not found!");
-	}
-
-	m_dirty = true;
-	//args.getS32("version"); // Version field value not used
-	std::string name = args.get("name");
-	strlcpy(m_name, name.c_str(), PLAYERNAME_SIZE);
-	setPitch(args.getFloat("pitch"));
-	setYaw(args.getFloat("yaw"));
-	setPosition(args.getV3F("position"));
-	try{
-		hp = args.getS32("hp");
-	}catch(SettingNotFoundException &e) {
-		hp = PLAYER_MAX_HP;
-	}
-	try{
-		m_breath = args.getS32("breath");
-	}catch(SettingNotFoundException &e) {
-		m_breath = PLAYER_MAX_BREATH;
-	}
-
-	inventory.deSerialize(is);
-
-	if(inventory.getList("craftpreview") == NULL) {
-		// Convert players without craftpreview
-		inventory.addList("craftpreview", 1);
-
-		bool craftresult_is_preview = true;
-		if(args.exists("craftresult_is_preview"))
-			craftresult_is_preview = args.getBool("craftresult_is_preview");
-		if(craftresult_is_preview)
-		{
-			// Clear craftresult
-			inventory.getList("craftresult")->changeItem(0, ItemStack());
-		}
-	}
-}
-
 u32 Player::addHud(HudElement *toadd)
 {
 	MutexAutoLock lock(m_mutex);
@@ -227,17 +144,24 @@ void Player::clearHud()
 	}
 }
 
+/*
+	RemotePlayer
+*/
 // static config cache for remoteplayer
 bool RemotePlayer::m_setting_cache_loaded = false;
 float RemotePlayer::m_setting_chat_message_limit_per_10sec = 0.0f;
 u16 RemotePlayer::m_setting_chat_message_limit_trigger_kick = 0;
 
-RemotePlayer::RemotePlayer(IGameDef *gamedef, const char *name):
-	Player(gamedef, name),
+RemotePlayer::RemotePlayer(const char *name, IItemDefManager *idef):
+	Player(name, idef),
+	protocol_version(0),
 	m_sao(NULL),
+	m_dirty(false),
 	m_last_chat_message_sent(time(NULL)),
 	m_chat_message_allowance(5.0f),
-	m_message_rate_overhead(0)
+	m_message_rate_overhead(0),
+	hud_hotbar_image(""),
+	hud_hotbar_selected_image("")
 {
 	if (!RemotePlayer::m_setting_cache_loaded) {
 		RemotePlayer::m_setting_chat_message_limit_per_10sec =
@@ -260,7 +184,7 @@ RemotePlayer::RemotePlayer(IGameDef *gamedef, const char *name):
 	movement_gravity                = g_settings->getFloat("movement_gravity")                * BS;
 }
 
-void RemotePlayer::save(std::string savedir)
+void RemotePlayer::save(std::string savedir, IGameDef *gamedef)
 {
 	/*
 	 * We have to open all possible player files in the players directory
@@ -269,7 +193,7 @@ void RemotePlayer::save(std::string savedir)
 	 */
 
 	// A player to deserialize files into to check their names
-	RemotePlayer testplayer(m_gamedef, "");
+	RemotePlayer testplayer("", gamedef->idef());
 
 	savedir += DIR_DELIM;
 	std::string path = savedir + m_name;
@@ -309,11 +233,75 @@ void RemotePlayer::save(std::string savedir)
 	return;
 }
 
-/*
-	RemotePlayer
-*/
+void RemotePlayer::deSerialize(std::istream &is, const std::string &playername)
+{
+	Settings args;
+
+	if (!args.parseConfigLines(is, "PlayerArgsEnd")) {
+		throw SerializationError("PlayerArgsEnd of player " +
+								 playername + " not found!");
+	}
+
+	m_dirty = true;
+	//args.getS32("version"); // Version field value not used
+	std::string name = args.get("name");
+	strlcpy(m_name, name.c_str(), PLAYERNAME_SIZE);
+	setPitch(args.getFloat("pitch"));
+	setYaw(args.getFloat("yaw"));
+	setPosition(args.getV3F("position"));
+	try{
+		hp = args.getS32("hp");
+	}catch(SettingNotFoundException &e) {
+		hp = PLAYER_MAX_HP;
+	}
+	try{
+		m_breath = args.getS32("breath");
+	}catch(SettingNotFoundException &e) {
+		m_breath = PLAYER_MAX_BREATH;
+	}
+
+	inventory.deSerialize(is);
+
+	if(inventory.getList("craftpreview") == NULL) {
+		// Convert players without craftpreview
+		inventory.addList("craftpreview", 1);
+
+		bool craftresult_is_preview = true;
+		if(args.exists("craftresult_is_preview"))
+			craftresult_is_preview = args.getBool("craftresult_is_preview");
+		if(craftresult_is_preview)
+		{
+			// Clear craftresult
+			inventory.getList("craftresult")->changeItem(0, ItemStack());
+		}
+	}
+}
+
+void RemotePlayer::serialize(std::ostream &os)
+{
+	// Utilize a Settings object for storing values
+	Settings args;
+	args.setS32("version", 1);
+	args.set("name", m_name);
+	//args.set("password", m_password);
+	args.setFloat("pitch", m_pitch);
+	args.setFloat("yaw", m_yaw);
+	args.setV3F("position", m_position);
+	args.setS32("hp", hp);
+	args.setS32("breath", m_breath);
+
+	args.writeLines(os);
+
+	os<<"PlayerArgsEnd\n";
+
+	inventory.serialize(os);
+}
+
 void RemotePlayer::setPosition(const v3f &position)
 {
+	if (position != m_position)
+		m_dirty = true;
+
 	Player::setPosition(position);
 	if(m_sao)
 		m_sao->setBasePosition(position);
diff --git a/src/player.h b/src/player.h
index fbd88fc71..1980a86a3 100644
--- a/src/player.h
+++ b/src/player.h
@@ -112,7 +112,7 @@ class Player
 {
 public:
 
-	Player(IGameDef *gamedef, const char *name);
+	Player(const char *name, IItemDefManager *idef);
 	virtual ~Player() = 0;
 
 	virtual void move(f32 dtime, Environment *env, f32 pos_max_d)
@@ -151,80 +151,32 @@ class Player
 
 	virtual void setPosition(const v3f &position)
 	{
-		if (position != m_position)
-			m_dirty = true;
 		m_position = position;
 	}
 
-	void setPitch(f32 pitch)
+	virtual void setPitch(f32 pitch)
 	{
-		if (pitch != m_pitch)
-			m_dirty = true;
 		m_pitch = pitch;
 	}
 
 	virtual void setYaw(f32 yaw)
 	{
-		if (yaw != m_yaw)
-			m_dirty = true;
 		m_yaw = yaw;
 	}
 
-	f32 getPitch()
-	{
-		return m_pitch;
-	}
-
-	f32 getYaw()
-	{
-		return m_yaw;
-	}
-
-	u16 getBreath()
-	{
-		return m_breath;
-	}
-
-	virtual void setBreath(u16 breath)
-	{
-		if (breath != m_breath)
-			m_dirty = true;
-		m_breath = breath;
-	}
-
-	// Deprecated
-	f32 getRadPitchDep()
-	{
-		return -1.0 * m_pitch * core::DEGTORAD;
-	}
-
-	// Deprecated
-	f32 getRadYawDep()
-	{
-		return (m_yaw + 90.) * core::DEGTORAD;
-	}
-
-	f32 getRadPitch()
-	{
-		return m_pitch * core::DEGTORAD;
-	}
+	f32 getPitch() const { return m_pitch; }
+	f32 getYaw() const { return m_yaw; }
+	u16 getBreath() const { return m_breath; }
 
-	f32 getRadYaw()
-	{
-		return m_yaw * core::DEGTORAD;
-	}
+	virtual void setBreath(u16 breath) { m_breath = breath; }
 
-	const char *getName() const
-	{
-		return m_name;
-	}
+	f32 getRadPitch() const { return m_pitch * core::DEGTORAD; }
+	f32 getRadYaw() const { return m_yaw * core::DEGTORAD; }
+	const char *getName() const { return m_name; }
+	aabb3f getCollisionbox() const { return m_collisionbox; }
 
-	aabb3f getCollisionbox()
+	u32 getFreeHudID()
 	{
-		return m_collisionbox;
-	}
-
-	u32 getFreeHudID() {
 		size_t size = hud.size();
 		for (size_t i = 0; i != size; i++) {
 			if (!hud[i])
@@ -233,41 +185,6 @@ class Player
 		return size;
 	}
 
-	void setHotbarImage(const std::string &name)
-	{
-		hud_hotbar_image = name;
-	}
-
-	std::string getHotbarImage()
-	{
-		return hud_hotbar_image;
-	}
-
-	void setHotbarSelectedImage(const std::string &name)
-	{
-		hud_hotbar_selected_image = name;
-	}
-
-	std::string getHotbarSelectedImage() {
-		return hud_hotbar_selected_image;
-	}
-
-	void setSky(const video::SColor &bgcolor, const std::string &type,
-		const std::vector<std::string> &params)
-	{
-		m_sky_bgcolor = bgcolor;
-		m_sky_type = type;
-		m_sky_params = params;
-	}
-
-	void getSky(video::SColor *bgcolor, std::string *type,
-		std::vector<std::string> *params)
-	{
-		*bgcolor = m_sky_bgcolor;
-		*type = m_sky_type;
-		*params = m_sky_params;
-	}
-
 	void setLocalAnimations(v2s32 frames[4], float frame_speed)
 	{
 		for (int i = 0; i < 4; i++)
@@ -282,49 +199,8 @@ class Player
 		*frame_speed = local_animation_speed;
 	}
 
-	virtual bool isLocal() const
-	{
-		return false;
-	}
+	virtual bool isLocal() const { return false; }
 
-	virtual void setPlayerSAO(PlayerSAO *sao)
-	{
-		FATAL_ERROR("FIXME");
-	}
-
-	/*
-		serialize() writes a bunch of text that can contain
-		any characters except a '\0', and such an ending that
-		deSerialize stops reading exactly at the right point.
-	*/
-	void serialize(std::ostream &os);
-	void deSerialize(std::istream &is, std::string playername);
-
-	bool checkModified() const
-	{
-		return m_dirty || inventory.checkModified();
-	}
-
-	void setModified(const bool x)
-	{
-		m_dirty = x;
-		if (!x)
-			inventory.setModified(x);
-	}
-
-	// Use a function, if isDead can be defined by other conditions
-	bool isDead() { return hp == 0; }
-
-	bool got_teleported;
-	bool touching_ground;
-	// This oscillates so that the player jumps a bit above the surface
-	bool in_liquid;
-	// This is more stable and defines the maximum speed of the player
-	bool in_liquid_stable;
-	// Gets the viscosity of water to calculate friction
-	u8 liquid_viscosity;
-	bool is_climbing;
-	bool swimming_vertical;
 	bool camera_barely_in_ceiling;
 	v3f eye_offset_first;
 	v3f eye_offset_third;
@@ -344,21 +220,11 @@ class Player
 	f32 movement_liquid_sink;
 	f32 movement_gravity;
 
-	float physics_override_speed;
-	float physics_override_jump;
-	float physics_override_gravity;
-	bool physics_override_sneak;
-	bool physics_override_sneak_glitch;
-
 	v2s32 local_animations[4];
 	float local_animation_speed;
 
 	u16 hp;
 
-	float hurt_tilt_timer;
-	float hurt_tilt_strength;
-
-	u16 protocol_version;
 	u16 peer_id;
 
 	std::string inventory_formspec;
@@ -368,7 +234,6 @@ class Player
 
 	u32 keyPressed;
 
-
 	HudElement* getHud(u32 id);
 	u32         addHud(HudElement* hud);
 	HudElement* removeHud(u32 id);
@@ -376,11 +241,7 @@ class Player
 
 	u32 hud_flags;
 	s32 hud_hotbar_itemcount;
-	std::string hud_hotbar_image;
-	std::string hud_hotbar_selected_image;
 protected:
-	IGameDef *m_gamedef;
-
 	char m_name[PLAYERNAME_SIZE];
 	u16 m_breath;
 	f32 m_pitch;
@@ -389,13 +250,7 @@ class Player
 	v3f m_position;
 	aabb3f m_collisionbox;
 
-	bool m_dirty;
-
 	std::vector<HudElement *> hud;
-
-	std::string m_sky_type;
-	video::SColor m_sky_bgcolor;
-	std::vector<std::string> m_sky_params;
 private:
 	// Protect some critical areas
 	// hud for example can be modified by EmergeThread
@@ -414,15 +269,14 @@ enum RemotePlayerChatResult {
 class RemotePlayer : public Player
 {
 public:
-	RemotePlayer(IGameDef *gamedef, const char *name);
+	RemotePlayer(const char *name, IItemDefManager *idef);
 	virtual ~RemotePlayer() {}
 
-	void save(std::string savedir);
+	void save(std::string savedir, IGameDef *gamedef);
+	void deSerialize(std::istream &is, const std::string &playername);
 
-	PlayerSAO *getPlayerSAO()
-	{ return m_sao; }
-	void setPlayerSAO(PlayerSAO *sao)
-	{ m_sao = sao; }
+	PlayerSAO *getPlayerSAO() { return m_sao; }
+	void setPlayerSAO(PlayerSAO *sao) { m_sao = sao; }
 	void setPosition(const v3f &position);
 
 	const RemotePlayerChatResult canSendChatMessage();
@@ -446,8 +300,92 @@ class RemotePlayer : public Player
 		*ratio = m_day_night_ratio;
 	}
 
+	// Use a function, if isDead can be defined by other conditions
+	bool isDead() const { return hp == 0; }
+
+	void setHotbarImage(const std::string &name)
+	{
+		hud_hotbar_image = name;
+	}
+
+	std::string getHotbarImage() const
+	{
+		return hud_hotbar_image;
+	}
+
+	void setHotbarSelectedImage(const std::string &name)
+	{
+		hud_hotbar_selected_image = name;
+	}
+
+	const std::string &getHotbarSelectedImage() const
+	{
+		return hud_hotbar_selected_image;
+	}
+
+	// Deprecated
+	f32 getRadPitchDep() const { return -1.0 * m_pitch * core::DEGTORAD; }
+
+	// Deprecated
+	f32 getRadYawDep() const { return (m_yaw + 90.) * core::DEGTORAD; }
+
+	void setSky(const video::SColor &bgcolor, const std::string &type,
+				const std::vector<std::string> &params)
+	{
+		m_sky_bgcolor = bgcolor;
+		m_sky_type = type;
+		m_sky_params = params;
+	}
+
+	void getSky(video::SColor *bgcolor, std::string *type,
+				std::vector<std::string> *params)
+	{
+		*bgcolor = m_sky_bgcolor;
+		*type = m_sky_type;
+		*params = m_sky_params;
+	}
+
+	bool checkModified() const { return m_dirty || inventory.checkModified(); }
+
+	void setModified(const bool x)
+	{
+		m_dirty = x;
+		if (!x)
+			inventory.setModified(x);
+	}
+
+	virtual void setBreath(u16 breath)
+	{
+		if (breath != m_breath)
+			m_dirty = true;
+		Player::setBreath(breath);
+	}
+
+	virtual void setPitch(f32 pitch)
+	{
+		if (pitch != m_pitch)
+			m_dirty = true;
+		Player::setPitch(pitch);
+	}
+
+	virtual void setYaw(f32 yaw)
+	{
+		if (yaw != m_yaw)
+			m_dirty = true;
+		Player::setYaw(yaw);
+	}
+
+	u16 protocol_version;
 private:
+	/*
+		serialize() writes a bunch of text that can contain
+		any characters except a '\0', and such an ending that
+		deSerialize stops reading exactly at the right point.
+	*/
+	void serialize(std::ostream &os);
+
 	PlayerSAO *m_sao;
+	bool m_dirty;
 
 	static bool m_setting_cache_loaded;
 	static float m_setting_chat_message_limit_per_10sec;
@@ -459,6 +397,12 @@ class RemotePlayer : public Player
 
 	bool m_day_night_ratio_do_override;
 	float m_day_night_ratio;
+	std::string hud_hotbar_image;
+	std::string hud_hotbar_selected_image;
+
+	std::string m_sky_type;
+	video::SColor m_sky_bgcolor;
+	std::vector<std::string> m_sky_params;
 };
 
 #endif
diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp
index 74b33da37..a1f83919c 100644
--- a/src/script/lua_api/l_object.cpp
+++ b/src/script/lua_api/l_object.cpp
@@ -1600,7 +1600,7 @@ int ObjectRef::l_hud_get_hotbar_selected_image(lua_State *L)
 	if (player == NULL)
 		return 0;
 
-	std::string name = getServer(L)->hudGetHotbarSelectedImage(player);
+	const std::string &name = getServer(L)->hudGetHotbarSelectedImage(player);
 	lua_pushlstring(L, name.c_str(), name.size());
 	return 1;
 }
diff --git a/src/server.cpp b/src/server.cpp
index edd97e225..71e71f43e 100644
--- a/src/server.cpp
+++ b/src/server.cpp
@@ -1108,7 +1108,7 @@ PlayerSAO* Server::StageTwoClientInit(u16 peer_id)
 	SendPlayerBreath(peer_id);
 
 	// Show death screen if necessary
-	if(player->isDead())
+	if (player->isDead())
 		SendDeathscreen(peer_id, false, v3f(0,0,0));
 
 	// Note things in chat if not in simple singleplayer mode
@@ -3080,14 +3080,6 @@ void Server::hudSetHotbarSelectedImage(RemotePlayer *player, std::string name)
 	SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_SELECTED_IMAGE, name);
 }
 
-std::string Server::hudGetHotbarSelectedImage(RemotePlayer *player)
-{
-	if (!player)
-		return "";
-
-	return player->getHotbarSelectedImage();
-}
-
 bool Server::setLocalPlayerAnimations(RemotePlayer *player,
 		v2s32 animation_frames[4], f32 frame_speed)
 {
@@ -3408,11 +3400,10 @@ PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id, u16 proto_version
 	/*
 		Try to get an existing player
 	*/
-	RemotePlayer *player = static_cast<RemotePlayer*>(m_env->getPlayer(name));
+	RemotePlayer *player = m_env->getPlayer(name);
 
 	// If player is already connected, cancel
-	if(player != NULL && player->peer_id != 0)
-	{
+	if (player != NULL && player->peer_id != 0) {
 		infostream<<"emergePlayer(): Player already connected"<<std::endl;
 		return NULL;
 	}
@@ -3420,8 +3411,7 @@ PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id, u16 proto_version
 	/*
 		If player with the wanted peer_id already exists, cancel.
 	*/
-	if(m_env->getPlayer(peer_id) != NULL)
-	{
+	if (m_env->getPlayer(peer_id) != NULL) {
 		infostream<<"emergePlayer(): Player with wrong name but same"
 				" peer_id already exists"<<std::endl;
 		return NULL;
@@ -3429,13 +3419,13 @@ PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id, u16 proto_version
 
 	// Load player if it isn't already loaded
 	if (!player) {
-		player = static_cast<RemotePlayer*>(m_env->loadPlayer(name));
+		player = m_env->loadPlayer(name);
 	}
 
 	// Create player if it doesn't exist
 	if (!player) {
 		newplayer = true;
-		player = new RemotePlayer(this, name);
+		player = new RemotePlayer(name, this->idef());
 		// Set player position
 		infostream<<"Server: Finding spawn place for player \""
 				<<name<<"\""<<std::endl;
diff --git a/src/server.h b/src/server.h
index 8eb1afc9f..fc4758c5f 100644
--- a/src/server.h
+++ b/src/server.h
@@ -317,7 +317,10 @@ class Server : public con::PeerHandler, public MapEventReceiver,
 	void hudSetHotbarImage(RemotePlayer *player, std::string name);
 	std::string hudGetHotbarImage(RemotePlayer *player);
 	void hudSetHotbarSelectedImage(RemotePlayer *player, std::string name);
-	std::string hudGetHotbarSelectedImage(RemotePlayer *player);
+	const std::string &hudGetHotbarSelectedImage(RemotePlayer *player) const
+	{
+		return player->getHotbarSelectedImage();
+	}
 
 	inline Address getPeerAddress(u16 peer_id)
 			{ return m_con.GetPeerAddress(peer_id); }
-- 
GitLab