From 1fb54b074c243bab1964b4a568d672e87d18655f Mon Sep 17 00:00:00 2001 From: Teravus Ovares Date: Mon, 28 Apr 2008 01:48:21 +0000 Subject: * Added basic 3-5 level undo on prim position/rotation/scale. * In the future this should be a config option... and, hopefully this tides the builders over for a little while. --- OpenSim/Framework/IClientAPI.cs | 2 + OpenSim/Framework/UndoStack.cs | 128 +++++++++++++++++++++ OpenSim/Region/ClientStack/ClientView.cs | 19 +++ OpenSim/Region/Environment/Scenes/InnerScene.cs | 14 +++ OpenSim/Region/Environment/Scenes/Scene.cs | 2 + .../Region/Environment/Scenes/SceneObjectGroup.cs | 25 +++- .../Region/Environment/Scenes/SceneObjectPart.cs | 69 ++++++++++- OpenSim/Region/Environment/Scenes/UndoState.cs | 118 +++++++++++++++++++ .../Region/Examples/SimpleModule/MyNpcCharacter.cs | 1 + 9 files changed, 370 insertions(+), 8 deletions(-) create mode 100644 OpenSim/Framework/UndoStack.cs create mode 100644 OpenSim/Region/Environment/Scenes/UndoState.cs (limited to 'OpenSim') diff --git a/OpenSim/Framework/IClientAPI.cs b/OpenSim/Framework/IClientAPI.cs index fb32397..985e085 100644 --- a/OpenSim/Framework/IClientAPI.cs +++ b/OpenSim/Framework/IClientAPI.cs @@ -570,6 +570,8 @@ namespace OpenSim.Framework event UUIDNameRequest OnTeleportHomeRequest; event ScriptAnswer OnScriptAnswer; + + event AgentSit OnUndo; LLVector3 StartPos { get; set; } diff --git a/OpenSim/Framework/UndoStack.cs b/OpenSim/Framework/UndoStack.cs new file mode 100644 index 0000000..0b97644 --- /dev/null +++ b/OpenSim/Framework/UndoStack.cs @@ -0,0 +1,128 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSim Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using System.Text; + +namespace OpenSim.Framework +{ + /// + /// Undo stack. Deletes entries beyond a certain capacity + /// + /// + [Serializable] + public class UndoStack + { + + private int m_new = 1; + private int m_old = 0; + private T[] m_Undos; + + public bool IsFull + { + get + { + return m_new == m_old; + } + } + + + public int Capacity + { + get + { + return m_Undos.Length - 1; + } + } + + + public UndoStack(int capacity) + { + m_Undos = new T[capacity + 1]; + } + + public void Push(T item) + { + if (IsFull) + { + m_old++; + if (m_old >= m_Undos.Length) + m_old -= m_Undos.Length; + } + if (++m_new >= m_Undos.Length) + m_new -= m_Undos.Length; + m_Undos[m_new] = item; + } + + public T Pop() + { + if (Count > 0) + { + T deleted = m_Undos[m_new]; + m_Undos[m_new--] = default(T); + if (m_new < 0) + m_new += m_Undos.Length; + return deleted; + } + else + throw new InvalidOperationException("Cannot pop from emtpy stack"); + } + + public int Count + { + get + { + int count = m_new - m_old - 1; + if (count < 0) + count += m_Undos.Length; + return count; + } + } + + + public T Peek() + { + return m_Undos[m_new]; + } + + + public void Clear() + { + if (Count > 0) + { + for (int i = 0; i < m_Undos.Length; i++) + { + m_Undos[i] = default(T); + } + m_new = 1; + m_old = 0; + } + } + + } +} \ No newline at end of file diff --git a/OpenSim/Region/ClientStack/ClientView.cs b/OpenSim/Region/ClientStack/ClientView.cs index 94de013..aac96d1 100644 --- a/OpenSim/Region/ClientStack/ClientView.cs +++ b/OpenSim/Region/ClientStack/ClientView.cs @@ -238,6 +238,7 @@ namespace OpenSim.Region.ClientStack private ScriptAnswer handlerScriptAnswer = null; private RequestPayPrice handlerRequestPayPrice = null; private ObjectDeselect handlerObjectDetach = null; + private AgentSit handlerOnUndo = null; /* Properties */ @@ -799,6 +800,7 @@ namespace OpenSim.Region.ClientStack public event ScriptAnswer OnScriptAnswer; public event RequestPayPrice OnRequestPayPrice; + public event AgentSit OnUndo; #region Scene/Avatar to Client @@ -3884,6 +3886,23 @@ namespace OpenSim.Region.ClientStack // That means multiple object perms may be updated in a single packet. break; + + case PacketType.Undo: + UndoPacket undoitem = (UndoPacket)Pack; + if (undoitem.ObjectData.Length > 0) + { + for (int i = 0; i < undoitem.ObjectData.Length; i++) + { + LLUUID objiD = undoitem.ObjectData[i].ObjectID; + handlerOnUndo = OnUndo; + if (handlerOnUndo != null) + { + handlerOnUndo(this, objiD); + } + + } + } + break; case PacketType.ObjectDuplicateOnRay: ObjectDuplicateOnRayPacket dupeOnRay = (ObjectDuplicateOnRayPacket)Pack; diff --git a/OpenSim/Region/Environment/Scenes/InnerScene.cs b/OpenSim/Region/Environment/Scenes/InnerScene.cs index d00f601..8a48f6e 100644 --- a/OpenSim/Region/Environment/Scenes/InnerScene.cs +++ b/OpenSim/Region/Environment/Scenes/InnerScene.cs @@ -312,6 +312,20 @@ namespace OpenSim.Region.Environment.Scenes } } + + public void HandleUndo(IClientAPI remoteClient, LLUUID primId) + { + if (primId != LLUUID.Zero) + { + SceneObjectPart part = m_parentScene.GetSceneObjectPart(primId); + if (part != null) + part.Undo(); + + } + + + } + /// /// Event Handling routine for Attach Object /// diff --git a/OpenSim/Region/Environment/Scenes/Scene.cs b/OpenSim/Region/Environment/Scenes/Scene.cs index a517e69..de4270f 100644 --- a/OpenSim/Region/Environment/Scenes/Scene.cs +++ b/OpenSim/Region/Environment/Scenes/Scene.cs @@ -78,6 +78,7 @@ namespace OpenSim.Region.Environment.Scenes private readonly Mutex updateLock; public bool m_physicalPrim; public bool m_seeIntoRegionFromNeighbor; + public int MaxUndoCount = 5; private int m_RestartTimerCounter; private readonly Timer m_restartTimer = new Timer(15000); // Wait before firing private int m_incrementsof15seconds = 0; @@ -1628,6 +1629,7 @@ namespace OpenSim.Region.Environment.Scenes client.OnTeleportHomeRequest += TeleportClientHome; client.OnSetStartLocationRequest += SetHomeRezPoint; + client.OnUndo += m_innerScene.HandleUndo; EventManager.TriggerOnNewClient(client); } diff --git a/OpenSim/Region/Environment/Scenes/SceneObjectGroup.cs b/OpenSim/Region/Environment/Scenes/SceneObjectGroup.cs index ba318a8..4711701 100644 --- a/OpenSim/Region/Environment/Scenes/SceneObjectGroup.cs +++ b/OpenSim/Region/Environment/Scenes/SceneObjectGroup.cs @@ -378,6 +378,7 @@ namespace OpenSim.Region.Environment.Scenes part.RegionHandle = m_regionHandle; part.TrimPermissions(); + part.StoreUndoState(); } break; case XmlNodeType.EndElement: @@ -436,6 +437,7 @@ namespace OpenSim.Region.Environment.Scenes { SceneObjectPart Part = SceneObjectPart.FromXml(reader); AddPart(Part); + Part.StoreUndoState(); } else { @@ -703,6 +705,8 @@ namespace OpenSim.Region.Environment.Scenes m_rootPart.ApplyPhysics(m_rootPart.ObjectFlags, m_scene.m_physicalPrim); AttachToBackup(); m_rootPart.ScheduleFullUpdate(); + m_rootPart.ClearUndoState(); + } public void DetachToInventoryPrep() { @@ -731,6 +735,7 @@ namespace OpenSim.Region.Environment.Scenes private void SetPartAsNonRoot(SceneObjectPart part) { part.ParentID = m_rootPart.LocalId; + part.ClearUndoState(); } /// @@ -783,6 +788,7 @@ namespace OpenSim.Region.Environment.Scenes try { m_parts.Add(part.UUID, part); + } catch (Exception e) { @@ -803,6 +809,7 @@ namespace OpenSim.Region.Environment.Scenes if (part.UUID != m_rootPart.UUID) { part.ParentID = m_rootPart.LocalId; + } } } @@ -815,10 +822,17 @@ namespace OpenSim.Region.Environment.Scenes foreach (SceneObjectPart part in m_parts.Values) { part.UUID = LLUUID.Random(); + } } } - + // helper provided for parts. + public int GetSceneMaxUndo() + { + if (m_scene != null) + return m_scene.MaxUndoCount; + return 5; + } public void ResetChildPrimPhysicsPositions() { AbsolutePosition = AbsolutePosition; @@ -845,12 +859,15 @@ namespace OpenSim.Region.Environment.Scenes { SceneObjectPart part = GetChildPart(localId); OnGrabPart(part, offsetPos, remoteClient); + } } public virtual void OnGrabPart(SceneObjectPart part, LLVector3 offsetPos, IClientAPI remoteClient) { + part.StoreUndoState(); part.OnGrab(offsetPos, remoteClient); + } public virtual void OnGrabGroup(LLVector3 offsetPos, IClientAPI remoteClient) @@ -1382,8 +1399,9 @@ namespace OpenSim.Region.Environment.Scenes { m_parts.Add(newPart.UUID, newPart); } - + SetPartAsNonRoot(newPart); + } /// @@ -1758,6 +1776,7 @@ namespace OpenSim.Region.Environment.Scenes { LinkNonRootPart(part, oldGroupPosition, oldRootRotation); } + part.ClearUndoState(); } DetachFromBackup(objectGroup); @@ -1781,7 +1800,7 @@ namespace OpenSim.Region.Environment.Scenes public void DelinkFromGroup(uint partID) { SceneObjectPart linkPart = GetChildPart(partID); - + linkPart.ClearUndoState(); if (null != linkPart) { // m_log.DebugFormat( diff --git a/OpenSim/Region/Environment/Scenes/SceneObjectPart.cs b/OpenSim/Region/Environment/Scenes/SceneObjectPart.cs index 0db9b91..6cfe3e9 100644 --- a/OpenSim/Region/Environment/Scenes/SceneObjectPart.cs +++ b/OpenSim/Region/Environment/Scenes/SceneObjectPart.cs @@ -26,6 +26,7 @@ */ using System; +using System.Collections; using System.Collections.Generic; using System.Drawing; using System.Runtime.Serialization; @@ -106,6 +107,8 @@ namespace OpenSim.Region.Environment.Scenes [XmlIgnore] public LLVector3 m_attachedPos = LLVector3.Zero; [XmlIgnore] public LLUUID fromAssetID = LLUUID.Zero; + [XmlIgnore] public bool m_undoing = false; + public Int32 CreationDate; public uint ParentID = 0; @@ -123,6 +126,8 @@ namespace OpenSim.Region.Environment.Scenes public uint EveryoneMask = (uint)PermissionMask.None; public uint NextOwnerMask = (uint)PermissionMask.All; + private UndoStack m_undo = new UndoStack(5); + public LLObject.ObjectFlags Flags = LLObject.ObjectFlags.None; public uint ObjectFlags @@ -266,6 +271,40 @@ namespace OpenSim.Region.Environment.Scenes //return new LLQuaternion(axiomPartRotation.x, axiomPartRotation.y, axiomPartRotation.z, axiomPartRotation.w); } + + public void StoreUndoState() + { + if (!m_undoing) + { + if (m_parentGroup != null) + { + if (m_undo.Count > 0) + { + UndoState last = m_undo.Peek(); + if (last != null) + { + if (last.Compare(this)) + return; + } + } + + + if (m_parentGroup.GetSceneMaxUndo() > 0) + { + UndoState nUndo = new UndoState(this); + + m_undo.Push(nUndo); + + } + } + } + } + + public void ClearUndoState() + { + m_undo.Clear(); + StoreUndoState(); + } public LLVector3 GroupPosition { @@ -290,7 +329,9 @@ namespace OpenSim.Region.Environment.Scenes return m_groupPosition; } set - { + { + StoreUndoState(); + m_groupPosition = value; if (PhysActor != null) @@ -334,7 +375,10 @@ namespace OpenSim.Region.Environment.Scenes public LLVector3 OffsetPosition { get { return m_offsetPosition; } - set { m_offsetPosition = value; + set + { + StoreUndoState(); + m_offsetPosition = value; try { // Hack to get the child prim to update world positions in the physics engine @@ -380,6 +424,7 @@ namespace OpenSim.Region.Environment.Scenes } set { + StoreUndoState(); m_rotationOffset = value; if (PhysActor != null) @@ -650,7 +695,8 @@ namespace OpenSim.Region.Environment.Scenes { get { return m_shape.Scale; } set - { + { + StoreUndoState(); m_shape.Scale = value; TriggerScriptChangedEvent(Changed.SCALE); } @@ -759,7 +805,8 @@ namespace OpenSim.Region.Environment.Scenes LLObject.ObjectFlags.CreateSelected; TrimPermissions(); - + //m_undo = new UndoStack(ParentGroup.GetSceneMaxUndo()); + ScheduleFullUpdate(); } @@ -802,7 +849,7 @@ namespace OpenSim.Region.Environment.Scenes TrimPermissions(); // ApplyPhysics(); - + ScheduleFullUpdate(); } @@ -1982,6 +2029,7 @@ namespace OpenSim.Region.Environment.Scenes public void UpdateRotation(LLQuaternion rot) { + //StoreUndoState(); RotationOffset = new LLQuaternion(rot.X, rot.Y, rot.Z, rot.W); ScheduleTerseUpdate(); } @@ -2097,6 +2145,7 @@ namespace OpenSim.Region.Environment.Scenes /// public void Resize(LLVector3 scale) { + StoreUndoState(); m_shape.Scale = scale; ScheduleFullUpdate(); @@ -2522,5 +2571,15 @@ namespace OpenSim.Region.Environment.Scenes info.AddValue("PayPrice", PayPrice); } + + public void Undo() + { + if (m_undo.Count > 0) + { + UndoState goback = m_undo.Pop(); + if (goback != null) + goback.PlaybackState(this); + } + } } } diff --git a/OpenSim/Region/Environment/Scenes/UndoState.cs b/OpenSim/Region/Environment/Scenes/UndoState.cs new file mode 100644 index 0000000..65c8669 --- /dev/null +++ b/OpenSim/Region/Environment/Scenes/UndoState.cs @@ -0,0 +1,118 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSim Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Collections.Generic; +using libsecondlife; + +namespace OpenSim.Region.Environment.Scenes +{ + public class UndoState + { + public LLVector3 Position = LLVector3.Zero; + public LLVector3 Scale = LLVector3.Zero; + public LLQuaternion Rotation = LLQuaternion.Identity; + + public UndoState(LLVector3 pos, LLQuaternion rot, LLVector3 scale) + { + Position = pos; + Rotation = rot; + Scale = scale; + } + + public UndoState(SceneObjectPart part) + { + if (part != null) + { + if (part.ParentID == 0) + { + Position = part.AbsolutePosition; + Rotation = part.RotationOffset; + + } + else + { + Position = part.GroupPosition; + Rotation = part.RotationOffset; + Scale = part.Shape.Scale; + + } + } + } + + public bool Compare(SceneObjectPart part) + { + if (part != null) + { + if (part.ParentID == 0) + { + if (Position == part.AbsolutePosition && Rotation == part.RotationOffset) + return true; + else + return false; + } + else + { + if (Position == part.GroupPosition && Rotation == part.RotationOffset && Scale == part.Shape.Scale) + return true; + else + return false; + + } + } + return false; + } + + public void PlaybackState(SceneObjectPart part) + { + if (part != null) + { + part.m_undoing = true; + + if (part.ParentID == 0) + { + part.ParentGroup.AbsolutePosition = Position; + part.UpdateRotation(Rotation); + part.ParentGroup.ScheduleGroupForTerseUpdate(); + } + else + { + part.OffsetPosition = Position; + part.UpdateRotation(Rotation); + part.Resize(Scale); + part.ScheduleTerseUpdate(); + } + part.m_undoing = false; + + } + } + + public UndoState() + { + } + } +} diff --git a/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs b/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs index 9e57da5..59a2fd8 100644 --- a/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs +++ b/OpenSim/Region/Examples/SimpleModule/MyNpcCharacter.cs @@ -171,6 +171,7 @@ namespace OpenSim.Region.Examples.SimpleModule public event ScriptAnswer OnScriptAnswer; public event RequestPayPrice OnRequestPayPrice; + public event AgentSit OnUndo; #pragma warning restore 67 -- cgit v1.1