Skip to content
This repository has been archived by the owner on Jan 18, 2024. It is now read-only.

Commit

Permalink
Core/Pets: re-implement pet aura saving
Browse files Browse the repository at this point in the history
  • Loading branch information
Ovahlord committed Oct 16, 2023
1 parent e30cfd4 commit 67c2790
Show file tree
Hide file tree
Showing 9 changed files with 201 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -614,13 +614,10 @@ void CharacterDatabaseConnection::DoPrepareStatements()
//PrepareStatement(CHAR_DEL_CHAR_PET_DECLINEDNAME_BY_OWNER, "DELETE FROM character_pet_declinedname WHERE Guid = ?", CONNECTION_ASYNC);
//PrepareStatement(CHAR_DEL_CHAR_PET_DECLINEDNAME, "DELETE FROM character_pet_declinedname WHERE PetNumber = ?", CONNECTION_ASYNC);
//PrepareStatement(CHAR_INS_CHAR_PET_DECLINEDNAME, "INSERT INTO character_pet_declinedname (PetNumber, Guid, genitive, dative, accusative, instrumental, prepositional) VALUES (?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_PET_AURA, "SELECT casterGuid, spell, effectMask, recalculateMask, stackCount, amount0, amount1, amount2, base_amount0, base_amount1, base_amount2, maxDuration, remainTime, remainCharges, critChance, applyResilience FROM pet_aura WHERE guid = ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_PET_SPELL, "SELECT spell, active FROM pet_spell WHERE guid = ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_PET_DECLINED_NAME, "SELECT genitive, dative, accusative, instrumental, prepositional FROM character_pet_declinedname WHERE Guid = ? AND PetNumber = ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_PET_DECLINED_NAME, "SELECT genitive, dative, accusative, instrumental, prepositional FROM character_pet_declinedname WHERE Guid = ? AND PetNumber = ?", CONNECTION_ASYNC);

