Пример #1
0
        public void SendChatPacket(WorldObject source, MessageBuilder builder, ChatMsg msgType, WorldObject whisperTarget = null, CreatureTextRange range = CreatureTextRange.Normal, Team team = Team.Other, bool gmOnly = false)
        {
            if (source == null)
            {
                return;
            }

            var localizer = new CreatureTextLocalizer(builder, msgType);

            switch (msgType)
            {
            case ChatMsg.MonsterWhisper:
            case ChatMsg.RaidBossWhisper:
            {
                if (range == CreatureTextRange.Normal)         //ignores team and gmOnly
                {
                    if (!whisperTarget || !whisperTarget.IsTypeId(TypeId.Player))
                    {
                        return;
                    }

                    localizer.Invoke(whisperTarget.ToPlayer());
                    return;
                }
                break;
            }

            default:
                break;
            }

            switch (range)
            {
            case CreatureTextRange.Area:
            {
                uint areaId  = source.GetAreaId();
                var  players = source.GetMap().GetPlayers();
                foreach (var pl in players)
                {
                    if (pl.GetAreaId() == areaId && (team == 0 || pl.GetTeam() == team) && (!gmOnly || pl.IsGameMaster()))
                    {
                        localizer.Invoke(pl);
                    }
                }
                return;
            }

            case CreatureTextRange.Zone:
            {
                uint zoneId  = source.GetZoneId();
                var  players = source.GetMap().GetPlayers();
                foreach (var pl in players)
                {
                    if (pl.GetZoneId() == zoneId && (team == 0 || pl.GetTeam() == team) && (!gmOnly || pl.IsGameMaster()))
                    {
                        localizer.Invoke(pl);
                    }
                }
                return;
            }

            case CreatureTextRange.Map:
            {
                var players = source.GetMap().GetPlayers();
                foreach (var pl in players)
                {
                    if ((team == 0 || pl.GetTeam() == team) && (!gmOnly || pl.IsGameMaster()))
                    {
                        localizer.Invoke(pl);
                    }
                }
                return;
            }

            case CreatureTextRange.World:
            {
                var smap = Global.WorldMgr.GetAllSessions();
                foreach (var session in smap)
                {
                    Player player = session.GetPlayer();
                    if (player != null)
                    {
                        if ((team == 0 || player.GetTeam() == team) && (!gmOnly || player.IsGameMaster()))
                        {
                            localizer.Invoke(player);
                        }
                    }
                }
                return;
            }

            case CreatureTextRange.Normal:
            default:
                break;
            }

            float dist   = GetRangeForChatType(msgType);
            var   worker = new PlayerDistWorker(source, dist, localizer);

            Cell.VisitWorldObjects(source, worker, dist);
        }
Пример #2
0
        public static bool LoadMap( String mapPath )
        {
            if ( !File.Exists( mapPath ) )
                return false;

            using ( FileStream stream = new FileStream( mapPath, FileMode.Open, FileAccess.Read ) )
            {
                StreamReader reader = new StreamReader( stream );

                String line;
                while ( !reader.EndOfStream && ( line = reader.ReadLine().Trim().ToLower() ) != "map" )
                {
                    if ( line.Length == 0 )
                        continue;

                    String[] split = line.Split( new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries );

                    if ( split.Length < 2 )
                        return false;

                    switch ( split[ 0 ] )
                    {
                        case "teams":
                            TeamCount = int.Parse( split[ 1 ] ); break;
                        case "width":
                            MapWidth = int.Parse( split[ 1 ] ); break;
                        case "height":
                            MapHeight = int.Parse( split[ 1 ] ); break;
                        default:
                            return false;
                    }
                }

                if( TeamCount == 0 || MapWidth == 0 || MapHeight == 0 )
                    return false;

                Program.Log( "teams", TeamCount );
                Program.Log( "width", MapWidth );
                Program.Log( "height", MapHeight );

                Map = new Tile[ MapWidth, MapHeight ];

                Teams = new Team[ TeamCount ];

                for ( int i = 0; i < GameState.TeamCount; ++i )
                    Teams[ i ] = new Team( i );

                Program.Log( "map" );

                if ( !reader.EndOfStream )
                {
                    int y = 0;
                    while ( !reader.EndOfStream && ( line = reader.ReadLine().ToLower() ).Trim() != "end" )
                    {
                        if ( line.Length < MapWidth * 2 )
                            return false;

                        line = line.Substring( 0, MapWidth * 2 );
                        Program.Log( line );

                        for ( int x = 0; x < MapWidth; ++x )
                        {
                            char c = line[ x << 1 ];
                            if( c == '#' )
                                Map[ x, y ] = Tile.Wall;
                            else if( char.IsNumber( c ) )
                            {
                                int teamNo;
                                if ( !int.TryParse( c.ToString(), out teamNo ) || teamNo >= TeamCount )
                                    return false;

                                Team team = Teams[ teamNo ];

                                Direction dir;
                                if ( !Direction.TryParse( line[ ( x << 1 ) + 1 ].ToString(), out dir ) )
                                    return false;

                                Position pos = new Position( x, y );

                                team.Bases.Add( pos );
                                team.Agents.Add( new Agent( team, pos, dir ) );

                                team.InitialBasePos = pos;
                                team.InitialBaseDir = dir;
                            }
                            else
                                Map[ x, y ] = Tile.Empty;
                        }
                        ++y;
                    }
                }

                Program.Log( "end" );
            }

            Dead = new List<Agent>();
            Packages = new List<Position>();

            return true;
        }
