From d19c8b815dc137ea4c19e5f5a54c40693059b455 Mon Sep 17 00:00:00 2001
From: RealBadAngel <mk@realbadangel.pl>
Date: Fri, 19 Jul 2013 19:50:33 +0200
Subject: [PATCH] Add set_breath and get_breath to lua API.

---
 doc/lua_api.txt                 |  5 ++++
 src/client.cpp                  | 32 +++++++++++++++++++++++--
 src/client.h                    |  1 +
 src/clientserver.h              | 12 ++++++++++
 src/content_sao.cpp             | 11 +++++++++
 src/content_sao.h               |  4 +++-
 src/environment.cpp             | 40 +++++++++++++++++++++++---------
 src/environment.h               |  7 +++++-
 src/player.cpp                  | 10 ++++++--
 src/player.h                    | 14 +++++++++--
 src/script/lua_api/l_object.cpp | 29 +++++++++++++++++++++++
 src/script/lua_api/l_object.h   |  6 +++++
 src/server.cpp                  | 41 +++++++++++++++++++++++++++++++++
 src/server.h                    |  2 ++
 14 files changed, 195 insertions(+), 19 deletions(-)

diff --git a/doc/lua_api.txt b/doc/lua_api.txt
index 74b3d3ba5..6a4d19838 100644
--- a/doc/lua_api.txt
+++ b/doc/lua_api.txt
@@ -1547,6 +1547,11 @@ Player-only: (no-op for other objects)
 - get_look_yaw(): yaw in radians (wraps around pretty randomly as of now)
 - set_look_pitch(radians): sets look pitch
 - set_look_yaw(radians): sets look yaw
+- get_breath() : returns players breath
+- set_breath(value) : sets players breath
+    values: 0    player is drowning,
+            1-10 number of bubbles remain,
+            11   bubbles bar is not shown
 - set_inventory_formspec(formspec)
   ^ Redefine player's inventory form
   ^ Should usually be called in on_joinplayer
diff --git a/src/client.cpp b/src/client.cpp
index c458684f6..e5ba14850 100644
--- a/src/client.cpp
+++ b/src/client.cpp
@@ -692,9 +692,14 @@ void Client::step(float dtime)
 					m_client_event_queue.push_back(event);
 				}
 			}
+			else if(event.type == CEE_PLAYER_BREATH)
+			{
+					u16 breath = event.player_breath.amount;
+					sendBreath(breath);
+			}
 		}
 	}
-	
+
 	/*
 		Print some info
 	*/
@@ -1579,6 +1584,15 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
 			m_client_event_queue.push_back(event);
 		}
 	}
+	else if(command == TOCLIENT_BREATH)
+	{
+		std::string datastring((char*)&data[2], datasize-2);
+		std::istringstream is(datastring, std::ios_base::binary);
+		Player *player = m_env.getLocalPlayer();
+		assert(player != NULL);
+		u16 breath = readU16(is);
+		player->setBreath(breath) ;
+	}
 	else if(command == TOCLIENT_MOVE_PLAYER)
 	{
 		std::string datastring((char*)&data[2], datasize-2);
@@ -2359,6 +2373,20 @@ void Client::sendDamage(u8 damage)
 	Send(0, data, true);
 }
 
