Example #1
0
        private bool CheckWhetherAttachmentShouldBeSent(SceneObjectGroup e)
        {
            bool hasBeenUpdated = false;
            var  attParts       = e.GetParts();

            lock (m_updateTimes)
            {
                foreach (SceneObjectPart part in attParts)
                {
                    if (!m_updateTimes.ContainsKey(part.LocalId))
                    {
                        //Threading issue? Shouldn't happen unless this method is called
                        //  while a group is being sent, but hasn't sent all prims yet
                        //  so... let's ignore the prim that is missing for now, and if
                        //  any other parts change, it'll re-send it all
                        hasBeenUpdated = true;
                        continue;
                    }
                    ScenePartUpdate update = m_updateTimes[part.LocalId];
                    if (update.LastFullUpdateTimeRequested == update.LastFullUpdateTime &&
                        update.LastTerseUpdateTime == update.LastTerseUpdateTimeRequested)
                    {
                        continue;//Only if we haven't sent them this prim before and it hasn't changed
                    }
                    //It's changed, check again
                    hasBeenUpdated = true;
                    break;
                }
            }
            return(hasBeenUpdated);
        }
Example #2
0
        public void SendPrimUpdates()
        {
            if (m_pendingObjects == null)
            {
                if (!m_presence.IsChildAgent || (m_presence.Scene.m_seeIntoRegionFromNeighbor))
                {
                    m_pendingObjects = new Queue <SceneObjectGroup>();

                    lock (m_pendingObjects)
                    {
                        foreach (EntityBase e in m_presence.Scene.Entities)
                        {
                            if (e != null && e is SceneObjectGroup)
                            {
                                m_pendingObjects.Enqueue((SceneObjectGroup)e);
                            }
                        }
                    }
                }
            }

            lock (m_pendingObjects)
            {
                while (m_pendingObjects != null && m_pendingObjects.Count > 0)
                {
                    SceneObjectGroup g = m_pendingObjects.Dequeue();
                    // Yes, this can really happen
                    if (g == null)
                    {
                        continue;
                    }

                    // This is where we should check for draw distance
                    // do culling and stuff. Problem with that is that until
                    // we recheck in movement, that won't work right.
                    // So it's not implemented now.
                    //

                    // Don't even queue if we have sent this one
                    //
                    if (!m_updateTimes.ContainsKey(g.UUID))
                    {
                        g.ScheduleFullUpdateToAvatar(m_presence);
                    }
                }

                while (m_partsUpdateQueue.Count > 0)
                {
                    SceneObjectPart part = m_partsUpdateQueue.Dequeue();

                    if (part.ParentGroup == null || part.ParentGroup.IsDeleted)
                    {
                        continue;
                    }

                    if (m_updateTimes.ContainsKey(part.UUID))
                    {
                        ScenePartUpdate update = m_updateTimes[part.UUID];

                        // We deal with the possibility that two updates occur at
                        // the same unix time at the update point itself.

                        if ((update.LastFullUpdateTime < part.TimeStampFull) ||
                            part.IsAttachment)
                        {
                            //                            m_log.DebugFormat(
                            //                                "[SCENE PRESENCE]: Fully   updating prim {0}, {1} - part timestamp {2}",
                            //                                part.Name, part.UUID, part.TimeStampFull);

                            part.SendFullUpdate(m_presence.ControllingClient,
                                                m_presence.GenerateClientFlags(part.UUID));

                            // We'll update to the part's timestamp rather than
                            // the current time to avoid the race condition
                            // whereby the next tick occurs while we are doing
                            // this update. If this happened, then subsequent
                            // updates which occurred on the same tick or the
                            // next tick of the last update would be ignored.

                            update.LastFullUpdateTime = part.TimeStampFull;
                        }
                        else if (update.LastTerseUpdateTime <= part.TimeStampTerse)
                        {
                            //                            m_log.DebugFormat(
                            //                                "[SCENE PRESENCE]: Tersely updating prim {0}, {1} - part timestamp {2}",
                            //                                part.Name, part.UUID, part.TimeStampTerse);

                            part.SendTerseUpdateToClient(m_presence.ControllingClient);

                            update.LastTerseUpdateTime = part.TimeStampTerse;
                        }
                    }
                    else
                    {
                        //never been sent to client before so do full update
                        ScenePartUpdate update = new ScenePartUpdate();
                        update.FullID             = part.UUID;
                        update.LastFullUpdateTime = part.TimeStampFull;
                        m_updateTimes.Add(part.UUID, update);

                        // Attachment handling
                        //
                        if (part.ParentGroup.RootPart.Shape.PCode == 9 && part.ParentGroup.RootPart.Shape.State != 0)
                        {
                            if (part != part.ParentGroup.RootPart)
                            {
                                continue;
                            }

                            part.ParentGroup.SendFullUpdateToClient(m_presence.ControllingClient);
                            continue;
                        }

                        part.SendFullUpdate(m_presence.ControllingClient,
                                            m_presence.GenerateClientFlags(part.UUID));
                    }
                }
            }
        }
