From ad163ee5c3f7d6ca31e0add052fb76466a9bfcc8 Mon Sep 17 00:00:00 2001
From: Foghrye4 <foghrye4@gmail.com>
Date: Sat, 8 Oct 2016 16:51:25 +0400
Subject: [PATCH] Prevent attached models from disappearing during parent
 reload (#4128)

---
 src/content_cao.cpp             | 41 ++++++++++++++++++---------------
 src/content_sao.cpp             | 20 ++++++++++++----
 src/content_sao.h               |  8 +++----
 src/genericobject.cpp           | 12 ++++++++++
 src/genericobject.h             |  5 +++-
 src/script/lua_api/l_object.cpp |  4 ++--
 src/serverobject.h              |  4 ++--
 7 files changed, 62 insertions(+), 32 deletions(-)

diff --git a/src/content_cao.cpp b/src/content_cao.cpp
index 33dae6822..207a630d7 100644
--- a/src/content_cao.cpp
+++ b/src/content_cao.cpp
@@ -1567,8 +1567,7 @@ void GenericCAO::processMessage(const std::string &data)
 	std::istringstream is(data, std::ios::binary);
 	// command
 	u8 cmd = readU8(is);
-	if(cmd == GENERIC_CMD_SET_PROPERTIES)
-	{
+	if (cmd == GENERIC_CMD_SET_PROPERTIES) {
 		m_prop = gob_read_set_properties(is);
 
 		m_selection_box = m_prop.collisionbox;
@@ -1587,9 +1586,7 @@ void GenericCAO::processMessage(const std::string &data)
 			m_prop.nametag = m_name;
 
 		expireVisuals();
-	}
-	else if(cmd == GENERIC_CMD_UPDATE_POSITION)
-	{
+	} else if (cmd == GENERIC_CMD_UPDATE_POSITION) {
 		// Not sent by the server if this object is an attachment.
 		// We might however get here if the server notices the object being detached before the client.
 		m_position = readV3F1000(is);
@@ -1619,12 +1616,10 @@ void GenericCAO::processMessage(const std::string &data)
 			pos_translator.init(m_position);
 		}
 		updateNodePos();
-	}
-	else if(cmd == GENERIC_CMD_SET_TEXTURE_MOD) {
+	} else if (cmd == GENERIC_CMD_SET_TEXTURE_MOD) {
 		std::string mod = deSerializeString(is);
 		updateTextures(mod);
-	}
-	else if(cmd == GENERIC_CMD_SET_SPRITE) {
+	} else if (cmd == GENERIC_CMD_SET_SPRITE) {
 		v2s16 p = readV2S16(is);
 		int num_frames = readU16(is);
 		float framelength = readF1000(is);
@@ -1636,8 +1631,7 @@ void GenericCAO::processMessage(const std::string &data)
 		m_tx_select_horiz_by_yawpitch = select_horiz_by_yawpitch;
 
 		updateTexturePos();
-	}
-	else if(cmd == GENERIC_CMD_SET_PHYSICS_OVERRIDE) {
+	} else if (cmd == GENERIC_CMD_SET_PHYSICS_OVERRIDE) {
 		float override_speed = readF1000(is);
 		float override_jump = readF1000(is);
 		float override_gravity = readF1000(is);
@@ -1655,8 +1649,7 @@ void GenericCAO::processMessage(const std::string &data)
 			player->physics_override_sneak = sneak;
 			player->physics_override_sneak_glitch = sneak_glitch;
 		}
-	}
-	else if(cmd == GENERIC_CMD_SET_ANIMATION) {
+	} else if (cmd == GENERIC_CMD_SET_ANIMATION) {
 		// TODO: change frames send as v2s32 value
 		v2f range = readV2F1000(is);
 		if (!m_is_local_player) {
@@ -1690,8 +1683,7 @@ void GenericCAO::processMessage(const std::string &data)
 					updateAnimation();
 			}
 		}
-	}
-	else if(cmd == GENERIC_CMD_SET_BONE_POSITION) {
+	} else if (cmd == GENERIC_CMD_SET_BONE_POSITION) {
 		std::string bone = deSerializeString(is);
 		v3f position = readV3F1000(is);
 		v3f rotation = readV3F1000(is);
@@ -1724,8 +1716,7 @@ void GenericCAO::processMessage(const std::string &data)
 		}
 
 		updateAttachments();
-	}
-	else if(cmd == GENERIC_CMD_PUNCHED) {
+	} else if (cmd == GENERIC_CMD_PUNCHED) {
 		/*s16 damage =*/ readS16(is);
 		s16 result_hp = readS16(is);
 
@@ -1753,8 +1744,7 @@ void GenericCAO::processMessage(const std::string &data)
 				updateTextures("^[brighten");
 			}
 		}
