void HandleAuctionSellItem(AuctionSellItem packet) { foreach (var aitem in packet.Items) { if (aitem.Guid.IsEmpty() || aitem.UseCount == 0 || aitem.UseCount > 1000) { return; } } if (packet.MinBid == 0 || packet.RunTime == 0) { return; } if (packet.MinBid > PlayerConst.MaxMoneyAmount || packet.BuyoutPrice > PlayerConst.MaxMoneyAmount) { Log.outDebug(LogFilter.Network, "WORLD: HandleAuctionSellItem - Player {0} ({1}) attempted to sell item with higher price than max gold amount.", GetPlayer().GetName(), GetPlayer().GetGUID().ToString()); SendAuctionCommandResult(null, AuctionAction.SellItem, AuctionError.DatabaseError); return; } Creature creature = GetPlayer().GetNPCIfCanInteractWith(packet.Auctioneer, NPCFlags.Auctioneer, NPCFlags2.None); if (!creature) { Log.outDebug(LogFilter.Network, "WORLD: HandleAuctionSellItem - {0} not found or you can't interact with him.", packet.Auctioneer.ToString()); return; } uint houseId = 0; AuctionHouseRecord auctionHouseEntry = Global.AuctionMgr.GetAuctionHouseEntry(creature.GetFaction(), ref houseId); if (auctionHouseEntry == null) { Log.outDebug(LogFilter.Network, "WORLD: HandleAuctionSellItem - {0} has wrong faction.", packet.Auctioneer.ToString()); return; } packet.RunTime *= Time.Minute; switch (packet.RunTime) { case 1 * SharedConst.MinAuctionTime: case 2 * SharedConst.MinAuctionTime: case 4 * SharedConst.MinAuctionTime: break; default: return; } if (GetPlayer().HasUnitState(UnitState.Died)) { GetPlayer().RemoveAurasByType(AuraType.FeignDeath); } uint finalCount = 0; Item[] items = new Item[packet.Items.Count]; for (var i = 0; i < packet.Items.Count; ++i) { items[i] = GetPlayer().GetItemByGuid(packet.Items[i].Guid); if (!items[i]) { SendAuctionCommandResult(null, AuctionAction.SellItem, AuctionError.ItemNotFound); return; } if (Global.AuctionMgr.GetAItem(items[i].GetGUID().GetCounter()) || !items[i].CanBeTraded() || items[i].IsNotEmptyBag() || items[i].GetTemplate().GetFlags().HasAnyFlag(ItemFlags.Conjured) || items[i].m_itemData.Expiration != 0 || items[i].GetCount() < packet.Items[i].UseCount) { SendAuctionCommandResult(null, AuctionAction.SellItem, AuctionError.DatabaseError); return; } finalCount += packet.Items[i].UseCount; } if (packet.Items.Empty()) { SendAuctionCommandResult(null, AuctionAction.SellItem, AuctionError.DatabaseError); return; } if (finalCount == 0) { SendAuctionCommandResult(null, AuctionAction.SellItem, AuctionError.DatabaseError); return; } // check if there are 2 identical guids, in this case user is most likely cheating for (int i = 0; i < packet.Items.Count; ++i) { for (int j = i + 1; j < packet.Items.Count; ++j) { if (packet.Items[i].Guid == packet.Items[j].Guid) { SendAuctionCommandResult(null, AuctionAction.SellItem, AuctionError.DatabaseError); return; } if (items[i].GetEntry() != items[j].GetEntry()) { SendAuctionCommandResult(null, AuctionAction.SellItem, AuctionError.ItemNotFound); return; } } } for (var i = 0; i < packet.Items.Count; ++i) { if (items[i].GetMaxStackCount() < finalCount) { SendAuctionCommandResult(null, AuctionAction.SellItem, AuctionError.DatabaseError); return; } } Item item = items[0]; uint auctionTime = (uint)(packet.RunTime * WorldConfig.GetFloatValue(WorldCfg.RateAuctionTime)); AuctionHouseObject auctionHouse = Global.AuctionMgr.GetAuctionsMap(creature.GetFaction()); ulong deposit = Global.AuctionMgr.GetAuctionDeposit(auctionHouseEntry, packet.RunTime, item, finalCount); if (!GetPlayer().HasEnoughMoney(deposit)) { SendAuctionCommandResult(null, AuctionAction.SellItem, AuctionError.NotEnoughtMoney); return; } AuctionEntry AH = new AuctionEntry(); SQLTransaction trans; if (WorldConfig.GetBoolValue(WorldCfg.AllowTwoSideInteractionAuction)) { AH.auctioneer = 23442; //@TODO - HARDCODED DB GUID, BAD BAD BAD } else { AH.auctioneer = creature.GetSpawnId(); } // Required stack size of auction matches to current item stack size, just move item to auctionhouse if (packet.Items.Count == 1 && item.GetCount() == packet.Items[0].UseCount) { if (HasPermission(RBACPermissions.LogGmTrade)) { Log.outCommand(GetAccountId(), "GM {0} (Account: {1}) create auction: {2} (Entry: {3} Count: {4})", GetPlayerName(), GetAccountId(), item.GetTemplate().GetName(), item.GetEntry(), item.GetCount()); } AH.Id = Global.ObjectMgr.GenerateAuctionID(); AH.itemGUIDLow = item.GetGUID().GetCounter(); AH.itemEntry = item.GetEntry(); AH.itemCount = item.GetCount(); AH.owner = GetPlayer().GetGUID().GetCounter(); AH.startbid = (uint)packet.MinBid; AH.bidder = 0; AH.bid = 0; AH.buyout = (uint)packet.BuyoutPrice; AH.expire_time = Time.UnixTime + auctionTime; AH.deposit = deposit; AH.etime = packet.RunTime; AH.auctionHouseEntry = auctionHouseEntry; Log.outInfo(LogFilter.Network, "CMSG_AUCTION_SELL_ITEM: {0} {1} is selling item {2} {3} to auctioneer {4} with count {5} with initial bid {6} with buyout {7} and with time {8} (in sec) in auctionhouse {9}", GetPlayer().GetGUID().ToString(), GetPlayer().GetName(), item.GetGUID().ToString(), item.GetTemplate().GetName(), AH.auctioneer, item.GetCount(), packet.MinBid, packet.BuyoutPrice, auctionTime, AH.GetHouseId()); Global.AuctionMgr.AddAItem(item); auctionHouse.AddAuction(AH); GetPlayer().MoveItemFromInventory(item.GetBagSlot(), item.GetSlot(), true); trans = new SQLTransaction(); item.DeleteFromInventoryDB(trans); item.SaveToDB(trans); AH.SaveToDB(trans); GetPlayer().SaveInventoryAndGoldToDB(trans); DB.Characters.CommitTransaction(trans); SendAuctionCommandResult(AH, AuctionAction.SellItem, AuctionError.Ok); GetPlayer().UpdateCriteria(CriteriaTypes.CreateAuction, 1); } else // Required stack size of auction does not match to current item stack size, clone item and set correct stack size { Item newItem = item.CloneItem(finalCount, GetPlayer()); if (!newItem) { Log.outError(LogFilter.Network, "CMSG_AuctionAction.SellItem: Could not create clone of item {0}", item.GetEntry()); SendAuctionCommandResult(null, AuctionAction.SellItem, AuctionError.DatabaseError); return; } if (HasPermission(RBACPermissions.LogGmTrade)) { Log.outCommand(GetAccountId(), "GM {0} (Account: {1}) create auction: {2} (Entry: {3} Count: {4})", GetPlayerName(), GetAccountId(), newItem.GetTemplate().GetName(), newItem.GetEntry(), newItem.GetCount()); } AH.Id = Global.ObjectMgr.GenerateAuctionID(); AH.itemGUIDLow = newItem.GetGUID().GetCounter(); AH.itemEntry = newItem.GetEntry(); AH.itemCount = newItem.GetCount(); AH.owner = GetPlayer().GetGUID().GetCounter(); AH.startbid = (uint)packet.MinBid; AH.bidder = 0; AH.bid = 0; AH.buyout = (uint)packet.BuyoutPrice; AH.expire_time = Time.UnixTime + auctionTime; AH.deposit = deposit; AH.etime = packet.RunTime; AH.auctionHouseEntry = auctionHouseEntry; Log.outInfo(LogFilter.Network, "CMSG_AuctionAction.SellItem: {0} {1} is selling {2} {3} to auctioneer {4} with count {5} with initial bid {6} with buyout {7} and with time {8} (in sec) in auctionhouse {9}", GetPlayer().GetGUID().ToString(), GetPlayer().GetName(), newItem.GetGUID().ToString(), newItem.GetTemplate().GetName(), AH.auctioneer, newItem.GetCount(), packet.MinBid, packet.BuyoutPrice, auctionTime, AH.GetHouseId()); Global.AuctionMgr.AddAItem(newItem); auctionHouse.AddAuction(AH); for (var i = 0; i < packet.Items.Count; ++i) { Item item2 = items[i]; // Item stack count equals required count, ready to delete item - cloned item will be used for auction if (item2.GetCount() == packet.Items[i].UseCount) { GetPlayer().MoveItemFromInventory(item2.GetBagSlot(), item2.GetSlot(), true); trans = new SQLTransaction(); item2.DeleteFromInventoryDB(trans); item2.DeleteFromDB(trans); DB.Characters.CommitTransaction(trans); } else // Item stack count is bigger than required count, update item stack count and save to database - cloned item will be used for auction { item2.SetCount(item2.GetCount() - packet.Items[i].UseCount); item2.SetState(ItemUpdateState.Changed, GetPlayer()); GetPlayer().ItemRemovedQuestCheck(item2.GetEntry(), packet.Items[i].UseCount); item2.SendUpdateToPlayer(GetPlayer()); trans = new SQLTransaction(); item2.SaveToDB(trans); DB.Characters.CommitTransaction(trans); } } trans = new SQLTransaction(); newItem.SaveToDB(trans); AH.SaveToDB(trans); GetPlayer().SaveInventoryAndGoldToDB(trans); DB.Characters.CommitTransaction(trans); SendAuctionCommandResult(AH, AuctionAction.SellItem, AuctionError.Ok); GetPlayer().UpdateCriteria(CriteriaTypes.CreateAuction, 1); } GetPlayer().ModifyMoney(-(long)deposit); }
public void SendAuctionWonMail(AuctionEntry auction, SQLTransaction trans) { Item item = GetAItem(auction.itemGUIDLow); if (!item) { return; } uint bidderAccId = 0; ObjectGuid bidderGuid = ObjectGuid.Create(HighGuid.Player, auction.bidder); Player bidder = Global.ObjAccessor.FindPlayer(bidderGuid); // data for gm.log string bidderName = ""; bool logGmTrade = false; if (bidder) { bidderAccId = bidder.GetSession().GetAccountId(); bidderName = bidder.GetName(); logGmTrade = bidder.GetSession().HasPermission(RBACPermissions.LogGmTrade); } else { bidderAccId = Global.CharacterCacheStorage.GetCharacterAccountIdByGuid(bidderGuid); logGmTrade = Global.AccountMgr.HasPermission(bidderAccId, RBACPermissions.LogGmTrade, Global.WorldMgr.GetRealm().Id.Realm); if (logGmTrade && !Global.CharacterCacheStorage.GetCharacterNameByGuid(bidderGuid, out bidderName)) { bidderName = Global.ObjectMgr.GetCypherString(CypherStrings.Unknown); } } if (logGmTrade) { ObjectGuid ownerGuid = ObjectGuid.Create(HighGuid.Player, auction.owner); string ownerName; if (!Global.CharacterCacheStorage.GetCharacterNameByGuid(ownerGuid, out ownerName)) { ownerName = Global.ObjectMgr.GetCypherString(CypherStrings.Unknown); } uint ownerAccId = Global.CharacterCacheStorage.GetCharacterAccountIdByGuid(ownerGuid); Log.outCommand(bidderAccId, $"GM {bidderName} (Account: {bidderAccId}) won item in auction: {item.GetTemplate().GetName()} (Entry: {item.GetEntry()} Count: {item.GetCount()}) and pay money: {auction.bid}. Original owner {ownerName} (Account: {ownerAccId})"); } // receiver exist if (bidder || bidderAccId != 0) { // set owner to bidder (to prevent delete item with sender char deleting) // owner in `data` will set at mail receive and item extracting PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.UPD_ITEM_OWNER); stmt.AddValue(0, auction.bidder); stmt.AddValue(1, item.GetGUID().GetCounter()); trans.Append(stmt); if (bidder) { bidder.GetSession().SendAuctionWonNotification(auction, item); // FIXME: for offline player need also bidder.UpdateCriteria(CriteriaTypes.WonAuctions, 1); } new MailDraft(auction.BuildAuctionMailSubject(MailAuctionAnswers.Won), AuctionEntry.BuildAuctionMailBody(auction.owner, auction.bid, auction.buyout, 0, 0)) .AddItem(item) .SendMailTo(trans, new MailReceiver(bidder, auction.bidder), new MailSender(auction), MailCheckMask.Copied); } else { // bidder doesn't exist, delete the item Global.AuctionMgr.RemoveAItem(auction.itemGUIDLow, true); } }
void HandleAcceptTrade(AcceptTrade acceptTrade) { TradeData my_trade = GetPlayer().GetTradeData(); if (my_trade == null) { return; } Player trader = my_trade.GetTrader(); TradeData his_trade = trader.GetTradeData(); if (his_trade == null) { return; } Item[] myItems = new Item[(int)TradeSlots.Count]; Item[] hisItems = new Item[(int)TradeSlots.Count]; // set before checks for propertly undo at problems (it already set in to client) my_trade.SetAccepted(true); TradeStatusPkt info = new TradeStatusPkt(); if (his_trade.GetServerStateIndex() != acceptTrade.StateIndex) { info.Status = TradeStatus.StateChanged; SendTradeStatus(info); my_trade.SetAccepted(false); return; } if (!GetPlayer().IsWithinDistInMap(trader, 11.11f, false)) { info.Status = TradeStatus.TooFarAway; SendTradeStatus(info); my_trade.SetAccepted(false); return; } // not accept case incorrect money amount if (!GetPlayer().HasEnoughMoney(my_trade.GetMoney())) { info.Status = TradeStatus.Failed; info.BagResult = InventoryResult.NotEnoughMoney; SendTradeStatus(info); my_trade.SetAccepted(false, true); return; } // not accept case incorrect money amount if (!trader.HasEnoughMoney(his_trade.GetMoney())) { info.Status = TradeStatus.Failed; info.BagResult = InventoryResult.NotEnoughMoney; trader.GetSession().SendTradeStatus(info); his_trade.SetAccepted(false, true); return; } if (GetPlayer().GetMoney() >= PlayerConst.MaxMoneyAmount - his_trade.GetMoney()) { info.Status = TradeStatus.Failed; info.BagResult = InventoryResult.TooMuchGold; SendTradeStatus(info); my_trade.SetAccepted(false, true); return; } if (trader.GetMoney() >= PlayerConst.MaxMoneyAmount - my_trade.GetMoney()) { info.Status = TradeStatus.Failed; info.BagResult = InventoryResult.TooMuchGold; trader.GetSession().SendTradeStatus(info); his_trade.SetAccepted(false, true); return; } // not accept if some items now can't be trade (cheating) for (byte i = 0; i < (byte)TradeSlots.Count; ++i) { Item item = my_trade.GetItem((TradeSlots)i); if (item) { if (!item.CanBeTraded(false, true)) { info.Status = TradeStatus.Cancelled; SendTradeStatus(info); return; } if (item.IsBindedNotWith(trader)) { info.Status = TradeStatus.Failed; info.BagResult = InventoryResult.TradeBoundItem; SendTradeStatus(info); return; } } item = his_trade.GetItem((TradeSlots)i); if (item) { if (!item.CanBeTraded(false, true)) { info.Status = TradeStatus.Cancelled; SendTradeStatus(info); return; } } } if (his_trade.IsAccepted()) { SetAcceptTradeMode(my_trade, his_trade, myItems, hisItems); Spell my_spell = null; SpellCastTargets my_targets = new SpellCastTargets(); Spell his_spell = null; SpellCastTargets his_targets = new SpellCastTargets(); // not accept if spell can't be casted now (cheating) uint my_spell_id = my_trade.GetSpell(); if (my_spell_id != 0) { SpellInfo spellEntry = Global.SpellMgr.GetSpellInfo(my_spell_id); Item castItem = my_trade.GetSpellCastItem(); if (spellEntry == null || !his_trade.GetItem(TradeSlots.NonTraded) || (my_trade.HasSpellCastItem() && !castItem)) { ClearAcceptTradeMode(my_trade, his_trade); ClearAcceptTradeMode(myItems, hisItems); my_trade.SetSpell(0); return; } my_spell = new Spell(GetPlayer(), spellEntry, TriggerCastFlags.FullMask); my_spell.m_CastItem = castItem; my_targets.SetTradeItemTarget(GetPlayer()); my_spell.m_targets = my_targets; SpellCastResult res = my_spell.CheckCast(true); if (res != SpellCastResult.SpellCastOk) { my_spell.SendCastResult(res); ClearAcceptTradeMode(my_trade, his_trade); ClearAcceptTradeMode(myItems, hisItems); my_spell.Dispose(); my_trade.SetSpell(0); return; } } // not accept if spell can't be casted now (cheating) uint his_spell_id = his_trade.GetSpell(); if (his_spell_id != 0) { SpellInfo spellEntry = Global.SpellMgr.GetSpellInfo(his_spell_id); Item castItem = his_trade.GetSpellCastItem(); if (spellEntry == null || !my_trade.GetItem(TradeSlots.NonTraded) || (his_trade.HasSpellCastItem() && !castItem)) { his_trade.SetSpell(0); ClearAcceptTradeMode(my_trade, his_trade); ClearAcceptTradeMode(myItems, hisItems); return; } his_spell = new Spell(trader, spellEntry, TriggerCastFlags.FullMask); his_spell.m_CastItem = castItem; his_targets.SetTradeItemTarget(trader); his_spell.m_targets = his_targets; SpellCastResult res = his_spell.CheckCast(true); if (res != SpellCastResult.SpellCastOk) { his_spell.SendCastResult(res); ClearAcceptTradeMode(my_trade, his_trade); ClearAcceptTradeMode(myItems, hisItems); my_spell.Dispose(); his_spell.Dispose(); his_trade.SetSpell(0); return; } } // inform partner client info.Status = TradeStatus.Accepted; trader.GetSession().SendTradeStatus(info); // test if item will fit in each inventory TradeStatusPkt myCanCompleteInfo = new TradeStatusPkt(); TradeStatusPkt hisCanCompleteInfo = new TradeStatusPkt(); hisCanCompleteInfo.BagResult = trader.CanStoreItems(myItems, (int)TradeSlots.TradedCount, ref hisCanCompleteInfo.ItemID); myCanCompleteInfo.BagResult = GetPlayer().CanStoreItems(hisItems, (int)TradeSlots.TradedCount, ref myCanCompleteInfo.ItemID); ClearAcceptTradeMode(myItems, hisItems); // in case of missing space report error if (myCanCompleteInfo.BagResult != InventoryResult.Ok) { ClearAcceptTradeMode(my_trade, his_trade); myCanCompleteInfo.Status = TradeStatus.Failed; trader.GetSession().SendTradeStatus(myCanCompleteInfo); myCanCompleteInfo.FailureForYou = true; SendTradeStatus(myCanCompleteInfo); my_trade.SetAccepted(false); his_trade.SetAccepted(false); return; } else if (hisCanCompleteInfo.BagResult != InventoryResult.Ok) { ClearAcceptTradeMode(my_trade, his_trade); hisCanCompleteInfo.Status = TradeStatus.Failed; SendTradeStatus(hisCanCompleteInfo); hisCanCompleteInfo.FailureForYou = true; trader.GetSession().SendTradeStatus(hisCanCompleteInfo); my_trade.SetAccepted(false); his_trade.SetAccepted(false); return; } // execute trade: 1. remove for (byte i = 0; i < (int)TradeSlots.TradedCount; ++i) { if (myItems[i]) { myItems[i].SetGiftCreator(GetPlayer().GetGUID()); GetPlayer().MoveItemFromInventory(myItems[i].GetBagSlot(), myItems[i].GetSlot(), true); } if (hisItems[i]) { hisItems[i].SetGiftCreator(trader.GetGUID()); trader.MoveItemFromInventory(hisItems[i].GetBagSlot(), hisItems[i].GetSlot(), true); } } // execute trade: 2. store MoveItems(myItems, hisItems); // logging money if (HasPermission(RBACPermissions.LogGmTrade)) { if (my_trade.GetMoney() > 0) { Log.outCommand(GetPlayer().GetSession().GetAccountId(), "GM {0} (Account: {1}) give money (Amount: {2}) to player: {3} (Account: {4})", GetPlayer().GetName(), GetPlayer().GetSession().GetAccountId(), my_trade.GetMoney(), trader.GetName(), trader.GetSession().GetAccountId()); } if (his_trade.GetMoney() > 0) { Log.outCommand(GetPlayer().GetSession().GetAccountId(), "GM {0} (Account: {1}) give money (Amount: {2}) to player: {3} (Account: {4})", trader.GetName(), trader.GetSession().GetAccountId(), his_trade.GetMoney(), GetPlayer().GetName(), GetPlayer().GetSession().GetAccountId()); } } // update money GetPlayer().ModifyMoney(-(long)my_trade.GetMoney()); GetPlayer().ModifyMoney((long)his_trade.GetMoney()); trader.ModifyMoney(-(long)his_trade.GetMoney()); trader.ModifyMoney((long)my_trade.GetMoney()); if (my_spell) { my_spell.Prepare(my_targets); } if (his_spell) { his_spell.Prepare(his_targets); } // cleanup ClearAcceptTradeMode(my_trade, his_trade); GetPlayer().SetTradeData(null); trader.SetTradeData(null); // desynchronized with the other saves here (SaveInventoryAndGoldToDB() not have own transaction guards) SQLTransaction trans = new SQLTransaction(); GetPlayer().SaveInventoryAndGoldToDB(trans); trader.SaveInventoryAndGoldToDB(trans); DB.Characters.CommitTransaction(trans); info.Status = TradeStatus.Complete; trader.GetSession().SendTradeStatus(info); SendTradeStatus(info); } else { info.Status = TradeStatus.Accepted; trader.GetSession().SendTradeStatus(info); } }
void MoveItems(Item[] myItems, Item[] hisItems) { Player trader = GetPlayer().GetTrader(); if (!trader) { return; } for (byte i = 0; i < (int)TradeSlots.TradedCount; ++i) { List <ItemPosCount> traderDst = new List <ItemPosCount>(); List <ItemPosCount> playerDst = new List <ItemPosCount>(); bool traderCanTrade = (myItems[i] == null || trader.CanStoreItem(ItemConst.NullBag, ItemConst.NullSlot, traderDst, myItems[i], false) == InventoryResult.Ok); bool playerCanTrade = (hisItems[i] == null || GetPlayer().CanStoreItem(ItemConst.NullBag, ItemConst.NullSlot, playerDst, hisItems[i], false) == InventoryResult.Ok); if (traderCanTrade && playerCanTrade) { // Ok, if trade item exists and can be stored // If we trade in both directions we had to check, if the trade will work before we actually do it // A roll back is not possible after we stored it if (myItems[i]) { // logging Log.outDebug(LogFilter.Network, "partner storing: {0}", myItems[i].GetGUID().ToString()); if (HasPermission(RBACPermissions.LogGmTrade)) { Log.outCommand(_player.GetSession().GetAccountId(), "GM {0} (Account: {1}) trade: {2} (Entry: {3} Count: {4}) to player: {5} (Account: {6})", GetPlayer().GetName(), GetPlayer().GetSession().GetAccountId(), myItems[i].GetTemplate().GetName(), myItems[i].GetEntry(), myItems[i].GetCount(), trader.GetName(), trader.GetSession().GetAccountId()); } // adjust time (depends on /played) if (myItems[i].HasItemFlag(ItemFieldFlags.BopTradeable)) { myItems[i].SetCreatePlayedTime(trader.GetTotalPlayedTime() - (GetPlayer().GetTotalPlayedTime() - myItems[i].m_itemData.CreatePlayedTime)); } // store trader.MoveItemToInventory(traderDst, myItems[i], true, true); } if (hisItems[i]) { // logging Log.outDebug(LogFilter.Network, "player storing: {0}", hisItems[i].GetGUID().ToString()); if (HasPermission(RBACPermissions.LogGmTrade)) { Log.outCommand(trader.GetSession().GetAccountId(), "GM {0} (Account: {1}) trade: {2} (Entry: {3} Count: {4}) to player: {5} (Account: {6})", trader.GetName(), trader.GetSession().GetAccountId(), hisItems[i].GetTemplate().GetName(), hisItems[i].GetEntry(), hisItems[i].GetCount(), GetPlayer().GetName(), GetPlayer().GetSession().GetAccountId()); } // adjust time (depends on /played) if (hisItems[i].HasItemFlag(ItemFieldFlags.BopTradeable)) { hisItems[i].SetCreatePlayedTime(GetPlayer().GetTotalPlayedTime() - (trader.GetTotalPlayedTime() - hisItems[i].m_itemData.CreatePlayedTime)); } // store GetPlayer().MoveItemToInventory(playerDst, hisItems[i], true, true); } } else { // in case of fatal error log error message // return the already removed items to the original owner if (myItems[i]) { if (!traderCanTrade) { Log.outError(LogFilter.Network, "trader can't store item: {0}", myItems[i].GetGUID().ToString()); } if (GetPlayer().CanStoreItem(ItemConst.NullBag, ItemConst.NullSlot, playerDst, myItems[i], false) == InventoryResult.Ok) { GetPlayer().MoveItemToInventory(playerDst, myItems[i], true, true); } else { Log.outError(LogFilter.Network, "player can't take item back: {0}", myItems[i].GetGUID().ToString()); } } // return the already removed items to the original owner if (hisItems[i]) { if (!playerCanTrade) { Log.outError(LogFilter.Network, "player can't store item: {0}", hisItems[i].GetGUID().ToString()); } if (trader.CanStoreItem(ItemConst.NullBag, ItemConst.NullSlot, traderDst, hisItems[i], false) == InventoryResult.Ok) { trader.MoveItemToInventory(traderDst, hisItems[i], true, true); } else { Log.outError(LogFilter.Network, "trader can't take item back: {0}", hisItems[i].GetGUID().ToString()); } } } } }
void HandleSendMail(SendMail packet) { if (packet.Info.Attachments.Count > SharedConst.MaxMailItems) // client limit { GetPlayer().SendMailResult(0, MailResponseType.Send, MailResponseResult.TooManyAttachments); return; } if (!CanOpenMailBox(packet.Info.Mailbox)) { return; } if (string.IsNullOrEmpty(packet.Info.Target)) { return; } Player player = GetPlayer(); if (player.getLevel() < WorldConfig.GetIntValue(WorldCfg.MailLevelReq)) { SendNotification(CypherStrings.MailSenderReq, WorldConfig.GetIntValue(WorldCfg.MailLevelReq)); return; } ObjectGuid receiverGuid = ObjectGuid.Empty; if (ObjectManager.NormalizePlayerName(ref packet.Info.Target)) { receiverGuid = Global.CharacterCacheStorage.GetCharacterGuidByName(packet.Info.Target); } if (receiverGuid.IsEmpty()) { Log.outInfo(LogFilter.Network, "Player {0} is sending mail to {1} (GUID: not existed!) with subject {2}" + "and body {3} includes {4} items, {5} copper and {6} COD copper with StationeryID = {7}", GetPlayerInfo(), packet.Info.Target, packet.Info.Subject, packet.Info.Body, packet.Info.Attachments.Count, packet.Info.SendMoney, packet.Info.Cod, packet.Info.StationeryID); player.SendMailResult(0, MailResponseType.Send, MailResponseResult.RecipientNotFound); return; } if (packet.Info.SendMoney < 0) { GetPlayer().SendMailResult(0, MailResponseType.Send, MailResponseResult.InternalError); Log.outWarn(LogFilter.Server, "Player {0} attempted to send mail to {1} ({2}) with negative money value (SendMoney: {3})", GetPlayerInfo(), packet.Info.Target, receiverGuid.ToString(), packet.Info.SendMoney); return; } if (packet.Info.Cod < 0) { GetPlayer().SendMailResult(0, MailResponseType.Send, MailResponseResult.InternalError); Log.outWarn(LogFilter.Server, "Player {0} attempted to send mail to {1} ({2}) with negative COD value (Cod: {3})", GetPlayerInfo(), packet.Info.Target, receiverGuid.ToString(), packet.Info.Cod); return; } Log.outInfo(LogFilter.Network, "Player {0} is sending mail to {1} ({2}) with subject {3} and body {4}" + "includes {5} items, {6} copper and {7} COD copper with StationeryID = {8}", GetPlayerInfo(), packet.Info.Target, receiverGuid.ToString(), packet.Info.Subject, packet.Info.Body, packet.Info.Attachments.Count, packet.Info.SendMoney, packet.Info.Cod, packet.Info.StationeryID); if (player.GetGUID() == receiverGuid) { player.SendMailResult(0, MailResponseType.Send, MailResponseResult.CannotSendToSelf); return; } uint cost = (uint)(!packet.Info.Attachments.Empty() ? 30 * packet.Info.Attachments.Count : 30); // price hardcoded in client long reqmoney = cost + packet.Info.SendMoney; // Check for overflow if (reqmoney < packet.Info.SendMoney) { player.SendMailResult(0, MailResponseType.Send, MailResponseResult.NotEnoughMoney); return; } if (!player.HasEnoughMoney(reqmoney) && !player.IsGameMaster()) { player.SendMailResult(0, MailResponseType.Send, MailResponseResult.NotEnoughMoney); return; } Player receiver = Global.ObjAccessor.FindPlayer(receiverGuid); Team receiverTeam = 0; byte mailsCount = 0; //do not allow to send to one player more than 100 mails byte receiverLevel = 0; uint receiverAccountId = 0; uint receiverBnetAccountId = 0; if (receiver) { receiverTeam = receiver.GetTeam(); mailsCount = (byte)receiver.GetMails().Count; receiverLevel = (byte)receiver.getLevel(); receiverAccountId = receiver.GetSession().GetAccountId(); receiverBnetAccountId = receiver.GetSession().GetBattlenetAccountId(); } else { CharacterCacheEntry characterInfo = Global.CharacterCacheStorage.GetCharacterCacheByGuid(receiverGuid); if (characterInfo != null) { receiverTeam = Player.TeamForRace(characterInfo.RaceId); receiverLevel = characterInfo.Level; receiverAccountId = characterInfo.AccountId; } PreparedStatement stmt = DB.Characters.GetPreparedStatement(CharStatements.SEL_MAIL_COUNT); stmt.AddValue(0, receiverGuid.GetCounter()); SQLResult result = DB.Characters.Query(stmt); if (!result.IsEmpty()) { mailsCount = (byte)result.Read <ulong>(0); } receiverBnetAccountId = Global.BNetAccountMgr.GetIdByGameAccount(receiverAccountId); } // do not allow to have more than 100 mails in mailbox.. mails count is in opcode byte!!! - so max can be 255.. if (mailsCount > 100) { player.SendMailResult(0, MailResponseType.Send, MailResponseResult.RecipientCapReached); return; } // test the receiver's Faction... or all items are account bound bool accountBound = !packet.Info.Attachments.Empty(); foreach (var att in packet.Info.Attachments) { Item item = player.GetItemByGuid(att.ItemGUID); if (item) { ItemTemplate itemProto = item.GetTemplate(); if (itemProto == null || !itemProto.GetFlags().HasAnyFlag(ItemFlags.IsBoundToAccount)) { accountBound = false; break; } } } if (!accountBound && player.GetTeam() != receiverTeam && !HasPermission(RBACPermissions.TwoSideInteractionMail)) { player.SendMailResult(0, MailResponseType.Send, MailResponseResult.NotYourTeam); return; } if (receiverLevel < WorldConfig.GetIntValue(WorldCfg.MailLevelReq)) { SendNotification(CypherStrings.MailReceiverReq, WorldConfig.GetIntValue(WorldCfg.MailLevelReq)); return; } List <Item> items = new List <Item>(); foreach (var att in packet.Info.Attachments) { if (att.ItemGUID.IsEmpty()) { player.SendMailResult(0, MailResponseType.Send, MailResponseResult.MailAttachmentInvalid); return; } Item item = player.GetItemByGuid(att.ItemGUID); // prevent sending bag with items (cheat: can be placed in bag after adding equipped empty bag to mail) if (!item) { player.SendMailResult(0, MailResponseType.Send, MailResponseResult.MailAttachmentInvalid); return; } if (!item.CanBeTraded(true)) { player.SendMailResult(0, MailResponseType.Send, MailResponseResult.EquipError, InventoryResult.MailBoundItem); return; } if (item.IsBoundAccountWide() && item.IsSoulBound() && player.GetSession().GetAccountId() != receiverAccountId) { if (!item.IsBattlenetAccountBound() || player.GetSession().GetBattlenetAccountId() == 0 || player.GetSession().GetBattlenetAccountId() != receiverBnetAccountId) { player.SendMailResult(0, MailResponseType.Send, MailResponseResult.EquipError, InventoryResult.NotSameAccount); return; } } if (item.GetTemplate().GetFlags().HasAnyFlag(ItemFlags.Conjured) || item.m_itemData.Expiration != 0) { player.SendMailResult(0, MailResponseType.Send, MailResponseResult.EquipError, InventoryResult.MailBoundItem); return; } if (packet.Info.Cod != 0 && item.HasItemFlag(ItemFieldFlags.Wrapped)) { player.SendMailResult(0, MailResponseType.Send, MailResponseResult.CantSendWrappedCod); return; } if (item.IsNotEmptyBag()) { player.SendMailResult(0, MailResponseType.Send, MailResponseResult.EquipError, InventoryResult.DestroyNonemptyBag); return; } items.Add(item); } player.SendMailResult(0, MailResponseType.Send, MailResponseResult.Ok); player.ModifyMoney(-reqmoney); player.UpdateCriteria(CriteriaTypes.GoldSpentForMail, cost); bool needItemDelay = false; MailDraft draft = new MailDraft(packet.Info.Subject, packet.Info.Body); SQLTransaction trans = new SQLTransaction(); if (!packet.Info.Attachments.Empty() || packet.Info.SendMoney > 0) { bool log = HasPermission(RBACPermissions.LogGmTrade); if (!packet.Info.Attachments.Empty()) { foreach (var item in items) { if (log) { Log.outCommand(GetAccountId(), "GM {0} ({1}) (Account: {2}) mail item: {3} (Entry: {4} Count: {5}) to player: {6} ({7}) (Account: {8})", GetPlayerName(), GetPlayer().GetGUID().ToString(), GetAccountId(), item.GetTemplate().GetName(), item.GetEntry(), item.GetCount(), packet.Info.Target, receiverGuid.ToString(), receiverAccountId); } item.SetNotRefundable(GetPlayer()); // makes the item no longer refundable player.MoveItemFromInventory(item.GetBagSlot(), item.GetSlot(), true); item.DeleteFromInventoryDB(trans); // deletes item from character's inventory item.SetOwnerGUID(receiverGuid); item.SetState(ItemUpdateState.Changed); item.SaveToDB(trans); // recursive and not have transaction guard into self, item not in inventory and can be save standalone draft.AddItem(item); } // if item send to character at another account, then apply item delivery delay needItemDelay = player.GetSession().GetAccountId() != receiverAccountId; } if (log && packet.Info.SendMoney > 0) { Log.outCommand(GetAccountId(), "GM {0} ({1}) (Account: {{2}) mail money: {3} to player: {4} ({5}) (Account: {6})", GetPlayerName(), GetPlayer().GetGUID().ToString(), GetAccountId(), packet.Info.SendMoney, packet.Info.Target, receiverGuid.ToString(), receiverAccountId); } } // If theres is an item, there is a one hour delivery delay if sent to another account's character. uint deliver_delay = needItemDelay ? WorldConfig.GetUIntValue(WorldCfg.MailDeliveryDelay) : 0; // Mail sent between guild members arrives instantly Guild guild = Global.GuildMgr.GetGuildById(player.GetGuildId()); if (guild) { if (guild.IsMember(receiverGuid)) { deliver_delay = 0; } } // don't ask for COD if there are no items if (packet.Info.Attachments.Empty()) { packet.Info.Cod = 0; } // will delete item or place to receiver mail list draft.AddMoney((ulong)packet.Info.SendMoney).AddCOD((uint)packet.Info.Cod).SendMailTo(trans, new MailReceiver(receiver, receiverGuid.GetCounter()), new MailSender(player), string.IsNullOrEmpty(packet.Info.Body) ? MailCheckMask.Copied : MailCheckMask.HasBody, deliver_delay); player.SaveInventoryAndGoldToDB(trans); DB.Characters.CommitTransaction(trans); }
void HandleMailTakeItem(MailTakeItem packet) { uint AttachID = packet.AttachID; if (!CanOpenMailBox(packet.Mailbox)) { return; } Player player = GetPlayer(); Mail m = player.GetMail(packet.MailID); if (m == null || m.state == MailState.Deleted || m.deliver_time > Time.UnixTime) { player.SendMailResult(packet.MailID, MailResponseType.ItemTaken, MailResponseResult.InternalError); return; } // verify that the mail has the item to avoid cheaters taking COD items without paying if (!m.items.Any(p => p.item_guid == AttachID)) { player.SendMailResult(packet.MailID, MailResponseType.ItemTaken, MailResponseResult.InternalError); return; } // prevent cheating with skip client money check if (!player.HasEnoughMoney(m.COD)) { player.SendMailResult(packet.MailID, MailResponseType.ItemTaken, MailResponseResult.NotEnoughMoney); return; } Item it = player.GetMItem(packet.AttachID); List <ItemPosCount> dest = new List <ItemPosCount>(); InventoryResult msg = GetPlayer().CanStoreItem(ItemConst.NullBag, ItemConst.NullSlot, dest, it, false); if (msg == InventoryResult.Ok) { SQLTransaction trans = new SQLTransaction(); m.RemoveItem(packet.AttachID); m.removedItems.Add(packet.AttachID); if (m.COD > 0) //if there is COD, take COD money from player and send them to sender by mail { ObjectGuid sender_guid = ObjectGuid.Create(HighGuid.Player, m.sender); Player receiver = Global.ObjAccessor.FindPlayer(sender_guid); uint sender_accId = 0; if (HasPermission(RBACPermissions.LogGmTrade)) { string sender_name; if (receiver) { sender_accId = receiver.GetSession().GetAccountId(); sender_name = receiver.GetName(); } else { // can be calculated early sender_accId = Global.CharacterCacheStorage.GetCharacterAccountIdByGuid(sender_guid); if (!Global.CharacterCacheStorage.GetCharacterNameByGuid(sender_guid, out sender_name)) { sender_name = Global.ObjectMgr.GetCypherString(CypherStrings.Unknown); } } Log.outCommand(GetAccountId(), "GM {0} (Account: {1}) receiver mail item: {2} (Entry: {3} Count: {4}) and send COD money: {5} to player: {6} (Account: {7})", GetPlayerName(), GetAccountId(), it.GetTemplate().GetName(), it.GetEntry(), it.GetCount(), m.COD, sender_name, sender_accId); } else if (!receiver) { sender_accId = Global.CharacterCacheStorage.GetCharacterAccountIdByGuid(sender_guid); } // check player existence if (receiver || sender_accId != 0) { new MailDraft(m.subject, "") .AddMoney(m.COD) .SendMailTo(trans, new MailReceiver(receiver, m.sender), new MailSender(MailMessageType.Normal, m.receiver), MailCheckMask.CodPayment); } player.ModifyMoney(-(long)m.COD); } m.COD = 0; m.state = MailState.Changed; player.m_mailsUpdated = true; player.RemoveMItem(it.GetGUID().GetCounter()); uint count = it.GetCount(); // save counts before store and possible merge with deleting it.SetState(ItemUpdateState.Unchanged); // need to set this state, otherwise item cannot be removed later, if neccessary player.MoveItemToInventory(dest, it, true); player.SaveInventoryAndGoldToDB(trans); player._SaveMail(trans); DB.Characters.CommitTransaction(trans); player.SendMailResult(packet.MailID, MailResponseType.ItemTaken, MailResponseResult.Ok, 0, packet.AttachID, count); } else { player.SendMailResult(packet.MailID, MailResponseType.ItemTaken, MailResponseResult.EquipError, msg); } }