Browse Source

Bulk Position Updates + Y coordinate fixes + reset spawn distance back

Fixes #63 - Simple implementation of bulk position updates, adapted from Stitch's old eq2emu pvp src
Image 4 years ago
parent
commit
eb05763702

+ 214 - 8
EQ2/source/WorldServer/Spawn.cpp

@@ -647,6 +647,217 @@ EQ2Packet* Spawn::spawn_update_packet(Player* player, int16 version, bool overri
 	return ret_packet;
 }
 
+uchar* Spawn::spawn_info_changes_ex(Player* player, int16 version) {
+	int16 index = player->player_spawn_index_map[this];
+
+	PacketStruct* packet = player->GetSpawnInfoStruct();
+
+	player->info_mutex.writelock(__FUNCTION__, __LINE__);
+
+	packet->ResetData();
+	InitializeInfoPacketData(player, packet);
+
+	string* data = packet->serializeString();
+	int32 size = data->length();
+	uchar* xor_info_packet = player->GetTempInfoPacketForXOR();
+
+	if (!xor_info_packet) {
+		xor_info_packet = player->SetTempInfoPacketForXOR(size);
+	}
+
+	uchar* orig_packet = player->GetSpawnInfoPacketForXOR(id);
+
+	if (orig_packet) {
+		memcpy(xor_info_packet, (uchar*)data->c_str(), size);
+		Encode(xor_info_packet, orig_packet, size);
+	}
+
+	bool changed = false;
+	for (int i = 0; i < size; ++i) {
+		if (xor_info_packet[i]) {
+			changed = true;
+			break;
+		}
+	}
+
+	if (!changed) {
+		player->info_mutex.releasewritelock(__FUNCTION__, __LINE__);
+		return nullptr;
+	}
+
+	uchar* tmp = new uchar[size + 10];
+	size = Pack(tmp, xor_info_packet, size, size, version);
+
+	player->info_mutex.releasewritelock(__FUNCTION__, __LINE__);
+
+	int32 orig_size = size;
+
+	size -= sizeof(int32);
+	size += CheckOverLoadSize(index);
+	info_packet_size = size;
+
+	uchar* tmp2 = new uchar[size];
+	uchar* ptr = tmp2;
+
+	ptr += DoOverLoad(index, ptr);
+
+	memcpy(ptr, tmp + sizeof(int32), orig_size - sizeof(int32));
+
+	delete[] tmp;
+
+	return move(tmp2);
+}
+
+uchar* Spawn::spawn_vis_changes_ex(Player* player, int16 version) {
+	PacketStruct* vis_struct = player->GetSpawnVisStruct();
+	int16 index = player->player_spawn_index_map[this];
+
+	player->vis_mutex.writelock(__FUNCTION__, __LINE__);
+
+	uchar* orig_packet = player->GetSpawnVisPacketForXOR(id);
+
+	vis_struct->ResetData();
+	InitializeVisPacketData(player, vis_struct);
+
+	string* data = vis_struct->serializeString();
+	int32 size = data->length();
+	uchar* xor_vis_packet = player->GetTempVisPacketForXOR();
+
+	if (!xor_vis_packet) {
+		xor_vis_packet = player->SetTempVisPacketForXOR(size);
+	}
+
+	if (orig_packet) {
+		memcpy(xor_vis_packet, (uchar*)data->c_str(), size);
+		Encode(xor_vis_packet, orig_packet, size);
+	}
+
+	bool changed = false;
+	for (int i = 0; i < size; ++i) {
+		if (xor_vis_packet[i]) {
+			changed = true;
+			break;
+		}
+	}
+
+	if (!changed) {
+		player->vis_mutex.releasewritelock(__FUNCTION__, __LINE__);
+		return nullptr;
+	}
+
+	uchar* tmp = new uchar[size + 10];
+	size = Pack(tmp, xor_vis_packet, size, size, version);
+
+	player->vis_mutex.releasewritelock(__FUNCTION__, __LINE__);
+
+	int32 orig_size = size;
+
+	size -= sizeof(int32);
+	size += CheckOverLoadSize(index);
+	vis_packet_size = size;
+
+	uchar* tmp2 = new uchar[size];
+	uchar* ptr = tmp2;
+
+	ptr += DoOverLoad(index, ptr);
+
+	memcpy(ptr, tmp + sizeof(int32), orig_size - sizeof(int32));
+
+	delete[] tmp;
+
+	return move(tmp2);
+}
+
+uchar* Spawn::spawn_pos_changes_ex(Player* player, int16 version) {
+	int16 index = player->GetIndexForSpawn(this);
+
+	PacketStruct* packet = player->GetSpawnPosStruct();
+
+	player->pos_mutex.writelock(__FUNCTION__, __LINE__);
+
+	uchar* orig_packet = player->GetSpawnPosPacketForXOR(id);
+
+	packet->ResetData();
+	InitializePosPacketData(player, packet);
+
+	string* data = packet->serializeString();
+	int32 size = data->length();
+	uchar* xor_pos_packet = player->GetTempPosPacketForXOR();
+
+	if (!xor_pos_packet) {
+		xor_pos_packet = player->SetTempPosPacketForXOR(size);
+	}
+
+	if (orig_packet) {
+		memcpy(xor_pos_packet, (uchar*)data->c_str(), size);
+		Encode(xor_pos_packet, orig_packet, size);
+	}
+
+	bool changed = false;
+	for (int i = 0; i < size; ++i) {
+		if (xor_pos_packet[i]) {
+			changed = true;
+			break;
+		}
+	}
+
+	if (!changed) {
+		player->pos_mutex.releasewritelock(__FUNCTION__, __LINE__);
+		return nullptr;
+	}
+
+	uchar* tmp = new uchar[size + 10];
+
+	size = Pack(tmp, xor_pos_packet, size, size, version);
+
+	player->pos_mutex.releasewritelock(__FUNCTION__, __LINE__);
+
+	int32 orig_size = size;
+
+	if (version >= 1188) {
+		size += 1;
+	}
+
+	if (IsPlayer()) {
+		size += 4;
+	}
+
+	size -= sizeof(int32);
+	size += CheckOverLoadSize(index);
+	pos_packet_size = size;
+
+	uchar* tmp2 = new uchar[size];
+	uchar* ptr = tmp2;
+
+	ptr += DoOverLoad(index, ptr);
+
+	// extra byte in coe+ clients, 0 for NPC's 1 for Players
+	int8 x = 0;
+
+	if (version >= 1188) {
+		if (IsPlayer()) {
+			x = 1;
+			memcpy(ptr, &x, sizeof(int8));
+			ptr += sizeof(int8);
+
+			int32 now = Timer::GetCurrentTime2();
+			memcpy(ptr, &now, sizeof(int32));
+			ptr += sizeof(int32);
+		}
+		else {
+			memcpy(ptr, &x, sizeof(int8));
+			ptr += sizeof(int8);
+		}
+	}
+
+	memcpy(ptr, tmp + sizeof(int32), orig_size - sizeof(int32));
+
+	delete[] tmp;
+
+	return move(tmp2);
+}
+
+
 EQ2Packet* Spawn::serialize(Player* player, int16 version){
 	return 0;
 }
