diff --git a/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/SymmetricSync/RegionSyncModule.cs b/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/SymmetricSync/RegionSyncModule.cs index ecbae7927c..0d92ecf313 100644 --- a/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/SymmetricSync/RegionSyncModule.cs +++ b/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/SymmetricSync/RegionSyncModule.cs @@ -19,6 +19,8 @@ using System.Net.Sockets; using System.Threading; using System.Text; +using System.IO; +using System.Xml; using Mono.Addins; using OpenMetaverse.StructuredData; @@ -61,6 +63,9 @@ namespace OpenSim.Region.CoreModules.RegionSync.RegionSyncModule m_isSyncRelay = m_sysConfig.GetBoolean("IsSyncRelay", false); m_isSyncListenerLocal = m_sysConfig.GetBoolean("IsSyncListenerLocal", false); + //Setup the PropertyBucketMap + PupolatePropertyBucketMap(m_sysConfig); + m_active = true; LogHeader += "-Actor " + m_actorID; @@ -176,6 +181,17 @@ namespace OpenSim.Region.CoreModules.RegionSync.RegionSyncModule get { return m_isSyncRelay; } } + private Dictionary m_primPropertyBucketMap = new Dictionary(); + public Dictionary PrimPropertyBucketMap + { + get { return m_primPropertyBucketMap; } + } + private List m_propertyBucketDescription = new List(); + public List PropertyBucketDescription + { + get { return m_propertyBucketDescription; } + } + private RegionSyncListener m_localSyncListener = null; private bool m_synced = false; @@ -184,7 +200,64 @@ namespace OpenSim.Region.CoreModules.RegionSync.RegionSyncModule private Dictionary m_primUpdates = new Dictionary(); private object m_updateScenePresenceLock = new object(); private Dictionary m_presenceUpdates = new Dictionary(); - private int m_sendingUpdates; + private int m_sendingUpdates=0; + + private int m_maxNumOfPropertyBuckets; + + //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. + string generalBucketName = "General"; + string physicsBucketName = "Physics"; + m_propertyBucketDescription.Add(generalBucketName); + m_propertyBucketDescription.Add(physicsBucketName); + m_maxNumOfPropertyBuckets = 2; + + foreach (string pName in SceneObjectPart.PropertyList) + { + switch (pName){ + case "GroupPosition": + case "OffsetPosition": + case "Scale": + case "Velocity": + case "AngularVelocity": + case "RotationOffset": + case "Position": + case "Size": + case "Force": + case "RotationalVelocity": + case "PA_Acceleration": + case "Torque": + case "Orientation": + case "IsPhysical": + case "Flying": + case "Buoyancy": + m_primPropertyBucketMap.Add(pName, physicsBucketName); + break; + default: + //all other properties belong to the "General" bucket. + m_primPropertyBucketMap.Add(pName, generalBucketName); + break; + } + } + } + + private bool IsSyncingWithOtherActors() + { + return (m_syncConnectors.Count > 0); + } public void QueueSceneObjectPartForUpdate(SceneObjectPart part) { @@ -209,6 +282,12 @@ namespace OpenSim.Region.CoreModules.RegionSync.RegionSyncModule //SendSceneUpdates put each update into an outgoing queue of each SyncConnector public void SendSceneUpdates() { + if (!IsSyncingWithOtherActors()) + { + //no SyncConnector connected. Do nothing. + return; + } + // Existing value of 1 indicates that updates are currently being sent so skip updates this pass if (Interlocked.Exchange(ref m_sendingUpdates, 1) == 1) { @@ -323,6 +402,11 @@ namespace OpenSim.Region.CoreModules.RegionSync.RegionSyncModule public void SendTerrainUpdates(string lastUpdateActorID) { + if (!IsSyncingWithOtherActors()) + { + //no SyncConnector connected. Do nothing. + return; + } if(m_isSyncRelay || m_actorID.Equals(lastUpdateActorID)) { //m_scene.Heightmap should have been updated already by the caller, send it out @@ -340,7 +424,13 @@ namespace OpenSim.Region.CoreModules.RegionSync.RegionSyncModule //private void RegionSyncModule_OnObjectBeingRemovedFromScene(SceneObjectGroup sog) public void SendDeleteObject(SceneObjectGroup sog, bool softDelete) { - m_log.DebugFormat("SendDeleteObject called for object {0}", sog.UUID); + if (!IsSyncingWithOtherActors()) + { + //no SyncConnector connected. Do nothing. + return; + } + + m_log.DebugFormat(LogHeader+"SendDeleteObject called for object {0}", sog.UUID); //Only send the message out if this is a relay node for sync messages, or this actor caused deleting the object //if (m_isSyncRelay || CheckObjectForSendingUpdate(sog)) @@ -362,6 +452,12 @@ namespace OpenSim.Region.CoreModules.RegionSync.RegionSyncModule { if(children.Count==0) return; + if (!IsSyncingWithOtherActors()) + { + //no SyncConnector connected. Do nothing. + return; + } + OSDMap data = new OSDMap(); string sogxml = SceneObjectSerializer.ToXml2Format(linkedGroup); data["linkedGroup"]=OSD.FromString(sogxml); @@ -383,6 +479,12 @@ namespace OpenSim.Region.CoreModules.RegionSync.RegionSyncModule { if (prims.Count==0 || beforeDelinkGroups.Count==0) return; + if (!IsSyncingWithOtherActors()) + { + //no SyncConnector connected. Do nothing. + return; + } + OSDMap data = new OSDMap(); data["partCount"] = OSD.FromInteger(prims.Count); int partNum = 0; @@ -420,6 +522,12 @@ namespace OpenSim.Region.CoreModules.RegionSync.RegionSyncModule public void PublishSceneEvent(EventManager.EventNames ev, Object[] evArgs) { + if (!IsSyncingWithOtherActors()) + { + //no SyncConnector connected. Do nothing. + return; + } + switch (ev) { case EventManager.EventNames.NewScript: @@ -500,9 +608,13 @@ namespace OpenSim.Region.CoreModules.RegionSync.RegionSyncModule Command cmdSyncStatus = new Command("status", CommandIntentions.COMMAND_HAZARDOUS, SyncStatus, "Displays synchronization status."); + //for debugging purpose + Command cmdSyncDebug = new Command("debug", CommandIntentions.COMMAND_HAZARDOUS, SyncDebug, "Trigger some debugging functions"); + m_commander.RegisterCommand("start", cmdSyncStart); m_commander.RegisterCommand("stop", cmdSyncStop); m_commander.RegisterCommand("status", cmdSyncStatus); + m_commander.RegisterCommand("debug", cmdSyncDebug); lock (m_scene) { @@ -864,6 +976,38 @@ namespace OpenSim.Region.CoreModules.RegionSync.RegionSyncModule m_log.Warn("[REGION SYNC MODULE]: SyncStatus() TO BE IMPLEMENTED !!!"); } + private void SyncDebug(Object[] args) + { + if (m_scene != null) + { + EntityBase[] entities = m_scene.GetEntities(); + foreach (EntityBase entity in entities) + { + if (entity is SceneObjectGroup) + { + //first test serialization + StringWriter sw = new StringWriter(); + XmlTextWriter writer = new XmlTextWriter(sw); + Dictionary bucketSyncInfoList = new Dictionary(); + BucketSyncInfo generalBucket = new BucketSyncInfo(DateTime.Now.Ticks, m_actorID, "General"); + bucketSyncInfoList.Add("General", generalBucket); + BucketSyncInfo physicsBucket = new BucketSyncInfo(DateTime.Now.Ticks, m_actorID, "Physics"); + bucketSyncInfoList.Add("Physics", physicsBucket); + SceneObjectSerializer.WriteBucketSyncInfo(writer, bucketSyncInfoList); + + string xmlString = sw.ToString(); + m_log.Debug("Serialized xml string: " + xmlString); + + //second, test de-serialization + XmlTextReader reader = new XmlTextReader(new StringReader(xmlString)); + SceneObjectPart part = new SceneObjectPart(); + SceneObjectSerializer.ProcessBucketSyncInfo(part, reader); + break; + } + } + } + } + //Start connections to each remote listener. //For now, there is only one remote listener. private bool StartSyncConnections() diff --git a/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/SymmetricSync/SyncConnector.cs b/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/SymmetricSync/SyncConnector.cs index 1620262886..93a5dab3c8 100755 --- a/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/SymmetricSync/SyncConnector.cs +++ b/OpenSim/Region/CoreModules/RegionSync/RegionSyncModule/SymmetricSync/SyncConnector.cs @@ -110,7 +110,7 @@ namespace OpenSim.Region.CoreModules.RegionSync.RegionSyncModule } catch (Exception e) { - m_log.WarnFormat("{0} [Start] Could not connect to RegionSyncServer at {1}:{2}", LogHeader, m_remoteListenerInfo.Addr, m_remoteListenerInfo.Port); + m_log.WarnFormat("{0} [Start] Could not connect to RegionSyncModule at {1}:{2}", LogHeader, m_remoteListenerInfo.Addr, m_remoteListenerInfo.Port); m_log.Warn(e.Message); return false; } diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs index 605e591bf8..8a951f1df8 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.RegionSyncModule != 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 153fee1154..dbde8f202c 100644 --- a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs +++ b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs @@ -107,6 +107,56 @@ namespace OpenSim.Region.Framework.Scenes #endregion Enumerations + //SYMMETRIC SYNC + + //Information for concurrency control of one bucket of prim proproperties. + public class BucketSyncInfo + { + private long m_lastUpdateTimeStamp; + 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 + { + get { return m_lastUpdateTimeStamp; } + } + + public string LastUpdateActorID + { + get { return m_lastUpdateActorID; } + } + + 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) + { + lock (m_updateLock) + { + m_lastUpdateTimeStamp = timeStamp; + m_lastUpdateActorID = actorID; + } + } + + } + //end of SYMMETRIC SYNC + public class SceneObjectPart : IScriptHost, ISceneEntity { /// @@ -127,7 +177,23 @@ namespace OpenSim.Region.Framework.Scenes #region Fields - public bool AllowedDrop; + //SYMMETRIC SYNC + //public bool AllowedDrop; + private bool m_allowedDrop; + public bool AllowedDrop + { + get { return m_allowedDrop; } + set + { + //m_allowedDrop = value; + SetAllowedDrop(value); + UpdateBucketSyncInfo("AllowedDrop"); + } + } + public void SetAllowedDrop(bool value) + { + m_allowedDrop = value; + } public bool DIE_AT_EDGE; @@ -732,6 +798,11 @@ namespace OpenSim.Region.Framework.Scenes } set { + SetGroupPosition(value); + UpdateBucketSyncInfo("GroupPosition"); + + /* + //Legacy Opensim code m_groupPosition = value; PhysicsActor actor = PhysActor; @@ -772,14 +843,63 @@ namespace OpenSim.Region.Framework.Scenes } } } + */ } } + //SYMMETRIC SYNC + public void SetGroupPosition(Vector3 value) + { + m_groupPosition = value; + + PhysicsActor actor = PhysActor; + if (actor != null) + { + try + { + // Root prim actually goes at Position + if (_parentID == 0) + { + actor.Position = value; + } + else + { + // To move the child prim in respect to the group position and rotation we have to calculate + actor.Position = GetWorldPosition(); + actor.Orientation = GetWorldRotation(); + } + + // Tell the physics engines that this prim changed. + m_parentGroup.Scene.PhysicsScene.AddPhysicsActorTaint(actor); + } + catch (Exception e) + { + m_log.Error("[SCENEOBJECTPART]: GROUP POSITION. " + e.Message); + } + } + + // TODO if we decide to do sitting in a more SL compatible way (multiple avatars per prim), this has to be fixed, too + if (m_sitTargetAvatar != UUID.Zero) + { + if (m_parentGroup != null) // TODO can there be a SOP without a SOG? + { + ScenePresence avatar; + if (m_parentGroup.Scene.TryGetScenePresence(m_sitTargetAvatar, out avatar)) + { + avatar.ParentPosition = GetWorldPosition(); + } + } + } + } + public Vector3 OffsetPosition { get { return m_offsetPosition; } set { + SetOffsetPosition(value); + UpdateBucketSyncInfo("OffsetPosition"); + /* StoreUndoState(); m_offsetPosition = value; @@ -795,6 +915,26 @@ namespace OpenSim.Region.Framework.Scenes m_parentGroup.Scene.PhysicsScene.AddPhysicsActorTaint(actor); } } + * */ + } + } + //SYMMETRIC SYNC + public void SetOffsetPosition(Vector3 value) + { + StoreUndoState(); + m_offsetPosition = value; + + if (ParentGroup != null && !ParentGroup.IsDeleted) + { + PhysicsActor actor = PhysActor; + if (_parentID != 0 && actor != null) + { + actor.Position = GetWorldPosition(); + actor.Orientation = GetWorldRotation(); + + // Tell the physics engines that this prim changed. + m_parentGroup.Scene.PhysicsScene.AddPhysicsActorTaint(actor); + } } } @@ -836,6 +976,9 @@ namespace OpenSim.Region.Framework.Scenes set { + SetRotationOffset(value); + UpdateBucketSyncInfo("RotationOffset"); + /* StoreUndoState(); m_rotationOffset = value; @@ -865,9 +1008,43 @@ namespace OpenSim.Region.Framework.Scenes m_log.Error("[SCENEOBJECTPART]: ROTATIONOFFSET" + ex.Message); } } + * */ } } + //SYMMETRIC SYNC + public void SetRotationOffset(Quaternion value) + { + StoreUndoState(); + m_rotationOffset = value; + + PhysicsActor actor = PhysActor; + if (actor != null) + { + try + { + // Root prim gets value directly + if (_parentID == 0) + { + actor.Orientation = value; + //m_log.Info("[PART]: RO1:" + actor.Orientation.ToString()); + } + else + { + // Child prim we have to calculate it's world rotationwel + Quaternion resultingrotation = GetWorldRotation(); + actor.Orientation = resultingrotation; + //m_log.Info("[PART]: RO2:" + actor.Orientation.ToString()); + } + m_parentGroup.Scene.PhysicsScene.AddPhysicsActorTaint(actor); + //} + } + catch (Exception ex) + { + m_log.Error("[SCENEOBJECTPART]: ROTATIONOFFSET" + ex.Message); + } + } + } /// public Vector3 Velocity @@ -888,6 +1065,9 @@ namespace OpenSim.Region.Framework.Scenes set { + SetVelocity(value); + UpdateBucketSyncInfo("Velocity"); + /* m_velocity = value; PhysicsActor actor = PhysActor; @@ -899,6 +1079,22 @@ namespace OpenSim.Region.Framework.Scenes m_parentGroup.Scene.PhysicsScene.AddPhysicsActorTaint(actor); } } + * */ + } + } + //SYMMETRIC SYNC + public void SetVelocity(Vector3 value) + { + m_velocity = value; + + PhysicsActor actor = PhysActor; + if (actor != null) + { + if (actor.IsPhysical) + { + actor.Velocity = value; + m_parentGroup.Scene.PhysicsScene.AddPhysicsActorTaint(actor); + } } } @@ -914,7 +1110,17 @@ namespace OpenSim.Region.Framework.Scenes } return m_angularVelocity; } - set { m_angularVelocity = value; } + set + { + SetAngularVelocity(value); + UpdateBucketSyncInfo("AngularVelocity"); + //m_angularVelocity = value; + } + } + //SYMMETRIC SYNC + public void SetAngularVelocity(Vector3 value) + { + m_angularVelocity = value; } /// @@ -1011,6 +1217,9 @@ namespace OpenSim.Region.Framework.Scenes get { return m_shape.Scale; } set { + SetScale(value); + UpdateBucketSyncInfo("Scale"); + /* StoreUndoState(); if (m_shape != null) { @@ -1030,8 +1239,33 @@ namespace OpenSim.Region.Framework.Scenes } } TriggerScriptChangedEvent(Changed.SCALE); + * */ } } + //SYMMETRIC SYNC + public void SetScale(Vector3 value) + { + StoreUndoState(); + if (m_shape != null) + { + m_shape.Scale = value; + + PhysicsActor actor = PhysActor; + if (actor != null && m_parentGroup != null) + { + if (m_parentGroup.Scene != null) + { + if (m_parentGroup.Scene.PhysicsScene != null) + { + actor.Size = m_shape.Scale; + m_parentGroup.Scene.PhysicsScene.AddPhysicsActorTaint(actor); + } + } + } + } + TriggerScriptChangedEvent(Changed.SCALE); + } + public byte UpdateFlag { @@ -4971,10 +5205,88 @@ namespace OpenSim.Region.Framework.Scenes } } + //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 + //properties Physics Engine needs to synchronize to other actors. + public static List PropertyList = new List() + { + //Following properties copied from SceneObjectSerializer() + "AllowedDrop", + "CreatorID", + "CreatorData", + "FolderID", + "InventorySerial", + "TaskInventory", + "UUID", + "LocalId", + "Name", + "Material", + "PassTouches", + "RegionHandle", + "ScriptAccessPin", + "GroupPosition", + "OffsetPosition", + "RotationOffset", + "Velocity", + "AngularVelocity", + //"Acceleration", + "SOP_Acceleration", //SOP and PA read/write their own local copies of acceleration, so we distinguish the copies + "Description", + "Color", + "Text", + "SitName", + "TouchName", + "LinkNum", + "ClickAction", + "Shape", + "Scale", + "UpdateFlag", + "SitTargetOrientation", + "SitTargetPosition", + "SitTargetPositionLL", + "SitTargetOrientationLL", + "ParentID", + "CreationDate", + "Category", + "SalePrice", + "ObjectSaleType", + "OwnershipCost", + "GroupID", + "OwnerID", + "LastOwnerID", + "BaseMask", + "OwnerMask", + "GroupMask", + "EveryoneMask", + "NextOwnerMask", + "Flags", + "CollisionSound", + "CollisionSoundVolume", + "MediaUrl", + "TextureAnimation", + "ParticleSystem", + //Property names below copied from PhysicsActor, they are necessary in synchronization, but not covered the above properties + //Physics properties "Velocity" is covered above + "Position", + "Size", + "Force", + "RotationalVelocity", + "PA_Acceleration", + "Torque", + "Orientation", + "IsPhysical", + "Flying", + "Buoyancy", + }; + + + + 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) { //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -5097,6 +5409,8 @@ namespace OpenSim.Region.Framework.Scenes return partUpdateResult; } + + private bool UpdateCollisionSound(UUID updatedCollisionSound) { @@ -5150,6 +5464,197 @@ namespace OpenSim.Region.Framework.Scenes } + //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 = new Dictionary(); + public Dictionary BucketSyncInfoList + { + get { return m_bucketSyncInfoList; } + set { m_bucketSyncInfoList = value; } + } + //TODO: serialization and deserialization processors to be added in SceneObjectSerializer + + //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; + //m_bucketCount = bucketNames.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) + { + 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]) + { + + } + } + + //Initialize and set the values of timestamp and actorID for each synchronization bucket. + //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; + + m_log.Debug("InitializeBucketSyncInfo called at " + timeStamp); + + for (int i = 0; i < m_propertyBucketNames.Count; i++) + { + string bucketName = m_propertyBucketNames[i]; + 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 (m_bucketSyncInfoList.ContainsKey(bucketName)) + { + m_bucketSyncInfoList[bucketName] = syncInfo; + } + else + { + m_bucketSyncInfoList.Add(bucketName, syncInfo); + } + if (!m_bucketSyncInfoList.ContainsKey(bucketName)) + { + m_bucketUpdateLocks.Add(bucketName, new Object()); + } + } + } + + /// + /// Update the timestamp and actorID information of the bucket the given property belongs to. + /// + /// Name of the property. Make sure the spelling is consistent with what are defined in PropertyList + private void UpdateBucketSyncInfo(string propertyName) + { + if (m_bucketSyncInfoList != null && m_bucketSyncInfoList.Count>0) + { + //int bucketIndex = m_primPropertyBucketMap[propertyName]; + string bucketName = m_primPropertyBucketMap[propertyName]; + long timeStamp = DateTime.Now.Ticks; + if (m_bucketSyncInfoList.ContainsKey(bucketName)) + { + m_bucketSyncInfoList[bucketName].UpdateSyncInfo(timeStamp, m_localActorID); + } + else + { + m_log.Warn("No SyncInfo of bucket (name: " + bucketName + ") found"); + } + } + } + + + + /* + + 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..b2f891078c 100644 --- a/OpenSim/Region/Framework/Scenes/Serialization/SceneObjectSerializer.cs +++ b/OpenSim/Region/Framework/Scenes/Serialization/SceneObjectSerializer.cs @@ -332,6 +332,7 @@ namespace OpenSim.Region.Framework.Scenes.Serialization //SYMMETRIC SYNC m_SOPXmlProcessors.Add("LastUpdateTimeStamp", ProcessUpdateTimeStamp); m_SOPXmlProcessors.Add("LastUpdateActorID", ProcessLastUpdateActorID); + m_SOPXmlProcessors.Add("BucketSyncInfoList", ProcessBucketSyncInfo); //end of SYMMETRIC SYNC #endregion @@ -417,9 +418,12 @@ namespace OpenSim.Region.Framework.Scenes.Serialization } #region SOPXmlProcessors + //SYMMETRIC SYNC NOTE: -- assignments in de-serialization should directly set the values w/o triggering SceneObjectPart.UpdateBucketSyncInfo; + //That is, calling SetXXX(value) instead of using "XXX = value". private static void ProcessAllowedDrop(SceneObjectPart obj, XmlTextReader reader) { - obj.AllowedDrop = Util.ReadBoolean(reader); + //obj.AllowedDrop = Util.ReadBoolean(reader); + obj.SetAllowedDrop(Util.ReadBoolean(reader)); } private static void ProcessCreatorID(SceneObjectPart obj, XmlTextReader reader) @@ -484,27 +488,32 @@ namespace OpenSim.Region.Framework.Scenes.Serialization private static void ProcessGroupPosition(SceneObjectPart obj, XmlTextReader reader) { - obj.GroupPosition = Util.ReadVector(reader, "GroupPosition"); + //obj.GroupPosition = Util.ReadVector(reader, "GroupPosition"); + obj.SetGroupPosition(Util.ReadVector(reader, "GroupPosition")); } private static void ProcessOffsetPosition(SceneObjectPart obj, XmlTextReader reader) { - obj.OffsetPosition = Util.ReadVector(reader, "OffsetPosition"); ; + //obj.OffsetPosition = Util.ReadVector(reader, "OffsetPosition"); ; + obj.SetOffsetPosition(Util.ReadVector(reader, "OffsetPosition")); } private static void ProcessRotationOffset(SceneObjectPart obj, XmlTextReader reader) { - obj.RotationOffset = Util.ReadQuaternion(reader, "RotationOffset"); + //obj.RotationOffset = Util.ReadQuaternion(reader, "RotationOffset"); + obj.SetRotationOffset(Util.ReadQuaternion(reader, "RotationOffset")); } private static void ProcessVelocity(SceneObjectPart obj, XmlTextReader reader) { - obj.Velocity = Util.ReadVector(reader, "Velocity"); + //obj.Velocity = Util.ReadVector(reader, "Velocity"); + obj.SetVelocity(Util.ReadVector(reader, "Velocity")); } private static void ProcessAngularVelocity(SceneObjectPart obj, XmlTextReader reader) { - obj.AngularVelocity = Util.ReadVector(reader, "AngularVelocity"); + //obj.AngularVelocity = Util.ReadVector(reader, "AngularVelocity"); + obj.SetVelocity(Util.ReadVector(reader, "AngularVelocity")); } private static void ProcessAcceleration(SceneObjectPart obj, XmlTextReader reader) @@ -563,7 +572,8 @@ namespace OpenSim.Region.Framework.Scenes.Serialization private static void ProcessScale(SceneObjectPart obj, XmlTextReader reader) { - obj.Scale = Util.ReadVector(reader, "Scale"); + //obj.Scale = Util.ReadVector(reader, "Scale"); + obj.SetScale(Util.ReadVector(reader, "Scale")); } private static void ProcessUpdateFlag(SceneObjectPart obj, XmlTextReader reader) @@ -702,6 +712,55 @@ namespace OpenSim.Region.Framework.Scenes.Serialization { obj.LastUpdateActorID = reader.ReadElementContentAsString("LastUpdateActorID", string.Empty); } + + public 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 @@ -1188,11 +1247,36 @@ namespace OpenSim.Region.Framework.Scenes.Serialization //SYMMETRIC SYNC writer.WriteElementString("LastUpdateTimeStamp", sop.LastUpdateTimeStamp.ToString()); writer.WriteElementString("LastUpdateActorID", sop.LastUpdateActorID); + WriteBucketSyncInfo(writer, sop.BucketSyncInfoList); //end of SYMMETRIC SYNC writer.WriteEndElement(); } + //SYMMETRIC SYNC + public static void WriteBucketSyncInfo(XmlTextWriter writer, Dictionary bucketSyncInfoList) + { + if (bucketSyncInfoList!=null || 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);