Selaa lähdekoodia

Fix #320 persist pet name cross zone

Emagi 1 vuosi sitten
vanhempi
commit
c2068993ea

+ 7 - 3
EQ2/source/WorldServer/Commands/Commands.cpp

@@ -8138,9 +8138,13 @@ void Commands::Command_Pet(Client* client, Seperator* sep)
 */ 
 void Commands::Command_PetName(Client* client, Seperator* sep)
 {
-	PrintSep(sep, "COMMAND_PETNAME");
-	LogWrite(MISC__TODO, 1, "Command", "TODO-Command: Pet Name Command");
-	client->Message(CHANNEL_COLOR_YELLOW, "Pets are not yet implemented.");
+	if (sep && sep->arg[0]) {
+		const char* pet_name = sep->argplus[0];
+		client->SetPetName(pet_name);
+	}
+	else {
+			client->GetPlayer()->GetInfoStruct()->set_pet_name("");
+	}	
 }
 
 /* 

+ 26 - 14
EQ2/source/WorldServer/LuaFunctions.cpp

@@ -5272,18 +5272,24 @@ int EQ2Emu_lua_SummonPet(lua_State* state) {
 		spawn->GetZone()->CallSpawnScript(spawn, SPAWN_SCRIPT_SPAWN);
 	}
 
-	// Get a random pet name
-	string random_pet_name;
-	int16 rand_index = MakeRandomInt(0, spawn->GetZone()->pet_names.size() - 1);
-	random_pet_name = spawn->GetZone()->pet_names.at(rand_index);
-	LogWrite(PET__DEBUG, 0, "Pets", "Randomize Pet Name: '%s' (rand: %i)", random_pet_name.c_str(), rand_index);
+
+	std::string petName = std::string("");
+	if(spawn->IsEntity()) {
+		petName = ((Entity*)spawn)->GetInfoStruct()->get_pet_name();
+	}
+	
+	if(petName.size() < 1) {
+		int16 rand_index = MakeRandomInt(0, spawn->GetZone()->pet_names.size() - 1);
+		petName = spawn->GetZone()->pet_names.at(rand_index);
+		LogWrite(PET__DEBUG, 0, "Pets", "Randomize Pet Name: '%s' (rand: %i)", petName.c_str(), rand_index);
+	}
 
 	// If player set various values for the char sheet (pet window)
 	if (spawn->IsPlayer()) {
 		Player* player = (Player*)spawn;
 		
 		player->GetInfoStruct()->set_pet_id(player->GetIDWithPlayerSpawn(pet));
-		player->GetInfoStruct()->set_pet_name(random_pet_name);
+		player->GetInfoStruct()->set_pet_name(petName);
 		player->GetInfoStruct()->set_pet_movement(2);
 		player->GetInfoStruct()->set_pet_behavior(3);
 		player->GetInfoStruct()->set_pet_health_pct(1.0f);
@@ -5293,7 +5299,7 @@ int EQ2Emu_lua_SummonPet(lua_State* state) {
 	}
 
 	// Set the pets name
-	pet->SetName(random_pet_name.c_str());
+	pet->SetName(petName.c_str());
 	// Set the level of the pet to the owners level or max level(if set) if owners level is greater
 	if (max_level > 0)
 		pet->SetLevel(spawn->GetLevel() >= max_level ? max_level : spawn->GetLevel());
@@ -7875,14 +7881,20 @@ int EQ2Emu_lua_SummonDumbFirePet(lua_State* state) {
 		spawn->GetZone()->CallSpawnScript(spawn, SPAWN_SCRIPT_SPAWN);
 	}
 
-	// Get a random pet name
-	string random_pet_name;
-	int16 rand_index = MakeRandomInt(0, spawn->GetZone()->pet_names.size() - 1);
-	random_pet_name = spawn->GetZone()->pet_names.at(rand_index);
-	LogWrite(PET__DEBUG, 0, "Pets", "Randomize Pet Name: '%s' (rand: %i)", random_pet_name.c_str(), rand_index);
-
+	std::string petName = std::string("");
+	if(spawn->IsEntity()) {
+		petName = ((Entity*)spawn)->GetInfoStruct()->get_pet_name();
+	}
+	
+	if(petName.size() < 1) {
+		int16 rand_index = MakeRandomInt(0, spawn->GetZone()->pet_names.size() - 1);
+		petName = spawn->GetZone()->pet_names.at(rand_index);
+		LogWrite(PET__DEBUG, 0, "Pets", "Randomize Pet Name: '%s' (rand: %i)", petName.c_str(), rand_index);
+	}
+	
 	// Set the pets name
-	pet->SetName(random_pet_name.c_str());
+	pet->SetName(petName.c_str());
+		
 	// Set the level of the pet to the owners level
 	pet->SetLevel(spawn->GetLevel());
 	// Set the faction of the pet to the same faction as the owner

+ 19 - 8
EQ2/source/WorldServer/Player.cpp

@@ -441,9 +441,15 @@ PacketStruct* PlayerInfo::serialize2(int16 version){
 		packet->setDataByName("coins_plat", info_struct->get_coin_plat());
 		packet->setDataByName("weight", info_struct->get_weight());
 		packet->setDataByName("max_weight", info_struct->get_max_weight());
-		char pet_name[32];
-		strncpy(pet_name, info_struct->get_pet_name().c_str(), 32);
-		packet->setDataByName("pet_name", pet_name);
+		
+		if(info_struct->get_pet_id() != 0xFFFFFFFF) {
+			char pet_name[32];
+			strncpy(pet_name, info_struct->get_pet_name().c_str(), 32);
+			packet->setDataByName("pet_name", pet_name);
+		}
+		else {
+			packet->setDataByName("pet_name", "No Pet");
+		}
 		packet->setDataByName("status_points", info_struct->get_status_points());
 		if(bind_zone_id > 0){
 			string bind_name = database.GetZoneName(bind_zone_id);
@@ -1220,10 +1226,16 @@ EQ2Packet* PlayerInfo::serializePet(int16 version) {
 
 			packet->setDataByName("spawn_id", info_struct->get_pet_id());
 			packet->setDataByName("spawn_id2", info_struct->get_pet_id());
-			char pet_name[32];
-			strncpy(pet_name, info_struct->get_pet_name().c_str(), 32);
-			packet->setDataByName("name", pet_name);
-			packet->setDataByName("no_pet", pet_name);
+			
+			if(info_struct->get_pet_id() != 0xFFFFFFFF) {
+				char pet_name[32];
+				strncpy(pet_name, info_struct->get_pet_name().c_str(), 32);
+				packet->setDataByName("name", pet_name);
+			}
+			else {
+				packet->setDataByName("name", "No Pet");
+				packet->setDataByName("no_pet", "No Pet");
+			}
 
 			if (version >= 57000) {
 				packet->setDataByName("current_power3", pet->GetPower());
@@ -5851,7 +5863,6 @@ void Player::RemoveAllPassives()
 
 void Player::ResetPetInfo() {
 	GetInfoStruct()->set_pet_id(0xFFFFFFFF);
-	GetInfoStruct()->set_pet_name(std::string("No Pet"));
 	GetInfoStruct()->set_pet_movement(0);
 	GetInfoStruct()->set_pet_behavior(0);
 	GetInfoStruct()->set_pet_health_pct(0.0f);

+ 16 - 5
EQ2/source/WorldServer/WorldDatabase.cpp

@@ -24,6 +24,10 @@ along with EQ2Emulator.  If not, see <http://www.gnu.org/licenses/>.
 #include <iomanip>
 #include <ios>
 #include <assert.h>
+#include <boost/algorithm/string/predicate.hpp>
+#include <boost/algorithm/string.hpp>
+
+
 #include "WorldDatabase.h"
 #include "../common/debug.h"
 #include "../common/packet_dump.h"
@@ -49,6 +53,7 @@ along with EQ2Emulator.  If not, see <http://www.gnu.org/licenses/>.
 #include "SpellProcess.h"
 #include "races.h"
 
+
 extern Classes classes;
 extern Commands commands;
 extern MasterTitlesList master_titles_list;
@@ -1648,7 +1653,6 @@ bool WorldDatabase::LoadCharacterStats(int32 id, int32 account_id, Client* clien
 			info->set_coin_silver(result.GetInt32Str("coin_silver"));
 			info->set_coin_gold(result.GetInt32Str("coin_gold"));
 			info->set_coin_plat(result.GetInt32Str("coin_plat"));
-			info->set_pet_name(result.GetStringStr("pet_name") ? std::string(result.GetStringStr("pet_name")) : std::string(""));
 			const char* bio = result.GetStringStr("biography");
 			if(bio && strlen(bio) > 0)
 				info->set_biography(std::string(bio));
@@ -1710,6 +1714,13 @@ bool WorldDatabase::LoadCharacterStats(int32 id, int32 account_id, Client* clien
 			client->GetPlayer()->SetTotalDissonanceBase(client->GetPlayer()->GetTotalDissonance());
 			
 			client->GetPlayer()->SetCurrentLanguage(result.GetInt32Str("current_language"));
+			
+			std::string petName = result.GetStringStr("pet_name");
+			boost::algorithm::to_lower(petName);
+			
+			if(petName != "no pet") { // some reason we default to "no pet", the code handles an empty string as setting a random pet name when its summoned
+				client->GetPlayer()->GetInfoStruct()->set_pet_name(result.GetStringStr("pet_name"));
+			}
 		}
 
 		return true;
@@ -2453,9 +2464,9 @@ int32 WorldDatabase::SaveCharacter(PacketStruct* create, int32 loginID){
 	return char_id;
 }
 
-int8 WorldDatabase::CheckNameFilter(const char* name) {
+int8 WorldDatabase::CheckNameFilter(const char* name, int8 min_length, int8 max_length) {
 	// the minimum 4 is enforced by the client too
-	if(!name || strlen(name) < 4 || strlen(name) > 15) // Even 20 char length is long...
+	if(!name || strlen(name) < min_length || strlen(name) > max_length) // Even 20 char length is long...
 		return BADNAMELENGTH_REPLY;
 	uchar* checkname = (uchar*)name;
 	for (int32 i = 0; i < strlen(name); i++)
@@ -4125,13 +4136,13 @@ void WorldDatabase::Save(Client* client){
 	if(client->GetCurrentZone())
 		zone_id = client->GetCurrentZone()->GetZoneID();
 	query.AddQueryAsync(client->GetCharacterID(), this, Q_UPDATE, "update characters set current_zone_id=%u, x=%f, y=%f, z=%f, heading=%f, level=%i,instance_id=%i,last_saved=%i, `class`=%i, `tradeskill_level`=%i, `tradeskill_class`=%i, `group_id`=%u, deity = %u, alignment = %u where id = %u", zone_id, player->GetX(), player->GetY(), player->GetZ(), player->GetHeading(), player->GetLevel(), instance_id, client->GetLastSavedTimeStamp(), client->GetPlayer()->GetAdventureClass(), client->GetPlayer()->GetTSLevel(), client->GetPlayer()->GetTradeskillClass(), client->GetPlayer()->GetGroupMemberInfo() ? client->GetPlayer()->GetGroupMemberInfo()->group_id : client->GetRejoinGroupID(), client->GetPlayer()->GetDeity(), client->GetPlayer()->GetInfoStruct()->get_alignment(), client->GetCharacterID());
-	query.AddQueryAsync(client->GetCharacterID(), this, Q_UPDATE, "update character_details set hp=%u, power=%u, str=%i, sta=%i, agi=%i, wis=%i, intel=%i, heat=%i, cold=%i, magic=%i, mental=%i, divine=%i, disease=%i, poison=%i, coin_copper=%u, coin_silver=%u, coin_gold=%u, coin_plat=%u, max_hp = %u, max_power=%u, xp = %u, xp_needed = %u, xp_debt = %f, xp_vitality = %f, tradeskill_xp = %u, tradeskill_xp_needed = %u, tradeskill_xp_vitality = %f, bank_copper = %u, bank_silver = %u, bank_gold = %u, bank_plat = %u, status_points = %u, bind_zone_id=%u, bind_x = %f, bind_y = %f, bind_z = %f, bind_heading = %f, house_zone_id=%u, combat_voice = %i, emote_voice = %i, biography='%s', flags=%u, flags2=%u, last_name='%s', assigned_aa = %i, unassigned_aa = %i, tradeskill_aa = %i, unassigned_tradeskill_aa = %i, prestige_aa = %i, unassigned_prestige_aa = %i, tradeskill_prestige_aa = %i, unassigned_tradeskill_prestige_aa = %i where char_id = %u",
+	query.AddQueryAsync(client->GetCharacterID(), this, Q_UPDATE, "update character_details set hp=%u, power=%u, str=%i, sta=%i, agi=%i, wis=%i, intel=%i, heat=%i, cold=%i, magic=%i, mental=%i, divine=%i, disease=%i, poison=%i, coin_copper=%u, coin_silver=%u, coin_gold=%u, coin_plat=%u, max_hp = %u, max_power=%u, xp = %u, xp_needed = %u, xp_debt = %f, xp_vitality = %f, tradeskill_xp = %u, tradeskill_xp_needed = %u, tradeskill_xp_vitality = %f, bank_copper = %u, bank_silver = %u, bank_gold = %u, bank_plat = %u, status_points = %u, bind_zone_id=%u, bind_x = %f, bind_y = %f, bind_z = %f, bind_heading = %f, house_zone_id=%u, combat_voice = %i, emote_voice = %i, biography='%s', flags=%u, flags2=%u, last_name='%s', assigned_aa = %i, unassigned_aa = %i, tradeskill_aa = %i, unassigned_tradeskill_aa = %i, prestige_aa = %i, unassigned_prestige_aa = %i, tradeskill_prestige_aa = %i, unassigned_tradeskill_prestige_aa = %i, pet_name = '%s' where char_id = %u",
 		player->GetHP(), player->GetPower(), player->GetStrBase(), player->GetStaBase(), player->GetAgiBase(), player->GetWisBase(), player->GetIntBase(), player->GetHeatResistanceBase(), player->GetColdResistanceBase(), player->GetMagicResistanceBase(),
 		player->GetMentalResistanceBase(), player->GetDivineResistanceBase(), player->GetDiseaseResistanceBase(), player->GetPoisonResistanceBase(), player->GetCoinsCopper(), player->GetCoinsSilver(), player->GetCoinsGold(), player->GetCoinsPlat(), player->GetTotalHPBase(), player->GetTotalPowerBase(), player->GetXP(), player->GetNeededXP(), player->GetXPDebt(), player->GetXPVitality(), player->GetTSXP(), player->GetNeededTSXP(), player->GetTSXPVitality(), player->GetBankCoinsCopper(),
 		player->GetBankCoinsSilver(), player->GetBankCoinsGold(), player->GetBankCoinsPlat(), player->GetStatusPoints(), client->GetPlayer()->GetPlayerInfo()->GetBindZoneID(), client->GetPlayer()->GetPlayerInfo()->GetBindZoneX(), client->GetPlayer()->GetPlayerInfo()->GetBindZoneY(), client->GetPlayer()->GetPlayerInfo()->GetBindZoneZ(), client->GetPlayer()->GetPlayerInfo()->GetBindZoneHeading(), client->GetPlayer()->GetPlayerInfo()->GetHouseZoneID(), 
 		client->GetPlayer()->GetCombatVoice(), client->GetPlayer()->GetEmoteVoice(), getSafeEscapeString(client->GetPlayer()->GetBiography().c_str()).c_str(), player->GetFlags(), player->GetFlags2(), client->GetPlayer()->GetLastName(), 
 		client->GetPlayer()->GetAssignedAA(), client->GetPlayer()->GetUnassignedAA(), client->GetPlayer()->GetTradeskillAA(), client->GetPlayer()->GetUnassignedTradeskillAA(), client->GetPlayer()->GetPrestigeAA(), 
-		client->GetPlayer()->GetUnassignedPretigeAA(), client->GetPlayer()->GetTradeskillPrestigeAA(), client->GetPlayer()->GetUnassignedTradeskillPrestigeAA(), client->GetCharacterID());
+		client->GetPlayer()->GetUnassignedPretigeAA(), client->GetPlayer()->GetTradeskillPrestigeAA(), client->GetPlayer()->GetUnassignedTradeskillPrestigeAA(), client->GetPlayer()->GetInfoStruct()->get_pet_name().c_str(), client->GetCharacterID());
 	map<string, int8>::iterator itr;
 	map<string, int8>* friends = player->GetFriends();
 	if(friends && friends->size() > 0){

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

@@ -357,7 +357,7 @@ public:
 
 	int32	GetMaxHotBarID();
 
-	int8	CheckNameFilter(const char* name);
+	int8	CheckNameFilter(const char* name, int8 min_length = 4, int8 max_length = 15);
 	static int32 NextUniqueHotbarID(){ 
 		next_id++;
 		return next_id; 

+ 30 - 2
EQ2/source/WorldServer/client.cpp

@@ -39,6 +39,7 @@ along with EQ2Emulator.  If not, see <http://www.gnu.org/licenses/>.
 #include "Chat/Chat.h"
 #include "SpellProcess.h"
 #include "Zone/ChestTrap.h"
+#include "../../common/GlobalHeaders.h"
 
 //#include "Quests.h"
 
@@ -2039,9 +2040,8 @@ bool Client::HandlePacket(EQApplicationPacket* app) {
 			packet->LoadPacketData(app->pBuffer, app->size);
 
 			string name = packet->getType_EQ2_16BitString_ByName("pet_name").data;
-			if (strlen(name.c_str()) != 0) {
+			if (strlen(name.c_str()) != 0 && SetPetName(name.c_str())) {
 				target->SetName(name.c_str());
-				player->GetInfoStruct()->set_pet_name(name);
 				GetCurrentZone()->SendUpdateTitles(target);
 				change = true;
 			}
@@ -11448,3 +11448,31 @@ void Client::HandleDialogSelectMsg(int32 conversation_id, int32 response_index)
 			CloseDialog(conversation_id);
 	}				
 }
+
+
+bool Client::SetPetName(const char* petName) {
+	int8 result = database.CheckNameFilter(petName,4,31);
+	if (result == BADNAMELENGTH_REPLY) {
+		SimpleMessage(CHANNEL_COLOR_YELLOW, "Name length is invalid, must be greater then 3 characters and less then 16.");
+		return false;
+	}
+	else if (result == NAMEINVALID_REPLY) {
+		SimpleMessage(CHANNEL_COLOR_YELLOW, "Name is invalid, can only contain letters.");
+		return false;
+	}
+	else if (result == NAMETAKEN_REPLY) {
+		SimpleMessage(CHANNEL_COLOR_YELLOW, "Name is already taken, please choose another.");
+		return false;
+	}
+	else if (result == NAMEFILTER_REPLY) {
+		SimpleMessage(CHANNEL_COLOR_YELLOW, "Name failed the filter check.");
+		return false;
+	}
+	else if (result == UNKNOWNERROR_REPLY) {
+		SimpleMessage(CHANNEL_COLOR_YELLOW, "Unknown error while checking the name.");
+		return false;
+	}
+
+	GetPlayer()->GetInfoStruct()->set_pet_name(petName);
+	return true;
+}

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

@@ -575,6 +575,7 @@ public:
 	int32	GetPlayerPOVGhostSpawnID() { return pov_ghost_spawn_id; }
 	
 	void	HandleDialogSelectMsg(int32 conversation_id, int32 response_index);
+	bool	SetPetName(const char* name);
 private:
 	void    SavePlayerImages();
 	void	SkillChanged(Skill* skill, int16 previous_value, int16 new_value);