diff --git a/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/SymmetricSync/RegionSyncModule.cs b/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/SymmetricSync/RegionSyncModule.cs index b26d2942d4..f2fe970005 100644 --- a/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/SymmetricSync/RegionSyncModule.cs +++ b/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/SymmetricSync/RegionSyncModule.cs @@ -179,12 +179,16 @@ namespace OpenSim.Region.CoreModules.RegionSync.RegionSyncModule get { return m_isSyncRelay; } } - private Dictionary m_primPropertyBucketMap = new Dictionary(); - public Dictionary PrimPropertyBucketMap + private Dictionary m_primPropertyBucketMap = new Dictionary(); + public Dictionary PrimPropertyBucketMap { get { return m_primPropertyBucketMap; } } - public List PropertyBucketDescription = new List(); + private List m_propertyBucketDescription = new List(); + public List PropertyBucketDescription + { + get { return m_propertyBucketDescription; } + } private RegionSyncListener m_localSyncListener = null; private bool m_synced = false; @@ -201,21 +205,24 @@ namespace OpenSim.Region.CoreModules.RegionSync.RegionSyncModule //Read in configuration for which property-bucket each property belongs to, and the description of each bucket private void PupolatePropertyBucketMap(IConfig config) { + //We start with a default bucket map. Will add the code to read in configuration from config files later. PupolatePropertyBuketMapByDefault(); + //Pass the bucket information to SceneObjectPart. + SceneObjectPart.InitializeBucketInfo(m_primPropertyBucketMap, m_propertyBucketDescription, m_actorID); + } //If nothing configured in the config file, this is the default settings for grouping properties into different bucket private void PupolatePropertyBuketMapByDefault() { //by default, there are two property buckets: the "General" bucket and the "Physics" bucket. - PropertyBucketDescription.Add("General"); - PropertyBucketDescription.Add("Physics"); + string generalBucketName = "General"; + string physicsBucketName = "Physics"; + m_propertyBucketDescription.Add(generalBucketName); + m_propertyBucketDescription.Add(physicsBucketName); m_maxNumOfPropertyBuckets = 2; - int generalBucketID = 0; - int physicsBucketID = 1; - foreach (string pName in SceneObjectPart.PropertyList) { switch (pName){ @@ -235,11 +242,11 @@ namespace OpenSim.Region.CoreModules.RegionSync.RegionSyncModule case "IsPhysical": case "Flying": case "Buoyancy": - m_primPropertyBucketMap.Add(pName, physicsBucketID); + m_primPropertyBucketMap.Add(pName, generalBucketName); break; default: //all other properties belong to the "General" bucket. - m_primPropertyBucketMap.Add(pName, generalBucketID); + m_primPropertyBucketMap.Add(pName, physicsBucketName); break; } } diff --git a/OpenSim/Region/Framework/Interfaces/IRegionSyncModule.cs b/OpenSim/Region/Framework/Interfaces/IRegionSyncModule.cs index 0b2fccb24b..812a98610d 100755 --- a/OpenSim/Region/Framework/Interfaces/IRegionSyncModule.cs +++ b/OpenSim/Region/Framework/Interfaces/IRegionSyncModule.cs @@ -55,7 +55,15 @@ namespace OpenSim.Region.Framework.Interfaces string ActorID { get; } DSGActorTypes DSGActorType { get; set; } bool IsSyncRelay { get; } - Dictionary PrimPropertyBucketMap { get; } + + /// + /// The mapping of a property (identified by its name) to the index of a bucket. + /// + Dictionary PrimPropertyBucketMap { get; } + /// + /// The text description of the properties in each bucket, e.g. "General", "Physics" + /// + List PropertyBucketDescription { get; } //Enqueue updates for scene-objects and scene-presences void QueueSceneObjectPartForUpdate(SceneObjectPart part); diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs index 605e591bf8..432f83ceb8 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs @@ -579,6 +579,15 @@ namespace OpenSim.Region.Framework.Scenes // 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(); + + //SYMMETRIC SYNC + if (m_scene.RegionSyncClientModule != null) + { + foreach (SceneObjectPart part in Parts) + { + part.InitializeBucketSyncInfo(); + } + } } public Vector3 GroupScale() diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs index 05140def97..42cff1809c 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs @@ -116,6 +116,7 @@ namespace OpenSim.Region.Framework.Scenes private string m_lastUpdateActorID; //lock for concurrent updates of the timestamp and actorID. private Object m_updateLock = new Object(); + private string m_bucketName; public long LastUpdateTimeStamp { @@ -127,10 +128,21 @@ namespace OpenSim.Region.Framework.Scenes get { return m_lastUpdateActorID; } } - public BucketSyncInfo(long timeStamp, string actorID) + public string BucketName + { + get { return m_bucketName; } + } + + public BucketSyncInfo(string bucketName) + { + m_bucketName = bucketName; + } + + public BucketSyncInfo(long timeStamp, string actorID, string bucketName) { m_lastUpdateTimeStamp = timeStamp; m_lastUpdateActorID = actorID; + m_bucketName = bucketName; } public void UpdateSyncInfo(long timeStamp, string actorID) @@ -5096,11 +5108,12 @@ namespace OpenSim.Region.Framework.Scenes - + /* private Object propertyUpdateLock = new Object(); //!!!!!! -- TODO: //!!!!!! -- We should call UpdateXXX functions to update each property, cause some of such updates involves sanity checking. + public Scene.ObjectUpdateResult UpdateAllProperties(SceneObjectPart updatedPart) { //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -5223,6 +5236,8 @@ namespace OpenSim.Region.Framework.Scenes return partUpdateResult; } + */ + private bool UpdateCollisionSound(UUID updatedCollisionSound) { @@ -5276,34 +5291,170 @@ namespace OpenSim.Region.Framework.Scenes } - //The following three variables should be initialized when this SceneObjectPart is added into the local Scene. - private List m_bucketSyncInfo = null; - private Dictionary m_primPropertyBucketMap = null; - private string m_localActorID = ""; + //The following variables should be initialized when this SceneObjectPart is added into the local Scene. + //private List SynchronizeUpdatesToScene = null; + //public List BucketSyncInfoList + private Dictionary m_bucketSyncInfoList = null; + public Dictionary BucketSyncInfoList + { + get { return m_bucketSyncInfoList; } + set { m_bucketSyncInfoList = value; } + } + //TODO: serialization and deserialization processors to be added in SceneObjectSerializer - public void InitializeBucketSyncInfo(Dictionary propertyBucketMap, string actorID) + //The following variables are initialized when RegionSyncModule reads the config file for mapping of properties and buckets + private static Dictionary m_primPropertyBucketMap = null; + private static List m_propertyBucketNames = null; + //private static List m_bucketUpdateLocks = null; + private static Dictionary m_bucketUpdateLocks = new Dictionary(); + + private static string m_localActorID = ""; + private static int m_bucketCount = 0; + //private delegate void BucketUpdateProcessor(int bucketIndex); + private delegate void BucketUpdateProcessor(string bucketName); + + private static Dictionary m_bucketUpdateProcessors = new Dictionary(); + + public static void InitializeBucketInfo(Dictionary propertyBucketMap, List bucketNames, string actorID) { m_primPropertyBucketMap = propertyBucketMap; + m_propertyBucketNames = bucketNames; m_localActorID = actorID; - int bucketNum = propertyBucketMap.Count; - long timeStamp = DateTime.Now.Ticks; - for (int i = 0; i < bucketNum; i++) + m_bucketCount = propertyBucketMap.Count; + + RegisterBucketUpdateProcessor(); + } + + /// + /// Link each bucket with the function that applies updates to properties in the bucket. This is the "hard-coded" part + /// in the property-buckets implementation. When new buckets are implemented, the processing functions need to be modified accordingly. + /// + private static void RegisterBucketUpdateProcessor() + { + foreach (string bucketName in m_propertyBucketNames) { - BucketSyncInfo syncInfo = new BucketSyncInfo(timeStamp, m_localActorID); - m_bucketSyncInfo.Add(syncInfo); + switch (bucketName) + { + case "General": + m_bucketUpdateProcessors.Add(bucketName, GeneralBucketUpdateProcessor); + break; + case "Physics": + m_bucketUpdateProcessors.Add(bucketName, PhysicsBucketUpdateProcessor); + break; + default: + m_log.Warn("Bucket " + bucketName + "'s update processing function not defined yet"); + break; + } + } + } + + private static void GeneralBucketUpdateProcessor(string bucketName) + { + lock (m_bucketUpdateLocks[bucketName]) + { + + } + } + + private static void PhysicsBucketUpdateProcessor(string bucketName) + { + lock (m_bucketUpdateLocks[bucketName]) + { + + } + } + + //Should be called when the SceneObjectGroup this part is in is added to scene, see SceneObjectGroup.AttachToScene + public void InitializeBucketSyncInfo() + { + if (m_primPropertyBucketMap == null) + { + m_log.Error("Bucket Information has not been initilized. Return."); + return; + } + long timeStamp = DateTime.Now.Ticks; + for (int i = 0; i < m_bucketCount; i++) + { + string bucketName = m_propertyBucketNames[i]; + BucketSyncInfo syncInfo = new BucketSyncInfo(timeStamp, m_localActorID, bucketName); + m_bucketSyncInfoList.Add(bucketName, syncInfo); + m_bucketUpdateLocks.Add(bucketName, new Object()); } } private void UpdateBucketSyncInfo(string propertyName) { - if (m_bucketSyncInfo != null) + if (m_bucketSyncInfoList != null) { - int bucketIndex = m_primPropertyBucketMap[propertyName]; + //int bucketIndex = m_primPropertyBucketMap[propertyName]; + string bucketName = m_primPropertyBucketMap[propertyName]; long timeStamp = DateTime.Now.Ticks; - m_bucketSyncInfo[bucketIndex].UpdateSyncInfo(timeStamp, m_localActorID); + m_bucketSyncInfoList[bucketName].UpdateSyncInfo(timeStamp, m_localActorID); } } + + + + + public Scene.ObjectUpdateResult UpdateAllProperties(SceneObjectPart updatedPart) + { + + ////////////////////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, and some PhysicsActor properties + //See RegionSyncModule.PupolatePropertyBuketMapByDefault for the properties that are handled. + + if (updatedPart == null) + return Scene.ObjectUpdateResult.Error; + + //Compate the timestamp of each bucket and update the properties if needed + + Scene.ObjectUpdateResult partUpdateResult = Scene.ObjectUpdateResult.Unchanged; + + for (int i=0; i updatedPart.BucketSyncInfoList[bucketName].LastUpdateTimeStamp) + { + //Our timestamp is more update to date, keep our values of the properties. Do not update anything. + continue; + } + + if (m_bucketSyncInfoList[bucketName].LastUpdateTimeStamp == updatedPart.BucketSyncInfoList[bucketName].LastUpdateTimeStamp) + { + if (!m_bucketSyncInfoList[bucketName].LastUpdateActorID.Equals(updatedPart.BucketSyncInfoList[bucketName].LastUpdateActorID)) + { + m_log.Warn("Different actors modified SceneObjetPart " + UUID + " with the same TimeStamp (" + m_bucketSyncInfoList[bucketName].LastUpdateActorID + + "," + updatedPart.BucketSyncInfoList[bucketName].LastUpdateActorID + ", CONFLICT RESOLUTION TO BE IMPLEMENTED!!!!"); + } + continue; + } + + //Second, if need to update local properties, call each bucket's update process + if (m_bucketUpdateProcessors.ContainsKey(bucketName)) + { + m_bucketUpdateProcessors[bucketName](bucketName); + partUpdateResult = Scene.ObjectUpdateResult.Updated; + } + else + { + m_log.Warn("No update processor for property bucket " + bucketName); + } + + + } + + return partUpdateResult; + + } + + //private void UpdateBucketProperties(string bucketDescription, + #endregion } diff --git a/OpenSim/Region/Framework/Scenes/Serialization/SceneObjectSerializer.cs b/OpenSim/Region/Framework/Scenes/Serialization/SceneObjectSerializer.cs index 0596cee96d..5d610e733a 100644 --- a/OpenSim/Region/Framework/Scenes/Serialization/SceneObjectSerializer.cs +++ b/OpenSim/Region/Framework/Scenes/Serialization/SceneObjectSerializer.cs @@ -330,8 +330,9 @@ namespace OpenSim.Region.Framework.Scenes.Serialization m_SOPXmlProcessors.Add("ParticleSystem", ProcessParticleSystem); //SYMMETRIC SYNC - m_SOPXmlProcessors.Add("LastUpdateTimeStamp", ProcessUpdateTimeStamp); - m_SOPXmlProcessors.Add("LastUpdateActorID", ProcessLastUpdateActorID); + //m_SOPXmlProcessors.Add("LastUpdateTimeStamp", ProcessUpdateTimeStamp); + //m_SOPXmlProcessors.Add("LastUpdateActorID", ProcessLastUpdateActorID); + m_SOPXmlProcessors.Add("BucketSyncInfoList", ProcessBucketSyncInfo); //end of SYMMETRIC SYNC #endregion @@ -702,6 +703,55 @@ namespace OpenSim.Region.Framework.Scenes.Serialization { obj.LastUpdateActorID = reader.ReadElementContentAsString("LastUpdateActorID", string.Empty); } + + private static void ProcessBucketSyncInfo(SceneObjectPart obj, XmlTextReader reader) + { + obj.BucketSyncInfoList = new Dictionary(); + + if (reader.IsEmptyElement) + { + reader.Read(); + return; + } + string elementName = "BucketSyncInfoList"; + reader.ReadStartElement(elementName, String.Empty); + + while (reader.Name == "Bucket") + { + reader.ReadStartElement("Bucket", String.Empty); // Bucket + string bucketName=""; + long timeStamp = 0; + string actorID = ""; + while (reader.NodeType != XmlNodeType.EndElement) + { + + switch (reader.Name) + { + case "Name": + bucketName = reader.ReadElementContentAsString("Name", String.Empty); + break; + case "TimeStamp": + timeStamp = reader.ReadElementContentAsLong("TimeStamp", String.Empty); + break; + case "ActorID": + actorID = reader.ReadElementContentAsString("ActorID", String.Empty); + break; + default: + reader.ReadOuterXml(); + break; + + } + + } + reader.ReadEndElement(); + BucketSyncInfo bucketSyncInfo = new BucketSyncInfo(timeStamp, actorID, bucketName); + obj.BucketSyncInfoList.Add(bucketName, bucketSyncInfo); + } + + if (reader.NodeType == XmlNodeType.EndElement) + reader.ReadEndElement(); // BucketSyncInfoList + } + //end of SYMMETRIC SYNC #endregion @@ -1186,13 +1236,36 @@ namespace OpenSim.Region.Framework.Scenes.Serialization WriteBytes(writer, "ParticleSystem", sop.ParticleSystem); //SYMMETRIC SYNC - writer.WriteElementString("LastUpdateTimeStamp", sop.LastUpdateTimeStamp.ToString()); - writer.WriteElementString("LastUpdateActorID", sop.LastUpdateActorID); + //writer.WriteElementString("LastUpdateTimeStamp", sop.LastUpdateTimeStamp.ToString()); + //writer.WriteElementString("LastUpdateActorID", sop.LastUpdateActorID); + WriteBucketSyncInfo(writer, sop.BucketSyncInfoList); //end of SYMMETRIC SYNC writer.WriteEndElement(); } + //SYMMETRIC SYNC + static void WriteBucketSyncInfo(XmlTextWriter writer, Dictionary bucketSyncInfoList) + { + if (bucketSyncInfoList.Count > 0) // otherwise skip this + { + writer.WriteStartElement("BucketSyncInfoList"); + foreach (KeyValuePair pair in bucketSyncInfoList) + { + BucketSyncInfo bucketSyncInfo = pair.Value; + writer.WriteStartElement("Bucket"); + writer.WriteElementString("Name", bucketSyncInfo.BucketName); + writer.WriteElementString("TimeStamp", bucketSyncInfo.LastUpdateTimeStamp.ToString()); + writer.WriteElementString("ActorID", bucketSyncInfo.LastUpdateActorID); + writer.WriteEndElement(); // Bucket + } + + writer.WriteEndElement(); // BucketSyncInfo + } + + } + //end of SYMMETRIC SYNC + static void WriteUUID(XmlTextWriter writer, string name, UUID id, Dictionary options) { writer.WriteStartElement(name);