PrepareStatement(CHAR_SEL_PET_AURAS, "SELECT guid, casterGuid, spell, effectMask, recalculateMask, stackCount, amount0, amount1, amount2, base_amount0, base_amount1, base_amount2, maxDuration, remainTime, remainCharges, critChance, applyResilience FROM pet_aura WHERE guid IN (SELECT PetNumber FROM character_pet WHERE Guid = ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_PET_AURAS, "DELETE FROM pet_aura WHERE guid = ?", CONNECTION_BOTH);
PrepareStatement(CHAR_DEL_PET_SPELLS, "DELETE FROM pet_spell WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_PET_SPELL_BY_SPELL, "DELETE FROM pet_spell WHERE guid = ? and spell = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_INS_PET_SPELL, "INSERT INTO pet_spell (guid, spell, active) VALUES (?, ?, ?)", CONNECTION_BOTH);
PrepareStatement(CHAR_INS_PET_AURA, "INSERT INTO pet_aura (guid, casterGuid, spell, effectMask, recalculateMask, stackCount, amount0, amount1, amount2, "
"base_amount0, base_amount1, base_amount2, maxDuration, remainTime, remainCharges, critChance, applyResilience) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_BOTH);

Expand All @@ -629,6 +626,7 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PrepareStatement(CHAR_SEL_CHAR_PET, "SELECT PetNumber, CreatureId, TamedCreatureId, DisplayId, SavedHealth, SavedPower, CreatedBySpellId, LastSaveTime, ReactState, Slot, HasBeenRenamed, IsActive, `Name`, ActionBar, Talents FROM character_pet WHERE Guid = ? ORDER BY Slot", CONNECTION_ASYNC);
PrepareStatement(CHAR_UPD_CHAR_PET, "UPDATE character_pet SET SavedHealth = ?, SavedPower = ?, LastSaveTime = ?, ReactState = ?, Slot = ?, HasBeenRenamed = ?, IsActive = ?, `Name` = ?, ActionBar = ?, Talents = ? WHERE PetNumber = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_INS_CHAR_PET, "INSERT INTO character_pet (Guid, PetNumber, CreatureId, TamedCreatureId, DisplayId, SavedHealth, SavedPower, CreatedBySpellId, LastSaveTime, ReactState, Slot, HasBeenRenamed, IsActive, `Name`, ActionBar, Talents) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_BOTH);

PrepareStatement(CHAR_SEL_PET_SPELL_COOLDOWNS, "SELECT guid, spell, time, categoryId, categoryEnd FROM pet_spell_cooldown WHERE guid IN (SELECT PetNumber FROM character_pet WHERE Guid = ?) AND time > UNIX_TIMESTAMP()", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_PET_SPELL_COOLDOWNS, "DELETE FROM pet_spell_cooldown WHERE guid = ?", CONNECTION_BOTH);
PrepareStatement(CHAR_INS_PET_SPELL_COOLDOWN, "INSERT INTO pet_spell_cooldown (guid, spell, time, categoryId, categoryEnd) VALUES (?, ?, ?, ?, ?)", CONNECTION_BOTH);
Expand Down
14 changes: 6 additions & 8 deletions src/server/database/Database/Implementation/CharacterDatabase.h
Original file line number Diff line number Diff line change
Expand Up @@ -519,17 +519,15 @@ enum CharacterDatabaseStatements : uint32
CHAR_REP_CALENDAR_INVITE,
CHAR_DEL_CALENDAR_INVITE,

CHAR_SEL_PET_AURA,
CHAR_SEL_PET_SPELL,
CHAR_SEL_PET_SPELL_COOLDOWNS,
CHAR_SEL_PET_DECLINED_NAME,
CHAR_SEL_PET_AURAS,
CHAR_DEL_PET_AURAS,
CHAR_INS_PET_AURA,

CHAR_SEL_PET_SPELL_COOLDOWNS,
CHAR_DEL_PET_SPELL_COOLDOWNS,
CHAR_INS_PET_SPELL_COOLDOWN,
CHAR_DEL_PET_SPELL_BY_SPELL,
CHAR_INS_PET_SPELL,
CHAR_INS_PET_AURA,
CHAR_DEL_PET_SPELLS,

CHAR_SEL_PET_DECLINED_NAME,

CHAR_INS_CHAR_PET,
CHAR_DEL_CHAR_PET,
Expand Down
90 changes: 90 additions & 0 deletions src/server/game/Entities/Creature/TemporarySummon/NewPet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
#include "ObjectMgr.h"
#include "PetPackets.h"
#include "Player.h"
#include "SpellAuras.h"
#include "SpellAuraEffects.h"
#include "SpellMgr.h"
#include "SpellHistory.h"
#include "SpellInfo.h"
Expand Down Expand Up @@ -77,6 +79,8 @@ bool NewPet::HandlePreSummonActions(Unit const* summoner, uint8 creatureLevel, u
for (auto const& itr : playerPetData->Cooldowns)
GetSpellHistory()->AddCooldown(itr.SpellId, 0, itr.CooldownEnd, itr.CategoryId, itr.CategoryEnd);

ApplySavedAuras(playerPetData);

if (IsClassPet())
SetName(playerPetData->Name);

Expand Down Expand Up @@ -303,6 +307,7 @@ void NewPet::UpdatePlayerPetData(PlayerPetData* petData)
petData->Talents = GenenerateTalentsDataString();

GetSpellHistory()->StoreSpellHistoryEntries(petData->Cooldowns);
StoreAppliedAuras(petData);

if (petData->Status != PlayerPetDataStatus::New)
petData->Status = PlayerPetDataStatus::Changed;
Expand Down Expand Up @@ -435,6 +440,91 @@ void NewPet::LearnAvailableSpellsForCurrentLevel()

}

void NewPet::ApplySavedAuras(PlayerPetData* petData)
{
uint32 timediff = uint32(GameTime::GetGameTime() - petData->LastSaveTime);

for (PlayerPetDataAura& petAura : petData->Auras)
{
SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(petAura.SpellId);
if (!spellInfo) // skip non-existing/outdated auras. They will be pruned from DB at the next save cycle
continue;

int32 remainingDuration = petAura.RemainingDuration;
// negative effects should continue counting down after logout
if (remainingDuration != -1 && (!spellInfo->IsPositive() || spellInfo->HasAttribute(SPELL_ATTR4_AURA_EXPIRES_OFFLINE)))
{
if (remainingDuration / IN_MILLISECONDS <= int32(timediff))
continue;

remainingDuration -= timediff * IN_MILLISECONDS;
}

uint8 remainingCharges = petAura.RemainingCharges;
// prevent wrong values of remaincharges
if (spellInfo->ProcCharges)
{
if (remainingCharges <= 0 || remainingCharges > spellInfo->ProcCharges)
remainingCharges = spellInfo->ProcCharges;
}
else
remainingCharges = 0;

AuraCreateInfo createInfo(spellInfo, petAura.EffectMask, this);
createInfo
.SetCasterGUID(petAura.CasterGuid)
.SetBaseAmount(petAura.BaseAmount.data());

if (Aura* aura = Aura::TryCreate(createInfo))
{
if (!aura->CanBeSaved())
{
aura->Remove();
continue;
}

aura->SetLoadedState(petAura.MaxDuration, remainingDuration, remainingCharges, petAura.StackAmount, petAura.RecalculateMask, petAura.CritChance, petAura.ApplyResilience, petAura.Amount.data());
aura->ApplyForTargets();
}
}
}

void NewPet::StoreAppliedAuras(PlayerPetData* petData)
{
petData->Auras.clear();

for (auto const& pair : m_ownedAuras)
{
Aura* aura = pair.second;
if (!aura->CanBeSaved())
continue;

PlayerPetDataAura& petAura = petData->Auras.emplace_back();

for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
{
if (aura->GetEffect(i))
{
petAura.BaseAmount[i] = aura->GetEffect(i)->GetBaseAmount();
petAura.Amount[i] = aura->GetEffect(i)->GetAmount();
petAura.EffectMask |= (1 << i);
if (aura->GetEffect(i)->CanBeRecalculated())
petAura.RecalculateMask |= (1 << i);
}
}

// Store auras that have been casted by ourselves as empty because when we are summoned the next time, we will have a different ObjectGuid
petAura.CasterGuid = aura->GetCasterGUID() == GetGUID() ? ObjectGuid::Empty : aura->GetCasterGUID();
petAura.SpellId = aura->GetId();
petAura.StackAmount = aura->GetStackAmount();
petAura.RemainingCharges = aura->GetCharges();
petAura.MaxDuration = aura->GetMaxDuration();
petAura.RemainingDuration = aura->GetDuration();
petAura.CritChance = aura->GetCritChance();
petAura.ApplyResilience = aura->CanApplyResilience();
}
}

bool NewPet::LearnSpell(uint32 spellId)
{
if (!AddSpell(spellId))
Expand Down
6 changes: 4 additions & 2 deletions src/server/game/Entities/Creature/TemporarySummon/NewPet.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,14 +71,14 @@ class TC_GAME_API NewPet final : public NewGuardian
// Synchronizes the pet's level with its summoner, updates the stats for the new level and learns new spells if available
void SynchronizeLevelWithSummoner();
// Returns true when the pet knows the given spell
bool HasSpell(uint32 spellID) const override;
bool HasSpell(uint32 spellId) const override;
// Enables or disables auto casting for the given spell
void ToggleAutocast(SpellInfo const* spellInfo, bool apply);
// Returns a map of a pet's known spells
PetSpellMap const& GetSpells() const { return _spells; }
// Sets the PlayerPetData map key for accessing the summoner's player pet data at any given time
void SetPlayerPetDataKey(uint8 slot, uint32 creatureId);
// Updates the PlayerPetData values of this pet for the specified summoner
// Updates the provided pet data object with the pet's current data, such as action bars, talents and cooldowns. This method should be used whenever you have to store the data (like unsummoning and saving pets)
void UpdatePlayerPetData(PlayerPetData* petData);
// Learns the specified talent for the given rank and learns the according spell/passive aura
void LearnTalent(uint32 talentId, uint32 rank);
Expand All @@ -91,6 +91,8 @@ class TC_GAME_API NewPet final : public NewGuardian
void SendSpellLearnedToSummoner(uint32 spellId);
void SendSpellUnlearnedToSummoner(uint32 spellId);
void LearnAvailableSpellsForCurrentLevel();
void ApplySavedAuras(PlayerPetData* petData);
void StoreAppliedAuras(PlayerPetData* petData);
bool LearnSpell(uint32 spellId);
bool UnlearnSpell(uint32 spellId, bool learnPreviousRank, bool clearActionbar = true);
bool AddSpell(uint32 spellId, ActiveStates active = ACT_DECIDE, PetSpellState state = PETSPELL_NEW, PetSpellType type = PETSPELL_NORMAL);
Expand Down
6 changes: 6 additions & 0 deletions src/server/game/Entities/Pet/Pet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -931,6 +931,7 @@ void Pet::_LoadSpellCooldowns()

void Pet::_LoadSpells()
{
/*
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_SPELL);
stmt->setUInt32(0, m_charmInfo->GetPetNumber());
PreparedQueryResult result = CharacterDatabase.Query(stmt);
Expand All @@ -945,10 +946,12 @@ void Pet::_LoadSpells()
}
while (result->NextRow());
}
*/
}

void Pet::_SaveSpells(CharacterDatabaseTransaction& trans)
{
/*
for (PetSpellMapOld::iterator itr = m_spells.begin(), next = m_spells.begin(); itr != m_spells.end(); itr = next)
{
++next;
Expand Down Expand Up @@ -994,10 +997,12 @@ void Pet::_SaveSpells(CharacterDatabaseTransaction& trans)
}
itr->second.state = PETSPELL_UNCHANGED;
}
*/
}

void Pet::_LoadAuras(uint32 timediff)
{
/*
TC_LOG_DEBUG("entities.pet", "Loading auras for pet %u", GetGUID().GetCounter());
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_AURA);
Expand Down Expand Up @@ -1075,6 +1080,7 @@ void Pet::_LoadAuras(uint32 timediff)
}
while (result->NextRow());
}
*/
}

void Pet::_SaveAuras(CharacterDatabaseTransaction& trans)
Expand Down
67 changes: 67 additions & 0 deletions src/server/game/Entities/Player/Player.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16924,6 +16924,7 @@ bool Player::LoadFromDB(ObjectGuid guid, CharacterDatabaseQueryHolder const& hol

_LoadPets(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_ALL_PETS));
_LoadPetCooldowns(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_ALL_PET_COOLDOWNS));
_LoadPetAuras(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_ALL_PET_AURAS));

// after spell load, learn rewarded spell if need also
_LoadQuestStatus(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_QUEST_STATUS));
Expand Down Expand Up @@ -17812,6 +17813,39 @@ void Player::_LoadPetCooldowns(PreparedQueryResult result)
} while (result->NextRow());
}

