Esempio n. 1
0
File: Chat.cs Progetto: thoys/simian
        private void SendTypingPackets(QueuedInterestListEvent[] eventDatas, IScenePresence presence)
        {
            if (!(presence is LLAgent) || presence.InterestList == null)
                return;
            LLAgent agent = (LLAgent)presence;

            // We can't combine chat blocks together, so send a packet for each typing event
            // that is pulled off the event queue
            for (int i = 0; i < eventDatas.Length; i++)
            {
                TypingData data = (TypingData)eventDatas[i].Event.State;
                ChatAudibleLevel audible = GetAudibleLevel(data.Source.ScenePosition, presence.ScenePosition, NORMAL_DIST);

                ChatFromSimulatorPacket packet = new ChatFromSimulatorPacket();
                packet.ChatData.Audible = (byte)audible;
                packet.ChatData.ChatType = (byte)(data.StartTyping ? ChatType.StartTyping : ChatType.StopTyping);
                packet.ChatData.FromName = Utils.StringToBytes(data.Source.Name);
                packet.ChatData.Message = Utils.EmptyBytes;
                packet.ChatData.OwnerID = data.Source.OwnerID;
                packet.ChatData.Position = data.Source.ScenePosition;
                packet.ChatData.SourceID = data.Source.ID;
                packet.ChatData.SourceType = (byte)ChatSourceType.Agent;

                m_udp.SendPacket(agent, packet, ThrottleCategory.Task, false);
            }
        }
Esempio n. 2
0
        private void SendTerrainPacket(QueuedInterestListEvent[] eventDatas, IScenePresence presence)
        {
            const int PATCHES_PER_PACKET = 3;

            if (!(presence is LLAgent) || presence.InterestList == null)
                return;
            LLAgent agent = (LLAgent)presence;

            List<int> patches = new List<int>(PATCHES_PER_PACKET);

            for (int i = 0; i < eventDatas.Length; i++)
            {
                int[] state = (int[])eventDatas[i].Event.State;
                int x = state[0];
                int y = state[1];

                patches.Add(y * 16 + x);

                if (patches.Count == PATCHES_PER_PACKET || i == eventDatas.Length - 1)
                {
                    LayerDataPacket packet = TerrainCompressor.CreateLandPacket(m_terrain.GetHeightmap(), patches.ToArray());
                    m_udp.SendPacket(agent, packet, ThrottleCategory.Land, false);
                    patches = new List<int>(PATCHES_PER_PACKET);
                }
            }
        }
Esempio n. 3
0
        private void SendViewerEffectPackets(QueuedInterestListEvent[] eventDatas, IScenePresence presence)
        {
            if (!(presence is LLAgent) || presence.InterestList == null)
                return;
            LLAgent agent = (LLAgent)presence;

            ViewerEffectPacket packet = new ViewerEffectPacket();
            packet.Header.Reliable = false;
            packet.AgentData.AgentID = presence.ID;
            packet.Effect = new ViewerEffectPacket.EffectBlock[eventDatas.Length];

            for (int i = 0; i < eventDatas.Length; i++)
                packet.Effect[i] = (ViewerEffectPacket.EffectBlock)eventDatas[i].Event.State;

            m_udp.SendPacket(agent, packet, ThrottleCategory.Task, true);
        }
