Added synchronization functions to RegionSyncModule, Scene, SceneGraph, SceneObjectGroup and

SceneObjectPart: examples:
HandleAddOrUpdateObjectBySynchronization
QueueSceneObjectPartForUpdate
SceneObjectGroup.UpdateObjectAllProperties
SceneObjectPart.UpdateAllProperties

Now script engine and Scene can sync on script updating or Scene editing objects.
dsg
Huaiyu (Kitty) Liu 2010-12-28 12:09:17 -08:00
parent f10059ccd9
commit 384895cbdd
7 changed files with 643 additions and 86 deletions

View File

@ -170,32 +170,130 @@ namespace OpenSim.Region.CoreModules.RegionSync.RegionSyncModule
// Lock is used to synchronize access to the update status and update queues // Lock is used to synchronize access to the update status and update queues
private object m_updateSceneObjectPartLock = new object(); private object m_updateSceneObjectPartLock = new object();
private Dictionary<UUID, SceneObjectGroup> m_primUpdates = new Dictionary<UUID, SceneObjectGroup>(); private Dictionary<UUID, SceneObjectGroup> m_primUpdates = new Dictionary<UUID, SceneObjectGroup>();
private object m_updatePresenceLock = new object(); private object m_updateScenePresenceLock = new object();
private Dictionary<UUID, ScenePresence> m_presenceUpdates = new Dictionary<UUID, ScenePresence>(); private Dictionary<UUID, ScenePresence> m_presenceUpdates = new Dictionary<UUID, ScenePresence>();
private int m_sendingUpdates;
public void QueueSceneObjectPartForUpdate(SceneObjectPart part) public void QueueSceneObjectPartForUpdate(SceneObjectPart part)
{ {
lock (m_updateSceneObjectPartLock) //if the last update of the prim is caused by this actor itself, or if the actor is a relay node, then enqueue the update
if (part.LastUpdateActorID.Equals(m_actorID) || m_isSyncRelay)
{ {
m_primUpdates[part.UUID] = part.ParentGroup; lock (m_updateSceneObjectPartLock)
{
m_primUpdates[part.UUID] = part.ParentGroup;
}
} }
} }
public void QueueScenePresenceForTerseUpdate(ScenePresence presence) public void QueueScenePresenceForTerseUpdate(ScenePresence presence)
{ {
lock (m_updateSceneObjectPartLock) lock (m_updateScenePresenceLock)
{ {
m_presenceUpdates[presence.UUID] = presence; m_presenceUpdates[presence.UUID] = presence;
} }
} }
public void SendObjectUpdates(List<SceneObjectGroup> sog) public void SendSceneUpdates()
{ {
// Existing value of 1 indicates that updates are currently being sent so skip updates this pass
if (Interlocked.Exchange(ref m_sendingUpdates, 1) == 1)
{
m_log.WarnFormat("[REGION SYNC SERVER MODULE] SendUpdates(): An update thread is already running.");
return;
}
List<SceneObjectGroup> primUpdates;
List<ScenePresence> presenceUpdates;
lock (m_updateSceneObjectPartLock)
{
primUpdates = new List<SceneObjectGroup>(m_primUpdates.Values);
//presenceUpdates = new List<ScenePresence>(m_presenceUpdates.Values);
m_primUpdates.Clear();
//m_presenceUpdates.Clear();
}
lock (m_updateScenePresenceLock)
{
presenceUpdates = new List<ScenePresence>(m_presenceUpdates.Values);
m_presenceUpdates.Clear();
}
// This could be another thread for sending outgoing messages or just have the Queue functions
// create and queue the messages directly into the outgoing server thread.
System.Threading.ThreadPool.QueueUserWorkItem(delegate
{
// Dan's note: Sending the message when it's first queued would yield lower latency but much higher load on the simulator
// as parts may be updated many many times very quickly. Need to implement a higher resolution send in heartbeat
foreach (SceneObjectGroup sog in primUpdates)
{
//If this is a relay node, or at least one part of the object has the last update caused by this actor, then send the update
if (m_isSyncRelay || CheckObjectForSendingUpdate(sog))
{
//send
List<SyncConnector> syncConnectors = GetSyncConnectorsForObjectUpdates(sog);
foreach (SyncConnector connector in syncConnectors)
{
string sogxml = SceneObjectSerializer.ToXml2Format(sog);
SymmetricSyncMessage syncMsg = new SymmetricSyncMessage(SymmetricSyncMessage.MsgType.UpdatedObject, sogxml);
connector.EnqueueOutgoingUpdate(sog.UUID, syncMsg.ToBytes());
}
}
}
/*
foreach (ScenePresence presence in presenceUpdates)
{
try
{
if (!presence.IsDeleted)
{
OSDMap data = new OSDMap(10);
data["id"] = OSD.FromUUID(presence.UUID);
// Do not include offset for appearance height. That will be handled by RegionSyncClient before sending to viewers
if(presence.AbsolutePosition.IsFinite())
data["pos"] = OSD.FromVector3(presence.AbsolutePosition);
else
data["pos"] = OSD.FromVector3(Vector3.Zero);
if(presence.Velocity.IsFinite())
data["vel"] = OSD.FromVector3(presence.Velocity);
else
data["vel"] = OSD.FromVector3(Vector3.Zero);
data["rot"] = OSD.FromQuaternion(presence.Rotation);
data["fly"] = OSD.FromBoolean(presence.Flying);
data["flags"] = OSD.FromUInteger((uint)presence.AgentControlFlags);
data["anim"] = OSD.FromString(presence.Animator.CurrentMovementAnimation);
// needed for a full update
if (presence.ParentID != presence.lastSentParentID)
{
data["coll"] = OSD.FromVector4(presence.CollisionPlane);
data["off"] = OSD.FromVector3(presence.OffsetPosition);
data["pID"] = OSD.FromUInteger(presence.ParentID);
presence.lastSentParentID = presence.ParentID;
}
RegionSyncMessage rsm = new RegionSyncMessage(RegionSyncMessage.MsgType.UpdatedAvatar, OSDParser.SerializeJsonString(data));
m_server.EnqueuePresenceUpdate(presence.UUID, rsm.ToBytes());
}
}
catch (Exception e)
{
m_log.ErrorFormat("[REGION SYNC SERVER MODULE] Caught exception sending presence updates for {0}: {1}", presence.Name, e.Message);
}
}
* */
// Indicate that the current batch of updates has been completed
Interlocked.Exchange(ref m_sendingUpdates, 0);
});
} }
#endregion //IRegionSyncModule #endregion //IRegionSyncModule
#region ICommandableModule Members #region ICommandableModule Members
@ -296,6 +394,60 @@ namespace OpenSim.Region.CoreModules.RegionSync.RegionSyncModule
m_log.Warn("[REGION SYNC MODULE]: StatsTimerElapsed -- NOT yet implemented."); m_log.Warn("[REGION SYNC MODULE]: StatsTimerElapsed -- NOT yet implemented.");
} }
/// <summary>
/// Check if we need to send out an update message for the given object.
/// </summary>
/// <param name="sog"></param>
/// <returns></returns>
private bool CheckObjectForSendingUpdate(SceneObjectGroup sog)
{
if (sog == null || sog.IsDeleted)
return false;
//If any part in the object has the last update caused by this actor itself, then send the update
foreach (SceneObjectPart part in sog.Parts)
{
if (part.LastUpdateActorID.Equals(m_actorID))
{
return true;
}
}
return false;
}
/// <summary>
/// Get the set of SyncConnectors to send updates of the given object.
/// </summary>
/// <param name="sog"></param>
/// <returns></returns>
private List<SyncConnector> GetSyncConnectorsForObjectUpdates(SceneObjectGroup sog)
{
List<SyncConnector> syncConnectors = new List<SyncConnector>();
if (m_isSyncRelay)
{
//This is a relay node in the synchronization overlay, forward it to all connectors.
//Note LastUpdateTimeStamp and LastUpdateActorID is one per SceneObjectPart, not one per SceneObjectGroup,
//hence an actor sending in an update on one SceneObjectPart of a SceneObjectGroup may need to know updates
//in other parts as well, so we are sending to all connectors.
ForEachSyncConnector(delegate(SyncConnector connector)
{
syncConnectors.Add(connector);
});
}
else
{
//This is a end node in the synchronization overlay (e.g. a non ScenePersistence actor). Get the right set of synconnectors.
//This may go more complex when an actor connects to several ScenePersistence actors.
ForEachSyncConnector(delegate(SyncConnector connector)
{
syncConnectors.Add(connector);
});
}
return syncConnectors;
}
//NOTE: We proably don't need to do this, and there might not be a need for OnPostSceneCreation event to let RegionSyncModule //NOTE: We proably don't need to do this, and there might not be a need for OnPostSceneCreation event to let RegionSyncModule
// and ActorSyncModules to gain some access to each other. We'll keep it here for a while, until we are sure it's not // and ActorSyncModules to gain some access to each other. We'll keep it here for a while, until we are sure it's not
// needed. // needed.
@ -526,7 +678,7 @@ namespace OpenSim.Region.CoreModules.RegionSync.RegionSyncModule
List<SyncConnector> closed = null; List<SyncConnector> closed = null;
foreach (SyncConnector syncConnector in m_syncConnectors) foreach (SyncConnector syncConnector in m_syncConnectors)
{ {
// If connected, send the message. // If connected, apply the action
if (syncConnector.Connected) if (syncConnector.Connected)
{ {
action(syncConnector); action(syncConnector);
@ -584,22 +736,50 @@ namespace OpenSim.Region.CoreModules.RegionSync.RegionSyncModule
return; return;
} }
case SymmetricSyncMessage.MsgType.NewObject: case SymmetricSyncMessage.MsgType.NewObject:
case SymmetricSyncMessage.MsgType.UpdatedObject:
{ {
string sogxml = Encoding.ASCII.GetString(msg.Data, 0, msg.Length); HandleAddOrUpdateObjectBySynchronization(msg);
//HandleAddNewObject(sog);
//m_log.Debug(LogHeader + ": " + sogxml); return;
SceneObjectGroup sog = SceneObjectSerializer.FromXml2Format(sogxml);
//HandleAddOrUpdateObjectInLocalScene(sog, true, true);
HandleAddNewObject(sog);
} }
return;
default: default:
return; return;
} }
} }
public void HandleAddOrUpdateObjectBySynchronization(SymmetricSyncMessage msg)
{
string sogxml = Encoding.ASCII.GetString(msg.Data, 0, msg.Length);
SceneObjectGroup sog = SceneObjectSerializer.FromXml2Format(sogxml);
if (sog.IsDeleted)
{
SymmetricSyncMessage.HandleTrivial(LogHeader, msg, String.Format("Ignoring update on deleted object, UUID: {0}.", sog.UUID));
return;
}
else
{
Scene.ObjectUpdateResult updateResult = m_scene.AddOrUpdateObjectBySynchronization(sog);
//if (added)
switch (updateResult)
{
case Scene.ObjectUpdateResult.New:
m_log.DebugFormat("[{0} Object \"{1}\" ({1}) ({2}) added.", LogHeader, sog.Name, sog.UUID.ToString(), sog.LocalId.ToString());
break;
case Scene.ObjectUpdateResult.Updated:
m_log.DebugFormat("[{0} Object \"{1}\" ({1}) ({2}) updated.", LogHeader, sog.Name, sog.UUID.ToString(), sog.LocalId.ToString());
break;
case Scene.ObjectUpdateResult.Error:
m_log.WarnFormat("[{0} Object \"{1}\" ({1}) ({2}) -- add or update ERROR.", LogHeader, sog.Name, sog.UUID.ToString(), sog.LocalId.ToString());
break;
case Scene.ObjectUpdateResult.Unchanged:
m_log.DebugFormat("[{0} Object \"{1}\" ({1}) ({2}) unchanged after receiving an update.", LogHeader, sog.Name, sog.UUID.ToString(), sog.LocalId.ToString());
break;
}
}
}
private void HandleAddNewObject(SceneObjectGroup sog) private void HandleAddNewObject(SceneObjectGroup sog)
{ {
//RegionSyncModule only add object to SceneGraph. Any actor specific actions will be implemented //RegionSyncModule only add object to SceneGraph. Any actor specific actions will be implemented

View File

@ -21,61 +21,22 @@ namespace OpenSim.Region.CoreModules.RegionSync.RegionSyncModule
public enum MsgType public enum MsgType
{ {
Null, Null,
//ConnectSyncClient, // Actor -> SIM(Scene)
//DisconnectSyncClient,
// CM -> SIM(Scene)
ActorConnect,
AgentAdd,
AgentUpdate,
AgentRemove,
AgentRequestSit,
AgentSit,
GrabObject,
GrabUpdate,
DeGrabObject,
StartAnim,
StopAnim,
GetTerrain, GetTerrain,
GetObjects, GetObjects,
SubscribeObjects,
GetAvatars,
SubscribeAvatars,
ChatFromClient,
AvatarTeleportOut, // An LLClientView (real client) was converted to a RegionSyncAvatar
AvatarTeleportIn, // A RegionSyncAvatar was converted to an LLClientView (real client)
// SIM -> CM // SIM -> CM
Terrain, Terrain,
NewObject, // objects NewObject, // objects
UpdatedObject, // objects UpdatedObject, // objects
RemovedObject, // objects RemovedObject, // objects
NewAvatar, // avatars
UpdatedAvatar, // avatars
AnimateAvatar,
AvatarAppearance,
RemovedAvatar, // avatars
BalanceClientLoad, // Tells CM a client load target and a place to teleport the extras
ChatFromSim,
SitResponse,
SendAnimations,
// BIDIR
EchoRequest,
EchoResponse,
RegionName,
RegionStatus,
//Added by KittyL
// Actor -> Scene
// ActorType, //to register the type (e.g. Client Manager or Script Engine) with Scene when sync channel is initialized
//SetObjectProperty,
// ActorStop,
ResetScene,
OnRezScript,
OnScriptReset,
OnUpdateScript,
//QuarkSubscription,
// Scene -> Script Engine // BIDIR
//NewObjectWithScript, //EchoRequest,
//SceneLocation, //EchoResponse,
RegionName,
//RegionStatus,
ActorID,
} }
#endregion #endregion
@ -164,27 +125,27 @@ namespace OpenSim.Region.CoreModules.RegionSync.RegionSyncModule
#endregion #endregion
public static void HandleSuccess(string header, RegionSyncMessage msg, string message) public static void HandleSuccess(string header, SymmetricSyncMessage msg, string message)
{ {
m_log.WarnFormat("{0} Handled {1}: {2}", header, msg.ToString(), message); m_log.WarnFormat("{0} Handled {1}: {2}", header, msg.ToString(), message);
} }
public static void HandleTrivial(string header, RegionSyncMessage msg, string message) public static void HandleTrivial(string header, SymmetricSyncMessage msg, string message)
{ {
m_log.WarnFormat("{0} Issue handling {1}: {2}", header, msg.ToString(), message); m_log.WarnFormat("{0} Issue handling {1}: {2}", header, msg.ToString(), message);
} }
public static void HandleWarning(string header, RegionSyncMessage msg, string message) public static void HandleWarning(string header, SymmetricSyncMessage msg, string message)
{ {
m_log.WarnFormat("{0} Warning handling {1}: {2}", header, msg.ToString(), message); m_log.WarnFormat("{0} Warning handling {1}: {2}", header, msg.ToString(), message);
} }
public static void HandleError(string header, RegionSyncMessage msg, string message) public static void HandleError(string header, SymmetricSyncMessage msg, string message)
{ {
m_log.WarnFormat("{0} Error handling {1}: {2}", header, msg.ToString(), message); m_log.WarnFormat("{0} Error handling {1}: {2}", header, msg.ToString(), message);
} }
public static bool HandlerDebug(string header, RegionSyncMessage msg, string message) public static bool HandlerDebug(string header, SymmetricSyncMessage msg, string message)
{ {
m_log.WarnFormat("{0} DBG ({1}): {2}", header, msg.ToString(), message); m_log.WarnFormat("{0} DBG ({1}): {2}", header, msg.ToString(), message);
return true; return true;

View File

@ -45,6 +45,14 @@ namespace OpenSim.Region.CoreModules.RegionSync.RegionSyncModule
get { return m_connectorNum; } get { return m_connectorNum; }
} }
//the actorID of the other end of the connection
private string m_syncOtherSideActorID;
public string OtherSideActorID
{
get { return m_syncOtherSideActorID; }
set { m_syncOtherSideActorID = value; }
}
//The region name of the other side of the connection //The region name of the other side of the connection
private string m_syncOtherSideRegionName=""; private string m_syncOtherSideRegionName="";
public string OtherSideRegionName public string OtherSideRegionName
@ -267,6 +275,17 @@ namespace OpenSim.Region.CoreModules.RegionSync.RegionSyncModule
m_log.DebugFormat("Syncing to region \"{0}\"", m_syncOtherSideRegionName); m_log.DebugFormat("Syncing to region \"{0}\"", m_syncOtherSideRegionName);
return; return;
} }
case SymmetricSyncMessage.MsgType.ActorID:
{
m_syncOtherSideActorID = Encoding.ASCII.GetString(msg.Data, 0, msg.Length);
if (m_regionSyncModule.IsSyncRelay)
{
SymmetricSyncMessage outMsg = new SymmetricSyncMessage(SymmetricSyncMessage.MsgType.ActorID, m_regionSyncModule.ActorID);
Send(outMsg);
}
m_log.DebugFormat("Syncing to actor \"{0}\"", m_syncOtherSideActorID);
return;
}
default: default:
break; break;
} }