Example #3
0
        public void SendPrimUpdates()
        {
            if (m_pendingObjects == null)
            {
                if (!m_presence.IsChildAgent || (m_presence.Scene.m_seeIntoRegionFromNeighbor))
                {
                    m_pendingObjects = new Queue<SceneObjectGroup>();

                    lock (m_pendingObjects)
                    {
                        EntityBase[] entities = m_presence.Scene.Entities.GetEntities();
                        foreach (EntityBase e in entities)
                        {
                            if (e != null && e is SceneObjectGroup)
                                m_pendingObjects.Enqueue((SceneObjectGroup)e);
                        }
                    }
                }
            }

            lock (m_pendingObjects)
            {
                while (m_pendingObjects != null && m_pendingObjects.Count > 0)
                {
                    SceneObjectGroup g = m_pendingObjects.Dequeue();
                     // Yes, this can really happen
                     if (g == null)
                        continue;

                    // This is where we should check for draw distance
                    // do culling and stuff. Problem with that is that until
                    // we recheck in movement, that won't work right.
                    // So it's not implemented now.
                    //

                    // Don't even queue if we have sent this one
                    //
                    if (!m_updateTimes.ContainsKey(g.UUID))
                        g.ScheduleFullUpdateToAvatar(m_presence);
                }

                while (m_partsUpdateQueue.Count > 0)
                {
                    SceneObjectPart part = m_partsUpdateQueue.Dequeue();
                    
                    if (part.ParentGroup == null || part.ParentGroup.IsDeleted)
                        continue;
                    
                    if (m_updateTimes.ContainsKey(part.UUID))
                    {
                        ScenePartUpdate update = m_updateTimes[part.UUID];

                        // We deal with the possibility that two updates occur at
                        // the same unix time at the update point itself.

                        if ((update.LastFullUpdateTime < part.TimeStampFull) ||
                                part.IsAttachment)
                        {
    //                            m_log.DebugFormat(
    //                                "[SCENE PRESENCE]: Fully   updating prim {0}, {1} - part timestamp {2}",
    //                                part.Name, part.UUID, part.TimeStampFull);

                            part.SendFullUpdate(m_presence.ControllingClient,
                                   m_presence.GenerateClientFlags(part.UUID));

                            // We'll update to the part's timestamp rather than
                            // the current time to avoid the race condition
                            // whereby the next tick occurs while we are doing
                            // this update. If this happened, then subsequent
                            // updates which occurred on the same tick or the
                            // next tick of the last update would be ignored.

                            update.LastFullUpdateTime = part.TimeStampFull;

                        }
                        else if (update.LastTerseUpdateTime <= part.TimeStampTerse)
                        {
    //                            m_log.DebugFormat(
    //                                "[SCENE PRESENCE]: Tersely updating prim {0}, {1} - part timestamp {2}",
    //                                part.Name, part.UUID, part.TimeStampTerse);

                            part.SendTerseUpdateToClient(m_presence.ControllingClient);

                            update.LastTerseUpdateTime = part.TimeStampTerse;
                        }
                    }
                    else
                    {
                        //never been sent to client before so do full update
                        ScenePartUpdate update = new ScenePartUpdate();
                        update.FullID = part.UUID;
                        update.LastFullUpdateTime = part.TimeStampFull;
                        m_updateTimes.Add(part.UUID, update);

                        // Attachment handling
                        //
                        if (part.ParentGroup.RootPart.Shape.PCode == 9 && part.ParentGroup.RootPart.Shape.State != 0)
                        {
                            if (part != part.ParentGroup.RootPart)
                                continue;

                            part.ParentGroup.SendFullUpdateToClient(m_presence.ControllingClient);
                            continue;
                        }

                        part.SendFullUpdate(m_presence.ControllingClient,
                                m_presence.GenerateClientFlags(part.UUID));
                    }
                }
            }
        }
