DB Layer:
- Make SQL Transactions actual objects used in code. (Thanks to Derex for the idea) * Uncommitted transactions will be automatically rolled back and cleaned up using ACE_Refcounted_Auto_Ptr, so no need to call Rollback() in the code. * Prevents recursive transactions and makes developers aware of transactions going on. * Gets rid of unneccesary overhead iterating over a concurrent map. - Some cleanups in affected code, including better usage of transaction control in AH / mail related code to prevent data loss. *** Experimental, use at own risk, recommended to backup your DBs. *** --HG-- branch : trunk
This commit is contained in:
@@ -245,6 +245,8 @@ void WorldSession::HandleSendMail(WorldPacket & recv_data)
|
||||
|
||||
MailDraft draft(subject, body);
|
||||
|
||||
SQLTransaction trans = CharacterDatabase.BeginTransaction();
|
||||
|
||||
if (items_count > 0 || money > 0)
|
||||
{
|
||||
if (items_count > 0)
|
||||
@@ -260,12 +262,11 @@ void WorldSession::HandleSendMail(WorldPacket & recv_data)
|
||||
|
||||
item->SetNotRefundable(GetPlayer()); // makes the item no longer refundable
|
||||
pl->MoveItemFromInventory(items[i]->GetBagSlot(), item->GetSlot(), true);
|
||||
CharacterDatabase.BeginTransaction();
|
||||
item->DeleteFromInventoryDB(); // deletes item from character's inventory
|
||||
item->SaveToDB(); // recursive and not have transaction guard into self, item not in inventory and can be save standalone
|
||||
|
||||
item->DeleteFromInventoryDB(trans); // deletes item from character's inventory
|
||||
item->SaveToDB(trans); // recursive and not have transaction guard into self, item not in inventory and can be save standalone
|
||||
// owner in data will set at mail receive and item extracting
|
||||
CharacterDatabase.PExecute("UPDATE item_instance SET owner_guid = '%u' WHERE guid='%u'", GUID_LOPART(rc), item->GetGUIDLow());
|
||||
CharacterDatabase.CommitTransaction();
|
||||
trans->PAppend("UPDATE item_instance SET owner_guid = '%u' WHERE guid='%u'", GUID_LOPART(rc), item->GetGUIDLow());
|
||||
draft.AddItem(item);
|
||||
}
|
||||
|
||||
@@ -287,11 +288,10 @@ void WorldSession::HandleSendMail(WorldPacket & recv_data)
|
||||
draft
|
||||
.AddMoney(money)
|
||||
.AddCOD(COD)
|
||||
.SendMailTo(MailReceiver(receive, GUID_LOPART(rc)), pl, body.empty() ? MAIL_CHECK_MASK_COPIED : MAIL_CHECK_MASK_HAS_BODY, deliver_delay);
|
||||
.SendMailTo(trans, MailReceiver(receive, GUID_LOPART(rc)), pl, body.empty() ? MAIL_CHECK_MASK_COPIED : MAIL_CHECK_MASK_HAS_BODY, deliver_delay);
|
||||
|
||||
CharacterDatabase.BeginTransaction();
|
||||
pl->SaveInventoryAndGoldToDB();
|
||||
CharacterDatabase.CommitTransaction();
|
||||
pl->SaveInventoryAndGoldToDB(trans);
|
||||
CharacterDatabase.CommitTransaction(trans);
|
||||
}
|
||||
|
||||
//called when mail is read
|
||||
@@ -366,10 +366,10 @@ void WorldSession::HandleMailReturnToSender(WorldPacket & recv_data)
|
||||
}
|
||||
//we can return mail now
|
||||
//so firstly delete the old one
|
||||
CharacterDatabase.BeginTransaction();
|
||||
CharacterDatabase.PExecute("DELETE FROM mail WHERE id = '%u'", mailId); // needed?
|
||||
CharacterDatabase.PExecute("DELETE FROM mail_items WHERE mail_id = '%u'", mailId);
|
||||
CharacterDatabase.CommitTransaction();
|
||||
SQLTransaction trans = CharacterDatabase.BeginTransaction();
|
||||
trans->PAppend("DELETE FROM mail WHERE id = '%u'", mailId); // needed?
|
||||
trans->PAppend("DELETE FROM mail_items WHERE mail_id = '%u'", mailId);
|
||||
CharacterDatabase.CommitTransaction(trans);
|
||||
pl->RemoveMail(mailId);
|
||||
|
||||
// only return mail if the player exists (and delete if not existing)
|
||||
@@ -436,6 +436,7 @@ void WorldSession::HandleMailTakeItem(WorldPacket & recv_data)
|
||||
uint8 msg = _player->CanStoreItem(NULL_BAG, NULL_SLOT, dest, it, false);
|
||||
if (msg == EQUIP_ERR_OK)
|
||||
{
|
||||
SQLTransaction trans = CharacterDatabase.BeginTransaction();
|
||||
m->RemoveItem(itemId);
|
||||
m->removedItems.push_back(itemId);
|
||||
|
||||
@@ -473,7 +474,7 @@ void WorldSession::HandleMailTakeItem(WorldPacket & recv_data)
|
||||
{
|
||||
MailDraft(m->subject, "")
|
||||
.AddMoney(m->COD)
|
||||
.SendMailTo(MailReceiver(receive, m->sender), MailSender(MAIL_NORMAL, m->receiver), MAIL_CHECK_MASK_COD_PAYMENT);
|
||||
.SendMailTo(trans, MailReceiver(receive, m->sender), MailSender(MAIL_NORMAL, m->receiver), MAIL_CHECK_MASK_COD_PAYMENT);
|
||||
}
|
||||
|
||||
pl->ModifyMoney(-int32(m->COD));
|
||||
@@ -486,10 +487,9 @@ void WorldSession::HandleMailTakeItem(WorldPacket & recv_data)
|
||||
uint32 count = it->GetCount(); // save counts before store and possible merge with deleting
|
||||
pl->MoveItemToInventory(dest,it,true);
|
||||
|
||||
CharacterDatabase.BeginTransaction();
|
||||
pl->SaveInventoryAndGoldToDB();
|
||||
pl->_SaveMail();
|
||||
CharacterDatabase.CommitTransaction();
|
||||
pl->SaveInventoryAndGoldToDB(trans);
|
||||
pl->_SaveMail(trans);
|
||||
CharacterDatabase.CommitTransaction(trans);
|
||||
|
||||
pl->SendMailResult(mailId, MAIL_ITEM_TAKEN, MAIL_OK, 0, itemId, count);
|
||||
}
|
||||
@@ -524,10 +524,10 @@ void WorldSession::HandleMailTakeMoney(WorldPacket & recv_data)
|
||||
pl->m_mailsUpdated = true;
|
||||
|
||||
// save money and mail to prevent cheating
|
||||
CharacterDatabase.BeginTransaction();
|
||||
pl->SaveGoldToDB();
|
||||
pl->_SaveMail();
|
||||
CharacterDatabase.CommitTransaction();
|
||||
SQLTransaction trans = CharacterDatabase.BeginTransaction();
|
||||
pl->SaveGoldToDB(trans);
|
||||
pl->_SaveMail(trans);
|
||||
CharacterDatabase.CommitTransaction(trans);
|
||||
}
|
||||
|
||||
//called when player lists his received mails
|
||||
@@ -826,7 +826,7 @@ MailDraft& MailDraft::AddItem(Item* item)
|
||||
m_items[item->GetGUIDLow()] = item; return *this;
|
||||
}
|
||||
|
||||
void MailDraft::prepareItems(Player* receiver)
|
||||
void MailDraft::prepareItems(Player* receiver, SQLTransaction& trans)
|
||||
{
|
||||
if (!m_mailTemplateId || !m_mailTemplateItemsNeed)
|
||||
return;
|
||||
@@ -845,21 +845,21 @@ void MailDraft::prepareItems(Player* receiver)
|
||||
{
|
||||
if (Item* item = Item::CreateItem(lootitem->itemid,lootitem->count,receiver))
|
||||
{
|
||||
item->SaveToDB(); // save for prevent lost at next mail load, if send fail then item will deleted
|
||||
item->SaveToDB(trans); // save for prevent lost at next mail load, if send fail then item will deleted
|
||||
AddItem(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MailDraft::deleteIncludedItems(bool inDB /*= false*/)
|
||||
void MailDraft::deleteIncludedItems(bool inDB /*= false*/, SQLTransaction& trans)
|
||||
{
|
||||
for (MailItemMap::iterator mailItemIter = m_items.begin(); mailItemIter != m_items.end(); ++mailItemIter)
|
||||
{
|
||||
Item* item = mailItemIter->second;
|
||||
|
||||
if (inDB)
|
||||
CharacterDatabase.PExecute("DELETE FROM item_instance WHERE guid='%u'", item->GetGUIDLow());
|
||||
trans->PAppend("DELETE FROM item_instance WHERE guid='%u'", item->GetGUIDLow());
|
||||
|
||||
delete item;
|
||||
}
|
||||
@@ -875,9 +875,11 @@ void MailDraft::SendReturnToSender(uint32 sender_acc, uint32 sender_guid, uint32
|
||||
if (!receiver)
|
||||
rc_account = sObjectMgr.GetPlayerAccountIdByGUID(MAKE_NEW_GUID(receiver_guid, 0, HIGHGUID_PLAYER));
|
||||
|
||||
SQLTransaction trans = CharacterDatabase.BeginTransaction();
|
||||
|
||||
if (!receiver && !rc_account) // sender not exist
|
||||
{
|
||||
deleteIncludedItems(true);
|
||||
deleteIncludedItems(true, trans);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -890,30 +892,29 @@ void MailDraft::SendReturnToSender(uint32 sender_acc, uint32 sender_guid, uint32
|
||||
needItemDelay = sender_acc != rc_account;
|
||||
|
||||
// set owner to new receiver (to prevent delete item with sender char deleting)
|
||||
CharacterDatabase.BeginTransaction();
|
||||
for (MailItemMap::iterator mailItemIter = m_items.begin(); mailItemIter != m_items.end(); ++mailItemIter)
|
||||
{
|
||||
Item* item = mailItemIter->second;
|
||||
item->SaveToDB(); // item not in inventory and can be save standalone
|
||||
item->SaveToDB(trans); // item not in inventory and can be save standalone
|
||||
// owner in data will set at mail receive and item extracting
|
||||
CharacterDatabase.PExecute("UPDATE item_instance SET owner_guid = '%u' WHERE guid='%u'", receiver_guid, item->GetGUIDLow());
|
||||
trans->PAppend("UPDATE item_instance SET owner_guid = '%u' WHERE guid='%u'", receiver_guid, item->GetGUIDLow());
|
||||
}
|
||||
CharacterDatabase.CommitTransaction();
|
||||
}
|
||||
|
||||
// If theres is an item, there is a one hour delivery delay.
|
||||
uint32 deliver_delay = needItemDelay ? sWorld.getConfig(CONFIG_MAIL_DELIVERY_DELAY) : 0;
|
||||
|
||||
// will delete item or place to receiver mail list
|
||||
SendMailTo(MailReceiver(receiver,receiver_guid), MailSender(MAIL_NORMAL, sender_guid), MAIL_CHECK_MASK_RETURNED, deliver_delay);
|
||||
SendMailTo(trans,MailReceiver(receiver,receiver_guid), MailSender(MAIL_NORMAL, sender_guid), MAIL_CHECK_MASK_RETURNED, deliver_delay);
|
||||
CharacterDatabase.CommitTransaction(trans);
|
||||
}
|
||||
|
||||
void MailDraft::SendMailTo(MailReceiver const& receiver, MailSender const& sender, MailCheckMask checked, uint32 deliver_delay)
|
||||
void MailDraft::SendMailTo(SQLTransaction& trans, MailReceiver const& receiver, MailSender const& sender, MailCheckMask checked, uint32 deliver_delay)
|
||||
{
|
||||
Player* pReceiver = receiver.GetPlayer(); // can be NULL
|
||||
|
||||
if (pReceiver)
|
||||
prepareItems(pReceiver); // generate mail template items
|
||||
prepareItems(pReceiver, trans); // generate mail template items
|
||||
|
||||
uint32 mailId = sObjectMgr.GenerateMailID();
|
||||
|
||||
@@ -937,20 +938,19 @@ void MailDraft::SendMailTo(MailReceiver const& receiver, MailSender const& sende
|
||||
// Add to DB
|
||||
std::string safe_subject = GetSubject();
|
||||
std::string safe_body = GetBody();
|
||||
CharacterDatabase.BeginTransaction();
|
||||
|
||||
CharacterDatabase.escape_string(safe_subject);
|
||||
CharacterDatabase.escape_string(safe_body);
|
||||
|
||||
CharacterDatabase.PExecute("INSERT INTO mail (id,messageType,stationery,mailTemplateId,sender,receiver,subject,body,has_items,expire_time,deliver_time,money,cod,checked) "
|
||||
trans->PAppend("INSERT INTO mail (id,messageType,stationery,mailTemplateId,sender,receiver,subject,body,has_items,expire_time,deliver_time,money,cod,checked) "
|
||||
"VALUES ('%u', '%u', '%u', '%u', '%u', '%u', '%s', '%s', '%u', '" UI64FMTD "','" UI64FMTD "', '%u', '%u', '%d')",
|
||||
mailId, sender.GetMailMessageType(), sender.GetStationery(), GetMailTemplateId(), sender.GetSenderId(), receiver.GetPlayerGUIDLow(), safe_subject.c_str(), safe_body.c_str(),(m_items.empty() ? 0 : 1), (uint64)expire_time, (uint64)deliver_time, m_money, m_COD, checked);
|
||||
|
||||
for (MailItemMap::const_iterator mailItemIter = m_items.begin(); mailItemIter != m_items.end(); ++mailItemIter)
|
||||
{
|
||||
Item* item = mailItemIter->second;
|
||||
CharacterDatabase.PExecute("INSERT INTO mail_items (mail_id,item_guid,item_template,receiver) VALUES ('%u', '%u', '%u','%u')", mailId, item->GetGUIDLow(), item->GetEntry(), receiver.GetPlayerGUIDLow());
|
||||
trans->PAppend("INSERT INTO mail_items (mail_id,item_guid,item_template,receiver) VALUES ('%u', '%u', '%u','%u')", mailId, item->GetGUIDLow(), item->GetEntry(), receiver.GetPlayerGUIDLow());
|
||||
}
|
||||
CharacterDatabase.CommitTransaction();
|
||||
|
||||
// For online receiver update in game mail status and data
|
||||
if (pReceiver)
|
||||
|
||||
@@ -128,16 +128,19 @@ class MailDraft
|
||||
uint32 GetMoney() const { return m_money; }
|
||||
uint32 GetCOD() const { return m_COD; }
|
||||
std::string const& GetBody() const { return m_body; }
|
||||
|
||||
public: // modifiers
|
||||
MailDraft& AddItem(Item* item);
|
||||
MailDraft& AddMoney(uint32 money) { m_money = money; return *this; }
|
||||
MailDraft& AddCOD(uint32 COD) { m_COD = COD; return *this; }
|
||||
|
||||
public: // finishers
|
||||
void SendReturnToSender(uint32 sender_acc, uint32 sender_guid, uint32 receiver_guid);
|
||||
void SendMailTo(MailReceiver const& receiver, MailSender const& sender, MailCheckMask checked = MAIL_CHECK_MASK_NONE, uint32 deliver_delay = 0);
|
||||
void SendMailTo(SQLTransaction& trans, MailReceiver const& receiver, MailSender const& sender, MailCheckMask checked = MAIL_CHECK_MASK_NONE, uint32 deliver_delay = 0);
|
||||
|
||||
private:
|
||||
void deleteIncludedItems(bool inDB = false);
|
||||
void prepareItems(Player* receiver); // called from SendMailTo for generate mailTemplateBase items
|
||||
void deleteIncludedItems(bool inDB = false, SQLTransaction& trans = SQLTransaction(NULL));
|
||||
void prepareItems(Player* receiver, SQLTransaction& trans); // called from SendMailTo for generate mailTemplateBase items
|
||||
|
||||
uint16 m_mailTemplateId;
|
||||
bool m_mailTemplateItemsNeed;
|
||||
|
||||
Reference in New Issue
Block a user