Esempio n. 4
0
File: Chat.cs Progetto: thoys/simian
        private void SendChatPackets(QueuedInterestListEvent[] eventDatas, IScenePresence presence)
        {
            if (!(presence is LLAgent) || presence.InterestList == null)
                return;
            LLAgent agent = (LLAgent)presence;

            // We can't combine chat blocks together, so send a packet for each chat event
            // that is pulled off the event queue
            for (int i = 0; i < eventDatas.Length; i++)
            {
                ChatArgs data = (ChatArgs)eventDatas[i].Event.State;
                ChatAudibleLevel audible;
                ChatType type;
                string sourceName;
                ChatSourceType sourceType;
                string message;

                ChatFromSimulatorPacket packet = new ChatFromSimulatorPacket();

                if (data.Source == null)
                {
                    // System message
                    audible = ChatAudibleLevel.Fully;
                    type = ChatType.Normal;
                    sourceName = m_scene.Name;
                    message = data.Message;
                    sourceType = ChatSourceType.System;

                    packet.ChatData.FromName = Utils.StringToBytes(m_scene.Name);
                    packet.ChatData.OwnerID = UUID.Zero;
                    packet.ChatData.Position = Vector3.Zero;
                    packet.ChatData.SourceID = m_scene.ID;
                }
                else
                {
                    // Message from an agent or object
                    sourceName = data.Source.Name;

                    switch (data.Type)
                    {
                        case EntityChatType.Debug:
                            type = ChatType.Debug;
                            audible = ChatAudibleLevel.Fully;
                            break;
                        case EntityChatType.Owner:
                            type = ChatType.OwnerSay;
                            audible = ChatAudibleLevel.Fully;
                            break;
                        case EntityChatType.Broadcast:
                            type = ChatType.Normal;
                            audible = ChatAudibleLevel.Fully;
                            break;
                        default:
                            type = GetChatType(data.AudibleDistance);
                            audible = GetAudibleLevel(data.Source.ScenePosition, presence.ScenePosition, data.AudibleDistance);
                            break;
                    }

                    if (audible == ChatAudibleLevel.Fully)
                        message = data.Message;
                    else
                        message = String.Empty;

                    if (data.Source is IScenePresence)
                        sourceType = ChatSourceType.Agent;
                    else
                        sourceType = ChatSourceType.Object;

                    packet.ChatData.FromName = Utils.StringToBytes(data.Source.Name);
                    packet.ChatData.OwnerID = data.Source.OwnerID;
                    packet.ChatData.Position = data.Source.ScenePosition;
                    packet.ChatData.SourceID = data.Source.ID;
                }

                packet.ChatData.Audible = (byte)audible;
                packet.ChatData.ChatType = (byte)type;
                packet.ChatData.Message = Utils.StringToBytes(message);
                packet.ChatData.SourceType = (byte)sourceType;

                m_udp.SendPacket(agent, packet, ThrottleCategory.Task, false);
            }
        }
Esempio n. 5
0
        private void SendAvatarAppearancePackets(QueuedInterestListEvent[] eventDatas, IScenePresence presence)
        {
            if (!(presence is LLAgent) || presence.InterestList == null)
                return;
            LLAgent agent = (LLAgent)presence;

            for (int i = 0; i < eventDatas.Length; i++)
            {
                IScenePresence curPresence = (IScenePresence)eventDatas[i].Event.State;
                if (curPresence == presence)
                {
                    m_log.Warn("Attempted to send an AvatarAppearance packet to the controlling agent");
                    continue;
                }

                AvatarAppearancePacket appearance = new AvatarAppearancePacket();
                appearance.Sender.ID = curPresence.ID;
                appearance.Sender.IsTrial = false;

                Primitive.TextureEntry textureEntry = null;
                byte[] visualParams = null;

                if (curPresence is LLAgent)
                {
                    LLAgent curAgent = (LLAgent)curPresence;

                    // If this agent has not set VisualParams yet, skip it
                    if (curAgent.VisualParams == null)
                        continue;

                    textureEntry = curAgent.TextureEntry;
                    visualParams = curAgent.VisualParams;
                }

                if (textureEntry == null)
                {
                    // Use a default texture entry for this avatar
                    textureEntry = DEFAULT_TEXTURE_ENTRY;
                }

                if (visualParams == null)
                {
                    // Use default visual params for this avatar
                    visualParams = DEFAULT_VISUAL_PARAMS;
                }

                appearance.ObjectData.TextureEntry = textureEntry.GetBytes();
                appearance.VisualParam = new AvatarAppearancePacket.VisualParamBlock[visualParams.Length];
                for (int j = 0; j < visualParams.Length; j++)
                {
                    appearance.VisualParam[j] = new AvatarAppearancePacket.VisualParamBlock();
                    appearance.VisualParam[j].ParamValue = visualParams[j];
                }

                m_udp.SendPacket(agent, appearance, ThrottleCategory.Task, false);
            }
        }
Esempio n. 6
0
        private void SendAvatarAnimationPackets(QueuedInterestListEvent[] eventDatas, IScenePresence presence)
        {
            if (!(presence is LLAgent) || presence.InterestList == null)
                return;
            LLAgent agent = (LLAgent)presence;

            for (int i = 0; i < eventDatas.Length; i++)
            {
                LLAgent animAgent = (LLAgent)eventDatas[i].Event.State;

                AvatarAnimationPacket packet = new AvatarAnimationPacket();
                packet.Sender.ID = animAgent.ID;

                Animation[] animations = animAgent.Animations.GetAnimations();

                packet.AnimationList = new AvatarAnimationPacket.AnimationListBlock[animations.Length];
                for (int j = 0; j < animations.Length; j++)
                {
                    Animation animation = animations[j];
                    packet.AnimationList[j] = new AvatarAnimationPacket.AnimationListBlock { AnimID = animation.ID, AnimSequenceID = animation.SequenceNum };
                }

                packet.AnimationSourceList = new AvatarAnimationPacket.AnimationSourceListBlock[1];
                packet.AnimationSourceList[0] = new AvatarAnimationPacket.AnimationSourceListBlock { ObjectID = animAgent.ID };

                packet.PhysicalAvatarEventList = new AvatarAnimationPacket.PhysicalAvatarEventListBlock[0];

                m_udp.SendPacket(agent, packet, ThrottleCategory.Task, false);
            }
        }
