warband (account-wide)

This commit is contained in:
luis
2026-10-09 18:18:53 -03:00
parent 5901d9b19a
commit afd39745ff
8 changed files with 105 additions and 43 deletions
+26 -39
View File
@@ -18280,45 +18280,10 @@ void Player::_LoadDeclinedNames(PreparedQueryResult result)
SetUpdateFieldValue(declinedNames.ModifyValue(&UF::DeclinedNames::Name, i), (*result)[i].GetString());
}
void Player::_LoadArenaTeamInfo(PreparedQueryResult result)
{
// arenateamid, played_week, played_season, personal_rating
uint16 personalRatingCache[] = { 0, 0, 0 };
if (result)
{
do
{
Field* fields = result->Fetch();
uint32 arenaTeamId = fields[0].GetUInt32();
ArenaTeam* arenaTeam = sArenaTeamMgr->GetArenaTeamById(arenaTeamId);
if (!arenaTeam)
{
TC_LOG_ERROR("entities.player.loading", "Player::_LoadArenaTeamInfo: couldn't load arenateam {}", arenaTeamId);
continue;
}
uint8 arenaSlot = arenaTeam->GetSlot();
ASSERT(arenaSlot < 3);
personalRatingCache[arenaSlot] = fields[4].GetUInt16();
SetArenaTeamInfoField(arenaSlot, ARENA_TEAM_ID, arenaTeamId);
SetArenaTeamInfoField(arenaSlot, ARENA_TEAM_TYPE, arenaTeam->GetType());
SetArenaTeamInfoField(arenaSlot, ARENA_TEAM_MEMBER, (arenaTeam->GetCaptain() == GetGUID()) ? 0 : 1);
SetArenaTeamInfoField(arenaSlot, ARENA_TEAM_GAMES_WEEK, uint32(fields[1].GetUInt16()));
SetArenaTeamInfoField(arenaSlot, ARENA_TEAM_GAMES_SEASON, uint32(fields[2].GetUInt16()));
SetArenaTeamInfoField(arenaSlot, ARENA_TEAM_WINS_SEASON, uint32(fields[3].GetUInt16()));
} while (result->NextRow());
}
for (uint8 slot = 0; slot <= 2; ++slot)
{
SetArenaTeamInfoField(slot, ARENA_TEAM_PERSONAL_RATING, uint32(personalRatingCache[slot]));
}
}
// Player::_LoadArenaTeamInfo was removed along with its login query (PLAYER_LOGIN_QUERY_LOAD_ARENA_INFO): every
// value it set went through SetArenaTeamInfoField, which is an intentional empty stub in this fork, so the loader
// read the arena rows and wrote them nowhere. Per-player arena team state is derived from ArenaTeam membership at
// runtime instead.
void Player::_LoadEquipmentSets(PreparedQueryResult result)
{
@@ -19283,6 +19248,18 @@ bool Player::LoadFromDB(ObjectGuid guid, CharacterDatabaseQueryHolder const& hol
if (!m_taxi.LoadTaxiMask(fields.taximask)) // must be before InitTaxiNodesForLevel
TC_LOG_WARN("entities.player.loading", "Player::LoadFromDB: Player ({}) has invalid taximask ({}) in DB. Forced partial load.", GetGUID().ToString(), fields.taximask);
//WowCommunity
// Warband (account-wide) taxi: merge the account mask into this character's mask and keep the account copy
// so newly learned nodes can be written back to warband_taxi_mask on save.
if (PreparedQueryResult warbandTaxiResult = holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_WARBAND_TAXI_MASK))
m_taxi.LoadAccountTaxiMask((*warbandTaxiResult)[0].GetString());
// Other max-level characters on this account (excluding self): KillRewarder grants +5% XP per alt, capped at
// 25%.
if (PreparedQueryResult maxLevelCharsResult = holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_WARBAND_MAX_LEVEL_COUNT))
_warbandMaxLevelCharCount = uint8(std::min<uint64>((*maxLevelCharsResult)[0].GetUInt64(), 5));
//WowCommunity
uint32 extraflags = fields.extra_flags;
_LoadPetStable(fields.summonedPetNumber, holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_PET_SLOTS));
@@ -21801,6 +21778,16 @@ void Player::SaveToDB(LoginDatabaseTransaction loginTransaction, CharacterDataba
_SaveAccountBankTabSettings(trans);
_SaveAccountBankItems(trans);
_SaveAccountBankCoinage(trans);
// Warband taxi: only rewrite warband_taxi_mask when this character learned a new account-wide node; the mask
// loaded at login is already the account's, so an untouched session has nothing to write.
if (m_taxi.IsAccountTaximaskDirty())
{
stmt = CharacterDatabase.GetPreparedStatement(CHAR_REP_WARBAND_TAXI_MASK);
stmt->setUInt32(0, GetSession()->GetBattlenetAccountId());
stmt->setString(1, m_taxi.SaveAccountTaxiMaskToString());
trans->Append(stmt);
m_taxi.ClearAccountTaximaskDirty();
}
for (auto const& [type, garrison] : _garrisons)
garrison->SaveToDB(trans);
//WowCommunity
+1 -1
View File
@@ -3607,7 +3607,7 @@ class TC_GAME_API Player final : public Unit, public GridObject<Player>
void _LoadStoredAuraTeleportLocations(PreparedQueryResult result);
bool _LoadHomeBind(PreparedQueryResult result);
void _LoadDeclinedNames(PreparedQueryResult result);
void _LoadArenaTeamInfo(PreparedQueryResult result);
// _LoadArenaTeamInfo removed: SetArenaTeamInfoField is an empty stub, so it persisted nothing (see Player.cpp).
void _LoadEquipmentSets(PreparedQueryResult result);
void _LoadTransmogCustomSets(PreparedQueryResult result);
void _LoadTransmogOutfits(PreparedQueryResult setsResult, PreparedQueryResult situationsResult, PreparedQueryResult slotsResult,
@@ -300,3 +300,51 @@ void PlayerTaxi::MergeAccountTaxiMask(TaxiMask const& accountMask)
m_taximask[field] |= submask;
}
}
void PlayerTaxi::LoadAccountTaxiMask(std::string const& data)
{
m_accountTaximask = LoadTaxiMaskFromString(data);
m_accountTaximaskDirty = false;
MergeAccountTaxiMask(m_accountTaximask);
// Backfill: nodes this character knew before account-wide taxi existed (or before this character ever saved
// with the account mask wired up) become part of the account mask; anything the account already had is a no-op.
for (TaxiNodesEntry const* node : sTaxiNodesStore)
{
if (node->GetFlags().HasFlag(TaxiNodeFlags::NotAccountWide))
continue;
uint32 field = uint32((node->ID - 1) / (sizeof(TaxiMask::value_type) * 8));
TaxiMask::value_type submask = TaxiMask::value_type(1 << ((node->ID - 1) % (sizeof(TaxiMask::value_type) * 8)));
if ((m_taximask[field] & submask) && !(m_accountTaximask[field] & submask))
{
m_accountTaximask[field] |= submask;
m_accountTaximaskDirty = true;
}
}
}
bool PlayerTaxi::SetAccountTaximaskNode(uint32 nodeidx)
{
TaxiNodesEntry const* node = sTaxiNodesStore.LookupEntry(nodeidx);
if (!node || node->GetFlags().HasFlag(TaxiNodeFlags::NotAccountWide))
return false;
uint32 field = uint32((nodeidx - 1) / (sizeof(TaxiMask::value_type) * 8));
TaxiMask::value_type submask = TaxiMask::value_type(1 << ((nodeidx - 1) % (sizeof(TaxiMask::value_type) * 8)));
if (m_accountTaximask[field] & submask)
return false;
m_accountTaximask[field] |= submask;
m_accountTaximaskDirty = true;
return true;
}
std::string PlayerTaxi::SaveAccountTaxiMaskToString() const
{
std::ostringstream ss;
for (std::size_t i = 0; i < m_accountTaximask.size(); ++i)
ss << uint32(m_accountTaximask[i]) << ' ';
return ss.str();
}
@@ -50,6 +50,12 @@ class TC_GAME_API PlayerTaxi
//WowCommunity
static TaxiMask LoadTaxiMaskFromString(std::string const& data);
void MergeAccountTaxiMask(TaxiMask const& accountMask);
void LoadAccountTaxiMask(std::string const& data);
bool SetAccountTaximaskNode(uint32 nodeidx);
TaxiMask const& GetAccountTaxiMask() const { return m_accountTaximask; }
bool IsAccountTaximaskDirty() const { return m_accountTaximaskDirty; }
void ClearAccountTaximaskDirty() { m_accountTaximaskDirty = false; }
std::string SaveAccountTaxiMaskToString() const;
//WowCommunity
bool IsTaximaskNodeKnown(uint32 nodeidx) const
@@ -96,6 +102,10 @@ class TC_GAME_API PlayerTaxi
friend std::ostringstream& operator<<(std::ostringstream& ss, PlayerTaxi const& taxi);
private:
TaxiMask m_taximask;
//WowCommunity
TaxiMask m_accountTaximask; // warband (account-wide) copy, persisted to warband_taxi_mask on save
bool m_accountTaximaskDirty = false;
//WowCommunity
std::deque<uint32> m_TaxiDestinations;
uint32 m_flightMasterFactionId = 0;
};
+5
View File
@@ -136,6 +136,8 @@ bool WorldSession::SendLearnNewTaxiNode(Creature* unit)
if (GetPlayer()->m_taxi.SetTaximaskNode(curloc))
{
GetPlayer()->m_taxi.SetAccountTaximaskNode(curloc); // warband: learned paths are shared account-wide
SendPacket(WorldPackets::Taxi::NewTaxiPath(curloc).Write());
WorldPackets::Taxi::TaxiNodeStatus data;
@@ -153,7 +155,10 @@ bool WorldSession::SendLearnNewTaxiNode(Creature* unit)
void WorldSession::SendDiscoverNewTaxiNode(uint32 nodeid)
{
if (GetPlayer()->m_taxi.SetTaximaskNode(nodeid))
{
GetPlayer()->m_taxi.SetAccountTaximaskNode(nodeid); // warband: discovered paths are shared account-wide
SendPacket(WorldPackets::Taxi::NewTaxiPath(nodeid).Write());
}
}
void WorldSession::HandleActivateTaxiOpcode(WorldPackets::Taxi::ActivateTaxi& activateTaxi)
@@ -41,6 +41,16 @@ void WorldSession::HandleGMTicketSystemStatusOpcode(WorldPackets::Ticket::GMTick
SendPacket(response.Write());
}
void WorldSession::HandleGmTicketAcknowledgeSurvey(WorldPackets::Ticket::GMTicketAcknowledgeSurvey& packet)
{
// The client sends this after the player dismisses the post-resolution survey for a case shown in
// SMSG_GM_TICKET_CASE_STATUS (CaseID = GMTICKET_CASE_ID_* base + ticket id, see SupportMgr.h). Cases are
// derived from open tickets only, so by the time the survey appears the ticket is already closed and drops
// out of the next case list on its own - the acknowledgement is purely a client-side UI dismissal and needs
// no server state or response. Logged so the case id remains traceable if the flow is ever extended.
TC_LOG_DEBUG("network", "WorldSession::HandleGmTicketAcknowledgeSurvey: {} acknowledged survey for case {}.", GetPlayerInfo(), packet.CaseID);
}
void WorldSession::HandleSubmitUserFeedback(WorldPackets::Ticket::SubmitUserFeedback& userFeedback)
{
if (userFeedback.IsSuggestion)
+3 -3
View File
@@ -151,7 +151,7 @@ void OpcodeTable::InitializeClientOpcodes()
DEFINE_HANDLER(CMSG_ACCEPT_WARGAME_INVITE, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleAcceptWargameInvite);
DEFINE_HANDLER(CMSG_ACCOUNT_BANK_DEPOSIT_MONEY, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleAccountBankDepositMoney);
DEFINE_HANDLER(CMSG_ACCOUNT_BANK_WITHDRAW_MONEY, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleAccountBankWithdrawMoney);
DEFINE_HANDLER(CMSG_ACCOUNT_NOTIFICATION_ACKNOWLEDGED, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::Handle_NULL);
DEFINE_HANDLER(CMSG_ACCOUNT_NOTIFICATION_ACKNOWLEDGED, STATUS_AUTHED, PROCESS_THREADUNSAFE, &WorldSession::HandleAccountNotificationAcknowledged);
DEFINE_HANDLER(CMSG_ACCOUNT_STORE_BEGIN_PURCHASE_OR_REFUND, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, &WorldSession::Handle_NULL);
DEFINE_HANDLER(CMSG_ACTIVATE_SOULBIND, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleActivateSoulbind);
DEFINE_HANDLER(CMSG_ACTIVATE_TAXI, STATUS_LOGGEDIN, PROCESS_THREADSAFE, &WorldSession::HandleActivateTaxiOpcode);
@@ -509,12 +509,12 @@ void OpcodeTable::InitializeClientOpcodes()
DEFINE_HANDLER(CMSG_GET_PVP_OPTIONS_ENABLED, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleGetPVPOptionsEnabled);
DEFINE_HANDLER(CMSG_GET_RAF_ACCOUNT_INFO, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleGetRafAccountInfo);
DEFINE_HANDLER(CMSG_GET_REGIONWIDE_CHARACTER_RESTRICTION_AND_MAIL_DATA, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleGetRegionwideCharacterRestrictionAndMailData);
DEFINE_HANDLER(CMSG_GET_REMAINING_GAME_TIME, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL);
DEFINE_HANDLER(CMSG_GET_REMAINING_GAME_TIME, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleGetRemainingGameTime);
DEFINE_HANDLER(CMSG_GET_TROPHY_LIST, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleGetTrophyList);
DEFINE_HANDLER(CMSG_GET_UNDELETE_CHARACTER_COOLDOWN_STATUS, STATUS_AUTHED, PROCESS_THREADUNSAFE, &WorldSession::HandleGetUndeleteCooldownStatus);
DEFINE_HANDLER(CMSG_GET_VAS_ACCOUNT_CHARACTER_LIST, STATUS_AUTHED, PROCESS_THREADUNSAFE, &WorldSession::HandleGetVasAccountCharacterList);
DEFINE_HANDLER(CMSG_GET_VAS_TRANSFER_TARGET_REALM_LIST, STATUS_AUTHED, PROCESS_THREADUNSAFE, &WorldSession::HandleGetVasTransferTargetRealmList);
DEFINE_HANDLER(CMSG_GM_TICKET_ACKNOWLEDGE_SURVEY, STATUS_UNHANDLED, PROCESS_INPLACE, &WorldSession::Handle_NULL);
DEFINE_HANDLER(CMSG_GM_TICKET_ACKNOWLEDGE_SURVEY, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleGmTicketAcknowledgeSurvey);
DEFINE_HANDLER(CMSG_GM_TICKET_GET_CASE_STATUS, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleGMTicketGetCaseStatusOpcode);
DEFINE_HANDLER(CMSG_GM_TICKET_GET_SYSTEM_STATUS, STATUS_LOGGEDIN, PROCESS_INPLACE, &WorldSession::HandleGMTicketSystemStatusOpcode);
DEFINE_HANDLER(CMSG_GOSSIP_REFRESH_OPTIONS, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, &WorldSession::HandleGossipRefreshOptions);
+2
View File
@@ -1198,6 +1198,7 @@ namespace WorldPackets
class SupportTicketSubmitComplaint;
class BugReport;
class Complaint;
class GMTicketAcknowledgeSurvey;
}
namespace Token
@@ -1822,6 +1823,7 @@ public:
// GM Ticket opcodes
void HandleGMTicketGetCaseStatusOpcode(WorldPackets::Ticket::GMTicketGetCaseStatus& packet);
void HandleGMTicketSystemStatusOpcode(WorldPackets::Ticket::GMTicketGetSystemStatus& packet);
void HandleGmTicketAcknowledgeSurvey(WorldPackets::Ticket::GMTicketAcknowledgeSurvey& packet);
void HandleSubmitUserFeedback(WorldPackets::Ticket::SubmitUserFeedback& userFeedback);
void HandleSupportTicketSubmitComplaint(WorldPackets::Ticket::SupportTicketSubmitComplaint& packet);
void HandleBugReportOpcode(WorldPackets::Ticket::BugReport& bugReport);