From f41fc4eb25ade48a358511564f3911a4605c1c31 Mon Sep 17 00:00:00 2001
From: Justin Clark-Casey (justincc)
Date: Wed, 5 Jun 2013 22:20:48 +0100
Subject: Avoid a deadlock where a script can attempt to take a
ScriptInstance.m_Scripts lock then a lock on SP.m_attachments whilst
SP.MakeRootAgent() attempts to take in the opposite order.
This is because scripts (at least on XEngine) start unsuspended - deceptively the ResumeScripts() calls in various places in the code are actually completely redundant (and useless).
The solution chosen here is to use a copy of the SP attachments and not have the list locked whilst creating the scripts when an avatar enters the region.
This looks to address http://opensimulator.org/mantis/view.php?id=6557
---
OpenSim/Region/Framework/Scenes/ScenePresence.cs | 32 ++++++++++++++++--------
1 file changed, 21 insertions(+), 11 deletions(-)
diff --git a/OpenSim/Region/Framework/Scenes/ScenePresence.cs b/OpenSim/Region/Framework/Scenes/ScenePresence.cs
index b8ff7f7..bab14dd 100644
--- a/OpenSim/Region/Framework/Scenes/ScenePresence.cs
+++ b/OpenSim/Region/Framework/Scenes/ScenePresence.cs
@@ -121,6 +121,8 @@ namespace OpenSim.Region.Framework.Scenes
///
/// TODO: For some reason, we effectively have a list both here and in Appearance. Need to work out if this is
/// necessary.
+ /// NOTE: To avoid deadlocks, do not lock m_attachments and then perform other tasks under that lock. Take a copy
+ /// of the list and act on that instead.
///
private List m_attachments = new List();
@@ -971,19 +973,27 @@ namespace OpenSim.Region.Framework.Scenes
// and CHANGED_REGION) when the attachments have been rezzed in the new region. This cannot currently
// be done in AttachmentsModule.CopyAttachments(AgentData ad, IScenePresence sp) itself since we are
// not transporting the required data.
- lock (m_attachments)
+ //
+ // We must take a copy of the attachments list here (rather than locking) to avoid a deadlock where a script in one of
+ // the attachments may start processing an event (which locks ScriptInstance.m_Script) that then calls a method here
+ // which needs to lock m_attachments. ResumeScripts() needs to take a ScriptInstance.m_Script lock to try to unset the Suspend status.
+ //
+ // FIXME: In theory, this deadlock should not arise since scripts should not be processing events until ResumeScripts().
+ // But XEngine starts all scripts unsuspended. Starting them suspended will not currently work because script rezzing
+ // is placed in an asynchronous queue in XEngine and so the ResumeScripts() call will almost certainly execute before the
+ // script is rezzed. This means the ResumeScripts() does absolutely nothing when using XEngine.
+ List attachments = GetAttachments();
+
+ if (attachments.Count > 0)
{
- if (HasAttachments())
- {
- m_log.DebugFormat(
- "[SCENE PRESENCE]: Restarting scripts in attachments for {0} in {1}", Name, Scene.Name);
+ m_log.DebugFormat(
+ "[SCENE PRESENCE]: Restarting scripts in attachments for {0} in {1}", Name, Scene.Name);
- // Resume scripts
- foreach (SceneObjectGroup sog in m_attachments)
- {
- sog.RootPart.ParentGroup.CreateScriptInstances(0, false, m_scene.DefaultScriptEngine, GetStateSource());
- sog.ResumeScripts();
- }
+ // Resume scripts
+ foreach (SceneObjectGroup sog in attachments)
+ {
+ sog.RootPart.ParentGroup.CreateScriptInstances(0, false, m_scene.DefaultScriptEngine, GetStateSource());
+ sog.ResumeScripts();
}
}
}
--
cgit v1.1
From a7dbafb0e383ca5043a71284cdc35569acc5e2be Mon Sep 17 00:00:00 2001
From: Melanie
Date: Wed, 5 Jun 2013 23:42:50 +0100
Subject: Port Avination's inventory send throttling
---
OpenSim/Framework/Util.cs | 108 +++++++++
.../Linden/Caps/WebFetchInvDescModule.cs | 265 +++++++++++++++++----
prebuild.xml | 1 +
3 files changed, 322 insertions(+), 52 deletions(-)
diff --git a/OpenSim/Framework/Util.cs b/OpenSim/Framework/Util.cs
index ada4e89..7f0850f 100644
--- a/OpenSim/Framework/Util.cs
+++ b/OpenSim/Framework/Util.cs
@@ -2233,4 +2233,112 @@ namespace OpenSim.Framework
return str.Replace("_", "\\_").Replace("%", "\\%");
}
}
+
+ public class DoubleQueue where T:class
+ {
+ private Queue m_lowQueue = new Queue();
+ private Queue m_highQueue = new Queue();
+
+ private object m_syncRoot = new object();
+ private Semaphore m_s = new Semaphore(0, 1);
+
+ public DoubleQueue()
+ {
+ }
+
+ public virtual int Count
+ {
+ get { return m_highQueue.Count + m_lowQueue.Count; }
+ }
+
+ public virtual void Enqueue(T data)
+ {
+ Enqueue(m_lowQueue, data);
+ }
+
+ public virtual void EnqueueLow(T data)
+ {
+ Enqueue(m_lowQueue, data);
+ }
+
+ public virtual void EnqueueHigh(T data)
+ {
+ Enqueue(m_highQueue, data);
+ }
+
+ private void Enqueue(Queue q, T data)
+ {
+ lock (m_syncRoot)
+ {
+ m_lowQueue.Enqueue(data);
+ m_s.WaitOne(0);
+ m_s.Release();
+ }
+ }
+
+ public virtual T Dequeue()
+ {
+ return Dequeue(Timeout.Infinite);
+ }
+
+ public virtual T Dequeue(int tmo)
+ {
+ return Dequeue(TimeSpan.FromMilliseconds(tmo));
+ }
+
+ public virtual T Dequeue(TimeSpan wait)
+ {
+ T res = null;
+
+ if (!Dequeue(wait, ref res))
+ return null;
+
+ return res;
+ }
+
+ public bool Dequeue(int timeout, ref T res)
+ {
+ return Dequeue(TimeSpan.FromMilliseconds(timeout), ref res);
+ }
+
+ public bool Dequeue(TimeSpan wait, ref T res)
+ {
+ if (!m_s.WaitOne(wait))
+ return false;
+
+ lock (m_syncRoot)
+ {
+ if (m_highQueue.Count > 0)
+ res = m_highQueue.Dequeue();
+ else
+ res = m_lowQueue.Dequeue();
+
+ if (m_highQueue.Count == 0 && m_lowQueue.Count == 0)
+ return true;
+
+ try
+ {
+ m_s.Release();
+ }
+ catch
+ {
+ }
+
+ return true;
+ }
+ }
+
+ public virtual void Clear()
+ {
+
+ lock (m_syncRoot)
+ {
+ // Make sure sem count is 0
+ m_s.WaitOne(0);
+
+ m_lowQueue.Clear();
+ m_highQueue.Clear();
+ }
+ }
+ }
}
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/WebFetchInvDescModule.cs b/OpenSim/Region/ClientStack/Linden/Caps/WebFetchInvDescModule.cs
index 6890f4a..7dd9770 100644
--- a/OpenSim/Region/ClientStack/Linden/Caps/WebFetchInvDescModule.cs
+++ b/OpenSim/Region/ClientStack/Linden/Caps/WebFetchInvDescModule.cs
@@ -27,18 +27,25 @@
using System;
using System.Collections;
+using System.Collections.Generic;
using System.Reflection;
+using System.Threading;
using log4net;
using Nini.Config;
using Mono.Addins;
using OpenMetaverse;
using OpenSim.Framework;
+using OpenSim.Framework.Monitoring;
+using OpenSim.Framework.Servers;
using OpenSim.Framework.Servers.HttpServer;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.Framework.Scenes;
+using OpenSim.Framework.Capabilities;
using OpenSim.Services.Interfaces;
using Caps = OpenSim.Framework.Capabilities.Caps;
using OpenSim.Capabilities.Handlers;
+using OpenMetaverse;
+using OpenMetaverse.StructuredData;
namespace OpenSim.Region.ClientStack.Linden
{
@@ -48,67 +55,74 @@ namespace OpenSim.Region.ClientStack.Linden
[Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "WebFetchInvDescModule")]
public class WebFetchInvDescModule : INonSharedRegionModule
{
-// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+ class aPollRequest
+ {
+ public PollServiceInventoryEventArgs thepoll;
+ public UUID reqID;
+ public Hashtable request;
+ public ScenePresence presence;
+ public List folders;
+ }
+
+ private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private Scene m_scene;
private IInventoryService m_InventoryService;
private ILibraryService m_LibraryService;
- private bool m_Enabled;
+ private static WebFetchInvDescHandler m_webFetchHandler;
- private string m_fetchInventoryDescendents2Url;
- private string m_webFetchInventoryDescendentsUrl;
+ private Dictionary m_capsDict = new Dictionary();
+ private static Thread[] m_workerThreads = null;
- private WebFetchInvDescHandler m_webFetchHandler;
+ private static DoubleQueue m_queue =
+ new DoubleQueue();
#region ISharedRegionModule Members
public void Initialise(IConfigSource source)
{
- IConfig config = source.Configs["ClientStack.LindenCaps"];
- if (config == null)
- return;
-
- m_fetchInventoryDescendents2Url = config.GetString("Cap_FetchInventoryDescendents2", string.Empty);
- m_webFetchInventoryDescendentsUrl = config.GetString("Cap_WebFetchInventoryDescendents", string.Empty);
-
- if (m_fetchInventoryDescendents2Url != string.Empty || m_webFetchInventoryDescendentsUrl != string.Empty)
- {
- m_Enabled = true;
- }
}
public void AddRegion(Scene s)
{
- if (!m_Enabled)
- return;
-
m_scene = s;
}
public void RemoveRegion(Scene s)
{
- if (!m_Enabled)
- return;
-
m_scene.EventManager.OnRegisterCaps -= RegisterCaps;
+ m_scene.EventManager.OnDeregisterCaps -= DeregisterCaps;
m_scene = null;
}
public void RegionLoaded(Scene s)
{
- if (!m_Enabled)
- return;
-
m_InventoryService = m_scene.InventoryService;
m_LibraryService = m_scene.LibraryService;
// We'll reuse the same handler for all requests.
- if (m_fetchInventoryDescendents2Url == "localhost" || m_webFetchInventoryDescendentsUrl == "localhost")
- m_webFetchHandler = new WebFetchInvDescHandler(m_InventoryService, m_LibraryService);
+ m_webFetchHandler = new WebFetchInvDescHandler(m_InventoryService, m_LibraryService);
m_scene.EventManager.OnRegisterCaps += RegisterCaps;
+ m_scene.EventManager.OnDeregisterCaps += DeregisterCaps;
+
+ if (m_workerThreads == null)
+ {
+ m_workerThreads = new Thread[2];
+
+ for (uint i = 0; i < 2; i++)
+ {
+ m_workerThreads[i] = Watchdog.StartThread(DoInventoryRequests,
+ String.Format("InventoryWorkerThread{0}", i),
+ ThreadPriority.Normal,
+ false,
+ true,
+ null,
+ int.MaxValue);
+ }
+ }
}
public void PostInitialise()
@@ -126,43 +140,190 @@ namespace OpenSim.Region.ClientStack.Linden
#endregion
- private void RegisterCaps(UUID agentID, Caps caps)
+ ~WebFetchInvDescModule()
{
- if (m_webFetchInventoryDescendentsUrl != "")
- RegisterFetchCap(agentID, caps, "WebFetchInventoryDescendents", m_webFetchInventoryDescendentsUrl);
-
- if (m_fetchInventoryDescendents2Url != "")
- RegisterFetchCap(agentID, caps, "FetchInventoryDescendents2", m_fetchInventoryDescendents2Url);
+ foreach (Thread t in m_workerThreads)
+ Watchdog.AbortThread(t.ManagedThreadId);
}
- private void RegisterFetchCap(UUID agentID, Caps caps, string capName, string url)
+ private class PollServiceInventoryEventArgs : PollServiceEventArgs
{
- string capUrl;
+ private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+
+ private Dictionary responses =
+ new Dictionary();
+
+ private Scene m_scene;
- if (url == "localhost")
+ public PollServiceInventoryEventArgs(Scene scene, UUID pId) :
+ base(null, null, null, null, pId)
{
- capUrl = "/CAPS/" + UUID.Random();
+ m_scene = scene;
+
+ HasEvents = (x, y) => { lock (responses) return responses.ContainsKey(x); };
+ GetEvents = (x, y, z) =>
+ {
+ lock (responses)
+ {
+ try
+ {
+ return responses[x];
+ }
+ finally
+ {
+ responses.Remove(x);
+ }
+ }
+ };
+
+ Request = (x, y) =>
+ {
+ ScenePresence sp = m_scene.GetScenePresence(Id);
+ if (sp == null)
+ {
+ m_log.ErrorFormat("[INVENTORY]: Unable to find ScenePresence for {0}", Id);
+ return;
+ }
+
+ aPollRequest reqinfo = new aPollRequest();
+ reqinfo.thepoll = this;
+ reqinfo.reqID = x;
+ reqinfo.request = y;
+ reqinfo.presence = sp;
+ reqinfo.folders = new List();
+
+ // Decode the request here
+ string request = y["body"].ToString();
+
+ request = request.Replace("00000000-0000-0000-0000-000000000000", "00000000-0000-0000-0000-000000000000");
+
+ request = request.Replace("fetch_folders0", "fetch_folders0");
+ request = request.Replace("fetch_folders1", "fetch_folders1");
+
+ Hashtable hash = new Hashtable();
+ try
+ {
+ hash = (Hashtable)LLSD.LLSDDeserialize(Utils.StringToBytes(request));
+ }
+ catch (LLSD.LLSDParseException e)
+ {
+ m_log.ErrorFormat("[INVENTORY]: Fetch error: {0}{1}" + e.Message, e.StackTrace);
+ m_log.Error("Request: " + request);
+ return;
+ }
+ catch (System.Xml.XmlException)
+ {
+ m_log.ErrorFormat("[INVENTORY]: XML Format error");
+ }
+
+ ArrayList foldersrequested = (ArrayList)hash["folders"];
+
+ bool highPriority = false;
+
+ for (int i = 0; i < foldersrequested.Count; i++)
+ {
+ Hashtable inventoryhash = (Hashtable)foldersrequested[i];
+ string folder = inventoryhash["folder_id"].ToString();
+ UUID folderID;
+ if (UUID.TryParse(folder, out folderID))
+ {
+ if (!reqinfo.folders.Contains(folderID))
+ {
+ //TODO: Port COF handling from Avination
+ reqinfo.folders.Add(folderID);
+ }
+ }
+ }
+
+ if (highPriority)
+ m_queue.EnqueueHigh(reqinfo);
+ else
+ m_queue.EnqueueLow(reqinfo);
+ };
+
+ NoEvents = (x, y) =>
+ {
+/*
+ lock (requests)
+ {
+ Hashtable request = requests.Find(id => id["RequestID"].ToString() == x.ToString());
+ requests.Remove(request);
+ }
+*/
+ Hashtable response = new Hashtable();
+
+ response["int_response_code"] = 500;
+ response["str_response_string"] = "Script timeout";
+ response["content_type"] = "text/plain";
+ response["keepalive"] = false;
+ response["reusecontext"] = false;
+
+ return response;
+ };
+ }
- IRequestHandler reqHandler
- = new RestStreamHandler(
- "POST",
- capUrl,
- m_webFetchHandler.FetchInventoryDescendentsRequest,
- "FetchInventoryDescendents2",
- agentID.ToString());
+ public void Process(aPollRequest requestinfo)
+ {
+ UUID requestID = requestinfo.reqID;
+
+ Hashtable response = new Hashtable();
+
+ response["int_response_code"] = 200;
+ response["content_type"] = "text/plain";
+ response["keepalive"] = false;
+ response["reusecontext"] = false;
- caps.RegisterHandler(capName, reqHandler);
+ response["str_response_string"] = m_webFetchHandler.FetchInventoryDescendentsRequest(
+ requestinfo.request["body"].ToString(), String.Empty, String.Empty, null, null);
+
+ lock (responses)
+ responses[requestID] = response;
}
- else
+ }
+
+ private void RegisterCaps(UUID agentID, Caps caps)
+ {
+ string capUrl = "/CAPS/" + UUID.Random() + "/";
+
+ // Register this as a poll service
+ PollServiceInventoryEventArgs args = new PollServiceInventoryEventArgs(m_scene, agentID);
+
+ MainServer.Instance.AddPollServiceHTTPHandler(capUrl, args);
+
+ string hostName = m_scene.RegionInfo.ExternalHostName;
+ uint port = (MainServer.Instance == null) ? 0 : MainServer.Instance.Port;
+ string protocol = "http";
+
+ if (MainServer.Instance.UseSSL)
{
- capUrl = url;
+ hostName = MainServer.Instance.SSLCommonName;
+ port = MainServer.Instance.SSLPort;
+ protocol = "https";
+ }
+ caps.RegisterHandler("FetchInventoryDescendents2", String.Format("{0}://{1}:{2}{3}", protocol, hostName, port, capUrl));
+
+ m_capsDict[agentID] = capUrl;
+ }
- caps.RegisterHandler(capName, capUrl);
+ private void DeregisterCaps(UUID agentID, Caps caps)
+ {
+ string capUrl;
+
+ if (m_capsDict.TryGetValue(agentID, out capUrl))
+ {
+ MainServer.Instance.RemoveHTTPHandler("", capUrl);
+ m_capsDict.Remove(agentID);
}
+ }
-// m_log.DebugFormat(
-// "[WEB FETCH INV DESC MODULE]: Registered capability {0} at {1} in region {2} for {3}",
-// capName, capUrl, m_scene.RegionInfo.RegionName, agentID);
+ private void DoInventoryRequests()
+ {
+ while (true)
+ {
+ aPollRequest poolreq = m_queue.Dequeue();
+
+ poolreq.thepoll.Process(poolreq);
+ }
}
}
-}
\ No newline at end of file
+}
diff --git a/prebuild.xml b/prebuild.xml
index 03cac76..5531558 100644
--- a/prebuild.xml
+++ b/prebuild.xml
@@ -1531,6 +1531,7 @@
+
--
cgit v1.1
From e1d98c9e4c579c9feb6bcc4e7473e2372f496fdb Mon Sep 17 00:00:00 2001
From: Melanie
Date: Thu, 6 Jun 2013 02:25:19 +0100
Subject: Committing Avination's Keyframe module. This is not hooked up yet and
will do nothing. More commits to follow.
---
OpenSim/Region/Framework/Scenes/KeyframeMotion.cs | 766 ++++++++++++++++++++++
1 file changed, 766 insertions(+)
create mode 100644 OpenSim/Region/Framework/Scenes/KeyframeMotion.cs
diff --git a/OpenSim/Region/Framework/Scenes/KeyframeMotion.cs b/OpenSim/Region/Framework/Scenes/KeyframeMotion.cs
new file mode 100644
index 0000000..d773ee7
--- /dev/null
+++ b/OpenSim/Region/Framework/Scenes/KeyframeMotion.cs
@@ -0,0 +1,766 @@
+// Proprietary code of Avination Virtual Limited
+// (c) 2012 Melanie Thielker
+//
+
+using System;
+using System.Timers;
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+using System.Diagnostics;
+using System.Reflection;
+using System.Threading;
+using OpenMetaverse;
+using OpenSim.Framework;
+using OpenSim.Region.Framework.Interfaces;
+using OpenSim.Region.Physics.Manager;
+using OpenSim.Region.Framework.Scenes.Serialization;
+using System.Runtime.Serialization.Formatters.Binary;
+using System.Runtime.Serialization;
+using Timer = System.Timers.Timer;
+using log4net;
+
+namespace OpenSim.Region.Framework.Scenes
+{
+ public class KeyframeTimer
+ {
+ private static Dictionarym_timers =
+ new Dictionary();
+
+ private Timer m_timer;
+ private Dictionary m_motions = new Dictionary();
+ private object m_lockObject = new object();
+ private object m_timerLock = new object();
+ private const double m_tickDuration = 50.0;
+ private Scene m_scene;
+
+ public double TickDuration
+ {
+ get { return m_tickDuration; }
+ }
+
+ public KeyframeTimer(Scene scene)
+ {
+ m_timer = new Timer();
+ m_timer.Interval = TickDuration;
+ m_timer.AutoReset = true;
+ m_timer.Elapsed += OnTimer;
+
+ m_scene = scene;
+
+ m_timer.Start();
+ }
+
+ private void OnTimer(object sender, ElapsedEventArgs ea)
+ {
+ if (!Monitor.TryEnter(m_timerLock))
+ return;
+
+ try
+ {
+ List motions;
+
+ lock (m_lockObject)
+ {
+ motions = new List(m_motions.Keys);
+ }
+
+ foreach (KeyframeMotion m in motions)
+ {
+ try
+ {
+ m.OnTimer(TickDuration);
+ }
+ catch (Exception inner)
+ {
+ // Don't stop processing
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ // Keep running no matter what
+ }
+ finally
+ {
+ Monitor.Exit(m_timerLock);
+ }
+ }
+
+ public static void Add(KeyframeMotion motion)
+ {
+ KeyframeTimer timer;
+
+ if (motion.Scene == null)
+ return;
+
+ lock (m_timers)
+ {
+ if (!m_timers.TryGetValue(motion.Scene, out timer))
+ {
+ timer = new KeyframeTimer(motion.Scene);
+ m_timers[motion.Scene] = timer;
+ }
+ }
+
+ lock (timer.m_lockObject)
+ {
+ timer.m_motions[motion] = null;
+ }
+ }
+
+ public static void Remove(KeyframeMotion motion)
+ {
+ KeyframeTimer timer;
+
+ if (motion.Scene == null)
+ return;
+
+ lock (m_timers)
+ {
+ if (!m_timers.TryGetValue(motion.Scene, out timer))
+ {
+ return;
+ }
+ }
+
+ lock (timer.m_lockObject)
+ {
+ timer.m_motions.Remove(motion);
+ }
+ }
+ }
+
+ [Serializable]
+ public class KeyframeMotion
+ {
+ private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+
+ public enum PlayMode : int
+ {
+ Forward = 0,
+ Reverse = 1,
+ Loop = 2,
+ PingPong = 3
+ };
+
+ [Flags]
+ public enum DataFormat : int
+ {
+ Translation = 2,
+ Rotation = 1
+ }
+
+ [Serializable]
+ public struct Keyframe
+ {
+ public Vector3? Position;
+ public Quaternion? Rotation;
+ public Quaternion StartRotation;
+ public int TimeMS;
+ public int TimeTotal;
+ public Vector3 AngularVelocity;
+ };
+
+ private Vector3 m_serializedPosition;
+ private Vector3 m_basePosition;
+ private Quaternion m_baseRotation;
+
+ private Keyframe m_currentFrame;
+
+ private List m_frames = new List();
+
+ private Keyframe[] m_keyframes;
+
+ // skip timer events.
+ //timer.stop doesn't assure there aren't event threads still being fired
+ [NonSerialized()]
+ private bool m_timerStopped;
+
+ [NonSerialized()]
+ private bool m_isCrossing;
+
+ [NonSerialized()]
+ private bool m_waitingCrossing;
+
+ // retry position for cross fail
+ [NonSerialized()]
+ private Vector3 m_nextPosition;
+
+ [NonSerialized()]
+ private SceneObjectGroup m_group;
+
+ private PlayMode m_mode = PlayMode.Forward;
+ private DataFormat m_data = DataFormat.Translation | DataFormat.Rotation;
+
+ private bool m_running = false;
+
+ [NonSerialized()]
+ private bool m_selected = false;
+
+ private int m_iterations = 0;
+
+ private int m_skipLoops = 0;
+
+ [NonSerialized()]
+ private Scene m_scene;
+
+ public Scene Scene
+ {
+ get { return m_scene; }
+ }
+
+ public DataFormat Data
+ {
+ get { return m_data; }
+ }
+
+ public bool Selected
+ {
+ set
+ {
+ if (m_group != null)
+ {
+ if (!value)
+ {
+ // Once we're let go, recompute positions
+ if (m_selected)
+ UpdateSceneObject(m_group);
+ }
+ else
+ {
+ // Save selection position in case we get moved
+ if (!m_selected)
+ {
+ StopTimer();
+ m_serializedPosition = m_group.AbsolutePosition;
+ }
+ }
+ }
+ m_isCrossing = false;
+ m_waitingCrossing = false;
+ m_selected = value;
+ }
+ }
+
+ private void StartTimer()
+ {
+ KeyframeTimer.Add(this);
+ m_timerStopped = false;
+ }
+
+ private void StopTimer()
+ {
+ m_timerStopped = true;
+ KeyframeTimer.Remove(this);
+ }
+
+ public static KeyframeMotion FromData(SceneObjectGroup grp, Byte[] data)
+ {
+ KeyframeMotion newMotion = null;
+
+ try
+ {
+ MemoryStream ms = new MemoryStream(data);
+ BinaryFormatter fmt = new BinaryFormatter();
+
+ newMotion = (KeyframeMotion)fmt.Deserialize(ms);
+
+ newMotion.m_group = grp;
+
+ if (grp != null)
+ {
+ newMotion.m_scene = grp.Scene;
+ if (grp.IsSelected)
+ newMotion.m_selected = true;
+ }
+
+ newMotion.m_timerStopped = false;
+ newMotion.m_running = true;
+ newMotion.m_isCrossing = false;
+ newMotion.m_waitingCrossing = false;
+ }
+ catch
+ {
+ newMotion = null;
+ }
+
+ return newMotion;
+ }
+
+ public void UpdateSceneObject(SceneObjectGroup grp)
+ {
+ m_isCrossing = false;
+ m_waitingCrossing = false;
+ StopTimer();
+
+ if (grp == null)
+ return;
+
+ m_group = grp;
+ m_scene = grp.Scene;
+
+ Vector3 grppos = grp.AbsolutePosition;
+ Vector3 offset = grppos - m_serializedPosition;
+ // avoid doing it more than once
+ // current this will happen draging a prim to other region
+ m_serializedPosition = grppos;
+
+ m_basePosition += offset;
+ m_currentFrame.Position += offset;
+
+ m_nextPosition += offset;
+
+ for (int i = 0; i < m_frames.Count; i++)
+ {
+ Keyframe k = m_frames[i];
+ k.Position += offset;
+ m_frames[i]=k;
+ }
+
+ if (m_running)
+ Start();
+ }
+
+ public KeyframeMotion(SceneObjectGroup grp, PlayMode mode, DataFormat data)
+ {
+ m_mode = mode;
+ m_data = data;
+
+ m_group = grp;
+ if (grp != null)
+ {
+ m_basePosition = grp.AbsolutePosition;
+ m_baseRotation = grp.GroupRotation;
+ m_scene = grp.Scene;
+ }
+
+ m_timerStopped = true;
+ m_isCrossing = false;
+ m_waitingCrossing = false;
+ }
+
+ public void SetKeyframes(Keyframe[] frames)
+ {
+ m_keyframes = frames;
+ }
+
+ public KeyframeMotion Copy(SceneObjectGroup newgrp)
+ {
+ StopTimer();
+
+ KeyframeMotion newmotion = new KeyframeMotion(null, m_mode, m_data);
+
+ newmotion.m_group = newgrp;
+ newmotion.m_scene = newgrp.Scene;
+
+ if (m_keyframes != null)
+ {
+ newmotion.m_keyframes = new Keyframe[m_keyframes.Length];
+ m_keyframes.CopyTo(newmotion.m_keyframes, 0);
+ }
+
+ newmotion.m_frames = new List(m_frames);
+
+ newmotion.m_basePosition = m_basePosition;
+ newmotion.m_baseRotation = m_baseRotation;
+
+ if (m_selected)
+ newmotion.m_serializedPosition = m_serializedPosition;
+ else
+ {
+ if (m_group != null)
+ newmotion.m_serializedPosition = m_group.AbsolutePosition;
+ else
+ newmotion.m_serializedPosition = m_serializedPosition;
+ }
+
+ newmotion.m_currentFrame = m_currentFrame;
+
+ newmotion.m_iterations = m_iterations;
+ newmotion.m_running = m_running;
+
+ if (m_running && !m_waitingCrossing)
+ StartTimer();
+
+ return newmotion;
+ }
+
+ public void Delete()
+ {
+ m_running = false;
+ StopTimer();
+ m_isCrossing = false;
+ m_waitingCrossing = false;
+ m_frames.Clear();
+ m_keyframes = null;
+ }
+
+ public void Start()
+ {
+ m_isCrossing = false;
+ m_waitingCrossing = false;
+ if (m_keyframes != null && m_group != null && m_keyframes.Length > 0)
+ {
+ StartTimer();
+ m_running = true;
+ }
+ else
+ {
+ m_running = false;
+ StopTimer();
+ }
+ }
+
+ public void Stop()
+ {
+ m_running = false;
+ m_isCrossing = false;
+ m_waitingCrossing = false;
+
+ StopTimer();
+
+ m_basePosition = m_group.AbsolutePosition;
+ m_baseRotation = m_group.GroupRotation;
+
+ m_group.RootPart.Velocity = Vector3.Zero;
+ m_group.RootPart.AngularVelocity = Vector3.Zero;
+ m_group.SendGroupRootTerseUpdate();
+// m_group.RootPart.ScheduleTerseUpdate();
+ m_frames.Clear();
+ }
+
+ public void Pause()
+ {
+ m_running = false;
+ StopTimer();
+
+ m_group.RootPart.Velocity = Vector3.Zero;
+ m_group.RootPart.AngularVelocity = Vector3.Zero;
+ m_group.SendGroupRootTerseUpdate();
+// m_group.RootPart.ScheduleTerseUpdate();
+
+ }
+
+ private void GetNextList()
+ {
+ m_frames.Clear();
+ Vector3 pos = m_basePosition;
+ Quaternion rot = m_baseRotation;
+
+ if (m_mode == PlayMode.Loop || m_mode == PlayMode.PingPong || m_iterations == 0)
+ {
+ int direction = 1;
+ if (m_mode == PlayMode.Reverse || ((m_mode == PlayMode.PingPong) && ((m_iterations & 1) != 0)))
+ direction = -1;
+
+ int start = 0;
+ int end = m_keyframes.Length;
+
+ if (direction < 0)
+ {
+ start = m_keyframes.Length - 1;
+ end = -1;
+ }
+
+ for (int i = start; i != end ; i += direction)
+ {
+ Keyframe k = m_keyframes[i];
+
+ if (k.Position.HasValue)
+ {
+ k.Position = (k.Position * direction);
+// k.Velocity = (Vector3)k.Position / (k.TimeMS / 1000.0f);
+ k.Position += pos;
+ }
+ else
+ {
+ k.Position = pos;
+// k.Velocity = Vector3.Zero;
+ }
+
+ k.StartRotation = rot;
+ if (k.Rotation.HasValue)
+ {
+ if (direction == -1)
+ k.Rotation = Quaternion.Conjugate((Quaternion)k.Rotation);
+ k.Rotation = rot * k.Rotation;
+ }
+ else
+ {
+ k.Rotation = rot;
+ }
+
+/* ang vel not in use for now
+
+ float angle = 0;
+
+ float aa = k.StartRotation.X * k.StartRotation.X + k.StartRotation.Y * k.StartRotation.Y + k.StartRotation.Z * k.StartRotation.Z + k.StartRotation.W * k.StartRotation.W;
+ float bb = ((Quaternion)k.Rotation).X * ((Quaternion)k.Rotation).X + ((Quaternion)k.Rotation).Y * ((Quaternion)k.Rotation).Y + ((Quaternion)k.Rotation).Z * ((Quaternion)k.Rotation).Z + ((Quaternion)k.Rotation).W * ((Quaternion)k.Rotation).W;
+ float aa_bb = aa * bb;
+
+ if (aa_bb == 0)
+ {
+ angle = 0;
+ }
+ else
+ {
+ float ab = k.StartRotation.X * ((Quaternion)k.Rotation).X +
+ k.StartRotation.Y * ((Quaternion)k.Rotation).Y +
+ k.StartRotation.Z * ((Quaternion)k.Rotation).Z +
+ k.StartRotation.W * ((Quaternion)k.Rotation).W;
+ float q = (ab * ab) / aa_bb;
+
+ if (q > 1.0f)
+ {
+ angle = 0;
+ }
+ else
+ {
+ angle = (float)Math.Acos(2 * q - 1);
+ }
+ }
+
+ k.AngularVelocity = (new Vector3(0, 0, 1) * (Quaternion)k.Rotation) * (angle / (k.TimeMS / 1000));
+ */
+ k.TimeTotal = k.TimeMS;
+
+ m_frames.Add(k);
+
+ pos = (Vector3)k.Position;
+ rot = (Quaternion)k.Rotation;
+ }
+
+ m_basePosition = pos;
+ m_baseRotation = rot;
+
+ m_iterations++;
+ }
+ }
+
+ public void OnTimer(double tickDuration)
+ {
+ if (m_skipLoops > 0)
+ {
+ m_skipLoops--;
+ return;
+ }
+
+ if (m_timerStopped) // trap events still in air even after a timer.stop
+ return;
+
+ if (m_group == null)
+ return;
+
+ bool update = false;
+
+ if (m_selected)
+ {
+ if (m_group.RootPart.Velocity != Vector3.Zero)
+ {
+ m_group.RootPart.Velocity = Vector3.Zero;
+ m_group.SendGroupRootTerseUpdate();
+
+ }
+ return;
+ }
+
+ if (m_isCrossing)
+ {
+ // if crossing and timer running then cross failed
+ // wait some time then
+ // retry to set the position that evtually caused the outbound
+ // if still outside region this will call startCrossing below
+ m_isCrossing = false;
+ m_group.AbsolutePosition = m_nextPosition;
+ if (!m_isCrossing)
+ {
+ StopTimer();
+ StartTimer();
+ }
+ return;
+ }
+
+ if (m_frames.Count == 0)
+ {
+ GetNextList();
+
+ if (m_frames.Count == 0)
+ {
+ Stop();
+ Scene scene = m_group.Scene;
+
+ IScriptModule[] scriptModules = scene.RequestModuleInterfaces();
+ foreach (IScriptModule m in scriptModules)
+ {
+ if (m == null)
+ continue;
+ m.PostObjectEvent(m_group.RootPart.UUID, "moving_end", new object[0]);
+ }
+
+ return;
+ }
+
+ m_currentFrame = m_frames[0];
+ m_currentFrame.TimeMS += (int)tickDuration;
+
+ //force a update on a keyframe transition
+ update = true;
+ }
+
+ m_currentFrame.TimeMS -= (int)tickDuration;
+
+ // Do the frame processing
+ double steps = (double)m_currentFrame.TimeMS / tickDuration;
+
+ if (steps <= 0.0)
+ {
+ m_group.RootPart.Velocity = Vector3.Zero;
+ m_group.RootPart.AngularVelocity = Vector3.Zero;
+
+ m_nextPosition = (Vector3)m_currentFrame.Position;
+ m_group.AbsolutePosition = m_nextPosition;
+
+ // we are sending imediate updates, no doing force a extra terseUpdate
+ // m_group.UpdateGroupRotationR((Quaternion)m_currentFrame.Rotation);
+
+ m_group.RootPart.RotationOffset = (Quaternion)m_currentFrame.Rotation;
+ m_frames.RemoveAt(0);
+ if (m_frames.Count > 0)
+ m_currentFrame = m_frames[0];
+
+ update = true;
+ }
+ else
+ {
+ float complete = ((float)m_currentFrame.TimeTotal - (float)m_currentFrame.TimeMS) / (float)m_currentFrame.TimeTotal;
+
+ Vector3 v = (Vector3)m_currentFrame.Position - m_group.AbsolutePosition;
+ Vector3 motionThisFrame = v / (float)steps;
+ v = v * 1000 / m_currentFrame.TimeMS;
+
+ if (Vector3.Mag(motionThisFrame) >= 0.05f)
+ {
+ // m_group.AbsolutePosition += motionThisFrame;
+ m_nextPosition = m_group.AbsolutePosition + motionThisFrame;
+ m_group.AbsolutePosition = m_nextPosition;
+
+ //m_group.RootPart.Velocity = v;
+ update = true;
+ }
+
+ if ((Quaternion)m_currentFrame.Rotation != m_group.GroupRotation)
+ {
+ Quaternion current = m_group.GroupRotation;
+
+ Quaternion step = Quaternion.Slerp(m_currentFrame.StartRotation, (Quaternion)m_currentFrame.Rotation, complete);
+ step.Normalize();
+/* use simpler change detection
+* float angle = 0;
+
+ float aa = current.X * current.X + current.Y * current.Y + current.Z * current.Z + current.W * current.W;
+ float bb = step.X * step.X + step.Y * step.Y + step.Z * step.Z + step.W * step.W;
+ float aa_bb = aa * bb;
+
+ if (aa_bb == 0)
+ {
+ angle = 0;
+ }
+ else
+ {
+ float ab = current.X * step.X +
+ current.Y * step.Y +
+ current.Z * step.Z +
+ current.W * step.W;
+ float q = (ab * ab) / aa_bb;
+
+ if (q > 1.0f)
+ {
+ angle = 0;
+ }
+ else
+ {
+ angle = (float)Math.Acos(2 * q - 1);
+ }
+ }
+
+ if (angle > 0.01f)
+*/
+ if(Math.Abs(step.X - current.X) > 0.001f
+ || Math.Abs(step.Y - current.Y) > 0.001f
+ || Math.Abs(step.Z - current.Z) > 0.001f)
+ // assuming w is a dependente var
+
+ {
+// m_group.UpdateGroupRotationR(step);
+ m_group.RootPart.RotationOffset = step;
+
+ //m_group.RootPart.UpdateAngularVelocity(m_currentFrame.AngularVelocity / 2);
+ update = true;
+ }
+ }
+ }
+
+ if (update)
+ {
+ m_group.SendGroupRootTerseUpdate();
+ }
+ }
+
+ public Byte[] Serialize()
+ {
+ StopTimer();
+ MemoryStream ms = new MemoryStream();
+
+ BinaryFormatter fmt = new BinaryFormatter();
+ SceneObjectGroup tmp = m_group;
+ m_group = null;
+ if (!m_selected && tmp != null)
+ m_serializedPosition = tmp.AbsolutePosition;
+ fmt.Serialize(ms, this);
+ m_group = tmp;
+ if (m_running && !m_waitingCrossing)
+ StartTimer();
+
+ return ms.ToArray();
+ }
+
+ public void StartCrossingCheck()
+ {
+ // timer will be restart by crossingFailure
+ // or never since crossing worked and this
+ // should be deleted
+ StopTimer();
+
+ m_isCrossing = true;
+ m_waitingCrossing = true;
+
+ // to remove / retune to smoth crossings
+ if (m_group.RootPart.Velocity != Vector3.Zero)
+ {
+ m_group.RootPart.Velocity = Vector3.Zero;
+ m_group.SendGroupRootTerseUpdate();
+// m_group.RootPart.ScheduleTerseUpdate();
+ }
+ }
+
+ public void CrossingFailure()
+ {
+ m_waitingCrossing = false;
+
+ if (m_group != null)
+ {
+ m_group.RootPart.Velocity = Vector3.Zero;
+ m_group.SendGroupRootTerseUpdate();
+// m_group.RootPart.ScheduleTerseUpdate();
+
+ if (m_running)
+ {
+ StopTimer();
+ m_skipLoops = 1200; // 60 seconds
+ StartTimer();
+ }
+ }
+ }
+ }
+}
--
cgit v1.1
From 81ad9255b5f44d988bf37cfaf6dc59b05fd744b7 Mon Sep 17 00:00:00 2001
From: Melanie
Date: Thu, 6 Jun 2013 03:03:05 +0100
Subject: Hook up Keyframe motion to almost everything. Failing to cross a sim
border may yield unexpected results in some cases. No database persistence
yet,
---
.../EntityTransfer/EntityTransferModule.cs | 3 +
.../InventoryAccess/InventoryAccessModule.cs | 3 +
OpenSim/Region/Framework/Scenes/Scene.cs | 9 ++
OpenSim/Region/Framework/Scenes/SceneGraph.cs | 11 ++
.../Region/Framework/Scenes/SceneObjectGroup.cs | 13 ++
OpenSim/Region/Framework/Scenes/SceneObjectPart.cs | 15 +++
.../Scenes/Serialization/SceneObjectSerializer.cs | 16 +++
.../Shared/Api/Implementation/LSL_Api.cs | 140 +++++++++++++++++++++
.../ScriptEngine/Shared/Api/Interface/ILSL_Api.cs | 1 +
.../Shared/Api/Runtime/LSL_Constants.cs | 13 ++
.../ScriptEngine/Shared/Api/Runtime/LSL_Stub.cs | 5 +
11 files changed, 229 insertions(+)
diff --git a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs
index f58a24f..85d26f3 100644
--- a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs
+++ b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs
@@ -2247,6 +2247,9 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
// move out of the region creating an infinite loop of failed attempts to cross
grp.UpdatePrimFlags(grp.RootPart.LocalId,false,grp.IsTemporary,grp.IsPhantom,false);
+ if (grp.RootPart.KeyframeMotion != null)
+ grp.RootPart.KeyframeMotion.CrossingFailure();
+
grp.ScheduleGroupForFullUpdate();
}
}
diff --git a/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs b/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs
index e6d6cbf..880205a 100644
--- a/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs
+++ b/OpenSim/Region/CoreModules/Framework/InventoryAccess/InventoryAccessModule.cs
@@ -357,6 +357,9 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess
foreach (SceneObjectGroup objectGroup in objlist)
{
+ if (objectGroup.RootPart.KeyframeMotion != null)
+ objectGroup.RootPart.KeyframeMotion.Stop();
+ objectGroup.RootPart.KeyframeMotion = null;
// Vector3 inventoryStoredPosition = new Vector3
// (((objectGroup.AbsolutePosition.X > (int)Constants.RegionSize)
// ? 250
diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs
index 0743ce7..a9f8a85 100644
--- a/OpenSim/Region/Framework/Scenes/Scene.cs
+++ b/OpenSim/Region/Framework/Scenes/Scene.cs
@@ -2364,6 +2364,12 @@ namespace OpenSim.Region.Framework.Scenes
foreach (SceneObjectPart part in partList)
{
+ if (part.KeyframeMotion != null)
+ {
+ part.KeyframeMotion.Delete();
+ part.KeyframeMotion = null;
+ }
+
if (part.IsJoint() && ((part.Flags & PrimFlags.Physics) != 0))
{
PhysicsScene.RequestJointDeletion(part.Name); // FIXME: what if the name changed?
@@ -2705,6 +2711,9 @@ namespace OpenSim.Region.Framework.Scenes
// before we restart the scripts, or else some functions won't work.
newObject.RootPart.ParentGroup.CreateScriptInstances(0, false, DefaultScriptEngine, GetStateSource(newObject));
newObject.ResumeScripts();
+
+ if (newObject.RootPart.KeyframeMotion != null)
+ newObject.RootPart.KeyframeMotion.UpdateSceneObject(newObject);
}
// Do this as late as possible so that listeners have full access to the incoming object
diff --git a/OpenSim/Region/Framework/Scenes/SceneGraph.cs b/OpenSim/Region/Framework/Scenes/SceneGraph.cs
index a84f6d3..bb7ae7f 100644
--- a/OpenSim/Region/Framework/Scenes/SceneGraph.cs
+++ b/OpenSim/Region/Framework/Scenes/SceneGraph.cs
@@ -1645,6 +1645,12 @@ namespace OpenSim.Region.Framework.Scenes
///
protected internal void LinkObjects(SceneObjectPart root, List children)
{
+ if (root.KeyframeMotion != null)
+ {
+ root.KeyframeMotion.Stop();
+ root.KeyframeMotion = null;
+ }
+
SceneObjectGroup parentGroup = root.ParentGroup;
if (parentGroup == null) return;
@@ -1722,6 +1728,11 @@ namespace OpenSim.Region.Framework.Scenes
{
if (part != null)
{
+ if (part.KeyframeMotion != null)
+ {
+ part.KeyframeMotion.Stop();
+ part.KeyframeMotion = null;
+ }
if (part.ParentGroup.PrimCount != 1) // Skip single
{
if (part.LinkNum < 2) // Root
diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs
index df23cc5..da80e4f 100644
--- a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs
+++ b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs
@@ -455,6 +455,9 @@ namespace OpenSim.Region.Framework.Scenes
|| Scene.TestBorderCross(val, Cardinals.S))
&& !IsAttachmentCheckFull() && (!Scene.LoadingPrims))
{
+ if (m_rootPart.KeyframeMotion != null)
+ m_rootPart.KeyframeMotion.StartCrossingCheck();
+
m_scene.CrossPrimGroupIntoNewRegion(val, this, true);
}
}
@@ -578,6 +581,8 @@ namespace OpenSim.Region.Framework.Scenes
childPa.Selected = value;
}
}
+ if (RootPart.KeyframeMotion != null)
+ RootPart.KeyframeMotion.Selected = value;
}
}
@@ -1551,6 +1556,8 @@ namespace OpenSim.Region.Framework.Scenes
newPart.DoPhysicsPropertyUpdate(originalPartPa.IsPhysical, true);
}
+ if (part.KeyframeMotion != null)
+ newPart.KeyframeMotion = part.KeyframeMotion.Copy(dupe);
}
if (userExposed)
@@ -1578,6 +1585,12 @@ namespace OpenSim.Region.Framework.Scenes
public void ScriptSetPhysicsStatus(bool usePhysics)
{
+ if (usePhysics)
+ {
+ if (RootPart.KeyframeMotion != null)
+ RootPart.KeyframeMotion.Stop();
+ RootPart.KeyframeMotion = null;
+ }
UpdatePrimFlags(RootPart.LocalId, usePhysics, IsTemporary, IsPhantom, IsVolumeDetect);
}
diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs
index ea8c3c5..ff3f738 100644
--- a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs
+++ b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs
@@ -354,6 +354,13 @@ namespace OpenSim.Region.Framework.Scenes
private UUID m_collisionSound;
private float m_collisionSoundVolume;
+ private KeyframeMotion m_keyframeMotion = null;
+
+ public KeyframeMotion KeyframeMotion
+ {
+ get; set;
+ }
+
#endregion Fields
// ~SceneObjectPart()
@@ -1799,6 +1806,8 @@ namespace OpenSim.Region.Framework.Scenes
Array.Copy(Shape.ExtraParams, extraP, extraP.Length);
dupe.Shape.ExtraParams = extraP;
+ // safeguard actual copy is done in sog.copy
+ dupe.KeyframeMotion = null;
dupe.PayPrice = (int[])PayPrice.Clone();
dupe.DynAttrs.CopyFrom(DynAttrs);
@@ -2001,6 +2010,9 @@ namespace OpenSim.Region.Framework.Scenes
{
if (UsePhysics)
{
+ if (ParentGroup.RootPart.KeyframeMotion != null)
+ ParentGroup.RootPart.KeyframeMotion.Stop();
+ ParentGroup.RootPart.KeyframeMotion = null;
ParentGroup.Scene.AddPhysicalPrim(1);
pa.OnRequestTerseUpdate += PhysicsRequestingTerseUpdate;
@@ -4327,6 +4339,9 @@ namespace OpenSim.Region.Framework.Scenes
if (isPhysical)
{
+ if (ParentGroup.RootPart.KeyframeMotion != null)
+ ParentGroup.RootPart.KeyframeMotion.Stop();
+ ParentGroup.RootPart.KeyframeMotion = null;
ParentGroup.Scene.AddPhysicalPrim(1);
pa.OnRequestTerseUpdate += PhysicsRequestingTerseUpdate;
diff --git a/OpenSim/Region/Framework/Scenes/Serialization/SceneObjectSerializer.cs b/OpenSim/Region/Framework/Scenes/Serialization/SceneObjectSerializer.cs
index 39420a6..3882b45 100644
--- a/OpenSim/Region/Framework/Scenes/Serialization/SceneObjectSerializer.cs
+++ b/OpenSim/Region/Framework/Scenes/Serialization/SceneObjectSerializer.cs
@@ -262,6 +262,12 @@ namespace OpenSim.Region.Framework.Scenes.Serialization
sr.Close();
}
+ XmlNodeList keymotion = doc.GetElementsByTagName("KeyframeMotion");
+ if (keymotion.Count > 0)
+ sceneObject.RootPart.KeyframeMotion = KeyframeMotion.FromData(sceneObject, Convert.FromBase64String(keymotion[0].InnerText));
+ else
+ sceneObject.RootPart.KeyframeMotion = null;
+
// Script state may, or may not, exist. Not having any, is NOT
// ever a problem.
sceneObject.LoadScriptState(doc);
@@ -1182,6 +1188,16 @@ namespace OpenSim.Region.Framework.Scenes.Serialization
});
writer.WriteEndElement();
+
+ if (sog.RootPart.KeyframeMotion != null)
+ {
+ Byte[] data = sog.RootPart.KeyframeMotion.Serialize();
+
+ writer.WriteStartElement(String.Empty, "KeyframeMotion", String.Empty);
+ writer.WriteBase64(data, 0, data.Length);
+ writer.WriteEndElement();
+ }
+
writer.WriteEndElement();
}
diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs
index 0b4b043..cd6092d 100644
--- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs
+++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs
@@ -7296,6 +7296,146 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
}
}
+ public void llSetKeyframedMotion(LSL_List frames, LSL_List options)
+ {
+ SceneObjectGroup group = m_host.ParentGroup;
+
+ if (group.RootPart.PhysActor != null && group.RootPart.PhysActor.IsPhysical)
+ return;
+ if (group.IsAttachment)
+ return;
+
+ if (frames.Data.Length > 0) // We are getting a new motion
+ {
+ if (group.RootPart.KeyframeMotion != null)
+ group.RootPart.KeyframeMotion.Delete();
+ group.RootPart.KeyframeMotion = null;
+
+ int idx = 0;
+
+ KeyframeMotion.PlayMode mode = KeyframeMotion.PlayMode.Forward;
+ KeyframeMotion.DataFormat data = KeyframeMotion.DataFormat.Translation | KeyframeMotion.DataFormat.Rotation;
+
+ while (idx < options.Data.Length)
+ {
+ int option = (int)options.GetLSLIntegerItem(idx++);
+ int remain = options.Data.Length - idx;
+
+ switch (option)
+ {
+ case ScriptBaseClass.KFM_MODE:
+ if (remain < 1)
+ break;
+ int modeval = (int)options.GetLSLIntegerItem(idx++);
+ switch(modeval)
+ {
+ case ScriptBaseClass.KFM_FORWARD:
+ mode = KeyframeMotion.PlayMode.Forward;
+ break;
+ case ScriptBaseClass.KFM_REVERSE:
+ mode = KeyframeMotion.PlayMode.Reverse;
+ break;
+ case ScriptBaseClass.KFM_LOOP:
+ mode = KeyframeMotion.PlayMode.Loop;
+ break;
+ case ScriptBaseClass.KFM_PING_PONG:
+ mode = KeyframeMotion.PlayMode.PingPong;
+ break;
+ }
+ break;
+ case ScriptBaseClass.KFM_DATA:
+ if (remain < 1)
+ break;
+ int dataval = (int)options.GetLSLIntegerItem(idx++);
+ data = (KeyframeMotion.DataFormat)dataval;
+ break;
+ }
+ }
+
+ group.RootPart.KeyframeMotion = new KeyframeMotion(group, mode, data);
+
+ idx = 0;
+
+ int elemLength = 2;
+ if (data == (KeyframeMotion.DataFormat.Translation | KeyframeMotion.DataFormat.Rotation))
+ elemLength = 3;
+
+ List keyframes = new List();
+ while (idx < frames.Data.Length)
+ {
+ int remain = frames.Data.Length - idx;
+
+ if (remain < elemLength)
+ break;
+
+ KeyframeMotion.Keyframe frame = new KeyframeMotion.Keyframe();
+ frame.Position = null;
+ frame.Rotation = null;
+
+ if ((data & KeyframeMotion.DataFormat.Translation) != 0)
+ {
+ LSL_Types.Vector3 tempv = frames.GetVector3Item(idx++);
+ frame.Position = new Vector3((float)tempv.x, (float)tempv.y, (float)tempv.z);
+ }
+ if ((data & KeyframeMotion.DataFormat.Rotation) != 0)
+ {
+ LSL_Types.Quaternion tempq = frames.GetQuaternionItem(idx++);
+ Quaternion q = new Quaternion((float)tempq.x, (float)tempq.y, (float)tempq.z, (float)tempq.s);
+ q.Normalize();
+ frame.Rotation = q;
+ }
+
+ float tempf = (float)frames.GetLSLFloatItem(idx++);
+ frame.TimeMS = (int)(tempf * 1000.0f);
+
+ keyframes.Add(frame);
+ }
+
+ group.RootPart.KeyframeMotion.SetKeyframes(keyframes.ToArray());
+ group.RootPart.KeyframeMotion.Start();
+ }
+ else
+ {
+ if (group.RootPart.KeyframeMotion == null)
+ return;
+
+ if (options.Data.Length == 0)
+ {
+ group.RootPart.KeyframeMotion.Stop();
+ return;
+ }
+
+ int code = (int)options.GetLSLIntegerItem(0);
+
+ int idx = 0;
+
+ while (idx < options.Data.Length)
+ {
+ int option = (int)options.GetLSLIntegerItem(idx++);
+ int remain = options.Data.Length - idx;
+
+ switch (option)
+ {
+ case ScriptBaseClass.KFM_COMMAND:
+ int cmd = (int)options.GetLSLIntegerItem(idx++);
+ switch (cmd)
+ {
+ case ScriptBaseClass.KFM_CMD_PLAY:
+ group.RootPart.KeyframeMotion.Start();
+ break;
+ case ScriptBaseClass.KFM_CMD_STOP:
+ group.RootPart.KeyframeMotion.Stop();
+ break;
+ case ScriptBaseClass.KFM_CMD_PAUSE:
+ group.RootPart.KeyframeMotion.Pause();
+ break;
+ }
+ break;
+ }
+ }
+ }
+ }
+
protected LSL_List SetPrimParams(SceneObjectPart part, LSL_List rules, string originFunc, ref uint rulesParsed)
{
int idx = 0;
diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Interface/ILSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Interface/ILSL_Api.cs
index 4ac179a..a6ea88c 100644
--- a/OpenSim/Region/ScriptEngine/Shared/Api/Interface/ILSL_Api.cs
+++ b/OpenSim/Region/ScriptEngine/Shared/Api/Interface/ILSL_Api.cs
@@ -427,6 +427,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Interfaces
void print(string str);
void SetPrimitiveParamsEx(LSL_Key prim, LSL_List rules, string originFunc);
+ void llSetKeyframedMotion(LSL_List frames, LSL_List options);
LSL_List GetPrimitiveParamsEx(LSL_Key prim, LSL_List rules);
}
}
diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/LSL_Constants.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/LSL_Constants.cs
index dc5ef13..559068d 100644
--- a/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/LSL_Constants.cs
+++ b/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/LSL_Constants.cs
@@ -748,6 +748,19 @@ namespace OpenSim.Region.ScriptEngine.Shared.ScriptBase
public static readonly LSLInteger RCERR_SIM_PERF_LOW = -2;
public static readonly LSLInteger RCERR_CAST_TIME_EXCEEDED = 3;
+ public const int KFM_MODE = 1;
+ public const int KFM_LOOP = 1;
+ public const int KFM_REVERSE = 3;
+ public const int KFM_FORWARD = 0;
+ public const int KFM_PING_PONG = 2;
+ public const int KFM_DATA = 2;
+ public const int KFM_TRANSLATION = 2;
+ public const int KFM_ROTATION = 1;
+ public const int KFM_COMMAND = 0;
+ public const int KFM_CMD_PLAY = 0;
+ public const int KFM_CMD_STOP = 1;
+ public const int KFM_CMD_PAUSE = 2;
+
///
/// process name parameter as regex
///
diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/LSL_Stub.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/LSL_Stub.cs
index c7a7cf6..398c125 100644
--- a/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/LSL_Stub.cs
+++ b/OpenSim/Region/ScriptEngine/Shared/Api/Runtime/LSL_Stub.cs
@@ -554,6 +554,11 @@ namespace OpenSim.Region.ScriptEngine.Shared.ScriptBase
return m_LSL_Functions.llGetLinkNumberOfSides(link);
}
+ public void llSetKeyframedMotion(LSL_List frames, LSL_List options)
+ {
+ m_LSL_Functions.llSetKeyframedMotion(frames, options);
+ }
+
public LSL_Integer llGetListEntryType(LSL_List src, int index)
{
return m_LSL_Functions.llGetListEntryType(src, index);
--
cgit v1.1