Пример #3
0
        void SendNonChatPacket(WorldObject source, ServerPacket data, ChatMsg msgType, WorldObject whisperTarget, CreatureTextRange range, Team team, bool gmOnly)
        {
            float dist = GetRangeForChatType(msgType);

            switch (msgType)
            {
            case ChatMsg.MonsterParty:
                if (!whisperTarget)
                {
                    return;
                }

                Player whisperPlayer = whisperTarget.ToPlayer();
                if (whisperPlayer)
                {
                    Group group = whisperPlayer.GetGroup();
                    if (group)
                    {
                        group.BroadcastWorker(player => player.SendPacket(data));
                    }
                }
                return;

            case ChatMsg.MonsterWhisper:
            case ChatMsg.RaidBossWhisper:
            {
                if (range == CreatureTextRange.Normal)        //ignores team and gmOnly
                {
                    if (!whisperTarget || !whisperTarget.IsTypeId(TypeId.Player))
                    {
                        return;
                    }

                    whisperTarget.ToPlayer().SendPacket(data);
                    return;
                }
                break;
            }

            default:
                break;
            }

            switch (range)
            {
            case CreatureTextRange.Area:
            {
                uint areaId  = source.GetAreaId();
                var  players = source.GetMap().GetPlayers();
                foreach (var pl in players)
                {
                    if (pl.GetAreaId() == areaId && (team == 0 || pl.GetTeam() == team) && (!gmOnly || pl.IsGameMaster()))
                    {
                        pl.SendPacket(data);
                    }
                }
                return;
            }

            case CreatureTextRange.Zone:
            {
                uint zoneId  = source.GetZoneId();
                var  players = source.GetMap().GetPlayers();
                foreach (var pl in players)
                {
                    if (pl.GetZoneId() == zoneId && (team == 0 || pl.GetTeam() == team) && (!gmOnly || pl.IsGameMaster()))
                    {
                        pl.SendPacket(data);
                    }
                }
                return;
            }

            case CreatureTextRange.Map:
            {
                var players = source.GetMap().GetPlayers();
                foreach (var pl in players)
                {
                    if ((team == 0 || pl.GetTeam() == team) && (!gmOnly || pl.IsGameMaster()))
                    {
                        pl.SendPacket(data);
                    }
                }
                return;
            }

            case CreatureTextRange.World:
            {
                var smap = Global.WorldMgr.GetAllSessions();
                foreach (var session in smap)
                {
                    Player player = session.GetPlayer();
                    if (player != null)
                    {
                        if ((team == 0 || player.GetTeam() == team) && (!gmOnly || player.IsGameMaster()))
                        {
                            player.SendPacket(data);
                        }
                    }
                }
                return;
            }

            case CreatureTextRange.Normal:
            default:
                break;
            }

            source.SendMessageToSetInRange(data, dist, true);
        }