Esempio n. 7
0
        private void SendKillPacket(QueuedInterestListEvent[] eventDatas, IScenePresence presence)
        {
            if (!(presence is LLAgent) || presence.InterestList == null)
                return;
            LLAgent agent = (LLAgent)presence;

            KillObjectPacket kill = new KillObjectPacket();
            kill.ObjectData = new KillObjectPacket.ObjectDataBlock[eventDatas.Length];

            for (int i = 0; i < eventDatas.Length; i++)
            {
                kill.ObjectData[i] = new KillObjectPacket.ObjectDataBlock();
                kill.ObjectData[i].ID = (uint)eventDatas[i].Event.State;
            }

            m_udp.SendPacket(agent, kill, ThrottleCategory.Task, true);
        }
Esempio n. 8
0
        private void SendEntityPackets(QueuedInterestListEvent[] eventDatas, IScenePresence presence)
        {
            if (!(presence is LLAgent) || presence.InterestList == null)
                return;
            LLAgent agent = (LLAgent)presence;

            Lazy<List<ObjectUpdatePacket.ObjectDataBlock>> objectUpdateBlocks = new Lazy<List<ObjectUpdatePacket.ObjectDataBlock>>();
            Lazy<List<ObjectUpdateCompressedPacket.ObjectDataBlock>> compressedUpdateBlocks = new Lazy<List<ObjectUpdateCompressedPacket.ObjectDataBlock>>();
            Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>> terseUpdateBlocks = new Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>>();

            for (int i = 0; i < eventDatas.Length; i++)
            {
                EntityAddOrUpdateArgs e = (EntityAddOrUpdateArgs)eventDatas[i].Event.State;
                ISceneEntity entity = e.Entity;

                #region UpdateFlags to packet type conversion

                UpdateFlags updateFlags = e.UpdateFlags;
                LLUpdateFlags llUpdateFlags = (LLUpdateFlags)e.ExtraFlags;

                bool canUseImproved = true;

                if (updateFlags.HasFlag(UpdateFlags.FullUpdate) ||
                    updateFlags.HasFlag(UpdateFlags.Parent) ||
                    updateFlags.HasFlag(UpdateFlags.Scale) ||
                    updateFlags.HasFlag(UpdateFlags.Shape) ||
                    llUpdateFlags.HasFlag(LLUpdateFlags.PrimFlags) ||
                    llUpdateFlags.HasFlag(LLUpdateFlags.Text) ||
                    llUpdateFlags.HasFlag(LLUpdateFlags.NameValue) ||
                    llUpdateFlags.HasFlag(LLUpdateFlags.ExtraData) ||
                    llUpdateFlags.HasFlag(LLUpdateFlags.TextureAnim) ||
                    llUpdateFlags.HasFlag(LLUpdateFlags.Sound) ||
                    llUpdateFlags.HasFlag(LLUpdateFlags.Particles) ||
                    llUpdateFlags.HasFlag(LLUpdateFlags.Material) ||
                    llUpdateFlags.HasFlag(LLUpdateFlags.ClickAction) ||
                    llUpdateFlags.HasFlag(LLUpdateFlags.MediaURL) ||
                    llUpdateFlags.HasFlag(LLUpdateFlags.Joint))
                {
                    canUseImproved = false;
                }

                #endregion UpdateFlags to packet type conversion

                #region Block Construction

                if (!canUseImproved)
                {
                    if (entity is IScenePresence)
                        objectUpdateBlocks.Value.Add(CreateAvatarObjectUpdateBlock((IScenePresence)entity, presence));
                    else
                        objectUpdateBlocks.Value.Add(CreateObjectUpdateBlock(entity, presence));
                }
                else
                {
                    terseUpdateBlocks.Value.Add(CreateTerseUpdateBlock(entity, llUpdateFlags.HasFlag(LLUpdateFlags.Textures)));
                }

                #endregion Block Construction

                // Unset CreateSelected after it has been sent once
                if (entity is LLPrimitive)
                {
                    LLPrimitive prim = (LLPrimitive)entity;
                    prim.Prim.Flags &= ~PrimFlags.CreateSelected;
                }
            }

            #region Packet Sending

            ushort timeDilation = (m_physics != null) ?
                Utils.FloatToUInt16(m_physics.TimeDilation, 0.0f, 1.0f) :
                UInt16.MaxValue;

            if (objectUpdateBlocks.IsValueCreated)
            {
                List<ObjectUpdatePacket.ObjectDataBlock> blocks = objectUpdateBlocks.Value;

                ObjectUpdatePacket packet = new ObjectUpdatePacket();
                packet.RegionData.RegionHandle = Util.PositionToRegionHandle(m_scene.MinPosition);
                packet.RegionData.TimeDilation = timeDilation;
                packet.ObjectData = new ObjectUpdatePacket.ObjectDataBlock[blocks.Count];

                for (int i = 0; i < blocks.Count; i++)
                    packet.ObjectData[i] = blocks[i];

                m_udp.SendPacket(agent, packet, ThrottleCategory.Task, true);
            }

            if (compressedUpdateBlocks.IsValueCreated)
            {
                List<ObjectUpdateCompressedPacket.ObjectDataBlock> blocks = compressedUpdateBlocks.Value;

                ObjectUpdateCompressedPacket packet = new ObjectUpdateCompressedPacket();
                packet.RegionData.RegionHandle = Util.PositionToRegionHandle(m_scene.MinPosition);
                packet.RegionData.TimeDilation = timeDilation;
                packet.ObjectData = new ObjectUpdateCompressedPacket.ObjectDataBlock[blocks.Count];

                for (int i = 0; i < blocks.Count; i++)
                    packet.ObjectData[i] = blocks[i];

                m_udp.SendPacket(agent, packet, ThrottleCategory.Task, true);
            }

            if (terseUpdateBlocks.IsValueCreated)
            {
                List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock> blocks = terseUpdateBlocks.Value;

                ImprovedTerseObjectUpdatePacket packet = new ImprovedTerseObjectUpdatePacket();
                packet.RegionData.RegionHandle = Util.PositionToRegionHandle(m_scene.MinPosition);
                packet.RegionData.TimeDilation = timeDilation;
                packet.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[blocks.Count];

                for (int i = 0; i < blocks.Count; i++)
                    packet.ObjectData[i] = blocks[i];

                m_udp.SendPacket(agent, packet, ThrottleCategory.Task, true);
            }

            #endregion Packet Sending
        }
