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