diff --git a/OpenSim/Region/Environment/Modules/ContentManagementSystem/AuraMetaEntity.cs b/OpenSim/Region/Environment/Modules/ContentManagementSystem/AuraMetaEntity.cs
new file mode 100644
index 0000000000..2155b4c6f1
--- /dev/null
+++ b/OpenSim/Region/Environment/Modules/ContentManagementSystem/AuraMetaEntity.cs
@@ -0,0 +1,114 @@
+// AuraMetaEntity.cs created with MonoDevelop
+// User: bongiojp at 3:03 PM 8/6/2008
+//
+// To change standard headers go to Edit->Preferences->Coding->Standard Headers
+//
+
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using libsecondlife;
+using Nini.Config;
+using OpenSim.Framework;
+using OpenSim.Region.Environment.Interfaces;
+using OpenSim.Region.Environment.Scenes;
+using log4net;
+using OpenSim.Region.Physics.Manager;
+using Axiom.Math;
+
+namespace OpenSim.Region.Environment.Modules.ContentManagement
+{
+
+
+ public class AuraMetaEntity : PointMetaEntity
+ {
+ //transparency of root part, NOT particle system. Should probably add support for changing particle system transparency.
+ public AuraMetaEntity(Scene scene, uint LocalId, LLVector3 groupPos, float transparency, LLVector3 color, LLVector3 scale) : base(scene, LocalId, groupPos, transparency)
+ {
+ SetAura(color, scale);
+ }
+
+ public AuraMetaEntity(Scene scene, LLUUID uuid, uint LocalId, LLVector3 groupPos, float transparency, LLVector3 color, LLVector3 scale) : base(scene, uuid, LocalId, groupPos, transparency)
+ {
+ SetAura(color, scale);
+ }
+
+ private float Average(LLVector3 values)
+ {
+ return (values.X + values.Y + values.Z)/3f;
+ }
+
+ public void SetAura(LLVector3 color, LLVector3 scale)
+ {
+ SetAura(color, Average(scale) * 2.0f);
+ }
+
+ public void SetAura(LLVector3 color, float radius)
+ {
+ SceneObjectPart From = m_Entity.RootPart;
+
+ //m_log.Debug("[META ENTITY] BEFORE: radius = " + radius);
+ float burstRadius = 0.1f;
+ Primitive.ParticleSystem.SourcePattern patternFlags = Primitive.ParticleSystem.SourcePattern.None;
+ float age = 1.5f;
+ float burstRate = 0.4f;
+ if (radius >= 8.0f)
+ {
+ //float sizeOfObject = radius / 2.0f;
+ burstRadius = (radius - 8.0f)/3f;
+ burstRate = 1.5f;
+ radius = 7.99f;
+ patternFlags = Primitive.ParticleSystem.SourcePattern.Explode;
+ age = 4.0f;
+ }
+ SetAura(From, color, radius, burstRadius, age, burstRate, patternFlags);
+ }
+ public void SetAura(SceneObjectPart From, LLVector3 color, float radius, float burstRadius, float age, float burstRate, libsecondlife.Primitive.ParticleSystem.SourcePattern patternFlags)
+ {
+ Primitive.ParticleSystem prules = new Primitive.ParticleSystem();
+ //prules.PartDataFlags = Primitive.ParticleSystem.ParticleDataFlags.Emissive |
+ // Primitive.ParticleSystem.ParticleDataFlags.FollowSrc; //PSYS_PART_FLAGS
+ //prules.PartDataFlags = Primitive.ParticleSystem.ParticleDataFlags.Beam |
+ // Primitive.ParticleSystem.ParticleDataFlags.TargetPos;
+ prules.PartStartColor.R = color.X; //PSYS_PART_START_COLOR
+ prules.PartStartColor.G = color.Y;
+ prules.PartStartColor.B = color.Z;
+ prules.PartStartColor.A = 0.5f; //PSYS_PART_START_ALPHA, transparency
+ prules.PartEndColor.R = color.X; //PSYS_PART_END_COLOR
+ prules.PartEndColor.G = color.Y;
+ prules.PartEndColor.B = color.Z;
+ prules.PartEndColor.A = 0.5f; //PSYS_PART_END_ALPHA, transparency
+ /*prules.PartStartScaleX = 0.5f; //PSYS_PART_START_SCALE
+ prules.PartStartScaleY = 0.5f;
+ prules.PartEndScaleX = 0.5f; //PSYS_PART_END_SCALE
+ prules.PartEndScaleY = 0.5f;
+ */
+ prules.PartStartScaleX = radius; //PSYS_PART_START_SCALE
+ prules.PartStartScaleY = radius;
+ prules.PartEndScaleX = radius; //PSYS_PART_END_SCALE
+ prules.PartEndScaleY = radius;
+ prules.PartMaxAge = age; //PSYS_PART_MAX_AGE
+ prules.PartAcceleration.X = 0.0f; //PSYS_SRC_ACCEL
+ prules.PartAcceleration.Y = 0.0f;
+ prules.PartAcceleration.Z = 0.0f;
+ prules.Pattern = patternFlags; //PSYS_SRC_PATTERN
+ //prules.Texture = LLUUID.Zero;//= LLUUID //PSYS_SRC_TEXTURE, default used if blank
+ prules.BurstRate = burstRate; //PSYS_SRC_BURST_RATE
+ prules.BurstPartCount = 2; //PSYS_SRC_BURST_PART_COUNT
+ //prules.BurstRadius = radius; //PSYS_SRC_BURST_RADIUS
+ prules.BurstRadius = burstRadius; //PSYS_SRC_BURST_RADIUS
+ prules.BurstSpeedMin = 0.001f; //PSYS_SRC_BURST_SPEED_MIN
+ prules.BurstSpeedMax = 0.001f; //PSYS_SRC_BURST_SPEED_MAX
+ prules.MaxAge = 0.0f; //PSYS_SRC_MAX_AGE
+ //prules.Target = To; //PSYS_SRC_TARGET_KEY
+ prules.AngularVelocity.X = 0.0f; //PSYS_SRC_OMEGA
+ prules.AngularVelocity.Y = 0.0f;
+ prules.AngularVelocity.Z = 0.0f;
+ prules.InnerAngle = 0.0f; //PSYS_SRC_ANGLE_BEGIN
+ prules.OuterAngle = 0.0f; //PSYS_SRC_ANGLE_END
+
+ prules.CRC = 1; //activates the particle system??
+ From.AddNewParticleSystem(prules);
+ }
+ }
+}
diff --git a/OpenSim/Region/Environment/Modules/ContentManagementSystem/BeamMetaEntity.cs b/OpenSim/Region/Environment/Modules/ContentManagementSystem/BeamMetaEntity.cs
new file mode 100644
index 0000000000..499d1bc020
--- /dev/null
+++ b/OpenSim/Region/Environment/Modules/ContentManagementSystem/BeamMetaEntity.cs
@@ -0,0 +1,98 @@
+// BeamMetaEntity.cs created with MonoDevelop
+// User: bongiojp at 3:03 PM 8/6/2008
+//
+// To change standard headers go to Edit->Preferences->Coding->Standard Headers
+//
+
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using libsecondlife;
+using Nini.Config;
+using OpenSim.Framework;
+using OpenSim.Region.Environment.Interfaces;
+using OpenSim.Region.Environment.Scenes;
+using log4net;
+using OpenSim.Region.Physics.Manager;
+using Axiom.Math;
+
+namespace OpenSim.Region.Environment.Modules.ContentManagement
+{
+
+
+ public class BeamMetaEntity : PointMetaEntity
+ {
+
+ public BeamMetaEntity(Scene scene, uint LocalId, LLVector3 groupPos, float transparency, SceneObjectPart To, LLVector3 color) : base(scene, LocalId, groupPos, transparency)
+ {
+ SetBeamToUUID(To, color);
+ }
+
+ public BeamMetaEntity(Scene scene, LLUUID uuid, uint LocalId, LLVector3 groupPos, float transparency, SceneObjectPart To, LLVector3 color) : base(scene, uuid, LocalId, groupPos, transparency)
+ {
+ SetBeamToUUID(To, color);
+ }
+
+ public void SetBeamToUUID(SceneObjectPart To, LLVector3 color)
+ {
+ SceneObjectPart From = m_Entity.RootPart;
+ //Scale size of particles to distance objects are apart (for better visibility)
+ LLVector3 FromPos = From.GetWorldPosition();
+ LLVector3 ToPos = From.GetWorldPosition();
+ LLUUID toUUID = To.UUID;
+ float distance = (float) (Math.Sqrt(Math.Pow(FromPos.X-ToPos.X, 2) +
+ Math.Pow(FromPos.X-ToPos.Y, 2) +
+ Math.Pow(FromPos.X-ToPos.Z, 2)
+ )
+ );
+ //float rate = (float) (distance/4f);
+ float rate = 0.5f;
+ float scale = (float) (distance/128f);
+ float speed = (float) (2.0f - distance/128f);
+
+ SetBeamToUUID(From, To, color, rate, scale, speed);
+ }
+
+ public void SetBeamToUUID(SceneObjectPart From, SceneObjectPart To, LLVector3 color, float rate, float scale, float speed)
+ {
+ Primitive.ParticleSystem prules = new Primitive.ParticleSystem();
+ //prules.PartDataFlags = Primitive.ParticleSystem.ParticleDataFlags.Emissive |
+ // Primitive.ParticleSystem.ParticleDataFlags.FollowSrc; //PSYS_PART_FLAGS
+ prules.PartDataFlags = Primitive.ParticleSystem.ParticleDataFlags.Beam |
+ Primitive.ParticleSystem.ParticleDataFlags.TargetPos;
+ prules.PartStartColor.R = color.X; //PSYS_PART_START_COLOR
+ prules.PartStartColor.G = color.Y;
+ prules.PartStartColor.B = color.Z;
+ prules.PartStartColor.A = 1.0f; //PSYS_PART_START_ALPHA, transparency
+ prules.PartEndColor.R = color.X; //PSYS_PART_END_COLOR
+ prules.PartEndColor.G = color.Y;
+ prules.PartEndColor.B = color.Z;
+ prules.PartEndColor.A = 1.0f; //PSYS_PART_END_ALPHA, transparency
+ prules.PartStartScaleX = scale; //PSYS_PART_START_SCALE
+ prules.PartStartScaleY = scale;
+ prules.PartEndScaleX = scale; //PSYS_PART_END_SCALE
+ prules.PartEndScaleY = scale;
+ prules.PartMaxAge = 1.0f; //PSYS_PART_MAX_AGE
+ prules.PartAcceleration.X = 0.0f; //PSYS_SRC_ACCEL
+ prules.PartAcceleration.Y = 0.0f;
+ prules.PartAcceleration.Z = 0.0f;
+ //prules.Pattern = Primitive.ParticleSystem.SourcePattern.Explode; //PSYS_SRC_PATTERN
+ //prules.Texture = LLUUID.Zero;//= LLUUID //PSYS_SRC_TEXTURE, default used if blank
+ prules.BurstRate = rate; //PSYS_SRC_BURST_RATE
+ prules.BurstPartCount = 1; //PSYS_SRC_BURST_PART_COUNT
+ prules.BurstRadius = 0.5f; //PSYS_SRC_BURST_RADIUS
+ prules.BurstSpeedMin = speed; //PSYS_SRC_BURST_SPEED_MIN
+ prules.BurstSpeedMax = speed; //PSYS_SRC_BURST_SPEED_MAX
+ prules.MaxAge = 0.0f; //PSYS_SRC_MAX_AGE
+ prules.Target = To.UUID; //PSYS_SRC_TARGET_KEY
+ prules.AngularVelocity.X = 0.0f; //PSYS_SRC_OMEGA
+ prules.AngularVelocity.Y = 0.0f;
+ prules.AngularVelocity.Z = 0.0f;
+ prules.InnerAngle = 0.0f; //PSYS_SRC_ANGLE_BEGIN
+ prules.OuterAngle = 0.0f; //PSYS_SRC_ANGLE_END
+
+ prules.CRC = 1; //activates the particle system??
+ From.AddNewParticleSystem(prules);
+ }
+ }
+}
diff --git a/OpenSim/Region/Environment/Modules/ContentManagementSystem/CMController.cs b/OpenSim/Region/Environment/Modules/ContentManagementSystem/CMController.cs
new file mode 100644
index 0000000000..465741f06a
--- /dev/null
+++ b/OpenSim/Region/Environment/Modules/ContentManagementSystem/CMController.cs
@@ -0,0 +1,684 @@
+// CMController.cs
+// User: bongiojp
+//
+
+using System;
+using System.Collections.Generic;
+using System.Collections;
+using libsecondlife;
+using OpenSim;
+using OpenSim.Framework;
+using OpenSim.Region.Environment.Interfaces;
+using OpenSim.Region.Environment.Scenes;
+using log4net;
+using OpenSim.Region.Physics.Manager;
+using Axiom.Math;
+using System.Threading;
+using System.Diagnostics;
+
+namespace OpenSim.Region.Environment.Modules.ContentManagement
+{
+
+ ///
+ /// The controller in a Model-View-Controller framework. This controller catches actions by the avatars, creates work packets, loops through these work packets in a separate thread,
+ /// then dictates to the model how the data should change and dictates to the view which data should be displayed. The main mechanism for interaction is through the simchat system.
+ ///
+ public class CMController
+ {
+
+ ///
+ /// The structure that defines the basic unit of work which is produced when a user sends commands to the ContentMangaementSystem.
+ ///
+ private struct Work
+ {
+ public WorkType Type;
+ public Object Data1; //Just space for holding data.
+ public Object Data2; //Just more space for holding data.
+ public uint LocalId; //Convenient
+ public LLUUID UUID; //Convenient
+ }
+
+ ///
+ /// Identifies what the data in struct Work should be used for.
+ ///
+ private enum WorkType
+ {
+ NONE,
+ OBJECTATTRIBUTECHANGE,
+ PRIMITIVEADDED,
+ OBJECTDUPLICATED,
+ OBJECTKILLED,
+ UNDODID,
+ NEWCLIENT,
+ SIMCHAT
+ }
+
+ ///
+ /// Used to keep track of whether a list has been produced yet and whether that list is up-to-date compard to latest revision on disk.
+ ///
+ [Flags]
+ private enum State
+ {
+ NONE = 0,
+ DIRTY = 1, // The meta entities may not correctly represent the last revision.
+ SHOWING_CHANGES = 1<<1 // The meta entities are being shown to user.
+ }
+
+ private static readonly log4net.ILog m_log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
+
+ ///
+ /// The queue that keeps track of which actions have happened. The MainLoop thread eats through this queue.
+ ///
+ private static OpenSim.Framework.BlockingQueue m_WorkQueue = new OpenSim.Framework.BlockingQueue();
+
+ ///
+ /// A list of all the scenes that should be revisioned. Controller is the only class that keeps track of all scenes in the region.
+ ///
+ Hashtable m_sceneList = Hashtable.Synchronized(new Hashtable());
+
+ ///
+ /// The estate module is used to identify which clients are estateManagers. Presently, the controller only pays attention to estate managers.
+ ///
+ IEstateModule m_estateModule = null;
+
+ Thread m_thread = null;
+ State m_state = State.NONE;
+ bool init = false;
+
+ //These have to be global variables, threading doesn't allow for passing parameters. (Used in MainLoop)
+ CMModel m_model = null;
+ CMView m_view = null;
+ int m_channel = -1;
+
+ ///
+ /// Initializes a work thread with an initial scene. Additional scenes should be added through the RegisterNewRegion method.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// The first scene to keep track of.
+ ///
+ ///
+ /// The simchat channel number to listen to for instructions
+ ///
+ public CMController(CMModel model, CMView view, Scene scene, int channel)
+ {
+ m_model = model; m_view = view; m_channel = channel;
+ RegisterNewRegion(scene);
+ Initialize(model, view, scene, channel);
+ }
+
+ private void Initialize(CMModel model, CMView view, Scene scene, int channel)
+ {
+ lock(this)
+ {
+ m_estateModule = scene.RequestModuleInterface();
+ m_thread = new Thread( MainLoop );
+ m_thread.Name = "Content Management";
+ m_thread.IsBackground = true;
+ m_thread.Start();
+ ThreadTracker.Add(m_thread);
+ m_state = State.NONE;
+ }
+ }
+
+ ///
+ /// Register a new scene object to keep track of for revisioning. Starts the controller monitoring actions of clients within the given scene.
+ ///
+ ///
+ /// A
+ ///
+ public void RegisterNewRegion(Scene scene)
+ {
+ m_sceneList.Add(scene.RegionInfo.RegionID, scene);
+
+ m_log.Debug("[CONTENT MANAGEMENT] Registering new region: " + scene.RegionInfo.RegionID);
+ m_log.Debug("[CONTENT MANAGEMENT] Initializing Content Management System.");
+
+ scene.EventManager.OnNewClient += StartManaging;
+ scene.EventManager.OnRemovePresence += StopManaging;
+ // scene.EventManager.OnAvatarEnteringNewParcel += AvatarEnteringParcel;
+ scene.EventManager.OnObjectBeingRemovedFromScene += GroupBeingDeleted;
+ }
+
+ ///
+ /// Run in a thread of its own. A endless loop that consumes (or blocks on) and work queue. Thw work queue is filled through client actions.
+ ///
+ private void MainLoop()
+ {
+ CMModel model = m_model; CMView view = m_view; int channel = m_channel;
+ Work currentJob = new Work();
+ while(true)
+ {
+ currentJob = m_WorkQueue.Dequeue();
+ m_log.Debug("[CONTENT MANAGEMENT] MAIN LOOP -- DeQueued a request");
+ m_log.Debug("[CONTENT MANAGEMENT] MAIN LOOP -- Work type: " + currentJob.Type);
+ switch(currentJob.Type)
+ {
+ case WorkType.NONE:
+ break;
+ case WorkType.OBJECTATTRIBUTECHANGE:
+ ObjectAttributeChanged(model, view, currentJob.LocalId);
+ break;
+ case WorkType.PRIMITIVEADDED:
+ PrimitiveAdded(model, view, currentJob);
+ break;
+ case WorkType.OBJECTDUPLICATED:
+ ObjectDuplicated(model, view, currentJob.LocalId);
+ break;
+ case WorkType.OBJECTKILLED:
+ ObjectKilled(model, view, (SceneObjectGroup) currentJob.Data1);
+ break;
+ case WorkType.UNDODID:
+ UndoDid(model, view, currentJob.UUID);
+ break;
+ case WorkType.NEWCLIENT:
+ NewClient(view, (IClientAPI) currentJob.Data1);
+ break;
+ case WorkType.SIMCHAT:
+ m_log.Debug("[CONTENT MANAGEMENT] MAIN LOOP -- Message received: " + ((ChatFromViewerArgs) currentJob.Data1).Message);
+ SimChat(model, view, (ChatFromViewerArgs) currentJob.Data1, channel);
+ break;
+ default:
+ m_log.Debug("[CONTENT MANAGEMENT] MAIN LOOP -- uuuuuuuuuh, what?");
+ break;
+ }
+ }
+ }
+
+ ///
+ /// Only called by the MainLoop. Updates the view of a new client with metaentities if diff-mode is currently enabled.
+ ///
+ private void NewClient(CMView view, IClientAPI client)
+ {
+ if ((m_state & State.SHOWING_CHANGES) > 0)
+ view.SendMetaEntitiesToNewClient(client);
+ }
+
+ ///
+ /// Only called by the MainLoop. Displays new green auras over the newly created part when a part is shift copied.
+ ///
+ private void ObjectDuplicated(CMModel model, CMView view, uint localId)
+ {
+ if ((m_state & State.SHOWING_CHANGES) > 0)
+ view.DisplayAuras(model.CheckForNewEntitiesMissingAuras( GetGroupByPrim(localId).Scene ));
+ }
+
+ ///
+ /// Only called by the MainLoop.
+ ///
+ private void ObjectKilled(CMModel model, CMView view, SceneObjectGroup group)
+ {
+ if ((m_state & State.SHOWING_CHANGES) > 0)
+ {
+ view.RemoveOrUpdateDeletedEntity(group);
+ model.RemoveOrUpdateDeletedEntity(group);
+ }
+ }
+
+ ///
+ /// Only called by the MainLoop.
+ ///
+ private void UndoDid(CMModel model, CMView view, LLUUID uuid)
+ {
+ if ((m_state & State.SHOWING_CHANGES) > 0)
+ {
+ ContentManagementEntity ent = model.FindMetaEntityAffectedByUndo(uuid);
+ if (ent != null)
+ view.DisplayEntity(ent);
+ }
+ }
+
+ ///
+ /// Only called by the MainLoop.
+ ///
+ private void ObjectAttributeChanged(CMModel model, CMView view, uint LocalId)
+ {
+ SceneObjectGroup group = null;
+ if ((m_state & State.SHOWING_CHANGES) > 0)
+ {
+ group = GetGroupByPrim(LocalId);
+ if (group != null)
+ {
+ view.DisplayAuras( model.UpdateNormalEntityEffects(group) ); //Might be a normal entity (green aura)
+ m_view.DisplayMetaEntity(group.UUID); //Might be a meta entity (blue aura)
+ }
+ }
+ }
+
+ ///
+ /// Only called by the MainLoop.
+ ///
+ private void PrimitiveAdded(CMModel model, CMView view, Work currentJob)
+ {
+ if ((m_state & State.SHOWING_CHANGES) > 0)
+ {
+ foreach(Object scene in m_sceneList.Values)
+ m_view.DisplayAuras(model.CheckForNewEntitiesMissingAuras((Scene) scene));
+ }
+ }
+
+ ///
+ /// Only called by the MainLoop. Takes the message from a user sent to the channel and executes the proper command.
+ ///
+ public void SimChat(CMModel model, CMView view, ChatFromViewerArgs e, int channel)
+ {
+ if (e.Channel != channel)
+ return;
+ if (e.Sender == null)
+ return;
+
+ m_log.Debug("[CONTENT MANAGEMENT] Message received: " + e.Message);
+
+ IClientAPI client = e.Sender;
+ Scene scene = (Scene) e.Scene;
+ string message = e.Message;
+ string[] args = e.Message.Split(new char[] {' '});
+
+ ScenePresence avatar = scene.GetScenePresence(client.AgentId);
+
+ if (!(m_estateModule.IsManager(avatar.UUID)))
+ {
+ m_log.Debug("[CONTENT MANAGEMENT] Message sent from non Estate Manager ... ignoring.");
+ view.SendSimChatMessage(scene, "You must be an estate manager to perform that action.");
+ return;
+ }
+
+ switch(args[0])
+ {
+ case "ci":
+ case "commit":
+ commit(message, scene, model, view);
+ break;
+ case "dm":
+ case "diff-mode":
+ diffmode(scene, model, view);
+ break;
+ case "rb":
+ case "rollback":
+ rollback(scene, model, view);
+ break;
+ case "help":
+ m_view.DisplayHelpMenu(scene);
+ break;
+ default:
+ view.SendSimChatMessage(scene, "Command not found: " + args[0]);
+ break;
+ }
+ }
+
+ ///
+ /// Only called from within the SimChat method. Hides all auras and meta entities,
+ /// retrieves the current scene object list with the most recent revision retrieved from the model for each scene,
+ /// then lets the view update the clients of the new objects.
+ ///
+ protected void rollback(Scene scene, CMModel model, CMView view)
+ {
+ if ((m_state & State.SHOWING_CHANGES) > 0)
+ {
+ view.HideAllAuras();
+ view.HideAllMetaEntities();
+ }
+
+ System.Collections.Generic.List proximitySceneList = ScenesInOrderOfProximity( m_sceneList, scene);
+ foreach(Scene currScene in proximitySceneList)
+ model.RollbackRegion(currScene);
+
+ if ((m_state & State.DIRTY) != 0 )
+ {
+ model.DeleteAllMetaObjects();
+ foreach(Scene currScene in proximitySceneList)
+ model.UpdateCMEntities(currScene);
+ }
+
+ if ((m_state & State.SHOWING_CHANGES) > 0)
+ view.DisplayRecentChanges();
+
+ }
+
+ ///
+ /// Only called from within the SimChat method.
+ ///
+ protected void diffmode(Scene scene, CMModel model, CMView view)
+ {
+ System.Collections.Generic.List proximitySceneList = ScenesInOrderOfProximity( m_sceneList, scene);
+
+ if ((m_state & State.SHOWING_CHANGES) > 0) // TURN OFF
+ {
+ view.SendSimChatMessage(scene, "Hiding all meta objects.");
+ view.HideAllMetaEntities();
+ view.HideAllAuras();
+ view.SendSimChatMessage(scene, "Diff-mode = OFF");
+
+ m_state &= ~State.SHOWING_CHANGES;
+ return;
+ }
+ else // TURN ON
+ {
+ if ((m_state & State.DIRTY) != 0 || m_state == State.NONE)
+ {
+ view.SendSimChatMessage(scene, "Hiding meta objects and replacing with latest revision");
+ //Hide objects from users and Forget about them
+ view.HideAllMetaEntities();
+ view.HideAllAuras();
+ model.DeleteAllMetaObjects();
+ //Recreate them from backend files
+ foreach(Object currScene in m_sceneList.Values)
+ model.UpdateCMEntities((Scene) currScene);
+ }
+ else if ((m_state & State.DIRTY) != 0) {
+ view.SendSimChatMessage(scene, "Forming list of meta entities with latest revision");
+ foreach(Scene currScene in proximitySceneList)
+ model.UpdateCMEntities(currScene);
+ }
+
+ view.SendSimChatMessage(scene, "Displaying differences between last revision and current environment");
+ foreach(Scene currScene in proximitySceneList)
+ model.CheckForNewEntitiesMissingAuras(currScene);
+ view.DisplayRecentChanges();
+
+ view.SendSimChatMessage(scene, "Diff-mode = ON");
+ m_state |= State.SHOWING_CHANGES;
+ m_state &= ~State.DIRTY;
+ }
+ }
+
+ ///
+ /// Only called from within the SimChat method.
+ ///
+ protected void commit(string message, Scene scene, CMModel model, CMView view)
+ {
+ System.Collections.Generic.List proximitySceneList = ScenesInOrderOfProximity( m_sceneList, scene);
+
+ string[] args = message.Split(new char[] {' '});
+
+ char[] logMessage = {' '};
+ if (args.Length > 1)
+ {
+ logMessage = new char[message.Length - (args[0].Length)];
+ message.CopyTo(args[0].Length, logMessage, 0, message.Length - (args[0].Length));
+ }
+
+ m_log.Debug("[CONTENT MANAGEMENT] Saving terrain and objects of region.");
+ foreach(Scene currScene in proximitySceneList)
+ {
+ model.CommitRegion(currScene, new String(logMessage));
+ view.SendSimChatMessage(scene, "Region Saved Successfully: " + currScene.RegionInfo.RegionName);
+ }
+
+ view.SendSimChatMessage(scene, "Successfully saved all regions.");
+ m_state |= State.DIRTY;
+
+ if ((m_state & State.SHOWING_CHANGES) > 0) //DISPLAY NEW CHANGES INSTEAD OF OLD CHANGES
+ {
+ view.SendSimChatMessage(scene, "Updating differences between new revision and current environment.");
+ //Hide objects from users and Forget about them
+ view.HideAllMetaEntities();
+ view.HideAllAuras();
+ model.DeleteAllMetaObjects();
+
+ //Recreate them from backend files
+ foreach(Scene currScene in proximitySceneList)
+ {
+ model.UpdateCMEntities(currScene);
+ view.SendSimChatMessage(scene, "Finished updating differences between current scene and last revision: " + currScene.RegionInfo.RegionName);
+ }
+
+ //Display new objects to users1
+ view.DisplayRecentChanges();
+ view.SendSimChatMessage(scene, "Finished updating for DIFF-MODE.");
+ m_state &= ~(State.DIRTY);
+ m_state |= State.SHOWING_CHANGES;
+ }
+ }
+
+ ///
+ /// Takes a list of scenes and forms a new orderd list according to the proximity of scenes to the second argument.
+ ///
+ protected static System.Collections.Generic.List ScenesInOrderOfProximity( Hashtable sceneList, Scene scene)
+ {
+ int somethingAddedToList = 1;
+ System.Collections.Generic.List newList = new List();
+ newList.Add(scene);
+
+ if (! sceneList.ContainsValue(scene))
+ {
+ foreach(Object sceneObj in sceneList)
+ newList.Add((Scene) sceneObj);
+ return newList;
+ }
+
+ while(somethingAddedToList > 0)
+ {
+ somethingAddedToList = 0;
+ for(int i = 0; i < newList.Count; i++)
+ {
+ foreach(Object sceneObj in sceneList.Values)
+ {
+ if (newList[i].CheckNeighborRegion(((Scene)sceneObj).RegionInfo) && (! newList.Contains((Scene)sceneObj)) )
+ {
+ newList.Add((Scene)sceneObj);
+ somethingAddedToList++;
+ }
+ }
+ }
+ }
+
+ foreach(Object sceneObj in sceneList.Values)
+ if (! newList.Contains((Scene)sceneObj))
+ newList.Add((Scene)sceneObj);
+
+ return newList;
+ }
+
+ ///
+ /// Searches in all scenes for a SceneObjectGroup that contains a part with a specific localID. If found, the object is returned. Else null is returned.
+ ///
+ private SceneObjectGroup GetGroupByPrim(uint localID)
+ {
+ foreach(Object currScene in m_sceneList.Values)
+ {
+ foreach (EntityBase ent in ((Scene)currScene).GetEntities())
+ {
+ if (ent is SceneObjectGroup)
+ {
+ if (((SceneObjectGroup)ent).HasChildPrim(localID))
+ return (SceneObjectGroup)ent;
+ }
+ }
+ }
+ return null;
+ }
+ //------------------------------------------------ EVENTS ----------------------------------------------------//
+
+ private void AvatarEnteringParcel(ScenePresence avatar, int localLandID, LLUUID regionID)
+ {
+ }
+
+ ///
+ /// Adds extra handlers to a number of events so that the controller can produce work based on the client's actions.
+ ///
+ protected void StartManaging(IClientAPI client)
+ {
+ m_log.Debug("[CONTENT MANAGEMENT] Registering channel with chat services.");
+ client.OnChatFromViewer += SimChatSent;
+ init = true;
+
+ OnNewClient(client);
+
+ m_log.Debug("[CONTENT MANAGEMENT] Adding handlers to client.");
+ client.OnUpdatePrimScale += UpdateSingleScale;
+ client.OnUpdatePrimGroupScale += UpdateMultipleScale;
+ client.OnUpdatePrimGroupPosition += UpdateMultiplePosition;
+ client.OnUpdatePrimSinglePosition += UpdateSinglePosition;
+ client.OnUpdatePrimGroupRotation += UpdateMultipleRotation;
+ client.OnUpdatePrimSingleRotation += UpdateSingleRotation;
+ client.OnAddPrim += UpdateNewParts;
+ client.OnObjectDuplicate += ObjectDuplicated;
+ client.OnObjectDuplicateOnRay += ObjectDuplicatedOnRay;
+ client.OnUndo += OnUnDid;
+ //client.OnUpdatePrimGroupMouseRotation += m_innerScene.UpdatePrimRotation;
+ }
+
+ ///
+ ///
+ ///
+ protected void StopManaging(LLUUID clientUUID)
+ {
+ foreach(Object sceneobj in m_sceneList.Values)
+ {
+ ScenePresence presence = ((Scene)sceneobj).GetScenePresence(clientUUID);
+ if (presence != null)
+ {
+ IClientAPI client = presence.ControllingClient;
+ m_log.Debug("[CONTENT MANAGEMENT] Unregistering channel with chat services.");
+ client.OnChatFromViewer -= SimChatSent;
+
+ m_log.Debug("[CONTENT MANAGEMENT] Removing handlers to client");
+ client.OnUpdatePrimScale -= UpdateSingleScale;
+ client.OnUpdatePrimGroupScale -= UpdateMultipleScale;
+ client.OnUpdatePrimGroupPosition -= UpdateMultiplePosition;
+ client.OnUpdatePrimSinglePosition -= UpdateSinglePosition;
+ client.OnUpdatePrimGroupRotation -= UpdateMultipleRotation;
+ client.OnUpdatePrimSingleRotation -= UpdateSingleRotation;
+ client.OnAddPrim -= UpdateNewParts;
+ client.OnObjectDuplicate -= ObjectDuplicated;
+ client.OnObjectDuplicateOnRay -= ObjectDuplicatedOnRay;
+ client.OnUndo -= OnUnDid;
+ //client.OnUpdatePrimGroupMouseRotation += m_innerScene.UpdatePrimRotation;
+ return;
+ }
+ }
+ }
+
+ protected void GroupBeingDeleted(SceneObjectGroup group)
+ {
+ m_log.Debug("[CONTENT MANAGEMENT] Something was deleted!!!");
+ Work moreWork = new Work();
+ moreWork.Type = WorkType.OBJECTKILLED;
+ moreWork.Data1 = group.Copy();
+ m_WorkQueue.Enqueue(moreWork);
+ }
+
+ //This is stupid, the same information is contained in the first and second argument
+ protected void SimChatSent(Object x, ChatFromViewerArgs e)
+ {
+ m_log.Debug("[CONTENT MANAGEMENT] SIMCHAT SENT !!!!!!!");
+ m_log.Debug("[CONTENT MANAGEMENT] message was: " + e.Message);
+ Work moreWork = new Work();
+ moreWork.Type = WorkType.SIMCHAT;
+ moreWork.Data1 = e;
+ m_WorkQueue.Enqueue(moreWork);
+ }
+
+ protected void ObjectDuplicated(uint localID, LLVector3 offset, uint dupeFlags, LLUUID AgentID, LLUUID GroupID)
+ {
+ Work moreWork = new Work();
+ moreWork.Type = WorkType.OBJECTDUPLICATED;
+ moreWork.LocalId = localID;
+ m_WorkQueue.Enqueue(moreWork);
+ m_log.Debug("[CONTENT MANAGEMENT] dup queue");
+ }
+
+ protected void ObjectDuplicatedOnRay(uint localID, uint dupeFlags, LLUUID AgentID, LLUUID GroupID,
+ LLUUID RayTargetObj, LLVector3 RayEnd, LLVector3 RayStart,
+ bool BypassRaycast, bool RayEndIsIntersection, bool CopyCenters, bool CopyRotates)
+ {
+ Work moreWork = new Work();
+ moreWork.Type = WorkType.OBJECTDUPLICATED;
+ moreWork.LocalId = localID;
+ m_WorkQueue.Enqueue(moreWork);
+ m_log.Debug("[CONTENT MANAGEMENT] dup queue");
+ }
+
+ protected void OnNewClient(IClientAPI client)
+ {
+ Work moreWork = new Work();
+ moreWork.Type = WorkType.NEWCLIENT;
+ moreWork.Data1 = client;
+ m_WorkQueue.Enqueue(moreWork);
+ m_log.Debug("[CONTENT MANAGEMENT] new client");
+ }
+
+ protected void OnUnDid(IClientAPI remoteClient, LLUUID primId)
+ {
+ Work moreWork = new Work();
+ moreWork.Type = WorkType.UNDODID;
+ moreWork.UUID = primId;
+ m_WorkQueue.Enqueue(moreWork);
+ m_log.Debug("[CONTENT MANAGEMENT] undid");
+ }
+
+ protected void UpdateSinglePosition(uint localID, LLVector3 pos, IClientAPI remoteClient)
+ {
+ Work moreWork = new Work();
+ moreWork.Type = WorkType.OBJECTATTRIBUTECHANGE;
+ moreWork.LocalId = localID;
+ m_WorkQueue.Enqueue(moreWork);
+ m_log.Debug("[CONTENT MANAGEMENT] move");
+ }
+
+ ///
+ ///
+ ///
+ protected void UpdateSingleRotation(uint localID, LLQuaternion rot, IClientAPI remoteClient)
+ {
+ Work moreWork = new Work();
+ moreWork.Type = WorkType.OBJECTATTRIBUTECHANGE;
+ moreWork.LocalId = localID;
+ m_WorkQueue.Enqueue(moreWork);
+ m_log.Debug("[CONTENT MANAGEMENT] rot");
+ }
+
+ protected void UpdateSingleScale(uint localID, LLVector3 scale, IClientAPI remoteClient)
+ {
+ Work moreWork = new Work();
+ moreWork.Type = WorkType.OBJECTATTRIBUTECHANGE;
+ moreWork.LocalId = localID;
+ m_WorkQueue.Enqueue(moreWork);
+ m_log.Debug("[CONTENT MANAGEMENT] scale");
+ }
+
+ protected void UpdateMultiplePosition(uint localID, LLVector3 pos, IClientAPI remoteClient)
+ {
+ Work moreWork = new Work();
+ moreWork.Type = WorkType.OBJECTATTRIBUTECHANGE;
+ moreWork.LocalId = localID;
+ m_WorkQueue.Enqueue(moreWork);
+ m_log.Debug("[CONTENT MANAGEMENT] pos");
+ }
+
+ protected void UpdateMultipleRotation(uint localID, LLQuaternion rot, IClientAPI remoteClient)
+ {
+ Work moreWork = new Work();
+ moreWork.Type = WorkType.OBJECTATTRIBUTECHANGE;
+ moreWork.LocalId = localID;
+ m_WorkQueue.Enqueue(moreWork);
+ m_log.Debug("[CONTENT MANAGEMENT] rot");
+ }
+
+ protected void UpdateMultipleScale(uint localID, LLVector3 scale, IClientAPI remoteClient)
+ {
+ Work moreWork = new Work();
+ moreWork.Type = WorkType.OBJECTATTRIBUTECHANGE;
+ moreWork.LocalId = localID;
+ m_WorkQueue.Enqueue(moreWork);
+ m_log.Debug("[CONTENT MANAGEMENT]scale");
+ }
+
+ protected void UpdateNewParts(LLUUID ownerID, LLVector3 RayEnd, LLQuaternion rot, PrimitiveBaseShape shape,
+ byte bypassRaycast, LLVector3 RayStart, LLUUID RayTargetID,
+ byte RayEndIsIntersection)
+ {
+ Work moreWork = new Work();
+ moreWork.Type = WorkType.PRIMITIVEADDED;
+ moreWork.UUID = ownerID;
+ m_WorkQueue.Enqueue(moreWork);
+ m_log.Debug("[CONTENT MANAGEMENT] new parts");
+ }
+ }
+}
diff --git a/OpenSim/Region/Environment/Modules/ContentManagementSystem/CMEntityCollection.cs b/OpenSim/Region/Environment/Modules/ContentManagementSystem/CMEntityCollection.cs
new file mode 100644
index 0000000000..9f50e239fb
--- /dev/null
+++ b/OpenSim/Region/Environment/Modules/ContentManagementSystem/CMEntityCollection.cs
@@ -0,0 +1,148 @@
+// CMEntityCollection.cs created with MonoDevelop
+// User: bongiojp at 10:09 AM 7/7/2008
+//
+// Creates, Deletes, Stores ContentManagementEntities
+//
+
+
+using System;
+using System.Collections.Generic;
+using System.Collections;
+using libsecondlife;
+using Nini.Config;
+using OpenSim;
+using OpenSim.Framework;
+using OpenSim.Region.Environment.Interfaces;
+using OpenSim.Region.Environment.Scenes;
+using log4net;
+using OpenSim.Region.Physics.Manager;
+using Axiom.Math;
+using System.Threading;
+
+namespace OpenSim.Region.Environment.Modules.ContentManagement
+{
+
+ public class CMEntityCollection
+ {
+ // private static readonly log4net.ILog m_log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
+
+ // Any ContentManagementEntities that represent old versions of current SceneObjectGroups or
+ // old versions of deleted SceneObjectGroups will be stored in this hash table.
+ // The LLUUID keys are from the SceneObjectGroup RootPart UUIDs
+ protected Hashtable m_CMEntityHash = Hashtable.Synchronized(new Hashtable()); //LLUUID to ContentManagementEntity
+
+ // SceneObjectParts that have not been revisioned will be given green auras stored in this hashtable
+ // The LLUUID keys are from the SceneObjectPart that they are supposed to be on.
+ protected Hashtable m_NewlyCreatedEntityAura = Hashtable.Synchronized(new Hashtable()); //LLUUID to AuraMetaEntity
+
+ public Hashtable Entities
+ {
+ get { return m_CMEntityHash; }
+ }
+
+ public Hashtable Auras
+ {
+ get {return m_NewlyCreatedEntityAura; }
+ }
+
+ public CMEntityCollection()
+ {}
+
+ public bool AddAura(ContentManagementEntity aura)
+ {
+ if (m_NewlyCreatedEntityAura.ContainsKey(aura.UUID))
+ return false;
+ m_NewlyCreatedEntityAura.Add(aura.UUID, aura);
+ return true;
+ }
+
+ public bool AddEntity(ContentManagementEntity ent)
+ {
+ if (m_CMEntityHash.ContainsKey(ent.UUID))
+ return false;
+ m_CMEntityHash.Add(ent.UUID, ent);
+ return true;
+ }
+
+ public bool RemoveNewlyCreatedEntityAura(LLUUID uuid)
+ {
+ if (!m_NewlyCreatedEntityAura.ContainsKey(uuid))
+ return false;
+ m_NewlyCreatedEntityAura.Remove(uuid);
+ return true;
+ }
+
+ public bool RemoveEntity(LLUUID uuid)
+ {
+ if (!m_CMEntityHash.ContainsKey(uuid))
+ return false;
+ m_CMEntityHash.Remove(uuid);
+ return true;
+ }
+
+ public void ClearAll()
+ {
+ m_CMEntityHash.Clear();
+ m_NewlyCreatedEntityAura.Clear();
+ }
+
+
+
+ // Old uuid and new sceneobjectgroup
+ public AuraMetaEntity CreateAuraForNewlyCreatedEntity(SceneObjectPart part)
+ {
+ AuraMetaEntity ent = new AuraMetaEntity(part.ParentGroup.Scene,
+ part.ParentGroup.Scene.PrimIDAllocate(),
+ part.GetWorldPosition(),
+ MetaEntity.TRANSLUCENT,
+ new LLVector3(0,254,0),
+ part.Scale
+ );
+ m_NewlyCreatedEntityAura.Add(part.UUID, ent);
+ return ent;
+ }
+
+ // Old uuid and new sceneobjectgroup
+ public ContentManagementEntity CreateNewEntity(SceneObjectGroup group)
+ {
+ ContentManagementEntity ent = new ContentManagementEntity(group, false);
+ m_CMEntityHash.Add(group.UUID, ent);
+ return ent;
+ }
+
+ public ContentManagementEntity CreateNewEntity(String xml, Scene scene)
+ {
+ ContentManagementEntity ent = new ContentManagementEntity(xml, scene, false);
+ if (ent == null)
+ return null;
+ m_CMEntityHash.Add(ent.UnchangedEntity.UUID, ent);
+ return ent;
+ }
+
+ // Check if there are SceneObjectGroups in the list that do not have corresponding ContentManagementGroups in the CMEntityHash
+ public System.Collections.ArrayList CheckForMissingEntities(System.Collections.Generic.List currList)
+ {
+ System.Collections.ArrayList missingList = new System.Collections.ArrayList();
+ SceneObjectGroup temp = null;
+ foreach( EntityBase currObj in currList )
+ {
+ if (! (currObj is SceneObjectGroup))
+ continue;
+ temp = (SceneObjectGroup) currObj;
+
+ if (m_CMEntityHash.ContainsKey(temp.UUID))
+ {
+ foreach(SceneObjectPart part in temp.Children.Values)
+ if (!((ContentManagementEntity)m_CMEntityHash[temp.UUID]).HasChildPrim(part.UUID))
+ missingList.Add(part);
+ }
+ else //Entire group is missing from revision. (and is a new part in region)
+ {
+ foreach(SceneObjectPart part in temp.Children.Values)
+ missingList.Add(part);
+ }
+ }
+ return missingList;
+ }
+ }
+}
diff --git a/OpenSim/Region/Environment/Modules/ContentManagementSystem/CMModel.cs b/OpenSim/Region/Environment/Modules/ContentManagementSystem/CMModel.cs
new file mode 100644
index 0000000000..d3b3b77606
--- /dev/null
+++ b/OpenSim/Region/Environment/Modules/ContentManagementSystem/CMModel.cs
@@ -0,0 +1,309 @@
+// CMModel.cs
+// User: bongiojp
+//
+//
+
+using System;
+using System.Collections.Generic;
+using System.Collections;
+using libsecondlife;
+using OpenSim;
+using OpenSim.Framework;
+using OpenSim.Region.Environment.Interfaces;
+using OpenSim.Region.Environment.Scenes;
+using log4net;
+using OpenSim.Region.Physics.Manager;
+using Axiom.Math;
+using System.Diagnostics;
+
+namespace OpenSim.Region.Environment.Modules.ContentManagement
+{
+
+ public class CMModel
+ {
+ static float TimeToUpdate = 0;
+ static float TimeToConvertXml = 0;
+
+ private static readonly log4net.ILog m_log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
+
+ IContentDatabase m_database = null;
+
+ ///
+ /// The class that contains all auras and metaentities used in the CMS.
+ ///
+ CMEntityCollection m_MetaEntityCollection = new CMEntityCollection();
+
+ public CMEntityCollection MetaEntityCollection
+ {
+ get { return m_MetaEntityCollection; }
+ }
+
+ public CMModel()
+ {
+ }
+
+ public void Initialise(string database)
+ {
+ if (database == "FileSystemDatabase")
+ m_database = new FileSystemDatabase();
+ else if (database == "GitDatabase")
+ m_database = new GitDatabase();
+ }
+
+ public void InitialiseDatabase(Scene scene, string dir)
+ {
+ m_database.Initialise(scene, dir);
+ }
+
+ ///
+ /// Should be called just once to finish initializing the database.
+ ///
+ public void PostInitialise()
+ {
+ m_database.PostInitialise();
+ }
+
+ public ContentManagementEntity FindMetaEntityAffectedByUndo(LLUUID uuid)
+ {
+ ContentManagementEntity ent = GetMetaGroupByPrim(uuid);
+ return ent;
+ }
+
+ ///
+ /// Removes the green aura when an a new scene object group is deleted.
+ ///
+ public void RemoveOrUpdateDeletedEntity(SceneObjectGroup group)
+ {
+ // Deal with new parts not revisioned that have been deleted.
+ foreach(SceneObjectPart part in group.Children.Values)
+ if (m_MetaEntityCollection.Auras.ContainsKey(part.UUID))
+ m_MetaEntityCollection.RemoveNewlyCreatedEntityAura(part.UUID);
+ }
+
+ ///
+ /// Uses the database to serialize all current scene objects into xml and save into a database with an accompanying log message.
+ ///
+ public void CommitRegion(Scene scene, String logMessage)
+ {
+ m_log.Debug("[CONTENT MANAG] saving " + scene.RegionInfo.RegionName + " with log message: " + logMessage + " length of message: " + logMessage.Length);
+ m_database.SaveRegion(scene.RegionInfo.RegionID, scene.RegionInfo.RegionName, logMessage);
+ m_log.Debug("[CONTENT MANAG] the region name we are dealing with heeeeeeeere: " + scene.RegionInfo.RegionName );
+ }
+
+ ///
+ /// Retrieves the latest revision of a region in xml form,
+ /// converts it to scene object groups and scene presences,
+ /// swaps the current scene's entity list with the revision's list.
+ /// Note: Since deleted objects while
+ ///
+ public void RollbackRegion(Scene scene)
+ {
+ System.Collections.ArrayList xmllist = null;
+ SceneObjectGroup temp = null;
+ System.Collections.Hashtable deleteListUUIDs = new Hashtable();
+ Dictionary SearchList = new Dictionary();
+ Dictionary ReplacementList = new Dictionary();
+ int revision = m_database.GetMostRecentRevision(scene.RegionInfo.RegionID);
+ EntityBase[] searchArray;
+
+ xmllist = m_database.GetRegionObjectXMLList(scene.RegionInfo.RegionID, revision);
+ if (xmllist == null)
+ {
+ m_log.Info("[CMMODEL]: Region (" + scene.RegionInfo.RegionID + ") does not have given revision number (" + revision + ").");
+ return;
+ }
+
+ m_log.Info("[CMMODEL]: Region (" + scene.RegionInfo.RegionID + ") revision number (" + revision + ").");
+ m_log.Info("[CMMODEL]: Scene Objects = " + xmllist.Count);
+ m_log.Info("[CMMODEL]: Converting scene entities list to specified revision.");
+
+ m_log.ErrorFormat("[CMMODEL]: 1");
+
+ foreach (string xml in xmllist)
+ {
+ try{
+ temp = new SceneObjectGroup(xml);
+ temp.SetScene(scene);
+ foreach(SceneObjectPart part in temp.Children.Values)
+ part.RegionHandle = scene.RegionInfo.RegionHandle;
+ ReplacementList.Add(temp.UUID, (EntityBase)temp);
+ }
+ catch(Exception e)
+ {
+ m_log.Info("[CMMODEL]: Error while creating replacement list for rollback: " + e);
+ }
+ }
+
+ //If in scene but not in revision and not a client, remove them
+ while (true)
+ {
+ try
+ {
+ foreach(EntityBase entity in scene.GetEntities())
+ {
+ if (entity == null)
+ continue;
+
+ if (entity is ScenePresence)
+ {
+ ReplacementList.Add(entity.UUID, entity);
+ continue;
+ }
+ else //if (!ReplacementList.ContainsKey(entity.UUID))
+ deleteListUUIDs.Add(entity.UUID, 0);
+ }
+ }
+ catch(Exception e)
+ {
+ m_log.ErrorFormat("[CMMODEL]: " + e);
+ deleteListUUIDs.Clear();
+ ReplacementList.Clear();
+ continue;
+ }
+ break;
+ }
+
+ foreach(LLUUID uuid in deleteListUUIDs.Keys)
+ {
+ try
+ {
+ // I thought that the DeleteGroup() function would handle all of this, but it doesn't. I'm not sure WHAT it handles.
+ ((SceneObjectGroup)scene.Entities[uuid]).DetachFromBackup((SceneObjectGroup)scene.Entities[uuid]);
+ scene.PhysicsScene.RemovePrim(((SceneObjectGroup)scene.Entities[uuid]).RootPart.PhysActor);
+ scene.SendKillObject(scene.Entities[uuid].LocalId);
+ scene.m_innerScene.DeleteSceneObject(uuid, false);
+ ((SceneObjectGroup)scene.Entities[uuid]).DeleteGroup();
+ }
+ catch(Exception e)
+ {
+ m_log.ErrorFormat("[CMMODEL]: Error while removing objects from scene: " + e);
+ }
+ }
+
+ lock (scene)
+ {
+ scene.Entities = ReplacementList;
+ }
+
+ foreach(EntityBase ent in ReplacementList.Values)
+ {
+ try
+ {
+ if (!(ent is SceneObjectGroup))
+ continue;
+
+ if ((((SceneObjectGroup)ent).RootPart.GetEffectiveObjectFlags() & (uint) LLObject.ObjectFlags.Phantom) == 0)
+ ((SceneObjectGroup)ent).ApplyPhysics(true);
+ ((SceneObjectGroup)ent).AttachToBackup();
+ ((SceneObjectGroup)ent).HasGroupChanged = true; // If not true, then attaching to backup does nothing because no change is detected.
+ ((SceneObjectGroup)ent).ScheduleGroupForFullUpdate();
+ }
+ catch(Exception e)
+ {
+ m_log.ErrorFormat("[CMMODEL]: Error while attaching new scene entities to backup and scheduling for a full update: " + e);
+ }
+ }
+ m_log.Info("[CMMODEL]: Scheduling a backup of new scene object groups to backup.");
+ scene.Backup();
+ }
+
+ ///
+ /// Detects if a scene object group from the scene list has moved or changed scale. The green aura
+ /// that surrounds the object is then moved or scaled with the group.
+ ///
+ public System.Collections.ArrayList UpdateNormalEntityEffects(SceneObjectGroup group)
+ {
+ System.Collections.ArrayList auraList = new System.Collections.ArrayList();
+ if (group == null)
+ return null;
+ foreach(SceneObjectPart part in group.Children.Values)
+ {
+ if (m_MetaEntityCollection.Auras.ContainsKey(part.UUID))
+ {
+ ((AuraMetaEntity)m_MetaEntityCollection.Auras[part.UUID]).SetAura(new LLVector3(0,254,0), part.Scale);
+ ((AuraMetaEntity)m_MetaEntityCollection.Auras[part.UUID]).RootPart.GroupPosition = part.GetWorldPosition();
+ auraList.Add((AuraMetaEntity)m_MetaEntityCollection.Auras[part.UUID]);
+ }
+ }
+ return auraList;
+ }
+
+ public void DeleteAllMetaObjects()
+ {
+ m_MetaEntityCollection.ClearAll();
+ }
+
+ ///
+ /// Downloads the latest revision of the given scene and converts the xml file to CMEntities. After this method, the view can find the differences
+ /// and display the differences to clients.
+ ///
+ public void UpdateCMEntities(Scene scene)
+ {
+ Stopwatch x = new Stopwatch();
+ x.Start();
+
+ System.Collections.ArrayList xmllist = null;
+ m_log.Debug("[CONTENT MANAGEMENT] Retrieving object xml files for region: " + scene.RegionInfo.RegionID);
+ xmllist = m_database.GetRegionObjectXMLList(scene.RegionInfo.RegionID);
+ m_log.Info("[FSDB]: got list");
+ if (xmllist == null)
+ return;
+
+ Stopwatch y = new Stopwatch();
+ y.Start();
+ foreach (string xml in xmllist)
+ m_MetaEntityCollection.CreateNewEntity(xml, scene);
+ y.Stop();
+ TimeToConvertXml += y.ElapsedMilliseconds;
+ m_log.Info("[FileSystemDatabase] Time spent converting xml to metaentities for " + scene.RegionInfo.RegionName + ": " + y.ElapsedMilliseconds);
+ m_log.Info("[FileSystemDatabase] Time spent converting xml to metaentities so far: " + TimeToConvertXml);
+
+ m_log.Info("[FSDB]: checking for new scene object parts missing green auras and create the auras");
+ CheckForNewEntitiesMissingAuras(scene);
+
+ x.Stop();
+ TimeToUpdate += x.ElapsedMilliseconds;
+ m_log.Info("[FileSystemDatabase] Time spent Updating entity list for " + scene.RegionInfo.RegionName + ": " + x.ElapsedMilliseconds);
+ m_log.Info("[FileSystemDatabase] Time spent Updating so far: " + TimeToUpdate);
+
+ }
+
+ ///
+ /// Compares the scene's object group list to the list of meta entities. If there is an object group that does not have a corresponding meta entity
+ /// it is a new part that must have a green aura (for diff mode).
+ /// Returns list of ContentManagementEntities
+ ///
+ public ArrayList CheckForNewEntitiesMissingAuras(Scene scene)
+ {
+ ArrayList missingList = null;
+ ArrayList newList = new ArrayList();
+
+ m_log.Debug("[CONTENT MANAGEMENT] Checking for new scene object parts in scene: " + scene.RegionInfo.RegionName);
+
+ //Check if the current scene has groups not included in the current list of MetaEntities
+ //If so, then the current scene's parts that are new should be marked green.
+ missingList = m_MetaEntityCollection.CheckForMissingEntities(scene.GetEntities());
+
+ foreach(Object missingPart in missingList)
+ {
+ if (m_MetaEntityCollection.Auras.ContainsKey(((SceneObjectPart)missingPart).UUID))
+ continue;
+ newList.Add(m_MetaEntityCollection.CreateAuraForNewlyCreatedEntity((SceneObjectPart)missingPart));
+ }
+ m_log.Info("Number of missing objects found: " + newList.Count);
+ return newList;
+ }
+
+//-------------------------------- HELPERS --------------------------------------------------------------------//
+
+ public ContentManagementEntity GetMetaGroupByPrim(LLUUID uuid)
+ {
+ foreach (Object ent in m_MetaEntityCollection.Entities.Values)
+ {
+ if (((ContentManagementEntity)ent).HasChildPrim(uuid))
+ return (ContentManagementEntity)ent;
+ }
+ return null;
+ }
+ }
+}
\ No newline at end of file
diff --git a/OpenSim/Region/Environment/Modules/ContentManagementSystem/CMView.cs b/OpenSim/Region/Environment/Modules/ContentManagementSystem/CMView.cs
new file mode 100644
index 0000000000..f8b0ec9483
--- /dev/null
+++ b/OpenSim/Region/Environment/Modules/ContentManagementSystem/CMView.cs
@@ -0,0 +1,156 @@
+// CMView.cs created with MonoDevelop
+// User: bongiojp at 11:57 AM 7/3/2008
+//
+// To change standard headers go to Edit->Preferences->Coding->Standard Headers
+//
+
+using System;
+using System.Collections.Generic;
+using System.Collections;
+using libsecondlife;
+using OpenSim;
+using OpenSim.Framework;
+using OpenSim.Region.Environment.Interfaces;
+using OpenSim.Region.Environment.Scenes;
+using log4net;
+using OpenSim.Region.Physics.Manager;
+using Axiom.Math;
+
+namespace OpenSim.Region.Environment.Modules.ContentManagement
+{
+
+ public class CMView
+ {
+ private static readonly log4net.ILog m_log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
+ CMModel m_model = null;
+
+ public CMView()
+ {}
+
+ public void Initialise(CMModel model)
+ {
+ m_model = model;
+ }
+
+ public void SendMetaEntitiesToNewClient(IClientAPI client)
+ {
+ }
+
+ ///
+ /// update all clients of red/green/blue auras and meta entities that the model knows about.
+ ///
+ public void DisplayRecentChanges()
+ {
+ m_log.Debug("[CONTENT MANAGEMENT] Sending update to clients for " + m_model.MetaEntityCollection.Entities.Count + " objects.");
+ DisplayEntities(m_model.MetaEntityCollection);
+ DisplayAuras(m_model.MetaEntityCollection);
+ }
+
+ ///
+ /// Figures out if the part deleted was a new scene object part or a revisioned part that's been deleted.
+ /// If it's a new scene object, any green aura attached to it is deleted.
+ /// If a revisioned part is deleted, a new full update is sent to the environment of the meta entity, which will
+ /// figure out that there should be a red aura and not a blue aura/beam.
+ ///
+ public void RemoveOrUpdateDeletedEntity(SceneObjectGroup group)
+ {
+ // Deal with revisioned parts that have been deleted.
+ if (m_model.MetaEntityCollection.Entities.ContainsKey(group.UUID))
+ ((ContentManagementEntity)m_model.MetaEntityCollection.Entities[group.UUID]).SendFullDiffUpdateToAll();
+
+ // Deal with new parts not revisioned that have been deleted.
+ foreach(SceneObjectPart part in group.Children.Values)
+ if (m_model.MetaEntityCollection.Auras.ContainsKey(part.UUID))
+ ((AuraMetaEntity)m_model.MetaEntityCollection.Auras[part.UUID]).HideFromAll();
+ }
+
+ // Auras To
+ public void DisplayAuras(CMEntityCollection auraCollection)
+ {
+ foreach( Object ent in auraCollection.Auras.Values)
+ ((AuraMetaEntity)ent).SendFullUpdateToAll();
+ }
+
+ // Entities to ALL
+ public void DisplayEntities(CMEntityCollection entityCollection)
+ {
+ foreach( Object ent in entityCollection.Entities.Values)
+ ((ContentManagementEntity)ent).SendFullDiffUpdateToAll();
+ }
+
+ // Auras To Client
+ public void DisplayAuras(CMEntityCollection auraCollection, IClientAPI client)
+ {
+ foreach( Object ent in auraCollection.Auras.Values)
+ ((AuraMetaEntity)ent).SendFullUpdate(client);
+ }
+
+ // Entities to Client
+ public void DisplayEntities(CMEntityCollection entityCollection, IClientAPI client)
+ {
+ foreach( Object ent in entityCollection.Entities.Values)
+ ((ContentManagementEntity)ent).SendFullDiffUpdate(client);
+ }
+
+ // Entity to ALL
+ public void DisplayEntity(ContentManagementEntity ent)
+ {
+ ent.SendFullDiffUpdateToAll();
+ }
+
+ public void DisplayMetaEntity(LLUUID uuid)
+ {
+ ContentManagementEntity group = m_model.GetMetaGroupByPrim(uuid);
+ if (group != null)
+ group.SendFullDiffUpdateToAll();
+ }
+
+ // Auras from List To ALL
+ public void DisplayAuras(ArrayList list)
+ {
+ foreach( Object ent in list)
+ {
+ m_log.Debug("[CONTENT MANAGEMENT] displaying new aura riiiiiiiiiiiight NOW");
+ ((AuraMetaEntity)ent).SendFullUpdateToAll();
+ }
+ }
+
+ // Entities from List to ALL
+ public void DisplayEntities(ArrayList list)
+ {
+ foreach( Object ent in list)
+ ((ContentManagementEntity)ent).SendFullDiffUpdateToAll();
+ }
+
+ public void DisplayHelpMenu(Scene scene)
+ {
+ string menu = "Menu:\n";
+ menu += "commit (ci) - saves current state of the region to a database on the server\n";
+ menu += "diff-mode (dm) - displays those aspects of region that have not been saved but changed since the very last revision. Will dynamically update as you change environment.\n";
+ SendSimChatMessage(scene, menu);
+ }
+
+ public void SendSimChatMessage(Scene scene, string message)
+ {
+ scene.SimChat(Helpers.StringToField(message),
+ ChatTypeEnum.Broadcast, 0, new LLVector3(0,0,0), "Content Manager", LLUUID.Zero, false);
+ }
+
+ public void Hide(ContentManagementEntity ent)
+ {
+ ent.HideFromAll();
+ }
+
+ public void HideAllMetaEntities()
+ {
+ foreach(Object obj in m_model.MetaEntityCollection.Entities.Values)
+ ((ContentManagementEntity)obj).HideFromAll();
+ }
+
+ public void HideAllAuras()
+ {
+ foreach(Object obj in m_model.MetaEntityCollection.Auras.Values)
+ ((MetaEntity)obj).HideFromAll();
+ }
+ }
+}
diff --git a/OpenSim/Region/Environment/Modules/ContentManagementSystem/ContentManagementEntity.cs b/OpenSim/Region/Environment/Modules/ContentManagementSystem/ContentManagementEntity.cs
new file mode 100644
index 0000000000..fb9df8f6ab
--- /dev/null
+++ b/OpenSim/Region/Environment/Modules/ContentManagementSystem/ContentManagementEntity.cs
@@ -0,0 +1,326 @@
+// ContentManagementEntity.cs
+// User: bongiojp
+//
+//
+
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using libsecondlife;
+using Nini.Config;
+using OpenSim.Framework;
+using OpenSim.Region.Environment.Interfaces;
+using OpenSim.Region.Environment.Scenes;
+using log4net;
+using OpenSim.Region.Physics.Manager;
+using Axiom.Math;
+
+namespace OpenSim.Region.Environment.Modules.ContentManagement
+{
+ public class ContentManagementEntity : MetaEntity
+ {
+ static float TimeToDiff = 0;
+ static float TimeToCreateEntities = 0;
+
+ // The LinkNum of parts in m_Entity and m_UnchangedEntity are the same though UUID and LocalId are different.
+ // This can come in handy.
+ protected SceneObjectGroup m_UnchangedEntity = null;
+ protected Dictionary m_BeamEntities = new Dictionary();
+ protected Dictionary m_AuraEntities = new Dictionary();
+
+ ///
+ /// Should be set to true when there is a difference between m_UnchangedEntity and the corresponding scene object group in the scene entity list.
+ ///
+ bool DiffersFromSceneGroup = false;
+
+ public SceneObjectGroup UnchangedEntity
+ {
+ get { return m_UnchangedEntity; }
+ }
+
+ public ContentManagementEntity(SceneObjectGroup Unchanged, bool physics) : base(Unchanged, false)
+ {
+ m_UnchangedEntity = Unchanged.Copy(Unchanged.RootPart.OwnerID, Unchanged.RootPart.GroupID, false);
+ }
+
+ public ContentManagementEntity(string objectXML, Scene scene, bool physics) : base(objectXML, scene, false)
+ {
+ m_UnchangedEntity = new SceneObjectGroup(objectXML);
+ }
+
+ public override void Hide(IClientAPI client)
+ {
+ base.Hide(client);
+ foreach(MetaEntity group in m_AuraEntities.Values)
+ group.Hide(client);
+ foreach(MetaEntity group in m_BeamEntities.Values)
+ group.Hide(client);
+ }
+
+ public override void HideFromAll()
+ {
+ base.HideFromAll();
+ foreach(MetaEntity group in m_AuraEntities.Values)
+ group.HideFromAll();
+ foreach(MetaEntity group in m_BeamEntities.Values)
+ group.HideFromAll();
+ }
+
+ public void SendFullDiffUpdateToAll()
+ {
+ FindDifferences();
+ if (DiffersFromSceneGroup)
+ {
+ SendFullUpdateToAll();
+ SendFullAuraUpdateToAll();
+ SendFullBeamUpdateToAll();
+ }
+ }
+
+ public void SendFullDiffUpdate(IClientAPI client)
+ {
+ FindDifferences();
+ if (DiffersFromSceneGroup)
+ {
+ SendFullUpdate(client);
+ SendFullAuraUpdate(client);
+ SendFullBeamUpdate(client);
+ }
+ }
+
+ public void SendFullBeamUpdate(IClientAPI client)
+ {
+ if (DiffersFromSceneGroup)
+ {
+ foreach(BeamMetaEntity group in m_BeamEntities.Values)
+ group.SendFullUpdate(client);
+ }
+ }
+
+ public void SendFullAuraUpdate(IClientAPI client)
+ {
+ if (DiffersFromSceneGroup)
+ {
+ foreach(AuraMetaEntity group in m_AuraEntities.Values)
+ group.SendFullUpdate(client);
+ }
+ }
+
+ public void SendFullBeamUpdateToAll()
+ {
+ if (DiffersFromSceneGroup)
+ {
+ foreach(BeamMetaEntity group in m_BeamEntities.Values)
+ group.SendFullUpdateToAll();
+ }
+ }
+
+ public void SendFullAuraUpdateToAll()
+ {
+ if (DiffersFromSceneGroup)
+ {
+ foreach(AuraMetaEntity group in m_AuraEntities.Values)
+ group.SendFullUpdateToAll();
+ }
+ }
+
+ ///
+ /// Search for a corresponding group UUID in the scene. If not found, then the revisioned group this CMEntity represents has been deleted. Mark the metaentity appropriately.
+ /// If a matching UUID is found in a scene object group, compare the two for differences. If differences exist, Mark the metaentity appropriately.
+ ///
+ public void FindDifferences()
+ {
+ System.Collections.Generic.List sceneEntityList = m_Entity.Scene.GetEntities();
+ DiffersFromSceneGroup = false;
+ // if group is not contained in scene's list
+ if(!ContainsKey(sceneEntityList, m_UnchangedEntity.UUID))
+ {
+ foreach(SceneObjectPart part in m_UnchangedEntity.Children.Values)
+ {
+ // if scene list no longer contains this part, display translucent part and mark with red aura
+ if(! ContainsKey(sceneEntityList, part.UUID))
+ {
+ // if already displaying a red aura over part, make sure its red
+ if (m_AuraEntities.ContainsKey(part.UUID))
+ {
+ m_AuraEntities[part.UUID].SetAura(new LLVector3(254,0,0), part.Scale);
+ }
+ else
+ {
+ AuraMetaEntity auraGroup = new AuraMetaEntity(m_Entity.Scene,
+ m_Entity.Scene.PrimIDAllocate(),
+ part.GetWorldPosition(),
+ MetaEntity.TRANSLUCENT,
+ new LLVector3(254,0,0),
+ part.Scale
+ );
+ m_AuraEntities.Add(part.UUID, auraGroup);
+ }
+ SceneObjectPart metaPart = m_Entity.GetLinkNumPart(part.LinkNum);
+ SetPartTransparency(metaPart, MetaEntity.TRANSLUCENT);
+ }
+ // otherwise, scene will not contain the part. note: a group can not remove a part without changing group id
+ }
+
+ // a deleted part has no where to point a beam particle system,
+ // if a metapart had a particle system (maybe it represented a moved part) remove it
+ if (m_BeamEntities.ContainsKey(m_UnchangedEntity.RootPart.UUID))
+ {
+ m_BeamEntities[m_UnchangedEntity.RootPart.UUID].HideFromAll();
+ m_BeamEntities.Remove(m_UnchangedEntity.RootPart.UUID);
+ }
+
+ DiffersFromSceneGroup = true;
+ }
+ // if scene list does contain group, compare each part in group for differences and display beams and auras appropriately
+ else
+ {
+ MarkWithDifferences((SceneObjectGroup)GetGroupByUUID(sceneEntityList, m_UnchangedEntity.UUID));
+ }
+ }
+
+ ///
+ /// Returns true if there was a change between meta entity and the entity group, false otherwise.
+ /// If true is returned, it is assumed the metaentity's appearance has changed to reflect the difference (though clients haven't been updated).
+ ///
+ public bool MarkWithDifferences(SceneObjectGroup sceneEntityGroup)
+ {
+ SceneObjectPart sceneEntityPart;
+ SceneObjectPart metaEntityPart;
+ Diff differences;
+ bool changed = false;
+
+ // Use "UnchangedEntity" to do comparisons because its text, transparency, and other attributes will be just as the user
+ // had originally saved.
+ // m_Entity will NOT necessarily be the same entity as the user had saved.
+ foreach(SceneObjectPart UnchangedPart in m_UnchangedEntity.Children.Values)
+ {
+ //This is the part that we use to show changes.
+ metaEntityPart = m_Entity.GetLinkNumPart(UnchangedPart.LinkNum);
+ if (sceneEntityGroup.Children.ContainsKey(UnchangedPart.UUID))
+ {
+ sceneEntityPart = sceneEntityGroup.Children[UnchangedPart.UUID];
+ differences = Difference.FindDifferences(UnchangedPart, sceneEntityPart);
+ if (differences != Diff.NONE)
+ metaEntityPart.Text = "CHANGE: " + differences.ToString();
+ if (differences != 0)
+ {
+ // Root Part that has been modified
+ if ((differences&Diff.POSITION) > 0)
+ {
+ // If the position of any part has changed, make sure the RootPart of the
+ // meta entity is pointing with a beam particle system
+ if (m_BeamEntities.ContainsKey(m_UnchangedEntity.RootPart.UUID))
+ {
+ m_BeamEntities[m_UnchangedEntity.RootPart.UUID].HideFromAll();
+ m_BeamEntities.Remove(m_UnchangedEntity.RootPart.UUID);
+ }
+ BeamMetaEntity beamGroup = new BeamMetaEntity(m_Entity.Scene,
+ m_Entity.Scene.PrimIDAllocate(),
+ m_UnchangedEntity.RootPart.GetWorldPosition(),
+ MetaEntity.TRANSLUCENT,
+ sceneEntityPart,
+ new LLVector3(0,0,254)
+ );
+ m_BeamEntities.Add(m_UnchangedEntity.RootPart.UUID, beamGroup);
+ }
+
+ if (m_AuraEntities.ContainsKey(UnchangedPart.UUID))
+ {
+ m_AuraEntities[UnchangedPart.UUID].HideFromAll();
+ m_AuraEntities.Remove(UnchangedPart.UUID);
+ }
+ AuraMetaEntity auraGroup = new AuraMetaEntity(m_Entity.Scene,
+ m_Entity.Scene.PrimIDAllocate(),
+ UnchangedPart.GetWorldPosition(),
+ MetaEntity.TRANSLUCENT,
+ new LLVector3(0,0,254),
+ UnchangedPart.Scale
+ );
+ m_AuraEntities.Add(UnchangedPart.UUID, auraGroup);
+ SetPartTransparency(metaEntityPart, MetaEntity.TRANSLUCENT);
+
+ DiffersFromSceneGroup = true;
+ }
+ else // no differences between scene part and meta part
+ {
+ if (m_BeamEntities.ContainsKey(m_UnchangedEntity.RootPart.UUID))
+ {
+ m_BeamEntities[m_UnchangedEntity.RootPart.UUID].HideFromAll();
+ m_BeamEntities.Remove(m_UnchangedEntity.RootPart.UUID);
+ }
+ if (m_AuraEntities.ContainsKey(UnchangedPart.UUID))
+ {
+ m_AuraEntities[UnchangedPart.UUID].HideFromAll();
+ m_AuraEntities.Remove(UnchangedPart.UUID);
+ }
+ SetPartTransparency(metaEntityPart, MetaEntity.NONE);
+ }
+ }
+ else //The entity currently in the scene is missing parts from the metaentity saved, so mark parts red as deleted.
+ {
+ if (m_AuraEntities.ContainsKey(UnchangedPart.UUID))
+ {
+ m_AuraEntities[UnchangedPart.UUID].HideFromAll();
+ m_AuraEntities.Remove(UnchangedPart.UUID);
+ }
+ AuraMetaEntity auraGroup = new AuraMetaEntity(m_Entity.Scene,
+ m_Entity.Scene.PrimIDAllocate(),
+ UnchangedPart.GetWorldPosition(),
+ MetaEntity.TRANSLUCENT,
+ new LLVector3(254,0,0),
+ UnchangedPart.Scale
+ );
+ m_AuraEntities.Add(UnchangedPart.UUID, auraGroup);
+ SetPartTransparency(metaEntityPart, MetaEntity.TRANSLUCENT);
+
+ DiffersFromSceneGroup = true;
+ }
+ }
+ return changed;
+ }
+
+ private SceneObjectGroup GetGroupByUUID(System.Collections.Generic.List list, LLUUID uuid)
+ {
+ foreach (EntityBase ent in list)
+ {
+ if (ent is SceneObjectGroup)
+ if (ent.UUID == uuid)
+ return (SceneObjectGroup)ent;
+ }
+ return null;
+ }
+
+ ///
+ /// Check if the revisioned scene object group that this CMEntity is based off of contains a child with the given UUID.
+ ///
+ public bool HasChildPrim(LLUUID uuid)
+ {
+ if (m_UnchangedEntity.Children.ContainsKey(uuid))
+ return true;
+ return false;
+ }
+
+ ///
+ /// Check if the revisioned scene object group that this CMEntity is based off of contains a child with the given LocalId.
+ ///
+ public bool HasChildPrim(uint localID)
+ {
+ foreach( SceneObjectPart part in m_UnchangedEntity.Children.Values)
+ if ( part.LocalId == localID )
+ return true;
+ return false;
+ }
+
+ ///
+ /// Check if an entitybase list (like that returned by scene.GetEntities() ) contains a group with the rootpart uuid that matches the current uuid.
+ ///
+ private bool ContainsKey(List list, LLUUID uuid)
+ {
+ foreach( EntityBase part in list)
+ if (part.UUID == uuid)
+ return true;
+ return false;
+ }
+ }
+}
diff --git a/OpenSim/Region/Environment/Modules/ContentManagementSystem/ContentManagementModule.cs b/OpenSim/Region/Environment/Modules/ContentManagementSystem/ContentManagementModule.cs
new file mode 100644
index 0000000000..22177ae031
--- /dev/null
+++ b/OpenSim/Region/Environment/Modules/ContentManagementSystem/ContentManagementModule.cs
@@ -0,0 +1,111 @@
+// ContentManagementModule.cs
+// User: bongiojp
+
+
+
+using System;
+using System.Collections.Generic;
+using libsecondlife;
+using Nini.Config;
+using OpenSim;
+using OpenSim.Framework;
+using OpenSim.Region.Environment.Interfaces;
+using OpenSim.Region.Environment.Scenes;
+using log4net;
+using OpenSim.Region.Physics.Manager;
+using Axiom.Math;
+using System.Threading;
+
+namespace OpenSim.Region.Environment.Modules.ContentManagement
+{
+ public class ContentManagementModule : IRegionModule
+ {
+ private static readonly log4net.ILog m_log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
+ CMController m_control = null;
+ CMModel m_model = null;
+ CMView m_view = null;
+ bool initialised = false;
+ bool m_posted = false;
+ bool m_enabled = true;
+
+ public void Initialise(Scene scene, IConfigSource source)
+ {
+ string databaseDir = "./";
+ string database = "FilesyStemDatabase";
+ int channel = 345;
+ try
+ {
+ if (!source.Configs["CMS"].GetBoolean("Enabled", false))
+ m_enabled = false;
+
+ databaseDir = source.Configs["CMS"].GetString("Directory", databaseDir);
+ database = source.Configs["CMS"].GetString("Database", database);
+ channel = source.Configs["CMS"].GetInt("Channel", channel);
+ if (database != "FileSystemDatabase" && database != "GitDatabase")
+ {
+ m_log.ErrorFormat("[Content Management]: The Database attribute must be defined as either FileSystemDatabase or GitDatabase");
+ m_enabled = false;
+ }
+ }
+ catch (Exception e)
+ {
+ m_log.ErrorFormat("[Content Management]: Exception thrown while reading parameters from configuration file. Message: " + e);
+ m_enabled = false;
+ }
+
+ if (!m_enabled)
+ {
+ m_log.Info("[Content Management]: Content Management System is not Enabled.");
+ return;
+ }
+
+ lock(this)
+ {
+ if (!initialised) //only init once
+ {
+ m_view = new CMView();
+ m_model = new CMModel();
+ m_control = new CMController(m_model, m_view, scene, channel);
+ m_model.Initialise(database);
+ m_view.Initialise(m_model);
+
+ initialised = true;
+ m_model.InitialiseDatabase(scene, databaseDir);
+ }
+ else
+ {
+ m_model.InitialiseDatabase(scene, databaseDir);
+ m_control.RegisterNewRegion(scene);
+ }
+ }
+ }
+
+ public void PostInitialise()
+ {
+ if (! m_enabled)
+ return;
+
+ lock(this)
+ {
+ if (!m_posted) //only post once
+ {
+ m_model.PostInitialise();
+ m_posted = true;
+ }
+ }
+ }
+
+ public void Close()
+ {}
+
+ public string Name
+ {
+ get { return "ContentManagementModule"; }
+ }
+
+ public bool IsSharedModule
+ {
+ get { return true; }
+ }
+ }
+}
diff --git a/OpenSim/Region/Environment/Modules/ContentManagementSystem/FileSystemDatabase.cs b/OpenSim/Region/Environment/Modules/ContentManagementSystem/FileSystemDatabase.cs
new file mode 100644
index 0000000000..e1f519b462
--- /dev/null
+++ b/OpenSim/Region/Environment/Modules/ContentManagementSystem/FileSystemDatabase.cs
@@ -0,0 +1,260 @@
+// FileSystemDatabase.cs
+// User: bongiojp
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Reflection;
+using System.Xml;
+using libsecondlife;
+using Nini.Config;
+using OpenSim.Framework;
+using OpenSim.Region.Environment.Interfaces;
+using OpenSim.Region.Environment.Modules.World.Serialiser;
+using OpenSim.Region.Environment.Modules.World.Terrain;
+using OpenSim.Region.Environment.Scenes;
+using log4net;
+using OpenSim.Region.Physics.Manager;
+using Axiom.Math;
+using Slash=System.IO.Path;
+using System.Diagnostics;
+
+namespace OpenSim.Region.Environment.Modules.ContentManagement
+{
+ public class FileSystemDatabase : IContentDatabase
+ {
+ public static float TimeToDownload = 0;
+ public static float TimeToSave = 0;
+
+ private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+
+ private string m_repodir = null;
+ private Dictionary m_serialiser = new Dictionary();
+ private Dictionary m_scenes = new Dictionary();
+
+ public FileSystemDatabase()
+ {
+ }
+
+ public void Initialise(Scene scene, string dir)
+ {
+ lock(this)
+ {
+ if (m_repodir == null)
+ m_repodir = dir;
+ }
+ lock(m_scenes)
+ m_scenes.Add(scene.RegionInfo.RegionID, scene);
+ }
+
+
+ // Run once and only once.
+ public void PostInitialise()
+ {
+ SetupSerialiser();
+
+ m_log.Info("[FSDB]: Creating repository in " + m_repodir + ".");
+ CreateDirectory();
+ }
+
+ // called by postinitialise
+ private void SetupSerialiser()
+ {
+ if (m_serialiser.Count == 0)
+ foreach(LLUUID region in m_scenes.Keys)
+ m_serialiser.Add(region,
+ m_scenes[region].RequestModuleInterface()
+ );
+ }
+
+ // called by postinitialise
+ private void CreateDirectory()
+ {
+ string scenedir;
+ if (!Directory.Exists(m_repodir))
+ Directory.CreateDirectory(m_repodir);
+
+ foreach (LLUUID region in m_scenes.Keys)
+ {
+ scenedir = m_repodir + Slash.DirectorySeparatorChar + region + Slash.DirectorySeparatorChar;
+ if (!Directory.Exists(scenedir))
+ Directory.CreateDirectory(scenedir);
+ }
+ }
+
+ public int NumOfRegionRev(LLUUID regionid)
+ {
+ string scenedir = m_repodir + Slash.DirectorySeparatorChar + regionid + Slash.DirectorySeparatorChar;
+ m_log.Info("[FSDB]: Reading scene dir: " + scenedir);
+ string[] directories = Directory.GetDirectories(scenedir);
+ return directories.Length;
+ }
+
+ public int GetMostRecentRevision(LLUUID regionid)
+ {
+ return NumOfRegionRev(regionid);
+ }
+
+ public void SaveRegion(LLUUID regionid, string regionName, string logMessage)
+ {
+ m_log.Info("[FSDB]: ...............................");
+ string scenedir = m_repodir + Slash.DirectorySeparatorChar + regionid + Slash.DirectorySeparatorChar;
+
+ m_log.Info("[FSDB]: checking if scene directory exists: " + scenedir);
+ if (!Directory.Exists(scenedir))
+ Directory.CreateDirectory(scenedir);
+
+ int newRevisionNum = GetMostRecentRevision(regionid)+1;
+ string revisiondir = scenedir + newRevisionNum + Slash.DirectorySeparatorChar;
+
+ m_log.Info("[FSDB]: checking if revision directory exists: " + revisiondir);
+ if (!Directory.Exists(revisiondir))
+ Directory.CreateDirectory(revisiondir);
+
+ try {
+ Stopwatch x = new Stopwatch();
+ x.Start();
+ if (m_scenes.ContainsKey(regionid))
+ {
+ m_serialiser[regionid].SerialiseRegion(m_scenes[regionid], revisiondir);
+ }
+ x.Stop();
+ TimeToSave += x.ElapsedMilliseconds;
+ m_log.Info("[FileSystemDatabase] Time spent serialising regions to files on disk for " + regionName + ": " + x.ElapsedMilliseconds);
+ m_log.Info("[FileSystemDatabase] Time spent serialising regions to files on disk so far: " + TimeToSave);
+ }
+ catch (Exception e)
+ {
+ m_log.ErrorFormat("[FSDB]: Serialisation of region failed: " + e);
+ return;
+ }
+
+ try {
+ // Finish by writing log message.
+ FileStream file = new FileStream(revisiondir + "log", FileMode.Create, FileAccess.ReadWrite);
+ StreamWriter sw = new StreamWriter(file);
+ sw.Write(logMessage);
+ sw.Close();
+ }
+ catch (Exception e)
+ {
+ m_log.ErrorFormat("[FSDB]: Failed trying to save log file " + e);
+ return;
+ }
+ }
+
+ public System.Collections.ArrayList GetRegionObjectXMLList(LLUUID regionid, int revision)
+ {
+ System.Collections.ArrayList objectList = new System.Collections.ArrayList();
+ string filename = m_repodir + Slash.DirectorySeparatorChar + regionid + Slash.DirectorySeparatorChar +
+ + revision + Slash.DirectorySeparatorChar + "objects.xml";
+ XmlDocument doc = new XmlDocument();
+ XmlNode rootNode;
+ //int primCount = 0;
+ //SceneObjectGroup obj = null;
+
+ if(File.Exists(filename))
+ {
+ XmlTextReader reader = new XmlTextReader(filename);
+ reader.WhitespaceHandling = WhitespaceHandling.None;
+ doc.Load(reader);
+ reader.Close();
+ rootNode = doc.FirstChild;
+ foreach (XmlNode aPrimNode in rootNode.ChildNodes)
+ {
+ objectList.Add(aPrimNode.OuterXml);
+ }
+ return objectList;
+ }
+ return null;
+ }
+
+ public System.Collections.ArrayList GetRegionObjectXMLList(LLUUID regionid)
+ {
+ int revision = NumOfRegionRev(regionid);
+ m_log.Info("[FSDB]: found revisions:" + revision);
+ System.Collections.ArrayList xmlList = new System.Collections.ArrayList();
+ string filename = m_repodir + Slash.DirectorySeparatorChar + regionid + Slash.DirectorySeparatorChar +
+ + revision + Slash.DirectorySeparatorChar + "objects.xml";
+ XmlDocument doc = new XmlDocument();
+ XmlNode rootNode;
+
+
+ m_log.Info("[FSDB]: Checking if " + filename + " exists.");
+ if(File.Exists(filename))
+ {
+ Stopwatch x = new Stopwatch();
+ x.Start();
+
+ XmlTextReader reader = new XmlTextReader(filename);
+ reader.WhitespaceHandling = WhitespaceHandling.None;
+ doc.Load(reader);
+ reader.Close();
+ rootNode = doc.FirstChild;
+
+ foreach (XmlNode aPrimNode in rootNode.ChildNodes)
+ xmlList.Add(aPrimNode.OuterXml);
+
+ x.Stop();
+ TimeToDownload += x.ElapsedMilliseconds;
+ m_log.Info("[FileSystemDatabase] Time spent retrieving xml files so far: " + TimeToDownload);
+
+ return xmlList;
+ }
+ return null;
+ }
+
+ public string GetRegionObjectHeightMap(LLUUID regionid)
+ {
+ String filename = m_repodir + Slash.DirectorySeparatorChar + regionid +
+ Slash.DirectorySeparatorChar + "heightmap.r32";
+ FileStream fs = new FileStream( filename, FileMode.Open);
+ StreamReader sr = new StreamReader(fs);
+ String result = sr.ReadToEnd();
+ sr.Close();
+ fs.Close();
+ return result;
+ }
+
+ public string GetRegionObjectHeightMap(LLUUID regionid, int revision)
+ {
+ String filename = m_repodir + Slash.DirectorySeparatorChar + regionid +
+ Slash.DirectorySeparatorChar + "heightmap.r32";
+ FileStream fs = new FileStream( filename, FileMode.Open);
+ StreamReader sr = new StreamReader(fs);
+ String result = sr.ReadToEnd();
+ sr.Close();
+ fs.Close();
+ return result;
+ }
+
+ public System.Collections.Generic.SortedDictionary ListOfRegionRevisions(LLUUID regionid)
+ {
+ SortedDictionary revisionDict = new SortedDictionary();
+
+ string scenedir = m_repodir + Slash.DirectorySeparatorChar + regionid + Slash.DirectorySeparatorChar;
+ string[] directories = Directory.GetDirectories(scenedir);
+
+ FileStream fs = null;
+ StreamReader sr = null;
+ String logMessage = "";
+ String logLocation = "";
+ foreach(string revisionDir in directories)
+ {
+ try {
+ logLocation = revisionDir + Slash.DirectorySeparatorChar + "log";
+ fs = new FileStream( logLocation, FileMode.Open);
+ sr = new StreamReader(fs);
+ logMessage = sr.ReadToEnd();
+ sr.Close();
+ fs.Close();
+ revisionDict.Add(revisionDir, logMessage);
+ }
+ catch (Exception)
+ {}
+ }
+
+ return revisionDict;
+ }
+ }
+}
diff --git a/OpenSim/Region/Environment/Modules/ContentManagementSystem/GitDatabase.cs b/OpenSim/Region/Environment/Modules/ContentManagementSystem/GitDatabase.cs
new file mode 100644
index 0000000000..e337482c59
--- /dev/null
+++ b/OpenSim/Region/Environment/Modules/ContentManagementSystem/GitDatabase.cs
@@ -0,0 +1,130 @@
+// GitDatabase.cs
+//
+//
+//
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Reflection;
+using System.Xml;
+using libsecondlife;
+using Nini.Config;
+using OpenSim.Framework;
+using OpenSim.Region.Environment.Interfaces;
+using OpenSim.Region.Environment.Modules.World.Serialiser;
+using OpenSim.Region.Environment.Modules.World.Terrain;
+using OpenSim.Region.Environment.Scenes;
+using log4net;
+using OpenSim.Region.Physics.Manager;
+using Axiom.Math;
+using Slash=System.IO.Path;
+
+namespace OpenSim.Region.Environment.Modules.ContentManagement
+{
+
+ ///
+ /// Just a stub :-(
+ ///
+ public class GitDatabase : IContentDatabase
+ {
+
+ public GitDatabase()
+ {
+ }
+
+ public void Initialise(Scene scene, String dir)
+ {
+
+ }
+
+ public void PostInitialise()
+ {
+
+ }
+
+ public int NumOfObjectRev(LLUUID id)
+ {
+ return 0;
+ }
+
+ public int NumOfRegionRev(LLUUID regionid)
+ {
+ return 0;
+ }
+
+ public bool InRepository(LLUUID id)
+ {
+ return false;
+ }
+
+ public void SaveRegion(LLUUID regionid, string regionName, string logMessage)
+ {
+
+ }
+
+ public System.Collections.ArrayList GetRegionObjectXMLList(LLUUID regionid)
+ {
+ return null;
+ }
+
+ public System.Collections.ArrayList GetRegionObjectXMLList(LLUUID regionid, int revision)
+ {
+ return null;
+ }
+
+ public string GetRegionObjectXML(LLUUID regionid)
+ {
+ return null;
+ }
+
+ public string GetRegionObjectXML(LLUUID regionid, int revision)
+ {
+ return null;
+ }
+
+ public string GetRegionObjectHeightMap(LLUUID regionid)
+ {
+ return null;
+ }
+
+ public string GetRegionObjectHeightMap(LLUUID regionid, int revision)
+ {
+ return null;
+ }
+
+ public System.Collections.ArrayList GetObjectsFromRegion(LLUUID regionid, int revision)
+ {
+ return null;
+ }
+
+ public System.Collections.Generic.SortedDictionary ListOfRegionRevisions(LLUUID id)
+ {
+ return null;
+ }
+
+ public void SaveObject(SceneObjectGroup entity)
+ {
+ }
+
+ public SceneObjectGroup GetMostRecentObjectRevision(LLUUID id)
+ {
+ return null;
+ }
+
+ public SceneObjectGroup GetObjectRevision(LLUUID id, int revision)
+ {
+ return null;
+ }
+
+ public System.Collections.Generic.SortedDictionary ListOfObjectRevisions(LLUUID id)
+ {
+ return null;
+ }
+
+ public int GetMostRecentRevision(LLUUID regionid)
+ {
+ return 0;
+ }
+ }
+}
diff --git a/OpenSim/Region/Environment/Modules/ContentManagementSystem/IContentDatabase.cs b/OpenSim/Region/Environment/Modules/ContentManagementSystem/IContentDatabase.cs
new file mode 100644
index 0000000000..f37f95df12
--- /dev/null
+++ b/OpenSim/Region/Environment/Modules/ContentManagementSystem/IContentDatabase.cs
@@ -0,0 +1,57 @@
+// IContentDatabase.cs
+// User: bongiojp
+//
+//
+//
+
+using System;
+using libsecondlife;
+using OpenSim.Region.Environment.Scenes;
+using Nini.Config;
+
+namespace OpenSim.Region.Environment.Modules.ContentManagement
+{
+ public interface IContentDatabase
+ {
+ ///
+ /// Similar to the IRegionModule function. This is the function to be called before attempting to interface with the database.
+ /// Initialise should be called one for each region to be contained in the database. The directory should be the full path
+ /// to the repository and will only be defined once, regardless of how many times the method is called.
+ ///
+ void Initialise(Scene scene, String dir);
+
+ ///
+ /// Should be called once after Initialise has been called.
+ ///
+ void PostInitialise();
+
+ ///
+ /// Returns the total number of revisions saved for a specific region.
+ ///
+ int NumOfRegionRev(LLUUID regionid);
+
+ ///
+ /// Saves the Region terrain map and objects within the region as xml to the database.
+ ///
+ void SaveRegion(LLUUID regionid, string regionName, string logMessage);
+
+ ///
+ /// Retrieves the xml that describes each individual object from the last revision or specific revision of the given region.
+ ///
+ System.Collections.ArrayList GetRegionObjectXMLList(LLUUID regionid);
+ System.Collections.ArrayList GetRegionObjectXMLList(LLUUID regionid, int revision);
+
+ string GetRegionObjectHeightMap(LLUUID regionid);
+ string GetRegionObjectHeightMap(LLUUID regionid, int revision);
+
+ ///
+ /// Returns a list of the revision numbers and corresponding log messages for a given region.
+ ///
+ System.Collections.Generic.SortedDictionary ListOfRegionRevisions(LLUUID id);
+
+ ///
+ /// Returns the most recent revision number of a region.
+ ///
+ int GetMostRecentRevision(LLUUID regionid);
+ }
+}
diff --git a/OpenSim/Region/Environment/Modules/ContentManagementSystem/MetaEntity.cs b/OpenSim/Region/Environment/Modules/ContentManagementSystem/MetaEntity.cs
new file mode 100644
index 0000000000..f0763d36b6
--- /dev/null
+++ b/OpenSim/Region/Environment/Modules/ContentManagementSystem/MetaEntity.cs
@@ -0,0 +1,219 @@
+// MetaEntity.cs
+// User: bongiojp
+//
+// TODO:
+// Create a physics manager to the meta object if there isn't one or the object knows of no scene but the user wants physics enabled.
+
+
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using libsecondlife;
+using Nini.Config;
+using OpenSim.Framework;
+using OpenSim.Region.Environment.Interfaces;
+using OpenSim.Region.Environment.Scenes;
+using log4net;
+using OpenSim.Region.Physics.Manager;
+using Axiom.Math;
+
+namespace OpenSim.Region.Environment.Modules.ContentManagement
+{
+ public class MetaEntity
+ {
+ protected static readonly ILog m_log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
+
+ protected SceneObjectGroup m_Entity = null; // The scene object group that represents this meta entity.
+ protected uint m_metaLocalid;
+
+ // Settings for transparency of metaentity
+ public const float NONE = 0f;
+ public const float TRANSLUCENT = .5f;
+ public const float INVISIBLE = .95f;
+
+ public Scene Scene
+ {
+ get { return m_Entity.Scene; }
+ }
+
+ public SceneObjectPart RootPart
+ {
+ get { return m_Entity.RootPart; }
+ set { m_Entity.RootPart = value; }
+ }
+
+ public LLUUID UUID
+ {
+ get { return m_Entity.UUID; }
+ set { m_Entity.UUID = value; }
+ }
+
+ public uint LocalId
+ {
+ get { return m_Entity.LocalId; }
+ set { m_Entity.LocalId = value; }
+ }
+
+ public SceneObjectGroup ObjectGroup
+ {
+ get { return m_Entity; }
+ }
+
+ public Dictionary Children
+ {
+ get { return m_Entity.Children; }
+ set { m_Entity.Children = value; }
+ }
+
+ public int PrimCount
+ {
+ get { return m_Entity.PrimCount; }
+ }
+
+ public MetaEntity()
+ {
+ }
+
+ ///
+ /// Makes a new meta entity by copying the given scene object group.
+ /// The physics boolean is just a stub right now.
+ ///
+ public MetaEntity(SceneObjectGroup orig, bool physics)
+ {
+ m_Entity = orig.Copy(orig.RootPart.OwnerID, orig.RootPart.GroupID, false);
+ Initialize(physics);
+ }
+
+ ///
+ /// Takes an XML description of a scene object group and converts it to a meta entity.
+ ///
+ public MetaEntity(string objectXML, Scene scene, bool physics)
+ {
+ m_Entity = new SceneObjectGroup(objectXML);
+ m_Entity.SetScene(scene);
+ Initialize(physics);
+ }
+
+ // The metaentity objectgroup must have unique localids as well as unique uuids.
+ // localids are used by the client to refer to parts.
+ // uuids are sent to the client and back to the server to identify parts on the server side.
+ ///
+ /// Changes localids and uuids of m_Entity.
+ ///
+ protected void Initialize(bool physics)
+ {
+ //make new uuids
+ Dictionary parts = new Dictionary();
+ foreach(SceneObjectPart part in m_Entity.Children.Values)
+ {
+ part.ResetIDs(part.LinkNum);
+ parts.Add(part.UUID, part);
+ }
+
+ // make new localids
+ foreach (SceneObjectPart part in m_Entity.Children.Values)
+ part.LocalId = m_Entity.Scene.PrimIDAllocate();
+
+ //finalize
+ m_Entity.UpdateParentIDs();
+ m_Entity.RootPart.PhysActor = null;
+ m_Entity.Children = parts;
+
+ }
+
+ public void SendFullUpdate(IClientAPI client)
+ {
+ // Not sure what clientFlags should be but 0 seems to work
+ SendFullUpdate(client, 0);
+ }
+ public void SendFullUpdateToAll()
+ {
+ uint clientFlags = 0;
+ m_Entity.Scene.ClientManager.ForEachClient(delegate(IClientAPI controller)
+ { m_Entity.SendFullUpdateToClient(controller, clientFlags); }
+ );
+ }
+
+ public void SendFullUpdate(IClientAPI client, uint clientFlags)
+ {
+ m_Entity.SendFullUpdateToClient(client, clientFlags);
+ }
+
+ ///
+ /// Hides the metaentity from a single client.
+ ///
+ public virtual void Hide(IClientAPI client)
+ {
+ //This deletes the group without removing from any databases.
+ //This is important because we are not IN any database.
+ //m_Entity.FakeDeleteGroup();
+ foreach( SceneObjectPart part in m_Entity.Children.Values)
+ client.SendKillObject(m_Entity.RegionHandle, part.LocalId);
+ }
+
+ ///
+ /// Sends a kill object message to all clients, effectively "hiding" the metaentity even though it's still on the server.
+ ///
+ public virtual void HideFromAll()
+ {
+ foreach( SceneObjectPart part in m_Entity.Children.Values)
+ m_Entity.Scene.ClientManager.ForEachClient(delegate(IClientAPI controller)
+ { controller.SendKillObject(m_Entity.RegionHandle, part.LocalId); }
+ );
+ }
+
+ ///
+ /// Makes a single SceneObjectPart see through.
+ ///
+ ///
+ /// A
+ /// The part to make see through
+ ///
+ ///
+ /// A
+ /// The degree of transparency to imbue the part with, 0f being solid and .95f being invisible.
+ ///
+ public static void SetPartTransparency(SceneObjectPart part, float transparencyAmount)
+ {
+ LLObject.TextureEntry tex = null;
+ LLColor texcolor;
+ try
+ {
+ tex = part.Shape.Textures;
+ texcolor = new LLColor();
+ }
+ catch(Exception)
+ {
+ //m_log.ErrorFormat("[Content Management]: Exception thrown while accessing textures of scene object: " + e);
+ return;
+ }
+
+ for (uint i = 0; i < tex.FaceTextures.Length; i++)
+ {
+ try {
+ if (tex.FaceTextures[i] != null)
+ {
+ texcolor = tex.FaceTextures[i].RGBA;
+ texcolor.A = transparencyAmount;
+ tex.FaceTextures[i].RGBA = texcolor;
+ }
+ }
+ catch (Exception)
+ {
+ //m_log.ErrorFormat("[Content Management]: Exception thrown while accessing different face textures of object: " + e);
+ continue;
+ }
+ }
+ try {
+ texcolor = tex.DefaultTexture.RGBA;
+ texcolor.A = transparencyAmount;
+ tex.DefaultTexture.RGBA = texcolor;
+ part.Shape.TextureEntry = tex.ToBytes();
+ }
+ catch (Exception)
+ {
+ //m_log.Info("[Content Management]: Exception thrown while accessing default face texture of object: " + e);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/OpenSim/Region/Environment/Modules/ContentManagementSystem/PointMetaEntity.cs b/OpenSim/Region/Environment/Modules/ContentManagementSystem/PointMetaEntity.cs
new file mode 100644
index 0000000000..55365ac4c4
--- /dev/null
+++ b/OpenSim/Region/Environment/Modules/ContentManagementSystem/PointMetaEntity.cs
@@ -0,0 +1,81 @@
+// PointMetaEntity.cs created with MonoDevelop
+// User: bongiojp at 3:03 PM 8/6/2008
+//
+// To change standard headers go to Edit->Preferences->Coding->Standard Headers
+//
+
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using libsecondlife;
+using Nini.Config;
+using OpenSim.Framework;
+using OpenSim.Region.Environment.Interfaces;
+using OpenSim.Region.Environment.Scenes;
+using log4net;
+using OpenSim.Region.Physics.Manager;
+using Axiom.Math;
+
+namespace OpenSim.Region.Environment.Modules.ContentManagement
+{
+
+
+ public class PointMetaEntity : MetaEntity
+ {
+
+ public PointMetaEntity(Scene scene, uint LocalId, LLVector3 groupPos, float transparency) : base()
+ {
+ CreatePointEntity(scene, LLUUID.Random(), LocalId, groupPos);
+ SetPartTransparency(m_Entity.RootPart, transparency);
+ }
+
+ public PointMetaEntity(Scene scene, LLUUID uuid, uint LocalId, LLVector3 groupPos, float transparency) : base()
+ {
+ CreatePointEntity(scene, uuid, LocalId, groupPos);
+ SetPartTransparency(m_Entity.RootPart, transparency);
+ }
+
+ private void CreatePointEntity(Scene scene, LLUUID uuid, uint LocalId, LLVector3 groupPos)
+ {
+ SceneObjectGroup x = new SceneObjectGroup();
+ SceneObjectPart y = new SceneObjectPart();
+
+ //Initialize part
+ y.Name = "Very Small Point";
+ y.RegionHandle = scene.RegionInfo.RegionHandle;
+ y.CreationDate = (Int32) (DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds;
+ y.OwnerID = LLUUID.Zero;
+ y.CreatorID = LLUUID.Zero;
+ y.LastOwnerID = LLUUID.Zero;
+ y.UUID = uuid;
+
+ y.LocalId = LocalId;
+
+ y.Shape = PrimitiveBaseShape.CreateBox();
+ y.Scale = new LLVector3(0.01f,0.01f,0.01f);
+ y.LastOwnerID = LLUUID.Zero;
+ y.GroupPosition = groupPos;
+ y.OffsetPosition = new LLVector3(0, 0, 0);
+ y.RotationOffset = new LLQuaternion(0,0,0,0);
+ y.Velocity = new LLVector3(0, 0, 0);
+ y.RotationalVelocity = new LLVector3(0, 0, 0);
+ y.AngularVelocity = new LLVector3(0, 0, 0);
+ y.Acceleration = new LLVector3(0, 0, 0);
+
+ y.Flags = 0;
+ y.TrimPermissions();
+
+ //Initialize group and add part as root part
+ x.SetScene(scene);
+ y.SetParent(x);
+ y.ParentID = 0;
+ y.LinkNum = 0;
+ x.Children.Add(y.UUID, y);
+ x.RootPart = y;
+ x.RegionHandle = scene.RegionInfo.RegionHandle;
+ x.SetScene(scene);
+
+ m_Entity = x;
+ }
+ }
+}
diff --git a/OpenSim/Region/Environment/Modules/ContentManagementSystem/README b/OpenSim/Region/Environment/Modules/ContentManagementSystem/README
new file mode 100644
index 0000000000..1a69fef231
--- /dev/null
+++ b/OpenSim/Region/Environment/Modules/ContentManagementSystem/README
@@ -0,0 +1,52 @@
+This module is meant to be built alone and not added to the Opensim code base. References are made to required dlls through a
+reference file, ContentManagement.mdp. Originally, for development, this project was contained in the Opensim/Region/Modules/
+directory.
+
+To compile: nant
+To use: Copy ContentManagement.dll into the bin directory of your Opensim build. You should find many other dlls in the same directory.
+
+
+--------------------------------------------------------------------------------------------------------------------
+To build the libgit.so file:
+
+#Download GIT git repository
+$ git clone git://git2.kernel.org/pub/OpenSim/Region/Environment/Modules/ContentManagementSystem/scm/git/git.git
+$ cd git
+
+#Compile GIT
+#Note that we are adding two extra flags to pass to gcc while compiling (-c and -fPIC)
+$ autoconf
+$ ./configure
+$ CFLAGS="-g -O2 -Wall -c -fPIC" make
+
+#Copy necessary object files (and some not so necessary) to their own directory for shared object file creation
+$ mkdir ../libgit-objects
+$ cp builtin*.o ../libgit-objects
+$ cp xdiff/*.o ../libgit-objects
+$ cp libgit.a ../libgit-objects
+
+#Remove the main symbol from any object files (like git.o)
+$ cd ../libgit-objects
+$ strip -N main *.o
+
+#Uncompress the plumbing objects from archive created by git
+$ ar x libgit.a
+
+#Create shared object file from all objects (including the zlib library)
+$ ld -shared -soname libgit.so.1 -o libgit.so.1.5.6.3 -lc -lz *.o
+
+
+#You can also just copy the following commands into a file and run as a script inside the git directory
+
+make clean
+autoconf
+./configure
+CFLAGS="-g -O2 -Wall -c -fPIC" make
+mkdir libgit-objects
+cp builtin*.o libgit-objects
+cp xdiff/*.o libgit-objects
+cp libgit.a libgit-objects
+cd libgit-objects
+strip -N main *.o
+ar x libgit.a
+ld -shared -soname libgit.so.1 -o libgit.so.1.5.6.3 -lc -lz *.o
\ No newline at end of file
diff --git a/OpenSim/Region/Environment/Modules/ContentManagementSystem/Requirements_documentation.odt b/OpenSim/Region/Environment/Modules/ContentManagementSystem/Requirements_documentation.odt
new file mode 100644
index 0000000000..3fe7382f30
Binary files /dev/null and b/OpenSim/Region/Environment/Modules/ContentManagementSystem/Requirements_documentation.odt differ
diff --git a/OpenSim/Region/Environment/Modules/ContentManagementSystem/SceneObjectGroupDiff.cs b/OpenSim/Region/Environment/Modules/ContentManagementSystem/SceneObjectGroupDiff.cs
new file mode 100644
index 0000000000..c3817efa3e
--- /dev/null
+++ b/OpenSim/Region/Environment/Modules/ContentManagementSystem/SceneObjectGroupDiff.cs
@@ -0,0 +1,169 @@
+// SceneObjectGroupDiff.cs
+// User: bongiojp
+
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using libsecondlife;
+using Nini.Config;
+using OpenSim.Framework;
+using OpenSim.Region.Environment.Interfaces;
+using OpenSim.Region.Environment.Scenes;
+using log4net;
+using OpenSim.Region.Physics.Manager;
+using Axiom.Math;
+using System.Diagnostics;
+
+namespace OpenSim.Region.Environment.Modules.ContentManagement
+{
+ [Flags]
+ public enum Diff
+ {
+ NONE = 0,
+ FACECOLOR = 1,
+ SHAPE = 1<<1,
+ MATERIAL = 1<<2,
+ TEXTURE = 1<<3,
+ SCALE = 1<<4,
+ POSITION = 1<<5,
+ OFFSETPOSITION = 1<<6,
+ ROTATIONOFFSET = 1<<7,
+ ROTATIONALVELOCITY = 1<<8,
+ ACCELERATION = 1<<9,
+ ANGULARVELOCITY = 1<<10,
+ VELOCITY = 1<<11,
+ OBJECTOWNER = 1<<12,
+ PERMISSIONS = 1<<13,
+ DESCRIPTION = 1<<14,
+ NAME = 1<<15,
+ SCRIPT = 1<<16,
+ CLICKACTION = 1<<17,
+ PARTICLESYSTEM = 1<<18,
+ GLOW = 1<<19,
+ SALEPRICE = 1<<20,
+ SITNAME = 1<<21,
+ SITTARGETORIENTATION = 1<<22,
+ SITTARGETPOSITION = 1<<23,
+ TEXT = 1<<24,
+ TOUCHNAME = 1<<25
+ };
+
+ public static class Difference
+ {
+ static float TimeToDiff = 0;
+
+ private static readonly ILog m_log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
+
+ private static int TruncateSignificant(float num, int digits)
+ {
+ return (int) Math.Ceiling((Math.Truncate(num * 10 * digits)/10*digits));
+ // return (int) ((num * (10*digits))/10*digits);
+ }
+
+ private static bool AreVectorsEquivalent(LLVector3 first, LLVector3 second)
+ {
+ if(TruncateSignificant(first.X, 2) == TruncateSignificant(second.X, 2)
+ && TruncateSignificant(first.Y, 2) == TruncateSignificant(second.Y, 2)
+ && TruncateSignificant(first.Z, 2) == TruncateSignificant(second.Z, 2)
+ )
+ return true;
+ else
+ return false;
+ }
+
+ private static bool AreQuaternionsEquivalent(LLQuaternion first, LLQuaternion second)
+ {
+ LLVector3 firstVector = llRot2Euler(first);
+ LLVector3 secondVector = llRot2Euler(second);
+ return AreVectorsEquivalent(firstVector, secondVector);
+ }
+
+ // Taken from Region/ScriptEngine/Common/LSL_BuiltIn_Commands.cs
+ // Also changed the original function from LSL_Types to LL types
+ private static LLVector3 llRot2Euler(LLQuaternion r)
+ {
+ LLQuaternion t = new LLQuaternion(r.X * r.X, r.Y * r.Y, r.Z * r.Z, r.W * r.W);
+ double m = (t.X + t.Y + t.Z + t.W);
+ if (m == 0) return new LLVector3();
+ double n = 2 * (r.Y * r.W + r.X * r.Z);
+ double p = m * m - n * n;
+ if (p > 0)
+ return new LLVector3((float)NormalizeAngle(Math.Atan2(2.0 * (r.X * r.W - r.Y * r.Z), (-t.X - t.Y + t.Z + t.W))),
+ (float)NormalizeAngle(Math.Atan2(n, Math.Sqrt(p))),
+ (float)NormalizeAngle(Math.Atan2(2.0 * (r.Z * r.W - r.X * r.Y), (t.X - t.Y - t.Z + t.W))));
+ else if (n > 0)
+ return new LLVector3(0.0f, (float)(Math.PI / 2), (float)NormalizeAngle(Math.Atan2((r.Z * r.W + r.X * r.Y), 0.5 - t.X - t.Z)));
+ else
+ return new LLVector3(0.0f, (float)(-Math.PI / 2), (float)NormalizeAngle(Math.Atan2((r.Z * r.W + r.X * r.Y), 0.5 - t.X - t.Z)));
+ }
+
+ // Taken from Region/ScriptEngine/Common/LSL_BuiltIn_Commands.cs
+ private static double NormalizeAngle(double angle)
+ {
+ angle = angle % (Math.PI * 2);
+ if (angle < 0) angle = angle + Math.PI * 2;
+ return angle;
+ }
+
+ ///
+ /// Compares the attributes (Vectors, Quaternions, Strings, etc.) between two scene object parts
+ /// and returns a Diff bitmask which details what the differences are.
+ ///
+ public static Diff FindDifferences(SceneObjectPart first, SceneObjectPart second)
+ {
+ Stopwatch x = new Stopwatch();
+ x.Start();
+
+ Diff result = 0;
+
+ // VECTOR COMPARISONS
+ if(! AreVectorsEquivalent(first.Acceleration, second.Acceleration))
+ result |= Diff.ACCELERATION;
+ if(! AreVectorsEquivalent(first.AbsolutePosition, second.AbsolutePosition))
+ result |= Diff.POSITION;
+ if(! AreVectorsEquivalent(first.AngularVelocity, second.AngularVelocity))
+ result |= Diff.ANGULARVELOCITY;
+ if(! AreVectorsEquivalent(first.OffsetPosition, second.OffsetPosition))
+ result |= Diff.OFFSETPOSITION;
+ if(! AreVectorsEquivalent(first.RotationalVelocity, second.RotationalVelocity))
+ result |= Diff.ROTATIONALVELOCITY;
+ if(! AreVectorsEquivalent(first.Scale, second.Scale))
+ result |= Diff.SCALE;
+ if(! AreVectorsEquivalent(first.Velocity, second.Velocity))
+ result |= Diff.VELOCITY;
+
+
+ // QUATERNION COMPARISONS
+ if(! AreQuaternionsEquivalent(first.RotationOffset, second.RotationOffset))
+ result |= Diff.ROTATIONOFFSET;
+
+
+ // MISC COMPARISONS (LLUUID, Byte)
+ if(first.ClickAction != second.ClickAction)
+ result |= Diff.CLICKACTION;
+ if(first.ObjectOwner != second.ObjectOwner)
+ result |= Diff.OBJECTOWNER;
+
+
+ // STRING COMPARISONS
+ if(first.Description != second.Description)
+ result |= Diff.DESCRIPTION;
+ if(first.Material != second.Material)
+ result |= Diff.MATERIAL;
+ if(first.Name != second.Name)
+ result |= Diff.NAME;
+ if(first.SitName != second.SitName)
+ result |= Diff.SITNAME;
+ if(first.Text != second.Text)
+ result |= Diff.TEXT;
+ if(first.TouchName != second.TouchName)
+ result |= Diff.TOUCHNAME;
+
+ x.Stop();
+ TimeToDiff += x.ElapsedMilliseconds;
+ //m_log.Info("[DIFFERENCES] Time spent diffing objects so far" + TimeToDiff);
+
+ return result;
+ }
+ }
+}
diff --git a/bin/opensim-ode.sh b/bin/opensim-ode.sh
index 86353174c6..b901425fc1 100755
--- a/bin/opensim-ode.sh
+++ b/bin/opensim-ode.sh
@@ -1,5 +1,4 @@
#!/bin/sh
echo "Starting OpenSimulator with ODE. If you get an error saying limit: Operation not permitted. Then you will need to chmod 0600 /etc/limits"
ulimit -s 262144
-sleep 5
mono OpenSim.exe -physics=OpenDynamicsEngine