Browse Source

Support for IsCollector visual state (turn in quest) wip, movement location crash fix

Image 2 years ago
parent
commit
1609cb9797

+ 1 - 0
EQ2/source/WorldServer/NPC.cpp

@@ -57,6 +57,7 @@ NPC::NPC(NPC* old_npc){
 		}
 		else
 			size = old_npc->size;
+		SetCollector(old_npc->IsCollector());
 		SetMerchantID(old_npc->GetMerchantID());
 		SetMerchantType(old_npc->GetMerchantType());
 		SetMerchantLevelRange(old_npc->GetMerchantMinLevel(), old_npc->GetMerchantMaxLevel());

+ 1 - 0
EQ2/source/WorldServer/Object.cpp

@@ -61,6 +61,7 @@ void Object::HandleUse(Client* client, string command){
 
 Object*	Object::Copy(){
 	Object* new_spawn = new Object();
+	new_spawn->SetCollector(IsCollector());
 	new_spawn->SetMerchantID(merchant_id);
 	new_spawn->SetMerchantType(merchant_type);
 	new_spawn->SetMerchantLevelRange(GetMerchantMinLevel(), GetMerchantMaxLevel());

+ 0 - 1
EQ2/source/WorldServer/Player.cpp

@@ -5004,7 +5004,6 @@ int8 Player::CheckQuestFlag(Spawn* spawn){
 		LogWrite(PLAYER__ERROR, 0, "Player", "CheckQuestFlag() called with an invalid spawn");
 		return ret;
 	}
-
 	if(spawn->HasProvidedQuests()){
 		vector<int32>* quests = spawn->GetProvidedQuests();
 		Quest* quest = 0;

+ 1 - 1
EQ2/source/WorldServer/Rules/Rules.cpp

@@ -313,7 +313,7 @@ void RuleManager::Init()
 	/* ZONE TIMERS */
 	RULE_INIT(R_Zone, RegenTimer, "6000");
 	RULE_INIT(R_Zone, ClientSaveTimer, "60000");
-	RULE_INIT(R_Zone, DefaultZoneShutdownTimer, "300000");
+	RULE_INIT(R_Zone, ShutdownDelayTimer, "120000");
 	RULE_INIT(R_Zone, WeatherTimer, "60000");						// default: 1 minute
 	RULE_INIT(R_Zone, SpawnDeleteTimer, "30000");					// default: 30 seconds, how long a spawn pointer is held onto after being removed from the world before deleting it
 

+ 1 - 1
EQ2/source/WorldServer/Rules/Rules.h

@@ -188,7 +188,7 @@ enum RuleType {
 	/* ZONE TIMERS */
 	RegenTimer,
 	ClientSaveTimer,
-	DefaultZoneShutdownTimer,
+	ShutdownDelayTimer,
 	WeatherTimer,
 	SpawnDeleteTimer,
 

+ 1 - 0
EQ2/source/WorldServer/Sign.cpp

@@ -120,6 +120,7 @@ Sign* Sign::Copy(){
 	}
 	else
 		new_spawn->size = size;
+	new_spawn->SetCollector(IsCollector());
 	new_spawn->SetMerchantID(merchant_id);
 	new_spawn->SetMerchantType(merchant_type);
 	new_spawn->SetMerchantLevelRange(GetMerchantMinLevel(), GetMerchantMaxLevel());

+ 45 - 3
EQ2/source/WorldServer/Spawn.cpp

@@ -132,6 +132,7 @@ Spawn::Spawn(){
 	is_omitted_by_db_flag = false;
 	loot_tier = 0;
 	deleted_spawn = false;
+	is_collector = false;
 }
 
 Spawn::~Spawn(){
@@ -282,7 +283,6 @@ void Spawn::InitializeVisPacketData(Player* player, PacketStruct* vis_packet) {
 		}
 
 	}
-
 	if (appearance.targetable == 1 || appearance.show_level == 1 || appearance.display_name == 1) {
 		if (!IsGroundSpawn()) {
 			int8 arrow_color = ARROW_COLOR_WHITE;
@@ -2367,7 +2367,10 @@ void Spawn::InitializeInfoPacketData(Player* spawn, PacketStruct* packet) {
 		packet->setDataByName("action_state", GetTempActionState());
 	else
 		packet->setDataByName("action_state", appearance.action_state);
-	if (GetTempVisualState() >= 0)
+	
+	if(IsCollector() && spawn->GetCollectionList()->HasCollectionsToHandIn())
+		packet->setDataByName("visual_state", 6674);
+	else if (GetTempVisualState() >= 0)
 		packet->setDataByName("visual_state", GetTempVisualState());
 	else
 		packet->setDataByName("visual_state", appearance.visual_state);
@@ -2824,7 +2827,30 @@ void Spawn::ProcessMovement(bool isSpawnListLocked){
 				
 					SetSpeed(speed);
 			}
-			MovementLocation* loc = GetCurrentRunningLocation();
+			MovementLocation tmpLoc;
+			MovementLocation* loc = 0;
+			if(MMovementLocations) {
+				MMovementLocations->readlock(__FUNCTION__, __LINE__);
+				
+				if(movement_locations && movement_locations->size() > 0){
+					loc = movement_locations->front();
+					if(loc) {
+						tmpLoc.attackable = loc->attackable;
+						tmpLoc.gridid = loc->gridid;
+						tmpLoc.lua_function = string(loc->lua_function);
+						tmpLoc.mapped = loc->mapped;
+						tmpLoc.reset_hp_on_runback = loc->reset_hp_on_runback;
+						tmpLoc.speed = loc->speed;
+						tmpLoc.stage = loc->stage;
+						tmpLoc.x = loc->x;
+						tmpLoc.y = loc->y;
+						tmpLoc.z = loc->z;
+						loc = &tmpLoc;
+					}
+				}
+				MMovementLocations->releasereadlock(__FUNCTION__, __LINE__);
+			}
+
 			float dist = GetDistance(followTarget, true);
 			if ((!EngagedInCombat() && m_followDistance > 0 && dist <= m_followDistance) || 
 				(dist <= rule_manager.GetGlobalRule(R_Combat, MaxCombatRange)->GetFloat())) {
@@ -3131,8 +3157,10 @@ void Spawn::AddRunningLocation(float x, float y, float z, float speed, float dis
 		x = x - (GetX() - x)*distance_away/distance;
 		z = z - (GetZ() - z)*distance_away/distance;
 	}
+	
 	if(!movement_locations){
 		movement_locations = new deque<MovementLocation*>();
+		safe_delete(MMovementLocations);
 		MMovementLocations = new Mutex();
 	}
 	MovementLocation* data = new MovementLocation;
@@ -3206,11 +3234,25 @@ void Spawn::NewWaypointChange(MovementLocation* data){
 bool Spawn::CalculateChange(){
 	bool remove_needed = false;
 	MovementLocation* data = 0;
+	MovementLocation tmpLoc;
 	if(movement_locations && MMovementLocations){
 		MMovementLocations->readlock(__FUNCTION__, __LINE__);
 		if(movement_locations->size() > 0){
 			// Target location
 			data = movement_locations->front();
+			if(data) {
+				tmpLoc.attackable = data->attackable;
+				tmpLoc.gridid = data->gridid;
+				tmpLoc.lua_function = string(data->lua_function);
+				tmpLoc.mapped = data->mapped;
+				tmpLoc.reset_hp_on_runback = data->reset_hp_on_runback;
+				tmpLoc.speed = data->speed;
+				tmpLoc.stage = data->stage;
+				tmpLoc.x = data->x;
+				tmpLoc.y = data->y;
+				tmpLoc.z = data->z;
+				data = &tmpLoc;
+			}
 			// If no target or we are at the target location need to remove this point
 			if(!data || (data->x == GetX() && data->y == GetY() && data->z == GetZ()))
 				remove_needed = true;

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

@@ -1006,6 +1006,8 @@ public:
 	int32	GetMerchantID();
 	void	SetMerchantType(int8 val);
 	int8	GetMerchantType();
+	void	SetCollector(bool is_it) { is_collector = is_it; }
+	bool	IsCollector() { return is_collector; }
 	void	SetMerchantLevelRange(int32 minLvl = 0, int32 maxLvl = 0);
 	bool	IsClientInMerchantLevelRange(Client* ent, bool sendMessageIfDenied = true);
 	int32	GetMerchantMinLevel();
@@ -1251,7 +1253,7 @@ public:
 	vector<Spawn*> GetPassengersOnRail();
 
 	void SetAppearancePosition(float x, float y, float z);
-	
+
 	void SetOmittedByDBFlag(bool val) { is_omitted_by_db_flag = val; }
 	bool IsOmittedByDBFlag() { return is_omitted_by_db_flag; }
 
@@ -1370,6 +1372,7 @@ private:
 
 	bool deleted_spawn;
 	Mutex m_GridMutex;
+	bool is_collector;
 };
 
 #endif

+ 1 - 0
EQ2/source/WorldServer/Widget.cpp

@@ -128,6 +128,7 @@ Widget*	Widget::Copy(){
 	}
 	else
 		new_spawn->size = size;
+	new_spawn->SetCollector(IsCollector());
 	new_spawn->SetMerchantID(merchant_id);
 	new_spawn->SetMerchantType(merchant_type);
 	new_spawn->SetMerchantLevelRange(GetMerchantMinLevel(), GetMerchantMaxLevel());

+ 8 - 0
EQ2/source/WorldServer/WorldDatabase.cpp

@@ -1017,6 +1017,10 @@ void WorldDatabase::LoadNPCs(ZoneServer* zone){
 		npc->appearance.activity_status = atoi(row[34]);
 		npc->faction_id = atoul(row[35]);
 		if(row[36]){
+			std::string sub_title = std::string(row[36]);
+			if(strncmp(row[36],"<Collector>", 11) == 0) {
+				npc->SetCollector(true);
+			}
 			if(strlen(row[36]) < sizeof(npc->appearance.sub_title))
 				strcpy(npc->appearance.sub_title, row[36]);
 			else
@@ -6701,6 +6705,10 @@ bool WorldDatabase::LoadNPC(ZoneServer* zone, int32 spawn_id) {
 		npc->appearance.activity_status = result.GetInt16(34);
 		npc->faction_id = result.GetInt32(35);
 		if(!result.IsNull(36)){
+			std::string sub_title = std::string(result.GetString(36));
+			if(sub_title.find("Collector") != std::string::npos) {
+				npc->SetCollector(true);
+			}
 			if(strlen(result.GetString(36)) < sizeof(npc->appearance.sub_title))
 				strcpy(npc->appearance.sub_title, result.GetString(36));
 			else

+ 19 - 4
EQ2/source/WorldServer/zoneserver.cpp

@@ -120,10 +120,11 @@ int32 MinInstanceID = 1000;
 
 // JA: Moved most default values to Rules and risky initializers to ZoneServer::Init() - 2012.12.07
 ZoneServer::ZoneServer(const char* name, bool incoming_clients) {
+	incoming_clients = 0;
+	
 	if(incoming_clients)
 		IncrementIncomingClients();
-	else
-		incoming_clients = 0;
+	
 	MIncomingClients.SetName("ZoneServer::MIncomingClients");
 
 	depop_zone = false;
@@ -3173,7 +3174,11 @@ void ZoneServer::RemoveClient(Client* client)
 		database.ToggleCharacterOnline(client, 0);
 		
 		client->GetPlayer()->DeleteSpellEffects(true);
-
+		
+		if(client->getConnection()) {
+			client->getConnection()->ResetSessionAttempts();
+			client->getConnection()->SetState(EQStreamState::WAIT_CLOSE);
+		}
 		RemoveSpawn(client->GetPlayer(), false, true, true, true, true);
 		connected_clients.Remove(client, true, DisconnectClientTimer); // changed from a hardcoded 30000 (30 sec) to the DisconnectClientTimer rule
 	}
@@ -3203,7 +3208,13 @@ void ZoneServer::ClientProcess()
 		{
 			if(incoming_clients && !shutdownDelayTimer.Enabled()) {
 				LogWrite(ZONE__INFO, 0, "Zone", "Incoming clients (%u) expected for %s, delaying shutdown timer...", incoming_clients, GetZoneName());
-				shutdownDelayTimer.Start(120000, true);
+				int32 timerDelay = rule_manager.GetGlobalRule(R_Zone, ShutdownDelayTimer)->GetInt32();
+
+				if(timerDelay < 10) {
+					LogWrite(ZONE__INFO, 0, "Zone", "Overriding %s shutdown delay timer as other clients are incoming, value %u too short, setting to 10...", GetZoneName(), timerDelay);
+					timerDelay = 10;
+				}
+				shutdownDelayTimer.Start(timerDelay, true);
 			}
 			else if(!incoming_clients || shutdownDelayCheck) {
 				LogWrite(ZONE__INFO, 0, "Zone", "Starting zone shutdown timer for %s...", GetZoneName());
@@ -3228,6 +3239,10 @@ void ZoneServer::ClientProcess()
 #endif
 			if(zoneShuttingDown || !client->Process(true))
 			{
+				if(client->getConnection() && client->getConnection()->HasSessionAttempts()) {
+					printf("Client has an attempt to reconnect..\n");
+					continue;
+				}
 				if(!zoneShuttingDown && !client->IsZoning())
 				{
 					LogWrite(ZONE__DEBUG, 0, "Zone", "Client is disconnecting in %s (camping = %s)", __FUNCTION__, (client->GetPlayer()->GetActivityStatus() & ACTIVITY_STATUS_CAMPING) == 0 ? "false" : "true");

+ 15 - 2
EQ2/source/common/EQStream.cpp

@@ -107,6 +107,7 @@ void EQStream::init(bool resetSession) {
 	retransmittimer = Timer::GetCurrentTime2();
 	retransmittimeout = 500 * RETRANSMIT_TIMEOUT_MULT;
 
+	reconnectAttempt = 0;
 	if (uint16(SequencedBase + SequencedQueue.size()) != NextOutSeq) {
 		LogWrite(PACKET__DEBUG, 9, "Packet",  "init Invalid Sequenced queue: BS %u + SQ %u != NOS %u", SequencedBase, SequencedQueue.size(), NextOutSeq);
 	}
@@ -258,10 +259,13 @@ void EQStream::ProcessPacket(EQProtocolPacket *p, EQProtocolPacket* lastp)
 				while(processed<p->size) {
 					if ((subpacket_length=(unsigned char)*(p->pBuffer+processed))==0xff) {
 						subpacket_length = ntohs(*(uint16*)(p->pBuffer + processed + 1));
+						//printf("OP_Combined subpacket_length %u\n",subpacket_length);
 						offset = 3;
 					}
 					else
 						offset = 1;
+					
+					//printf("OP_Combined processed %u p->size %u subpacket length %u count %i\n",processed, p->size, subpacket_length, count);
 					count++;
 #ifdef LE_DEBUG
 					printf( "OP_Combined Packet %i (%u) (%u):\n", count, subpacket_length, processed);
@@ -538,6 +542,10 @@ void EQStream::ProcessPacket(EQProtocolPacket *p, EQProtocolPacket* lastp)
 				}
 
 				sessionAttempts++;
+				if(GetState() == WAIT_CLOSE) {
+					printf("WAIT_CLOSE Reconnect with streamactive %u, sessionAttempts %u\n", streamactive, sessionAttempts);
+					reconnectAttempt++;
+				}
 				init(GetState() != ESTABLISHED);
 				OutboundQueueClear();
 				SessionRequest *Request=(SessionRequest *)p->pBuffer;
@@ -653,6 +661,8 @@ void EQStream::ProcessPacket(EQProtocolPacket *p, EQProtocolPacket* lastp)
 				NonSequencedPush(new EQProtocolPacket(OP_SessionStatResponse,p->pBuffer,p->size));
 				if(!crypto->isEncrypted())
 					SendKeyRequest();
+				else
+					SendSessionResponse();
 			}
 			break;
 			case OP_SessionStatResponse: {
@@ -1465,7 +1475,7 @@ char temp[15];
 	if (length>=2) {
 		DumpPacket(buffer, length);
 		p=new EQProtocolPacket(buffer[1],&buffer[2],length-2);
-
+		//printf("Read packet: opcode %i length %u, expected-length: %u\n",buffer[1], length, p->size);
 		uint32 ip=from->sin_addr.s_addr;
 		sprintf(temp,"%d.%d.%d.%d:%d",
 			*(unsigned char *)&ip,
@@ -1513,7 +1523,7 @@ void EQStream::SendSessionRequest()
 void EQStream::SendDisconnect(bool setstate)
 {
 	try{
-		if(GetState() != ESTABLISHED)
+		if(GetState() != ESTABLISHED && GetState() != WAIT_CLOSE)
 			return;
 		
 		EQProtocolPacket *out=new EQProtocolPacket(OP_SessionDisconnect,NULL,sizeof(uint32)+sizeof(int16));
@@ -1639,9 +1649,12 @@ DumpPacket(buffer, length);
 		DumpPacket(buffer, newlength);
 #endif
 		uint16 opcode=ntohs(*(const uint16 *)newbuffer);
+		//printf("Read packet: opcode %i newlength %u, newbuffer2len: %u, newbuffer3len: %u\n",opcode, newlength, newbuffer[2], newbuffer[3]);
 		if(opcode > 0 && opcode <= OP_OutOfSession)
 		{
 			EQProtocolPacket p(newbuffer,newlength);
+			if(opcode == 3)
+				DumpPacket(newbuffer, newlength);
 			ProcessPacket(&p);
 		}
 		else

+ 5 - 2
EQ2/source/common/EQStream.h

@@ -46,6 +46,7 @@ using namespace std;
 
 typedef enum {
 	ESTABLISHED,
+	WAIT_CLOSE,
 	CLOSING,
 	DISCONNECTING,
 	CLOSED
@@ -156,6 +157,7 @@ class EQStream {
 		//uint32 buffer_len;
 
 		uint16 sessionAttempts;
+		uint16 reconnectAttempt;
 		bool streamactive;
 
 		uint32 Session, Key;
@@ -239,7 +241,8 @@ class EQStream {
 		int16	client_version;
 		int16	GetClientVersion(){ return client_version; }
 		void	SetClientVersion(int16 version){ client_version = version; }
-
+		void	ResetSessionAttempts() { reconnectAttempt = 0; }
+		bool	HasSessionAttempts() { return reconnectAttempt>0; }
 		EQStream() { init(); remote_ip = 0; remote_port = 0; State = CLOSED; StreamType = UnknownStream; compressed = true; 
 		encoded = false; app_opcode_size = 2;}
 		EQStream(sockaddr_in addr);
@@ -351,7 +354,7 @@ class EQStream {
 		static EQProtocolPacket *Read(int eq_fd, sockaddr_in *from);
 
 		void Close() { SendDisconnect(); }
-		bool CheckActive() { return GetState()==ESTABLISHED; }
+		bool CheckActive() { return (GetState()==ESTABLISHED); }
 		bool CheckClosed() { return GetState()==CLOSED; }
 		void SetOpcodeSize(uint8 s) { app_opcode_size = s; }
 		void SetStreamType(EQStreamType t);