feat(Core): Add clustering support (#16832)
Co-authored-by: 3kynox <thierry.prost@gmail.com> Co-authored-by: nox <nox@noxen.net> Co-authored-by: Ludwig <sudlud@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
f23ac046ab
commit
6f0ba8e896
@@ -18,6 +18,8 @@
|
||||
#include "ChannelMgr.h"
|
||||
#include "ObjectMgr.h" // for normalizePlayerName
|
||||
#include "Player.h"
|
||||
#include "Language.h"
|
||||
#include "TC9Sidecar.h"
|
||||
#include <cctype>
|
||||
|
||||
void WorldSession::HandleJoinChannel(WorldPacket& recvPacket)
|
||||
@@ -38,6 +40,23 @@ void WorldSession::HandleJoinChannel(WorldPacket& recvPacket)
|
||||
AreaTableEntry const* zone = sAreaTableStore.LookupEntry(GetPlayer()->GetZoneId());
|
||||
if (!zone || !GetPlayer()->CanJoinConstantChannelInZone(channel, zone))
|
||||
return;
|
||||
|
||||
// Cluster mode rebuilds the localized channel name so nodes agree on it;
|
||||
// stock servers keep the client-supplied name.
|
||||
if (sToCloud9Sidecar->ClusterModeEnabled())
|
||||
{
|
||||
auto const locale = GetSessionDbcLocale();
|
||||
std::string const& zoneName = zone->area_name[locale];
|
||||
std::string const cityName = sObjectMgr->GetAcoreStringForDBCLocale(LANG_CHANNEL_CITY);
|
||||
char const* nameExt = (channel->flags & CHANNEL_DBC_FLAG_CITY_ONLY) ? cityName.c_str() : zoneName.c_str();
|
||||
|
||||
std::array<char, 128> buffer{};
|
||||
if (char const* pattern = channel->pattern[locale])
|
||||
{
|
||||
std::snprintf(buffer.data(), buffer.size(), pattern, nameExt);
|
||||
channelName = buffer.data();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (channelName.empty())
|
||||
|
||||
@@ -54,6 +54,7 @@
|
||||
#include "SpellAuraEffects.h"
|
||||
#include "SpellAuras.h"
|
||||
#include "StringConvert.h"
|
||||
#include "TC9Sidecar.h"
|
||||
#include "Tokenize.h"
|
||||
#include "Transport.h"
|
||||
#include "Util.h"
|
||||
@@ -81,125 +82,126 @@ bool LoginQueryHolder::Initialize()
|
||||
SetSize(MAX_PLAYER_LOGIN_QUERY);
|
||||
|
||||
bool res = true;
|
||||
ObjectGuid::LowType lowGuid = m_guid.GetCounter();
|
||||
uint64 rawGUID = m_guid.GetRawValue();
|
||||
|
||||
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER);
|
||||
stmt->SetData(0, lowGuid);
|
||||
stmt->SetData(0, rawGUID);
|
||||
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_FROM, stmt);
|
||||
|
||||
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_AURAS);
|
||||
stmt->SetData(0, lowGuid);
|
||||
stmt->SetData(0, rawGUID);
|
||||
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_AURAS, stmt);
|
||||
|
||||
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_SPELL);
|
||||
stmt->SetData(0, lowGuid);
|
||||
stmt->SetData(0, rawGUID);
|
||||
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_SPELLS, stmt);
|
||||
|
||||
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_QUESTSTATUS);
|
||||
stmt->SetData(0, lowGuid);
|
||||
stmt->SetData(0, rawGUID);
|
||||
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_QUEST_STATUS, stmt);
|
||||
|
||||
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_DAILYQUESTSTATUS);
|
||||
stmt->SetData(0, lowGuid);
|
||||
stmt->SetData(0, rawGUID);
|
||||
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_DAILY_QUEST_STATUS, stmt);
|
||||
|
||||
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_WEEKLYQUESTSTATUS);
|
||||
stmt->SetData(0, lowGuid);
|
||||
stmt->SetData(0, rawGUID);
|
||||
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_WEEKLY_QUEST_STATUS, stmt);
|
||||
|
||||
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_MONTHLYQUESTSTATUS);
|
||||
stmt->SetData(0, lowGuid);
|
||||
stmt->SetData(0, rawGUID);
|
||||
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_MONTHLY_QUEST_STATUS, stmt);
|
||||
|
||||
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_SEASONALQUESTSTATUS);
|
||||
stmt->SetData(0, lowGuid);
|
||||
stmt->SetData(0, rawGUID);
|
||||
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_SEASONAL_QUEST_STATUS, stmt);
|
||||
|
||||
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_REPUTATION);
|
||||
stmt->SetData(0, lowGuid);
|
||||
stmt->SetData(0, rawGUID);
|
||||
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_REPUTATION, stmt);
|
||||
|
||||
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_INVENTORY);
|
||||
stmt->SetData(0, lowGuid);
|
||||
stmt->SetData(0, rawGUID);
|
||||
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_INVENTORY, stmt);
|
||||
|
||||
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_ACTIONS);
|
||||
stmt->SetData(0, lowGuid);
|
||||
stmt->SetData(0, rawGUID);
|
||||
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_ACTIONS, stmt);
|
||||
|
||||
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_MAIL);
|
||||
stmt->SetData(0, lowGuid);
|
||||
stmt->SetData(0, rawGUID);
|
||||
|
||||
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_MAILS, stmt);
|
||||
|
||||
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_MAILITEMS);
|
||||
stmt->SetData(0, lowGuid);
|
||||
stmt->SetData(0, rawGUID);
|
||||
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_MAIL_ITEMS, stmt);
|
||||
|
||||
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_SOCIALLIST);
|
||||
stmt->SetData(0, lowGuid);
|
||||
stmt->SetData(0, rawGUID);
|
||||
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_SOCIAL_LIST, stmt);
|
||||
|
||||
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_HOMEBIND);
|
||||
stmt->SetData(0, lowGuid);
|
||||
stmt->SetData(0, rawGUID);
|
||||
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_HOME_BIND, stmt);
|
||||
|
||||
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_SPELLCOOLDOWNS);
|
||||
stmt->SetData(0, lowGuid);
|
||||
stmt->SetData(0, rawGUID);
|
||||
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_SPELL_COOLDOWNS, stmt);
|
||||
|
||||
if (sWorld->getBoolConfig(CONFIG_DECLINED_NAMES_USED))
|
||||
{
|
||||
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_DECLINEDNAMES);
|
||||
stmt->SetData(0, lowGuid);
|
||||
stmt->SetData(0, rawGUID);
|
||||
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_DECLINED_NAMES, stmt);
|
||||
}
|
||||
|
||||
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_ACHIEVEMENTS);
|
||||
stmt->SetData(0, lowGuid);
|
||||
stmt->SetData(0, rawGUID);
|
||||
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_ACHIEVEMENTS, stmt);
|
||||
|
||||
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_CRITERIAPROGRESS);
|
||||
stmt->SetData(0, lowGuid);
|
||||
stmt->SetData(0, rawGUID);
|
||||
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_CRITERIA_PROGRESS, stmt);
|
||||
|
||||
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_EQUIPMENTSETS);
|
||||
stmt->SetData(0, lowGuid);
|
||||
stmt->SetData(0, rawGUID);
|
||||
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_EQUIPMENT_SETS, stmt);
|
||||
|
||||
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_ENTRY_POINT);
|
||||
stmt->SetData(0, lowGuid);
|
||||
stmt->SetData(0, rawGUID);
|
||||
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_ENTRY_POINT, stmt);
|
||||
|
||||
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_GLYPHS);
|
||||
stmt->SetData(0, lowGuid);
|
||||
stmt->SetData(0, rawGUID);
|
||||
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_GLYPHS, stmt);
|
||||
|
||||
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_TALENTS);
|
||||
stmt->SetData(0, lowGuid);
|
||||
stmt->SetData(0, rawGUID);
|
||||
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_TALENTS, stmt);
|
||||
|
||||
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PLAYER_ACCOUNT_DATA);
|
||||
stmt->SetData(0, lowGuid);
|
||||
stmt->SetData(0, rawGUID);
|
||||
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_ACCOUNT_DATA, stmt);
|
||||
|
||||
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_SKILLS);
|
||||
stmt->SetData(0, lowGuid);
|
||||
stmt->SetData(0, rawGUID);
|
||||
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_SKILLS, stmt);
|
||||
|
||||
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_RANDOMBG);
|
||||
stmt->SetData(0, lowGuid);
|
||||
stmt->SetData(0, rawGUID);
|
||||
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_RANDOM_BG, stmt);
|
||||
|
||||
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_BANNED);
|
||||
stmt->SetData(0, lowGuid);
|
||||
stmt->SetData(0, rawGUID);
|
||||
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_BANNED, stmt);
|
||||
|
||||
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_QUESTSTATUSREW);
|
||||
stmt->SetData(0, lowGuid);
|
||||
stmt->SetData(0, rawGUID);
|
||||
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_QUEST_STATUS_REW, stmt);
|
||||
|
||||
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_BREW_OF_THE_MONTH);
|
||||
stmt->SetData(0, lowGuid);
|
||||
stmt->SetData(0, rawGUID);
|
||||
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_BREW_OF_THE_MONTH, stmt);
|
||||
|
||||
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_ACCOUNT_INSTANCELOCKTIMES);
|
||||
@@ -207,19 +209,19 @@ bool LoginQueryHolder::Initialize()
|
||||
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_INSTANCE_LOCK_TIMES, stmt);
|
||||
|
||||
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CORPSE_LOCATION);
|
||||
stmt->SetData(0, lowGuid);
|
||||
stmt->SetData(0, rawGUID);
|
||||
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_CORPSE_LOCATION, stmt);
|
||||
|
||||
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_SETTINGS);
|
||||
stmt->SetData(0, lowGuid);
|
||||
stmt->SetData(0, rawGUID);
|
||||
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_CHARACTER_SETTINGS, stmt);
|
||||
|
||||
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_PETS);
|
||||
stmt->SetData(0, lowGuid);
|
||||
stmt->SetData(0, rawGUID);
|
||||
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_PET_SLOTS, stmt);
|
||||
|
||||
stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_ACHIEVEMENT_OFFLINE_UPDATES);
|
||||
stmt->SetData(0, lowGuid);
|
||||
stmt->SetData(0, rawGUID);
|
||||
res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_OFFLINE_ACHIEVEMENTS_UPDATES, stmt);
|
||||
|
||||
return res;
|
||||
@@ -699,7 +701,9 @@ void WorldSession::HandlePlayerLoginOpcode(WorldPacket& recvData)
|
||||
ObjectGuid playerGuid;
|
||||
recvData >> playerGuid;
|
||||
|
||||
if (!IsLegitCharacterForAccount(playerGuid))
|
||||
// The ownership check is delegated to the gateway in cluster mode, but a
|
||||
// non-player GUID is invalid regardless of who authenticated the session.
|
||||
if (!playerGuid.IsPlayer() || (!sToCloud9Sidecar->ClusterModeEnabled() && !IsLegitCharacterForAccount(playerGuid)))
|
||||
{
|
||||
LOG_ERROR("network", "Account ({}) can't login with that character ({}).", GetAccountId(), playerGuid.ToString());
|
||||
KickPlayer("Account can't login with this character");
|
||||
@@ -847,20 +851,28 @@ void WorldSession::HandlePlayerLoginFromDB(LoginQueryHolder const& holder)
|
||||
chH.PSendSysMessage("{}", GitRevision::GetFullVersion());
|
||||
}
|
||||
|
||||
if (uint32 guildId = sCharacterCache->GetCharacterGuildIdByGuid(pCurrChar->GetGUID()))
|
||||
if (!sToCloud9Sidecar->ClusterModeEnabled())
|
||||
{
|
||||
Guild* guild = sGuildMgr->GetGuildById(guildId);
|
||||
Guild::Member const* member = guild ? guild->GetMember(pCurrChar->GetGUID()) : nullptr;
|
||||
if (member)
|
||||
if (uint32 guildId = sCharacterCache->GetCharacterGuildIdByGuid(pCurrChar->GetGUID()))
|
||||
{
|
||||
pCurrChar->SetInGuild(guildId);
|
||||
pCurrChar->SetRank(member->GetRankId());
|
||||
guild->SendLoginInfo(this);
|
||||
Guild* guild = sGuildMgr->GetGuildById(guildId);
|
||||
Guild::Member const* member = guild ? guild->GetMember(pCurrChar->GetGUID()) : nullptr;
|
||||
if (member)
|
||||
{
|
||||
pCurrChar->SetInGuild(guildId);
|
||||
pCurrChar->SetRank(member->GetRankId());
|
||||
guild->SendLoginInfo(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_ERROR("network.opcode", "Player {} ({}) marked as member of not existing guild (id: {}), removing guild membership for player.",
|
||||
pCurrChar->GetName(), pCurrChar->GetGUID().ToString(), guildId);
|
||||
pCurrChar->SetInGuild(0);
|
||||
pCurrChar->SetRank(0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_ERROR("network.opcode", "Player {} ({}) marked as member of not existing guild (id: {}), removing guild membership for player.",
|
||||
pCurrChar->GetName(), pCurrChar->GetGUID().ToString(), guildId);
|
||||
pCurrChar->SetInGuild(0);
|
||||
pCurrChar->SetRank(0);
|
||||
}
|
||||
@@ -914,9 +926,12 @@ void WorldSession::HandlePlayerLoginFromDB(LoginQueryHolder const& holder)
|
||||
|
||||
pCurrChar->SendInitialPacketsAfterAddToMap();
|
||||
|
||||
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_ONLINE);
|
||||
stmt->SetData(0, pCurrChar->GetGUID().GetCounter());
|
||||
CharacterDatabase.Execute(stmt);
|
||||
if (!sToCloud9Sidecar->ClusterModeEnabled())
|
||||
{
|
||||
CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_ONLINE);
|
||||
stmt->SetData(0, pCurrChar->GetGUID().GetCounter());
|
||||
CharacterDatabase.Execute(stmt);
|
||||
}
|
||||
|
||||
LoginDatabasePreparedStatement* loginStmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_ACCOUNT_ONLINE);
|
||||
loginStmt->SetData(0, realm.Id.Realm);
|
||||
|
||||
@@ -713,6 +713,18 @@ namespace Acore
|
||||
|
||||
void WorldSession::HandleTextEmoteOpcode(WorldPacket& recvData)
|
||||
{
|
||||
uint32 text_emote;
|
||||
recvData >> text_emote;
|
||||
|
||||
constexpr uint32 readyEmote = 126;
|
||||
|
||||
// Handle confirmation redirect when the player types "/ready" after the new node becomes available for redirection.
|
||||
if (text_emote == readyEmote && GetPlayer()->GetMap()->IsPlayerRedirectKickTimerActive())
|
||||
{
|
||||
HandleTC9PrepareForRedirect(recvData);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!GetPlayer()->IsAlive())
|
||||
return;
|
||||
|
||||
@@ -728,10 +740,9 @@ void WorldSession::HandleTextEmoteOpcode(WorldPacket& recvData)
|
||||
if (GetPlayer()->IsSpectator())
|
||||
return;
|
||||
|
||||
uint32 text_emote, emoteNum;
|
||||
uint32 emoteNum;
|
||||
ObjectGuid guid;
|
||||
|
||||
recvData >> text_emote;
|
||||
recvData >> emoteNum;
|
||||
recvData >> guid;
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include "SocialMgr.h"
|
||||
#include "Spell.h"
|
||||
#include "SpellMgr.h"
|
||||
#include "TC9Sidecar.h"
|
||||
#include "World.h"
|
||||
#include "WorldPacket.h"
|
||||
#include "WorldSession.h"
|
||||
@@ -505,6 +506,79 @@ void WorldSession::HandleAcceptTradeOpcode(WorldPacket& /*recvPacket*/)
|
||||
}
|
||||
}
|
||||
|
||||
bool needsCrossrealmHandling = sToCloud9Sidecar->IsCrossrealm() && _player->GetGUID().GetRealmID() != trader->GetGUID().GetRealmID();
|
||||
|
||||
// Create new items for crossrealm usage.
|
||||
if (needsCrossrealmHandling)
|
||||
{
|
||||
CharacterDatabasePreparedStatement* stmt = nullptr;
|
||||
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
|
||||
stmt = CharacterDatabase.GetPreparedStatement(CHAR_NO_OP_PROVIDE_REALM_CONTEXT);
|
||||
stmt->SetData(0, _player->GetGUID().GetRealmID());
|
||||
trans->Append(stmt);
|
||||
|
||||
for (uint8 i = 0; i < TRADE_SLOT_TRADED_COUNT; i++)
|
||||
{
|
||||
if (myItems[i])
|
||||
{
|
||||
Item* newItem = myItems[i]->CloneItem(myItems[i]->GetCount(), trader);
|
||||
if (!newItem)
|
||||
{
|
||||
ItemPosCountVec playerDst;
|
||||
if (_player->CanStoreItem(NULL_BAG, NULL_SLOT, playerDst, myItems[i], false) == EQUIP_ERR_OK)
|
||||
_player->MoveItemToInventory(playerDst, myItems[i], true, true);
|
||||
|
||||
// Should we handle else statement here?
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE);
|
||||
stmt->SetData(0, myItems[i]->GetGUID().GetCounter());
|
||||
trans->Append(stmt);
|
||||
|
||||
delete myItems[i];
|
||||
|
||||
myItems[i] = newItem;
|
||||
}
|
||||
}
|
||||
CharacterDatabase.CommitTransaction(trans);
|
||||
|
||||
trans = CharacterDatabase.BeginTransaction();
|
||||
|
||||
stmt = CharacterDatabase.GetPreparedStatement(CHAR_NO_OP_PROVIDE_REALM_CONTEXT);
|
||||
stmt->SetData(0, trader->GetGUID().GetRealmID());
|
||||
trans->Append(stmt);
|
||||
|
||||
for (uint8 i = 0; i < TRADE_SLOT_TRADED_COUNT; i++)
|
||||
{
|
||||
if (hisItems[i])
|
||||
{
|
||||
Item* newItem = hisItems[i]->CloneItem(hisItems[i]->GetCount(), _player);
|
||||
if (!newItem)
|
||||
{
|
||||
ItemPosCountVec playerDst;
|
||||
if (trader->CanStoreItem(NULL_BAG, NULL_SLOT, playerDst, hisItems[i], false) == EQUIP_ERR_OK)
|
||||
trader->MoveItemToInventory(playerDst, hisItems[i], true, true);
|
||||
|
||||
// Should we handle else statement here?
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ITEM_INSTANCE);
|
||||
stmt->SetData(0, hisItems[i]->GetGUID().GetCounter());
|
||||
trans->Append(stmt);
|
||||
|
||||
delete hisItems[i];
|
||||
|
||||
hisItems[i] = newItem;
|
||||
}
|
||||
}
|
||||
CharacterDatabase.CommitTransaction(trans);
|
||||
|
||||
}
|
||||
|
||||
// execute trade: 2. store
|
||||
moveItems(myItems, hisItems);
|
||||
|
||||
@@ -575,11 +649,25 @@ void WorldSession::HandleAcceptTradeOpcode(WorldPacket& /*recvPacket*/)
|
||||
delete trader->m_trade;
|
||||
trader->m_trade = nullptr;
|
||||
|
||||
// desynchronized with the other saves here (SaveInventoryAndGoldToDB() not have own transaction guards)
|
||||
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
|
||||
_player->SaveInventoryAndGoldToDB(trans);
|
||||
trader->SaveInventoryAndGoldToDB(trans);
|
||||
CharacterDatabase.CommitTransaction(trans);
|
||||
// We can't use single transaction with different databases.
|
||||
if (needsCrossrealmHandling)
|
||||
{
|
||||
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
|
||||
_player->SaveInventoryAndGoldToDB(trans);
|
||||
CharacterDatabase.CommitTransaction(trans);
|
||||
|
||||
trans = CharacterDatabase.BeginTransaction();
|
||||
trader->SaveInventoryAndGoldToDB(trans);
|
||||
CharacterDatabase.CommitTransaction(trans);
|
||||
}
|
||||
else
|
||||
{
|
||||
// desynchronized with the other saves here (SaveInventoryAndGoldToDB() not have own transaction guards)
|
||||
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
|
||||
_player->SaveInventoryAndGoldToDB(trans);
|
||||
trader->SaveInventoryAndGoldToDB(trans);
|
||||
CharacterDatabase.CommitTransaction(trans);
|
||||
}
|
||||
|
||||
info.Status = TRADE_STATUS_TRADE_COMPLETE;
|
||||
trader->GetSession()->SendTradeStatus(info);
|
||||
|
||||
Reference in New Issue
Block a user