Example #4
0
        /// <summary>
        /// Send updates to the client about prims which have been placed on the update queue.  We don't
        /// necessarily send updates for all the parts on the queue, e.g. if an updates with a more recent
        /// timestamp has already been sent.
        ///
        /// SHOULD ONLY BE CALLED WITHIN THE SCENE LOOP
        /// </summary>
        public void SendPrimUpdates()
        {
            if (m_presence.Closed)
            {
                ClearAllTracking();
                return;
            }

            //Bots don't get to check for updates
            if (m_presence.IsBot)
            {
                return;
            }

            if (m_presence.IsInTransit)
            {
                return; // disable prim updates during a crossing, but leave them queued for after transition
            }
            if (!m_presence.IsChildAgent && !m_presence.IsFullyInRegion)
            {
                return;
            }

            m_perfMonMS = Environment.TickCount;

            if (((UseCulling == false) || (m_presence.DrawDistance != 0)) && NeedsFullSceneUpdate)
            {
                NeedsFullSceneUpdate = false;
                if (UseCulling == false)//Send the entire heightmap
                {
                    m_presence.ControllingClient.SendLayerData(m_presence.Scene.Heightmap.GetFloatsSerialized());
                }

                if (UseCulling == true && !m_presence.IsChildAgent)
                {
                    m_timeBeforeChildAgentUpdate = Environment.TickCount;
                }

                CheckForDistantEntitiesToShow();
            }

            if (m_timeBeforeChildAgentUpdate != 0 && (Environment.TickCount - m_timeBeforeChildAgentUpdate) >= 5000)
            {
                m_log.Debug("[SCENE VIEW]: Sending child agent update to update all child regions about fully established user");
                //Send out an initial child agent update so that the child region can add their objects accordingly now that we are all set up
                m_presence.SendChildAgentUpdate();
                //Don't ever send another child update from here again
                m_timeBeforeChildAgentUpdate = 0;
            }

            // Before pulling the AbsPosition, since this isn't locked, make sure it's a good position.
            Vector3 clientAbsPosition = Vector3.Zero;

            if (!m_presence.HasSafePosition(out clientAbsPosition))
            {
                return; // this one has gone into late transit or something, or the prim it's on has.
            }
            int queueCount = m_partsUpdateQueue.Count;
            int time       = Environment.TickCount;

            SceneObjectGroup lastSog    = null;
            bool             condition1 = false;
            IReadOnlyCollection <SceneObjectPart> lastParentGroupParts = null;

            while (m_partsUpdateQueue.Count > 0 && HasFinishedInitialUpdate)
            {
                if (m_presence.Closed)
                {
                    ClearAllTracking();
                    return;
                }

                KeyValuePair <SceneObjectPart, PrimUpdateFlags>?kvp = m_partsUpdateQueue.Dequeue();

                if (!kvp.HasValue || kvp.Value.Key.ParentGroup == null || kvp.Value.Key.ParentGroup.IsDeleted)
                {
                    continue;
                }

                SceneObjectPart part = kvp.Value.Key;

                double distance;

                // Cull part updates based on the position of the SOP.
                if ((lastSog == part.ParentGroup && condition1) ||
                    (lastSog != part.ParentGroup &&
                     UseCulling && !ShowEntityToClient(clientAbsPosition, part.ParentGroup, out distance) &&
                     !ShowEntityToClient(m_presence.CameraPosition, part.ParentGroup, out distance)))
                {
                    condition1 = true;

                    lock (m_updateTimes)
                    {
                        ScenePartUpdate newUpdate;
                        if (!m_updateTimes.TryGetValue(part.LocalId, out newUpdate))
                        {
                            newUpdate = new ScenePartUpdate();
                        }
                        //Update this, so that we know to resend it later once it comes back into view
                        newUpdate.LastFullUpdateTimeRequested  = part.FullUpdateCounter;
                        newUpdate.LastTerseUpdateTimeRequested = part.TerseUpdateCounter;
                        m_updateTimes[part.LocalId]            = newUpdate;
                    }

                    lastSog = part.ParentGroup;
                    continue;
                }
                else
                {
                    condition1 = false;
                }

                IReadOnlyCollection <SceneObjectPart> parentGroupParts = null;
                bool needsParentGroupParts = false;
                lock (m_updateTimes)
                {
                    if (m_updateTimes.ContainsKey(part.ParentGroup.LocalId))
                    {
                        needsParentGroupParts = true;
                    }
                }

                if (!needsParentGroupParts)
                {
                    lastParentGroupParts = null;
                }
                else if (needsParentGroupParts && lastSog == part.ParentGroup && lastParentGroupParts != null)
                {
                    parentGroupParts = lastParentGroupParts;
                }
                else
                {
                    parentGroupParts     = part.ParentGroup.GetParts();
                    lastParentGroupParts = parentGroupParts;
                }

                lock (m_updateTimes)
                {
                    bool hasBeenUpdated = false;
                    if (m_updateTimes.ContainsKey(part.ParentGroup.LocalId))
                    {
                        if (parentGroupParts == null)
                        {
                            if (lastSog == part.ParentGroup && lastParentGroupParts != null)
                            {
                                parentGroupParts = lastParentGroupParts;
                            }
                            else
                            {
                                parentGroupParts     = part.ParentGroup.GetParts();
                                lastParentGroupParts = parentGroupParts;
                            }
                        }

                        foreach (SceneObjectPart p in parentGroupParts)
                        {
                            ScenePartUpdate update;
                            if (!m_updateTimes.TryGetValue(p.LocalId, out update))
                            {
                                //Threading issue? Shouldn't happen unless this method is called
                                //  while a group is being sent, but hasn't sent all prims yet
                                //  so... let's ignore the prim that is missing for now, and if
                                //  any other parts change, it'll re-send it all
                                hasBeenUpdated = true;
                                break;
                            }

                            if (update.LastFullUpdateTimeRequested == update.LastFullUpdateTime &&
                                update.LastTerseUpdateTime == update.LastTerseUpdateTimeRequested)
                            {
                                continue;//Only if we haven't sent them this prim before and it hasn't changed
                            }

                            //It's changed, check again
                            hasBeenUpdated = true;
                            break;
                        }
                    }
                    else
                    {
                        hasBeenUpdated = true;
                    }

                    if (hasBeenUpdated)
                    {
                        SendGroupUpdate(part.ParentGroup, kvp.Value.Value);
                        lastSog = part.ParentGroup;
                        continue;
                    }
                }

                lastSog = part.ParentGroup;
                SendPartUpdate(part, kvp.Value.Value);
            }

            /*if (queueCount > 0)
             * {
             *  m_log.DebugFormat("Update queue flush of {0} objects took {1}", queueCount, Environment.TickCount - time);
             * }*/

            //ControllingClient.FlushPrimUpdates();

            m_presence.Scene.StatsReporter.AddAgentTime(Environment.TickCount - m_perfMonMS);
        }