void Player::_LoadPetAuras(PreparedQueryResult result)
{
// "SELECT guid, casterGuid, spell, effectMask, recalculateMask, stackCount, amount0, amount1, amount2, base_amount0, base_amount1, base_amount2, maxDuration, remainTime, remainCharges,
// critChance, applyResilience FROM pet_aura WHERE guid IN (SELECT PetNumber FROM character_pet WHERE Guid = ?)"
if (!result)
return;

do
{
Field* fields = result->Fetch();

uint32 petNumber = fields[0].GetUInt32();
PlayerPetData* petData = GetPlayerPetDataByPetNumber(petNumber);
if (!petData)
continue;

PlayerPetDataAura& aura = petData->Auras.emplace_back();
aura.CasterGuid = ObjectGuid(fields[1].GetUInt64());
aura.SpellId = fields[2].GetUInt32();
aura.EffectMask = fields[3].GetUInt8();
aura.RecalculateMask = fields[4].GetUInt8();
aura.StackAmount = fields[5].GetUInt8();
aura.Amount = { fields[6].GetInt32(), fields[7].GetInt32(), fields[8].GetInt32() };
aura.BaseAmount = { fields[9].GetInt32(), fields[10].GetInt32(), fields[11].GetInt32() };
aura.MaxDuration = fields[12].GetInt32();
aura.RemainingDuration = fields[13].GetInt32();
aura.RemainingCharges = fields[14].GetUInt8();
aura.CritChance = fields[15].GetFloat();
aura.ApplyResilience = fields[16].GetBool();

} while (result->NextRow());
}

