[11391] Auction: multi sell, sale pending list and other fixes.

This commit is contained in:
TOM_RUS 2011-04-24 21:53:49 +04:00
parent 730a907252
commit e39a8b55a0
8 changed files with 415 additions and 256 deletions

View file

@ -21,7 +21,7 @@
DROP TABLE IF EXISTS `character_db_version`;
CREATE TABLE `character_db_version` (
`required_11299_02_characters_pet_aura` bit(1) default NULL
`required_11391_01_characters_auction` bit(1) default NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Last applied sql update to DB';
--

View file

@ -0,0 +1,4 @@
ALTER TABLE character_db_version CHANGE COLUMN required_11299_02_characters_pet_aura required_11391_01_characters_auction bit;
ALTER TABLE `auction`
ADD COLUMN `moneyTime` BIGINT(40) DEFAULT '0' NOT NULL AFTER `time`;

View file

@ -67,28 +67,52 @@ void WorldSession::SendAuctionHello(Unit* unit)
}
// call this method when player bids, creates, or deletes auction
void WorldSession::SendAuctionCommandResult(uint32 auctionId, uint32 Action, uint32 ErrorCode, uint32 bidError )
void WorldSession::SendAuctionCommandResult(AuctionEntry *auc, AuctionAction Action, AuctionError ErrorCode, InventoryResult invError )
{
WorldPacket data( SMSG_AUCTION_COMMAND_RESULT, 16 );
data << uint32(auctionId);
data << uint32(auc ? auc->Id : 0);
data << uint32(Action);
data << uint32(ErrorCode);
if ( !ErrorCode && Action )
data << uint32(bidError); // when bid, then send 0, once...
switch(ErrorCode)
{
case AUCTION_OK:
if(Action == AUCTION_BID_PLACED)
data << uint32(auc->GetAuctionOutBid()); // new AuctionOutBid?
break;
case AUCTION_ERR_INVENTORY:
data << uint32(invError);
break;
case AUCTION_ERR_HIGHER_BID:
data << ObjectGuid(HIGHGUID_PLAYER,auc->bidder);// new bidder guid
data << uint32(auc->bid); // new bid
data << uint32(auc->GetAuctionOutBid()); // new AuctionOutBid?
break;
default:
break;
}
SendPacket(&data);
}
// this function sends notification, if bidder is online
void WorldSession::SendAuctionBidderNotification( uint32 location, uint32 auctionId, ObjectGuid bidderGuid, uint32 bidSum, uint32 diff, uint32 item_template)
void WorldSession::SendAuctionBidderNotification(AuctionEntry* auction)
{
WorldPacket data(SMSG_AUCTION_BIDDER_NOTIFICATION, (8*4));
data << uint32(location);
data << uint32(auctionId);
data << bidderGuid;
data << uint32(bidSum);
data << uint32(diff);
data << uint32(item_template);
data << uint32(0);
data << uint32(auction->GetHouseId());
data << uint32(auction->Id);
data << ObjectGuid(HIGHGUID_PLAYER, auction->bidder);
// if 0, client shows ERR_AUCTION_WON_S, else ERR_AUCTION_OUTBID_S
data << uint32(auction->moneyDeliveryTime ? 0 : auction->bid);
data << uint32(auction->GetAuctionOutBid()); // AuctionOutBid?
data << uint32(auction->itemTemplate);
Item *item = sAuctionMgr.GetAItem(auction->itemGuidLow);
uint32 randomId = item ? item->GetItemRandomPropertyId() : 0;
data << uint32(randomId); // random property (value > 0) or suffix (value < 0)
SendPacket(&data);
}
@ -97,17 +121,46 @@ void WorldSession::SendAuctionOwnerNotification( AuctionEntry* auction)
{
WorldPacket data(SMSG_AUCTION_OWNER_NOTIFICATION, (7*4));
data << uint32(auction->Id);
data << uint32(auction->bid);
data << uint32(0); // unk
data << uint32(0); // unk
data << uint32(0); // unk
data << uint32(auction->item_template);
data << uint32(0); // unk
data << uint32(auction->bid); // if 0, client shows ERR_AUCTION_EXPIRED_S, else ERR_AUCTION_SOLD_S (works only when guid==0)
data << uint32(auction->GetAuctionOutBid()); // AuctionOutBid?
ObjectGuid guid = ObjectGuid();
if(!auction->moneyDeliveryTime) // not sold yet
guid = ObjectGuid(HIGHGUID_PLAYER, auction->bidder);// bidder==0 and moneyDeliveryTime==0 for expired auctions, so it will show error message properly
// if guid!=0, client updates auctions with new bid, outbid and bidderGuid, else it shows error messages as described above
data << guid; // bidder guid
data << uint32(auction->itemTemplate); // item entry
Item *item = sAuctionMgr.GetAItem(auction->itemGuidLow);
uint32 randomId = item ? item->GetItemRandomPropertyId() : 0;
data << uint32(randomId); // random property (value > 0) or suffix (value < 0)
float timeLeft = float(auction->moneyDeliveryTime - time(NULL)) / float(DAY);
data << float(timeLeft); // time till money arrive? only used if bid != 0
SendPacket(&data);
}
// shows ERR_AUCTION_REMOVED_S
void WorldSession::SendAuctionRemovedNotification(AuctionEntry* auction)
{
WorldPacket data(SMSG_AUCTION_REMOVED_NOTIFICATION, (3*4));
data << uint32(auction->Id);
data << uint32(auction->itemTemplate);
Item *item = sAuctionMgr.GetAItem(auction->itemGuidLow);
uint32 randomId = item ? item->GetItemRandomPropertyId() : 0;
data << uint32(randomId); // random property (value > 0) or suffix (value < 0)
SendPacket(&data);
}
// this function sends mail to old bidder
void WorldSession::SendAuctionOutbiddedMail(AuctionEntry *auction, uint32 newPrice)
void WorldSession::SendAuctionOutbiddedMail(AuctionEntry *auction)
{
ObjectGuid oldBidder_guid = ObjectGuid(HIGHGUID_PLAYER, auction->bidder);
Player *oldBidder = sObjectMgr.GetPlayer(oldBidder_guid);
@ -120,12 +173,10 @@ void WorldSession::SendAuctionOutbiddedMail(AuctionEntry *auction, uint32 newPri
if(oldBidder || oldBidder_accId)
{
std::ostringstream msgAuctionOutbiddedSubject;
msgAuctionOutbiddedSubject << auction->item_template << ":0:" << AUCTION_OUTBIDDED << ":0:0";
msgAuctionOutbiddedSubject << auction->itemTemplate << ":0:" << AUCTION_OUTBIDDED << ":0:0";
if (oldBidder)
oldBidder->GetSession()->SendAuctionBidderNotification(
auction->GetHouseId(), auction->Id, _player->GetObjectGuid(),
newPrice, auction->GetAuctionOutBid(), auction->item_template);
oldBidder->GetSession()->SendAuctionBidderNotification(auction);
MailDraft(msgAuctionOutbiddedSubject.str(), "") // TODO: fix body
.SetMoney(auction->bid)
@ -147,7 +198,10 @@ void WorldSession::SendAuctionCancelledToBidderMail( AuctionEntry* auction )
if(bidder || bidder_accId)
{
std::ostringstream msgAuctionCancelledSubject;
msgAuctionCancelledSubject << auction->item_template << ":0:" << AUCTION_CANCELLED_TO_BIDDER << ":0:0";
msgAuctionCancelledSubject << auction->itemTemplate << ":0:" << AUCTION_CANCELLED_TO_BIDDER << ":0:0";
if(bidder)
bidder->GetSession()->SendAuctionRemovedNotification(auction);
MailDraft(msgAuctionCancelledSubject.str(), "") // TODO: fix body
.SetMoney(auction->bid)
@ -193,17 +247,33 @@ void WorldSession::HandleAuctionSellItem( WorldPacket & recv_data )
DEBUG_LOG("WORLD: HandleAuctionSellItem");
ObjectGuid auctioneerGuid;
ObjectGuid itemGuid;
uint32 etime, bid, buyout;
uint32 etime, bid, buyout, itemCount;
std::vector<ObjectGuid> guids;
std::vector<uint32> stackSizes;
recv_data >> auctioneerGuid;
recv_data.read_skip<uint32>(); // const 1?
recv_data >> itemGuid;
recv_data.read_skip<uint32>(); // stack size
recv_data >> itemCount;
if(itemCount > MAX_BAG_SIZE * 5)
{
recv_data.rpos(recv_data.wpos()); // should not happen
return;
}
guids.resize(itemCount);
stackSizes.resize(itemCount);
for(uint32 i = 0; i < itemCount; ++i)
{
recv_data >> guids[i]; // item guid
recv_data >> stackSizes[i]; // stack size
}
recv_data >> bid;
recv_data >> buyout;
recv_data >> etime;
if (itemGuid.IsEmpty() || !bid || !etime)
if (!bid || !etime)
return; // check for cheaters
Player *pl = GetPlayer();
@ -233,39 +303,50 @@ void WorldSession::HandleAuctionSellItem( WorldPacket & recv_data )
if(GetPlayer()->hasUnitState(UNIT_STAT_DIED))
GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
for(uint32 i = 0; i < itemCount; ++i)
{
ObjectGuid itemGuid = guids[i];
if(itemGuid.IsEmpty())
continue;
uint32 stackSize = stackSizes[i];
Item *it = pl->GetItemByGuid(itemGuid);
// do not allow to sell already auctioned items
if (sAuctionMgr.GetAItem(itemGuid.GetCounter()))
{
sLog.outError("AuctionError, %s is sending %s, but item is already in another auction", pl->GetGuidStr().c_str(), itemGuid.GetString().c_str());
SendAuctionCommandResult(0, AUCTION_SELL_ITEM, AUCTION_INTERNAL_ERROR);
return;
SendAuctionCommandResult(NULL, AUCTION_STARTED, AUCTION_ERR_INVENTORY, EQUIP_ERR_ITEM_NOT_FOUND);
continue;
}
// prevent sending bag with items (cheat: can be placed in bag after adding equipped empty bag to auction)
if(!it)
{
SendAuctionCommandResult(0, AUCTION_SELL_ITEM, AUCTION_ITEM_NOT_FOUND);
return;
SendAuctionCommandResult(NULL, AUCTION_STARTED, AUCTION_ERR_INVENTORY, EQUIP_ERR_ITEM_NOT_FOUND);
continue;
}
if(!it->CanBeTraded())
{
SendAuctionCommandResult(0, AUCTION_SELL_ITEM, AUCTION_INTERNAL_ERROR);
return;
SendAuctionCommandResult(NULL, AUCTION_STARTED, AUCTION_ERR_INVENTORY, EQUIP_ERR_CANNOT_TRADE_THAT);
continue;
}
if ((it->GetProto()->Flags & ITEM_FLAG_CONJURED) || it->GetUInt32Value(ITEM_FIELD_DURATION))
{
SendAuctionCommandResult(0, AUCTION_SELL_ITEM, AUCTION_INTERNAL_ERROR);
return;
SendAuctionCommandResult(NULL, AUCTION_STARTED, AUCTION_ERR_INVENTORY, EQUIP_ERR_CANNOT_TRADE_THAT);
continue;
}
//we have to take deposit :
// check money for deposit
uint32 deposit = AuctionHouseMgr::GetAuctionDeposit( auctionHouseEntry, etime, it );
if ( pl->GetMoney() < deposit )
{
SendAuctionCommandResult(0, AUCTION_SELL_ITEM, AUCTION_NOT_ENOUGHT_MONEY);
return;
SendAuctionCommandResult(NULL, AUCTION_STARTED, AUCTION_ERR_NOT_ENOUGH_MONEY);
continue;
}
if( GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_BOOL_GM_LOG_TRADE) )
@ -274,20 +355,34 @@ void WorldSession::HandleAuctionSellItem( WorldPacket & recv_data )
GetPlayerName(), GetAccountId(), it->GetProto()->Name1, it->GetEntry(), it->GetCount());
}
if(stackSize == 0)
stackSize = 1;
if(stackSize > it->GetMaxStackCount()) // too big stack size
stackSize = it->GetMaxStackCount();
if(!pl->HasItemCount(it->GetEntry(), stackSize)) // not enough items
continue;
Item *newItem = it->CloneItem(stackSize, pl);
pl->DestroyItemCount(it, stackSize, true);
pl->ModifyMoney( -int32(deposit) );
uint32 auction_time = uint32(etime * sWorld.getConfig(CONFIG_FLOAT_RATE_AUCTION_TIME));
AuctionEntry *AH = new AuctionEntry;
AH->Id = sObjectMgr.GenerateAuctionID();
AH->item_guidlow = itemGuid.GetCounter();
AH->item_template = it->GetEntry();
AH->itemGuidLow = newItem->GetObjectGuid().GetCounter();
AH->itemTemplate = newItem->GetEntry();
AH->owner = pl->GetGUIDLow();
AH->startbid = bid;
AH->bidder = 0;
AH->bid = 0;
AH->buyout = buyout;
AH->expire_time = time(NULL) + auction_time;
AH->expireTime = time(NULL) + auction_time;
AH->moneyDeliveryTime = 0;
AH->deposit = deposit;
AH->auctionHouseEntry = auctionHouseEntry;
@ -295,20 +390,19 @@ void WorldSession::HandleAuctionSellItem( WorldPacket & recv_data )
itemGuid.GetString().c_str(), auctioneerGuid.GetString().c_str(), bid, buyout, auction_time, AH->GetHouseId());
auctionHouse->AddAuction(AH);
sAuctionMgr.AddAItem(it);
pl->MoveItemFromInventory( it->GetBagSlot(), it->GetSlot(), true);
sAuctionMgr.AddAItem(newItem);
CharacterDatabase.BeginTransaction();
it->DeleteFromInventoryDB();
it->SaveToDB(); // recursive and not have transaction guard into self, not in inventiory and can be save standalone
newItem->SaveToDB();
AH->SaveToDB();
pl->SaveInventoryAndGoldToDB();
CharacterDatabase.CommitTransaction();
SendAuctionCommandResult(AH->Id, AUCTION_SELL_ITEM, AUCTION_OK);
SendAuctionCommandResult(AH, AUCTION_STARTED, AUCTION_OK);
GetPlayer()->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_CREATE_AUCTION, 1);
}
}
// this function is called when client bids or buys out auction
void WorldSession::HandleAuctionPlaceBid( WorldPacket & recv_data )
@ -335,14 +429,13 @@ void WorldSession::HandleAuctionPlaceBid( WorldPacket & recv_data )
if(GetPlayer()->hasUnitState(UNIT_STAT_DIED))
GetPlayer()->RemoveSpellsCausingAura(SPELL_AURA_FEIGN_DEATH);
AuctionEntry *auction = auctionHouse->GetAuction(auctionId);
Player *pl = GetPlayer();
if( !auction || auction->owner == pl->GetGUIDLow() )
{
// you cannot bid your own auction:
SendAuctionCommandResult( 0, AUCTION_PLACE_BID, CANNOT_BID_YOUR_AUCTION_ERROR );
SendAuctionCommandResult( NULL, AUCTION_BID_PLACED, AUCTION_ERR_BID_OWN );
return;
}
@ -353,7 +446,7 @@ void WorldSession::HandleAuctionPlaceBid( WorldPacket & recv_data )
if (!auction_owner && sObjectMgr.GetPlayerAccountIdByGUID(ownerGuid) == pl->GetSession()->GetAccountId())
{
// you cannot bid your another character auction:
SendAuctionCommandResult( 0, AUCTION_PLACE_BID, CANNOT_BID_YOUR_AUCTION_ERROR );
SendAuctionCommandResult( NULL, AUCTION_BID_PLACED, AUCTION_ERR_BID_OWN );
return;
}
@ -372,41 +465,38 @@ void WorldSession::HandleAuctionPlaceBid( WorldPacket & recv_data )
if (price > pl->GetMoney())
{
// you don't have enough money!, client tests!
// SendAuctionCommandResult(auction->auctionId, AUCTION_PLACE_BID, ???);
// SendAuctionCommandResult(auction->auctionId, AUCTION_ERR_INVENTORY, EQUIP_ERR_NOT_ENOUGH_MONEY);
return;
}
if ((price < auction->buyout) || (auction->buyout == 0))
if ((price < auction->buyout) || (auction->buyout == 0))// bid
{
if (auction->bidder > 0)
{
if ( auction->bidder == pl->GetGUIDLow() )
if (pl->GetGUIDLow() == auction->bidder)
{
pl->ModifyMoney(-int32(price - auction->bid));
}
else
{
// mail to last bidder and return money
SendAuctionOutbiddedMail( auction , price );
pl->ModifyMoney( -int32(price) );
}
}
else
{
pl->ModifyMoney(-int32(price));
if (auction->bidder) // return money to old bidder if present
SendAuctionOutbiddedMail(auction);
}
auction->bidder = pl->GetGUIDLow();
auction->bid = price;
if(auction_owner)
auction_owner->GetSession()->SendAuctionOwnerNotification(auction);
GetPlayer()->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID, price);
// after this update we should save player's money ...
CharacterDatabase.PExecute("UPDATE auction SET buyguid = '%u', lastbid = '%u' WHERE id = '%u'", auction->bidder, auction->bid, auction->Id);
SendAuctionCommandResult(auction->Id, AUCTION_PLACE_BID, AUCTION_OK, 0 );
SendAuctionCommandResult(auction, AUCTION_BID_PLACED, AUCTION_OK);
}
else
else // buyout
{
// buyout:
if (pl->GetGUIDLow() == auction->bidder)
{
pl->ModifyMoney(-int32(auction->buyout - auction->bid));
@ -414,26 +504,22 @@ void WorldSession::HandleAuctionPlaceBid( WorldPacket & recv_data )
else
{
pl->ModifyMoney(-int32(auction->buyout));
if ( auction->bidder ) // buyout for bidded auction ..
{
SendAuctionOutbiddedMail( auction, auction->buyout );
}
if (auction->bidder) // return money to old bidder if present
SendAuctionOutbiddedMail(auction);
}
auction->bidder = pl->GetGUIDLow();
auction->bid = auction->buyout;
GetPlayer()->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID, auction->buyout);
sAuctionMgr.SendAuctionSalePendingMail( auction );
sAuctionMgr.SendAuctionSuccessfulMail( auction );
auction->moneyDeliveryTime = time(NULL) + HOUR;
sAuctionMgr.SendAuctionWonMail(auction);
SendAuctionCommandResult(auction->Id, AUCTION_PLACE_BID, AUCTION_OK);
SendAuctionCommandResult(auction, AUCTION_BID_PLACED, AUCTION_OK);
sAuctionMgr.RemoveAItem(auction->item_guidlow);
auctionHouse->RemoveAuction(auction->Id);
auction->DeleteFromDB();
delete auction;
CharacterDatabase.PExecute("UPDATE auction SET moneyTime = '" UI64FMTD "', buyguid = '%u', lastbid = '%u' WHERE id = '%u'", (uint64)auction->moneyDeliveryTime, auction->bidder, auction->bid, auction->Id);
}
CharacterDatabase.BeginTransaction();
pl->SaveInventoryAndGoldToDB();
@ -467,7 +553,7 @@ void WorldSession::HandleAuctionRemoveItem( WorldPacket & recv_data )
if (auction && auction->owner == pl->GetGUIDLow())
{
Item *pItem = sAuctionMgr.GetAItem(auction->item_guidlow);
Item *pItem = sAuctionMgr.GetAItem(auction->itemGuidLow);
if (pItem)
{
if (auction->bidder > 0) // If we have a bidder, we have to send him the money he paid
@ -475,13 +561,13 @@ void WorldSession::HandleAuctionRemoveItem( WorldPacket & recv_data )
uint32 auctionCut = auction->GetAuctionCut();
if ( pl->GetMoney() < auctionCut ) // player doesn't have enough money, maybe message needed
return;
//some auctionBidderNotification would be needed, but don't know that parts..
SendAuctionCancelledToBidderMail( auction );
pl->ModifyMoney( -int32(auctionCut) );
}
// Return the item by mail
std::ostringstream msgAuctionCanceledOwner;
msgAuctionCanceledOwner << auction->item_template << ":0:" << AUCTION_CANCELED << ":0:0";
msgAuctionCanceledOwner << auction->itemTemplate << ":0:" << AUCTION_CANCELED << ":0:0";
// item will deleted or added to received mail list
MailDraft(msgAuctionCanceledOwner.str(), "") // TODO: fix body
@ -490,27 +576,27 @@ void WorldSession::HandleAuctionRemoveItem( WorldPacket & recv_data )
}
else
{
sLog.outError("Auction id: %u has nonexistent item (item guid : %u)!!!", auction->Id, auction->item_guidlow);
SendAuctionCommandResult( 0, AUCTION_CANCEL, AUCTION_INTERNAL_ERROR );
sLog.outError("Auction id: %u has nonexistent item (item guid : %u)!!!", auction->Id, auction->itemGuidLow);
SendAuctionCommandResult( NULL, AUCTION_REMOVED, AUCTION_ERR_INVENTORY, EQUIP_ERR_ITEM_NOT_FOUND );
return;
}
}
else
{
SendAuctionCommandResult( 0, AUCTION_CANCEL, AUCTION_INTERNAL_ERROR );
SendAuctionCommandResult( NULL, AUCTION_REMOVED, AUCTION_ERR_DATABASE );
// this code isn't possible ... maybe there should be ASSERT
sLog.outError("CHEATER : %u, he tried to cancel auction (id: %u) of another player, or auction is NULL", pl->GetGUIDLow(), auctionId);
return;
}
// inform player, that auction is removed
SendAuctionCommandResult( auction->Id, AUCTION_CANCEL, AUCTION_OK );
SendAuctionCommandResult(auction, AUCTION_REMOVED, AUCTION_OK );
// Now remove the auction
CharacterDatabase.BeginTransaction();
auction->DeleteFromDB();
pl->SaveInventoryAndGoldToDB();
CharacterDatabase.CommitTransaction();
sAuctionMgr.RemoveAItem( auction->item_guidlow );
sAuctionMgr.RemoveAItem( auction->itemGuidLow );
auctionHouse->RemoveAuction( auction->Id );
delete auction;
}
@ -600,7 +686,7 @@ void WorldSession::HandleAuctionListOwnerItems( WorldPacket & recv_data )
auctionHouse->BuildListOwnerItems(data, _player, count, totalcount);
data.put<uint32>(0, count);
data << uint32(totalcount);
data << uint32(0); // 2.3.0 delay for next list request?
data << uint32(300); // 2.3.0 delay for next list request?
SendPacket(&data);
}
@ -611,7 +697,7 @@ void WorldSession::HandleAuctionListItems( WorldPacket & recv_data )
ObjectGuid auctioneerGuid;
std::string searchedname;
uint8 levelmin, levelmax, usable;
uint8 levelmin, levelmax, usable, isFull, sortCount;
uint32 listfrom, auctionSlotID, auctionMainCategory, auctionSubCategory, quality;
recv_data >> auctioneerGuid;
@ -619,10 +705,15 @@ void WorldSession::HandleAuctionListItems( WorldPacket & recv_data )
recv_data >> searchedname;
recv_data >> levelmin >> levelmax;
recv_data >> auctionSlotID >> auctionMainCategory >> auctionSubCategory;
recv_data >> quality >> usable;
recv_data >> auctionSlotID >> auctionMainCategory >> auctionSubCategory >> quality;
recv_data >> usable >> isFull >> sortCount;
recv_data.read_skip(16); // unknown 16 bytes: 00 07 01 00 00 01 05 00 06 00 09 01 08 00 03 00
// auction columns sorting
for(uint32 i = 0; i < sortCount; ++i)
{
recv_data.read_skip<uint8>(); // column?
recv_data.read_skip<uint8>(); // direction?
}
AuctionHouseEntry const* auctionHouseEntry = GetCheckedAuctionHouseForAuctioneer(auctioneerGuid);
if (!auctionHouseEntry)
@ -673,17 +764,14 @@ void WorldSession::HandleAuctionListPendingSales( WorldPacket & recv_data )
if (!auctionHouseEntry)
return;
// always return pointer
AuctionHouseObject* auctionHouse = sAuctionMgr.GetAuctionsMap(auctionHouseEntry);
uint32 count = 0;
WorldPacket data(SMSG_AUCTION_LIST_PENDING_SALES, 4);
data << uint32(count); // count
/*for(uint32 i = 0; i < count; ++i)
{
data << ""; // string
data << ""; // string
data << uint32(0);
data << uint32(0);
data << float(0);
}*/
auctionHouse->BuildListPendingSales(data, _player, count);
data.put<uint32>(0, count);
SendPacket(&data);
}