-	}
-	else if(cmd == GENERIC_CMD_UPDATE_ARMOR_GROUPS) {
+	} else if (cmd == GENERIC_CMD_UPDATE_ARMOR_GROUPS) {
 		m_armor_groups.clear();
 		int armor_groups_size = readU16(is);
 		for(int i=0; i<armor_groups_size; i++)
@@ -1770,6 +1760,19 @@ void GenericCAO::processMessage(const std::string &data)
 		if (m_nametag != NULL) {
 			m_nametag->nametag_color = m_prop.nametag_color;
 		}
+	} else if (cmd == GENERIC_CMD_SPAWN_INFANT) {
+		u16 child_id = readU16(is);
+		u8 type = readU8(is);
+
+		if (GenericCAO *childobj = m_env->getGenericCAO(child_id)) {
+			childobj->initialize(deSerializeLongString(is));
+		} else {
+			m_env->addActiveObject(child_id, type, deSerializeLongString(is));
+		}
+	} else {
+		warningstream << FUNCTION_NAME
+			<< ": unknown command or outdated client \""
+			<< cmd << std::endl;
 	}
 }
 
diff --git a/src/content_sao.cpp b/src/content_sao.cpp
index 2317cbdfe..1664f5993 100644
--- a/src/content_sao.cpp
+++ b/src/content_sao.cpp
@@ -380,7 +380,7 @@ std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version)
 		writeF1000(os, m_yaw);
 		writeS16(os, m_hp);
 
-		writeU8(os, 4 + m_bone_position.size()); // number of messages stuffed in here
+		writeU8(os, 4 + m_bone_position.size() + m_attachment_child_ids.size()); // number of messages stuffed in here
 		os<<serializeLongString(getPropertyPacket()); // message 1
 		os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
 		os<<serializeLongString(gob_cmd_update_animation(
@@ -391,6 +391,12 @@ std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version)
 					(*ii).second.X, (*ii).second.Y)); // m_bone_position.size
 		}
 		os<<serializeLongString(gob_cmd_update_attachment(m_attachment_parent_id, m_attachment_bone, m_attachment_position, m_attachment_rotation)); // 4
+		for (UNORDERED_SET<int>::const_iterator ii = m_attachment_child_ids.begin(); 
+				(ii != m_attachment_child_ids.end()); ++ii) {
+			if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
+				os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(), obj->getClientInitializationData(protocol_version)));
+			}
+		}
 	}
 	else
 	{
@@ -618,7 +624,7 @@ void LuaEntitySAO::removeAttachmentChild(int child_id)
 	m_attachment_child_ids.erase(child_id);
 }
 
-std::set<int> LuaEntitySAO::getAttachmentChildIds()
+UNORDERED_SET<int> LuaEntitySAO::getAttachmentChildIds()
 {
 	return m_attachment_child_ids;
 }
@@ -860,7 +866,7 @@ std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
 		writeF1000(os, m_player->getYaw());
 		writeS16(os, getHP());
 
-		writeU8(os, 6 + m_bone_position.size()); // number of messages stuffed in here
+		writeU8(os, 6 + m_bone_position.size() + m_attachment_child_ids.size()); // number of messages stuffed in here
 		os<<serializeLongString(getPropertyPacket()); // message 1
 		os<<serializeLongString(gob_cmd_update_armor_groups(m_armor_groups)); // 2
 		os<<serializeLongString(gob_cmd_update_animation(
@@ -874,6 +880,12 @@ std::string PlayerSAO::getClientInitializationData(u16 protocol_version)
 				m_physics_override_jump, m_physics_override_gravity, m_physics_override_sneak,
 				m_physics_override_sneak_glitch)); // 5
 		os << serializeLongString(gob_cmd_update_nametag_attributes(m_prop.nametag_color)); // 6 (GENERIC_CMD_UPDATE_NAMETAG_ATTRIBUTES) : Deprecated, for backwards compatibility only.
+		for (UNORDERED_SET<int>::const_iterator ii = m_attachment_child_ids.begin(); 
+				ii != m_attachment_child_ids.end(); ++ii) {
+			if (ServerActiveObject *obj = m_env->getActiveObject(*ii)) {
+				os << serializeLongString(gob_cmd_update_infant(*ii, obj->getSendType(), obj->getClientInitializationData(protocol_version)));
+			}
+		}
 	}
 	else
 	{
@@ -1266,7 +1278,7 @@ void PlayerSAO::removeAttachmentChild(int child_id)
 	m_attachment_child_ids.erase(child_id);
 }
 
-std::set<int> PlayerSAO::getAttachmentChildIds()
+UNORDERED_SET<int> PlayerSAO::getAttachmentChildIds()
 {
 	return m_attachment_child_ids;
 }
diff --git a/src/content_sao.h b/src/content_sao.h
index c97db4922..341ebb5da 100644
--- a/src/content_sao.h
+++ b/src/content_sao.h
@@ -67,7 +67,7 @@ class LuaEntitySAO : public ServerActiveObject
 	void getAttachment(int *parent_id, std::string *bone, v3f *position, v3f *rotation);
 	void addAttachmentChild(int child_id);
 	void removeAttachmentChild(int child_id);