@@ -2227,24 +2438,19 @@ bool Spawn::CalculateChange(){
 			// Normalize the forward vector and multiply by speed, this gives us our change in coords, just need to add them to our current coords
 			float len = sqrtf(tar_vx * tar_vx + tar_vy * tar_vy + tar_vz * tar_vz);
 			tar_vx = (tar_vx / len) * speed;
-
-			if (GetZone() == NULL || GetZone()->Grid == NULL)
-				tar_vy = (tar_vy / len) * speed;
-
+			tar_vy = (tar_vy / len) * speed;
 			tar_vz = (tar_vz / len) * speed;
 
 			// Distance less then 0.5 just set the npc to the target location
-			if (GetDistance(data->x, data->y, data->z, ( IsWidget() && (!GetZone() || (GetZone() && GetZone()->Grid == NULL))) ? false : true) <= 0.5f) {
+			if (GetDistance(data->x, data->y, data->z, IsWidget() ? false : true) <= 0.5f) {
 				SetX(data->x, false);
 				SetZ(data->z, false);
 				SetY(data->y, false, true);
-				remove_needed = true;
 			}
 			else {
 				SetX(nx + tar_vx, false);
 				SetZ(nz + tar_vz, false);
-				SetY(ny, false, true);
-				remove_needed = true;
+				SetY(ny + tar_vy, false);
 			}
 
 			if (GetZone()->Grid != nullptr) {

+ 22 - 4
EQ2/source/WorldServer/Spawn.h

@@ -194,6 +194,20 @@ struct MovementLocation{
 	bool	mapped;
 };
 
+struct SpawnUpdate {
+	int32 spawn_id;
+	bool info_changed;
+	bool vis_changed;
+	bool pos_changed;
+	shared_ptr<Client> client;
+};
+
+struct SpawnData {
+	Spawn* spawn;
+	uchar* data;
+	int32 size;
+};
+
 class Spawn {
 public:
 	Spawn();
@@ -738,6 +752,10 @@ public:
 	uchar* spawn_pos_changes(Player* spawn, int16 version);
 	uchar* spawn_vis_changes(Player* spawn, int16 version);
 
+	uchar* spawn_info_changes_ex(Player* spawn, int16 version);
+	uchar* spawn_pos_changes_ex(Player* spawn, int16 version);
+	uchar* spawn_vis_changes_ex(Player* spawn, int16 version);
+
 	virtual bool EngagedInCombat(){ return false; }
 	virtual bool IsObject(){ return false; }
 	virtual bool IsGroundSpawn(){ return false; }
@@ -955,6 +973,10 @@ public:
 		if (movement_locations)
 			movement_locations->clear();
 	}
+
+	int16 pos_packet_size;
+	int16 info_packet_size;
+	int16 vis_packet_size;
 protected:
 	bool	send_spawn_changes;
 	bool	invulnerable;
@@ -982,7 +1004,6 @@ protected:
 	map<int32, vector<int16>* > required_quests;
 	map<int32, LUAHistory> required_history;
 	EquipmentItemList equipment_list;
-
 private:	
 	deque<MovementLocation*>* movement_locations;
 	Mutex*			MMovementLocations;
@@ -1028,9 +1049,6 @@ private:
 	int32 m_addedToWorldTimestamp;
 	int16 m_spawnAnimLeeway;
 
-	int16 pos_packet_size;
-	int16 info_packet_size;
-	int16 vis_packet_size;
 	Mutex m_Update;
 };
 

+ 166 - 0
EQ2/source/WorldServer/client.cpp

@@ -8241,4 +8241,170 @@ bool Client::HandleNewLogin(int32 account_id, int32 access_code)
 	}
 
 	return true;
+}
+
+
+void Client::SendSpawnChanges(set<Spawn*>& spawns) {
+	map<int32, SpawnData> info_changes;
+	map<int32, SpawnData> pos_changes;
+	map<int32, SpawnData> vis_changes;
+
+	int32 info_size = 0;
+	int32 pos_size = 0;
+	int32 vis_size = 0;
+
+	int count = 0;
+	for (const auto& spawn : spawns) {
+		if (count > 40 || (info_size+pos_size+vis_size) > 400)
+		{
+			MakeSpawnChangePacket(info_changes, pos_changes, vis_changes, info_size, pos_size, vis_size);
+
+			for (auto& kv : info_changes) {
+				safe_delete_array(kv.second.data);
+			}
+			info_changes.clear();
+			for (auto& kv : pos_changes) {
+				safe_delete_array(kv.second.data);
+			}
+
+			pos_changes.clear();
+			for (auto& kv : vis_changes) {
+				safe_delete_array(kv.second.data);
+			}
+			vis_changes.clear();
+
+			info_size = 0;
+			pos_size = 0;
+			vis_size = 0;
+		}
+
+		if (!player->WasSentSpawn(spawn->GetID()))
+			continue;
+		int16 index = player->player_spawn_index_map[spawn];
+		/*
+		if (spawn->info_changed) {
+			auto info_change = spawn->spawn_info_changes_ex(GetPlayer(), GetVersion());
+
+			if (info_change) {
+				SpawnData data;
+				data.spawn = spawn;
+				data.data = info_change;
+				data.size = spawn->info_packet_size;
+				info_size += spawn->info_packet_size;
+
+				info_changes[index] = data;
+			}
+		}*/
+
+		if (spawn->position_changed) {
+			auto pos_change = spawn->spawn_pos_changes_ex(GetPlayer(), GetVersion());
+
+			if (pos_change) {
+				SpawnData data;
+				data.spawn = spawn;
+				data.data = pos_change;
+				data.size = spawn->pos_packet_size;
+				pos_size += spawn->pos_packet_size;
+
+				pos_changes[index] = data;
+			}
+			count++;
+		}
+		/*
+		if (spawn->vis_changed) {
+			auto vis_change = spawn->spawn_vis_changes_ex(GetPlayer(), GetVersion());
+
+			if (vis_change) {
+				SpawnData data;
+				data.spawn = spawn;
+				data.data = vis_change;
+				data.size = spawn->vis_packet_size;
+				vis_size += spawn->vis_packet_size;
+
+				vis_changes[index] = data;
+			}
+		}*/
+
+	}
+
+	if (info_size == 0 && pos_size == 0 && vis_size == 0) {
+		return;
+	}
+
+	MakeSpawnChangePacket(info_changes, pos_changes, vis_changes, info_size, pos_size, vis_size);
+
+	for (auto& kv : info_changes) {
+		safe_delete_array(kv.second.data);
+	}
+
+	for (auto& kv : pos_changes) {
+		safe_delete_array(kv.second.data);
+	}
+
+	for (auto& kv : vis_changes) {
+		safe_delete_array(kv.second.data);
+	}
+}
+
+void Client::MakeSpawnChangePacket(map<int32, SpawnData> info_changes, map<int32, SpawnData> pos_changes, map<int32, SpawnData> vis_changes, int32 info_size, int32 pos_size, int32 vis_size)
+{
+	static const int8 oversized = 255;
+	int16 opcode_val = EQOpcodeManager[GetOpcodeVersion(version)]->EmuToEQ(OP_EqUpdateGhostCmd);
+	int32 size = info_size + pos_size + vis_size + 11;
+	size += CheckOverLoadSize(info_size);
+	size += CheckOverLoadSize(pos_size);
+	size += CheckOverLoadSize(vis_size);
+
+	uchar* tmp = new uchar[size];
+	uchar* ptr = tmp;
+
+	memset(tmp, 0, size);
+
+	size -= 4;
+	memcpy(ptr, &size, sizeof(int32));
+	size += 4;
+	ptr += sizeof(int32);
+
+	memcpy(ptr, &oversized, sizeof(int8));
+	ptr += sizeof(int8);
+
+	memcpy(ptr, &opcode_val, sizeof(int16));
+	ptr += sizeof(int16);
+
+	int32 current_time = Timer::GetCurrentTime2();
+	memcpy(ptr, &current_time, sizeof(int32));
+	ptr += sizeof(int32);
+
+	ptr += DoOverLoad(info_size, ptr);
+
+	for (const auto& kv : info_changes) {
+		auto info = kv.second;
+		memcpy(ptr, info.data, info.size);
+		ptr += info.size;
+	}
+
+	ptr += DoOverLoad(pos_size, ptr);
+
+	for (const auto& kv : pos_changes) {
+		auto pos = kv.second;
+		memcpy(ptr, pos.data, pos.size);
+		ptr += pos.size;
+	}
+
+	ptr += DoOverLoad(vis_size, ptr);
+
+	for (const auto& kv : vis_changes) {
+		auto vis = kv.second;
+		memcpy(ptr, vis.data, vis.size);
+		ptr += vis.size;
+	}
+
+	EQ2Packet* packet = new EQ2Packet(OP_ClientCmdMsg, tmp, size);
+
+	//	DumpPacket(packet->pBuffer, packet->size);
+	if (packet) {
+		QueuePacket(packet);
+	}
+
+	delete[] tmp;
 }