Esempio n. 9
0
        private QueuedInterestListEvent ObjectUpdateCombiner(QueuedInterestListEvent currentData, QueuedInterestListEvent newData)
        {
            // If this event and the previous event are both updates, combine the UpdateFlags together
            if (currentData.Event.Type == OBJECT_UPDATE && newData.Event.Type == OBJECT_UPDATE)
            {
                EntityAddOrUpdateArgs currentArgs = (EntityAddOrUpdateArgs)currentData.Event.State;
                EntityAddOrUpdateArgs newArgs = (EntityAddOrUpdateArgs)newData.Event.State;

                System.Diagnostics.Debug.Assert(currentArgs.Entity.ID == newArgs.Entity.ID,
                    "Attempting to combine two different entities");

                // Combine the update flags on these two updates
                newArgs.UpdateFlags |= currentArgs.UpdateFlags;
                newArgs.ExtraFlags |= currentArgs.ExtraFlags;
            }

            // Otherwise, the new event overrides the previous event
            return newData;
        }
Esempio n. 10
0
        private void SendPreloadSoundsPacket(QueuedInterestListEvent[] eventDatas, IScenePresence presence)
        {
            if (!(presence is LLAgent) || presence.InterestList == null)
                return;
            LLAgent agent = (LLAgent)presence;

            PreloadSoundPacket preload = new PreloadSoundPacket();
            preload.DataBlock = new PreloadSoundPacket.DataBlockBlock[eventDatas.Length];

            for (int i = 0; i < eventDatas.Length; i++)
            {
                object[] state = (object[])eventDatas[i].Event.State;
                ISceneEntity source = (ISceneEntity)state[0];
                UUID soundID = (UUID)state[1];

                preload.DataBlock[i] = new PreloadSoundPacket.DataBlockBlock
                    { ObjectID = source.ID, OwnerID = source.OwnerID, SoundID = soundID };
            }

            m_udp.SendPacket(agent, preload, ThrottleCategory.Task, true);
        }
