OregonCore  revision 3611e8a-git
Your Favourite TBC server
TradeHandler.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of the OregonCore Project. See AUTHORS file for Copyright information
3  *
4  * This program is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU General Public License as published by the
6  * Free Software Foundation; either version 2 of the License, or (at your
7  * option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12  * more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include "Common.h"
19 #include "WorldPacket.h"
20 #include "WorldSession.h"
21 #include "World.h"
22 #include "ObjectAccessor.h"
23 #include "Log.h"
24 #include "Opcodes.h"
25 #include "Player.h"
26 #include "Item.h"
27 #include "SocialMgr.h"
28 #include "Language.h"
29 
31 {
41  // 9?
45  // 13?
53  TRADE_STATUS_TRIAL_ACCOUNT = 21, // Trial accounts can not perform that action
54  TRADE_STATUS_ONLY_CONJURED = 22 // You can only trade conjured items... (cross realm BG related).
55 };
56 
58 {
59  WorldPacket data;
60 
61  switch (status)
62  {
64  data.Initialize(SMSG_TRADE_STATUS, 4 + 8);
65  data << uint32(status);
66  data << uint64(0);
67  break;
69  data.Initialize(SMSG_TRADE_STATUS, 4 + 4);
70  data << uint32(status);
71  data << uint32(0); // added in 2.4.0
72  break;
74  data.Initialize(SMSG_TRADE_STATUS, 4 + 4 + 1 + 4);
75  data << uint32(status);
76  data << uint32(0);
77  data << uint8(0);
78  data << uint32(0);
79  break;
81  data.Initialize(SMSG_TRADE_STATUS, 4 + 1);
82  data << uint32(status);
83  data << uint8(0);
84  break;
85  default:
87  data << uint32(status);
88  break;
89  }
90 
91  SendPacket(&data);
92 }
93 
95 {
96  sLog.outDebug("WORLD: Ignore Trade %u", _player->GetGUIDLow());
97  // recvPacket.print_storage();
98 }
99 
101 {
102  sLog.outDebug("WORLD: Busy Trade %u", _player->GetGUIDLow());
103  // recvPacket.print_storage();
104 }
105 
107 {
108  Item* item = NULL;
109 
110  if (!_player || !_player->pTrader)
111  return;
112 
113  // reset trade status
114  if (_player->acceptTrade)
115  {
116  _player->acceptTrade = false;
118  }
119 
121  {
122  _player->pTrader->acceptTrade = false;
124  }
125 
126  WorldPacket data(SMSG_TRADE_STATUS_EXTENDED, (100)); // guess size
127  data << (uint8) 1; // can be different (only seen 0 and 1)
128  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?)
129  data << uint32(TRADE_SLOT_COUNT); // trade slots count/number?, = next field in most cases
130  data << uint32(TRADE_SLOT_COUNT); // trade slots count/number?, = prev field in most cases
131  data << (uint32) _player->pTrader->tradeGold; // trader gold
132  data << (uint32) 0; // spell casted on lowest slot item
133 
134  for (uint8 i = 0; i < TRADE_SLOT_COUNT; ++i)
135  {
136  item = (_player->pTrader->tradeItems[i] != 0 ? _player->pTrader->GetItemByGuid( _player->pTrader->tradeItems[i] ) : NULL);
137 
138  data << (uint8) i; // trade slot number, if not specified, then end of packet
139 
140  if (item)
141  {
142  data << (uint32) item->GetProto()->ItemId; // entry
143  // display id
144  data << (uint32) item->GetProto()->DisplayInfoID;
145  // stack count
147  data << (uint32) 0; // probably gift=1, created_by=0?
148  // gift creator
151  for (uint8 j = 0; j < 3; ++j)
152  data << (uint32) 0; // enchantment id (permanent/gems?)
153  // creator
154  data << (uint64) item->GetUInt64Value(ITEM_FIELD_CREATOR);
155  data << (uint32) item->GetSpellCharges(); // charges
156  data << (uint32) item->GetItemSuffixFactor(); // SuffixFactor
157  // random properties id
158  data << (uint32) item->GetItemRandomPropertyId();
159  data << (uint32) item->GetProto()->LockID; // lock id
160  // max durability
162  // durability
164  }
165  else
166  {
167  for (uint8 j = 0; j < 18; j++)
168  data << uint32(0);
169  }
170  }
171  SendPacket(&data);
172 }
173 
174 //==============================================================
175 // transfer the items to the players
176 
177 void WorldSession::moveItems(Item* myItems[], Item* hisItems[])
178 {
179  for (int i = 0; i < TRADE_SLOT_TRADED_COUNT; i++)
180  {
181  ItemPosCountVec traderDst;
182  ItemPosCountVec playerDst;
183  bool traderCanTrade = (myItems[i] == NULL || _player->pTrader->CanStoreItem(NULL_BAG, NULL_SLOT, traderDst, myItems[i], false) == EQUIP_ERR_OK);
184  bool playerCanTrade = (hisItems[i] == NULL || _player->CanStoreItem(NULL_BAG, NULL_SLOT, playerDst, hisItems[i], false) == EQUIP_ERR_OK);
185  if (traderCanTrade && playerCanTrade)
186  {
187  // Ok, if trade item exists and can be stored
188  // If we trade in both directions we had to check, if the trade will work before we actually do it
189  // A roll back is not possible after we stored it
190  if (myItems[i])
191  {
192  // logging
193  sLog.outDebug("partner storing: %u", myItems[i]->GetGUIDLow());
195  {
196  sLog.outCommand(_player->GetSession()->GetAccountId(), "GM %s (Account: %u) trade: %s (Entry: %d Count: %u) to player: %s (Account: %u)",
198  myItems[i]->GetProto()->Name1, myItems[i]->GetEntry(), myItems[i]->GetCount(),
200  }
201 
202  // store
203  _player->pTrader->MoveItemToInventory(traderDst, myItems[i], true, true);
204  }
205 
206  if (hisItems[i])
207  {
208  // logging
209  sLog.outDebug("player storing: %u", hisItems[i]->GetGUIDLow());
211  {
212  sLog.outCommand(_player->pTrader->GetSession()->GetAccountId(), "GM %s (Account: %u) trade: %s (Entry: %d Count: %u) to player: %s (Account: %u)",
214  hisItems[i]->GetProto()->Name1, hisItems[i]->GetEntry(), hisItems[i]->GetCount(),
216  }
217 
218  // store
219  _player->MoveItemToInventory(playerDst, hisItems[i], true, true);
220  }
221  }
222  else
223  {
224  // in case of fatal error log error message
225  // return the already removed items to the original owner
226  if (myItems[i])
227  {
228  if (!traderCanTrade)
229  sLog.outError("trader can't store item: %u", myItems[i]->GetGUIDLow());
230  if (_player->CanStoreItem(NULL_BAG, NULL_SLOT, playerDst, myItems[i], false) == EQUIP_ERR_OK)
231  _player->MoveItemToInventory(playerDst, myItems[i], true, true);
232  else
233  sLog.outError("player can't take item back: %u", myItems[i]->GetGUIDLow());
234  }
235  // return the already removed items to the original owner
236  if (hisItems[i])
237  {
238  if (!playerCanTrade)
239  sLog.outError("player can't store item: %u", hisItems[i]->GetGUIDLow());
240  if (_player->pTrader->CanStoreItem(NULL_BAG, NULL_SLOT, traderDst, hisItems[i], false) == EQUIP_ERR_OK)
241  _player->pTrader->MoveItemToInventory(traderDst, hisItems[i], true, true);
242  else
243  sLog.outError("trader can't take item back: %u", hisItems[i]->GetGUIDLow());
244  }
245  }
246  }
247 }
248 
249 //==============================================================
250 
252 {
253  recvPacket.read_skip<uint32>();
254 
255  Item* myItems[TRADE_SLOT_TRADED_COUNT] = { NULL, NULL, NULL, NULL, NULL, NULL };
256  Item* hisItems[TRADE_SLOT_TRADED_COUNT] = { NULL, NULL, NULL, NULL, NULL, NULL };
257  bool myCanCompleteTrade = true, hisCanCompleteTrade = true;
258 
259  if (!GetPlayer()->pTrader)
260  return;
261 
262  // not accept case incorrect money amount
263  if (_player->tradeGold > _player->GetMoney())
264  {
267  _player->acceptTrade = false;
268  return;
269  }
270 
271  // not accept case incorrect money amount
273  {
276  _player->pTrader->acceptTrade = false;
277  return;
278  }
279 
280  // not accept if some items now can't be trade (cheating)
281  for (int i = 0; i < TRADE_SLOT_TRADED_COUNT; ++i)
282  {
283  if (_player->tradeItems[i] != 0)
284  {
285  if (Item* item = _player->GetItemByGuid(_player->tradeItems[i]))
286  {
287  if (!item->CanBeTraded())
288  {
290  return;
291  }
292  }
293  }
294  if (_player->pTrader->tradeItems[i] != 0)
295  {
297  {
298  if (!item->CanBeTraded())
299  {
301  return;
302  }
303  }
304  }
305  }
306 
307  _player->acceptTrade = true;
309  {
310  // inform partner client
312 
313  // store items in local list and set 'in-trade' flag
314  for (int i = 0; i < TRADE_SLOT_TRADED_COUNT; i++)
315  {
316  if (_player->tradeItems[i] != 0 )
317  {
318  //Can return NULL
319  myItems[i] = _player->GetItemByGuid( _player->tradeItems[i] );
320  if (myItems[i])
321  {
322  myItems[i]->SetInTrade();
323  sLog.outDebug("Player trade item bag: %u slot: %u", myItems[i]->GetBagSlot(), myItems[i]->GetSlot());
324  }
325  }
326  if (_player->pTrader->tradeItems[i] != 0)
327  {
328  //Can return NULL
329  hisItems[i] = _player->pTrader->GetItemByGuid(_player->pTrader->tradeItems[i]);
330  if (hisItems[i])
331  {
332  hisItems[i]->SetInTrade();
333  sLog.outDebug("Player trade item bag: %u slot: %u", hisItems[i]->GetBagSlot(), hisItems[i]->GetSlot());
334  }
335  }
336  }
337 
338  // test if item will fit in each inventory
339  hisCanCompleteTrade = (_player->pTrader->CanStoreItems(myItems, TRADE_SLOT_TRADED_COUNT) == EQUIP_ERR_OK);
340  myCanCompleteTrade = (_player->CanStoreItems(hisItems, TRADE_SLOT_TRADED_COUNT) == EQUIP_ERR_OK);
341 
342  // clear 'in-trade' flag
343  for (int i = 0; i < TRADE_SLOT_TRADED_COUNT; i++)
344  {
345  if (myItems[i]) myItems[i]->SetInTrade(false);
346  if (hisItems[i]) hisItems[i]->SetInTrade(false);
347  }
348 
349  // in case of missing space report error
350  if (!myCanCompleteTrade)
351  {
356  return;
357  }
358  else if (!hisCanCompleteTrade)
359  {
364  return;
365  }
366 
367  // execute trade: 1. remove
368  for (int i = 0; i < TRADE_SLOT_TRADED_COUNT; ++i)
369  {
370  Item* iPtr = NULL;
371  if (myItems[i])
372  {
375  _player->MoveItemFromInventory(iPtr->GetBagSlot(), iPtr->GetSlot(), true);
376  }
377  if (hisItems[i])
378  {
381  _player->pTrader->MoveItemFromInventory(iPtr->GetBagSlot(), iPtr->GetSlot(), true);
382  }
383  }
384 
385  // execute trade: 2. store
386  moveItems(myItems, hisItems);
387 
388  // logging money
389  if (sWorld.getConfig(CONFIG_GM_LOG_TRADE))
390  {
392  {
393  sLog.outCommand(_player->GetSession()->GetAccountId(), "GM %s (Account: %u) give money (Amount: %u) to player: %s (Account: %u)",
397  }
399  {
400  sLog.outCommand(_player->pTrader->GetSession()->GetAccountId(), "GM %s (Account: %u) give money (Amount: %u) to player: %s (Account: %u)",
404  }
405  }
406 
407  // update money
412 
413  _player->ClearTrade();
415 
416  // desynchronized with the other saves here (SaveInventoryAndGoldToDB() not have own transaction guards)
421 
424 
425  _player->pTrader->pTrader = NULL;
426  _player->pTrader = NULL;
427  }
428  else
430 }
431 
433 {
434  if (!GetPlayer()->pTrader)
435  return;
436 
438  _player->acceptTrade = false;
439 }
440 
442 {
443  if (!_player->pTrader)
444  return;
445 
448 
450  _player->ClearTrade();
451 }
452 
454 {
456 }
457 
459 {
460  // sended also after LOGOUT COMPLETE
461  if (_player) // needed because STATUS_LOGGEDIN_OR_RECENTLY_LOGGOUT
462  _player->TradeCancel(true);
463 }
464 
466 {
467  uint64 ID;
468  recvPacket >> ID;
469 
470  if (GetPlayer()->pTrader)
471  return;
472 
473  if (!GetPlayer()->IsAlive())
474  {
476  return;
477  }
478 
479  if (GetPlayer()->HasUnitState(UNIT_STATE_STUNNED))
480  {
482  return;
483  }
484 
485  if (isLogingOut())
486  {
488  return;
489  }
490 
491  if (GetPlayer()->isInFlight())
492  {
494  return;
495  }
496 
497  Player* pOther = ObjectAccessor::FindPlayer(ID);
498 
499  if (!pOther)
500  {
502  return;
503  }
504 
505  if (pOther == GetPlayer() || pOther->pTrader)
506  {
508  return;
509  }
510 
511  if (!pOther->IsAlive())
512  {
514  return;
515  }
516 
517  if (pOther->isInFlight())
518  {
520  return;
521  }
522 
523  if (pOther->HasUnitState(UNIT_STATE_STUNNED))
524  {
526  return;
527  }
528 
529  if (pOther->GetSession()->isLogingOut())
530  {
532  return;
533  }
534 
535  if (pOther->GetSocial()->HasIgnore(GetPlayer()->GetGUIDLow()))
536  {
538  return;
539  }
540 
541  if (!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_TRADE) && pOther->GetTeam() != _player->GetTeam())
542  {
544  return;
545  }
546 
547  if (pOther->GetDistance2d(_player) > 10.0f)
548  {
550  return;
551  }
552 
553  // OK start trade
554  _player->pTrader = pOther;
555  pOther->pTrader = _player;
556 
557  WorldPacket data(SMSG_TRADE_STATUS, 12);
559  data << (uint64)_player->GetGUID();
560  _player->pTrader->GetSession()->SendPacket(&data);
561 }
562 
564 {
565  uint32 gold;
566  recvPacket >> gold;
567 
568  if (!_player->pTrader)
569  return;
570 
571  // gold can be incorrect, but this is checked at trade finished.
572  _player->tradeGold = gold;
573 
575 }
576 
578 {
579  // send update
580  uint8 tradeSlot;
581  uint8 bag;
582  uint8 slot;
583 
584  recvPacket >> tradeSlot;
585  recvPacket >> bag;
586  recvPacket >> slot;
587 
588  if (!_player->pTrader)
589  return;
590 
591  // invalid slot number
592  if (tradeSlot >= TRADE_SLOT_COUNT)
593  {
595  return;
596  }
597 
598  // check cheating, can't fail with correct client operations
599  Item* item = _player->GetItemByPos(bag, slot);
600  if (!item || (tradeSlot != TRADE_SLOT_NONTRADED && !item->CanBeTraded()))
601  {
603  return;
604  }
605 
606  uint64 iGUID = item->GetGUID();
607 
608  // prevent place single item into many trade slots using cheating and client bugs
609  for (int i = 0; i < TRADE_SLOT_COUNT; ++i)
610  {
611  if (_player->tradeItems[i] == iGUID)
612  {
613  // cheating attempt
615  return;
616  }
617  }
618 
619  _player->tradeItems[tradeSlot] = iGUID;
620 
622 }
623 
625 {
626  uint8 tradeSlot;
627  recvPacket >> tradeSlot;
628 
629  if (!_player->pTrader)
630  return;
631 
632  // invalid slot number
633  if (tradeSlot >= TRADE_SLOT_COUNT)
634  return;
635 
636  _player->tradeItems[tradeSlot] = 0;
637 
639 }
640 
void SendNotification(const char *format,...) ATTR_PRINTF(2
uint32 GetCount() const
Definition: Item.h:251
ItemTemplate const * GetProto() const
Definition: Item.cpp:460
void MoveItemFromInventory(uint8 bag, uint8 slot, bool update)
Definition: Player.cpp:10687
const uint32 & GetUInt32Value(uint16 index) const
Definition: Object.h:228
int32 GetSpellCharges(uint8 index=0) const
Definition: Item.h:330
void read_skip()
Definition: ByteBuffer.h:276
uint64 tradeItems[TRADE_SLOT_COUNT]
Definition: Player.h:2839
uint32 GetItemSuffixFactor() const
Definition: Item.h:302
bool BeginTransaction()
Definition: Database.cpp:533
void HandleClearTradeItemOpcode(WorldPacket &recvPacket)
bool HasIgnore(uint32 ignore_guid)
Definition: SocialMgr.cpp:162
void SendCancelTrade()
const uint64 & GetUInt64Value(uint16 index) const
Definition: Object.h:234
void SetInTrade(bool b=true)
Definition: Item.h:238
#define sLog
Log class singleton.
Definition: Log.h:187
ACE_INT32 int32
Definition: Define.h:67
void Initialize(uint16 opcode, size_t newres=200)
Definition: WorldPacket.h:37
uint32 GetAccountId() const
Definition: WorldSession.h:100
Player * GetPlayer() const
Definition: WorldSession.h:104
uint32 GetGUIDLow() const
Definition: Object.h:160
uint8 CanStoreItems(Item **pItem, int count) const
Definition: Player.cpp:9530
int32 GetItemRandomPropertyId() const
Definition: Item.h:298
void SendPacket(WorldPacket const *packet)
bool CommitTransaction()
Definition: Database.cpp:551
void HandleIgnoreTradeOpcode(WorldPacket &recvPacket)
float GetDistance2d(const WorldObject *obj) const
Definition: Object.h:721
void TradeCancel(bool sendback)
Definition: Player.cpp:11638
void ClearTrade()
Definition: Player.cpp:11630
TradeStatus
void HandleUnacceptTradeOpcode(WorldPacket &recvPacket)
void HandleAcceptTradeOpcode(WorldPacket &recvPacket)
ACE_UINT8 uint8
Definition: Define.h:73
PlayerSocial * GetSocial()
Definition: Player.h:985
uint32 DisplayInfoID
Definition: Unit.h:297
uint8 CanStoreItem(uint8 bag, uint8 slot, ItemPosCountVec &dest, Item *pItem, bool swap=false) const
Definition: Player.h:1151
void HandleBusyTradeOpcode(WorldPacket &recvPacket)
void HandleSetTradeGoldOpcode(WorldPacket &recvPacket)
bool IsAlive() const
Definition: Unit.h:1433
Definition: Item.h:196
static Player * FindPlayer(uint64, bool force=false)
void SendTradeStatus(uint32 status)
bool acceptTrade
Definition: Player.h:2838
void HandleInitiateTradeOpcode(WorldPacket &recvPacket)
void HandleCancelTradeOpcode(WorldPacket &recvPacket)
void SetUInt64Value(uint16 index, const uint64 &value)
Definition: Object.cpp:798
uint8 GetSlot() const
Definition: Item.h:266
const char * GetName() const
Definition: Object.h:692
std::vector< ItemPosCount > ItemPosCountVec
Definition: Player.h:605
ACE_UINT64 uint64
Definition: Define.h:70
void HandleBeginTradeOpcode(WorldPacket &recvPacket)
void moveItems(Item *myItems[], Item *hisItems[])
void SendUpdateTrade()
Player * _player
Definition: WorldSession.h:729
uint32 GetMoney()
Definition: Player.h:1524
void HandleSetTradeItemOpcode(WorldPacket &recvPacket)
uint32 GetTeam() const
Definition: Player.h:2075
bool HasUnitState(const uint32 f) const
Definition: Unit.h:1002
bool isLogingOut() const
Definition: WorldSession.h:135
void SaveInventoryAndGoldToDB()
Definition: Player.cpp:16417
void MoveItemToInventory(ItemPosCountVec const &dest, Item *pItem, bool update, bool in_characterInventoryDB=false)
Definition: Player.cpp:10703
bool CanBeTraded() const
Definition: Item.cpp:738
uint32 GetEntry() const
Definition: Object.h:186
uint32 tradeGold
Definition: Player.h:2840
WorldSession * GetSession() const
Definition: Player.h:1959
#define sWorld
Definition: World.h:860
DatabaseType CharacterDatabase
Accessor to the character database.
Definition: Main.cpp:54
void uint32 GetSecurity() const
Definition: WorldSession.h:96
bool isInFlight() const
Definition: Unit.h:1308
void ModifyMoney(int32 d)
Definition: Player.h:1528
Player * pTrader
Definition: Player.h:2837
ACE_UINT32 uint32
Definition: Define.h:71
Item * GetItemByPos(uint16 pos) const
Definition: Player.cpp:8612
uint8 GetBagSlot() const
Definition: Item.cpp:728
Definition: Player.h:922
Item * GetItemByGuid(uint64 guid) const
Definition: Player.cpp:8583
const uint64 & GetGUID() const
Definition: Object.h:156
uint32 GetEnchantmentId(EnchantmentSlot slot) const
Definition: Item.h:313