+void Client::sendBreath(u16 breath)
+{
+	DSTACK(__FUNCTION_NAME);
+	std::ostringstream os(std::ios_base::binary);
+
+	writeU16(os, TOSERVER_BREATH);
+	writeU16(os, breath);
+	// Make data buffer
+	std::string s = os.str();
+	SharedBuffer<u8> data((u8*)s.c_str(), s.size());
+	// Send as reliable
+	Send(0, data, true);
+}
+
 void Client::sendRespawn()
 {
 	DSTACK(__FUNCTION_NAME);
@@ -2694,7 +2722,7 @@ u16 Client::getBreath()
 {
 	Player *player = m_env.getLocalPlayer();
 	assert(player != NULL);
-	return player->breath;
+	return player->getBreath();
 }
 
 bool Client::getChatMessage(std::wstring &message)
diff --git a/src/client.h b/src/client.h
index 0b69abf56..84cabf3f8 100644
--- a/src/client.h
+++ b/src/client.h
@@ -306,6 +306,7 @@ class Client : public con::PeerHandler, public InventoryManager, public IGameDef
 	void sendChangePassword(const std::wstring oldpassword,
 			const std::wstring newpassword);
 	void sendDamage(u8 damage);
+	void sendBreath(u16 breath);
 	void sendRespawn();
 
 	ClientEnvironment& getEnv()
diff --git a/src/clientserver.h b/src/clientserver.h
index a43baab23..a84d29a90 100644
--- a/src/clientserver.h
+++ b/src/clientserver.h
@@ -488,6 +488,12 @@ enum ToClientCommand
 		u16 len
 		u8[len] value
 	*/
+
+	TOCLIENT_BREATH = 0x4e,
+	/*
+		u16 command
+		u16 breath
+	*/
 };
 
 enum ToServerCommand
@@ -711,6 +717,12 @@ enum ToServerCommand
 	/*
 		u16 command
 	*/
+
+	TOSERVER_BREATH = 0x42,
+	/*
+		u16 command
+		u16 breath
+	*/
 };
 
 #endif
diff --git a/src/content_sao.cpp b/src/content_sao.cpp
index cc02a7431..993859f1c 100644
--- a/src/content_sao.cpp
+++ b/src/content_sao.cpp
@@ -935,6 +935,7 @@ PlayerSAO::PlayerSAO(ServerEnvironment *env_, Player *player_, u16 peer_id_,
 	m_moved(false),
 	m_inventory_not_sent(false),
 	m_hp_not_sent(false),
+	m_breath_not_sent(false),
 	m_wielded_item_not_sent(false),
 	m_physics_override_speed(1),
 	m_physics_override_jump(1),
@@ -1370,6 +1371,16 @@ void PlayerSAO::setHP(s16 hp)
 	}
 }
 
