* 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.
0.6.0-stable
Teravus Ovares 2008-04-28 01:48:21 +00:00
parent 7693a7dac9
commit 1fb54b074c
9 changed files with 370 additions and 8 deletions

View File

@ -571,6 +571,8 @@ namespace OpenSim.Framework
event ScriptAnswer OnScriptAnswer; event ScriptAnswer OnScriptAnswer;
event AgentSit OnUndo;
LLVector3 StartPos { get; set; } LLVector3 StartPos { get; set; }
LLUUID AgentId { get; } LLUUID AgentId { get; }

View File

@ -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
{
/// <summary>
/// Undo stack. Deletes entries beyond a certain capacity
/// </summary>
/// <typeparam name="T"></typeparam>
[Serializable]
public class UndoStack<T>
{
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;
}
}
}
}

View File

@ -238,6 +238,7 @@ namespace OpenSim.Region.ClientStack
private ScriptAnswer handlerScriptAnswer = null; private ScriptAnswer handlerScriptAnswer = null;
private RequestPayPrice handlerRequestPayPrice = null; private RequestPayPrice handlerRequestPayPrice = null;
private ObjectDeselect handlerObjectDetach = null; private ObjectDeselect handlerObjectDetach = null;
private AgentSit handlerOnUndo = null;
/* Properties */ /* Properties */
@ -799,6 +800,7 @@ namespace OpenSim.Region.ClientStack
public event ScriptAnswer OnScriptAnswer; public event ScriptAnswer OnScriptAnswer;
public event RequestPayPrice OnRequestPayPrice; public event RequestPayPrice OnRequestPayPrice;
public event AgentSit OnUndo;
#region Scene/Avatar to Client #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. // That means multiple object perms may be updated in a single packet.
break; 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: case PacketType.ObjectDuplicateOnRay:
ObjectDuplicateOnRayPacket dupeOnRay = (ObjectDuplicateOnRayPacket)Pack; ObjectDuplicateOnRayPacket dupeOnRay = (ObjectDuplicateOnRayPacket)Pack;

View File

@ -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();
}
}
/// <summary> /// <summary>
/// Event Handling routine for Attach Object /// Event Handling routine for Attach Object
/// </summary> /// </summary>

View File

@ -78,6 +78,7 @@ namespace OpenSim.Region.Environment.Scenes
private readonly Mutex updateLock; private readonly Mutex updateLock;
public bool m_physicalPrim; public bool m_physicalPrim;
public bool m_seeIntoRegionFromNeighbor; public bool m_seeIntoRegionFromNeighbor;
public int MaxUndoCount = 5;
private int m_RestartTimerCounter; private int m_RestartTimerCounter;
private readonly Timer m_restartTimer = new Timer(15000); // Wait before firing private readonly Timer m_restartTimer = new Timer(15000); // Wait before firing
private int m_incrementsof15seconds = 0; private int m_incrementsof15seconds = 0;
@ -1628,6 +1629,7 @@ namespace OpenSim.Region.Environment.Scenes
client.OnTeleportHomeRequest += TeleportClientHome; client.OnTeleportHomeRequest += TeleportClientHome;
client.OnSetStartLocationRequest += SetHomeRezPoint; client.OnSetStartLocationRequest += SetHomeRezPoint;
client.OnUndo += m_innerScene.HandleUndo;
EventManager.TriggerOnNewClient(client); EventManager.TriggerOnNewClient(client);
} }

View File