Esempio n. 11
0
        private void SendEntityPackets(QueuedInterestListEvent[] eventDatas, IScenePresence presence)
        {
            if (!(presence is LLAgent) || presence.InterestList == null)
                return;
            LLAgent agent = (LLAgent)presence;

            Lazy<List<ObjectUpdatePacket.ObjectDataBlock>> objectUpdateBlocks = new Lazy<List<ObjectUpdatePacket.ObjectDataBlock>>();
            Lazy<List<ObjectUpdateCompressedPacket.ObjectDataBlock>> compressedUpdateBlocks = new Lazy<List<ObjectUpdateCompressedPacket.ObjectDataBlock>>();
            Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>> terseUpdateBlocks = new Lazy<List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock>>();
            Lazy<List<ObjectUpdateCachedPacket.ObjectDataBlock>> cachedUpdateBlocks = new Lazy<List<ObjectUpdateCachedPacket.ObjectDataBlock>>();

            for (int i = 0; i < eventDatas.Length; i++)
            {
                EntityAddOrUpdateArgs e = (EntityAddOrUpdateArgs)eventDatas[i].Event.State;
                ISceneEntity entity = e.Entity;

                #region Determine packet type

                UpdateFlags updateFlags = e.UpdateFlags;
                LLUpdateFlags llUpdateFlags = (LLUpdateFlags)e.ExtraFlags;
                LLPrimitive prim = entity as LLPrimitive;

                bool canUseCached = false;
                bool canUseTerse = true;
                DateTime lastSeen;

                if (CACHE_CHECK_ENABLED &&
                    prim != null &&
                    updateFlags.HasFlag(UpdateFlags.FullUpdate) &&
                    !llUpdateFlags.HasFlag(LLUpdateFlags.NoCachedUpdate) &&
                    m_recentAvatars.TryGetValue(presence.ID, out lastSeen) &&
                    lastSeen > prim.LastUpdated)
                {
                    // This avatar was marked as leaving the same later than the last update
                    // timestamp of this prim. Send a cache check
                    canUseCached = true;
                }
                else if (updateFlags.HasFlag(UpdateFlags.FullUpdate) ||
                    updateFlags.HasFlag(UpdateFlags.Parent) ||
                    updateFlags.HasFlag(UpdateFlags.Scale) ||
                    updateFlags.HasFlag(UpdateFlags.Shape) ||
                    llUpdateFlags.HasFlag(LLUpdateFlags.PrimFlags) ||
                    llUpdateFlags.HasFlag(LLUpdateFlags.Text) ||
                    llUpdateFlags.HasFlag(LLUpdateFlags.NameValue) ||
                    llUpdateFlags.HasFlag(LLUpdateFlags.ExtraData) ||
                    llUpdateFlags.HasFlag(LLUpdateFlags.TextureAnim) ||
                    llUpdateFlags.HasFlag(LLUpdateFlags.Sound) ||
                    llUpdateFlags.HasFlag(LLUpdateFlags.Particles) ||
                    llUpdateFlags.HasFlag(LLUpdateFlags.Material) ||
                    llUpdateFlags.HasFlag(LLUpdateFlags.ClickAction) ||
                    llUpdateFlags.HasFlag(LLUpdateFlags.MediaURL) ||
                    llUpdateFlags.HasFlag(LLUpdateFlags.Joint))
                {
                    canUseTerse = false;
                }

                #endregion Determine packet type

                #region Block Construction

                if (canUseCached && prim != null)
                {
                    cachedUpdateBlocks.Value.Add(new ObjectUpdateCachedPacket.ObjectDataBlock
                        { CRC = prim.GetCrc(), ID = prim.LocalID });
                }
                else if (!canUseTerse)
                {
                    if (entity is IScenePresence)
                    {
                        IScenePresence thisPresence = (IScenePresence)entity;
                        ObjectUpdatePacket.ObjectDataBlock block = CreateAvatarObjectUpdateBlock(thisPresence);
                        block.UpdateFlags = (uint)GetUpdateFlags(thisPresence, presence);
                        objectUpdateBlocks.Value.Add(block);
                    }
                    else if (prim != null)
                    {
                        ObjectUpdateCompressedPacket.ObjectDataBlock block = CreateCompressedObjectUpdateBlock(prim, prim.GetCrc());
                        block.UpdateFlags = (uint)GetUpdateFlags(prim, presence, m_permissions);
                        compressedUpdateBlocks.Value.Add(block);

                        // ObjectUpdateCompressed doesn't carry velocity or acceleration fields, so
                        // we need to send a separate terse packet if this prim has a non-zero
                        // velocity or acceleration
                        if (prim.Velocity != Vector3.Zero || prim.Acceleration != Vector3.Zero)
                            terseUpdateBlocks.Value.Add(CreateTerseUpdateBlock(entity, false));

                        //ObjectUpdatePacket.ObjectDataBlock block = CreateObjectUpdateBlock(prim);
                        //block.UpdateFlags = (uint)GetUpdateFlags(prim, presence, m_permissions);
                        //block.CRC = prim.GetCrc();
                        //objectUpdateBlocks.Value.Add(block);
                    }
                    else
                    {
                        // TODO: Create a generic representation for non-LLPrimitive entities?
                        continue;
                    }
                }
                else
                {
                    terseUpdateBlocks.Value.Add(CreateTerseUpdateBlock(entity, llUpdateFlags.HasFlag(LLUpdateFlags.Textures)));
                }

                #endregion Block Construction

                // Unset CreateSelected after it has been sent once
                if (prim != null)
                    prim.Prim.Flags &= ~PrimFlags.CreateSelected;
            }

            #region Packet Sending

            ushort timeDilation = (m_physics != null) ?
                Utils.FloatToUInt16(m_physics.TimeDilation, 0.0f, 1.0f) :
                UInt16.MaxValue;

            if (objectUpdateBlocks.IsValueCreated)
            {
                List<ObjectUpdatePacket.ObjectDataBlock> blocks = objectUpdateBlocks.Value;

                ObjectUpdatePacket packet = new ObjectUpdatePacket();
                packet.RegionData.RegionHandle = Util.PositionToRegionHandle(m_scene.MinPosition);
                packet.RegionData.TimeDilation = timeDilation;
                packet.ObjectData = new ObjectUpdatePacket.ObjectDataBlock[blocks.Count];

                for (int i = 0; i < blocks.Count; i++)
                    packet.ObjectData[i] = blocks[i];

                m_udp.SendPacket(agent, packet, ThrottleCategory.Task, true);
            }

            if (compressedUpdateBlocks.IsValueCreated)
            {
                List<ObjectUpdateCompressedPacket.ObjectDataBlock> blocks = compressedUpdateBlocks.Value;

                ObjectUpdateCompressedPacket packet = new ObjectUpdateCompressedPacket();
                packet.RegionData.RegionHandle = Util.PositionToRegionHandle(m_scene.MinPosition);
                packet.RegionData.TimeDilation = timeDilation;
                packet.ObjectData = new ObjectUpdateCompressedPacket.ObjectDataBlock[blocks.Count];

                for (int i = 0; i < blocks.Count; i++)
                    packet.ObjectData[i] = blocks[i];

                m_udp.SendPacket(agent, packet, ThrottleCategory.Task, true);
            }

            if (terseUpdateBlocks.IsValueCreated)
            {
                List<ImprovedTerseObjectUpdatePacket.ObjectDataBlock> blocks = terseUpdateBlocks.Value;

                ImprovedTerseObjectUpdatePacket packet = new ImprovedTerseObjectUpdatePacket();
                packet.RegionData.RegionHandle = Util.PositionToRegionHandle(m_scene.MinPosition);
                packet.RegionData.TimeDilation = timeDilation;
                packet.ObjectData = new ImprovedTerseObjectUpdatePacket.ObjectDataBlock[blocks.Count];

                for (int i = 0; i < blocks.Count; i++)
                    packet.ObjectData[i] = blocks[i];

                m_udp.SendPacket(agent, packet, ThrottleCategory.Task, true);
            }

            if (cachedUpdateBlocks.IsValueCreated)
            {
                List<ObjectUpdateCachedPacket.ObjectDataBlock> blocks = cachedUpdateBlocks.Value;

                ObjectUpdateCachedPacket packet = new ObjectUpdateCachedPacket();
                packet.RegionData.RegionHandle = Util.PositionToRegionHandle(m_scene.MinPosition);
                packet.RegionData.TimeDilation = timeDilation;
                packet.ObjectData = new ObjectUpdateCachedPacket.ObjectDataBlock[blocks.Count];

                for (int i = 0; i < blocks.Count; i++)
                    packet.ObjectData[i] = blocks[i];

                m_udp.SendPacket(agent, packet, ThrottleCategory.Task, true);
            }

            #endregion Packet Sending
        }