+ 2 - 0
EQ2/source/WorldServer/client.h

@@ -397,6 +397,8 @@ public:
 	ServerSpawnPlacementMode GetSpawnPlacementMode() { return spawnPlacementMode; }
 
 	bool HandleNewLogin(int32 account_id, int32 access_code);
+	void SendSpawnChanges(set<Spawn*>& spawns);
+	void MakeSpawnChangePacket(map<int32, SpawnData> info_changes, map<int32, SpawnData> pos_changes, map<int32, SpawnData> vis_changes, int32 info_size, int32 pos_size, int32 vis_size);
 private:
 	void    SavePlayerImages();
 	void	SkillChanged(Skill* skill, int16 previous_value, int16 new_value);

+ 24 - 1
EQ2/source/WorldServer/zoneserver.cpp

@@ -1860,7 +1860,8 @@ void ZoneServer::ProcessDrowning(){
 	}
 }
 
-void ZoneServer::SendSpawnChanges(){	
+void ZoneServer::SendSpawnChanges(){
+	set<Spawn*> spawns_to_send;
 	Spawn* spawn = 0;
 	MutexList<int32>::iterator spawn_iter = changed_spawns.begin();
 	int count = 0;
@@ -1868,6 +1869,11 @@ void ZoneServer::SendSpawnChanges(){
 		spawn = GetSpawnByID(spawn_iter->value);
 		if(spawn && spawn->changed){
 			if(!spawn->IsPlayer() || (spawn->IsPlayer() && (spawn->info_changed || spawn->vis_changed))){
+				if (spawn->position_changed)
+				{
+					spawn->position_changed = false;
+					spawns_to_send.insert(spawn);
+				}
 				SendSpawnChanges(spawn);
 			}
 		}
@@ -1875,6 +1881,23 @@ void ZoneServer::SendSpawnChanges(){
 			changed_spawns.Remove(spawn_iter->value);
 	}
 	changed_spawns.clear();
+
+	for (const auto& spawn : spawns_to_send) {
+		spawn->position_changed = true;
+	}
+
+	vector<Client*>::iterator client_itr;
+	Client* client = 0;
+	MClientList.readlock(__FUNCTION__, __LINE__);
+	for (client_itr = clients.begin(); client_itr != clients.end(); client_itr++) {
+		client = *client_itr;
+		client->SendSpawnChanges(spawns_to_send);
+	}
+	MClientList.releasereadlock(__FUNCTION__, __LINE__);
+
+	for (const auto& spawn : spawns_to_send) {
+		spawn->position_changed = false;
+	}
 }
 
 void ZoneServer::SendPlayerPositionChanges(Player* player){

+ 2 - 2
EQ2/source/WorldServer/zoneserver.h

@@ -95,10 +95,10 @@ class Bot;
 #define MAX_REVIVEPOINT_DISTANCE 1000
 
 /* JA: TODO Turn into R_World Rules */
-#define SEND_SPAWN_DISTANCE 90		/* when spawns appear visually to the client */
+#define SEND_SPAWN_DISTANCE 150		/* when spawns appear visually to the client */
 #define HEAR_SPAWN_DISTANCE	30		/* max distance a client can be from a spawn to 'hear' it */
 #define MAX_CHASE_DISTANCE 80
-#define REMOVE_SPAWN_DISTANCE 100
+#define REMOVE_SPAWN_DISTANCE 180
 
 #define TRACKING_STOP				0
 #define TRACKING_START				1