+u16 PlayerSAO::getBreath() const
+{
+	return m_player->getBreath();
+}
+
+void PlayerSAO::setBreath(u16 breath)
+{
+	m_player->setBreath(breath);
+}
+
 void PlayerSAO::setArmorGroups(const ItemGroupList &armor_groups)
 {
 	m_armor_groups = armor_groups;
diff --git a/src/content_sao.h b/src/content_sao.h
index dca02bb00..bfce83d02 100644
--- a/src/content_sao.h
+++ b/src/content_sao.h
@@ -162,7 +162,8 @@ class PlayerSAO : public ServerActiveObject
 	void rightClick(ServerActiveObject *clicker);
 	s16 getHP() const;
 	void setHP(s16 hp);
-	
+	u16 getBreath() const;
+	void setBreath(u16 breath);
 	void setArmorGroups(const ItemGroupList &armor_groups);
 	void setAnimation(v2f frame_range, float frame_speed, float frame_blend);
 	void setBonePosition(std::string bone, v3f position, v3f rotation);
@@ -282,6 +283,7 @@ class PlayerSAO : public ServerActiveObject
 	bool m_moved;
 	bool m_inventory_not_sent;
 	bool m_hp_not_sent;
+	bool m_breath_not_sent;
 	bool m_wielded_item_not_sent;
 
 	float m_physics_override_speed;
diff --git a/src/environment.cpp b/src/environment.cpp
index cd8783984..57fdfd7e5 100644
--- a/src/environment.cpp
+++ b/src/environment.cpp
@@ -2242,15 +2242,19 @@ void ClientEnvironment::step(float dtime)
 		v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
 		MapNode n = m_map->getNodeNoEx(p);
 		ContentFeatures c = m_gamedef->ndef()->get(n);
-
-		if(c.isLiquid() && c.drowning){
-			if(lplayer->breath > 10)
-				lplayer->breath = 11;
-			if(lplayer->breath > 0)
-				lplayer->breath -= 1;
+		if(c.isLiquid() && c.drowning && lplayer->hp > 0){
+			u16 breath = lplayer->getBreath();
+			if(breath > 10){
+				breath = 11;
+			}
+			if(breath > 0){
+				breath -= 1;
+			}
+			lplayer->setBreath(breath);
+			updateLocalPlayerBreath(breath);
 		}
 
-		if(lplayer->breath == 0){
+		if(lplayer->getBreath() == 0){
 			damageLocalPlayer(1, true);
 		}
 	}
@@ -2262,10 +2266,16 @@ void ClientEnvironment::step(float dtime)
 		v3s16 p = floatToInt(pf + v3f(0, BS*1.6, 0), BS);
 		MapNode n = m_map->getNodeNoEx(p);
 		ContentFeatures c = m_gamedef->ndef()->get(n);
-
-		if(!c.isLiquid() || !c.drowning){
-			if(lplayer->breath <= 10)
-				lplayer->breath += 1;
+		if (!lplayer->hp){
+			lplayer->setBreath(11);
+		}
+		else if(!c.isLiquid() || !c.drowning){
+			u16 breath = lplayer->getBreath();
+			if(breath <= 10){
+				breath += 1;
+				lplayer->setBreath(breath);
+				updateLocalPlayerBreath(breath);
+			}
 		}
 	}
 
@@ -2528,6 +2538,14 @@ void ClientEnvironment::damageLocalPlayer(u8 damage, bool handle_hp)
 	m_client_event_queue.push_back(event);
 }
 
+void ClientEnvironment::updateLocalPlayerBreath(u16 breath)
+{
+	ClientEnvEvent event;
+	event.type = CEE_PLAYER_BREATH;
+	event.player_breath.amount = breath;
+	m_client_event_queue.push_back(event);
+}
+
 /*
 	Client likes to call these
 */
diff --git a/src/environment.h b/src/environment.h
index ac479999c..e175d70d9 100644
--- a/src/environment.h
+++ b/src/environment.h
@@ -395,7 +395,8 @@ class ClientSimpleObject;
 enum ClientEnvEventType
 {
 	CEE_NONE,
-	CEE_PLAYER_DAMAGE
+	CEE_PLAYER_DAMAGE,
+	CEE_PLAYER_BREATH
 };
 
 struct ClientEnvEvent
@@ -408,6 +409,9 @@ struct ClientEnvEvent
 			u8 amount;
 			bool send_to_server;
 		} player_damage;
+		struct{
+			u16 amount;
+		} player_breath;
 	};
 };
 
@@ -462,6 +466,7 @@ class ClientEnvironment : public Environment
 	*/
 
 	void damageLocalPlayer(u8 damage, bool handle_hp=true);
