No longer calling SyncInfoUpdate to update timestamp. Bucket based concurrency control now in place for a few physics properties

whose SetXXX() functions have been implemented in SceneObjectPart.
dsg
Huaiyu (Kitty) Liu 2011-02-03 17:21:00 -08:00
parent 3cff68340f
commit 62a9e0b7c4
7 changed files with 153 additions and 45 deletions

View File

@ -262,7 +262,22 @@ namespace OpenSim.Region.CoreModules.RegionSync.RegionSyncModule
public void QueueSceneObjectPartForUpdate(SceneObjectPart part) public void QueueSceneObjectPartForUpdate(SceneObjectPart part)
{ {
//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 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) //if (part.LastUpdateActorID.Equals(m_actorID) || m_isSyncRelay)
bool updated = m_isSyncRelay;
if (!updated)
{
foreach (KeyValuePair<string, BucketSyncInfo> pair in part.BucketSyncInfoList)
{
if (pair.Value.LastUpdateActorID.Equals(m_actorID))
{
updated = true;
break;
}
}
}
if(updated)
{ {
lock (m_updateSceneObjectPartLock) lock (m_updateSceneObjectPartLock)
{ {
@ -273,10 +288,12 @@ namespace OpenSim.Region.CoreModules.RegionSync.RegionSyncModule
public void QueueScenePresenceForTerseUpdate(ScenePresence presence) public void QueueScenePresenceForTerseUpdate(ScenePresence presence)
{ {
/*
lock (m_updateScenePresenceLock) lock (m_updateScenePresenceLock)
{ {
m_presenceUpdates[presence.UUID] = presence; m_presenceUpdates[presence.UUID] = presence;
} }
* */
} }
//SendSceneUpdates put each update into an outgoing queue of each SyncConnector //SendSceneUpdates put each update into an outgoing queue of each SyncConnector
@ -740,7 +757,8 @@ namespace OpenSim.Region.CoreModules.RegionSync.RegionSyncModule
} }
/// <summary> /// <summary>
/// Check if we need to send out an update message for the given object. /// Check if we need to send out an update message for the given object. For now, we have a very inefficient solution:
/// If any synchronization bucket in any part shows a property in that bucket has changed, we'll serialize and ship the whole object.
/// </summary> /// </summary>
/// <param name="sog"></param> /// <param name="sog"></param>
/// <returns></returns> /// <returns></returns>
@ -749,10 +767,20 @@ namespace OpenSim.Region.CoreModules.RegionSync.RegionSyncModule
//If any part in the object has the last update caused by this actor itself, then send the update //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) foreach (SceneObjectPart part in sog.Parts)
{ {
/*
if (part.LastUpdateActorID.Equals(m_actorID)) if (part.LastUpdateActorID.Equals(m_actorID))
{ {
return true; return true;
} }
* */
foreach (KeyValuePair<string, BucketSyncInfo> pair in part.BucketSyncInfoList)
{
if (pair.Value.LastUpdateActorID.Equals(m_actorID))
{
return true;
}
}
} }
return false; return false;
@ -840,7 +868,7 @@ namespace OpenSim.Region.CoreModules.RegionSync.RegionSyncModule
} }
//Start symmetric synchronization initialization automatically //Start symmetric synchronization initialization automatically
//SyncStart(null); SyncStart(null);
} }
private void StartLocalSyncListener() private void StartLocalSyncListener()

View File

@ -1720,7 +1720,7 @@ namespace OpenSim.Region.Framework.Scenes
* */ * */
if (RegionSyncModule != null) if (RegionSyncModule != null)
{ {
part.SyncInfoUpdate(); //part.SyncInfoUpdate();
EventManager.TriggerNewScript(remoteClient.AgentId, part, copyID); EventManager.TriggerNewScript(remoteClient.AgentId, part, copyID);
} }
else else
@ -1798,7 +1798,7 @@ namespace OpenSim.Region.Framework.Scenes
//part.ParentGroup.ResumeScripts(); //part.ParentGroup.ResumeScripts();
if (RegionSyncModule != null) if (RegionSyncModule != null)
{ {
part.SyncInfoUpdate(); //part.SyncInfoUpdate();
EventManager.TriggerNewScript(remoteClient.AgentId, part, taskItem.ItemID); EventManager.TriggerNewScript(remoteClient.AgentId, part, taskItem.ItemID);
} }
else else