View File

@ -598,6 +598,29 @@ namespace OpenSim.Region.Framework.Scenes
} }
//This enumeration would help to identify if after a NewObject/UpdatedObject message is received,
//the object is a new object and hence added to the scene graph, or it an object with some properties
//just updated, or the copy of the object in the UpdatedObject message is the same with local copy
//(before we add time-stamp to identify updates from different actors/scene, it could be possible the same
//update be forwarded, say from script engine to scene, and then back to script engine.
public enum ObjectUpdateResult
{
New, //the New/UpdatedObject message ends up adding a new object to local scene graph
Updated, //the object has some property updated after processing the New/UpdatedObject
Unchanged, //no property of the object has been changed after processing the New/UpdatedObject
//(it probably is the same update this end has sent out before
Error //Errors happen during processing the message, e.g. the entity with the given UUID is not of type SceneObjectGroup
}
//This function should only be called by an actor who's local Scene is just a cache of the authorative Scene.
//If the object already exists, use the new copy to replace it.
//Return true if added, false if just updated
public ObjectUpdateResult AddOrUpdateObjectBySynchronization(SceneObjectGroup sog)
{
return m_sceneGraph.AddOrUpdateObjectBySynchronization(sog);
}
#endregion //SYMMETRIC SYNC #endregion //SYMMETRIC SYNC
public ICapabilitiesModule CapsModule public ICapabilitiesModule CapsModule
@ -1484,24 +1507,14 @@ namespace OpenSim.Region.Framework.Scenes
m_regionSyncServerModule.SendUpdates(); m_regionSyncServerModule.SendUpdates();
} }
/* //SYMMETRIC SYNC
// The authoritative sim should not try to send coarse locations
// Leave this up to the client managers //NOTE: If it is configured as symmetric sync in opensim.ini, the above IsSyncedServer() or IsSyncedClient() should all return false
if (!IsSyncedServer()) if (RegionSyncModule != null)
{ {
if (m_frame % m_update_coarse_locations == 0) RegionSyncModule.SendSceneUpdates();
{
List<Vector3> coarseLocations;
List<UUID> avatarUUIDs;
SceneGraph.GetCoarseLocations(out coarseLocations, out avatarUUIDs, 60);
// Send coarse locations to clients
ForEachScenePresence(delegate(ScenePresence presence)
{
presence.SendCoarseLocations(coarseLocations, avatarUUIDs);
});
}
} }
* */ //end of SYMMETRIC SYNC
int tmpPhysicsMS2 = Util.EnvironmentTickCount(); int tmpPhysicsMS2 = Util.EnvironmentTickCount();
// Do not simulate physics locally if this is a synced client // Do not simulate physics locally if this is a synced client