void Player::SendPetSpellsMessage(NewPet* pet, bool remove /*= false*/)
{
CharmInfo* charmInfo = pet->GetCharmInfo();
Expand Down Expand Up @@ -20022,6 +20056,7 @@ void Player::_SavePets(CharacterDatabaseTransaction& trans)

trans->Append(stmt);

// Pet cooldowns
stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PET_SPELL_COOLDOWNS);
stmt->setUInt32(0, petData->PetNumber);
trans->Append(stmt);
Expand All @@ -20040,6 +20075,34 @@ void Player::_SavePets(CharacterDatabaseTransaction& trans)
stmt->setUInt32(4, uint32(SpellHistory::Clock::to_time_t(itr.CooldownEnd)));
trans->Append(stmt);
}

// Pet auras
stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PET_AURAS);
stmt->setUInt32(0, petData->PetNumber);
trans->Append(stmt);

for (auto const& itr : petData->Auras)
{
stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_PET_AURA);
stmt->setUInt32(0, petData->PetNumber);
stmt->setUInt64(1, itr.CasterGuid.GetRawValue());
stmt->setUInt32(2, itr.SpellId);
stmt->setUInt8(3, itr.EffectMask);
stmt->setUInt8(4, itr.RecalculateMask);
stmt->setUInt8(5, itr.StackAmount);
stmt->setInt32(6, itr.Amount[EFFECT_0]);
stmt->setInt32(7, itr.Amount[EFFECT_1]);
stmt->setInt32(8, itr.Amount[EFFECT_2]);
stmt->setInt32(9, itr.BaseAmount[EFFECT_0]);
stmt->setInt32(10, itr.BaseAmount[EFFECT_1]);
stmt->setInt32(11, itr.BaseAmount[EFFECT_2]);
stmt->setInt32(12, itr.MaxDuration);
stmt->setInt32(13, itr.RemainingDuration);
stmt->setUInt8(14, itr.RemainingCharges);
stmt->setFloat(15, itr.CritChance);
stmt->setBool(16, itr.ApplyResilience);
trans->Append(stmt);
}
}

