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