Example #5
0
        /// <summary>
        /// Checks to see whether any new prims have come into view since the last time culling checks were done
        /// </summary>
        private void CheckForDistantPrimsToShow()
        {
            List <EntityBase> SOGs;

            lock (m_presence.Scene.SyncRoot)
            {
                if (!m_presence.IsChildAgent || m_presence.Scene.m_seeIntoRegionFromNeighbor)
                {
                    SOGs = m_presence.Scene.Entities.GetAllByType <SceneObjectGroup>();
                }
                else
                {
                    return;
                }
            }

            Vector3 clientAbsPosition = m_presence.AbsolutePosition;
            List <KeyValuePair <double, SceneObjectGroup> > grps = new List <KeyValuePair <double, SceneObjectGroup> >();

            foreach (EntityBase sog in SOGs)
            {
                SceneObjectGroup e = (SceneObjectGroup)sog;

                if (m_presence.Closed)
                {
                    ClearAllTracking();
                    return;
                }

                if ((e).IsAttachment)
                {
                    if (CheckWhetherAttachmentShouldBeSent(e))
                    {
                        grps.Add(new KeyValuePair <double, SceneObjectGroup>(0, e));
                    }
                    continue;
                }

                if ((e).IsAttachedHUD && (e).OwnerID != m_presence.UUID)
                {
                    continue;//Don't ever send HUD attachments to non-owners
                }
                IReadOnlyCollection <SceneObjectPart> sogParts = null;
                bool needsParts = false;
                lock (m_updateTimes)
                {
                    if (m_updateTimes.ContainsKey(e.LocalId))
                    {
                        needsParts = true;
                    }
                }

                if (needsParts)
                {
                    sogParts = e.GetParts();
                }

                lock (m_updateTimes)
                {
                    if (m_updateTimes.ContainsKey(e.LocalId))
                    {
                        bool hasBeenUpdated = false;
                        if (sogParts == null)
                        {
                            sogParts = e.GetParts();
                        }

                        foreach (SceneObjectPart part in sogParts)
                        {
                            if (!m_updateTimes.ContainsKey(part.LocalId))
                            {
                                //Threading issue? Shouldn't happen unless this method is called
                                //  while a group is being sent, but hasn't sent all prims yet
                                //  so... let's ignore the prim that is missing for now, and if
                                //  any other parts change, it'll re-send it all
                                hasBeenUpdated = true;
                                break;
                            }
                            ScenePartUpdate update = m_updateTimes[part.LocalId];
                            if (update.LastFullUpdateTimeRequested == update.LastFullUpdateTime &&
                                update.LastTerseUpdateTime == update.LastTerseUpdateTimeRequested)
                            {
                                continue;//Only if we haven't sent them this prim before and it hasn't changed
                            }
                            //It's changed, check again
                            hasBeenUpdated = true;
                            break;
                        }
                        if (!hasBeenUpdated)
                        {
                            continue;//Only if we haven't sent them this prim before and it hasn't changed
                        }
                    }
                }

                double distance;
                if (!ShowEntityToClient(clientAbsPosition, e, out distance) &&
                    !ShowEntityToClient(m_presence.CameraPosition, e, out distance))
                {
                    continue;
                }

                grps.Add(new KeyValuePair <double, SceneObjectGroup>(distance, e));
            }

            KeyValuePair <double, SceneObjectGroup>[] arry = grps.ToArray();
            C5.Sorting.IntroSort <KeyValuePair <double, SceneObjectGroup> >(arry, 0, arry.Length, new DoubleComparer());

            //Sort by distance here, so that we send the closest updates first
            foreach (KeyValuePair <double, SceneObjectGroup> kvp in arry)
            {
                if (m_presence.Closed)
                {
                    ClearAllTracking();
                    return;
                }

                SendGroupUpdate(kvp.Value, PrimUpdateFlags.FindBest);
            }
        }