Пример #4
0
        public void SendSound(Creature source, uint sound, ChatMsg msgType, WorldObject whisperTarget = null, CreatureTextRange range = CreatureTextRange.Normal, Team team = Team.Other, bool gmOnly = false, uint keyBroadcastTextId = 0)
        {
            if (sound == 0 || !source)
            {
                return;
            }

            SendNonChatPacket(source, new PlaySound(source.GetGUID(), sound, keyBroadcastTextId), msgType, whisperTarget, range, team, gmOnly);
        }
Пример #5
0
        public uint SendChat(Creature source, byte textGroup, WorldObject whisperTarget = null, ChatMsg msgType = ChatMsg.Addon, Language language = Language.Addon,
                             CreatureTextRange range = CreatureTextRange.Normal, uint sound = 0, Team team = Team.Other, bool gmOnly = false, Player srcPlr = null)
        {
            if (source == null)
            {
                return(0);
            }

            var sList = mTextMap.LookupByKey(source.GetEntry());

            if (sList == null)
            {
                Log.outError(LogFilter.Sql, "GossipManager: Could not find Text for Creature({0}) Entry {1} in 'creature_text' table. Ignoring.", source.GetName(), source.GetEntry());
                return(0);
            }

            var textGroupContainer = sList.LookupByKey(textGroup);

            if (textGroupContainer.Empty())
            {
                Log.outError(LogFilter.ChatSystem, "GossipManager: Could not find TextGroup {0} for Creature({1}) GuidLow {2} Entry {3}. Ignoring.", textGroup, source.GetName(), source.GetGUID().ToString(), source.GetEntry());
                return(0);
            }

            List <CreatureTextEntry> tempGroup = new();
            var repeatGroup = source.GetTextRepeatGroup(textGroup);

            foreach (var entry in textGroupContainer)
            {
                if (!repeatGroup.Contains(entry.id))
                {
                    tempGroup.Add(entry);
                }
            }

            if (tempGroup.Empty())
            {
                source.ClearTextRepeatGroup(textGroup);
                tempGroup = textGroupContainer;
            }

            var textEntry = tempGroup.SelectRandomElementByWeight(t => t.probability);

            ChatMsg  finalType  = (msgType == ChatMsg.Addon) ? textEntry.type : msgType;
            Language finalLang  = (language == Language.Addon) ? textEntry.lang : language;
            uint     finalSound = textEntry.sound;

            if (sound != 0)
            {
                finalSound = sound;
            }
            else
            {
                BroadcastTextRecord bct = CliDB.BroadcastTextStorage.LookupByKey(textEntry.BroadcastTextId);
                if (bct != null)
                {
                    uint broadcastTextSoundId = bct.SoundEntriesID[source.GetGender() == Gender.Female ? 1 : 0];
                    if (broadcastTextSoundId != 0)
                    {
                        finalSound = broadcastTextSoundId;
                    }
                }
            }

            if (range == CreatureTextRange.Normal)
            {
                range = textEntry.TextRange;
            }

            if (finalSound != 0)
            {
                SendSound(source, finalSound, finalType, whisperTarget, range, team, gmOnly, textEntry.BroadcastTextId);
            }

            Unit finalSource = source;

            if (srcPlr)
            {
                finalSource = srcPlr;
            }

            if (textEntry.emote != 0)
            {
                SendEmote(finalSource, textEntry.emote);
            }

            if (srcPlr)
            {
                PlayerTextBuilder builder = new(source, finalSource, finalSource.GetGender(), finalType, textEntry.groupId, textEntry.id, finalLang, whisperTarget);
                SendChatPacket(finalSource, builder, finalType, whisperTarget, range, team, gmOnly);
            }
            else
            {
                CreatureTextBuilder builder = new(finalSource, finalSource.GetGender(), finalType, textEntry.groupId, textEntry.id, finalLang, whisperTarget);
                SendChatPacket(finalSource, builder, finalType, whisperTarget, range, team, gmOnly);
            }

            source.SetTextRepeatId(textGroup, textEntry.id);
            return(textEntry.duration);
        }