View File

@ -1923,7 +1923,102 @@ namespace OpenSim.Region.Framework.Scenes
} }
#endregion #endregion // REGION SYNC
#region SYMMETRIC SYNC
public Scene.ObjectUpdateResult AddOrUpdateObjectBySynchronization(SceneObjectGroup updatedSog)
{
UUID sogID = updatedSog.UUID;
if (Entities.ContainsKey(sogID))
{
//update the object
EntityBase entity = Entities[sogID];
if (entity is SceneObjectGroup)
{
SceneObjectGroup localSog = (SceneObjectGroup)entity;
Scene.ObjectUpdateResult updateResult = localSog.UpdateObjectAllProperties(updatedSog);
return updateResult;
}
else
{
m_log.Warn("Entity with " + sogID + " is not of type SceneObjectGroup");
//return false;
return Scene.ObjectUpdateResult.Error;
}
}
else
{
AddSceneObjectByStateSynch(updatedSog);
return Scene.ObjectUpdateResult.New;
}
}
//This is an object added due to receiving a state synchronization message from Scene or an actor. Do similar things as the original AddSceneObject(),
//but call ScheduleGroupForFullUpdate_TimeStampUnchanged() instead, so as not to modify the timestamp or actorID, since the object was not created
//locally.
protected bool AddSceneObjectByStateSynch(SceneObjectGroup sceneObject)
{
if (sceneObject == null || sceneObject.RootPart == null || sceneObject.RootPart.UUID == UUID.Zero)
return false;
if (Entities.ContainsKey(sceneObject.UUID))
return false;
SceneObjectPart[] children = sceneObject.Parts;
// Clamp child prim sizes and add child prims to the m_numPrim count
if (m_parentScene.m_clampPrimSize)
{
foreach (SceneObjectPart part in children)
{
Vector3 scale = part.Shape.Scale;
if (scale.X > m_parentScene.m_maxNonphys)
scale.X = m_parentScene.m_maxNonphys;
if (scale.Y > m_parentScene.m_maxNonphys)
scale.Y = m_parentScene.m_maxNonphys;
if (scale.Z > m_parentScene.m_maxNonphys)
scale.Z = m_parentScene.m_maxNonphys;
part.Shape.Scale = scale;
}
}
m_numPrim += children.Length;
sceneObject.AttachToScene(m_parentScene);
//SYMMETRIC SYNC,
sceneObject.ScheduleGroupForFullUpdate_SyncInfoUnchanged();
//end of SYMMETRIC SYNC,
Entities.Add(sceneObject);
//ScenePersistenceSyncModule will attach the object to backup when it catches the OnObjectCreate event.
//if (attachToBackup)
// sceneObject.AttachToBackup();
if (OnObjectCreate != null)
OnObjectCreate(sceneObject);
lock (SceneObjectGroupsByFullID)
{
SceneObjectGroupsByFullID[sceneObject.UUID] = sceneObject;
foreach (SceneObjectPart part in children)
SceneObjectGroupsByFullID[part.UUID] = sceneObject;
}
lock (SceneObjectGroupsByLocalID)
{
SceneObjectGroupsByLocalID[sceneObject.LocalId] = sceneObject;
foreach (SceneObjectPart part in children)
SceneObjectGroupsByLocalID[part.LocalId] = sceneObject;
}
return true;
}
#endregion //SYMMETRIC SYNC
} }
} }

