Project SkyFire Core
SkyFire 5.4.8 server core API documentation
Loading...
Searching...
No Matches
TradeHandler.cpp
Go to the documentation of this file.
1/*
2* This file is part of Project SkyFire https://www.projectskyfire.org.
3* See LICENSE.md file for Copyright information
4*/
5
6#include "AccountMgr.h"
7#include "Common.h"
8#include "Item.h"
9#include "Language.h"
10#include "Log.h"
11#include "ObjectAccessor.h"
12#include "Opcodes.h"
13#include "Player.h"
14#include "SocialMgr.h"
15#include "Spell.h"
16#include "World.h"
17#include "WorldPacket.h"
18#include "WorldSession.h"
19
21{
22 WorldPacket data;
23
24 data.Initialize(SMSG_TRADE_STATUS, 1 + 4 + 4);
25 data.WriteBit(0); // unk bit, usually 0
26 data.WriteBits(status, 5);
27
28 switch (status)
29 {
31 data.WriteBits(0, 8); // zero guid
32 data.FlushBits();
33 break;
35 data.FlushBits();
36 data << uint32(0); // unk
37 break;
39 data.WriteBit(0); // unk
40 data.FlushBits();
41 data << uint32(0); // unk
42 data << uint32(0); // unk
43 break;
46 data.FlushBits();
47 data << uint8(0); // unk
48 break;
49 case TRADE_STATUS_NOT_ENOUGH_CURRENCY: // Not implemented
50 case TRADE_STATUS_CURRENCY_NOT_TRADABLE: // Not implemented
51 data.FlushBits();
52 data << uint32(0); // unk
53 data << uint32(0); // unk
54 default:
55 data.FlushBits();
56 break;
57 }
58
59 SendPacket(&data);
60}
61
63{
64 SF_LOG_DEBUG("network", "WORLD: Ignore Trade %u", _player->GetGUIDLow());
65 _player->TradeCancel(true, TRADE_STATUS_PLAYER_IGNORED);
66}
67
69{
70 SF_LOG_DEBUG("network", "WORLD: Busy Trade %u", _player->GetGUIDLow());
71 _player->TradeCancel(true, TRADE_STATUS_PLAYER_BUSY);
72}
73
74void WorldSession::SendUpdateTrade(bool trader_data /*= true*/)
75{
76 TradeData* view_trade = trader_data ? _player->GetTradeData()->GetTraderData() : _player->GetTradeData();
77
78 ByteBuffer itemData(7 * 2 + 7 * 4 + 3 * 4 + 3 * 4 + 1);
79
80 uint8 count = 0;
81 for (uint8 i = 0; i < TRADE_SLOT_COUNT; ++i)
82 if (view_trade->GetItem(TradeSlots(i)))
83 ++count;
84
85 WorldPacket data(SMSG_TRADE_STATUS_EXTENDED, 4 * 6 + 8 + 1 + 3 + count * 70);
86 data << uint32(0); // this value must be equal to value from TRADE_STATUS_INITIATED status packet (different value for different players to block multiple trades?)
87 data << uint32(0); // unk 2
88 data << uint32(view_trade->GetSpell()); // spell casted on lowest slot item
89 data << uint8(trader_data); // 1 means traders data, 0 means own
90 data << uint64(view_trade->GetMoney()); // trader gold
91 data << uint32(TRADE_SLOT_COUNT); // trade slots count/number?, = next field in most cases
92 data << uint32(0); // unk 5
93 data << uint32(TRADE_SLOT_COUNT); // trade slots count/number?, = prev field in most cases
94 data.WriteBits(count, 20);
95
96 for (uint8 i = 0; i < TRADE_SLOT_COUNT; ++i)
97 {
98 Item* item = view_trade->GetItem(TradeSlots(i));
99 if (!item)
100 continue;
101
102 ObjectGuid giftCreatorGuid = item->GetUInt64Value(ITEM_FIELD_GIFT_CREATOR);
103 ObjectGuid creatorGuid = item->GetUInt64Value(ITEM_FIELD_CREATOR);
104
105 bool notWrapped = data.WriteBit(!item->HasFlag(ITEM_FIELD_DYNAMIC_FLAGS, ITEM_FLAG_WRAPPED));
106 data.WriteBit(giftCreatorGuid[2]);
107
108 if (notWrapped)
109 {
110 data.WriteBit(creatorGuid[3]);
111 data.WriteBit(creatorGuid[5]);
112 data.WriteBit(creatorGuid[1]);
113 data.WriteBit(creatorGuid[6]);
114 data.WriteBit(creatorGuid[0]);
115 data.WriteBit(item->GetTemplate()->LockID != 0);
116 data.WriteBit(creatorGuid[4]);
117 data.WriteBit(creatorGuid[7]);
118 data.WriteBit(creatorGuid[2]);
119
120 itemData.WriteByteSeq(creatorGuid[3]);
121
123 itemData << uint32(0);
125
126 itemData.WriteByteSeq(creatorGuid[1]);
127 itemData.WriteByteSeq(creatorGuid[5]);
128 itemData.WriteByteSeq(creatorGuid[7]);
129 itemData.WriteByteSeq(creatorGuid[6]);
130 itemData.WriteByteSeq(creatorGuid[0]);
131
133 itemData << uint32(item->GetUInt32Value(ITEM_FIELD_DURABILITY)); // ok
134
135 itemData.WriteByteSeq(creatorGuid[2]);
136
137 for (uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT + MAX_GEM_SOCKETS /*3*/; ++enchant_slot)
138 itemData << uint32(item->GetEnchantmentId(EnchantmentSlot(enchant_slot)));
139
140 itemData << uint32(item->GetItemRandomPropertyId());
141 itemData << uint32(item->GetSpellCharges());
142 itemData << uint32(item->GetItemSuffixFactor());
143
144 itemData.WriteByteSeq(creatorGuid[4]);
145 }
146
147 data.WriteBit(giftCreatorGuid[0]);
148 data.WriteBit(giftCreatorGuid[4]);
149 data.WriteBit(giftCreatorGuid[7]);
150 data.WriteBit(giftCreatorGuid[3]);
151 data.WriteBit(giftCreatorGuid[6]);
152 data.WriteBit(giftCreatorGuid[1]);
153 data.WriteBit(giftCreatorGuid[5]);
154
155 itemData.WriteByteSeq(giftCreatorGuid[4]);
156
157 itemData << uint8(i);
158
159 itemData.WriteByteSeq(giftCreatorGuid[5]);
160 itemData.WriteByteSeq(giftCreatorGuid[1]);
161 itemData.WriteByteSeq(giftCreatorGuid[2]);
162 itemData.WriteByteSeq(giftCreatorGuid[3]);
163
164 itemData << uint32(item->GetTemplate()->ItemId);
165
166 itemData.WriteByteSeq(giftCreatorGuid[7]);
167 itemData.WriteByteSeq(giftCreatorGuid[0]);
168
169 itemData << uint32(item->GetCount());
170
171 itemData.WriteByteSeq(giftCreatorGuid[6]);
172 }
173
174 data.FlushBits();
175 data.append(itemData);
176
177 SendPacket(&data);
178}
179
180//==============================================================
181// transfer the items to the players
182
183void WorldSession::moveItems(Item* myItems[], Item* hisItems[])
184{
185 Player* trader = _player->GetTrader();
186 if (!trader)
187 return;
188
189 for (uint8 i = 0; i < TRADE_SLOT_TRADED_COUNT; ++i)
190 {
191 ItemPosCountVec traderDst;
192 ItemPosCountVec playerDst;
193 bool traderCanTrade = (myItems[i] == NULL || trader->CanStoreItem(NULL_BAG, NULL_SLOT, traderDst, myItems[i], false) == EQUIP_ERR_OK);
194 bool playerCanTrade = (hisItems[i] == NULL || _player->CanStoreItem(NULL_BAG, NULL_SLOT, playerDst, hisItems[i], false) == EQUIP_ERR_OK);
195 if (traderCanTrade && playerCanTrade)
196 {
197 // Ok, if trade item exists and can be stored
198 // If we trade in both directions we had to check, if the trade will work before we actually do it
199 // A roll back is not possible after we stored it
200 if (myItems[i])
201 {
202 // logging
203 SF_LOG_DEBUG("network", "partner storing: %u", myItems[i]->GetGUIDLow());
205 {
206 sLog->outCommand(_player->GetSession()->GetAccountId(), "GM %s (Account: %u) trade: %s (Entry: %d Count: %u) to player: %s (Account: %u)",
207 _player->GetName().c_str(), _player->GetSession()->GetAccountId(),
208 myItems[i]->GetTemplate()->Name1.c_str(), myItems[i]->GetEntry(), myItems[i]->GetCount(),
209 trader->GetName().c_str(), trader->GetSession()->GetAccountId());
210 }
211
212 // adjust time (depends on /played)
213 if (myItems[i]->HasFlag(ITEM_FIELD_DYNAMIC_FLAGS, ITEM_FLAG_BOP_TRADEABLE))
214 myItems[i]->SetUInt32Value(ITEM_FIELD_CREATE_PLAYED_TIME, trader->GetTotalPlayedTime() - (_player->GetTotalPlayedTime() - myItems[i]->GetUInt32Value(ITEM_FIELD_CREATE_PLAYED_TIME)));
215 // store
216 trader->MoveItemToInventory(traderDst, myItems[i], true, true);
217 }
218 if (hisItems[i])
219 {
220 // logging
221 SF_LOG_DEBUG("network", "player storing: %u", hisItems[i]->GetGUIDLow());
223 {
224 sLog->outCommand(trader->GetSession()->GetAccountId(), "GM %s (Account: %u) trade: %s (Entry: %d Count: %u) to player: %s (Account: %u)",
225 trader->GetName().c_str(), trader->GetSession()->GetAccountId(),
226 hisItems[i]->GetTemplate()->Name1.c_str(), hisItems[i]->GetEntry(), hisItems[i]->GetCount(),
227 _player->GetName().c_str(), _player->GetSession()->GetAccountId());
228 }
229
230 // adjust time (depends on /played)
231 if (hisItems[i]->HasFlag(ITEM_FIELD_DYNAMIC_FLAGS, ITEM_FLAG_BOP_TRADEABLE))
232 hisItems[i]->SetUInt32Value(ITEM_FIELD_CREATE_PLAYED_TIME, _player->GetTotalPlayedTime() - (trader->GetTotalPlayedTime() - hisItems[i]->GetUInt32Value(ITEM_FIELD_CREATE_PLAYED_TIME)));
233 // store
234 _player->MoveItemToInventory(playerDst, hisItems[i], true, true);
235 }
236 }
237 else
238 {
239 // in case of fatal error log error message
240 // return the already removed items to the original owner
241 if (myItems[i])
242 {
243 if (!traderCanTrade)
244 SF_LOG_ERROR("network", "trader can't store item: %u", myItems[i]->GetGUIDLow());
245 if (_player->CanStoreItem(NULL_BAG, NULL_SLOT, playerDst, myItems[i], false) == EQUIP_ERR_OK)
246 _player->MoveItemToInventory(playerDst, myItems[i], true, true);
247 else
248 SF_LOG_ERROR("network", "player can't take item back: %u", myItems[i]->GetGUIDLow());
249 }
250 // return the already removed items to the original owner
251 if (hisItems[i])
252 {
253 if (!playerCanTrade)
254 SF_LOG_ERROR("network", "player can't store item: %u", hisItems[i]->GetGUIDLow());
255 if (trader->CanStoreItem(NULL_BAG, NULL_SLOT, traderDst, hisItems[i], false) == EQUIP_ERR_OK)
256 trader->MoveItemToInventory(traderDst, hisItems[i], true, true);
257 else
258 SF_LOG_ERROR("network", "trader can't take item back: %u", hisItems[i]->GetGUIDLow());
259 }
260 }
261 }
262}
263
264//==============================================================
265
266static void setAcceptTradeMode(TradeData* myTrade, TradeData* hisTrade, Item** myItems, Item** hisItems)
267{
268 myTrade->SetInAcceptProcess(true);
269 hisTrade->SetInAcceptProcess(true);
270
271 // store items in local list and set 'in-trade' flag
272 for (uint8 i = 0; i < TRADE_SLOT_TRADED_COUNT; ++i)
273 {
274 if (Item* item = myTrade->GetItem(TradeSlots(i)))
275 {
276 SF_LOG_DEBUG("network", "player trade item %u bag: %u slot: %u", item->GetGUIDLow(), item->GetBagSlot(), item->GetSlot());
277 //Can return NULL
278 myItems[i] = item;
279 myItems[i]->SetInTrade();
280 }
281
282 if (Item* item = hisTrade->GetItem(TradeSlots(i)))
283 {
284 SF_LOG_DEBUG("network", "partner trade item %u bag: %u slot: %u", item->GetGUIDLow(), item->GetBagSlot(), item->GetSlot());
285 hisItems[i] = item;
286 hisItems[i]->SetInTrade();
287 }
288 }
289}
290
291static void clearAcceptTradeMode(TradeData* myTrade, TradeData* hisTrade)
292{
293 myTrade->SetInAcceptProcess(false);
294 hisTrade->SetInAcceptProcess(false);
295}
296
297static void clearAcceptTradeMode(Item** myItems, Item** hisItems)
298{
299 // clear 'in-trade' flag
300 for (uint8 i = 0; i < TRADE_SLOT_TRADED_COUNT; ++i)
301 {
302 if (myItems[i])
303 myItems[i]->SetInTrade(false);
304 if (hisItems[i])
305 hisItems[i]->SetInTrade(false);
306 }
307}
308
310{
311 TradeData* my_trade = _player->m_trade;
312 if (!my_trade)
313 return;
314
315 Player* trader = my_trade->GetTrader();
316
317 TradeData* his_trade = trader->m_trade;
318 if (!his_trade)
319 return;
320
321 Item* myItems[TRADE_SLOT_TRADED_COUNT] = { NULL, NULL, NULL, NULL, NULL, NULL };
322 Item* hisItems[TRADE_SLOT_TRADED_COUNT] = { NULL, NULL, NULL, NULL, NULL, NULL };
323
324 // set before checks for propertly undo at problems (it already set in to client)
325 my_trade->SetAccepted(true);
326
327 // not accept case incorrect money amount
328 if (!_player->HasEnoughMoney(my_trade->GetMoney()))
329 {
331 my_trade->SetAccepted(false, true);
332 return;
333 }
334
335 // not accept case incorrect money amount
336 if (!trader->HasEnoughMoney(his_trade->GetMoney()))
337 {
339 his_trade->SetAccepted(false, true);
340 return;
341 }
342
343 if (_player->GetMoney() >= uint64(MAX_MONEY_AMOUNT) - his_trade->GetMoney())
344 {
345 _player->SendEquipError(EQUIP_ERR_TOO_MUCH_GOLD, NULL, NULL);
346 my_trade->SetAccepted(false, true);
347 return;
348 }
349
350 if (trader->GetMoney() >= uint64(MAX_MONEY_AMOUNT) - my_trade->GetMoney())
351 {
352 trader->SendEquipError(EQUIP_ERR_TOO_MUCH_GOLD, NULL, NULL);
353 his_trade->SetAccepted(false, true);
354 return;
355 }
356
357 // not accept if some items now can't be trade (cheating)
358 for (uint8 i = 0; i < TRADE_SLOT_TRADED_COUNT; ++i)
359 {
360 if (Item* item = my_trade->GetItem(TradeSlots(i)))
361 {
362 if (!item->CanBeTraded(false, true))
363 {
365 return;
366 }
367
368 if (item->IsBindedNotWith(trader))
369 {
371 SendTradeStatus(TRADE_STATUS_FAILED/*TRADE_STATUS_CANCELLED*/);
372 return;
373 }
374 }
375
376 if (Item* item = his_trade->GetItem(TradeSlots(i)))
377 {
378 if (!item->CanBeTraded(false, true))
379 {
381 return;
382 }
383 //if (item->IsBindedNotWith(_player)) // dont mark as invalid when his item isnt good (not exploitable because if item is invalid trade will fail anyway later on the same check)
384 //{
385 // SendTradeStatus(TRADE_STATUS_NOT_ON_TAPLIST);
386 // his_trade->SetAccepted(false, true);
387 // return;
388 //}
389 }
390 }
391
392 if (his_trade->IsAccepted())
393 {
394 setAcceptTradeMode(my_trade, his_trade, myItems, hisItems);
395
396 Spell* my_spell = NULL;
397 SpellCastTargets my_targets;
398
399 Spell* his_spell = NULL;
400 SpellCastTargets his_targets;
401
402 // not accept if spell can't be casted now (cheating)
403 if (uint32 my_spell_id = my_trade->GetSpell())
404 {
405 SpellInfo const* spellEntry = sSpellMgr->GetSpellInfo(my_spell_id);
406 Item* castItem = my_trade->GetSpellCastItem();
407
408 if (!spellEntry || !his_trade->GetItem(TRADE_SLOT_NONTRADED) ||
409 (my_trade->HasSpellCastItem() && !castItem))
410 {
411 clearAcceptTradeMode(my_trade, his_trade);
412 clearAcceptTradeMode(myItems, hisItems);
413
414 my_trade->SetSpell(0);
415 return;
416 }
417
418 my_spell = new Spell(_player, spellEntry, TRIGGERED_FULL_MASK);
419 my_spell->m_CastItem = castItem;
420 my_targets.SetTradeItemTarget(_player);
421 my_spell->m_targets = my_targets;
422
423 SpellCastResult res = my_spell->CheckCast(true);
425 {
426 my_spell->SendCastResult(res);
427
428 clearAcceptTradeMode(my_trade, his_trade);
429 clearAcceptTradeMode(myItems, hisItems);
430
431 delete my_spell;
432 my_trade->SetSpell(0);
433 return;
434 }
435 }
436
437 // not accept if spell can't be casted now (cheating)
438 if (uint32 his_spell_id = his_trade->GetSpell())
439 {
440 SpellInfo const* spellEntry = sSpellMgr->GetSpellInfo(his_spell_id);
441 Item* castItem = his_trade->GetSpellCastItem();
442
443 if (!spellEntry || !my_trade->GetItem(TRADE_SLOT_NONTRADED) || (his_trade->HasSpellCastItem() && !castItem))
444 {
445 delete my_spell;
446 his_trade->SetSpell(0);
447
448 clearAcceptTradeMode(my_trade, his_trade);
449 clearAcceptTradeMode(myItems, hisItems);
450 return;
451 }
452
453 his_spell = new Spell(trader, spellEntry, TRIGGERED_FULL_MASK);
454 his_spell->m_CastItem = castItem;
455 his_targets.SetTradeItemTarget(trader);
456 his_spell->m_targets = his_targets;
457
458 SpellCastResult res = his_spell->CheckCast(true);
460 {
461 his_spell->SendCastResult(res);
462
463 clearAcceptTradeMode(my_trade, his_trade);
464 clearAcceptTradeMode(myItems, hisItems);
465
466 delete my_spell;
467 delete his_spell;
468
469 his_trade->SetSpell(0);
470 return;
471 }
472 }
473
474 // inform partner client
476
477 // test if item will fit in each inventory
478 bool hisCanCompleteTrade = (trader->CanStoreItems(myItems, TRADE_SLOT_TRADED_COUNT) == EQUIP_ERR_OK);
479 bool myCanCompleteTrade = (_player->CanStoreItems(hisItems, TRADE_SLOT_TRADED_COUNT) == EQUIP_ERR_OK);
480
481 clearAcceptTradeMode(myItems, hisItems);
482
483 // in case of missing space report error
484 if (!myCanCompleteTrade)
485 {
486 clearAcceptTradeMode(my_trade, his_trade);
487
490 my_trade->SetAccepted(false);
491 his_trade->SetAccepted(false);
492 return;
493 }
494 else if (!hisCanCompleteTrade)
495 {
496 clearAcceptTradeMode(my_trade, his_trade);
497
500 my_trade->SetAccepted(false);
501 his_trade->SetAccepted(false);
502 return;
503 }
504
505 // execute trade: 1. remove
506 for (uint8 i = 0; i < TRADE_SLOT_TRADED_COUNT; ++i)
507 {
508 if (myItems[i])
509 {
510 myItems[i]->SetUInt64Value(ITEM_FIELD_GIFT_CREATOR, _player->GetGUID());
511 _player->MoveItemFromInventory(myItems[i]->GetBagSlot(), myItems[i]->GetSlot(), true);
512 }
513 if (hisItems[i])
514 {
515 hisItems[i]->SetUInt64Value(ITEM_FIELD_GIFT_CREATOR, trader->GetGUID());
516 trader->MoveItemFromInventory(hisItems[i]->GetBagSlot(), hisItems[i]->GetSlot(), true);
517 }
518 }
519
520 // execute trade: 2. store
521 moveItems(myItems, hisItems);
522
523 // logging money
525 {
526 if (my_trade->GetMoney() > 0)
527 {
528 sLog->outCommand(_player->GetSession()->GetAccountId(), "GM %s (Account: %u) give money (Amount: " UI64FMTD ") to player: %s (Account: %u)",
529 _player->GetName().c_str(), _player->GetSession()->GetAccountId(),
530 my_trade->GetMoney(),
531 trader->GetName().c_str(), trader->GetSession()->GetAccountId());
532 }
533
534 if (his_trade->GetMoney() > 0)
535 {
536 sLog->outCommand(trader->GetSession()->GetAccountId(), "GM %s (Account: %u) give money (Amount: " UI64FMTD ") to player: %s (Account: %u)",
537 trader->GetName().c_str(), trader->GetSession()->GetAccountId(),
538 his_trade->GetMoney(),
539 _player->GetName().c_str(), _player->GetSession()->GetAccountId());
540 }
541 }
542
543 // update money
544 _player->ModifyMoney(-int64(my_trade->GetMoney()));
545 _player->ModifyMoney(his_trade->GetMoney());
546 trader->ModifyMoney(-int64(his_trade->GetMoney()));
547 trader->ModifyMoney(my_trade->GetMoney());
548
549 if (my_spell)
550 my_spell->prepare(&my_targets);
551
552 if (his_spell)
553 his_spell->prepare(&his_targets);
554
555 // cleanup
556 clearAcceptTradeMode(my_trade, his_trade);
557 delete _player->m_trade;
558 _player->m_trade = NULL;
559 delete trader->m_trade;
560 trader->m_trade = NULL;
561
562 // desynchronized with the other saves here (SaveInventoryAndGoldToDB() not have own transaction guards)
563 SQLTransaction trans = CharacterDatabase.BeginTransaction();
564 _player->SaveInventoryAndGoldToDB(trans);
565 trader->SaveInventoryAndGoldToDB(trans);
566 CharacterDatabase.CommitTransaction(trans);
567
570 }
571 else
572 {
574 }
575}
576
578{
579 TradeData* my_trade = _player->GetTradeData();
580 if (!my_trade)
581 return;
582
583 my_trade->SetAccepted(false, true);
584}
585
587{
588 TradeData* my_trade = _player->m_trade;
589 if (!my_trade)
590 return;
591
594}
595
597{
599 return;
600
601 SendTradeStatus(status);
602}
603
605{
606 // sent also after LOGOUT COMPLETE
607 if (_player) // needed because STATUS_LOGGEDIN_OR_RECENTLY_LOGGOUT
608 _player->TradeCancel(true);
609}
610
612{
613 ObjectGuid guid;
614
615 recvPacket.ReadGuidMask(guid, 5, 1, 4, 2, 3, 7, 0, 6);
616 recvPacket.ReadGuidBytes(guid, 4, 6, 2, 0, 3, 7, 5, 1);
617
618 if (GetPlayer()->m_trade)
619 return;
620
621 if (!GetPlayer()->IsAlive())
622 {
624 return;
625 }
626
627 if (GetPlayer()->HasUnitState(UNIT_STATE_STUNNED))
628 {
630 return;
631 }
632
633 if (isLogingOut())
634 {
636 return;
637 }
638
639 if (GetPlayer()->IsInFlight())
640 {
642 return;
643 }
644
645 if (GetPlayer()->getLevel() < sWorld->getIntConfig(WorldIntConfigs::CONFIG_TRADE_LEVEL_REQ))
646 {
648 return;
649 }
650
651 Player* pOther = ObjectAccessor::FindPlayer(guid);
652
653 if (!pOther)
654 {
656 return;
657 }
658
659 if (pOther == GetPlayer() || pOther->m_trade)
660 {
662 return;
663 }
664
665 if (!pOther->IsAlive())
666 {
668 return;
669 }
670
671 if (pOther->IsInFlight())
672 {
674 return;
675 }
676
677 if (pOther->HasUnitState(UNIT_STATE_STUNNED))
678 {
680 return;
681 }
682
683 if (pOther->GetSession()->isLogingOut())
684 {
686 return;
687 }
688
689 if (!sWorld->GetBoolConfig(WorldBoolConfigs::CONFIG_ALLOW_TWO_SIDE_TRADE) && pOther->GetTeam() != _player->GetTeam())
690 {
692 return;
693 }
694
695 if (!pOther->IsWithinDistInMap(_player, 10.0f, false))
696 {
698 return;
699 }
700
701 if (pOther->getLevel() < sWorld->getIntConfig(WorldIntConfigs::CONFIG_TRADE_LEVEL_REQ))
702 {
704 return;
705 }
706
707 // OK start trade
708 _player->m_trade = new TradeData(_player, pOther);
709 pOther->m_trade = new TradeData(pOther, _player);
710
711 WorldPacket data(SMSG_TRADE_STATUS, 2 + 7);
712 data.WriteBit(0); // unk bit, usually 0
714
715 ObjectGuid playerGuid = _player->GetGUID();
716 data.WriteGuidMask(playerGuid, 6, 2, 1, 4, 7, 3, 0, 5);
717 data.WriteGuidBytes(playerGuid, 6, 2, 1, 7, 5, 4, 0, 3);
718
719 pOther->GetSession()->SendPacket(&data);
720}
721
723{
724 uint64 gold;
725 recvPacket >> gold;
726
727 TradeData* my_trade = _player->GetTradeData();
728 if (!my_trade)
729 return;
730
731 Player* trader = my_trade->GetTrader();
732
733 if (trader->getVirtualRealm() != _player->getVirtualRealm())
734 {
736 return;
737 }
738
739 // gold can be incorrect, but this is checked at trade finished.
740 my_trade->SetMoney(gold);
741}
742
744{
745 // send update
746 uint8 tradeSlot;
747 uint8 bag;
748 uint8 slot;
749
750 recvPacket >> tradeSlot;
751 recvPacket >> slot;
752 recvPacket >> bag;
753
754 TradeData* my_trade = _player->GetTradeData();
755 if (!my_trade)
756 return;
757
758 // invalid slot number
759 if (tradeSlot >= TRADE_SLOT_COUNT)
760 {
762 return;
763 }
764
765 // check cheating, can't fail with correct client operations
766 Item* item = _player->GetItemByPos(bag, slot);
767 if (!item || (tradeSlot != TRADE_SLOT_NONTRADED && !item->CanBeTraded(false, true)))
768 {
770 return;
771 }
772
773 Player* trader = my_trade->GetTrader();
774
775 if (trader->getVirtualRealm() != _player->getVirtualRealm())
776 {
778 {
780 return;
781 }
782 }
783
784 uint64 iGUID = item->GetGUID();
785
786 // prevent place single item into many trade slots using cheating and client bugs
787 if (my_trade->HasItem(iGUID))
788 {
789 // cheating attempt
791 return;
792 }
793
794 my_trade->SetItem(TradeSlots(tradeSlot), item);
795}
796
798{
799 uint8 tradeSlot;
800 recvPacket >> tradeSlot;
801
802 TradeData* my_trade = _player->m_trade;
803 if (!my_trade)
804 return;
805
806 // invalid slot number
807 if (tradeSlot >= TRADE_SLOT_COUNT)
808 return;
809
810 my_trade->SetItem(TradeSlots(tradeSlot), NULL);
811}
#define UI64FMTD
Definition Define.h:64
std::uint8_t uint8
Definition Define.h:79
std::uint32_t uint32
Definition Define.h:77
std::uint64_t uint64
Definition Define.h:76
std::int64_t int64
Definition Define.h:72
#define MAX_GEM_SOCKETS
Definition Item.h:170
EnchantmentSlot
Definition Item.h:149
@ PERM_ENCHANTMENT_SLOT
Definition Item.h:150
@ SOCK_ENCHANTMENT_SLOT
Definition Item.h:152
@ EQUIP_ERR_TOO_MUCH_GOLD
Definition Item.h:105
@ EQUIP_ERR_OK
Definition Item.h:28
@ ITEM_CLASS_CONSUMABLE
@ ITEM_FLAG_BOP_TRADEABLE
@ ITEM_FLAG_WRAPPED
@ LANG_NOT_PARTNER_FREE_TRADE_SLOTS
Definition Language.h:748
@ LANG_TRADE_OTHER_REQ
Definition Language.h:1116
@ LANG_TRADE_REQ
Definition Language.h:1115
@ LANG_NOT_FREE_TRADE_SLOTS
Definition Language.h:747
@ LANG_NOT_ENOUGH_GOLD
Definition Language.h:746
#define SF_LOG_DEBUG(filterType__,...)
Definition Log.h:134
#define SF_LOG_ERROR(filterType__,...)
Definition Log.h:143
#define sLog
Definition Log.h:106
std::vector< ItemPosCount > ItemPosCountVec
Definition Player.h:777
#define MAX_MONEY_AMOUNT
Definition Player.h:927
TradeSlots
Definition Player.h:780
@ TRADE_SLOT_COUNT
Definition Player.h:781
@ TRADE_SLOT_TRADED_COUNT
Definition Player.h:782
@ TRADE_SLOT_NONTRADED
Definition Player.h:783
TradeStatus
@ TRADE_STATUS_CANCELLED
@ TRADE_STATUS_FAILED
@ TRADE_STATUS_NOT_ON_TAPLIST
@ TRADE_STATUS_NOT_ENOUGH_CURRENCY
@ TRADE_STATUS_PROPOSED
@ TRADE_STATUS_TARGET_DEAD
@ TRADE_STATUS_PLAYER_NOT_FOUND
@ TRADE_STATUS_INITIATED
@ TRADE_STATUS_ALREADY_TRADING
@ TRADE_STATUS_CURRENCY_NOT_TRADABLE
@ TRADE_STATUS_WRONG_REALM
@ TRADE_STATUS_ACCEPTED
@ TRADE_STATUS_DEAD
@ TRADE_STATUS_LOGGING_OUT
@ TRADE_STATUS_TOO_FAR_AWAY
@ TRADE_STATUS_TARGET_STUNNED
@ TRADE_STATUS_PLAYER_IGNORED
@ TRADE_STATUS_COMPLETE
@ TRADE_STATUS_STUNNED
@ TRADE_STATUS_TARGET_LOGGING_OUT
@ TRADE_STATUS_WRONG_FACTION
@ TRADE_STATUS_PLAYER_BUSY
SpellCastResult
#define sSpellMgr
Definition SpellMgr.h:744
static void setAcceptTradeMode(TradeData *myTrade, TradeData *hisTrade, Item **myItems, Item **hisItems)
static void clearAcceptTradeMode(TradeData *myTrade, TradeData *hisTrade)
Skyfire::AutoPtr< Transaction, Skyfire::Mutex > SQLTransaction
Definition Transaction.h:42
@ TRIGGERED_FULL_MASK
Will ignore most target checks (mostly DBC target checks).
Definition Unit.h:435
@ NULL_BAG
Definition Unit.h:336
@ NULL_SLOT
Definition Unit.h:337
@ UNIT_STATE_STUNNED
Definition Unit.h:515
@ ITEM_FIELD_DURABILITY
@ ITEM_FIELD_MAX_DURABILITY
@ ITEM_FIELD_CREATOR
@ ITEM_FIELD_GIFT_CREATOR
@ ITEM_FIELD_DYNAMIC_FLAGS
@ ITEM_FIELD_CREATE_PLAYED_TIME
@ ITEM_DYNAMIC_MODIFIERS
void WriteGuidBytes(const ObjectGuid &guid, Offsets... offsets)
Definition ByteBuffer.h:264
bool WriteBit(uint32 bit)
Definition ByteBuffer.h:164
void WriteGuidMask(const ObjectGuid &guid, Offsets... offsets)
Definition ByteBuffer.h:248
void ReadGuidBytes(ObjectGuid &guid, Offsets... offsets)
Definition ByteBuffer.h:256
void append(T value)
Definition ByteBuffer.h:147
void ReadGuidMask(ObjectGuid &guid, Offsets... offsets)
Definition ByteBuffer.h:240
void WriteBits(T value, size_t bits)
Definition ByteBuffer.h:192
void WriteByteSeq(uint8 b)
Definition ByteBuffer.h:227
void FlushBits()
Definition ByteBuffer.h:154
Definition Item.h:202
uint32 GetEnchantmentId(EnchantmentSlot slot) const
Definition Item.h:287
int32 GetSpellCharges(uint8 index=0) const
Definition Item.h:298
bool CanBeTraded(bool mail=false, bool trade=false) const
Definition Item.cpp:744
int32 GetItemRandomPropertyId() const
Definition Item.h:278
ItemTemplate const * GetTemplate() const
Definition Item.cpp:528
uint32 GetItemSuffixFactor() const
Definition Item.h:279
uint32 GetCount() const
Definition Item.h:258
void SetInTrade(bool b=true)
Definition Item.h:249
static Player * FindPlayer(uint64)
uint64 GetGUID() const
Definition Object.h:119
uint32 GetUInt32Value(uint16 index) const
Definition Object.cpp:325
uint32 GetDynamicUInt32Value(uint32 tab, uint16 index) const
Definition Object.cpp:357
uint32 GetEntry() const
Definition Object.h:125
uint64 GetUInt64Value(uint16 index) const
Definition Object.cpp:331
bool HasFlag(uint16 index, uint32 flag) const
Definition Object.cpp:1309
void SetUInt32Value(uint16 index, uint32 value)
Definition Object.cpp:1038
void SetUInt64Value(uint16 index, uint64 value)
Definition Object.cpp:1079
void SendEquipError(InventoryResult msg, Item *pItem, Item *pItem2=NULL, uint32 itemid=0)
uint32 GetTeam() const
Definition Player.h:2527
void SaveInventoryAndGoldToDB(SQLTransaction &trans)
Definition Player.cpp:14609
bool ModifyMoney(int64 amount, bool sendError=true)
Definition Player.cpp:18342
uint32 GetTotalPlayedTime() const
Definition Player.h:1424
InventoryResult CanStoreItem(uint8 bag, uint8 slot, ItemPosCountVec &dest, Item *pItem, bool swap=false) const
TradeData * m_trade
Definition Player.h:3437
InventoryResult CanStoreItems(Item **pItem, int count) const
WorldSession * GetSession() const
Definition Player.h:2417
void MoveItemFromInventory(uint8 bag, uint8 slot, bool update)
void MoveItemToInventory(ItemPosCountVec const &dest, Item *pItem, bool update, bool in_characterInventoryDB=false)
uint64 GetMoney() const
Definition Player.h:1945
bool HasEnoughMoney(uint64 amount) const
Definition Player.h:1950
void SetTradeItemTarget(Player *caster)
Definition Spell.cpp:314
Definition Spell.h:289
static void SendCastResult(Player *caster, SpellInfo const *spellInfo, uint8 cast_count, SpellCastResult result, SpellCustomErrors customError=SPELL_CUSTOM_ERROR_NONE, Opcodes opcode=SMSG_CAST_FAILED)
Definition Spell.cpp:3874
SpellCastTargets m_targets
Definition Spell.h:535
void prepare(SpellCastTargets const *targets, AuraEffect const *triggeredByAura=NULL)
Definition Spell.cpp:2939
SpellCastResult CheckCast(bool strict)
Definition Spell.cpp:5539
Item * m_CastItem
Definition Spell.h:531
Item * GetSpellCastItem() const
Definition Player.cpp:163
bool HasItem(uint64 itemGuid) const
Definition Player.cpp:145
bool HasSpellCastItem() const
Definition Player.h:1088
void SetMoney(uint64 money)
Definition Player.cpp:207
Player * GetTrader() const
Definition Player.h:1070
void SetInAcceptProcess(bool state)
Definition Player.h:1109
Item * GetItem(TradeSlots slot) const
Definition Player.cpp:140
bool IsAccepted() const
Definition Player.h:1099
uint64 GetMoney() const
Definition Player.h:1093
uint32 GetSpell() const
Definition Player.h:1081
void SetAccepted(bool state, bool crosssend=false)
Definition Player.cpp:228
void SetItem(TradeSlots slot, Item *item)
Definition Player.cpp:168
void SetSpell(uint32 spell_id, Item *castItem=NULL)
Definition Player.cpp:190
uint32 getVirtualRealm() const
Definition Unit.h:1555
bool IsAlive() const
Definition Unit.h:2032
bool IsInFlight() const
Definition Unit.h:1891
bool HasUnitState(const uint32 f) const
Definition Unit.h:1485
uint8 getLevel() const
Definition Unit.h:1528
bool IsWithinDistInMap(WorldObject const *obj, float dist2compare, bool is3D=true) const
Definition Object.cpp:1735
std::string const & GetName() const
Definition Object.h:664
void Initialize(Opcodes opcode, size_t newres=200)
Definition WorldPacket.h:31
const char * GetSkyFireString(int32 entry) const
void moveItems(Item *myItems[], Item *hisItems[])
void HandleIgnoreTradeOpcode(WorldPacket &recvPacket)
bool isLogingOut() const
Is the user engaged in a log out process?
void SendNotification(const char *format,...) ATTR_PRINTF(2
void HandleClearTradeItemOpcode(WorldPacket &recvPacket)
void HandleSetTradeItemOpcode(WorldPacket &recvPacket)
void HandleUnacceptTradeOpcode(WorldPacket &recvPacket)
bool PlayerRecentlyLoggedOut() const
bool PlayerLogout() const
void HandleSetTradeGoldOpcode(WorldPacket &recvPacket)
Player * GetPlayer() const
void HandleBusyTradeOpcode(WorldPacket &recvPacket)
void SendCancelTrade(TradeStatus status)
void HandleBeginTradeOpcode(WorldPacket &recvPacket)
void HandleCancelTradeOpcode(WorldPacket &recvPacket)
bool HasPermission(uint32 permissionId)
void SendPacket(WorldPacket const *packet, bool forced=false)
Send a packet to the client.
void HandleAcceptTradeOpcode(WorldPacket &recvPacket)
uint32 GetAccountId() const
Player * _player
void SendTradeStatus(TradeStatus status)
void HandleInitiateTradeOpcode(WorldPacket &recvPacket)
void SendUpdateTrade(bool trader_data=true)
CharacterDatabaseWorkerPool CharacterDatabase
Accessor to the character database.
Definition Main.cpp:41
@ SMSG_TRADE_STATUS
Definition Opcodes.h:1033
@ SMSG_TRADE_STATUS_EXTENDED
Definition Opcodes.h:1034
#define sWorld
Definition World.h:910
@ CONFIG_TRADE_LEVEL_REQ
Definition World.h:280
@ CONFIG_ALLOW_TWO_SIDE_TRADE
Definition World.h:101
@ RBAC_PERM_LOG_GM_TRADE
Definition RBAC.h:51
std::string Name1