View File

@ -2560,16 +2560,6 @@ namespace OpenSim.Region.Framework.Scenes
// m_log.DebugFormat("[SCENE]: Exit DeleteSceneObject() for {0} {1}", group.Name, group.UUID); // m_log.DebugFormat("[SCENE]: Exit DeleteSceneObject() for {0} {1}", group.Name, group.UUID);
//SYMMETRIC SYNC
//Set the ActorID and TimeStamp info for this latest update
/*
foreach (SceneObjectPart part in group.Parts)
{
part.SyncInfoUpdate();
}
*
* */
//Propagate the RemovedObject message //Propagate the RemovedObject message
if (RegionSyncModule != null) if (RegionSyncModule != null)
{ {

View File

@ -1599,7 +1599,7 @@ namespace OpenSim.Region.Framework.Scenes
if (m_parentScene.RegionSyncModule != null) if (m_parentScene.RegionSyncModule != null)
{ {
//Tell other actors to link the SceneObjectParts together as a new group. //Tell other actors to link the SceneObjectParts together as a new group.
parentGroup.SyncInfoUpdate(); //parentGroup.SyncInfoUpdate();
m_parentScene.RegionSyncModule.SendLinkObject(parentGroup, root, children); m_parentScene.RegionSyncModule.SendLinkObject(parentGroup, root, children);
} }
@ -1747,6 +1747,7 @@ namespace OpenSim.Region.Framework.Scenes
//SYMMETRIC SYNC //SYMMETRIC SYNC
//set timestamp //set timestamp
/*
long timeStamp = DateTime.Now.Ticks; long timeStamp = DateTime.Now.Ticks;
string actorID = m_parentScene.GetSyncActorID(); string actorID = m_parentScene.GetSyncActorID();
foreach (SceneObjectGroup sog in afterDelinkGroups) foreach (SceneObjectGroup sog in afterDelinkGroups)
@ -1756,6 +1757,7 @@ namespace OpenSim.Region.Framework.Scenes
sog.SyncInfoUpdate(timeStamp, actorID); ; sog.SyncInfoUpdate(timeStamp, actorID); ;
} }
} }
* */
//Send out DelinkObject message to other actors to sychronize their object list //Send out DelinkObject message to other actors to sychronize their object list
m_parentScene.RegionSyncModule.SendDeLinkObject(prims, beforeDelinkGroups, afterDelinkGroups); m_parentScene.RegionSyncModule.SendDeLinkObject(prims, beforeDelinkGroups, afterDelinkGroups);
@ -2065,7 +2067,10 @@ namespace OpenSim.Region.Framework.Scenes
} }
m_numPrim += children.Length; m_numPrim += children.Length;
//SYMMETRIC SYNC,
sceneObject.AttachToScene(m_parentScene); sceneObject.AttachToScene(m_parentScene);
//sceneObject.AttachToSceneBySync(m_parentScene);
//end of SYMMETRIC SYNC,
//SYMMETRIC SYNC, //SYMMETRIC SYNC,
sceneObject.ScheduleGroupForFullUpdate_SyncInfoUnchanged(); sceneObject.ScheduleGroupForFullUpdate_SyncInfoUnchanged();

View File

@ -3868,6 +3868,7 @@ namespace OpenSim.Region.Framework.Scenes
} }
/*
public void SyncInfoUpdate() public void SyncInfoUpdate()
{ {
long timeStamp = DateTime.Now.Ticks; long timeStamp = DateTime.Now.Ticks;
@ -3885,6 +3886,46 @@ namespace OpenSim.Region.Framework.Scenes
part.SyncInfoUpdate(timeStamp, actorID); part.SyncInfoUpdate(timeStamp, actorID);
} }
} }
* */
/// <summary>
/// Attach this object to a scene after a new object is created due to receiving a sync message.
/// Code similar to AttachToScene, except that this does not invoke InitializeBucketSyncInfo of each part,
/// as that information is included in the incoming message.
/// </summary>
/// <param name="scene"></param>
public void AttachToSceneBySync(Scene scene)
{
m_scene = scene;
RegionHandle = m_scene.RegionInfo.RegionHandle;
if (m_rootPart.Shape.PCode != 9 || m_rootPart.Shape.State == 0)
m_rootPart.ParentID = 0;
if (m_rootPart.LocalId == 0)
m_rootPart.LocalId = m_scene.AllocateLocalId();
SceneObjectPart[] parts = m_parts.GetArray();
for (int i = 0; i < parts.Length; i++)
{
SceneObjectPart part = parts[i];
if (Object.ReferenceEquals(part, m_rootPart))
continue;
if (part.LocalId == 0)
part.LocalId = m_scene.AllocateLocalId();
part.ParentID = m_rootPart.LocalId;
//m_log.DebugFormat("[SCENE]: Given local id {0} to part {1}, linknum {2}, parent {3} {4}", part.LocalId, part.UUID, part.LinkNum, part.ParentID, part.ParentUUID);
}
ApplyPhysics(m_scene.m_physicalPrim);
// Don't trigger the update here - otherwise some client issues occur when multiple updates are scheduled
// for the same object with very different properties. The caller must schedule the update.
//ScheduleGroupForFullUpdate();
}
#endregion #endregion
} }