Пример #6
0
        void HandleCalendarInvite(CalendarInvitePkt calendarInvite)
        {
            ObjectGuid playerGuid = GetPlayer().GetGUID();

            ObjectGuid inviteeGuid    = ObjectGuid.Empty;
            Team       inviteeTeam    = 0;
            ulong      inviteeGuildId = 0;

            if (!ObjectManager.NormalizePlayerName(ref calendarInvite.Name))
            {
                return;
            }

            Player player = Global.ObjAccessor.FindPlayerByName(calendarInvite.Name);

            if (player)
            {
                // Invitee is online
                inviteeGuid    = player.GetGUID();
                inviteeTeam    = player.GetTeam();
                inviteeGuildId = player.GetGuildId();
            }
            else
            {
                // Invitee offline, get data from database
                ObjectGuid guid = Global.CharacterCacheStorage.GetCharacterGuidByName(calendarInvite.Name);
                if (!guid.IsEmpty())
                {
                    CharacterCacheEntry characterInfo = Global.CharacterCacheStorage.GetCharacterCacheByGuid(guid);
                    if (characterInfo != null)
                    {
                        inviteeGuid    = guid;
                        inviteeTeam    = Player.TeamForRace(characterInfo.RaceId);
                        inviteeGuildId = characterInfo.GuildId;
                    }
                }
            }

            if (inviteeGuid.IsEmpty())
            {
                Global.CalendarMgr.SendCalendarCommandResult(playerGuid, CalendarError.PlayerNotFound);
                return;
            }

            if (GetPlayer().GetTeam() != inviteeTeam && !WorldConfig.GetBoolValue(WorldCfg.AllowTwoSideInteractionCalendar))
            {
                Global.CalendarMgr.SendCalendarCommandResult(playerGuid, CalendarError.NotAllied);
                return;
            }

            SQLResult result1 = DB.Characters.Query("SELECT flags FROM character_social WHERE guid = {0} AND friend = {1}", inviteeGuid, playerGuid);

            if (!result1.IsEmpty())
            {
                if (Convert.ToBoolean(result1.Read <byte>(0) & (byte)SocialFlag.Ignored))
                {
                    Global.CalendarMgr.SendCalendarCommandResult(playerGuid, CalendarError.IgnoringYouS, calendarInvite.Name);
                    return;
                }
            }

            if (!calendarInvite.Creating)
            {
                CalendarEvent calendarEvent = Global.CalendarMgr.GetEvent(calendarInvite.EventID);
                if (calendarEvent != null)
                {
                    if (calendarEvent.IsGuildEvent() && calendarEvent.GuildId == inviteeGuildId)
                    {
                        // we can't invite guild members to guild events
                        Global.CalendarMgr.SendCalendarCommandResult(playerGuid, CalendarError.NoGuildInvites);
                        return;
                    }

                    CalendarInvite invite = new(Global.CalendarMgr.GetFreeInviteId(), calendarInvite.EventID, inviteeGuid, playerGuid, SharedConst.CalendarDefaultResponseTime, CalendarInviteStatus.Invited, CalendarModerationRank.Player, "");
                    Global.CalendarMgr.AddInvite(calendarEvent, invite);
                }
                else
                {
                    Global.CalendarMgr.SendCalendarCommandResult(playerGuid, CalendarError.EventInvalid);
                }
            }
            else
            {
                if (calendarInvite.IsSignUp && inviteeGuildId == GetPlayer().GetGuildId())
                {
                    Global.CalendarMgr.SendCalendarCommandResult(playerGuid, CalendarError.NoGuildInvites);
                    return;
                }

                CalendarInvite invite = new(calendarInvite.EventID, 0, inviteeGuid, playerGuid, SharedConst.CalendarDefaultResponseTime, CalendarInviteStatus.Invited, CalendarModerationRank.Player, "");
                Global.CalendarMgr.SendCalendarEventInvite(invite);
            }
        }
Пример #7
0
        void HandleSendMail(SendMail packet)
        {
            if (packet.Info.Attachments.Count > SharedConst.MaxClientMailItems)                      // 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;

            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();

            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(packet.Info.Subject, packet.Info.Body);

            SQLTransaction trans = new();

            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);
        }