for (uint32 deletedPetNumber : _deletedPlayerPetDataSet)
Expand All @@ -20051,6 +20114,10 @@ void Player::_SavePets(CharacterDatabaseTransaction& trans)
stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PET_SPELL_COOLDOWNS);
stmt->setUInt32(0, deletedPetNumber);
trans->Append(stmt);

stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PET_AURAS);
stmt->setUInt32(0, deletedPetNumber);
trans->Append(stmt);
}

_deletedPlayerPetDataSet.clear();
Expand Down
4 changes: 3 additions & 1 deletion src/server/game/Entities/Player/Player.h
Original file line number Diff line number Diff line change
Expand Up @@ -802,7 +802,8 @@ enum PlayerLoginQueryIndex
PLAYER_LOGIN_QUERY_LOAD_CORPSE_LOCATION = 36,
PLAYER_LOGIN_QUERY_LOAD_ALL_PETS = 37,
PLAYER_LOGIN_QUERY_LOAD_ALL_PET_COOLDOWNS = 38,
PLAYER_LOGIN_QUERY_LOAD_LFG_REWARD_STATUS = 39,
PLAYER_LOGIN_QUERY_LOAD_ALL_PET_AURAS = 39,
PLAYER_LOGIN_QUERY_LOAD_LFG_REWARD_STATUS = 40,
MAX_PLAYER_LOGIN_QUERY
};

Expand Down Expand Up @@ -2465,6 +2466,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
void _LoadLFGRewardStatus(PreparedQueryResult result);
void _LoadPets(PreparedQueryResult result);
void _LoadPetCooldowns(PreparedQueryResult result);
void _LoadPetAuras(PreparedQueryResult result);

/*********************************************************/
/*** SAVE SYSTEM ***/
Expand Down
17 changes: 17 additions & 0 deletions src/server/game/Entities/Player/PlayerPetData.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,22 @@
#include "PetDefines.h"
#include "UnitDefines.h"

struct PlayerPetDataAura
{
std::array<int32, MAX_SPELL_EFFECTS> Amount = { };
std::array<int32, MAX_SPELL_EFFECTS> BaseAmount = { };
ObjectGuid CasterGuid;
uint32 SpellId = 0;
uint8 EffectMask = 0;
uint8 RecalculateMask = 0;
uint8 StackAmount = 0;
int32 MaxDuration = 0;
int32 RemainingDuration = 0;
uint8 RemainingCharges = 0;
float CritChance = 0.f;
bool ApplyResilience = false;
};

struct PlayerPetData
{
PlayerPetData() { }
Expand All @@ -42,6 +58,7 @@ struct PlayerPetData
std::string ActionBar;
std::string Talents;
std::vector<SpellHistory::CooldownEntry> Cooldowns;
std::vector<PlayerPetDataAura> Auras;
PlayerPetDataStatus Status = PlayerPetDataStatus::New;
};

Expand Down
4 changes: 4 additions & 0 deletions src/server/game/Handlers/CharacterHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,10 @@ bool LoginQueryHolder::Initialize()
stmt->setUInt32(0, lowGuid);
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_ALL_PET_COOLDOWNS, stmt);

stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_AURAS);
stmt->setUInt32(0, lowGuid);
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_ALL_PET_AURAS, stmt);

stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_REWARDSTATUS_LFG);
stmt->setUInt32(0, lowGuid);
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_LFG_REWARD_STATUS, stmt);
Expand Down

0 comments on commit 67c2790

Please sign in to comment.