View File

@ -3459,6 +3459,141 @@ namespace OpenSim.Region.Framework.Scenes
#region SYMMETRIC SYNC #region SYMMETRIC SYNC
//update the existing copy of the object with updated properties in 'updatedSog'
//NOTE: updates on script content are handled seperately (e.g. user edited the script and saved it) -- SESyncServerOnUpdateScript(), a handler of EventManager.OnUpdateScript
//public void UpdateObjectProperties(SceneObjectGroup updatedSog)
/// <summary>
/// Update the existing copy of the object with updated properties in 'updatedSog'. For now we update
/// all properties. Later on this should be edited to allow only updating a bucket of properties.
/// </summary>
/// <param name="updatedSog"></param>
/// <returns></returns>
public Scene.ObjectUpdateResult UpdateObjectAllProperties(SceneObjectGroup updatedSog)
{
if (!this.GroupID.Equals(updatedSog.GroupID))
return Scene.ObjectUpdateResult.Error;
////////////////////////////////////////////////////////////////////////////////////////////////////
//NOTE!!!
//We do not want to simply call SceneObjectGroup.Copy here to clone the object:
//the prims (SceneObjectParts) in updatedSog are different instances than those in the local copy,
//and we want to preserve the references to the prims in this local copy, especially for scripts
//of each prim, where the scripts have references to the local copy. If the local copy is replaced,
//the prims (parts) will be replaces and we need to update all the references that were pointing to
//the previous prims.
////////////////////////////////////////////////////////////////////////////////////////////////////
Scene.ObjectUpdateResult groupUpdateResult = Scene.ObjectUpdateResult.Unchanged;
Dictionary<UUID, SceneObjectPart> updatedParts = new Dictionary<UUID, SceneObjectPart>();
bool partsRemoved = false; //has any old part been removed?
bool rootPartChanged = false; //has the rootpart be changed to a different prim?
lock (m_parts)
{
//update rootpart, if changed
if (m_rootPart.UUID != updatedSog.RootPart.UUID)
{
m_rootPart = updatedSog.RootPart;
rootPartChanged = true;
}
//foreach (KeyValuePair<UUID, SceneObjectPart> pair in updatedSog.Parts)
foreach (SceneObjectPart updatedPart in updatedSog.Parts)
{
UUID partUUID = updatedPart.UUID;
Scene.ObjectUpdateResult partUpdateResult = Scene.ObjectUpdateResult.Unchanged;
if (HasChildPrim(partUUID))
{
//update the existing part
SceneObjectPart oldPart = GetChildPart(partUUID);
partUpdateResult = oldPart.UpdateAllProperties(updatedPart);
updatedParts.Add(partUUID, updatedPart);
}
else
{
//a new part
//m_parts.Add(partUUID, updatedPart);
AddPart(updatedPart);
partUpdateResult = Scene.ObjectUpdateResult.New;
}
if (partUpdateResult != Scene.ObjectUpdateResult.Unchanged)
{
if (partUpdateResult == Scene.ObjectUpdateResult.New)
groupUpdateResult = Scene.ObjectUpdateResult.Updated;
else
groupUpdateResult = partUpdateResult; //Error or Updated
}
}
//For any parts that are not in the updatesParts (the old parts that are still in updatedSog), delete them.
foreach (SceneObjectPart oldPart in this.Parts)
{
if (!updatedParts.ContainsKey(oldPart.UUID))
{
m_parts.Remove(oldPart.UUID);
partsRemoved = true;
}
}
//Update the rootpart's ID in each non root parts
if (rootPartChanged)
{
UpdateParentIDs();
}
}
if (partsRemoved)
{
groupUpdateResult = Scene.ObjectUpdateResult.Updated;
}
/*
//update the authoritative scene that this object is located, which is identified by (LocX, LocY)
if (this.m_locX != updatedSog.LocX)
{
this.m_locX = updatedSog.LocX;
groupUpdateResult = Scene.ObjectUpdateResult.Updated;
}
if (this.m_locY != updatedSog.LocY)
{
this.m_locY = updatedSog.LocY;
groupUpdateResult = Scene.ObjectUpdateResult.Updated;
}
* */
//Schedule updates to be sent out, if the local copy has just been updated
//(1) if we are debugging the actor with a viewer attaching to it,
//we need to schedule updates to be sent to the viewer.
//(2) or if we are a relaying node to relay updates, we need to forward the updates.
//NOTE: LastUpdateTimeStamp and LastUpdateActorID should be kept the same as in the received copy of the object.
if (groupUpdateResult == Scene.ObjectUpdateResult.Updated)
{
ScheduleGroupForFullUpdate_SyncInfoUnchanged();
}
return groupUpdateResult;
}
public void ScheduleGroupForFullUpdate_SyncInfoUnchanged()
{
if (IsAttachment)
m_log.DebugFormat("[SOG]: Scheduling full update for {0} {1}", Name, LocalId);
checkAtTargets();
RootPart.ScheduleFullUpdate_SyncInfoUnchanged();
lock (m_parts)
{
foreach (SceneObjectPart part in this.Parts)
{
if (part != RootPart)
part.ScheduleFullUpdate_SyncInfoUnchanged();
}
}
}
#endregion #endregion
} }
} }