+	void updateLocalPlayerBreath(u16 breath);
 
 	/*
 		Client likes to call these
diff --git a/src/player.cpp b/src/player.cpp
index 2a7a3084c..8028fe6af 100644
--- a/src/player.cpp
+++ b/src/player.cpp
@@ -36,10 +36,10 @@ Player::Player(IGameDef *gamedef):
 	camera_barely_in_ceiling(false),
 	inventory(gamedef->idef()),
 	hp(PLAYER_MAX_HP),
-	breath(-1),
 	peer_id(PEER_ID_INEXISTENT),
 // protected
 	m_gamedef(gamedef),
+	m_breath(-1),
 	m_pitch(0),
 	m_yaw(0),
 	m_speed(0,0,0),
@@ -177,11 +177,12 @@ void Player::serialize(std::ostream &os)
 	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);
 }
 
@@ -213,6 +214,11 @@ void Player::deSerialize(std::istream &is, std::string playername)
 	}catch(SettingNotFoundException &e){
 		hp = 20;
 	}
+	try{
+		m_breath = args.getS32("breath");
+	}catch(SettingNotFoundException &e){
+		m_breath = 11;
+	}
 
 	inventory.deSerialize(is);
 
diff --git a/src/player.h b/src/player.h
index 60645a60f..7ddc40b37 100644
--- a/src/player.h
+++ b/src/player.h
@@ -160,6 +160,16 @@ class Player
 		return m_yaw;
 	}
 
+	u16 getBreath()
+	{
+		return m_breath;
+	}
+
+	virtual void setBreath(u16 breath)
+	{
+		m_breath = breath;
+	}
+
 	f32 getRadPitch()
 	{
 		return -1.0 * m_pitch * core::DEGTORAD;
@@ -249,13 +259,12 @@ class Player
 	float physics_override_gravity;
 
 	u16 hp;
-	u16 breath;
 
 	float hurt_tilt_timer;
 	float hurt_tilt_strength;
 
 	u16 peer_id;
-	
+
 	std::string inventory_formspec;
 	
 	PlayerControl control;
@@ -274,6 +283,7 @@ class Player
 	IGameDef *m_gamedef;
 
 	char m_name[PLAYERNAME_SIZE];
+	u16 m_breath;
 	f32 m_pitch;
 	f32 m_yaw;
 	v3f m_speed;
diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp
index f90b59285..ee24789c5 100644
--- a/src/script/lua_api/l_object.cpp
+++ b/src/script/lua_api/l_object.cpp
@@ -702,6 +702,33 @@ int ObjectRef::l_set_look_yaw(lua_State *L)
 	return 1;
 }
 
+// set_breath(self, breath)
+int ObjectRef::l_set_breath(lua_State *L)
+{
+	NO_MAP_LOCK_REQUIRED;
+	ObjectRef *ref = checkobject(L, 1);
+	PlayerSAO* co = getplayersao(ref);
+	if(co == NULL) return 0;
+	u16 breath = luaL_checknumber(L, 2);
+	// Do it
+	co->setBreath(breath);
+	co->m_breath_not_sent = true;
+	return 0;
+}
+
+// get_breath(self)
+int ObjectRef::l_get_breath(lua_State *L)
+{
+	NO_MAP_LOCK_REQUIRED;
+	ObjectRef *ref = checkobject(L, 1);
+	PlayerSAO* co = getplayersao(ref);
+	if(co == NULL) return 0;
+	// Do it
+	u16 breath = co->getBreath();
+	lua_pushinteger (L, breath);
+	return 1;
+}
+
 // set_inventory_formspec(self, formspec)
 int ObjectRef::l_set_inventory_formspec(lua_State *L)
 {
@@ -1098,6 +1125,8 @@ const luaL_reg ObjectRef::methods[] = {
 	luamethod(ObjectRef, get_look_yaw),
 	luamethod(ObjectRef, set_look_yaw),
 	luamethod(ObjectRef, set_look_pitch),
+	luamethod(ObjectRef, get_breath),
+	luamethod(ObjectRef, set_breath),
 	luamethod(ObjectRef, set_inventory_formspec),
 	luamethod(ObjectRef, get_inventory_formspec),
 	luamethod(ObjectRef, get_player_control),
diff --git a/src/script/lua_api/l_object.h b/src/script/lua_api/l_object.h
index 57dac0e64..a82638442 100644
--- a/src/script/lua_api/l_object.h
+++ b/src/script/lua_api/l_object.h
@@ -179,6 +179,12 @@ class ObjectRef
 	// set_look_yaw(self, radians)
 	static int l_set_look_yaw(lua_State *L);
 
+	// set_breath(self, breath)
+	static int l_set_breath(lua_State *L);
+
+	// get_breath(self, breath)
+	static int l_get_breath(lua_State *L);
+
 	// set_inventory_formspec(self, formspec)
 	static int l_set_inventory_formspec(lua_State *L);
 
diff --git a/src/server.cpp b/src/server.cpp
index 955858c70..3bba193da 100644
--- a/src/server.cpp
+++ b/src/server.cpp
@@ -1139,6 +1139,13 @@ void Server::AsyncRunStep()
 					SendPlayerHP(client->peer_id);
 			}
 
+			/*
+				Send player breath if changed
+			*/
+			if(playersao->m_breath_not_sent){
+				SendPlayerBreath(client->peer_id);
+			}
+
 			/*
 				Send player inventories if necessary
 			*/
