diff --git a/Source/Framework/Constants/BattlePetConst.cs b/Source/Framework/Constants/BattlePetConst.cs index cde907569..0761c34bb 100644 --- a/Source/Framework/Constants/BattlePetConst.cs +++ b/Source/Framework/Constants/BattlePetConst.cs @@ -14,31 +14,24 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ +using System; namespace Framework.Constants { + [Flags] + public enum BattlePetError + { + CantHaveMorePetsOfThatType = 3, // You can't have any more pets of that type. + CantHaveMorePets = 4, // You can't have any more pets. + TooHighLevelToUncage = 7, // This pet is too high level for you to uncage. + } + public enum FlagsControlType { Apply = 1, Remove = 2 } - public enum BattlePetError - { - CantHaveMorePetsOfThatType = 3, - CantHaveMorePets = 4, - TooHighLevelToUncage = 7, - - // TODO: find correct values if possible and needed (also wrong order) - DuplicateConvertedPet, - NeedToUnlock, - BadParam, - LockedPetAlreadyExists, - Ok, - Uncapturable, - CantInvalidCharacterGuid - } - // taken from BattlePetState.db2 - it seems to store some initial values for battle pets // there are only values used in BattlePetSpeciesState.db2 and BattlePetBreedState.db2 // TODO: expand this enum if needed diff --git a/Source/Framework/Database/Databases/LoginDatabase.cs b/Source/Framework/Database/Databases/LoginDatabase.cs index 1cb306469..7dfe5d6fd 100644 --- a/Source/Framework/Database/Databases/LoginDatabase.cs +++ b/Source/Framework/Database/Databases/LoginDatabase.cs @@ -151,10 +151,10 @@ namespace Framework.Database PrepareStatement(LoginStatements.REP_ACCOUNT_TOYS, "REPLACE INTO battlenet_account_toys (accountId, itemId, isFavourite, hasFanfare) VALUES (?, ?, ?, ?)"); // Battle Pets - PrepareStatement(LoginStatements.SEL_BATTLE_PETS, "SELECT bp.guid, bp.species, bp.breed, bp.displayId, bp.level, bp.exp, bp.health, bp.quality, bp.flags, bp.name, dn.genitive, dn.dative, dn.accusative, dn.instrumental, dn.prepositional FROM battle_pets bp LEFT JOIN battle_pet_declinedname dn ON bp.guid = dn.guid WHERE bp.battlenetAccountId = ?"); - PrepareStatement(LoginStatements.INS_BATTLE_PETS, "INSERT INTO battle_pets (guid, battlenetAccountId, species, breed, displayId, level, exp, health, quality, flags, name) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); + PrepareStatement(LoginStatements.SEL_BATTLE_PETS, "SELECT bp.guid, bp.species, bp.breed, bp.displayId, bp.level, bp.exp, bp.health, bp.quality, bp.flags, bp.name, bp.nameTimestamp, dn.genitive, dn.dative, dn.accusative, dn.instrumental, dn.prepositional FROM battle_pets bp LEFT JOIN battle_pet_declinedname dn ON bp.guid = dn.guid WHERE bp.battlenetAccountId = ?"); + PrepareStatement(LoginStatements.INS_BATTLE_PETS, "INSERT INTO battle_pets (guid, battlenetAccountId, species, breed, displayId, level, exp, health, quality, flags, name, nameTimestamp) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); PrepareStatement(LoginStatements.DEL_BATTLE_PETS, "DELETE FROM battle_pets WHERE battlenetAccountId = ? AND guid = ?"); - PrepareStatement(LoginStatements.UPD_BATTLE_PETS, "UPDATE battle_pets SET level = ?, exp = ?, health = ?, quality = ?, flags = ?, name = ? WHERE battlenetAccountId = ? AND guid = ?"); + PrepareStatement(LoginStatements.UPD_BATTLE_PETS, "UPDATE battle_pets SET level = ?, exp = ?, health = ?, quality = ?, flags = ?, name = ?, nameTimestamp = ? WHERE battlenetAccountId = ? AND guid = ?"); PrepareStatement(LoginStatements.SEL_BATTLE_PET_SLOTS, "SELECT id, battlePetGuid, locked FROM battle_pet_slots WHERE battlenetAccountId = ?"); PrepareStatement(LoginStatements.INS_BATTLE_PET_SLOTS, "INSERT INTO battle_pet_slots (id, battlenetAccountId, battlePetGuid, locked) VALUES (?, ?, ?, ?)"); PrepareStatement(LoginStatements.DEL_BATTLE_PET_SLOTS, "DELETE FROM battle_pet_slots WHERE battlenetAccountId = ?"); diff --git a/Source/Game/BattlePets/BattlePetManager.cs b/Source/Game/BattlePets/BattlePetManager.cs index f01feb5aa..485baa5f3 100644 --- a/Source/Game/BattlePets/BattlePetManager.cs +++ b/Source/Game/BattlePets/BattlePetManager.cs @@ -195,13 +195,14 @@ namespace Game.BattlePets pet.PacketInfo.Quality = petsResult.Read(7); pet.PacketInfo.Flags = petsResult.Read(8); pet.PacketInfo.Name = petsResult.Read(9); + pet.NameTimestamp = petsResult.Read(10); pet.PacketInfo.CreatureID = speciesEntry.CreatureID; - if (!petsResult.IsNull(10)) + if (!petsResult.IsNull(11)) { pet.DeclinedName = new(); for (byte i = 0; i < SharedConst.MaxDeclinedNameCases; ++i) - pet.DeclinedName.name[i] = petsResult.Read(10 + i); + pet.DeclinedName.name[i] = petsResult.Read(11 + i); } pet.SaveInfo = BattlePetSaveInfo.Unchanged; @@ -248,6 +249,7 @@ namespace Game.BattlePets stmt.AddValue(8, pair.Value.PacketInfo.Quality); stmt.AddValue(9, pair.Value.PacketInfo.Flags); stmt.AddValue(10, pair.Value.PacketInfo.Name); + stmt.AddValue(11, pair.Value.NameTimestamp); trans.Append(stmt); if (pair.Value.DeclinedName != null) @@ -272,8 +274,9 @@ namespace Game.BattlePets stmt.AddValue(3, pair.Value.PacketInfo.Quality); stmt.AddValue(4, pair.Value.PacketInfo.Flags); stmt.AddValue(5, pair.Value.PacketInfo.Name); - stmt.AddValue(6, _owner.GetBattlenetAccountId()); - stmt.AddValue(7, pair.Key); + stmt.AddValue(6, pair.Value.NameTimestamp); + stmt.AddValue(7, _owner.GetBattlenetAccountId()); + stmt.AddValue(8, pair.Key); trans.Append(stmt); stmt = DB.Login.GetPreparedStatement(LoginStatements.DEL_BATTLE_PET_DECLINED_NAME); @@ -349,6 +352,7 @@ namespace Game.BattlePets pet.PacketInfo.Name = ""; pet.CalculateStats(); pet.PacketInfo.Health = pet.PacketInfo.MaxHealth; + pet.NameTimestamp = 0; pet.SaveInfo = BattlePetSaveInfo.New; _pets[pet.PacketInfo.Guid.GetCounter()] = pet; @@ -395,6 +399,7 @@ namespace Game.BattlePets return; pet.PacketInfo.Name = name; + pet.NameTimestamp = !pet.PacketInfo.Name.IsEmpty() ? GameTime.GetGameTime() : 0; pet.DeclinedName = new DeclinedName(); if (declinedName != null) @@ -402,6 +407,14 @@ namespace Game.BattlePets if (pet.SaveInfo != BattlePetSaveInfo.New) pet.SaveInfo = BattlePetSaveInfo.Changed; + + // Update the timestamp if the battle pet is summoned + Player player = _owner.GetPlayer(); + Creature summonedBattlePet = ObjectAccessor.GetCreatureOrPetOrVehicle(player, player.GetCritterGUID()); + if (summonedBattlePet != null) + if (player.GetSummonedBattlePetGUID() == summonedBattlePet.GetBattlePetCompanionGUID()) + if (summonedBattlePet.GetBattlePetCompanionGUID() == guid) + summonedBattlePet.SetBattlePetCompanionNameTimestamp((uint)pet.NameTimestamp); } bool IsPetInSlot(ObjectGuid guid) @@ -533,20 +546,21 @@ namespace Game.BattlePets return; // TODO: set proper CreatureID for spell DEFAULT_SUMMON_BATTLE_PET_SPELL (default EffectMiscValueA is 40721 - Murkimus the Gladiator) - _owner.GetPlayer().SetSummonedBattlePetGUID(guid); - _owner.GetPlayer().SetCurrentBattlePetBreedQuality(pet.PacketInfo.Quality); - _owner.GetPlayer().CastSpell(_owner.GetPlayer(), speciesEntry.SummonSpellID != 0 ? speciesEntry.SummonSpellID : SharedConst.DefaultSummonBattlePetSpell); + Player player = _owner.GetPlayer(); + player.SetSummonedBattlePetGUID(guid); + player.SetCurrentBattlePetBreedQuality(pet.PacketInfo.Quality); + player.CastSpell(_owner.GetPlayer(), speciesEntry.SummonSpellID != 0 ? speciesEntry.SummonSpellID : SharedConst.DefaultSummonBattlePetSpell); } public void DismissPet() { - Player ownerPlayer = _owner.GetPlayer(); - Creature pet = ObjectAccessor.GetCreatureOrPetOrVehicle(ownerPlayer, ownerPlayer.GetCritterGUID()); - if (pet && ownerPlayer.m_activePlayerData.SummonedBattlePetGUID == pet.GetBattlePetCompanionGUID()) + Player player = _owner.GetPlayer(); + Creature pet = ObjectAccessor.GetCreatureOrPetOrVehicle(player, player.GetCritterGUID()); + if (pet && player.GetSummonedBattlePetGUID() == pet.GetBattlePetCompanionGUID()) { pet.DespawnOrUnsummon(); - ownerPlayer.SetSummonedBattlePetGUID(ObjectGuid.Empty); - ownerPlayer.SetCurrentBattlePetBreedQuality((byte)BattlePetBreedQuality.Poor); + player.SetSummonedBattlePetGUID(ObjectGuid.Empty); + player.SetCurrentBattlePetBreedQuality((byte)BattlePetBreedQuality.Poor); } } @@ -669,6 +683,7 @@ namespace Game.BattlePets } public BattlePetStruct PacketInfo; + public long NameTimestamp; public DeclinedName DeclinedName; public BattlePetSaveInfo SaveInfo; } diff --git a/Source/Game/Entities/Player/Player.cs b/Source/Game/Entities/Player/Player.cs index 068a5c18e..54809158b 100644 --- a/Source/Game/Entities/Player/Player.cs +++ b/Source/Game/Entities/Player/Player.cs @@ -7444,6 +7444,7 @@ namespace Game.Entities } public void ClearSelfResSpell() { ClearDynamicUpdateFieldValues(m_values.ModifyValue(m_activePlayerData).ModifyValue(m_activePlayerData.SelfResSpells)); } + public ObjectGuid GetSummonedBattlePetGUID() { return m_activePlayerData.SummonedBattlePetGUID; } public void SetSummonedBattlePetGUID(ObjectGuid guid) { SetUpdateFieldValue(m_values.ModifyValue(m_activePlayerData).ModifyValue(m_activePlayerData.SummonedBattlePetGUID), guid); } public void AddTrackCreatureFlag(uint flags) { SetUpdateFieldFlagValue(m_values.ModifyValue(m_activePlayerData).ModifyValue(m_activePlayerData.TrackCreatureMask), flags); } diff --git a/Source/Game/Entities/Unit/Unit.Pets.cs b/Source/Game/Entities/Unit/Unit.Pets.cs index b1cd34625..7e9dd5d80 100644 --- a/Source/Game/Entities/Unit/Unit.Pets.cs +++ b/Source/Game/Entities/Unit/Unit.Pets.cs @@ -147,10 +147,11 @@ namespace Game.Entities Player thisPlayer = ToPlayer(); if (thisPlayer != null) { - var pet = thisPlayer.GetSession().GetBattlePetMgr().GetPet(thisPlayer.m_activePlayerData.SummonedBattlePetGUID); + var pet = thisPlayer.GetSession().GetBattlePetMgr().GetPet(thisPlayer.GetSummonedBattlePetGUID()); if (pet != null) { - minion.SetBattlePetCompanionGUID(thisPlayer.m_activePlayerData.SummonedBattlePetGUID); + minion.SetBattlePetCompanionGUID(thisPlayer.GetSummonedBattlePetGUID()); + minion.SetBattlePetCompanionNameTimestamp((uint)pet.NameTimestamp); minion.SetWildBattlePetLevel(pet.PacketInfo.Level); } } diff --git a/Source/Game/Entities/Unit/Unit.cs b/Source/Game/Entities/Unit/Unit.cs index 7a83e64d4..0f256f666 100644 --- a/Source/Game/Entities/Unit/Unit.cs +++ b/Source/Game/Entities/Unit/Unit.cs @@ -1906,8 +1906,11 @@ namespace Game.Entities return IsCharmed() ? GetCharmer() : GetOwner(); } - public void SetWildBattlePetLevel(uint wildBattlePetLevel) { SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.WildBattlePetLevel), wildBattlePetLevel); } + public uint GetBattlePetCompanionNameTimestamp() { return m_unitData.BattlePetCompanionNameTimestamp; } + public void SetBattlePetCompanionNameTimestamp(uint timestamp) { SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.BattlePetCompanionNameTimestamp), timestamp); } + public uint GetWildBattlePetLevel() { return m_unitData.WildBattlePetLevel; } + public void SetWildBattlePetLevel(uint wildBattlePetLevel) { SetUpdateFieldValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.WildBattlePetLevel), wildBattlePetLevel); } public bool HasUnitFlag(UnitFlags flags) { return (m_unitData.Flags & (uint)flags) != 0; } public void AddUnitFlag(UnitFlags flags) { SetUpdateFieldFlagValue(m_values.ModifyValue(m_unitData).ModifyValue(m_unitData.Flags), (uint)flags); } diff --git a/Source/Game/Handlers/BattlePetHandler.cs b/Source/Game/Handlers/BattlePetHandler.cs index 45f9aa78f..23ca2e7c5 100644 --- a/Source/Game/Handlers/BattlePetHandler.cs +++ b/Source/Game/Handlers/BattlePetHandler.cs @@ -17,6 +17,7 @@ using Framework.Constants; using Game.BattlePets; +using Game.Entities; using Game.Networking; using Game.Networking.Packets; @@ -54,7 +55,48 @@ namespace Game [WorldPacketHandler(ClientOpcodes.BattlePetModifyName)] void HandleBattlePetModifyName(BattlePetModifyName battlePetModifyName) { - GetBattlePetMgr().ModifyName(battlePetModifyName.PetGuid, battlePetModifyName.Name, battlePetModifyName.DeclinedName.Value); + GetBattlePetMgr().ModifyName(battlePetModifyName.PetGuid, battlePetModifyName.Name, battlePetModifyName.DeclinedNames.Value); + } + + [WorldPacketHandler(ClientOpcodes.QueryBattlePetName)] + void HandleQueryBattlePetName(QueryBattlePetName queryBattlePetName) + { + QueryBattlePetNameResponse response = new(); + response.BattlePetID = queryBattlePetName.BattlePetID; + + Creature summonedBattlePet = ObjectAccessor.GetCreatureOrPetOrVehicle(_player, queryBattlePetName.UnitGUID); + if (!summonedBattlePet || !summonedBattlePet.IsSummon()) + { + SendPacket(response); + return; + } + + response.CreatureID = summonedBattlePet.GetEntry(); + response.Timestamp = summonedBattlePet.GetBattlePetCompanionNameTimestamp(); + + Unit petOwner = summonedBattlePet.ToTempSummon().GetSummoner(); + if (!petOwner.IsPlayer()) + { + SendPacket(response); + return; + } + + BattlePetMgr.BattlePet battlePet = petOwner.ToPlayer().GetSession().GetBattlePetMgr().GetPet(queryBattlePetName.BattlePetID); + if (battlePet == null) + { + SendPacket(response); + return; + } + + response.Allow = true; + response.Name = battlePet.PacketInfo.Name; + if (battlePet.DeclinedName != null) + { + response.HasDeclined = true; + response.DeclinedNames = battlePet.DeclinedName; + } + + SendPacket(response); } [WorldPacketHandler(ClientOpcodes.BattlePetDeletePet)] @@ -97,7 +139,7 @@ namespace Game [WorldPacketHandler(ClientOpcodes.BattlePetSummon, Processing = PacketProcessing.Inplace)] void HandleBattlePetSummon(BattlePetSummon battlePetSummon) { - if (_player.m_activePlayerData.SummonedBattlePetGUID != battlePetSummon.PetGuid) + if (_player.GetSummonedBattlePetGUID() != battlePetSummon.PetGuid) GetBattlePetMgr().SummonPet(battlePetSummon.PetGuid); else GetBattlePetMgr().DismissPet(); diff --git a/Source/Game/Networking/Packets/BattlePetPackets.cs b/Source/Game/Networking/Packets/BattlePetPackets.cs index 432351971..e9bf1f067 100644 --- a/Source/Game/Networking/Packets/BattlePetPackets.cs +++ b/Source/Game/Networking/Packets/BattlePetPackets.cs @@ -139,7 +139,7 @@ namespace Game.Networking.Packets if (_worldPacket.HasBit()) { - DeclinedName.Set(new DeclinedName()); + DeclinedNames.Set(new DeclinedName()); byte[] declinedNameLengths = new byte[SharedConst.MaxDeclinedNameCases]; @@ -147,7 +147,7 @@ namespace Game.Networking.Packets declinedNameLengths[i] = _worldPacket.ReadBits(7); for (byte i = 0; i < SharedConst.MaxDeclinedNameCases; ++i) - DeclinedName.Value.name[i] = _worldPacket.ReadString(declinedNameLengths[i]); + DeclinedNames.Value.name[i] = _worldPacket.ReadString(declinedNameLengths[i]); } Name = _worldPacket.ReadString(nameLength); @@ -155,9 +155,62 @@ namespace Game.Networking.Packets public ObjectGuid PetGuid; public string Name; - public Optional DeclinedName; + public Optional DeclinedNames; } + class QueryBattlePetName : ClientPacket + { + public QueryBattlePetName(WorldPacket packet) : base(packet) { } + + public override void Read() + { + BattlePetID = _worldPacket.ReadPackedGuid(); + UnitGUID = _worldPacket.ReadPackedGuid(); + } + + public ObjectGuid BattlePetID; + public ObjectGuid UnitGUID; + } + + class QueryBattlePetNameResponse : ServerPacket + { + public QueryBattlePetNameResponse() : base(ServerOpcodes.QueryBattlePetNameResponse, ConnectionType.Instance) { } + + public override void Write() + { + _worldPacket.WritePackedGuid(BattlePetID); + _worldPacket.WriteUInt32(CreatureID); + _worldPacket.WriteInt64(Timestamp); + + _worldPacket.WriteBit(Allow); + + if (Allow) + { + _worldPacket.WriteBits(Name.GetByteCount(), 8); + _worldPacket.WriteBit(HasDeclined); + + for (byte i = 0; i < SharedConst.MaxDeclinedNameCases; ++i) + _worldPacket.WriteBits(DeclinedNames.name[i].GetByteCount(), 7); + + for (byte i = 0; i < SharedConst.MaxDeclinedNameCases; ++i) + _worldPacket.WriteString(DeclinedNames.name[i]); + + _worldPacket.WriteString(Name); + } + + _worldPacket.FlushBits(); + } + + public ObjectGuid BattlePetID; + public uint CreatureID; + public long Timestamp; + public bool Allow; + + public bool HasDeclined; + public DeclinedName DeclinedNames; + public string Name; + } + class BattlePetDeletePet : ClientPacket { public BattlePetDeletePet(WorldPacket packet) : base(packet) { } diff --git a/sql/updates/auth/master/2021_11_20_00_auth.sql b/sql/updates/auth/master/2021_11_20_00_auth.sql new file mode 100644 index 000000000..da75c1e44 --- /dev/null +++ b/sql/updates/auth/master/2021_11_20_00_auth.sql @@ -0,0 +1,3 @@ +ALTER TABLE `battle_pets` ADD `nameTimestamp` bigint(20) NOT NULL DEFAULT '0' AFTER `name`; + +UPDATE `battle_pets` SET `nameTimestamp`=UNIX_TIMESTAMP() WHERE LENGTH(`name`) > 0;