View File

@ -3147,6 +3147,8 @@ namespace OpenSim.Region.Framework.Scenes
//SYMMETRIC SYNC //SYMMETRIC SYNC
m_parentGroup.Scene.RegionSyncModule.QueueSceneObjectPartForUpdate(this);
//end of SYMMETRIC SYNC //end of SYMMETRIC SYNC
} }
@ -4962,6 +4964,158 @@ namespace OpenSim.Region.Framework.Scenes
} }
} }
//!!!!!! -- TODO:
//!!!!!! -- We should call UpdateXXX functions to update each property, cause some of such updates involves sanity checking.
public Scene.ObjectUpdateResult UpdateAllProperties(SceneObjectPart updatedPart)
{
////////////////////////////////////////////////////////////////////////////////////////////////////
//NOTE!!!: So far this function is written with Script Engine updating local Scene cache in mind.
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////Assumptions: ////////////////////
//(1) prim's UUID and LocalID shall not change (UUID is the unique identifies, LocalID is used to refer to the prim by, say scripts)
//(2) RegionHandle won't be updated -- each copy of Scene is hosted on a region with different region handle
//(3) ParentID won't be updated -- if the rootpart of the SceneObjectGroup changed, that will be updated in SceneObjectGroup.UpdateObjectProperties
////////////////////Furture enhancements:////////////////////
//For now, we only update the set of properties that are included in serialization.
//See SceneObjectSerializer for the properties that are included in a serialized SceneObjectPart.
//Later on, we may implement update functions that allow updating certain properties or certain buckets of properties.
if (updatedPart == null)
return Scene.ObjectUpdateResult.Error;
if (m_lastUpdateTimeStamp > updatedPart.LastUpdateTimeStamp)
{
//Our timestamp is more update to date, keep our values of the properties. Do not update anything.
return Scene.ObjectUpdateResult.Unchanged;
}
if (m_lastUpdateTimeStamp == updatedPart.LastUpdateTimeStamp)
{
//if (m_parentGroup.Scene.GetActorID() != updatedPart.LastUpdatedByActorID)
if (m_lastUpdateByActorID != updatedPart.LastUpdateActorID)
{
m_log.Warn("Different actors modified SceneObjetPart " + UUID + " with the same TimeStamp, CONFLICT RESOLUTION TO BE IMPLEMENTED!!!!");
return Scene.ObjectUpdateResult.Unchanged;
}
//My own update was relayed back. Don't relay it.
return Scene.ObjectUpdateResult.Unchanged;
}
//Otherwise, our timestamp is less up to date, update the prim with the received copy
Scene.ObjectUpdateResult partUpdateResult = Scene.ObjectUpdateResult.Updated;
//See SceneObjectSerializer for the properties that are included in a serialized SceneObjectPart.
this.AllowedDrop = updatedPart.AllowedDrop;
this.CreatorID = updatedPart.CreatorID;
this.CreatorData = updatedPart.CreatorData;
this.FolderID = updatedPart.FolderID;
this.InventorySerial = updatedPart.InventorySerial;
this.TaskInventory = updatedPart.TaskInventory;
//Following two properties, UUID and LocalId, shall not be updated.
//this.UUID
//this.LocalId
this.Name = updatedPart.Name;
this.Material = updatedPart.Material;
this.PassTouches = updatedPart.PassTouches;
//RegionHandle shall not be copied, since updatedSog is sent by a different actor, which has a different local region
//this.RegionHandle
this.ScriptAccessPin = updatedPart.ScriptAccessPin;
this.GroupPosition = updatedPart.GroupPosition;
this.OffsetPosition = updatedPart.OffsetPosition;
this.RotationOffset = updatedPart.RotationOffset;
this.Velocity = updatedPart.Velocity;
this.AngularVelocity = updatedPart.AngularVelocity;
this.Acceleration = updatedPart.Acceleration;
this.Description = updatedPart.Description;
this.Color = updatedPart.Color;
this.Text = updatedPart.Text;
this.SitName = updatedPart.SitName;
this.TouchName = updatedPart.TouchName;
this.LinkNum = updatedPart.LinkNum;
this.ClickAction = updatedPart.ClickAction;
this.Shape = updatedPart.Shape;
this.Scale = updatedPart.Scale;
this.UpdateFlag = updatedPart.UpdateFlag;
this.SitTargetOrientation = updatedPart.SitTargetOrientation;
this.SitTargetPosition = updatedPart.SitTargetPosition;
this.SitTargetPositionLL = updatedPart.SitTargetPositionLL;
this.SitTargetOrientationLL = updatedPart.SitTargetOrientationLL;
//ParentID should still point to the rootpart in the local sog, do not update. If the root part changed, we will update it in SceneObjectGroup.UpdateObjectProperties()
//this.ParentID;
this.CreationDate = updatedPart.CreationDate;
this.Category = updatedPart.Category;
this.SalePrice = updatedPart.SalePrice;
this.ObjectSaleType = updatedPart.ObjectSaleType;
this.OwnershipCost = updatedPart.OwnershipCost;
this.GroupID = updatedPart.GroupID;
this.OwnerID = updatedPart.OwnerID;
this.LastOwnerID = updatedPart.LastOwnerID;
this.BaseMask = updatedPart.BaseMask;
this.OwnerMask = updatedPart.OwnerMask;
this.GroupMask = updatedPart.GroupMask;
this.EveryoneMask = updatedPart.EveryoneMask;
this.NextOwnerMask = updatedPart.NextOwnerMask;
this.Flags = updatedPart.Flags;
this.CollisionSound = updatedPart.CollisionSound;
this.CollisionSoundVolume = updatedPart.CollisionSoundVolume;
this.MediaUrl = updatedPart.MediaUrl;
this.TextureAnimation = updatedPart.TextureAnimation;
this.ParticleSystem = updatedPart.ParticleSystem;
//Update the timestamp and LastUpdatedByActorID first.
this.m_lastUpdateByActorID = updatedPart.LastUpdateActorID;
this.m_lastUpdateTimeStamp = updatedPart.LastUpdateTimeStamp;
/*
this.m_inventory.Items = (TaskInventoryDictionary)updatedPart.m_inventory.Items.Clone();
//update shape information, for now, only update fileds in Shape whose set functions are defined in PrimitiveBaseShape
this.Shape = updatedPart.Shape.Copy();
this.Shape.TextureEntry = updatedPart.Shape.TextureEntry;
* */
return partUpdateResult;
}
/// <summary>
/// Schedules this prim for a full update, without changing the timestamp or actorID (info on when and who modified any property).
/// NOTE: this is the same as the original SceneObjectPart.ScheduleFullUpdate().
/// </summary>
public void ScheduleFullUpdate_SyncInfoUnchanged()
{
// m_log.DebugFormat("[SCENE OBJECT PART]: Scheduling full update for {0} {1}", Name, LocalId);
if (m_parentGroup != null)
{
m_parentGroup.QueueForUpdateCheck();
}
int timeNow = Util.UnixTimeSinceEpoch();
// If multiple updates are scheduled on the same second, we still need to perform all of them
// So we'll force the issue by bumping up the timestamp so that later processing sees these need
// to be performed.
if (timeNow <= TimeStampFull)
{
TimeStampFull += 1;
}
else
{
TimeStampFull = (uint)timeNow;
}
m_updateFlag = 2;
// m_log.DebugFormat(
// "[SCENE OBJECT PART]: Scheduling full update for {0}, {1} at {2}",
// UUID, Name, TimeStampFull);
}
#endregion #endregion
} }