-	std::set<int> getAttachmentChildIds();
+	UNORDERED_SET<int> getAttachmentChildIds();
 	ObjectProperties* accessObjectProperties();
 	void notifyObjectPropertiesModified();
 	/* LuaEntitySAO-specific */
@@ -116,7 +116,7 @@ class LuaEntitySAO : public ServerActiveObject
 	bool m_bone_position_sent;
 
 	int m_attachment_parent_id;
-	std::set<int> m_attachment_child_ids;
+	UNORDERED_SET<int> m_attachment_child_ids;
 	std::string m_attachment_bone;
 	v3f m_attachment_position;
 	v3f m_attachment_rotation;
@@ -210,7 +210,7 @@ class PlayerSAO : public ServerActiveObject
 	void getAttachment(int *parent_id, std::string *bone, v3f *position, v3f *rotation);
 	void addAttachmentChild(int child_id);
 	void removeAttachmentChild(int child_id);
-	std::set<int> getAttachmentChildIds();
+	UNORDERED_SET<int> getAttachmentChildIds();
 	ObjectProperties* accessObjectProperties();
 	void notifyObjectPropertiesModified();
 
@@ -320,7 +320,7 @@ class PlayerSAO : public ServerActiveObject
 	bool m_bone_position_sent;
 
 	int m_attachment_parent_id;
-	std::set<int> m_attachment_child_ids;
+	UNORDERED_SET<int> m_attachment_child_ids;
 	std::string m_attachment_bone;
 	v3f m_attachment_position;
 	v3f m_attachment_rotation;
diff --git a/src/genericobject.cpp b/src/genericobject.cpp
index 368cae1ff..c4660cf44 100644
--- a/src/genericobject.cpp
+++ b/src/genericobject.cpp
@@ -182,3 +182,15 @@ std::string gob_cmd_update_nametag_attributes(video::SColor color)
 	writeARGB8(os, color);
 	return os.str();
 }
+
+std::string gob_cmd_update_infant(u16 id, u8 type, std::string client_initialization_data)
+{
+	std::ostringstream os(std::ios::binary);
+	// command 
+	writeU8(os, GENERIC_CMD_SPAWN_INFANT);
+	// parameters
+	writeU16(os, id);
+	writeU8(os, type);
+	os<<serializeLongString(client_initialization_data);
+	return os.str();
+}
diff --git a/src/genericobject.h b/src/genericobject.h
index b92570831..48e71db75 100644
--- a/src/genericobject.h
+++ b/src/genericobject.h
@@ -35,7 +35,8 @@ enum GenericCMD {
 	GENERIC_CMD_SET_BONE_POSITION,
 	GENERIC_CMD_ATTACH_TO,
 	GENERIC_CMD_SET_PHYSICS_OVERRIDE,
-	GENERIC_CMD_UPDATE_NAMETAG_ATTRIBUTES
+	GENERIC_CMD_UPDATE_NAMETAG_ATTRIBUTES,
+	GENERIC_CMD_SPAWN_INFANT
 };
 
 #include "object_properties.h"
@@ -77,5 +78,7 @@ std::string gob_cmd_update_attachment(int parent_id, std::string bone, v3f posit
 
 std::string gob_cmd_update_nametag_attributes(video::SColor color);
 
+std::string gob_cmd_update_infant(u16 id, u8 type, std::string client_initialization_data);
+
 #endif
 
diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp
index 4e1a1c159..34e175ad0 100644
--- a/src/script/lua_api/l_object.cpp
+++ b/src/script/lua_api/l_object.cpp
@@ -137,8 +137,8 @@ int ObjectRef::l_remove(lua_State *L)
 	if (co->getType() == ACTIVEOBJECT_TYPE_PLAYER)
 		return 0;
 
-	std::set<int> child_ids = co->getAttachmentChildIds();
-	std::set<int>::iterator it;
+	UNORDERED_SET<int> child_ids = co->getAttachmentChildIds();
+	UNORDERED_SET<int>::iterator it;
 	for (it = child_ids.begin(); it != child_ids.end(); ++it) {
 		ServerActiveObject *child = env->getActiveObject(*it);
 		child->setAttachment(0, "", v3f(0, 0, 0), v3f(0, 0, 0));
diff --git a/src/serverobject.h b/src/serverobject.h
index 597eb63a8..9f8d5403c 100644
--- a/src/serverobject.h
+++ b/src/serverobject.h
@@ -167,8 +167,8 @@ class ServerActiveObject : public ActiveObject
 	{}
 	virtual void removeAttachmentChild(int child_id)
 	{}
-	virtual std::set<int> getAttachmentChildIds()
-	{ return std::set<int>(); }
+	virtual UNORDERED_SET<int> getAttachmentChildIds()
+	{ return UNORDERED_SET<int>(); }
 	virtual ObjectProperties* accessObjectProperties()
 	{ return NULL; }
 	virtual void notifyObjectPropertiesModified()
-- 
GitLab