Example #6
0
        public void SendPartUpdate(SceneObjectPart part, PrimUpdateFlags updateFlags)
        {
            ScenePartUpdate update = null;

            int partFullUpdateCounter  = part.FullUpdateCounter;
            int partTerseUpdateCounter = part.TerseUpdateCounter;

            bool sendFullUpdate = false, sendFullInitialUpdate = false, sendTerseUpdate = false;

            lock (m_updateTimes)
            {
                if (m_updateTimes.TryGetValue(part.LocalId, out update))
                {
                    if ((update.LastFullUpdateTime != partFullUpdateCounter) ||
                        part.ParentGroup.IsAttachment)
                    {
                        //                            m_log.DebugFormat(
                        //                                "[SCENE PRESENCE]: Fully   updating prim {0}, {1} - part timestamp {2}",
                        //                                part.Name, part.UUID, part.TimeStampFull);

                        update.LastFullUpdateTime          = partFullUpdateCounter;
                        update.LastFullUpdateTimeRequested = partFullUpdateCounter;
                        //also cancel any pending terses since the full covers it
                        update.LastTerseUpdateTime          = partTerseUpdateCounter;
                        update.LastTerseUpdateTimeRequested = partTerseUpdateCounter;

                        sendFullUpdate = true;
                    }
                    else if (update.LastTerseUpdateTime != partTerseUpdateCounter)
                    {
                        //                            m_log.DebugFormat(
                        //                                "[SCENE PRESENCE]: Tersely updating prim {0}, {1} - part timestamp {2}",
                        //                                part.Name, part.UUID, part.TimeStampTerse);

                        update.LastTerseUpdateTime          = partTerseUpdateCounter;
                        update.LastTerseUpdateTimeRequested = partTerseUpdateCounter;

                        sendTerseUpdate = true;
                    }
                }
                else
                {
                    //never been sent to client before so do full update
                    ScenePartUpdate newUpdate = new ScenePartUpdate();
                    newUpdate.FullID                      = part.UUID;
                    newUpdate.LastFullUpdateTime          = partFullUpdateCounter;
                    newUpdate.LastFullUpdateTimeRequested = partFullUpdateCounter;

                    m_updateTimes.Add(part.LocalId, newUpdate);
                    sendFullInitialUpdate = true;
                }
            }
            if (sendFullUpdate)
            {
                part.SendFullUpdate(m_presence.ControllingClient, m_presence.GenerateClientFlags(part.UUID), updateFlags);
            }
            else if (sendTerseUpdate)
            {
                part.SendTerseUpdateToClient(m_presence.ControllingClient);
            }
            else if (sendFullInitialUpdate)
            {
                // Attachment handling
                //
                if (part.ParentGroup.IsAttachment)
                {
                    if (part != part.ParentGroup.RootPart)
                    {
                        return;
                    }

                    part.ParentGroup.SendFullUpdateToClient(m_presence.ControllingClient, PrimUpdateFlags.FullUpdate);
                    return;
                }

                part.SendFullUpdate(m_presence.ControllingClient, m_presence.GenerateClientFlags(part.UUID), PrimUpdateFlags.FullUpdate);
            }
        }