View file

@ -79,7 +79,7 @@ uint32 AuctionHouseMgr::GetAuctionDeposit(AuctionHouseEntry const* entry, uint32
// does not clear ram
void AuctionHouseMgr::SendAuctionWonMail( AuctionEntry *auction )
{
Item *pItem = GetAItem(auction->item_guidlow);
Item *pItem = GetAItem(auction->itemGuidLow);
if(!pItem)
return;
@ -127,11 +127,16 @@ void AuctionHouseMgr::SendAuctionWonMail( AuctionEntry *auction )
else if (!bidder)
bidder_accId = sObjectMgr.GetPlayerAccountIdByGUID(bidder_guid);
ObjectGuid ownerGuid = ObjectGuid(HIGHGUID_PLAYER, auction->owner);
Player* auction_owner = sObjectMgr.GetPlayer(ownerGuid);
if(auction_owner)
auction_owner->GetSession()->SendAuctionOwnerNotification( auction );
// receiver exist
if(bidder || bidder_accId)
{
std::ostringstream msgAuctionWonSubject;
msgAuctionWonSubject << auction->item_template << ":0:" << AUCTION_WON;
msgAuctionWonSubject << auction->itemTemplate << ":0:" << AUCTION_WON;
std::ostringstream msgAuctionWonBody;
msgAuctionWonBody.width(16);
@ -146,7 +151,7 @@ void AuctionHouseMgr::SendAuctionWonMail( AuctionEntry *auction )
if (bidder)
{
bidder->GetSession()->SendAuctionBidderNotification( auction->GetHouseId(), auction->Id, bidder_guid, 0, 0, auction->item_template);
bidder->GetSession()->SendAuctionBidderNotification(auction);
// FIXME: for offline player need also
bidder->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_WON_AUCTIONS, 1);
}
@ -176,7 +181,7 @@ void AuctionHouseMgr::SendAuctionSalePendingMail( AuctionEntry * auction )
if(owner || sObjectMgr.GetPlayerAccountIdByGUID(owner_guid))
{
std::ostringstream msgAuctionSalePendingSubject;
msgAuctionSalePendingSubject << auction->item_template << ":0:" << AUCTION_SALE_PENDING;
msgAuctionSalePendingSubject << auction->itemTemplate << ":0:" << AUCTION_SALE_PENDING;
std::ostringstream msgAuctionSalePendingBody;
uint32 auctionCut = auction->GetAuctionCut();
@ -210,7 +215,7 @@ void AuctionHouseMgr::SendAuctionSuccessfulMail( AuctionEntry * auction )
if(owner || owner_accId)
{
std::ostringstream msgAuctionSuccessfulSubject;
msgAuctionSuccessfulSubject << auction->item_template << ":0:" << AUCTION_SUCCESSFUL;
msgAuctionSuccessfulSubject << auction->itemTemplate << ":0:" << AUCTION_SUCCESSFUL;
std::ostringstream auctionSuccessfulBody;
uint32 auctionCut = auction->GetAuctionCut();
@ -229,23 +234,21 @@ void AuctionHouseMgr::SendAuctionSuccessfulMail( AuctionEntry * auction )
// FIXME: what do if owner offline
owner->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GOLD_EARNED_BY_AUCTIONS, profit);
owner->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_SOLD, auction->bid);
//send auction owner notification, bidder must be current!
owner->GetSession()->SendAuctionOwnerNotification( auction );
}
MailDraft(msgAuctionSuccessfulSubject.str(), auctionSuccessfulBody.str())
.SetMoney(profit)
.SendMailTo(MailReceiver(owner, owner_guid), auction, MAIL_CHECK_MASK_COPIED, HOUR);
.SendMailTo(MailReceiver(owner, owner_guid), auction, MAIL_CHECK_MASK_COPIED);
}
}
// does not clear ram
void AuctionHouseMgr::SendAuctionExpiredMail( AuctionEntry * auction )
{ // return an item in auction to its owner by mail
Item *pItem = GetAItem(auction->item_guidlow);
Item *pItem = GetAItem(auction->itemGuidLow);
if(!pItem)
{
sLog.outError("Auction item (GUID: %u) not found, and lost.",auction->item_guidlow);
sLog.outError("Auction item (GUID: %u) not found, and lost.", auction->itemGuidLow);
return;
}
@ -260,7 +263,7 @@ void AuctionHouseMgr::SendAuctionExpiredMail( AuctionEntry * auction )
if(owner || owner_accId)
{
std::ostringstream subject;
subject << auction->item_template << ":0:" << AUCTION_EXPIRED << ":0:0";
subject << auction->itemGuidLow << ":0:" << AUCTION_EXPIRED << ":0:0";
if ( owner )
owner->GetSession()->SendAuctionOwnerNotification( auction );
@ -359,7 +362,7 @@ void AuctionHouseMgr::LoadAuctions()
return;
}
result = CharacterDatabase.Query( "SELECT id,houseid,itemguid,item_template,itemowner,buyoutprice,time,buyguid,lastbid,startbid,deposit FROM auction" );
result = CharacterDatabase.Query( "SELECT id,houseid,itemguid,item_template,itemowner,buyoutprice,time,moneyTime,buyguid,lastbid,startbid,deposit FROM auction" );
if( !result )
{
barGoLink bar(1);
@ -382,24 +385,25 @@ void AuctionHouseMgr::LoadAuctions()
auction = new AuctionEntry;
auction->Id = fields[0].GetUInt32();
uint32 houseid = fields[1].GetUInt32();
auction->item_guidlow = fields[2].GetUInt32();
auction->item_template = fields[3].GetUInt32();
auction->itemGuidLow = fields[2].GetUInt32();
auction->itemTemplate = fields[3].GetUInt32();
auction->owner = fields[4].GetUInt32();
auction->buyout = fields[5].GetUInt32();
auction->expire_time = fields[6].GetUInt32();
auction->bidder = fields[7].GetUInt32();
auction->bid = fields[8].GetUInt32();
auction->startbid = fields[9].GetUInt32();
auction->deposit = fields[10].GetUInt32();
auction->expireTime = fields[6].GetUInt32();
auction->moneyDeliveryTime = fields[7].GetUInt32();
auction->bidder = fields[8].GetUInt32();
auction->bid = fields[9].GetUInt32();
auction->startbid = fields[10].GetUInt32();
auction->deposit = fields[11].GetUInt32();
auction->auctionHouseEntry = NULL; // init later
// check if sold item exists for guid
// and item_template in fact (GetAItem will fail if problematic in result check in AuctionHouseMgr::LoadAuctionItems)
Item* pItem = GetAItem(auction->item_guidlow);
Item* pItem = GetAItem(auction->itemGuidLow);
if (!pItem)
{
auction->DeleteFromDB();
sLog.outError("Auction %u has not a existing item : %u, deleted", auction->Id, auction->item_guidlow);
sLog.outError("Auction %u has not a existing item : %u, deleted", auction->Id, auction->itemGuidLow);
delete auction;
continue;
}
@ -413,14 +417,14 @@ void AuctionHouseMgr::LoadAuctions()
// Attempt send item back to owner
std::ostringstream msgAuctionCanceledOwner;
msgAuctionCanceledOwner << auction->item_template << ":0:" << AUCTION_CANCELED << ":0:0";
msgAuctionCanceledOwner << auction->itemTemplate << ":0:" << AUCTION_CANCELED << ":0:0";
// item will deleted or added to received mail list
MailDraft(msgAuctionCanceledOwner.str(), "") // TODO: fix body
.AddItem(pItem)
.SendMailTo(MailReceiver(ObjectGuid(HIGHGUID_PLAYER, auction->owner)), auction, MAIL_CHECK_MASK_COPIED);
RemoveAItem(auction->item_guidlow);
RemoveAItem(auction->itemGuidLow);
auction->DeleteFromDB();
delete auction;
@ -547,7 +551,22 @@ void AuctionHouseObject::Update()
{
next = itr;
++next;
if (curTime > (itr->second->expire_time))
if (itr->second->moneyDeliveryTime)
{
if (curTime > itr->second->moneyDeliveryTime)
{
sAuctionMgr.SendAuctionSuccessfulMail( itr->second );
itr->second->DeleteFromDB();
sAuctionMgr.RemoveAItem(itr->second->itemGuidLow);
delete itr->second;
RemoveAuction(itr->first);
}
}
else
{
if (curTime > itr->second->expireTime)
{
///- Either cancel the auction if there was no bidder
if (itr->second->bidder == 0)
@ -557,27 +576,28 @@ void AuctionHouseObject::Update()
///- Or perform the transaction
else
{
//we should send an "item sold" message if the seller is online
//we send the item to the winner
//we send the money to the seller
sAuctionMgr.SendAuctionSuccessfulMail( itr->second );
itr->second->moneyDeliveryTime = time(NULL) + HOUR;
sAuctionMgr.SendAuctionWonMail( itr->second );
continue;
}
///- In any case clear the auction
itr->second->DeleteFromDB();
sAuctionMgr.RemoveAItem(itr->second->item_guidlow);
sAuctionMgr.RemoveAItem(itr->second->itemGuidLow);
delete itr->second;
RemoveAuction(itr->first);
}
}
}
}
void AuctionHouseObject::BuildListBidderItems(WorldPacket& data, Player* player, uint32& count, uint32& totalcount)
{
for (AuctionEntryMap::const_iterator itr = AuctionsMap.begin();itr != AuctionsMap.end();++itr)
{
AuctionEntry *Aentry = itr->second;
if(Aentry->moneyDeliveryTime)
continue;
if( Aentry && Aentry->bidder == player->GetGUIDLow() )
{
if (itr->second->BuildAuctionInfo(data))
@ -592,6 +612,8 @@ void AuctionHouseObject::BuildListOwnerItems(WorldPacket& data, Player* player,
for (AuctionEntryMap::const_iterator itr = AuctionsMap.begin();itr != AuctionsMap.end();++itr)
{
AuctionEntry *Aentry = itr->second;
if(Aentry->moneyDeliveryTime)
continue;
if( Aentry && Aentry->owner == player->GetGUIDLow() )
{
if(Aentry->BuildAuctionInfo(data))
@ -611,7 +633,9 @@ void AuctionHouseObject::BuildListAuctionItems(WorldPacket& data, Player* player
for (AuctionEntryMap::const_iterator itr = AuctionsMap.begin();itr != AuctionsMap.end();++itr)
{
AuctionEntry *Aentry = itr->second;
Item *item = sAuctionMgr.GetAItem(Aentry->item_guidlow);
if(Aentry->moneyDeliveryTime)
continue;
Item *item = sAuctionMgr.GetAItem(Aentry->itemGuidLow);
if (!item)
continue;
@ -662,10 +686,45 @@ void AuctionHouseObject::BuildListAuctionItems(WorldPacket& data, Player* player
}
}
void AuctionHouseObject::BuildListPendingSales(WorldPacket& data, Player* player, uint32& count)
{
for (AuctionEntryMap::const_iterator itr = AuctionsMap.begin(); itr != AuctionsMap.end(); ++itr)
{
AuctionEntry *Aentry = itr->second;
if(!Aentry->moneyDeliveryTime)
continue;
if( Aentry && Aentry->owner == player->GetGUIDLow() )
{
Item *pItem = sAuctionMgr.GetAItem(Aentry->itemGuidLow);
if (!pItem)
{
sLog.outError("Auction: item with guid %u doesn't exist!", Aentry->itemGuidLow);
continue;
}
std::ostringstream str1;
str1 << Aentry->itemTemplate << ":" << pItem->GetItemRandomPropertyId() << ":" << AUCTION_SUCCESSFUL << ":" << Aentry->Id << ":" << pItem->GetCount();
std::ostringstream str2;
str2.width(16);
str2 << std::right << std::hex << Aentry->bidder << std::dec << ":";
str2 << Aentry->bid << ":" << Aentry->buyout << ":" << Aentry->deposit << ":" << Aentry->GetAuctionCut();
data << str1.str(); // string "%d:%d:%d:%d:%d" -> itemId, ItemRandomPropertyId, 2, auctionId, unk1 (stack size?, unused)
data << str2.str(); // string "%16I64X:%d:%d:%d:%d" -> bidderGuid, bid, buyout, deposit, auctionCut
data << uint32(97250); // unk1
data << uint32(68); // unk2
float timeLeft = float(Aentry->moneyDeliveryTime - time(NULL)) / float(DAY);
data << float(timeLeft); // time left
++count;
}
}
}
// this function inserts to WorldPacket auction's data
bool AuctionEntry::BuildAuctionInfo(WorldPacket & data) const
{
Item *pItem = sAuctionMgr.GetAItem(item_guidlow);
Item *pItem = sAuctionMgr.GetAItem(itemGuidLow);
if (!pItem)
{
sLog.outError("auction to item, that doesn't exist !!!!");
@ -685,13 +744,12 @@ bool AuctionEntry::BuildAuctionInfo(WorldPacket & data) const
data << uint32(pItem->GetItemSuffixFactor()); // SuffixFactor
data << uint32(pItem->GetCount()); // item->count
data << uint32(pItem->GetSpellCharges()); // item->charge FFFFFFF
data << uint32(0); //Unknown
data << uint32(0); // item flags (dynamic?) (0x04 no lockId?)
data << ObjectGuid(HIGHGUID_PLAYER, owner); // Auction->owner
data << uint32(startbid); // Auction->startbid (not sure if useful)
data << uint32(bid ? GetAuctionOutBid() : 0);
//minimal outbid
data << uint32(bid ? GetAuctionOutBid() : 0); // minimal outbid
data << uint32(buyout); // auction->buyout
data << uint32((expire_time-time(NULL))*IN_MILLISECONDS);//time left
data << uint32((expireTime-time(NULL))*IN_MILLISECONDS);// time left
data << ObjectGuid(HIGHGUID_PLAYER, bidder); // auction->bidder current
data << uint32(bid); // current bid
return true;
@ -720,7 +778,7 @@ void AuctionEntry::DeleteFromDB() const
void AuctionEntry::SaveToDB() const
{
//No SQL injection (no strings)
CharacterDatabase.PExecute("INSERT INTO auction (id,houseid,itemguid,item_template,itemowner,buyoutprice,time,buyguid,lastbid,startbid,deposit) "
"VALUES ('%u', '%u', '%u', '%u', '%u', '%u', '" UI64FMTD "', '%u', '%u', '%u', '%u')",
Id, auctionHouseEntry->houseId, item_guidlow, item_template, owner, buyout, (uint64)expire_time, bidder, bid, startbid, deposit);
CharacterDatabase.PExecute("INSERT INTO auction (id,houseid,itemguid,item_template,itemowner,buyoutprice,time,moneyTime,buyguid,lastbid,startbid,deposit) "
"VALUES ('%u', '%u', '%u', '%u', '%u', '%u', '" UI64FMTD "', '" UI64FMTD "', '%u', '%u', '%u', '%u')",
Id, auctionHouseEntry->houseId, itemGuidLow, itemTemplate, owner, buyout, (uint64)expireTime, (uint64)moneyDeliveryTime, bidder, bid, startbid, deposit);
}

View file

@ -33,30 +33,35 @@ class WorldPacket;
enum AuctionError
{
AUCTION_OK = 0,
AUCTION_INTERNAL_ERROR = 2,
AUCTION_NOT_ENOUGHT_MONEY = 3,
AUCTION_ITEM_NOT_FOUND = 4,
CANNOT_BID_YOUR_AUCTION_ERROR = 10
AUCTION_OK = 0, // depends on enum AuctionAction
AUCTION_ERR_INVENTORY = 1, // depends on enum InventoryChangeResult
AUCTION_ERR_DATABASE = 2, // ERR_AUCTION_DATABASE_ERROR (default)
AUCTION_ERR_NOT_ENOUGH_MONEY = 3, // ERR_NOT_ENOUGH_MONEY
AUCTION_ERR_ITEM_NOT_FOUND = 4, // ERR_ITEM_NOT_FOUND
AUCTION_ERR_HIGHER_BID = 5, // ERR_AUCTION_HIGHER_BID
AUCTION_ERR_BID_INCREMENT = 7, // ERR_AUCTION_BID_INCREMENT
AUCTION_ERR_BID_OWN = 10, // ERR_AUCTION_BID_OWN
AUCTION_ERR_RESTRICTED_ACCOUNT = 13 // ERR_RESTRICTED_ACCOUNT
};
enum AuctionAction
{
AUCTION_SELL_ITEM = 0,
AUCTION_CANCEL = 1,
AUCTION_PLACE_BID = 2
AUCTION_STARTED = 0, // ERR_AUCTION_STARTED
AUCTION_REMOVED = 1, // ERR_AUCTION_REMOVED
AUCTION_BID_PLACED = 2 // ERR_AUCTION_BID_PLACED
};
struct AuctionEntry
{
uint32 Id;
uint32 item_guidlow;
uint32 item_template;
uint32 itemGuidLow;
uint32 itemTemplate;
uint32 owner;
uint32 startbid; // maybe useless
uint32 bid;
uint32 buyout;
time_t expire_time;
time_t expireTime;
time_t moneyDeliveryTime;
uint32 bidder;
uint32 deposit; // deposit can be calculated only when creating auction
AuctionHouseEntry const* auctionHouseEntry; // in AuctionHouse.dbc
@ -107,6 +112,7 @@ class AuctionHouseObject
void BuildListBidderItems(WorldPacket& data, Player* player, uint32& count, uint32& totalcount);
void BuildListOwnerItems(WorldPacket& data, Player* player, uint32& count, uint32& totalcount);
void BuildListPendingSales(WorldPacket& data, Player* player, uint32& count);
void BuildListAuctionItems(WorldPacket& data, Player* player,
std::wstring const& searchedname, uint32 listfrom, uint32 levelmin, uint32 levelmax, uint32 usable,
uint32 inventoryType, uint32 itemClass, uint32 itemSubClass, uint32 quality,

View file

@ -26,6 +26,8 @@
#include "Common.h"
#include "SharedDefines.h"
#include "ObjectGuid.h"
#include "AuctionHouseMgr.h"
#include "Item.h"
struct ItemPrototype;
struct AuctionEntry;
@ -348,10 +350,11 @@ class MANGOS_DLL_SPEC WorldSession
//auction
void SendAuctionHello(Unit *unit);
void SendAuctionCommandResult( uint32 auctionId, uint32 Action, uint32 ErrorCode, uint32 bidError = 0);
void SendAuctionBidderNotification( uint32 location, uint32 auctionId, ObjectGuid bidderGuid, uint32 bidSum, uint32 diff, uint32 item_template);
void SendAuctionCommandResult(AuctionEntry *auc, AuctionAction Action, AuctionError ErrorCode, InventoryResult invError = EQUIP_ERR_OK);
void SendAuctionBidderNotification(AuctionEntry *auction);
void SendAuctionOwnerNotification(AuctionEntry *auction);
void SendAuctionOutbiddedMail( AuctionEntry * auction, uint32 newPrice );
void SendAuctionRemovedNotification(AuctionEntry* auction);
void SendAuctionOutbiddedMail(AuctionEntry *auction);
void SendAuctionCancelledToBidderMail(AuctionEntry *auction);
AuctionHouseEntry const* GetCheckedAuctionHouseForAuctioneer(ObjectGuid guid);

View file

@ -1,4 +1,4 @@
#ifndef __REVISION_NR_H__
#define __REVISION_NR_H__
#define REVISION_NR "11390"
#define REVISION_NR "11391"
#endif // __REVISION_NR_H__

View file

@ -1,6 +1,6 @@
#ifndef __REVISION_SQL_H__
#define __REVISION_SQL_H__
#define REVISION_DB_CHARACTERS "required_11299_02_characters_pet_aura"
#define REVISION_DB_CHARACTERS "required_11391_01_characters_auction"
#define REVISION_DB_MANGOS "required_11385_01_mangos_creature_template"
#define REVISION_DB_REALMD "required_10008_01_realmd_realmd_db_version"
#endif // __REVISION_SQL_H__