View File

@ -121,11 +121,13 @@ namespace OpenSim.Region.Framework.Scenes
public long LastUpdateTimeStamp public long LastUpdateTimeStamp
{ {
get { return m_lastUpdateTimeStamp; } get { return m_lastUpdateTimeStamp; }
set { m_lastUpdateTimeStamp = value; }
} }
public string LastUpdateActorID public string LastUpdateActorID
{ {
get { return m_lastUpdateActorID; } get { return m_lastUpdateActorID; }
set { m_lastUpdateActorID = value; }
} }
public string BucketName public string BucketName
@ -3131,7 +3133,7 @@ namespace OpenSim.Region.Framework.Scenes
//SYMMETRIC SYNC //SYMMETRIC SYNC
//update information (timestamp, actorID, etc) needed for synchronization across copies of Scene //update information (timestamp, actorID, etc) needed for synchronization across copies of Scene
SyncInfoUpdate(); //SyncInfoUpdate();
//end of SYMMETRIC SYNC //end of SYMMETRIC SYNC
} }
@ -3159,7 +3161,7 @@ namespace OpenSim.Region.Framework.Scenes
//SYMMETRIC SYNC //SYMMETRIC SYNC
//update information (timestamp, actorID, etc) needed for synchronization across copies of Scene //update information (timestamp, actorID, etc) needed for synchronization across copies of Scene
SyncInfoUpdate(); //SyncInfoUpdate();
//end of SYMMETRIC SYNC //end of SYMMETRIC SYNC
} }
@ -5158,6 +5160,7 @@ namespace OpenSim.Region.Framework.Scenes
//The ID the identifies which actor has caused the most recent update to the prim. //The ID the identifies which actor has caused the most recent update to the prim.
//We use type "string" for the ID only to make it human-readable. //We use type "string" for the ID only to make it human-readable.
/*
private string m_lastUpdateActorID=""; private string m_lastUpdateActorID="";
public string LastUpdateActorID public string LastUpdateActorID
{ {
@ -5170,6 +5173,7 @@ namespace OpenSim.Region.Framework.Scenes
m_lastUpdateTimeStamp = time; m_lastUpdateTimeStamp = time;
} }
public void SetLastUpdateActorID() public void SetLastUpdateActorID()
{ {
if (m_parentGroup != null) if (m_parentGroup != null)
@ -5182,17 +5186,19 @@ namespace OpenSim.Region.Framework.Scenes
} }
} }
private Object m_SyncInfoLock = new Object(); private Object m_SyncInfoLock = new Object();
public void SyncInfoUpdate(long timeStamp, string actorID) public void SyncInfoUpdate(long timeStamp, string actorID)
{ {
//update timestamp and actorID atomically //update timestamp and actorID atomically
lock (m_SyncInfoLock) lock (m_SyncInfoLock)
{ {
UpdateTimestamp(timeStamp); UpdateTimestamp(timeStamp);
m_lastUpdateActorID = actorID; m_lastUpdateActorID = actorID;
} }
}
}
public void SyncInfoUpdate() public void SyncInfoUpdate()
{ {
@ -5204,6 +5210,7 @@ namespace OpenSim.Region.Framework.Scenes
SyncInfoUpdate(DateTime.Now.Ticks, m_parentGroup.Scene.GetSyncActorID()); SyncInfoUpdate(DateTime.Now.Ticks, m_parentGroup.Scene.GetSyncActorID());
} }
} }
* */
//The list of each prim's properties. This is the list of properties that matter in synchronizing prim copies on different actors. //The list of each prim's properties. This is the list of properties that matter in synchronizing prim copies on different actors.
//This list is created based on properties included in the serialization/deserialization process (see SceneObjectSerializer()) and the //This list is created based on properties included in the serialization/deserialization process (see SceneObjectSerializer()) and the
@ -5281,7 +5288,7 @@ namespace OpenSim.Region.Framework.Scenes
/*
private Object propertyUpdateLock = new Object(); private Object propertyUpdateLock = new Object();
//!!!!!! -- TODO: //!!!!!! -- TODO:
@ -5410,6 +5417,8 @@ namespace OpenSim.Region.Framework.Scenes
return partUpdateResult; return partUpdateResult;
} }
* */
private bool UpdateCollisionSound(UUID updatedCollisionSound) private bool UpdateCollisionSound(UUID updatedCollisionSound)
@ -5424,8 +5433,12 @@ namespace OpenSim.Region.Framework.Scenes
public string DebugObjectPartProperties() public string DebugObjectPartProperties()
{ {
string debugMsg = "UUID " + UUID + ", Name " + Name + ", localID " + LocalId + ", lastUpdateActorID " + LastUpdateActorID + ", lastUpdateTimeStamp " + LastUpdateTimeStamp; string debugMsg = "UUID " + UUID + ", Name " + Name + ", localID " + LocalId;
debugMsg += ", parentID " + ParentID + ", parentUUID " + ParentUUID; debugMsg += ", parentID " + ParentID + ", parentUUID " + ParentUUID;
foreach (KeyValuePair<string, BucketSyncInfo> pair in m_bucketSyncInfoList)
{
debugMsg += ", Bucket " + pair.Key + ": TimeStamp - " + pair.Value.LastUpdateTimeStamp + ", ActorID - " + pair.Value.LastUpdateActorID;
}
return debugMsg; return debugMsg;
} }
@ -5484,9 +5497,10 @@ namespace OpenSim.Region.Framework.Scenes
private static string m_localActorID = ""; private static string m_localActorID = "";
//private static int m_bucketCount = 0; //private static int m_bucketCount = 0;
//private delegate void BucketUpdateProcessor(int bucketIndex); //private delegate void BucketUpdateProcessor(int bucketIndex);
private delegate void BucketUpdateProcessor(string bucketName); private delegate void BucketUpdateProcessor(SceneObjectPart updatedPart, string bucketName);
private static Dictionary<string, BucketUpdateProcessor> m_bucketUpdateProcessors = new Dictionary<string, BucketUpdateProcessor>(); //private static Dictionary<string, BucketUpdateProcessor> m_bucketUpdateProcessors = new Dictionary<string, BucketUpdateProcessor>();
private Dictionary<string, BucketUpdateProcessor> m_bucketUpdateProcessors = new Dictionary<string, BucketUpdateProcessor>();
public static void InitializeBucketInfo(Dictionary<string, string> propertyBucketMap, List<string> bucketNames, string actorID) public static void InitializeBucketInfo(Dictionary<string, string> propertyBucketMap, List<string> bucketNames, string actorID)
{ {
@ -5495,14 +5509,17 @@ namespace OpenSim.Region.Framework.Scenes
m_localActorID = actorID; m_localActorID = actorID;
//m_bucketCount = bucketNames.Count; //m_bucketCount = bucketNames.Count;
RegisterBucketUpdateProcessor(); //RegisterBucketUpdateProcessor();
} }
/// <summary> /// <summary>
/// Link each bucket with the function that applies updates to properties in the bucket. This is the "hard-coded" part /// Link each bucket with the function that applies updates to properties in the bucket upon receiving sync messages.
/// in the property-buckets implementation. When new buckets are implemented, the processing functions need to be modified accordingly. /// This is the "hard-coded" part in the property-buckets implementation. When new buckets are implemented,
/// the processing functions need to be modified accordingly.
/// </summary> /// </summary>
private static void RegisterBucketUpdateProcessor() //private static void RegisterBucketUpdateProcessor()
private void RegisterBucketUpdateProcessor()
{ {
foreach (string bucketName in m_propertyBucketNames) foreach (string bucketName in m_propertyBucketNames)
{ {
@ -5521,19 +5538,42 @@ namespace OpenSim.Region.Framework.Scenes
} }
} }
private static void GeneralBucketUpdateProcessor(string bucketName) private void GeneralBucketUpdateProcessor(SceneObjectPart updatedPart, string bucketName)
{ {
lock (m_bucketUpdateLocks[bucketName]) lock (m_bucketUpdateLocks[bucketName])
{ {
m_bucketSyncInfoList[bucketName].LastUpdateTimeStamp = updatedPart.BucketSyncInfoList[bucketName].LastUpdateTimeStamp;
m_bucketSyncInfoList[bucketName].LastUpdateActorID = updatedPart.BucketSyncInfoList[bucketName].LastUpdateActorID;
} }
} }
private static void PhysicsBucketUpdateProcessor(string bucketName) private void PhysicsBucketUpdateProcessor(SceneObjectPart updatedPart, string bucketName)
{ {
lock (m_bucketUpdateLocks[bucketName]) lock (m_bucketUpdateLocks[bucketName])
{ {
SetGroupPosition(updatedPart.GroupPosition);
SetOffsetPosition(updatedPart.OffsetPosition);
SetScale(updatedPart.Scale);
SetVelocity(updatedPart.Velocity);
SetAngularVelocity(updatedPart.AngularVelocity);
SetRotationOffset(updatedPart.RotationOffset);
//implementation in PhysicsActor
/*
"Position":
"Size":
"Force":
"RotationalVelocity":
"PA_Acceleration":
"Torque":
"Orientation":
"IsPhysical":
"Flying":
"Buoyancy":
* */
m_bucketSyncInfoList[bucketName].LastUpdateTimeStamp = updatedPart.BucketSyncInfoList[bucketName].LastUpdateTimeStamp;
m_bucketSyncInfoList[bucketName].LastUpdateActorID = updatedPart.BucketSyncInfoList[bucketName].LastUpdateActorID;
} }
} }
@ -5556,19 +5596,22 @@ namespace OpenSim.Region.Framework.Scenes
BucketSyncInfo syncInfo = new BucketSyncInfo(timeStamp, m_localActorID, bucketName); BucketSyncInfo syncInfo = new BucketSyncInfo(timeStamp, m_localActorID, bucketName);
//If the object is created by de-serialization, then it already has m_bucketSyncInfoList populated with the right number of buckets //If the object is created by de-serialization, then it already has m_bucketSyncInfoList populated with the right number of buckets
if (m_bucketSyncInfoList.ContainsKey(bucketName)) if (!m_bucketSyncInfoList.ContainsKey(bucketName))
{ //if (m_bucketSyncInfoList.ContainsKey(bucketName))
m_bucketSyncInfoList[bucketName] = syncInfo; //{
} // m_bucketSyncInfoList[bucketName] = syncInfo;
else //}
//else
{ {
m_bucketSyncInfoList.Add(bucketName, syncInfo); m_bucketSyncInfoList.Add(bucketName, syncInfo);
} }
if (!m_bucketSyncInfoList.ContainsKey(bucketName)) if (!m_bucketUpdateLocks.ContainsKey(bucketName))
{ {
m_bucketUpdateLocks.Add(bucketName, new Object()); m_bucketUpdateLocks.Add(bucketName, new Object());
} }
} }
RegisterBucketUpdateProcessor();
} }
/// <summary> /// <summary>
@ -5595,8 +5638,6 @@ namespace OpenSim.Region.Framework.Scenes
/*
public Scene.ObjectUpdateResult UpdateAllProperties(SceneObjectPart updatedPart) public Scene.ObjectUpdateResult UpdateAllProperties(SceneObjectPart updatedPart)
{ {
@ -5616,7 +5657,8 @@ namespace OpenSim.Region.Framework.Scenes
Scene.ObjectUpdateResult partUpdateResult = Scene.ObjectUpdateResult.Unchanged; Scene.ObjectUpdateResult partUpdateResult = Scene.ObjectUpdateResult.Unchanged;
for (int i=0; i<m_bucketCount; i++){ for (int i = 0; i < m_propertyBucketNames.Count; i++)
{
string bucketName = m_propertyBucketNames[i]; string bucketName = m_propertyBucketNames[i];
//First, compare the bucket's timestamp and actorID //First, compare the bucket's timestamp and actorID
if (m_bucketSyncInfoList[bucketName].LastUpdateTimeStamp > updatedPart.BucketSyncInfoList[bucketName].LastUpdateTimeStamp) if (m_bucketSyncInfoList[bucketName].LastUpdateTimeStamp > updatedPart.BucketSyncInfoList[bucketName].LastUpdateTimeStamp)
@ -5638,7 +5680,7 @@ namespace OpenSim.Region.Framework.Scenes
//Second, if need to update local properties, call each bucket's update process //Second, if need to update local properties, call each bucket's update process
if (m_bucketUpdateProcessors.ContainsKey(bucketName)) if (m_bucketUpdateProcessors.ContainsKey(bucketName))
{ {
m_bucketUpdateProcessors[bucketName](bucketName); m_bucketUpdateProcessors[bucketName](updatedPart, bucketName);
partUpdateResult = Scene.ObjectUpdateResult.Updated; partUpdateResult = Scene.ObjectUpdateResult.Updated;
} }
else else
@ -5652,7 +5694,7 @@ namespace OpenSim.Region.Framework.Scenes
return partUpdateResult; return partUpdateResult;
} }
*/
//private void UpdateBucketProperties(string bucketDescription, //private void UpdateBucketProperties(string bucketDescription,
#endregion #endregion