@ -378,6 +378,7 @@ namespace OpenSim.Region.Environment.Scenes
part.RegionHandle = m_regionHandle; part.RegionHandle = m_regionHandle;
part.TrimPermissions(); part.TrimPermissions();
part.StoreUndoState();
} }
break; break;
case XmlNodeType.EndElement: case XmlNodeType.EndElement:
@ -436,6 +437,7 @@ namespace OpenSim.Region.Environment.Scenes
{ {
SceneObjectPart Part = SceneObjectPart.FromXml(reader); SceneObjectPart Part = SceneObjectPart.FromXml(reader);
AddPart(Part); AddPart(Part);
Part.StoreUndoState();
} }
else else
{ {
@ -703,6 +705,8 @@ namespace OpenSim.Region.Environment.Scenes
m_rootPart.ApplyPhysics(m_rootPart.ObjectFlags, m_scene.m_physicalPrim); m_rootPart.ApplyPhysics(m_rootPart.ObjectFlags, m_scene.m_physicalPrim);
AttachToBackup(); AttachToBackup();
m_rootPart.ScheduleFullUpdate(); m_rootPart.ScheduleFullUpdate();
m_rootPart.ClearUndoState();
} }
public void DetachToInventoryPrep() public void DetachToInventoryPrep()
{ {
@ -731,6 +735,7 @@ namespace OpenSim.Region.Environment.Scenes
private void SetPartAsNonRoot(SceneObjectPart part) private void SetPartAsNonRoot(SceneObjectPart part)
{ {
part.ParentID = m_rootPart.LocalId; part.ParentID = m_rootPart.LocalId;
part.ClearUndoState();
} }
/// <summary> /// <summary>
@ -783,6 +788,7 @@ namespace OpenSim.Region.Environment.Scenes
try try
{ {
m_parts.Add(part.UUID, part); m_parts.Add(part.UUID, part);
} }
catch (Exception e) catch (Exception e)
{ {
@ -803,6 +809,7 @@ namespace OpenSim.Region.Environment.Scenes
if (part.UUID != m_rootPart.UUID) if (part.UUID != m_rootPart.UUID)
{ {
part.ParentID = m_rootPart.LocalId; part.ParentID = m_rootPart.LocalId;
} }
} }
} }
@ -815,10 +822,17 @@ namespace OpenSim.Region.Environment.Scenes
foreach (SceneObjectPart part in m_parts.Values) foreach (SceneObjectPart part in m_parts.Values)
{ {
part.UUID = LLUUID.Random(); part.UUID = LLUUID.Random();
} }
} }
} }
// helper provided for parts.
public int GetSceneMaxUndo()
{
if (m_scene != null)
return m_scene.MaxUndoCount;
return 5;
}
public void ResetChildPrimPhysicsPositions() public void ResetChildPrimPhysicsPositions()
{ {
AbsolutePosition = AbsolutePosition; AbsolutePosition = AbsolutePosition;
@ -845,12 +859,15 @@ namespace OpenSim.Region.Environment.Scenes
{ {
SceneObjectPart part = GetChildPart(localId); SceneObjectPart part = GetChildPart(localId);
OnGrabPart(part, offsetPos, remoteClient); OnGrabPart(part, offsetPos, remoteClient);
} }
} }
public virtual void OnGrabPart(SceneObjectPart part, LLVector3 offsetPos, IClientAPI remoteClient) public virtual void OnGrabPart(SceneObjectPart part, LLVector3 offsetPos, IClientAPI remoteClient)
{ {
part.StoreUndoState();
part.OnGrab(offsetPos, remoteClient); part.OnGrab(offsetPos, remoteClient);
} }
public virtual void OnGrabGroup(LLVector3 offsetPos, IClientAPI remoteClient) public virtual void OnGrabGroup(LLVector3 offsetPos, IClientAPI remoteClient)
@ -1384,6 +1401,7 @@ namespace OpenSim.Region.Environment.Scenes
} }
SetPartAsNonRoot(newPart); SetPartAsNonRoot(newPart);
} }
/// <summary> /// <summary>
@ -1758,6 +1776,7 @@ namespace OpenSim.Region.Environment.Scenes
{ {
LinkNonRootPart(part, oldGroupPosition, oldRootRotation); LinkNonRootPart(part, oldGroupPosition, oldRootRotation);
} }
part.ClearUndoState();
} }
DetachFromBackup(objectGroup); DetachFromBackup(objectGroup);
@ -1781,7 +1800,7 @@ namespace OpenSim.Region.Environment.Scenes
public void DelinkFromGroup(uint partID) public void DelinkFromGroup(uint partID)
{ {
SceneObjectPart linkPart = GetChildPart(partID); SceneObjectPart linkPart = GetChildPart(partID);
linkPart.ClearUndoState();
if (null != linkPart) if (null != linkPart)
{ {
// m_log.DebugFormat( // m_log.DebugFormat(

View File

@ -26,6 +26,7 @@
*/ */
using System; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Drawing; using System.Drawing;
using System.Runtime.Serialization; using System.Runtime.Serialization;
@ -106,6 +107,8 @@ namespace OpenSim.Region.Environment.Scenes
[XmlIgnore] public LLVector3 m_attachedPos = LLVector3.Zero; [XmlIgnore] public LLVector3 m_attachedPos = LLVector3.Zero;
[XmlIgnore] public LLUUID fromAssetID = LLUUID.Zero; [XmlIgnore] public LLUUID fromAssetID = LLUUID.Zero;
[XmlIgnore] public bool m_undoing = false;
public Int32 CreationDate; public Int32 CreationDate;
public uint ParentID = 0; public uint ParentID = 0;
@ -123,6 +126,8 @@ namespace OpenSim.Region.Environment.Scenes
public uint EveryoneMask = (uint)PermissionMask.None; public uint EveryoneMask = (uint)PermissionMask.None;
public uint NextOwnerMask = (uint)PermissionMask.All; public uint NextOwnerMask = (uint)PermissionMask.All;
private UndoStack<UndoState> m_undo = new UndoStack<UndoState>(5);
public LLObject.ObjectFlags Flags = LLObject.ObjectFlags.None; public LLObject.ObjectFlags Flags = LLObject.ObjectFlags.None;
public uint ObjectFlags public uint ObjectFlags
@ -267,6 +272,40 @@ namespace OpenSim.Region.Environment.Scenes
} }
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 public LLVector3 GroupPosition
{ {
get get
@ -291,6 +330,8 @@ namespace OpenSim.Region.Environment.Scenes
} }
set set
{ {
StoreUndoState();
m_groupPosition = value; m_groupPosition = value;
if (PhysActor != null) if (PhysActor != null)
@ -334,7 +375,10 @@ namespace OpenSim.Region.Environment.Scenes
public LLVector3 OffsetPosition public LLVector3 OffsetPosition
{ {
get { return m_offsetPosition; } get { return m_offsetPosition; }
set { m_offsetPosition = value; set
{
StoreUndoState();
m_offsetPosition = value;
try try
{ {
// Hack to get the child prim to update world positions in the physics engine // Hack to get the child prim to update world positions in the physics engine
@ -380,6 +424,7 @@ namespace OpenSim.Region.Environment.Scenes
} }
set set
{ {
StoreUndoState();
m_rotationOffset = value; m_rotationOffset = value;
if (PhysActor != null) if (PhysActor != null)
@ -651,6 +696,7 @@ namespace OpenSim.Region.Environment.Scenes
get { return m_shape.Scale; } get { return m_shape.Scale; }
set set
{ {
StoreUndoState();
m_shape.Scale = value; m_shape.Scale = value;
TriggerScriptChangedEvent(Changed.SCALE); TriggerScriptChangedEvent(Changed.SCALE);
} }
@ -759,6 +805,7 @@ namespace OpenSim.Region.Environment.Scenes
LLObject.ObjectFlags.CreateSelected; LLObject.ObjectFlags.CreateSelected;
TrimPermissions(); TrimPermissions();
//m_undo = new UndoStack<UndoState>(ParentGroup.GetSceneMaxUndo());
ScheduleFullUpdate(); ScheduleFullUpdate();
} }
@ -1982,6 +2029,7 @@ namespace OpenSim.Region.Environment.Scenes
public void UpdateRotation(LLQuaternion rot) public void UpdateRotation(LLQuaternion rot)
{ {
//StoreUndoState();
RotationOffset = new LLQuaternion(rot.X, rot.Y, rot.Z, rot.W); RotationOffset = new LLQuaternion(rot.X, rot.Y, rot.Z, rot.W);
ScheduleTerseUpdate(); ScheduleTerseUpdate();
} }
@ -2097,6 +2145,7 @@ namespace OpenSim.Region.Environment.Scenes
/// <param name="scale"></param> /// <param name="scale"></param>
public void Resize(LLVector3 scale) public void Resize(LLVector3 scale)
{ {
StoreUndoState();
m_shape.Scale = scale; m_shape.Scale = scale;
ScheduleFullUpdate(); ScheduleFullUpdate();
@ -2522,5 +2571,15 @@ namespace OpenSim.Region.Environment.Scenes
info.AddValue("PayPrice", PayPrice); info.AddValue("PayPrice", PayPrice);
} }
public void Undo()
{
if (m_undo.Count > 0)
{
UndoState goback = m_undo.Pop();
if (goback != null)
goback.PlaybackState(this);
}
}
} }
} }

View File

@ -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()
{
}
}
}

View File

@ -171,6 +171,7 @@ namespace OpenSim.Region.Examples.SimpleModule
public event ScriptAnswer OnScriptAnswer; public event ScriptAnswer OnScriptAnswer;
public event RequestPayPrice OnRequestPayPrice; public event RequestPayPrice OnRequestPayPrice;
public event AgentSit OnUndo;
#pragma warning restore 67 #pragma warning restore 67