diff options
Diffstat (limited to 'src/game/TradeHandler.cpp')
-rw-r--r-- | src/game/TradeHandler.cpp | 83 |
1 files changed, 83 insertions, 0 deletions
diff --git a/src/game/TradeHandler.cpp b/src/game/TradeHandler.cpp index e7c87cef3e6..b4cc62ba39a 100644 --- a/src/game/TradeHandler.cpp +++ b/src/game/TradeHandler.cpp @@ -17,6 +17,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + #include "Common.h" #include "WorldPacket.h" #include "WorldSession.h" @@ -28,6 +29,7 @@ #include "Item.h" #include "SocialMgr.h" #include "Language.h" + enum TradeStatus { TRADE_STATUS_BUSY = 0, @@ -54,9 +56,11 @@ enum TradeStatus TRADE_STATUS_TRIAL_ACCOUNT = 21, // Trial accounts can not perform that action TRADE_STATUS_ONLY_CONJURED = 22 // You can only trade conjured items... (cross realm BG related). }; + void WorldSession::SendTradeStatus(uint32 status) { WorldPacket data; + switch(status) { case TRADE_STATUS_BEGIN_TRADE: @@ -86,34 +90,42 @@ void WorldSession::SendTradeStatus(uint32 status) data << uint32(status); break; } + SendPacket(&data); } + void WorldSession::HandleIgnoreTradeOpcode(WorldPacket& /*recvPacket*/) { sLog.outDebug( "WORLD: Ignore Trade %u",_player->GetGUIDLow()); // recvPacket.print_storage(); } + void WorldSession::HandleBusyTradeOpcode(WorldPacket& /*recvPacket*/) { sLog.outDebug( "WORLD: Busy Trade %u",_player->GetGUIDLow()); // recvPacket.print_storage(); } + void WorldSession::SendUpdateTrade() { Item *item = NULL; + if( !_player || !_player->pTrader ) return; + // reset trade status if (_player->acceptTrade) { _player->acceptTrade = false; SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE); } + if (_player->pTrader->acceptTrade) { _player->pTrader->acceptTrade = false; _player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE); } + WorldPacket data(SMSG_TRADE_STATUS_EXTENDED, (100)); // guess size data << (uint8 ) 1; // can be different (only seen 0 and 1) data << (uint32) 0; // added in 2.4.0, this value must be equal to value from TRADE_STATUS_OPEN_WINDOW status packet (different value for different players to block multiple trades?) @@ -121,10 +133,13 @@ void WorldSession::SendUpdateTrade() data << (uint32) TRADE_SLOT_COUNT; // trade slots count/number?, = prev field in most cases data << (uint32) _player->pTrader->tradeGold; // trader gold data << (uint32) 0; // spell casted on lowest slot item + for(uint8 i = 0; i < TRADE_SLOT_COUNT; ++i) { item = (_player->pTrader->tradeItems[i] != NULL_SLOT ? _player->pTrader->GetItemByPos( _player->pTrader->tradeItems[i] ) : NULL); + data << (uint8) i; // trade slot number, if not specified, then end of packet + if(item) { data << (uint32) item->GetProto()->ItemId; // entry @@ -158,8 +173,10 @@ void WorldSession::SendUpdateTrade() } SendPacket(&data); } + //============================================================== // transfer the items to the players + void WorldSession::moveItems(Item* myItems[], Item* hisItems[]) { for(int i=0; i<TRADE_SLOT_TRADED_COUNT; ++i) @@ -184,6 +201,7 @@ void WorldSession::moveItems(Item* myItems[], Item* hisItems[]) myItems[i]->GetProto()->Name1,myItems[i]->GetEntry(),myItems[i]->GetCount(), _player->pTrader->GetName(),_player->pTrader->GetSession()->GetAccountId()); } + // store _player->pTrader->MoveItemToInventory( traderDst, myItems[i], true, true); } @@ -198,6 +216,7 @@ void WorldSession::moveItems(Item* myItems[], Item* hisItems[]) hisItems[i]->GetProto()->Name1,hisItems[i]->GetEntry(),hisItems[i]->GetCount(), _player->GetName(),_player->GetSession()->GetAccountId()); } + // store _player->MoveItemToInventory( playerDst, hisItems[i], true, true); } @@ -228,14 +247,18 @@ void WorldSession::moveItems(Item* myItems[], Item* hisItems[]) } } } + //============================================================== + void WorldSession::HandleAcceptTradeOpcode(WorldPacket& /*recvPacket*/) { Item *myItems[TRADE_SLOT_TRADED_COUNT] = { NULL, NULL, NULL, NULL, NULL, NULL }; Item *hisItems[TRADE_SLOT_TRADED_COUNT] = { NULL, NULL, NULL, NULL, NULL, NULL }; bool myCanCompleteTrade=true,hisCanCompleteTrade=true; + if ( !GetPlayer()->pTrader ) return; + // not accept case incorrect money amount if( _player->tradeGold > _player->GetMoney() ) { @@ -244,6 +267,7 @@ void WorldSession::HandleAcceptTradeOpcode(WorldPacket& /*recvPacket*/) _player->acceptTrade = false; return; } + // not accept case incorrect money amount if( _player->pTrader->tradeGold > _player->pTrader->GetMoney() ) { @@ -252,6 +276,7 @@ void WorldSession::HandleAcceptTradeOpcode(WorldPacket& /*recvPacket*/) _player->pTrader->acceptTrade = false; return; } + // not accept if some items now can't be trade (cheating) for(int i=0; i<TRADE_SLOT_TRADED_COUNT; ++i) { @@ -278,11 +303,13 @@ void WorldSession::HandleAcceptTradeOpcode(WorldPacket& /*recvPacket*/) } } } + _player->acceptTrade = true; if (_player->pTrader->acceptTrade ) { // inform partner client _player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_TRADE_ACCEPT); + // store items in local list and set 'in-trade' flag for(int i=0; i<TRADE_SLOT_TRADED_COUNT; ++i) { @@ -303,15 +330,18 @@ void WorldSession::HandleAcceptTradeOpcode(WorldPacket& /*recvPacket*/) hisItems[i]->SetInTrade(); } } + // test if item will fit in each inventory hisCanCompleteTrade = (_player->pTrader->CanStoreItems( myItems,TRADE_SLOT_TRADED_COUNT )== EQUIP_ERR_OK); myCanCompleteTrade = (_player->CanStoreItems( hisItems,TRADE_SLOT_TRADED_COUNT ) == EQUIP_ERR_OK); + // clear 'in-trade' flag for(int i=0; i<TRADE_SLOT_TRADED_COUNT; ++i) { if(myItems[i]) myItems[i]->SetInTrade(false); if(hisItems[i]) hisItems[i]->SetInTrade(false); } + // in case of missing space report error if(!myCanCompleteTrade) { @@ -329,6 +359,7 @@ void WorldSession::HandleAcceptTradeOpcode(WorldPacket& /*recvPacket*/) _player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE); return; } + // execute trade: 1. remove for(int i=0; i<TRADE_SLOT_TRADED_COUNT; ++i) { @@ -343,8 +374,10 @@ void WorldSession::HandleAcceptTradeOpcode(WorldPacket& /*recvPacket*/) _player->pTrader->MoveItemFromInventory(_player->pTrader->tradeItems[i] >> 8, _player->pTrader->tradeItems[i] & 255, true); } } + // execute trade: 2. store moveItems(myItems, hisItems); + // logging money if(sWorld.getConfig(CONFIG_GM_LOG_TRADE)) { @@ -363,20 +396,25 @@ void WorldSession::HandleAcceptTradeOpcode(WorldPacket& /*recvPacket*/) _player->GetName(),_player->GetSession()->GetAccountId()); } } + // update money _player->ModifyMoney( -int32(_player->tradeGold) ); _player->ModifyMoney(_player->pTrader->tradeGold ); _player->pTrader->ModifyMoney( -int32(_player->pTrader->tradeGold) ); _player->pTrader->ModifyMoney(_player->tradeGold ); + _player->ClearTrade(); _player->pTrader->ClearTrade(); + // desynchronized with the other saves here (SaveInventoryAndGoldToDB() not have own transaction guards) CharacterDatabase.BeginTransaction(); _player->SaveInventoryAndGoldToDB(); _player->pTrader->SaveInventoryAndGoldToDB(); CharacterDatabase.CommitTransaction(); + _player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_TRADE_COMPLETE); SendTradeStatus(TRADE_STATUS_TRADE_COMPLETE); + _player->pTrader->pTrader = NULL; _player->pTrader = NULL; } @@ -385,139 +423,175 @@ void WorldSession::HandleAcceptTradeOpcode(WorldPacket& /*recvPacket*/) _player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_TRADE_ACCEPT); } } + void WorldSession::HandleUnacceptTradeOpcode(WorldPacket& /*recvPacket*/) { if ( !GetPlayer()->pTrader ) return; + _player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE); _player->acceptTrade = false; } + void WorldSession::HandleBeginTradeOpcode(WorldPacket& /*recvPacket*/) { if(!_player->pTrader) return; + _player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_OPEN_WINDOW); _player->pTrader->ClearTrade(); + SendTradeStatus(TRADE_STATUS_OPEN_WINDOW); _player->ClearTrade(); } + void WorldSession::SendCancelTrade() { SendTradeStatus(TRADE_STATUS_TRADE_CANCELED); } + void WorldSession::HandleCancelTradeOpcode(WorldPacket& /*recvPacket*/) { // sended also after LOGOUT COMPLETE if(_player) // needed because STATUS_LOGGEDIN_OR_RECENTLY_LOGGOUT _player->TradeCancel(true); } + void WorldSession::HandleInitiateTradeOpcode(WorldPacket& recvPacket) { if (GetPlayer()->pTrader) return; + uint64 ID; + if (!GetPlayer()->isAlive()) { SendTradeStatus(TRADE_STATUS_YOU_DEAD); return; } + if (GetPlayer()->hasUnitState(UNIT_STAT_STUNNED)) { SendTradeStatus(TRADE_STATUS_YOU_STUNNED); return; } + if (isLogingOut()) { SendTradeStatus(TRADE_STATUS_YOU_LOGOUT); return; } + if (GetPlayer()->isInFlight()) { SendTradeStatus(TRADE_STATUS_TARGET_TO_FAR); return; } + recvPacket >> ID; + Player* pOther = ObjectAccessor::FindPlayer( ID ); + if (!pOther) { SendTradeStatus(TRADE_STATUS_NO_TARGET); return; } + if (pOther == GetPlayer() || pOther->pTrader) { SendTradeStatus(TRADE_STATUS_BUSY); return; } + if (!pOther->isAlive()) { SendTradeStatus(TRADE_STATUS_TARGET_DEAD); return; } + if (pOther->isInFlight()) { SendTradeStatus(TRADE_STATUS_TARGET_TO_FAR); return; } + if (pOther->hasUnitState(UNIT_STAT_STUNNED)) { SendTradeStatus(TRADE_STATUS_TARGET_STUNNED); return; } + if (pOther->GetSession()->isLogingOut()) { SendTradeStatus(TRADE_STATUS_TARGET_LOGOUT); return; } + if (pOther->GetSocial()->HasIgnore(GetPlayer()->GetGUIDLow())) { SendTradeStatus(TRADE_STATUS_IGNORE_YOU); return; } + if(!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_TRADE) && pOther->GetTeam() !=_player->GetTeam() ) { SendTradeStatus(TRADE_STATUS_WRONG_FACTION); return; } + if (!pOther->IsWithinDistInMap(_player,10.0f,false)) { SendTradeStatus(TRADE_STATUS_TARGET_TO_FAR); return; } + // OK start trade _player->pTrader = pOther; pOther->pTrader =_player; + WorldPacket data(SMSG_TRADE_STATUS, 12); data << (uint32) TRADE_STATUS_BEGIN_TRADE; data << (uint64)_player->GetGUID(); _player->pTrader->GetSession()->SendPacket(&data); } + void WorldSession::HandleSetTradeGoldOpcode(WorldPacket& recvPacket) { if(!_player->pTrader) return; + uint32 gold; + recvPacket >> gold; + // gold can be incorrect, but this is checked at trade finished. _player->tradeGold = gold; + _player->pTrader->GetSession()->SendUpdateTrade(); } + void WorldSession::HandleSetTradeItemOpcode(WorldPacket& recvPacket) { if(!_player->pTrader) return; + // send update uint8 tradeSlot; uint8 bag; uint8 slot; + recvPacket >> tradeSlot; recvPacket >> bag; recvPacket >> slot; + // invalid slot number if(tradeSlot >= TRADE_SLOT_COUNT) { SendTradeStatus(TRADE_STATUS_TRADE_CANCELED); return; } + // check cheating, can't fail with correct client operations Item* item = _player->GetItemByPos(bag,slot); if (!item || (tradeSlot!=TRADE_SLOT_NONTRADED && !item->CanBeTraded())) @@ -525,7 +599,9 @@ void WorldSession::HandleSetTradeItemOpcode(WorldPacket& recvPacket) SendTradeStatus(TRADE_STATUS_TRADE_CANCELED); return; } + uint16 pos = (bag << 8) | slot; + // prevent place single item into many trade slots using cheating and client bugs for(int i = 0; i < TRADE_SLOT_COUNT; ++i) { @@ -536,19 +612,26 @@ void WorldSession::HandleSetTradeItemOpcode(WorldPacket& recvPacket) return; } } + _player->tradeItems[tradeSlot] = pos; + _player->pTrader->GetSession()->SendUpdateTrade(); } + void WorldSession::HandleClearTradeItemOpcode(WorldPacket& recvPacket) { if(!_player->pTrader) return; + uint8 tradeSlot; recvPacket >> tradeSlot; + // invalid slot number if(tradeSlot >= TRADE_SLOT_COUNT) return; + _player->tradeItems[tradeSlot] = NULL_SLOT; + _player->pTrader->GetSession()->SendUpdateTrade(); } |