View File

@ -330,8 +330,8 @@ namespace OpenSim.Region.Framework.Scenes.Serialization
m_SOPXmlProcessors.Add("ParticleSystem", ProcessParticleSystem); m_SOPXmlProcessors.Add("ParticleSystem", ProcessParticleSystem);
//SYMMETRIC SYNC //SYMMETRIC SYNC
m_SOPXmlProcessors.Add("LastUpdateTimeStamp", ProcessUpdateTimeStamp); //m_SOPXmlProcessors.Add("LastUpdateTimeStamp", ProcessUpdateTimeStamp);
m_SOPXmlProcessors.Add("LastUpdateActorID", ProcessLastUpdateActorID); //m_SOPXmlProcessors.Add("LastUpdateActorID", ProcessLastUpdateActorID);
m_SOPXmlProcessors.Add("BucketSyncInfoList", ProcessBucketSyncInfo); m_SOPXmlProcessors.Add("BucketSyncInfoList", ProcessBucketSyncInfo);
//end of SYMMETRIC SYNC //end of SYMMETRIC SYNC
@ -703,6 +703,7 @@ namespace OpenSim.Region.Framework.Scenes.Serialization
} }
//SYMMETRIC SYNC //SYMMETRIC SYNC
/*
private static void ProcessUpdateTimeStamp(SceneObjectPart obj, XmlTextReader reader) private static void ProcessUpdateTimeStamp(SceneObjectPart obj, XmlTextReader reader)
{ {
obj.LastUpdateTimeStamp = reader.ReadElementContentAsLong("LastUpdateTimeStamp", string.Empty); obj.LastUpdateTimeStamp = reader.ReadElementContentAsLong("LastUpdateTimeStamp", string.Empty);
@ -712,6 +713,7 @@ namespace OpenSim.Region.Framework.Scenes.Serialization
{ {
obj.LastUpdateActorID = reader.ReadElementContentAsString("LastUpdateActorID", string.Empty); obj.LastUpdateActorID = reader.ReadElementContentAsString("LastUpdateActorID", string.Empty);
} }
* */
public static void ProcessBucketSyncInfo(SceneObjectPart obj, XmlTextReader reader) public static void ProcessBucketSyncInfo(SceneObjectPart obj, XmlTextReader reader)
{ {
@ -1245,8 +1247,8 @@ namespace OpenSim.Region.Framework.Scenes.Serialization
WriteBytes(writer, "ParticleSystem", sop.ParticleSystem); WriteBytes(writer, "ParticleSystem", sop.ParticleSystem);
//SYMMETRIC SYNC //SYMMETRIC SYNC
writer.WriteElementString("LastUpdateTimeStamp", sop.LastUpdateTimeStamp.ToString()); //writer.WriteElementString("LastUpdateTimeStamp", sop.LastUpdateTimeStamp.ToString());
writer.WriteElementString("LastUpdateActorID", sop.LastUpdateActorID); //writer.WriteElementString("LastUpdateActorID", sop.LastUpdateActorID);
WriteBucketSyncInfo(writer, sop.BucketSyncInfoList); WriteBucketSyncInfo(writer, sop.BucketSyncInfoList);
//end of SYMMETRIC SYNC //end of SYMMETRIC SYNC