Merge branch 'danmerge_symsync' into dev
						commit
						0b164072b3
					
				|  | @ -411,6 +411,12 @@ namespace OpenSim | ||||||
| 
 | 
 | ||||||
|             scene.StartTimer(); |             scene.StartTimer(); | ||||||
| 
 | 
 | ||||||
|  |             //SYMMETRIC SYNC | ||||||
|  |             //For INonSharedRegionModule, there is no more PostInitialise(). So we trigger OnPostSceneCreation event here | ||||||
|  |             //to let the Sync modules to do their work once all modules are loaded and scene has interfaces to all of them. | ||||||
|  |             scene.EventManager.TriggerOnPostSceneCreation(scene); | ||||||
|  |             //end of SYMMETRIC SYNC | ||||||
|  | 
 | ||||||
|             return clientServer; |             return clientServer; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -75,7 +75,8 @@ namespace OpenSim.Region.CoreModules.RegionSync.RegionSyncModule | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             // If syncConfig does not indicate "server", do not start up server mode |             // If syncConfig does not indicate "server", do not start up server mode | ||||||
|             string mode = syncConfig.GetString("Mode", "server").ToLower(); |             //string mode = syncConfig.GetString("Mode", "server").ToLower(); | ||||||
|  |             string mode = syncConfig.GetString("Mode", "").ToLower(); | ||||||
|             if(mode != "server") |             if(mode != "server") | ||||||
|             { |             { | ||||||
|                 scene.RegionSyncEnabled = false; |                 scene.RegionSyncEnabled = false; | ||||||
|  |  | ||||||
|  | @ -0,0 +1,169 @@ | ||||||
|  | /* | ||||||
|  |  * Copyright (c) Contributors: TO BE FILLED | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | using System; | ||||||
|  | using System.Collections.Generic; | ||||||
|  | using System.Reflection; | ||||||
|  | using Nini.Config; | ||||||
|  | using OpenMetaverse; | ||||||
|  | using OpenSim.Framework; | ||||||
|  | using OpenSim.Framework.Client; | ||||||
|  | using OpenSim.Region.CoreModules.Framework.InterfaceCommander; | ||||||
|  | using OpenSim.Region.Framework.Interfaces; | ||||||
|  | using OpenSim.Region.Framework.Scenes; | ||||||
|  | using log4net; | ||||||
|  | using System.Net; | ||||||
|  | using System.Net.Sockets; | ||||||
|  | using System.Threading; | ||||||
|  | using Mono.Addins; | ||||||
|  | 
 | ||||||
|  | namespace OpenSim.Region.CoreModules.RegionSync.RegionSyncModule | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  |     [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "AttachmentsModule")] | ||||||
|  |     public class ClientManagerSyncModule : INonSharedRegionModule, IDSGActorSyncModule | ||||||
|  |     { | ||||||
|  |         #region INonSharedRegionModule | ||||||
|  | 
 | ||||||
|  |         public void Initialise(IConfigSource config) | ||||||
|  |         { | ||||||
|  |             m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||||||
|  | 
 | ||||||
|  |             IConfig syncConfig = config.Configs["RegionSyncModule"]; | ||||||
|  |             m_active = false; | ||||||
|  |             if (syncConfig == null) | ||||||
|  |             { | ||||||
|  |                 m_log.Warn(LogHeader + " No RegionSyncModule config section found. Shutting down."); | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  |             else if (!syncConfig.GetBoolean("Enabled", false)) | ||||||
|  |             { | ||||||
|  |                 m_log.Warn(LogHeader + " RegionSyncModule is not enabled. Shutting down."); | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             string actorType = syncConfig.GetString("DSGActorType", "").ToLower(); | ||||||
|  | 
 | ||||||
|  |             //Read in configuration, if the local actor is configured to be a client manager, load this module. | ||||||
|  |             if (!actorType.Equals("client_manager")) | ||||||
|  |             { | ||||||
|  |                 m_log.Warn(LogHeader + ": not configured as Scene Persistence Actor. Shut down."); | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             m_actorID = syncConfig.GetString("ActorID", ""); | ||||||
|  |             if (m_actorID.Equals("")) | ||||||
|  |             { | ||||||
|  |                 m_log.Warn(LogHeader + ": ActorID not specified in config file. Shutting down."); | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             m_active = true; | ||||||
|  | 
 | ||||||
|  |             m_log.Warn(LogHeader + " Initialised"); | ||||||
|  | 
 | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         //Called after Initialise() | ||||||
|  |         public void AddRegion(Scene scene) | ||||||
|  |         { | ||||||
|  |             if (!m_active) | ||||||
|  |                 return; | ||||||
|  | 
 | ||||||
|  |             //connect with scene | ||||||
|  |             m_scene = scene; | ||||||
|  | 
 | ||||||
|  |             //register the module with SceneGraph. If needed, SceneGraph checks the module's ActorType to know what type of module it is. | ||||||
|  |             m_scene.RegisterModuleInterface<IDSGActorSyncModule>(this); | ||||||
|  | 
 | ||||||
|  |             // Setup the command line interface | ||||||
|  |             //m_scene.EventManager.OnPluginConsole += EventManager_OnPluginConsole; | ||||||
|  |             //InstallInterfaces(); | ||||||
|  | 
 | ||||||
|  |             //Register for the OnPostSceneCreation event | ||||||
|  |             //m_scene.EventManager.OnPostSceneCreation += OnPostSceneCreation; | ||||||
|  | 
 | ||||||
|  |             //Register for Scene/SceneGraph events | ||||||
|  |             m_scene.SceneGraph.OnObjectCreate += new ObjectCreateDelegate(ClientManager_OnObjectCreate); | ||||||
|  |             m_scene.EventManager.OnSymmetricSyncStop += ClientManager_OnSymmetricSyncStop; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         //Called after AddRegion() has been called for all region modules of the scene. | ||||||
|  |         //NOTE::However, at this point, Scene may not have requested all the needed region module interfaces yet. | ||||||
|  |         public void RegionLoaded(Scene scene) | ||||||
|  |         { | ||||||
|  |             if (!m_active) | ||||||
|  |                 return; | ||||||
|  | 
 | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void RemoveRegion(Scene scene) | ||||||
|  |         { | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public Type ReplaceableInterface | ||||||
|  |         { | ||||||
|  |             get { return null; } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void Close() | ||||||
|  |         { | ||||||
|  |             m_scene = null; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public string Name | ||||||
|  |         { | ||||||
|  |             get { return "ClientManagerSyncModule"; } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         #endregion //INonSharedRegionModule | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         #region IDSGActorSyncModule members and functions | ||||||
|  | 
 | ||||||
|  |         private DSGActorTypes m_actorType = DSGActorTypes.ClientManager; | ||||||
|  |         public DSGActorTypes ActorType | ||||||
|  |         { | ||||||
|  |             get { return m_actorType; } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private string m_actorID; | ||||||
|  |         public string ActorID | ||||||
|  |         { | ||||||
|  |             get { return m_actorID; } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         #endregion //IDSGActorSyncModule | ||||||
|  | 
 | ||||||
|  |         #region ClientManagerSyncModule memebers and functions | ||||||
|  |         private ILog m_log; | ||||||
|  |         private bool m_active = false; | ||||||
|  |         public bool Active | ||||||
|  |         { | ||||||
|  |             get { return m_active; } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private Scene m_scene; | ||||||
|  | 
 | ||||||
|  |         private string LogHeader = "[ClientManagerSyncModule]"; | ||||||
|  | 
 | ||||||
|  |         /// <summary> | ||||||
|  |         /// Script Engine's action upon an object is added to the local scene | ||||||
|  |         /// </summary> | ||||||
|  |         private void ClientManager_OnObjectCreate(EntityBase entity) | ||||||
|  |         { | ||||||
|  |             if (entity is SceneObjectGroup) | ||||||
|  |             { | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void ClientManager_OnSymmetricSyncStop() | ||||||
|  |         { | ||||||
|  |             //remove all objects | ||||||
|  |             m_scene.DeleteAllSceneObjects(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         #endregion //ScriptEngineSyncModule | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,169 @@ | ||||||
|  | /* | ||||||
|  |  * Copyright (c) Contributors: TO BE FILLED | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | using System; | ||||||
|  | using System.Collections.Generic; | ||||||
|  | using System.Reflection; | ||||||
|  | using Nini.Config; | ||||||
|  | using OpenMetaverse; | ||||||
|  | using OpenSim.Framework; | ||||||
|  | using OpenSim.Framework.Client; | ||||||
|  | using OpenSim.Region.CoreModules.Framework.InterfaceCommander; | ||||||
|  | using OpenSim.Region.Framework.Interfaces; | ||||||
|  | using OpenSim.Region.Framework.Scenes; | ||||||
|  | using log4net; | ||||||
|  | using System.Net; | ||||||
|  | using System.Net.Sockets; | ||||||
|  | using System.Threading; | ||||||
|  | using Mono.Addins; | ||||||
|  | 
 | ||||||
|  | namespace OpenSim.Region.CoreModules.RegionSync.RegionSyncModule | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  |     [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "AttachmentsModule")] | ||||||
|  |     public class PhysicsEngineSyncModule : INonSharedRegionModule, IDSGActorSyncModule | ||||||
|  |     { | ||||||
|  |         #region INonSharedRegionModule | ||||||
|  | 
 | ||||||
|  |         public void Initialise(IConfigSource config) | ||||||
|  |         { | ||||||
|  |             m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||||||
|  | 
 | ||||||
|  |             IConfig syncConfig = config.Configs["RegionSyncModule"]; | ||||||
|  |             m_active = false; | ||||||
|  |             if (syncConfig == null) | ||||||
|  |             { | ||||||
|  |                 m_log.Warn(LogHeader + " No RegionSyncModule config section found. Shutting down."); | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  |             else if (!syncConfig.GetBoolean("Enabled", false)) | ||||||
|  |             { | ||||||
|  |                 m_log.Warn(LogHeader + " RegionSyncModule is not enabled. Shutting down."); | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             string actorType = syncConfig.GetString("DSGActorType", "").ToLower(); | ||||||
|  | 
 | ||||||
|  |             //Read in configuration, if the local actor is configured to be a client manager, load this module. | ||||||
|  |             if (!actorType.Equals("physics_engine")) | ||||||
|  |             { | ||||||
|  |                 m_log.Warn(LogHeader + ": not configured as Scene Persistence Actor. Shut down."); | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             m_actorID = syncConfig.GetString("ActorID", ""); | ||||||
|  |             if (m_actorID.Equals("")) | ||||||
|  |             { | ||||||
|  |                 m_log.Warn(LogHeader + ": ActorID not specified in config file. Shutting down."); | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             m_active = true; | ||||||
|  | 
 | ||||||
|  |             m_log.Warn(LogHeader + " Initialised"); | ||||||
|  | 
 | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         //Called after Initialise() | ||||||
|  |         public void AddRegion(Scene scene) | ||||||
|  |         { | ||||||
|  |             if (!m_active) | ||||||
|  |                 return; | ||||||
|  | 
 | ||||||
|  |             //connect with scene | ||||||
|  |             m_scene = scene; | ||||||
|  | 
 | ||||||
|  |             //register the module with SceneGraph. If needed, SceneGraph checks the module's ActorType to know what type of module it is. | ||||||
|  |             m_scene.RegisterModuleInterface<IDSGActorSyncModule>(this); | ||||||
|  | 
 | ||||||
|  |             // Setup the command line interface | ||||||
|  |             //m_scene.EventManager.OnPluginConsole += EventManager_OnPluginConsole; | ||||||
|  |             //InstallInterfaces(); | ||||||
|  | 
 | ||||||
|  |             //Register for the OnPostSceneCreation event | ||||||
|  |             //m_scene.EventManager.OnPostSceneCreation += OnPostSceneCreation; | ||||||
|  | 
 | ||||||
|  |             //Register for Scene/SceneGraph events | ||||||
|  |             m_scene.SceneGraph.OnObjectCreate += new ObjectCreateDelegate(PhysicsEngine_OnObjectCreate); | ||||||
|  |             m_scene.EventManager.OnSymmetricSyncStop += PhysicsEngine_OnSymmetricSyncStop; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         //Called after AddRegion() has been called for all region modules of the scene. | ||||||
|  |         //NOTE::However, at this point, Scene may not have requested all the needed region module interfaces yet. | ||||||
|  |         public void RegionLoaded(Scene scene) | ||||||
|  |         { | ||||||
|  |             if (!m_active) | ||||||
|  |                 return; | ||||||
|  | 
 | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void RemoveRegion(Scene scene) | ||||||
|  |         { | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public Type ReplaceableInterface | ||||||
|  |         { | ||||||
|  |             get { return null; } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void Close() | ||||||
|  |         { | ||||||
|  |             m_scene = null; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public string Name | ||||||
|  |         { | ||||||
|  |             get { return "PhysicsEngineSyncModule"; } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         #endregion //INonSharedRegionModule | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         #region IDSGActorSyncModule members and functions | ||||||
|  | 
 | ||||||
|  |         private DSGActorTypes m_actorType = DSGActorTypes.PhysicsEngine; | ||||||
|  |         public DSGActorTypes ActorType | ||||||
|  |         { | ||||||
|  |             get { return m_actorType; } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private string m_actorID; | ||||||
|  |         public string ActorID | ||||||
|  |         { | ||||||
|  |             get { return m_actorID; } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         #endregion //IDSGActorSyncModule | ||||||
|  | 
 | ||||||
|  |         #region PhysicsEngineSyncModule memebers and functions | ||||||
|  |         private ILog m_log; | ||||||
|  |         private bool m_active = false; | ||||||
|  |         public bool Active | ||||||
|  |         { | ||||||
|  |             get { return m_active; } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private Scene m_scene; | ||||||
|  | 
 | ||||||
|  |         private string LogHeader = "[PhysicsEngineSyncModule]"; | ||||||
|  | 
 | ||||||
|  |         /// <summary> | ||||||
|  |         /// Script Engine's action upon an object is added to the local scene | ||||||
|  |         /// </summary> | ||||||
|  |         private void PhysicsEngine_OnObjectCreate(EntityBase entity) | ||||||
|  |         { | ||||||
|  |             if (entity is SceneObjectGroup) | ||||||
|  |             { | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void PhysicsEngine_OnSymmetricSyncStop() | ||||||
|  |         { | ||||||
|  |             //remove all objects | ||||||
|  |             m_scene.DeleteAllSceneObjects(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         #endregion //ScriptEngineSyncModule | ||||||
|  |     } | ||||||
|  | } | ||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							|  | @ -0,0 +1,174 @@ | ||||||
|  | /* | ||||||
|  |  * Copyright (c) Contributors: TO BE FILLED | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | using System; | ||||||
|  | using System.Collections.Generic; | ||||||
|  | using System.Reflection; | ||||||
|  | using Nini.Config; | ||||||
|  | using OpenMetaverse; | ||||||
|  | using OpenSim.Framework; | ||||||
|  | using OpenSim.Framework.Client; | ||||||
|  | using OpenSim.Region.CoreModules.Framework.InterfaceCommander; | ||||||
|  | using OpenSim.Region.Framework.Interfaces; | ||||||
|  | using OpenSim.Region.Framework.Scenes; | ||||||
|  | using log4net; | ||||||
|  | using System.Net; | ||||||
|  | using System.Net.Sockets; | ||||||
|  | using System.Threading; | ||||||
|  | using Mono.Addins; | ||||||
|  | 
 | ||||||
|  | namespace OpenSim.Region.CoreModules.RegionSync.RegionSyncModule | ||||||
|  | { | ||||||
|  |     [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "AttachmentsModule")] | ||||||
|  |     public class ScenePersistenceSyncModule : INonSharedRegionModule, IDSGActorSyncModule     | ||||||
|  |     { | ||||||
|  |         #region INonSharedRegionModule | ||||||
|  | 
 | ||||||
|  |         public void Initialise(IConfigSource config) | ||||||
|  |         { | ||||||
|  |             m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||||||
|  | 
 | ||||||
|  |             IConfig syncConfig = config.Configs["RegionSyncModule"]; | ||||||
|  |             m_active = false; | ||||||
|  |             if (syncConfig == null) | ||||||
|  |             { | ||||||
|  |                 m_log.Warn(LogHeader + " No RegionSyncModule config section found. Shutting down."); | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  |             else if (!syncConfig.GetBoolean("Enabled", false)) | ||||||
|  |             { | ||||||
|  |                 m_log.Warn(LogHeader + " RegionSyncModule is not enabled. Shutting down."); | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             string actorType = syncConfig.GetString("DSGActorType", "").ToLower(); | ||||||
|  |             if (!actorType.Equals("scene_persistence")) | ||||||
|  |             { | ||||||
|  |                 m_log.Warn(LogHeader + ": not configured as Scene Persistence Actor. Shutting down."); | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             m_actorID = syncConfig.GetString("ActorID", ""); | ||||||
|  |             if (m_actorID.Equals("")) | ||||||
|  |             { | ||||||
|  |                 m_log.Warn(LogHeader + ": ActorID not specified in config file. Shutting down."); | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             m_active = true; | ||||||
|  | 
 | ||||||
|  |             m_log.Warn(LogHeader+" Initialised"); | ||||||
|  | 
 | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         //Called after Initialise() | ||||||
|  |         public void AddRegion(Scene scene) | ||||||
|  |         { | ||||||
|  |             if (!m_active) | ||||||
|  |                 return; | ||||||
|  |             m_log.Warn(LogHeader + " AddRegion() called"); | ||||||
|  |             //connect with scene | ||||||
|  |             m_scene = scene; | ||||||
|  | 
 | ||||||
|  |             //register the module with SceneGraph. If needed, SceneGraph checks the module's ActorType to know what type of module it is. | ||||||
|  |             m_scene.RegisterModuleInterface<IDSGActorSyncModule>(this); | ||||||
|  | 
 | ||||||
|  |             // Setup the command line interface | ||||||
|  |             //m_scene.EventManager.OnPluginConsole += EventManager_OnPluginConsole; | ||||||
|  |             //InstallInterfaces(); | ||||||
|  | 
 | ||||||
|  |             //Register for the OnPostSceneCreation event | ||||||
|  |             //m_scene.EventManager.OnPostSceneCreation += OnPostSceneCreation; | ||||||
|  | 
 | ||||||
|  |             //Register for Scene/SceneGraph events | ||||||
|  |             m_scene.SceneGraph.OnObjectCreate += new ObjectCreateDelegate(ScenePersistence_OnObjectCreate); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         //Called after AddRegion() has been called for all region modules of the scene. | ||||||
|  |         //NOTE::However, at this point, Scene may not have requested all the needed region module interfaces yet.  | ||||||
|  |         //      So to try to access other region modules in RegionLoaded, e.g. RegionSyncModule, is not a good idea. | ||||||
|  |         public void RegionLoaded(Scene scene) | ||||||
|  |         { | ||||||
|  |             if (!m_active) | ||||||
|  |                 return; | ||||||
|  |             m_log.Warn(LogHeader + " RegionLoaded() called"); | ||||||
|  | 
 | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void RemoveRegion(Scene scene) | ||||||
|  |         { | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public Type ReplaceableInterface | ||||||
|  |         { | ||||||
|  |             get { return null; } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void Close() | ||||||
|  |         { | ||||||
|  |             m_scene = null; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public string Name | ||||||
|  |         { | ||||||
|  |             get { return "ScenePersistenceSyncModule"; } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         #endregion //INonSharedRegionModule | ||||||
|  | 
 | ||||||
|  |         #region IDSGActorSyncModule members and functions | ||||||
|  | 
 | ||||||
|  |         private DSGActorTypes m_actorType = DSGActorTypes.ScenePersistence; | ||||||
|  |         public DSGActorTypes ActorType | ||||||
|  |         { | ||||||
|  |             get { return m_actorType; } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private string m_actorID; | ||||||
|  |         public string ActorID | ||||||
|  |         { | ||||||
|  |             get { return m_actorID; } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         #endregion //IDSGActorSyncModule | ||||||
|  | 
 | ||||||
|  |         #region ScenePersistenceSyncModule memebers and functions | ||||||
|  |         private ILog m_log; | ||||||
|  |         private bool m_active = false; | ||||||
|  |         public bool Active | ||||||
|  |         { | ||||||
|  |             get { return m_active; } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private Scene m_scene; | ||||||
|  | 
 | ||||||
|  |         private string LogHeader = "[ScenePersistenceSyncModule]"; | ||||||
|  | 
 | ||||||
|  |         public void OnPostSceneCreation(Scene createdScene) | ||||||
|  |         { | ||||||
|  |             //If this is the local scene the actor is working on, do something | ||||||
|  |             if (createdScene == m_scene) | ||||||
|  |             { | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         /// <summary> | ||||||
|  |         /// ScenePersistence's actions upon an object is added to the local scene. | ||||||
|  |         /// </summary> | ||||||
|  |         private void ScenePersistence_OnObjectCreate(EntityBase entity) | ||||||
|  |         { | ||||||
|  |             if (entity is SceneObjectGroup) | ||||||
|  |             { | ||||||
|  |                 m_log.Warn(LogHeader + ": link to backup for " + entity.UUID); | ||||||
|  |                 SceneObjectGroup sog = (SceneObjectGroup)entity; | ||||||
|  |                 sog.AttachToBackup(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         #endregion //ScenePersistenceSyncModule | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -0,0 +1,184 @@ | ||||||
|  | /* | ||||||
|  |  * Copyright (c) Contributors: TO BE FILLED | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | using System; | ||||||
|  | using System.Collections.Generic; | ||||||
|  | using System.Reflection; | ||||||
|  | using Nini.Config; | ||||||
|  | using OpenMetaverse; | ||||||
|  | using OpenSim.Framework; | ||||||
|  | using OpenSim.Framework.Client; | ||||||
|  | using OpenSim.Region.CoreModules.Framework.InterfaceCommander; | ||||||
|  | using OpenSim.Region.Framework.Interfaces; | ||||||
|  | using OpenSim.Region.Framework.Scenes; | ||||||
|  | using log4net; | ||||||
|  | using System.Net; | ||||||
|  | using System.Net.Sockets; | ||||||
|  | using System.Threading; | ||||||
|  | using Mono.Addins; | ||||||
|  | 
 | ||||||
|  | namespace OpenSim.Region.CoreModules.RegionSync.RegionSyncModule | ||||||
|  | { | ||||||
|  |      | ||||||
|  |     [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "AttachmentsModule")] | ||||||
|  |     public class ScriptEngineSyncModule : INonSharedRegionModule, IDSGActorSyncModule | ||||||
|  |     { | ||||||
|  |         #region INonSharedRegionModule | ||||||
|  | 
 | ||||||
|  |         public void Initialise(IConfigSource config) | ||||||
|  |         { | ||||||
|  |             m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | ||||||
|  | 
 | ||||||
|  |             IConfig syncConfig = config.Configs["RegionSyncModule"]; | ||||||
|  |             m_active = false; | ||||||
|  |             if (syncConfig == null) | ||||||
|  |             { | ||||||
|  |                 m_log.Warn(LogHeader + " No RegionSyncModule config section found. Shutting down."); | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  |             else if (!syncConfig.GetBoolean("Enabled", false)) | ||||||
|  |             { | ||||||
|  |                 m_log.Warn(LogHeader + " RegionSyncModule is not enabled. Shutting down."); | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             string actorType = syncConfig.GetString("DSGActorType", "").ToLower(); | ||||||
|  |             if (!actorType.Equals("script_engine")) | ||||||
|  |             { | ||||||
|  |                 m_log.Warn(LogHeader + ": not configured as Scene Persistence Actor. Shut down."); | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             m_actorID = syncConfig.GetString("ActorID", ""); | ||||||
|  |             if (m_actorID.Equals("")) | ||||||
|  |             { | ||||||
|  |                 m_log.Warn(LogHeader + ": ActorID not specified in config file. Shutting down."); | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             m_active = true; | ||||||
|  | 
 | ||||||
|  |             m_log.Warn(LogHeader + " Initialised"); | ||||||
|  | 
 | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         //Called after Initialise() | ||||||
|  |         public void AddRegion(Scene scene) | ||||||
|  |         { | ||||||
|  |             if (!m_active) | ||||||
|  |                 return; | ||||||
|  |             m_log.Warn(LogHeader + " AddRegion() called"); | ||||||
|  |             //connect with scene | ||||||
|  |             m_scene = scene; | ||||||
|  | 
 | ||||||
|  |             //register the module with SceneGraph. If needed, SceneGraph checks the module's ActorType to know what type of module it is. | ||||||
|  |             m_scene.RegisterModuleInterface<IDSGActorSyncModule>(this); | ||||||
|  | 
 | ||||||
|  |             // Setup the command line interface | ||||||
|  |             //m_scene.EventManager.OnPluginConsole += EventManager_OnPluginConsole; | ||||||
|  |             //InstallInterfaces(); | ||||||
|  | 
 | ||||||
|  |             //Register for the OnPostSceneCreation event | ||||||
|  |             //m_scene.EventManager.OnPostSceneCreation += OnPostSceneCreation; | ||||||
|  | 
 | ||||||
|  |             //Register for Scene/SceneGraph events | ||||||
|  |             m_scene.SceneGraph.OnObjectCreate += new ObjectCreateDelegate(ScriptEngine_OnObjectCreate); | ||||||
|  |             m_scene.EventManager.OnSymmetricSyncStop += ScriptEngine_OnSymmetricSyncStop; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         //Called after AddRegion() has been called for all region modules of the scene. | ||||||
|  |         //NOTE::However, at this point, Scene may not have requested all the needed region module interfaces yet. | ||||||
|  |         public void RegionLoaded(Scene scene) | ||||||
|  |         { | ||||||
|  |             if (!m_active) | ||||||
|  |                 return; | ||||||
|  | 
 | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void RemoveRegion(Scene scene) | ||||||
|  |         { | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public Type ReplaceableInterface | ||||||
|  |         { | ||||||
|  |             get { return null; } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void Close() | ||||||
|  |         { | ||||||
|  |             m_scene = null; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public string Name | ||||||
|  |         { | ||||||
|  |             get { return "ScriptEngineSyncModule"; } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         #endregion //INonSharedRegionModule | ||||||
|  | 
 | ||||||
|  |         #region IDSGActorSyncModule members and functions | ||||||
|  | 
 | ||||||
|  |         private DSGActorTypes m_actorType = DSGActorTypes.ScriptEngine; | ||||||
|  |         public DSGActorTypes ActorType | ||||||
|  |         { | ||||||
|  |             get { return m_actorType; } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private string m_actorID; | ||||||
|  |         public string ActorID | ||||||
|  |         { | ||||||
|  |             get { return m_actorID; } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         #endregion //IDSGActorSyncModule | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         #region ScriptEngineSyncModule memebers and functions | ||||||
|  |         private ILog m_log; | ||||||
|  |         private bool m_active = false; | ||||||
|  |         public bool Active | ||||||
|  |         { | ||||||
|  |             get { return m_active; } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private Scene m_scene; | ||||||
|  | 
 | ||||||
|  |         private string LogHeader = "[ScriptEngineSyncModule]"; | ||||||
|  | 
 | ||||||
|  |         public void OnPostSceneCreation(Scene createdScene) | ||||||
|  |         { | ||||||
|  |             //If this is the local scene the actor is working on, do something | ||||||
|  |             if (createdScene == m_scene) | ||||||
|  |             { | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         /// <summary> | ||||||
|  |         /// Script Engine's action upon an object is added to the local scene | ||||||
|  |         /// </summary> | ||||||
|  |         private void ScriptEngine_OnObjectCreate(EntityBase entity) | ||||||
|  |         { | ||||||
|  |             if (entity is SceneObjectGroup) | ||||||
|  |             { | ||||||
|  |                 m_log.Warn(LogHeader + ": start script for obj " + entity.UUID); | ||||||
|  |                 SceneObjectGroup sog = (SceneObjectGroup)entity;  | ||||||
|  |                 sog.CreateScriptInstances(0, false, m_scene.DefaultScriptEngine, 0); | ||||||
|  |                 sog.ResumeScripts(); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void ScriptEngine_OnSymmetricSyncStop() | ||||||
|  |         { | ||||||
|  |             //Inform script engine to save script states and stop scripts | ||||||
|  |             m_scene.EventManager.TriggerScriptEngineSyncStop(); | ||||||
|  |             //remove all objects | ||||||
|  |             m_scene.DeleteAllSceneObjects(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         #endregion //ScriptEngineSyncModule | ||||||
|  | 
 | ||||||
|  |     } | ||||||
|  |       | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -0,0 +1,153 @@ | ||||||
|  | /* | ||||||
|  |  * Copyright (c) Contributors: TO BE FILLED | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | using System; | ||||||
|  | using System.IO; | ||||||
|  | using OpenMetaverse; | ||||||
|  | using log4net; | ||||||
|  | 
 | ||||||
|  | namespace OpenSim.Region.CoreModules.RegionSync.RegionSyncModule | ||||||
|  | { | ||||||
|  |     //Initial code in SymmetricSyncMessage copied from RegionSyncMessage.  | ||||||
|  | 
 | ||||||
|  |     /// <summary> | ||||||
|  |     /// Types of symmetric sync messages among actors.  | ||||||
|  |     /// NOTE:: To enable message subscription, we may need to move the definition of MsgType to, say IRegionSyncModule, so that it can be exposed to other region modules. | ||||||
|  |     /// </summary> | ||||||
|  |     public class SymmetricSyncMessage | ||||||
|  |     { | ||||||
|  |         #region MsgType Enum | ||||||
|  |         public enum MsgType | ||||||
|  |         { | ||||||
|  |             Null, | ||||||
|  |             // Actor -> SIM(Scene) | ||||||
|  |             GetTerrain, | ||||||
|  |             GetObjects, | ||||||
|  |              | ||||||
|  |             // SIM -> CM | ||||||
|  |             Terrain, | ||||||
|  |             NewObject,       // objects | ||||||
|  |             UpdatedObject,   // objects | ||||||
|  |             RemovedObject,   // objects | ||||||
|  |             // BIDIR | ||||||
|  |             //EchoRequest, | ||||||
|  |             //EchoResponse, | ||||||
|  |             RegionName, | ||||||
|  |             //RegionStatus, | ||||||
|  |             ActorID, | ||||||
|  |         } | ||||||
|  |         #endregion | ||||||
|  | 
 | ||||||
|  |         #region Member Data | ||||||
|  |         private MsgType m_type; | ||||||
|  |         private byte[] m_data; | ||||||
|  |         static ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); | ||||||
|  |         #endregion | ||||||
|  | 
 | ||||||
|  |         #region Constructors | ||||||
|  |         public SymmetricSyncMessage(MsgType type, byte[] data) | ||||||
|  |         { | ||||||
|  |             m_type = type; | ||||||
|  |             m_data = data; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public SymmetricSyncMessage(MsgType type, string msg) | ||||||
|  |         { | ||||||
|  |             m_type = type; | ||||||
|  |             m_data = System.Text.Encoding.ASCII.GetBytes(msg); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public SymmetricSyncMessage(MsgType type) | ||||||
|  |         { | ||||||
|  |             m_type = type; | ||||||
|  |             m_data = new byte[0]; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public SymmetricSyncMessage(Stream stream) | ||||||
|  |         { | ||||||
|  |             //ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); | ||||||
|  |             //try | ||||||
|  |             { | ||||||
|  |                 m_type = (MsgType)Utils.BytesToInt(GetBytesFromStream(stream, 4)); | ||||||
|  |                 int length = Utils.BytesToInt(GetBytesFromStream(stream, 4)); | ||||||
|  |                 m_data = GetBytesFromStream(stream, length); | ||||||
|  |                 //log.WarnFormat("RegionSyncMessage Constructed {0} ({1} bytes)", m_type.ToString(), length); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private byte[] GetBytesFromStream(Stream stream, int count) | ||||||
|  |         { | ||||||
|  |             // Loop to receive the message length | ||||||
|  |             byte[] ret = new byte[count]; | ||||||
|  |             int i = 0; | ||||||
|  |             while (i < count) | ||||||
|  |             { | ||||||
|  |                 i += stream.Read(ret, i, count - i); | ||||||
|  |             } | ||||||
|  |             return ret; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         #endregion | ||||||
|  | 
 | ||||||
|  |         #region Accessors | ||||||
|  |         public MsgType Type | ||||||
|  |         { | ||||||
|  |             get { return m_type; } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public int Length | ||||||
|  |         { | ||||||
|  |             get { return m_data.Length; } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public byte[] Data | ||||||
|  |         { | ||||||
|  |             get { return m_data; } | ||||||
|  |         } | ||||||
|  |         #endregion | ||||||
|  | 
 | ||||||
|  |         #region Conversions | ||||||
|  |         public byte[] ToBytes() | ||||||
|  |         { | ||||||
|  |             byte[] buf = new byte[m_data.Length + 8]; | ||||||
|  |             Utils.IntToBytes((int)m_type, buf, 0); | ||||||
|  |             Utils.IntToBytes(m_data.Length, buf, 4); | ||||||
|  |             Array.Copy(m_data, 0, buf, 8, m_data.Length); | ||||||
|  |             return buf; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public override string ToString() | ||||||
|  |         { | ||||||
|  |             return String.Format("{0} ({1} bytes)", m_type.ToString(), m_data.Length.ToString()); | ||||||
|  |         } | ||||||
|  |         #endregion | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         public static void HandleSuccess(string header, SymmetricSyncMessage msg, string message) | ||||||
|  |         { | ||||||
|  |             m_log.WarnFormat("{0} Handled {1}: {2}", header, msg.ToString(), message); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public static void HandleTrivial(string header, SymmetricSyncMessage msg, string message) | ||||||
|  |         { | ||||||
|  |             m_log.WarnFormat("{0} Issue handling {1}: {2}", header, msg.ToString(), message); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public static void HandleWarning(string header, SymmetricSyncMessage msg, string message) | ||||||
|  |         { | ||||||
|  |             m_log.WarnFormat("{0} Warning handling {1}: {2}", header, msg.ToString(), message); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public static void HandleError(string header, SymmetricSyncMessage msg, string message) | ||||||
|  |         { | ||||||
|  |             m_log.WarnFormat("{0} Error handling {1}: {2}", header, msg.ToString(), message); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public static bool HandlerDebug(string header, SymmetricSyncMessage msg, string message) | ||||||
|  |         { | ||||||
|  |             m_log.WarnFormat("{0} DBG ({1}): {2}", header, msg.ToString(), message); | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,298 @@ | ||||||
|  | /* | ||||||
|  |  * Copyright (c) Contributors: TO BE FILLED | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | using System; | ||||||
|  | using System.IO; | ||||||
|  | using System.Collections.Generic; | ||||||
|  | using System.Net.Sockets; | ||||||
|  | using System.Threading; | ||||||
|  | using System.Text; | ||||||
|  | using log4net; | ||||||
|  | using OpenMetaverse; | ||||||
|  | 
 | ||||||
|  | namespace OpenSim.Region.CoreModules.RegionSync.RegionSyncModule | ||||||
|  | { | ||||||
|  |     // For implementations, a lot was copied from RegionSyncClientView, especially the SendLoop/ReceiveLoop. | ||||||
|  |     public class SyncConnector | ||||||
|  |     { | ||||||
|  |         private TcpClient m_tcpConnection = null; | ||||||
|  |         private RegionSyncListenerInfo m_remoteListenerInfo = null; | ||||||
|  |         private Thread m_rcvLoop; | ||||||
|  |         private Thread m_send_loop; | ||||||
|  | 
 | ||||||
|  |         private string LogHeader = "[SYNC CONNECTOR]"; | ||||||
|  |         // The logfile | ||||||
|  |         private ILog m_log; | ||||||
|  | 
 | ||||||
|  |         //members for in/out messages queueing | ||||||
|  |         object stats = new object(); | ||||||
|  |         private long queuedUpdates=0; | ||||||
|  |         private long dequeuedUpdates=0; | ||||||
|  |         private long msgsIn=0; | ||||||
|  |         private long msgsOut=0; | ||||||
|  |         private long bytesIn=0; | ||||||
|  |         private long bytesOut=0; | ||||||
|  |         private int msgCount = 0; | ||||||
|  |         // A queue for outgoing traffic.  | ||||||
|  |         private BlockingUpdateQueue m_outQ = new BlockingUpdateQueue(); | ||||||
|  | 
 | ||||||
|  |         private RegionSyncModule m_regionSyncModule = null; | ||||||
|  | 
 | ||||||
|  |         private int m_connectorNum; | ||||||
|  |         public int 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 | ||||||
|  |         private string m_syncOtherSideRegionName=""; | ||||||
|  |         public string OtherSideRegionName | ||||||
|  |         { | ||||||
|  |             get { return m_syncOtherSideRegionName; } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // Check if the client is connected | ||||||
|  |         public bool Connected | ||||||
|  |         { get { return (m_tcpConnection !=null && m_tcpConnection.Connected); } } | ||||||
|  | 
 | ||||||
|  |         public string Description | ||||||
|  |         { | ||||||
|  |             get | ||||||
|  |             { | ||||||
|  |                 if (m_syncOtherSideRegionName == null) | ||||||
|  |                     return String.Format("SyncConnector #{0}", m_connectorNum); | ||||||
|  |                 return String.Format("SyncConnector #{0} ({1:10})", m_connectorNum, m_syncOtherSideRegionName); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         /// <summary> | ||||||
|  |         /// The constructor that will be called when a SyncConnector is created passively: a remote SyncConnector has initiated the connection. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="connectorNum"></param> | ||||||
|  |         /// <param name="tcpclient"></param> | ||||||
|  |         public SyncConnector(int connectorNum, TcpClient tcpclient, RegionSyncModule syncModule) | ||||||
|  |         { | ||||||
|  |             m_tcpConnection = tcpclient; | ||||||
|  |             m_connectorNum = connectorNum; | ||||||
|  |             m_regionSyncModule = syncModule; | ||||||
|  |             m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         /// <summary> | ||||||
|  |         /// The constructor that will be called when a SyncConnector is created actively: it is created to send connection request to a remote listener | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="connectorNum"></param> | ||||||
|  |         /// <param name="listenerInfo"></param> | ||||||
|  |         public SyncConnector(int connectorNum, RegionSyncListenerInfo listenerInfo, RegionSyncModule syncModule) | ||||||
|  |         { | ||||||
|  |             m_remoteListenerInfo = listenerInfo; | ||||||
|  |             m_connectorNum = connectorNum; | ||||||
|  |             m_regionSyncModule = syncModule; | ||||||
|  |             m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         //Connect to the remote listener | ||||||
|  |         public bool Connect() | ||||||
|  |         { | ||||||
|  |             m_tcpConnection = new TcpClient(); | ||||||
|  |             try | ||||||
|  |             { | ||||||
|  |                 m_tcpConnection.Connect(m_remoteListenerInfo.Addr, m_remoteListenerInfo.Port); | ||||||
|  |             } | ||||||
|  |             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.Warn(e.Message); | ||||||
|  |                 return false; | ||||||
|  |             } | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         /// <summary> | ||||||
|  |         /// Start both the send and receive threads | ||||||
|  |         /// </summary> | ||||||
|  |         public void StartCommThreads() | ||||||
|  |         { | ||||||
|  |             // Create a thread for the receive loop | ||||||
|  |             m_rcvLoop = new Thread(new ThreadStart(ReceiveLoop)); | ||||||
|  |             m_rcvLoop.Name = Description + " (ReceiveLoop)"; | ||||||
|  |             m_log.WarnFormat("{0} Starting {1} thread", Description, m_rcvLoop.Name); | ||||||
|  |             m_rcvLoop.Start(); | ||||||
|  | 
 | ||||||
|  |             // Create a thread for the send loop | ||||||
|  |             m_send_loop = new Thread(new ThreadStart(delegate() { SendLoop(); })); | ||||||
|  |             m_send_loop.Name = Description + " (SendLoop)"; | ||||||
|  |             m_log.WarnFormat("{0} Starting {1} thread", Description, m_send_loop.Name); | ||||||
|  |             m_send_loop.Start(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void Shutdown() | ||||||
|  |         { | ||||||
|  |             m_log.Warn(LogHeader + " shutdown connection"); | ||||||
|  |             // Abort receive and send loop | ||||||
|  |             m_rcvLoop.Abort(); | ||||||
|  |             m_send_loop.Abort(); | ||||||
|  | 
 | ||||||
|  |             // Close the connection | ||||||
|  |             m_tcpConnection.Client.Close(); | ||||||
|  |             m_tcpConnection.Close(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         /////////////////////////////////////////////////////////// | ||||||
|  |         // Sending messages out to the other side of the connection | ||||||
|  |         /////////////////////////////////////////////////////////// | ||||||
|  |         // Send messages from the update Q as fast as we can DeQueue them | ||||||
|  |         // *** This is the main send loop thread for each connected client | ||||||
|  |         private void SendLoop() | ||||||
|  |         { | ||||||
|  |             try | ||||||
|  |             { | ||||||
|  |                 while (true) | ||||||
|  |                 { | ||||||
|  |                     // Dequeue is thread safe | ||||||
|  |                     byte[] update = m_outQ.Dequeue(); | ||||||
|  |                     lock (stats) | ||||||
|  |                         dequeuedUpdates++; | ||||||
|  |                     Send(update); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             catch (Exception e) | ||||||
|  |             { | ||||||
|  |                 m_log.ErrorFormat("{0} has disconnected: {1} (SendLoop)", Description, e.Message); | ||||||
|  |             } | ||||||
|  |             Shutdown(); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         /// <summary> | ||||||
|  |         /// Enqueue update of an object/avatar into the outgoing queue, and return right away | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="id">UUID of the object/avatar</param> | ||||||
|  |         /// <param name="update">the update infomation in byte format</param> | ||||||
|  |         public void EnqueueOutgoingUpdate(UUID id, byte[] update) | ||||||
|  |         { | ||||||
|  |             lock (stats) | ||||||
|  |                 queuedUpdates++; | ||||||
|  |             // Enqueue is thread safe | ||||||
|  |             m_outQ.Enqueue(id, update); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         //Send out a messge directly. This should only by called for short messages that are not sent frequently. | ||||||
|  |         //Don't call this function for sending out updates. Call EnqueueOutgoingUpdate instead | ||||||
|  |         public void Send(SymmetricSyncMessage msg) | ||||||
|  |         { | ||||||
|  |             Send(msg.ToBytes()); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void Send(byte[] data) | ||||||
|  |         { | ||||||
|  |             if (m_tcpConnection.Connected) | ||||||
|  |             { | ||||||
|  |                 try | ||||||
|  |                 { | ||||||
|  |                     lock (stats) | ||||||
|  |                     { | ||||||
|  |                         msgsOut++; | ||||||
|  |                         bytesOut += data.Length; | ||||||
|  |                     } | ||||||
|  |                     m_tcpConnection.GetStream().BeginWrite(data, 0, data.Length, ar => | ||||||
|  |                     { | ||||||
|  |                         if (m_tcpConnection.Connected) | ||||||
|  |                         { | ||||||
|  |                             try | ||||||
|  |                             { | ||||||
|  |                                 m_tcpConnection.GetStream().EndWrite(ar); | ||||||
|  |                             } | ||||||
|  |                             catch (Exception) | ||||||
|  |                             { } | ||||||
|  |                         } | ||||||
|  |                     }, null); | ||||||
|  |                 } | ||||||
|  |                 catch (IOException) | ||||||
|  |                 { | ||||||
|  |                     m_log.WarnFormat("{0}:{1} has disconnected.", Description, m_connectorNum); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         /////////////////////////////////////////////////////////// | ||||||
|  |         // Receiving messages from the other side ofthe connection | ||||||
|  |         /////////////////////////////////////////////////////////// | ||||||
|  |         private void ReceiveLoop() | ||||||
|  |         { | ||||||
|  |             m_log.WarnFormat("{0} Thread running: {1}", LogHeader, m_rcvLoop.Name); | ||||||
|  |             while (true && m_tcpConnection.Connected) | ||||||
|  |             { | ||||||
|  |                 SymmetricSyncMessage msg; | ||||||
|  |                 // Try to get the message from the network stream | ||||||
|  |                 try | ||||||
|  |                 { | ||||||
|  |                     msg = new SymmetricSyncMessage(m_tcpConnection.GetStream()); | ||||||
|  |                     //m_log.WarnFormat("{0} Received: {1}", LogHeader, msg.ToString()); | ||||||
|  |                 } | ||||||
|  |                 // If there is a problem reading from the client, shut 'er down.  | ||||||
|  |                 catch | ||||||
|  |                 { | ||||||
|  |                     //ShutdownClient(); | ||||||
|  |                     m_log.WarnFormat("{0}:{1} has disconnected.", Description, m_connectorNum); | ||||||
|  |                     Shutdown(); | ||||||
|  |                     return; | ||||||
|  |                 } | ||||||
|  |                 // Try handling the message | ||||||
|  |                 try | ||||||
|  |                 { | ||||||
|  |                     HandleMessage(msg); | ||||||
|  |                 } | ||||||
|  |                 catch (Exception e) | ||||||
|  |                 { | ||||||
|  |                     m_log.WarnFormat("{0} Encountered an exception: {1} (MSGTYPE = {2})", Description, e.Message, msg.ToString()); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void HandleMessage(SymmetricSyncMessage msg) | ||||||
|  |         { | ||||||
|  | 
 | ||||||
|  |             msgCount++; | ||||||
|  |             switch (msg.Type) | ||||||
|  |             { | ||||||
|  |                 case SymmetricSyncMessage.MsgType.RegionName: | ||||||
|  |                     { | ||||||
|  |                         m_syncOtherSideRegionName = Encoding.ASCII.GetString(msg.Data, 0, msg.Length); | ||||||
|  |                         if (m_regionSyncModule.IsSyncRelay) | ||||||
|  |                         { | ||||||
|  |                             SymmetricSyncMessage outMsg = new SymmetricSyncMessage(SymmetricSyncMessage.MsgType.RegionName, m_regionSyncModule.LocalScene.RegionInfo.RegionName); | ||||||
|  |                             Send(outMsg); | ||||||
|  |                         } | ||||||
|  |                         m_log.DebugFormat("Syncing to region \"{0}\"", m_syncOtherSideRegionName);  | ||||||
|  |                         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: | ||||||
|  |                     break; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             //For any other messages, we simply deliver the message to RegionSyncModule for now. | ||||||
|  |             //Later on, we may deliver messages to different modules, say sync message to RegionSyncModule and event message to ActorSyncModule. | ||||||
|  |             m_regionSyncModule.HandleIncomingMessage(msg); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -589,6 +589,56 @@ namespace OpenSim.Region.CoreModules.World.Terrain | ||||||
|             client.OnUnackedTerrain += client_OnUnackedTerrain; |             client.OnUnackedTerrain += client_OnUnackedTerrain; | ||||||
|         } |         } | ||||||
|          |          | ||||||
|  |         //SYMMETRIC SYNC | ||||||
|  |         private long m_lastUpdateTimeStamp = DateTime.Now.Ticks; | ||||||
|  |         public long LastUpdateTimeStamp | ||||||
|  |         { | ||||||
|  |             get { return m_lastUpdateTimeStamp; } | ||||||
|  |             set { m_lastUpdateTimeStamp = value; } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private string m_lastUpdateActorID; | ||||||
|  |         public string LastUpdateActorID | ||||||
|  |         { | ||||||
|  |             get { return m_lastUpdateActorID; } | ||||||
|  |             set { m_lastUpdateActorID = value; } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private void SyncInfoUpdate(long timeStamp, string actorID) | ||||||
|  |         { | ||||||
|  |             m_lastUpdateTimeStamp = timeStamp; | ||||||
|  |             m_lastUpdateActorID = actorID; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         /* | ||||||
|  |         public void CheckForTerrainUpdatesBySynchronization(long timeStamp, string actorID) | ||||||
|  |         { | ||||||
|  |             SyncInfoUpdate(timeStamp, actorID); | ||||||
|  |             CheckForTerrainUpdates(false); | ||||||
|  |         } | ||||||
|  |          * */ | ||||||
|  | 
 | ||||||
|  |         public void TaintTerrianBySynchronization(long timeStamp, string actorID) | ||||||
|  |         { | ||||||
|  |             SyncInfoUpdate(timeStamp, actorID); | ||||||
|  |             CheckForTerrainUpdates(false, timeStamp, actorID); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public bool TerrianModifiedLocally(string localActorID) | ||||||
|  |         { | ||||||
|  |             if (localActorID == m_lastUpdateActorID) | ||||||
|  |                 return true; | ||||||
|  |             return false; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void GetSyncInfo(out long lastUpdateTimeStamp, out string lastUpdateActorID) | ||||||
|  |         { | ||||||
|  |             lastUpdateTimeStamp = m_lastUpdateTimeStamp; | ||||||
|  |             lastUpdateActorID = m_lastUpdateActorID; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         //end of SYMMETRIC SYNC | ||||||
|  | 
 | ||||||
|         /// <summary> |         /// <summary> | ||||||
|         /// Checks to see if the terrain has been modified since last check |         /// Checks to see if the terrain has been modified since last check | ||||||
|         /// but won't attempt to limit those changes to the limits specified in the estate settings |         /// but won't attempt to limit those changes to the limits specified in the estate settings | ||||||
|  | @ -596,7 +646,21 @@ namespace OpenSim.Region.CoreModules.World.Terrain | ||||||
|         /// </summary> |         /// </summary> | ||||||
|         private void CheckForTerrainUpdates() |         private void CheckForTerrainUpdates() | ||||||
|         { |         { | ||||||
|             CheckForTerrainUpdates(false); |             //SYMMETRIC SYNC | ||||||
|  | 
 | ||||||
|  |             //Assumption: Thus function is only called when the terrain is updated by the local actor.  | ||||||
|  |             //            Updating terrain during receiving sync messages from another actor will call CheckForTerrainUpdates. | ||||||
|  | 
 | ||||||
|  |             //Update the timestamp to the current time tick, and set the LastUpdateActorID to be self | ||||||
|  |             long currentTimeTick = DateTime.Now.Ticks; | ||||||
|  |             string localActorID = m_scene.GetSyncActorID(); | ||||||
|  |             SyncInfoUpdate(currentTimeTick, localActorID); | ||||||
|  |             //Check if the terrain has been modified and send out sync message if modified. | ||||||
|  |             CheckForTerrainUpdates(false, currentTimeTick, localActorID); | ||||||
|  | 
 | ||||||
|  |             //end of SYMMETRIC SYNC | ||||||
|  | 
 | ||||||
|  |             //CheckForTerrainUpdates(false); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         /// <summary> |         /// <summary> | ||||||
|  | @ -607,7 +671,10 @@ namespace OpenSim.Region.CoreModules.World.Terrain | ||||||
|         /// currently invoked by client_OnModifyTerrain only and not the Commander interfaces |         /// currently invoked by client_OnModifyTerrain only and not the Commander interfaces | ||||||
|         /// <param name="respectEstateSettings">should height map deltas be limited to the estate settings limits</param> |         /// <param name="respectEstateSettings">should height map deltas be limited to the estate settings limits</param> | ||||||
|         /// </summary> |         /// </summary> | ||||||
|         private void CheckForTerrainUpdates(bool respectEstateSettings) |         //private void CheckForTerrainUpdates(bool respectEstateSettings) | ||||||
|  |         //SYMMETRIC SYNC: Change the interface, to input the right sync information for the most recent update | ||||||
|  |         private void CheckForTerrainUpdates(bool respectEstateSettings, long lastUpdateTimeStamp, string lastUpdateActorID) | ||||||
|  |         //end of SYMMETRIC SYNC | ||||||
|         { |         { | ||||||
|             bool shouldTaint = false; |             bool shouldTaint = false; | ||||||
|             float[] serialised = m_channel.GetFloatsSerialised(); |             float[] serialised = m_channel.GetFloatsSerialised(); | ||||||
|  | @ -636,6 +703,13 @@ namespace OpenSim.Region.CoreModules.World.Terrain | ||||||
|             if (shouldTaint) |             if (shouldTaint) | ||||||
|             { |             { | ||||||
|                 m_tainted = true; |                 m_tainted = true; | ||||||
|  |                 //SYMMETRIC SYNC | ||||||
|  |                 //Terrain has been modified, send out sync message if needed | ||||||
|  |                 if (m_scene.RegionSyncModule != null) | ||||||
|  |                 { | ||||||
|  |                     m_scene.RegionSyncModule.SendTerrainUpdates(m_lastUpdateActorID); | ||||||
|  |                 } | ||||||
|  |                 //end of SYMMETRIC SYNC | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  | @ -748,7 +822,10 @@ namespace OpenSim.Region.CoreModules.World.Terrain | ||||||
|                         m_painteffects[(StandardTerrainEffects) action].PaintEffect( |                         m_painteffects[(StandardTerrainEffects) action].PaintEffect( | ||||||
|                             m_channel, allowMask, west, south, height, size, seconds); |                             m_channel, allowMask, west, south, height, size, seconds); | ||||||
| 
 | 
 | ||||||
|                         CheckForTerrainUpdates(!god); //revert changes outside estate limits |                         //CheckForTerrainUpdates(!god); //revert changes outside estate limits | ||||||
|  |                         //SYMMETRIC SYNC | ||||||
|  |                         CheckForTerrainUpdates(!god, DateTime.Now.Ticks, m_scene.GetSyncActorID()); | ||||||
|  |                         //end of SYMMETRIC SYNC | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|                 else |                 else | ||||||
|  | @ -789,7 +866,10 @@ namespace OpenSim.Region.CoreModules.World.Terrain | ||||||
|                         m_floodeffects[(StandardTerrainEffects) action].FloodEffect( |                         m_floodeffects[(StandardTerrainEffects) action].FloodEffect( | ||||||
|                             m_channel, fillArea, size); |                             m_channel, fillArea, size); | ||||||
| 
 | 
 | ||||||
|                         CheckForTerrainUpdates(!god); //revert changes outside estate limits |                         //CheckForTerrainUpdates(!god); //revert changes outside estate limits | ||||||
|  |                         //SYMMETRIC SYNC | ||||||
|  |                         CheckForTerrainUpdates(!god, DateTime.Now.Ticks, m_scene.GetSyncActorID()); | ||||||
|  |                         //end of SYMMETRIC SYNC | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|                 else |                 else | ||||||
|  |  | ||||||
|  | @ -64,5 +64,21 @@ namespace OpenSim.Region.Framework.Interfaces | ||||||
|         void InstallPlugin(string name, ITerrainEffect plug); |         void InstallPlugin(string name, ITerrainEffect plug); | ||||||
| 
 | 
 | ||||||
|         void UndoTerrain(ITerrainChannel channel); |         void UndoTerrain(ITerrainChannel channel); | ||||||
|  | 
 | ||||||
|  |         //SYMMETRIC SYNC | ||||||
|  |         void TaintTerrianBySynchronization(long timeStamp, string actorID); | ||||||
|  |         /// <summary> | ||||||
|  |         /// Return true if the most recent update on terrain is done locally (i.e. not by receiving a terrain-sync message). | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="localActorID"></param> | ||||||
|  |         /// <returns></returns> | ||||||
|  |         bool TerrianModifiedLocally(string localActorID); | ||||||
|  |         /// <summary> | ||||||
|  |         /// Obtain the timestemp and actorID information for the most recent update on terrain. | ||||||
|  |         /// </summary> | ||||||
|  |         /// <param name="lastUpdateTimeStamp"></param> | ||||||
|  |         /// <param name="lastUpdateActorID"></param> | ||||||
|  |         void GetSyncInfo(out long lastUpdateTimeStamp, out string lastUpdateActorID); | ||||||
|  |         //end of SYMMETRIC SYNC | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -376,7 +376,6 @@ namespace OpenSim.Region.Framework.Scenes | ||||||
|         public delegate void RegionUp(GridRegion region); |         public delegate void RegionUp(GridRegion region); | ||||||
|         public event RegionUp OnRegionUp; |         public event RegionUp OnRegionUp; | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
|         public class MoneyTransferArgs : EventArgs |         public class MoneyTransferArgs : EventArgs | ||||||
|         { |         { | ||||||
|             public UUID sender; |             public UUID sender; | ||||||
|  | @ -2184,7 +2183,7 @@ namespace OpenSim.Region.Framework.Scenes | ||||||
|                     catch (Exception e) |                     catch (Exception e) | ||||||
|                     { |                     { | ||||||
|                         m_log.ErrorFormat( |                         m_log.ErrorFormat( | ||||||
|                             "[EVENT MANAGER]: Delegate for TriggerOnSceneObjectLoaded failed - continuing.  {0} {1}",  |                             "[EVENT MANAGER]: Delegate for TriggerScriptEngineSyncStop failed - continuing.  {0} {1}",  | ||||||
|                             e.Message, e.StackTrace); |                             e.Message, e.StackTrace); | ||||||
|                     } |                     } | ||||||
|                 } |                 } | ||||||
|  | @ -2243,5 +2242,55 @@ namespace OpenSim.Region.Framework.Scenes | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         #endregion |         #endregion | ||||||
|  | 
 | ||||||
|  |         //SYMMETRIC SYNC | ||||||
|  |         public event PostSceneCreation OnPostSceneCreation; | ||||||
|  |         public delegate void PostSceneCreation(Scene createdScene); | ||||||
|  | 
 | ||||||
|  |         public void TriggerOnPostSceneCreation(Scene createdScene) | ||||||
|  |         { | ||||||
|  |             PostSceneCreation handler = OnPostSceneCreation; | ||||||
|  |             if (handler != null) | ||||||
|  |             { | ||||||
|  |                 foreach (PostSceneCreation d in handler.GetInvocationList()) | ||||||
|  |                 { | ||||||
|  |                     try | ||||||
|  |                     { | ||||||
|  |                         d(createdScene); | ||||||
|  |                     } | ||||||
|  |                     catch (Exception e) | ||||||
|  |                     { | ||||||
|  |                         m_log.ErrorFormat( | ||||||
|  |                             "[EVENT MANAGER]: Delegate for TriggerOnPostSceneCreation failed - continuing.  {0} {1}", | ||||||
|  |                             e.Message, e.StackTrace); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public delegate void SymmetricSyncStop(); | ||||||
|  |         public event SymmetricSyncStop OnSymmetricSyncStop; | ||||||
|  |         public void TriggerOnSymmetricSyncStop() | ||||||
|  |         { | ||||||
|  |             SymmetricSyncStop handlerSymmetricSyncStop = OnSymmetricSyncStop; | ||||||
|  |             if (handlerSymmetricSyncStop != null) | ||||||
|  |             { | ||||||
|  |                 foreach (SymmetricSyncStop d in handlerSymmetricSyncStop.GetInvocationList()) | ||||||
|  |                 { | ||||||
|  |                     try | ||||||
|  |                     { | ||||||
|  |                         d(); | ||||||
|  |                     } | ||||||
|  |                     catch (Exception e) | ||||||
|  |                     { | ||||||
|  |                         m_log.ErrorFormat( | ||||||
|  |                             "[EVENT MANAGER]: Delegate for TriggerOnSymmetricSyncStop failed - continuing.  {0} {1}", | ||||||
|  |                             e.Message, e.StackTrace); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         //end of SYMMETRIC SYNC | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -408,7 +408,10 @@ namespace OpenSim.Region.Framework.Scenes | ||||||
|         { |         { | ||||||
|             get { return m_AvatarFactory; } |             get { return m_AvatarFactory; } | ||||||
|         } |         } | ||||||
|         #region REGION SYNC | 
 | ||||||
|  | 
 | ||||||
|  |         #region REGION SYNC -- Asymmetric sync, old style, depreciated --------- | ||||||
|  | 
 | ||||||
|         protected IRegionSyncServerModule m_regionSyncServerModule; |         protected IRegionSyncServerModule m_regionSyncServerModule; | ||||||
|         protected IRegionSyncClientModule m_regionSyncClientModule; |         protected IRegionSyncClientModule m_regionSyncClientModule; | ||||||
| 
 | 
 | ||||||
|  | @ -576,6 +579,100 @@ namespace OpenSim.Region.Framework.Scenes | ||||||
|         // } |         // } | ||||||
| 
 | 
 | ||||||
|         #endregion  |         #endregion  | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         #region SYMMETRIC SYNC | ||||||
|  |         /////////////////////////////////////////////////////////////////////////////////////////////// | ||||||
|  |         //KittyL: 12/23/2010. SYMMETRIC SYNC: Implementation for the symmetric synchronization model. | ||||||
|  |         /////////////////////////////////////////////////////////////////////////////////////////////// | ||||||
|  | 
 | ||||||
|  |         private IRegionSyncModule m_regionSyncModule = null; | ||||||
|  |         public IRegionSyncModule RegionSyncModule | ||||||
|  |         { | ||||||
|  |             get { return m_regionSyncModule; } | ||||||
|  |             //set { m_regionSyncModule = value; } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private IDSGActorSyncModule m_DSGActorSyncModule = null; | ||||||
|  |         public IDSGActorSyncModule ActorSyncModule | ||||||
|  |         { | ||||||
|  |             get { return m_DSGActorSyncModule; } | ||||||
|  | 
 | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         //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  | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public string GetSyncActorID() | ||||||
|  |         { | ||||||
|  |             if (m_DSGActorSyncModule != null) | ||||||
|  |             { | ||||||
|  |                 return m_DSGActorSyncModule.ActorID; | ||||||
|  |             } | ||||||
|  |             return ""; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         //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); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         //Similar to DeleteSceneObject, except that this does not change LastUpdateActorID and LastUpdateTimeStamp | ||||||
|  |         public void DeleteSceneObjectBySynchronization(SceneObjectGroup group) | ||||||
|  |         { | ||||||
|  |             //            m_log.DebugFormat("[SCENE]: Deleting scene object {0} {1}", group.Name, group.UUID); | ||||||
|  | 
 | ||||||
|  |             //SceneObjectPart rootPart = group.GetChildPart(group.UUID); | ||||||
|  | 
 | ||||||
|  |             // Serialise calls to RemoveScriptInstances to avoid | ||||||
|  |             // deadlocking on m_parts inside SceneObjectGroup | ||||||
|  |             lock (m_deleting_scene_object) | ||||||
|  |             { | ||||||
|  |                 group.RemoveScriptInstances(true); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             SceneObjectPart[] partList = group.Parts; | ||||||
|  | 
 | ||||||
|  |             foreach (SceneObjectPart part in partList) | ||||||
|  |             { | ||||||
|  |                 if (part.IsJoint() && ((part.Flags & PrimFlags.Physics) != 0)) | ||||||
|  |                 { | ||||||
|  |                     PhysicsScene.RequestJointDeletion(part.Name); // FIXME: what if the name changed? | ||||||
|  |                 } | ||||||
|  |                 else if (part.PhysActor != null) | ||||||
|  |                 { | ||||||
|  |                     PhysicsScene.RemovePrim(part.PhysActor); | ||||||
|  |                     part.PhysActor = null; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if (UnlinkSceneObject(group, false)) | ||||||
|  |             { | ||||||
|  |                 EventManager.TriggerObjectBeingRemovedFromScene(group); | ||||||
|  |                 EventManager.TriggerParcelPrimCountTainted(); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             bool silent = false; //do not suppress broadcasting changes to other clients, for debugging with viewers | ||||||
|  |             group.DeleteGroupFromScene(silent); | ||||||
|  | 
 | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         #endregion //SYMMETRIC SYNC | ||||||
|  | 
 | ||||||
|         public ICapabilitiesModule CapsModule |         public ICapabilitiesModule CapsModule | ||||||
|         { |         { | ||||||
|             get { return m_capsModule; } |             get { return m_capsModule; } | ||||||
|  | @ -1265,6 +1362,16 @@ namespace OpenSim.Region.Framework.Scenes | ||||||
|             RegionSyncClientModule = RequestModuleInterface<IRegionSyncClientModule>(); |             RegionSyncClientModule = RequestModuleInterface<IRegionSyncClientModule>(); | ||||||
|             ScriptEngineToSceneConnectorModule = RequestModuleInterface<IScriptEngineToSceneConnectorModule>(); |             ScriptEngineToSceneConnectorModule = RequestModuleInterface<IScriptEngineToSceneConnectorModule>(); | ||||||
| 
 | 
 | ||||||
|  |             ////////////////////////////////////////////////////////////////////// | ||||||
|  |             //SYMMETRIC SYNC (KittyL: started 12/23/2010) | ||||||
|  |             ////////////////////////////////////////////////////////////////////// | ||||||
|  |             m_regionSyncModule = RequestModuleInterface<IRegionSyncModule>(); | ||||||
|  |             m_DSGActorSyncModule = RequestModuleInterface<IDSGActorSyncModule>(); | ||||||
|  | 
 | ||||||
|  |             ////////////////////////////////////////////////////////////////////// | ||||||
|  |             //end of SYMMETRIC SYNC | ||||||
|  |             ////////////////////////////////////////////////////////////////////// | ||||||
|  |              | ||||||
|             // Shoving this in here for now, because we have the needed |             // Shoving this in here for now, because we have the needed | ||||||
|             // interfaces at this point |             // interfaces at this point | ||||||
|             // |             // | ||||||
|  | @ -1416,7 +1523,7 @@ namespace OpenSim.Region.Framework.Scenes | ||||||
|                     // If it's a client manager, just send prim updates |                     // If it's a client manager, just send prim updates | ||||||
|                     // This will get fixed later to only send to locally logged in presences rather than all presences |                     // This will get fixed later to only send to locally logged in presences rather than all presences | ||||||
|                     // but requires pulling apart the concept of a client from the concept of a presence/avatar |                     // but requires pulling apart the concept of a client from the concept of a presence/avatar | ||||||
|                     if (IsSyncedClient() || !RegionSyncEnabled) |                     if (IsSyncedClient()) | ||||||
| 
 | 
 | ||||||
|                     { |                     { | ||||||
|                         ForEachScenePresence(delegate(ScenePresence sp) { sp.SendPrimUpdates(); }); |                         ForEachScenePresence(delegate(ScenePresence sp) { sp.SendPrimUpdates(); }); | ||||||
|  | @ -1450,24 +1557,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 | ||||||
|  | @ -2317,6 +2414,15 @@ namespace OpenSim.Region.Framework.Scenes | ||||||
|             group.DeleteGroupFromScene(silent); |             group.DeleteGroupFromScene(silent); | ||||||
| 
 | 
 | ||||||
| //            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(); | ||||||
|  |             } | ||||||
|  |             //end of SYMMETRIC SYNC | ||||||
|  | 
 | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         /// <summary> |         /// <summary> | ||||||
|  |  | ||||||
|  | @ -373,23 +373,13 @@ namespace OpenSim.Region.Framework.Scenes | ||||||
| 
 | 
 | ||||||
|             sceneObject.AttachToScene(m_parentScene); |             sceneObject.AttachToScene(m_parentScene); | ||||||
| 
 | 
 | ||||||
|             //KittyL: edited to support script engine actor |  | ||||||
|             //if (sendClientUpdates) |  | ||||||
|             //    sceneObject.ScheduleGroupForFullUpdate(); |  | ||||||
|             if (sendClientUpdates) |             if (sendClientUpdates) | ||||||
|             { |  | ||||||
|                 sceneObject.ScheduleGroupForFullUpdate(); |                 sceneObject.ScheduleGroupForFullUpdate(); | ||||||
|             } |  | ||||||
|              |              | ||||||
|             Entities.Add(sceneObject); |             Entities.Add(sceneObject); | ||||||
| 
 | 
 | ||||||
|             //KittyL: edited to support script engine actor |             if (attachToBackup) | ||||||
|             //if (attachToBackup) |  | ||||||
|             //    sceneObject.AttachToBackup(); |  | ||||||
|             if (attachToBackup && m_parentScene.IsAuthoritativeScene()) |  | ||||||
|             { |  | ||||||
|                 sceneObject.AttachToBackup(); |                 sceneObject.AttachToBackup(); | ||||||
|             } |  | ||||||
|              |              | ||||||
|             if (OnObjectCreate != null) |             if (OnObjectCreate != null) | ||||||
|                 OnObjectCreate(sceneObject); |                 OnObjectCreate(sceneObject); | ||||||
|  | @ -465,7 +455,10 @@ namespace OpenSim.Region.Framework.Scenes | ||||||
|         protected internal void AddToUpdateList(SceneObjectGroup obj) |         protected internal void AddToUpdateList(SceneObjectGroup obj) | ||||||
|         { |         { | ||||||
|             lock (m_updateList) |             lock (m_updateList) | ||||||
|  |             { | ||||||
|                 m_updateList[obj.UUID] = obj; |                 m_updateList[obj.UUID] = obj; | ||||||
|  |                 m_log.Debug("added " + obj.UUID + " to m_updateList"); | ||||||
|  |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         /// <summary> |         /// <summary> | ||||||
|  | @ -484,6 +477,13 @@ namespace OpenSim.Region.Framework.Scenes | ||||||
|                 lock (m_updateList) |                 lock (m_updateList) | ||||||
|                 { |                 { | ||||||
|                     updates = new List<SceneObjectGroup>(m_updateList.Values); |                     updates = new List<SceneObjectGroup>(m_updateList.Values); | ||||||
|  | 
 | ||||||
|  |                     if (updates.Count > 0) | ||||||
|  |                     { | ||||||
|  |                         m_log.Debug("SceneGraph: " + updates.Count + " objects to send updates for"); | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|                     m_updateList.Clear(); |                     m_updateList.Clear(); | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|  | @ -1928,7 +1928,103 @@ 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 | ||||||
|  |             { | ||||||
|  |                 m_log.Debug("AddSceneObjectByStateSynch to be called"); | ||||||
|  |                 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 | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -3455,6 +3455,145 @@ namespace OpenSim.Region.Framework.Scenes | ||||||
|             this.m_locX = updatedSog.LocX; |             this.m_locX = updatedSog.LocX; | ||||||
|             this.m_locY = updatedSog.LocY; |             this.m_locY = updatedSog.LocY; | ||||||
|         } |         } | ||||||
|  | #endregion  | ||||||
|  | 
 | ||||||
|  |         #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 | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -2892,6 +2892,13 @@ namespace OpenSim.Region.Framework.Scenes | ||||||
|             //            m_log.DebugFormat( |             //            m_log.DebugFormat( | ||||||
|             //                "[SCENE OBJECT PART]: Scheduling full  update for {0}, {1} at {2}", |             //                "[SCENE OBJECT PART]: Scheduling full  update for {0}, {1} at {2}", | ||||||
|             //                UUID, Name, TimeStampFull); |             //                UUID, Name, TimeStampFull); | ||||||
|  | 
 | ||||||
|  |             //SYMMETRIC SYNC | ||||||
|  | 
 | ||||||
|  |             //update information (timestamp, actorID, etc) needed for synchronization across copies of Scene | ||||||
|  |             SyncInfoUpdate(); | ||||||
|  |              | ||||||
|  |             //end of SYMMETRIC SYNC | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         /// <summary> |         /// <summary> | ||||||
|  | @ -2913,6 +2920,13 @@ namespace OpenSim.Region.Framework.Scenes | ||||||
|             //                m_log.DebugFormat( |             //                m_log.DebugFormat( | ||||||
|             //                    "[SCENE OBJECT PART]: Scheduling terse update for {0}, {1} at {2}", |             //                    "[SCENE OBJECT PART]: Scheduling terse update for {0}, {1} at {2}", | ||||||
|             //                    UUID, Name, TimeStampTerse); |             //                    UUID, Name, TimeStampTerse); | ||||||
|  | 
 | ||||||
|  |                 //SYMMETRIC SYNC | ||||||
|  | 
 | ||||||
|  |                 //update information (timestamp, actorID, etc) needed for synchronization across copies of Scene | ||||||
|  |                 SyncInfoUpdate(); | ||||||
|  | 
 | ||||||
|  |                 //end of SYMMETRIC SYNC | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  | @ -3130,6 +3144,12 @@ namespace OpenSim.Region.Framework.Scenes | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|             ClearUpdateSchedule(); |             ClearUpdateSchedule(); | ||||||
|  | 
 | ||||||
|  |             //SYMMETRIC SYNC | ||||||
|  | 
 | ||||||
|  |             m_parentGroup.Scene.RegionSyncModule.QueueSceneObjectPartForUpdate(this); | ||||||
|  |              | ||||||
|  |             //end of SYMMETRIC SYNC | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         /// <summary> |         /// <summary> | ||||||
|  | @ -4888,5 +4908,208 @@ namespace OpenSim.Region.Framework.Scenes | ||||||
| 
 | 
 | ||||||
|         #endregion |         #endregion | ||||||
| 
 | 
 | ||||||
|  |         #region SYMMETRIC SYNC | ||||||
|  | 
 | ||||||
|  |         //Time stamp for the most recent update on this prim. We only have one time-stamp per prim for now. | ||||||
|  |         //The goal is to evetually have time-stamp per property bucket for each prim. | ||||||
|  |         private long m_lastUpdateTimeStamp = DateTime.Now.Ticks; | ||||||
|  |         public long LastUpdateTimeStamp | ||||||
|  |         { | ||||||
|  |             get { return m_lastUpdateTimeStamp; } | ||||||
|  |             set { m_lastUpdateTimeStamp = value; } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         //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.  | ||||||
|  |         private string m_lastUpdateActorID; | ||||||
|  |         public string LastUpdateActorID | ||||||
|  |         { | ||||||
|  |             get { return m_lastUpdateActorID; } | ||||||
|  |             set { m_lastUpdateActorID = value; } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void UpdateTimestamp() | ||||||
|  |         { | ||||||
|  |             m_lastUpdateTimeStamp = DateTime.Now.Ticks; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void SetLastUpdateActorID() | ||||||
|  |         { | ||||||
|  |             if (m_parentGroup != null) | ||||||
|  |             { | ||||||
|  |                 m_lastUpdateActorID = m_parentGroup.Scene.ActorSyncModule.ActorID; | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 m_log.Error("Prim " + UUID + " is not in a SceneObjectGroup yet"); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         public void SyncInfoUpdate() | ||||||
|  |         { | ||||||
|  |             //Trick: calling UpdateTimestamp here makes sure that when an object was received and de-serialized, before | ||||||
|  |             //       its parts are linked together, neither TimeStamp or ActorID will be modified. This is because during de-serialization,  | ||||||
|  |             //       ScheduleFullUpdate() is called when m_parentGroup == null | ||||||
|  |             if (m_parentGroup != null) | ||||||
|  |             { | ||||||
|  |                 UpdateTimestamp(); | ||||||
|  |                 m_lastUpdateActorID = m_parentGroup.Scene.ActorSyncModule.ActorID; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         //!!!!!! -- 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_lastUpdateActorID != 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_lastUpdateActorID = 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]: ScheduleFullUpdate_SyncInfoUnchanged 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  | ||||||
|  | 
 | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -215,6 +215,10 @@ namespace OpenSim.Region.Framework.Scenes.Serialization | ||||||
| 
 | 
 | ||||||
|                     sceneObject.AddPart(part); |                     sceneObject.AddPart(part); | ||||||
| 
 | 
 | ||||||
|  |                     //SYMMETRIC SYNC | ||||||
|  |                     //KittyL: 12/27/2010, added ActorID for symmetric synch model | ||||||
|  |                     part.SetLastUpdateActorID(); | ||||||
|  | 
 | ||||||
|                     // SceneObjectGroup.AddPart() tries to be smart and automatically set the LinkNum. |                     // SceneObjectGroup.AddPart() tries to be smart and automatically set the LinkNum. | ||||||
|                     // We override that here |                     // We override that here | ||||||
|                     if (originalLinkNum != 0) |                     if (originalLinkNum != 0) | ||||||
|  | @ -324,6 +328,12 @@ namespace OpenSim.Region.Framework.Scenes.Serialization | ||||||
|             m_SOPXmlProcessors.Add("MediaUrl", ProcessMediaUrl); |             m_SOPXmlProcessors.Add("MediaUrl", ProcessMediaUrl); | ||||||
|             m_SOPXmlProcessors.Add("TextureAnimation", ProcessTextureAnimation); |             m_SOPXmlProcessors.Add("TextureAnimation", ProcessTextureAnimation); | ||||||
|             m_SOPXmlProcessors.Add("ParticleSystem", ProcessParticleSystem); |             m_SOPXmlProcessors.Add("ParticleSystem", ProcessParticleSystem); | ||||||
|  | 
 | ||||||
|  |             //SYMMETRIC SYNC | ||||||
|  |             m_SOPXmlProcessors.Add("LastUpdateTimeStamp", ProcessUpdateTimeStamp); | ||||||
|  |             m_SOPXmlProcessors.Add("LastUpdateActorID", ProcessLastUpdateActorID); | ||||||
|  |             //end of SYMMETRIC SYNC | ||||||
|  | 
 | ||||||
|             #endregion |             #endregion | ||||||
| 
 | 
 | ||||||
|             #region TaskInventoryXmlProcessors initialization |             #region TaskInventoryXmlProcessors initialization | ||||||
|  | @ -681,6 +691,19 @@ namespace OpenSim.Region.Framework.Scenes.Serialization | ||||||
|         { |         { | ||||||
|             obj.ParticleSystem = Convert.FromBase64String(reader.ReadElementContentAsString("ParticleSystem", String.Empty)); |             obj.ParticleSystem = Convert.FromBase64String(reader.ReadElementContentAsString("ParticleSystem", String.Empty)); | ||||||
|         } |         } | ||||||
|  | 
 | ||||||
|  |         //SYMMETRIC SYNC | ||||||
|  |         private static void ProcessUpdateTimeStamp(SceneObjectPart obj, XmlTextReader reader) | ||||||
|  |         { | ||||||
|  |             obj.LastUpdateTimeStamp = reader.ReadElementContentAsLong("LastUpdateTimeStamp", string.Empty); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         private static void ProcessLastUpdateActorID(SceneObjectPart obj, XmlTextReader reader) | ||||||
|  |         { | ||||||
|  |             obj.LastUpdateActorID = reader.ReadElementContentAsString("LastUpdateActorID", string.Empty); | ||||||
|  |         } | ||||||
|  |         //end of SYMMETRIC SYNC | ||||||
|  | 
 | ||||||
|         #endregion |         #endregion | ||||||
| 
 | 
 | ||||||
|         #region TaskInventoryXmlProcessors |         #region TaskInventoryXmlProcessors | ||||||
|  | @ -1159,6 +1182,11 @@ namespace OpenSim.Region.Framework.Scenes.Serialization | ||||||
|             WriteBytes(writer, "TextureAnimation", sop.TextureAnimation); |             WriteBytes(writer, "TextureAnimation", sop.TextureAnimation); | ||||||
|             WriteBytes(writer, "ParticleSystem", sop.ParticleSystem); |             WriteBytes(writer, "ParticleSystem", sop.ParticleSystem); | ||||||
| 
 | 
 | ||||||
|  |             //SYMMETRIC SYNC | ||||||
|  |             writer.WriteElementString("LastUpdateTimeStamp", sop.LastUpdateTimeStamp.ToString()); | ||||||
|  |             writer.WriteElementString("LastUpdateActorID", sop.LastUpdateActorID); | ||||||
|  |             //end of SYMMETRIC SYNC | ||||||
|  | 
 | ||||||
|             writer.WriteEndElement(); |             writer.WriteEndElement(); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -65,7 +65,8 @@ | ||||||
|     AssetLoaderArgs = "assets/AssetSets.xml" |     AssetLoaderArgs = "assets/AssetSets.xml" | ||||||
| 
 | 
 | ||||||
| [Groups] | [Groups] | ||||||
|     Enabled = true |     ;Enabled = true | ||||||
|  |     Enabled = false | ||||||
|     Module  = GroupsModule |     Module  = GroupsModule | ||||||
|     DebugEnabled     = false |     DebugEnabled     = false | ||||||
|     NoticesEnabled   = true |     NoticesEnabled   = true | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	 Dan Lake
						Dan Lake