@@ -2105,6 +2112,9 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
 		if(g_settings->getBool("enable_damage"))
 			SendPlayerHP(peer_id);
 
+		// Send Breath
+		SendPlayerBreath(peer_id);
+
 		// Send detached inventories
 		sendDetachedInventories(peer_id);
 
@@ -2583,6 +2593,13 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
 				SendPlayerHP(peer_id);
 		}
 	}
+	else if(command == TOSERVER_BREATH)
+	{
+		std::string datastring((char*)&data[2], datasize-2);
+		std::istringstream is(datastring, std::ios_base::binary);
+		u16 breath = readU16(is);
+		playersao->setBreath(breath);
+	}
 	else if(command == TOSERVER_PASSWORD)
 	{
 		/*
@@ -3326,6 +3343,21 @@ void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
 	con.Send(peer_id, 0, data, true);
 }
 
+void Server::SendBreath(con::Connection &con, u16 peer_id, u16 breath)
+{
+	DSTACK(__FUNCTION_NAME);
+	std::ostringstream os(std::ios_base::binary);
+
+	writeU16(os, TOCLIENT_BREATH);
+	writeU16(os, breath);
+
+	// Make data buffer
+	std::string s = os.str();
+	SharedBuffer<u8> data((u8*)s.c_str(), s.size());
+	// Send as reliable
+	con.Send(peer_id, 0, data, true);
+}
+
 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
 		const std::wstring &reason)
 {
@@ -3755,6 +3787,15 @@ void Server::SendPlayerHP(u16 peer_id)
 	SendHP(m_con, peer_id, playersao->getHP());
 }
 
+void Server::SendPlayerBreath(u16 peer_id)
+{
+	DSTACK(__FUNCTION_NAME);
+	PlayerSAO *playersao = getPlayerSAO(peer_id);
+	assert(playersao);
+	playersao->m_breath_not_sent = false;
+	SendBreath(m_con, peer_id, playersao->getBreath());
+}
+
 void Server::SendMovePlayer(u16 peer_id)
 {
 	DSTACK(__FUNCTION_NAME);
diff --git a/src/server.h b/src/server.h
index edc5c4895..65762d901 100644
--- a/src/server.h
+++ b/src/server.h
@@ -557,6 +557,7 @@ class Server : public con::PeerHandler, public MapEventReceiver,
 
 	static void SendMovement(con::Connection &con, u16 peer_id);
 	static void SendHP(con::Connection &con, u16 peer_id, u8 hp);
+	static void SendBreath(con::Connection &con, u16 peer_id, u16 breath);
 	static void SendAccessDenied(con::Connection &con, u16 peer_id,
 			const std::wstring &reason);
 	static void SendDeathscreen(con::Connection &con, u16 peer_id,
@@ -578,6 +579,7 @@ class Server : public con::PeerHandler, public MapEventReceiver,
 	void SendChatMessage(u16 peer_id, const std::wstring &message);
 	void BroadcastChatMessage(const std::wstring &message);
 	void SendPlayerHP(u16 peer_id);
+	void SendPlayerBreath(u16 peer_id);
 	void SendMovePlayer(u16 peer_id);
 	void SendPlayerPrivileges(u16 peer_id);
 	void SendPlayerInventoryFormspec(u16 peer_id);
-- 
GitLab