Example #7
0
        public void SendPrimUpdates()
        {
            if (m_pendingObjects == null)
            {
                if (!m_presence.IsChildAgent || (m_presence.Scene.RegionInfo.SeeIntoThisSimFromNeighbor))
                {
                    m_pendingObjects = new Queue<SceneObjectGroup>();
                    EntityBase[] entities = m_presence.Scene.Entities.GetEntities();

                    lock (m_pendingObjects)
                    {
                        foreach (EntityBase e in entities)
                        {
                            if (e != null && e is SceneObjectGroup)
                                m_pendingObjects.Enqueue((SceneObjectGroup)e);
                        }
                    }
                    entities = null;
                }
            }

            lock (m_pendingObjects)
            {
                while (m_pendingObjects != null && m_pendingObjects.Count > 0)
                {
                    SceneObjectGroup g = m_pendingObjects.Dequeue();
                    // Yes, this can really happen
                    if (g == null)
                        continue;

                    // This is where we should check for draw distance
                    // do culling and stuff. Problem with that is that until
                    // we recheck in movement, that won't work right.
                    // So it's not implemented now.
                    // - This note is from OS core, and has since been implemented as seen below

                    if (m_presence.Scene.CheckForObjectCulling)
                    {
                        //Check for part position against the av and the camera position
                        if (!Util.DistanceLessThan(m_presence.CameraPosition, g.AbsolutePosition, m_presence.DrawDistance))
                            if (m_presence.DrawDistance != 0)
                                continue;
                    }

                    // Don't even queue if we have sent this one
                    //
                    if (!m_updateTimes.ContainsKey(g.UUID))
                        g.SendFullUpdateToClient(m_presence.ControllingClient, PrimUpdateFlags.FullUpdate); //New object, send full
                }
                //Do this HERE so that all those updates added are prioritized.
                m_presence.ControllingClient.ReprioritizeUpdates();
            }

            while (m_partsUpdateQueue.Count > 0)
            {
                PrimUpdate update = m_partsUpdateQueue.Dequeue();

                if (update == null)
                    continue;

                if (update.Part.ParentGroup == null || update.Part.ParentGroup.IsDeleted)
                    continue;

                if (m_presence.Scene.CheckForObjectCulling)
                {
                    //Check for part position against the av and the camera position
                    if ((!Util.DistanceLessThan(m_presence.AbsolutePosition, update.Part.AbsolutePosition, m_presence.DrawDistance) &&
                        !Util.DistanceLessThan(m_presence.CameraPosition, update.Part.AbsolutePosition, m_presence.DrawDistance)))
                        if (m_presence.DrawDistance != 0)
                            continue;
                }

                if (m_updateTimes.ContainsKey(update.Part.UUID))
                {
                    ScenePartUpdate partupdate = m_updateTimes[update.Part.UUID];

                    // We deal with the possibility that two updates occur at
                    // the same unix time at the update point itself.

                    if ((partupdate.LastFullUpdateTime < update.Part.TimeStampFull) ||
                            update.Part.IsAttachment)
                    {
                        //                            m_log.DebugFormat(
                        //                                "[SCENE PRESENCE]: Fully   updating prim {0}, {1} - part timestamp {2}",
                        //                                part.Name, part.UUID, part.TimeStampFull);

                        update.Part.SendFullUpdate(m_presence.ControllingClient,
                               m_presence.GenerateClientFlags(update.Part), update.UpdateFlags);

                        // We'll update to the part's timestamp rather than
                        // the current time to avoid the race condition
                        // whereby the next tick occurs while we are doing
                        // this update. If this happened, then subsequent
                        // updates which occurred on the same tick or the
                        // next tick of the last update would be ignored.

                        partupdate.LastFullUpdateTime = update.Part.TimeStampFull;

                    }
                    else if (partupdate.LastTerseUpdateTime <= update.Part.TimeStampTerse)
                    {
                        //                            m_log.DebugFormat(
                        //                                "[SCENE PRESENCE]: Tersely updating prim {0}, {1} - part timestamp {2}",
                        //                                part.Name, part.UUID, part.TimeStampTerse);

                        update.Part.SendTerseUpdateToClient(m_presence.ControllingClient);

                        partupdate.LastTerseUpdateTime = update.Part.TimeStampTerse;
                    }
                }
                else
                {
                    //never been sent to client before so do full update
                    ScenePartUpdate partupdate = new ScenePartUpdate();
                    partupdate.FullID = update.Part.UUID;
                    partupdate.LastFullUpdateTime = update.Part.TimeStampFull;
                    m_updateTimes.Add(update.Part.UUID, partupdate);

                    // Attachment handling
                    //
                    if (update.Part.ParentGroup.RootPart.Shape.PCode == 9 && update.Part.ParentGroup.RootPart.Shape.State != 0)
                    {
                        if (update.Part != update.Part.ParentGroup.RootPart)
                            continue;

                        update.Part.ParentGroup.SendFullUpdateToClient(m_presence.ControllingClient, update.UpdateFlags);
                        continue;
                    }

                    update.Part.SendFullUpdate(m_presence.ControllingClient,
                            m_presence.GenerateClientFlags(update.Part), update.UpdateFlags);
                }
            }
        }