From 134f86e8d5c414409631b25b8c6f0ee45fbd8631 Mon Sep 17 00:00:00 2001 From: David Walter Seikel Date: Thu, 3 Nov 2016 21:44:39 +1000 Subject: Initial update to OpenSim 0.8.2.1 source code. --- .../Api/Implementation/AsyncCommandManager.cs | 329 +- .../Shared/Api/Implementation/LSL_Api.cs | 6111 ++++++++++++++++---- .../Shared/Api/Implementation/LS_Api.cs | 431 +- .../Shared/Api/Implementation/MOD_Api.cs | 79 +- .../Shared/Api/Implementation/OSSL_Api.cs | 488 +- .../Api/Implementation/Plugins/SensorRepeat.cs | 18 +- .../Api/Implementation/Properties/AssemblyInfo.cs | 4 +- 7 files changed, 5910 insertions(+), 1550 deletions(-) (limited to 'OpenSim/Region/ScriptEngine/Shared/Api/Implementation') diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/AsyncCommandManager.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/AsyncCommandManager.cs index 47a9cdc..036cb5d 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/AsyncCommandManager.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/AsyncCommandManager.cs @@ -28,7 +28,9 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Reflection; using System.Threading; +using log4net; using OpenMetaverse; using OpenSim.Framework; using OpenSim.Framework.Monitoring; @@ -45,15 +47,24 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api /// public class AsyncCommandManager { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + private static Thread cmdHandlerThread; private static int cmdHandlerThreadCycleSleepms; - private static List m_Scenes = new List(); + /// + /// Lock for reading/writing static components of AsyncCommandManager. + /// + /// + /// This lock exists so that multiple threads from different engines and/or different copies of the same engine + /// are prevented from running non-thread safe code (e.g. read/write of lists) concurrently. + /// + private static object staticLock = new object(); + private static List m_ScriptEngines = new List(); public IScriptEngine m_ScriptEngine; - private IScene m_Scene; private static Dictionary m_Dataserver = new Dictionary(); @@ -70,67 +81,99 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api public Dataserver DataserverPlugin { - get { return m_Dataserver[m_ScriptEngine]; } + get + { + lock (staticLock) + return m_Dataserver[m_ScriptEngine]; + } } public Timer TimerPlugin { - get { return m_Timer[m_ScriptEngine]; } + get + { + lock (staticLock) + return m_Timer[m_ScriptEngine]; + } } public HttpRequest HttpRequestPlugin { - get { return m_HttpRequest[m_ScriptEngine]; } + get + { + lock (staticLock) + return m_HttpRequest[m_ScriptEngine]; + } } public Listener ListenerPlugin { - get { return m_Listener[m_ScriptEngine]; } + get + { + lock (staticLock) + return m_Listener[m_ScriptEngine]; + } } public SensorRepeat SensorRepeatPlugin { - get { return m_SensorRepeat[m_ScriptEngine]; } + get + { + lock (staticLock) + return m_SensorRepeat[m_ScriptEngine]; + } } public XmlRequest XmlRequestPlugin { - get { return m_XmlRequest[m_ScriptEngine]; } + get + { + lock (staticLock) + return m_XmlRequest[m_ScriptEngine]; + } } public IScriptEngine[] ScriptEngines { - get { return m_ScriptEngines.ToArray(); } + get + { + lock (staticLock) + return m_ScriptEngines.ToArray(); + } } public AsyncCommandManager(IScriptEngine _ScriptEngine) { m_ScriptEngine = _ScriptEngine; - m_Scene = m_ScriptEngine.World; - - if (m_Scenes.Count == 0) - ReadConfig(); - - if (!m_Scenes.Contains(m_Scene)) - m_Scenes.Add(m_Scene); - if (!m_ScriptEngines.Contains(m_ScriptEngine)) - m_ScriptEngines.Add(m_ScriptEngine); - - // Create instances of all plugins - if (!m_Dataserver.ContainsKey(m_ScriptEngine)) - m_Dataserver[m_ScriptEngine] = new Dataserver(this); - if (!m_Timer.ContainsKey(m_ScriptEngine)) - m_Timer[m_ScriptEngine] = new Timer(this); - if (!m_HttpRequest.ContainsKey(m_ScriptEngine)) - m_HttpRequest[m_ScriptEngine] = new HttpRequest(this); - if (!m_Listener.ContainsKey(m_ScriptEngine)) - m_Listener[m_ScriptEngine] = new Listener(this); - if (!m_SensorRepeat.ContainsKey(m_ScriptEngine)) - m_SensorRepeat[m_ScriptEngine] = new SensorRepeat(this); - if (!m_XmlRequest.ContainsKey(m_ScriptEngine)) - m_XmlRequest[m_ScriptEngine] = new XmlRequest(this); - - StartThread(); + + // If there is more than one scene in the simulator or multiple script engines are used on the same region + // then more than one thread could arrive at this block of code simultaneously. However, it cannot be + // executed concurrently both because concurrent list operations are not thread-safe and because of other + // race conditions such as the later check of cmdHandlerThread == null. + lock (staticLock) + { + if (m_ScriptEngines.Count == 0) + ReadConfig(); + + if (!m_ScriptEngines.Contains(m_ScriptEngine)) + m_ScriptEngines.Add(m_ScriptEngine); + + // Create instances of all plugins + if (!m_Dataserver.ContainsKey(m_ScriptEngine)) + m_Dataserver[m_ScriptEngine] = new Dataserver(this); + if (!m_Timer.ContainsKey(m_ScriptEngine)) + m_Timer[m_ScriptEngine] = new Timer(this); + if (!m_HttpRequest.ContainsKey(m_ScriptEngine)) + m_HttpRequest[m_ScriptEngine] = new HttpRequest(this); + if (!m_Listener.ContainsKey(m_ScriptEngine)) + m_Listener[m_ScriptEngine] = new Listener(this); + if (!m_SensorRepeat.ContainsKey(m_ScriptEngine)) + m_SensorRepeat[m_ScriptEngine] = new SensorRepeat(this); + if (!m_XmlRequest.ContainsKey(m_ScriptEngine)) + m_XmlRequest[m_ScriptEngine] = new XmlRequest(this); + + StartThread(); + } } private static void StartThread() @@ -139,7 +182,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api { // Start the thread that will be doing the work cmdHandlerThread - = Watchdog.StartThread( + = WorkManager.StartThread( CmdHandlerThreadLoop, "AsyncLSLCmdHandlerThread", ThreadPriority.Normal, true, true); } } @@ -179,42 +222,43 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api { try { - while (true) - { - Thread.Sleep(cmdHandlerThreadCycleSleepms); + Thread.Sleep(cmdHandlerThreadCycleSleepms); - DoOneCmdHandlerPass(); + DoOneCmdHandlerPass(); - Watchdog.UpdateThread(); - } + Watchdog.UpdateThread(); } - catch + catch (Exception e) { + m_log.Error("[ASYNC COMMAND MANAGER]: Exception in command handler pass: ", e); } } } private static void DoOneCmdHandlerPass() { - // Check HttpRequests - m_HttpRequest[m_ScriptEngines[0]].CheckHttpRequests(); + lock (staticLock) + { + // Check HttpRequests + m_HttpRequest[m_ScriptEngines[0]].CheckHttpRequests(); - // Check XMLRPCRequests - m_XmlRequest[m_ScriptEngines[0]].CheckXMLRPCRequests(); + // Check XMLRPCRequests + m_XmlRequest[m_ScriptEngines[0]].CheckXMLRPCRequests(); - foreach (IScriptEngine s in m_ScriptEngines) - { - // Check Listeners - m_Listener[s].CheckListeners(); + foreach (IScriptEngine s in m_ScriptEngines) + { + // Check Listeners + m_Listener[s].CheckListeners(); - // Check timers - m_Timer[s].CheckTimerEvents(); + // Check timers + m_Timer[s].CheckTimerEvents(); - // Check Sensors - m_SensorRepeat[s].CheckSenseRepeaterEvents(); + // Check Sensors + m_SensorRepeat[s].CheckSenseRepeaterEvents(); - // Check dataserver - m_Dataserver[s].ExpireRequests(); + // Check dataserver + m_Dataserver[s].ExpireRequests(); + } } } @@ -225,19 +269,44 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api /// public static void RemoveScript(IScriptEngine engine, uint localID, UUID itemID) { +// m_log.DebugFormat("[ASYNC COMMAND MANAGER]: Removing facilities for script {0}", itemID); + + lock (staticLock) + { + // Remove dataserver events + m_Dataserver[engine].RemoveEvents(localID, itemID); + + // Remove from: Timers + m_Timer[engine].UnSetTimerEvents(localID, itemID); + + // Remove from: HttpRequest + IHttpRequestModule iHttpReq = engine.World.RequestModuleInterface(); + if (iHttpReq != null) + iHttpReq.StopHttpRequestsForScript(itemID); + + IWorldComm comms = engine.World.RequestModuleInterface(); + if (comms != null) + comms.DeleteListener(itemID); + + IXMLRPC xmlrpc = engine.World.RequestModuleInterface(); + if (xmlrpc != null) + { + xmlrpc.DeleteChannels(itemID); + xmlrpc.CancelSRDRequests(itemID); + } + + // Remove Sensors + m_SensorRepeat[engine].UnSetSenseRepeaterEvents(localID, itemID); + } + } + + public static void StateChange(IScriptEngine engine, uint localID, UUID itemID) + { // Remove a specific script // Remove dataserver events m_Dataserver[engine].RemoveEvents(localID, itemID); - // Remove from: Timers - m_Timer[engine].UnSetTimerEvents(localID, itemID); - - // Remove from: HttpRequest - IHttpRequestModule iHttpReq = engine.World.RequestModuleInterface(); - if (iHttpReq != null) - iHttpReq.StopHttpRequest(localID, itemID); - IWorldComm comms = engine.World.RequestModuleInterface(); if (comms != null) comms.DeleteListener(itemID); @@ -248,9 +317,9 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api xmlrpc.DeleteChannels(itemID); xmlrpc.CancelSRDRequests(itemID); } - // Remove Sensors m_SensorRepeat[engine].UnSetSenseRepeaterEvents(localID, itemID); + } /// @@ -260,10 +329,13 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api /// public static SensorRepeat GetSensorRepeatPlugin(IScriptEngine engine) { - if (m_SensorRepeat.ContainsKey(engine)) - return m_SensorRepeat[engine]; - else - return null; + lock (staticLock) + { + if (m_SensorRepeat.ContainsKey(engine)) + return m_SensorRepeat[engine]; + else + return null; + } } /// @@ -273,10 +345,13 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api /// public static Dataserver GetDataserverPlugin(IScriptEngine engine) { - if (m_Dataserver.ContainsKey(engine)) - return m_Dataserver[engine]; - else - return null; + lock (staticLock) + { + if (m_Dataserver.ContainsKey(engine)) + return m_Dataserver[engine]; + else + return null; + } } /// @@ -286,10 +361,13 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api /// public static Timer GetTimerPlugin(IScriptEngine engine) { - if (m_Timer.ContainsKey(engine)) - return m_Timer[engine]; - else - return null; + lock (staticLock) + { + if (m_Timer.ContainsKey(engine)) + return m_Timer[engine]; + else + return null; + } } /// @@ -299,38 +377,44 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api /// public static Listener GetListenerPlugin(IScriptEngine engine) { - if (m_Listener.ContainsKey(engine)) - return m_Listener[engine]; - else - return null; + lock (staticLock) + { + if (m_Listener.ContainsKey(engine)) + return m_Listener[engine]; + else + return null; + } } public static Object[] GetSerializationData(IScriptEngine engine, UUID itemID) { List data = new List(); - Object[] listeners = m_Listener[engine].GetSerializationData(itemID); - if (listeners.Length > 0) + lock (staticLock) { - data.Add("listener"); - data.Add(listeners.Length); - data.AddRange(listeners); - } + Object[] listeners = m_Listener[engine].GetSerializationData(itemID); + if (listeners.Length > 0) + { + data.Add("listener"); + data.Add(listeners.Length); + data.AddRange(listeners); + } - Object[] timers=m_Timer[engine].GetSerializationData(itemID); - if (timers.Length > 0) - { - data.Add("timer"); - data.Add(timers.Length); - data.AddRange(timers); - } + Object[] timers=m_Timer[engine].GetSerializationData(itemID); + if (timers.Length > 0) + { + data.Add("timer"); + data.Add(timers.Length); + data.AddRange(timers); + } - Object[] sensors = m_SensorRepeat[engine].GetSerializationData(itemID); - if (sensors.Length > 0) - { - data.Add("sensor"); - data.Add(sensors.Length); - data.AddRange(sensors); + Object[] sensors = m_SensorRepeat[engine].GetSerializationData(itemID); + if (sensors.Length > 0) + { + data.Add("sensor"); + data.Add(sensors.Length); + data.AddRange(sensors); + } } return data.ToArray(); @@ -355,41 +439,26 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api idx+=len; + lock (staticLock) + { switch (type) { - case "listener": - m_Listener[engine].CreateFromData(localID, itemID, - hostID, item); - break; - case "timer": - m_Timer[engine].CreateFromData(localID, itemID, - hostID, item); - break; - case "sensor": - m_SensorRepeat[engine].CreateFromData(localID, - itemID, hostID, item); - break; + case "listener": + m_Listener[engine].CreateFromData(localID, itemID, + hostID, item); + break; + case "timer": + m_Timer[engine].CreateFromData(localID, itemID, + hostID, item); + break; + case "sensor": + m_SensorRepeat[engine].CreateFromData(localID, + itemID, hostID, item); + break; + } } } } } - - #region Check llRemoteData channels - - #endregion - - #region Check llListeners - - #endregion - - /// - /// If set to true then threads and stuff should try to make a graceful exit - /// - public bool PleaseShutdown - { - get { return _PleaseShutdown; } - set { _PleaseShutdown = value; } - } - private bool _PleaseShutdown = false; } } diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs index 1ab107a..4eda443 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs @@ -28,6 +28,9 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Diagnostics; +using System.Drawing; +using System.Drawing.Imaging; using System.Runtime.Remoting.Lifetime; using System.Text; using System.Threading; @@ -35,7 +38,9 @@ using System.Text.RegularExpressions; using Nini.Config; using log4net; using OpenMetaverse; +using OpenMetaverse.Assets; using OpenMetaverse.Packets; +using OpenMetaverse.Rendering; using OpenSim; using OpenSim.Framework; @@ -45,7 +50,8 @@ using OpenSim.Region.CoreModules.World.Terrain; using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Scenes; using OpenSim.Region.Framework.Scenes.Animation; -using OpenSim.Region.Physics.Manager; +using OpenSim.Region.Framework.Scenes.Scripting; +using OpenSim.Region.PhysicsModules.SharedBase; using OpenSim.Region.ScriptEngine.Shared; using OpenSim.Region.ScriptEngine.Shared.Api.Plugins; using OpenSim.Region.ScriptEngine.Shared.ScriptBase; @@ -66,6 +72,8 @@ using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion; using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString; using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3; using System.Reflection; +using System.Linq; +using PermissionMask = OpenSim.Framework.PermissionMask; namespace OpenSim.Region.ScriptEngine.Shared.Api { @@ -83,15 +91,22 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api public class LSL_Api : MarshalByRefObject, ILSL_Api, IScriptApi { private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + public int LlRequestAgentDataCacheTimeoutMs { get; set; } + protected IScriptEngine m_ScriptEngine; protected SceneObjectPart m_host; /// + /// Used for script sleeps when we are using co-operative script termination. + /// + /// null if co-operative script termination is not active + /// /// The item that hosts this script /// protected TaskInventoryItem m_item; - protected bool throwErrorOnNotImplemented = true; + protected bool throwErrorOnNotImplemented = false; protected AsyncCommandManager AsyncCommands = null; protected float m_ScriptDelayFactor = 1.0f; protected float m_ScriptDistanceFactor = 1.0f; @@ -108,49 +123,260 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api protected IUrlModule m_UrlModule = null; protected Dictionary m_userInfoCache = new Dictionary(); protected int EMAIL_PAUSE_TIME = 20; // documented delay value for smtp. + protected int m_sleepMsOnSetTexture = 200; + protected int m_sleepMsOnSetLinkTexture = 200; + protected int m_sleepMsOnScaleTexture = 200; + protected int m_sleepMsOnOffsetTexture = 200; + protected int m_sleepMsOnRotateTexture = 200; + protected int m_sleepMsOnSetPos = 200; + protected int m_sleepMsOnSetRot = 200; + protected int m_sleepMsOnSetLocalRot = 200; + protected int m_sleepMsOnPreloadSound = 1000; + protected int m_sleepMsOnMakeExplosion = 100; + protected int m_sleepMsOnMakeFountain = 100; + protected int m_sleepMsOnMakeSmoke = 100; + protected int m_sleepMsOnMakeFire = 100; + protected int m_sleepMsOnRezAtRoot = 100; + protected int m_sleepMsOnInstantMessage = 2000; + protected int m_sleepMsOnEmail = 20000; + protected int m_sleepMsOnCreateLink = 1000; + protected int m_sleepMsOnGiveInventory = 3000; + protected int m_sleepMsOnRequestAgentData = 100; + protected int m_sleepMsOnRequestInventoryData = 1000; + protected int m_sleepMsOnSetDamage = 5000; + protected int m_sleepMsOnTextBox = 1000; + protected int m_sleepMsOnAdjustSoundVolume = 100; + protected int m_sleepMsOnEjectFromLand = 5000; + protected int m_sleepMsOnAddToLandPassList = 100; + protected int m_sleepMsOnDialog = 1000; + protected int m_sleepMsOnRemoteLoadScript = 3000; + protected int m_sleepMsOnRemoteLoadScriptPin = 3000; + protected int m_sleepMsOnOpenRemoteDataChannel = 1000; + protected int m_sleepMsOnSendRemoteData = 3000; + protected int m_sleepMsOnRemoteDataReply = 3000; + protected int m_sleepMsOnCloseRemoteDataChannel = 1000; + protected int m_sleepMsOnSetPrimitiveParams = 200; + protected int m_sleepMsOnSetLinkPrimitiveParams = 200; + protected int m_sleepMsOnXorBase64Strings = 300; + protected int m_sleepMsOnSetParcelMusicURL = 2000; + protected int m_sleepMsOnGetPrimMediaParams = 1000; + protected int m_sleepMsOnGetLinkMedia = 1000; + protected int m_sleepMsOnSetPrimMediaParams = 1000; + protected int m_sleepMsOnSetLinkMedia = 1000; + protected int m_sleepMsOnClearPrimMedia = 1000; + protected int m_sleepMsOnClearLinkMedia = 1000; + protected int m_sleepMsOnRequestSimulatorData = 1000; + protected int m_sleepMsOnLoadURL = 10000; + protected int m_sleepMsOnParcelMediaCommandList = 2000; + protected int m_sleepMsOnParcelMediaQuery = 2000; + protected int m_sleepMsOnModPow = 1000; + protected int m_sleepMsOnSetPrimURL = 2000; + protected int m_sleepMsOnRefreshPrimURL = 20000; + protected int m_sleepMsOnMapDestination = 1000; + protected int m_sleepMsOnAddToLandBanList = 100; + protected int m_sleepMsOnRemoveFromLandPassList = 100; + protected int m_sleepMsOnRemoveFromLandBanList = 100; + protected int m_sleepMsOnResetLandBanList = 100; + protected int m_sleepMsOnResetLandPassList = 100; + protected int m_sleepMsOnGetParcelPrimOwners = 2000; + protected int m_sleepMsOnGetNumberOfNotecardLines = 100; + protected int m_sleepMsOnGetNotecardLine = 100; + protected string m_internalObjectHost = "lsl.opensim.local"; + protected bool m_restrictEmail = false; protected ISoundModule m_SoundModule = null; - public void Initialize(IScriptEngine ScriptEngine, SceneObjectPart host, TaskInventoryItem item) + protected float m_avatarHeightCorrection = 0.2f; + protected bool m_useSimpleBoxesInGetBoundingBox = false; + protected bool m_addStatsInGetBoundingBox = false; + + //LSL Avatar Bounding Box (lABB), lower (1) and upper (2), + //standing (Std), Groundsitting (Grs), Sitting (Sit), + //along X, Y and Z axes, constants (0) and coefficients (1) + protected float m_lABB1StdX0 = -0.275f; + protected float m_lABB2StdX0 = 0.275f; + protected float m_lABB1StdY0 = -0.35f; + protected float m_lABB2StdY0 = 0.35f; + protected float m_lABB1StdZ0 = -0.1f; + protected float m_lABB1StdZ1 = -0.5f; + protected float m_lABB2StdZ0 = 0.1f; + protected float m_lABB2StdZ1 = 0.5f; + protected float m_lABB1GrsX0 = -0.3875f; + protected float m_lABB2GrsX0 = 0.3875f; + protected float m_lABB1GrsY0 = -0.5f; + protected float m_lABB2GrsY0 = 0.5f; + protected float m_lABB1GrsZ0 = -0.05f; + protected float m_lABB1GrsZ1 = -0.375f; + protected float m_lABB2GrsZ0 = 0.5f; + protected float m_lABB2GrsZ1 = 0.0f; + protected float m_lABB1SitX0 = -0.5875f; + protected float m_lABB2SitX0 = 0.1875f; + protected float m_lABB1SitY0 = -0.35f; + protected float m_lABB2SitY0 = 0.35f; + protected float m_lABB1SitZ0 = -0.35f; + protected float m_lABB1SitZ1 = -0.375f; + protected float m_lABB2SitZ0 = -0.25f; + protected float m_lABB2SitZ1 = 0.25f; + + protected float m_primSafetyCoeffX = 2.414214f; + protected float m_primSafetyCoeffY = 2.414214f; + protected float m_primSafetyCoeffZ = 1.618034f; + protected bool m_useCastRayV3 = false; + protected float m_floatToleranceInCastRay = 0.00001f; + protected float m_floatTolerance2InCastRay = 0.001f; + protected DetailLevel m_primLodInCastRay = DetailLevel.Medium; + protected DetailLevel m_sculptLodInCastRay = DetailLevel.Medium; + protected DetailLevel m_meshLodInCastRay = DetailLevel.Highest; + protected DetailLevel m_avatarLodInCastRay = DetailLevel.Medium; + protected int m_maxHitsInCastRay = 16; + protected int m_maxHitsPerPrimInCastRay = 16; + protected int m_maxHitsPerObjectInCastRay = 16; + protected bool m_detectExitsInCastRay = false; + protected bool m_filterPartsInCastRay = false; + protected bool m_doAttachmentsInCastRay = false; + protected int m_msThrottleInCastRay = 200; + protected int m_msPerRegionInCastRay = 40; + protected int m_msPerAvatarInCastRay = 10; + protected int m_msMinInCastRay = 2; + protected int m_msMaxInCastRay = 40; + protected static List m_castRayCalls = new List(); + protected bool m_useMeshCacheInCastRay = true; + protected static Dictionary m_cachedMeshes = new Dictionary(); + + //An array of HTTP/1.1 headers that are not allowed to be used + //as custom headers by llHTTPRequest. + private string[] HttpStandardHeaders = + { + "Accept", "Accept-Charset", "Accept-Encoding", "Accept-Language", + "Accept-Ranges", "Age", "Allow", "Authorization", "Cache-Control", + "Connection", "Content-Encoding", "Content-Language", + "Content-Length", "Content-Location", "Content-MD5", + "Content-Range", "Content-Type", "Date", "ETag", "Expect", + "Expires", "From", "Host", "If-Match", "If-Modified-Since", + "If-None-Match", "If-Range", "If-Unmodified-Since", "Last-Modified", + "Location", "Max-Forwards", "Pragma", "Proxy-Authenticate", + "Proxy-Authorization", "Range", "Referer", "Retry-After", "Server", + "TE", "Trailer", "Transfer-Encoding", "Upgrade", "User-Agent", + "Vary", "Via", "Warning", "WWW-Authenticate" + }; + + public void Initialize( + IScriptEngine scriptEngine, SceneObjectPart host, TaskInventoryItem item) { - m_ScriptEngine = ScriptEngine; + m_ScriptEngine = scriptEngine; m_host = host; m_item = item; - LoadLimits(); // read script limits from config. + LoadConfig(); m_TransferModule = m_ScriptEngine.World.RequestModuleInterface(); m_UrlModule = m_ScriptEngine.World.RequestModuleInterface(); m_SoundModule = m_ScriptEngine.World.RequestModuleInterface(); - AsyncCommands = new AsyncCommandManager(ScriptEngine); + AsyncCommands = new AsyncCommandManager(m_ScriptEngine); } - /* load configuration items that affect script, object and run-time behavior. */ - private void LoadLimits() + /// + /// Load configuration items that affect script, object and run-time behavior. */ + /// + private void LoadConfig() { - m_ScriptDelayFactor = - m_ScriptEngine.Config.GetFloat("ScriptDelayFactor", 1.0f); - m_ScriptDistanceFactor = - m_ScriptEngine.Config.GetFloat("ScriptDistanceLimitFactor", 1.0f); - m_MinTimerInterval = - m_ScriptEngine.Config.GetFloat("MinTimerInterval", 0.5f); - m_automaticLinkPermission = - m_ScriptEngine.Config.GetBoolean("AutomaticLinkPermission", false); - m_notecardLineReadCharsMax = - m_ScriptEngine.Config.GetInt("NotecardLineReadCharsMax", 255); + LlRequestAgentDataCacheTimeoutMs = 20000; + + IConfig seConfig = m_ScriptEngine.Config; + + if (seConfig != null) + { + m_ScriptDelayFactor = + seConfig.GetFloat("ScriptDelayFactor", m_ScriptDelayFactor); + m_ScriptDistanceFactor = + seConfig.GetFloat("ScriptDistanceLimitFactor", m_ScriptDistanceFactor); + m_MinTimerInterval = + seConfig.GetFloat("MinTimerInterval", m_MinTimerInterval); + m_automaticLinkPermission = + seConfig.GetBoolean("AutomaticLinkPermission", m_automaticLinkPermission); + m_notecardLineReadCharsMax = + seConfig.GetInt("NotecardLineReadCharsMax", m_notecardLineReadCharsMax); + + // Rezzing an object with a velocity can create recoil. This feature seems to have been + // removed from recent versions of SL. The code computes recoil (vel*mass) and scales + // it by this factor. May be zero to turn off recoil all together. + m_recoilScaleFactor = m_ScriptEngine.Config.GetFloat("RecoilScaleFactor", m_recoilScaleFactor); + } + if (m_notecardLineReadCharsMax > 65535) m_notecardLineReadCharsMax = 65535; + // load limits for particular subsystems. - IConfig SMTPConfig; - if ((SMTPConfig = m_ScriptEngine.ConfigSource.Configs["SMTP"]) != null) { - // there's an smtp config, so load in the snooze time. - EMAIL_PAUSE_TIME = SMTPConfig.GetInt("email_pause_time", EMAIL_PAUSE_TIME); - } - // Rezzing an object with a velocity can create recoil. This feature seems to have been - // removed from recent versions of SL. The code computes recoil (vel*mass) and scales - // it by this factor. May be zero to turn off recoil all together. - m_recoilScaleFactor = m_ScriptEngine.Config.GetFloat("RecoilScaleFactor", m_recoilScaleFactor); + IConfigSource seConfigSource = m_ScriptEngine.ConfigSource; + + if (seConfigSource != null) + { + IConfig lslConfig = seConfigSource.Configs["LL-Functions"]; + if (lslConfig != null) + { + m_restrictEmail = lslConfig.GetBoolean("RestrictEmail", m_restrictEmail); + m_avatarHeightCorrection = lslConfig.GetFloat("AvatarHeightCorrection", m_avatarHeightCorrection); + m_useSimpleBoxesInGetBoundingBox = lslConfig.GetBoolean("UseSimpleBoxesInGetBoundingBox", m_useSimpleBoxesInGetBoundingBox); + m_addStatsInGetBoundingBox = lslConfig.GetBoolean("AddStatsInGetBoundingBox", m_addStatsInGetBoundingBox); + m_lABB1StdX0 = lslConfig.GetFloat("LowerAvatarBoundingBoxStandingXconst", m_lABB1StdX0); + m_lABB2StdX0 = lslConfig.GetFloat("UpperAvatarBoundingBoxStandingXconst", m_lABB2StdX0); + m_lABB1StdY0 = lslConfig.GetFloat("LowerAvatarBoundingBoxStandingYconst", m_lABB1StdY0); + m_lABB2StdY0 = lslConfig.GetFloat("UpperAvatarBoundingBoxStandingYconst", m_lABB2StdY0); + m_lABB1StdZ0 = lslConfig.GetFloat("LowerAvatarBoundingBoxStandingZconst", m_lABB1StdZ0); + m_lABB1StdZ1 = lslConfig.GetFloat("LowerAvatarBoundingBoxStandingZcoeff", m_lABB1StdZ1); + m_lABB2StdZ0 = lslConfig.GetFloat("UpperAvatarBoundingBoxStandingZconst", m_lABB2StdZ0); + m_lABB2StdZ1 = lslConfig.GetFloat("UpperAvatarBoundingBoxStandingZcoeff", m_lABB2StdZ1); + m_lABB1GrsX0 = lslConfig.GetFloat("LowerAvatarBoundingBoxGroundsittingXconst", m_lABB1GrsX0); + m_lABB2GrsX0 = lslConfig.GetFloat("UpperAvatarBoundingBoxGroundsittingXconst", m_lABB2GrsX0); + m_lABB1GrsY0 = lslConfig.GetFloat("LowerAvatarBoundingBoxGroundsittingYconst", m_lABB1GrsY0); + m_lABB2GrsY0 = lslConfig.GetFloat("UpperAvatarBoundingBoxGroundsittingYconst", m_lABB2GrsY0); + m_lABB1GrsZ0 = lslConfig.GetFloat("LowerAvatarBoundingBoxGroundsittingZconst", m_lABB1GrsZ0); + m_lABB1GrsZ1 = lslConfig.GetFloat("LowerAvatarBoundingBoxGroundsittingZcoeff", m_lABB1GrsZ1); + m_lABB2GrsZ0 = lslConfig.GetFloat("UpperAvatarBoundingBoxGroundsittingZconst", m_lABB2GrsZ0); + m_lABB2GrsZ1 = lslConfig.GetFloat("UpperAvatarBoundingBoxGroundsittingZcoeff", m_lABB2GrsZ1); + m_lABB1SitX0 = lslConfig.GetFloat("LowerAvatarBoundingBoxSittingXconst", m_lABB1SitX0); + m_lABB2SitX0 = lslConfig.GetFloat("UpperAvatarBoundingBoxSittingXconst", m_lABB2SitX0); + m_lABB1SitY0 = lslConfig.GetFloat("LowerAvatarBoundingBoxSittingYconst", m_lABB1SitY0); + m_lABB2SitY0 = lslConfig.GetFloat("UpperAvatarBoundingBoxSittingYconst", m_lABB2SitY0); + m_lABB1SitZ0 = lslConfig.GetFloat("LowerAvatarBoundingBoxSittingZconst", m_lABB1SitZ0); + m_lABB1SitZ1 = lslConfig.GetFloat("LowerAvatarBoundingBoxSittingZcoeff", m_lABB1SitZ1); + m_lABB2SitZ0 = lslConfig.GetFloat("UpperAvatarBoundingBoxSittingZconst", m_lABB2SitZ0); + m_lABB2SitZ1 = lslConfig.GetFloat("UpperAvatarBoundingBoxSittingZcoeff", m_lABB2SitZ1); + m_primSafetyCoeffX = lslConfig.GetFloat("PrimBoundingBoxSafetyCoefficientX", m_primSafetyCoeffX); + m_primSafetyCoeffY = lslConfig.GetFloat("PrimBoundingBoxSafetyCoefficientY", m_primSafetyCoeffY); + m_primSafetyCoeffZ = lslConfig.GetFloat("PrimBoundingBoxSafetyCoefficientZ", m_primSafetyCoeffZ); + m_useCastRayV3 = lslConfig.GetBoolean("UseLlCastRayV3", m_useCastRayV3); + m_floatToleranceInCastRay = lslConfig.GetFloat("FloatToleranceInLlCastRay", m_floatToleranceInCastRay); + m_floatTolerance2InCastRay = lslConfig.GetFloat("FloatTolerance2InLlCastRay", m_floatTolerance2InCastRay); + m_primLodInCastRay = (DetailLevel)lslConfig.GetInt("PrimDetailLevelInLlCastRay", (int)m_primLodInCastRay); + m_sculptLodInCastRay = (DetailLevel)lslConfig.GetInt("SculptDetailLevelInLlCastRay", (int)m_sculptLodInCastRay); + m_meshLodInCastRay = (DetailLevel)lslConfig.GetInt("MeshDetailLevelInLlCastRay", (int)m_meshLodInCastRay); + m_avatarLodInCastRay = (DetailLevel)lslConfig.GetInt("AvatarDetailLevelInLlCastRay", (int)m_avatarLodInCastRay); + m_maxHitsInCastRay = lslConfig.GetInt("MaxHitsInLlCastRay", m_maxHitsInCastRay); + m_maxHitsPerPrimInCastRay = lslConfig.GetInt("MaxHitsPerPrimInLlCastRay", m_maxHitsPerPrimInCastRay); + m_maxHitsPerObjectInCastRay = lslConfig.GetInt("MaxHitsPerObjectInLlCastRay", m_maxHitsPerObjectInCastRay); + m_detectExitsInCastRay = lslConfig.GetBoolean("DetectExitHitsInLlCastRay", m_detectExitsInCastRay); + m_filterPartsInCastRay = lslConfig.GetBoolean("FilterPartsInLlCastRay", m_filterPartsInCastRay); + m_doAttachmentsInCastRay = lslConfig.GetBoolean("DoAttachmentsInLlCastRay", m_doAttachmentsInCastRay); + m_msThrottleInCastRay = lslConfig.GetInt("ThrottleTimeInMsInLlCastRay", m_msThrottleInCastRay); + m_msPerRegionInCastRay = lslConfig.GetInt("AvailableTimeInMsPerRegionInLlCastRay", m_msPerRegionInCastRay); + m_msPerAvatarInCastRay = lslConfig.GetInt("AvailableTimeInMsPerAvatarInLlCastRay", m_msPerAvatarInCastRay); + m_msMinInCastRay = lslConfig.GetInt("RequiredAvailableTimeInMsInLlCastRay", m_msMinInCastRay); + m_msMaxInCastRay = lslConfig.GetInt("MaximumAvailableTimeInMsInLlCastRay", m_msMaxInCastRay); + m_useMeshCacheInCastRay = lslConfig.GetBoolean("UseMeshCacheInLlCastRay", m_useMeshCacheInCastRay); + } + + IConfig smtpConfig = seConfigSource.Configs["SMTP"]; + if (smtpConfig != null) + { + // there's an smtp config, so load in the snooze time. + EMAIL_PAUSE_TIME = smtpConfig.GetInt("email_pause_time", EMAIL_PAUSE_TIME); + + m_internalObjectHost = smtpConfig.GetString("internal_object_host", m_internalObjectHost); + } + } + m_sleepMsOnEmail = EMAIL_PAUSE_TIME * 1000; } public override Object InitializeLifetimeService() @@ -171,9 +397,26 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api delay = (int)((float)delay * m_ScriptDelayFactor); if (delay == 0) return; - System.Threading.Thread.Sleep(delay); + + Sleep(delay); + } + + protected virtual void Sleep(int delay) + { + if (m_item == null) // Some unit tests don't set this + { + Thread.Sleep(delay); + return; + } + + m_ScriptEngine.SleepScript(m_item.ItemID, delay); } + /// + /// Check for co-operative termination. + /// + /// If called with 0, then just the check is performed with no wait. + public Scene World { get { return m_ScriptEngine.World; } @@ -209,7 +452,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api if ((item = GetScriptByName(name)) != UUID.Zero) m_ScriptEngine.ResetScript(item); else - ShoutError("llResetOtherScript: script "+name+" not found"); + Error("llResetOtherScript", "Can't find script '" + name + "'"); } public LSL_Integer llGetScriptState(string name) @@ -223,7 +466,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api return m_ScriptEngine.GetScriptState(item) ?1:0; } - ShoutError("llGetScriptState: script "+name+" not found"); + Error("llGetScriptState", "Can't find script '" + name + "'"); // If we didn't find it, then it's safe to // assume it is not running. @@ -246,7 +489,78 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api } else { - ShoutError("llSetScriptState: script "+name+" not found"); + Error("llSetScriptState", "Can't find script '" + name + "'"); + } + } + + /// + /// Get a given link entity from a linkset (linked objects and any sitting avatars). + /// + /// + /// If there are any ScenePresence's in the linkset (i.e. because they are sat upon one of the prims), then + /// these are counted as extra entities that correspond to linknums beyond the number of prims in the linkset. + /// The ScenePresences receive linknums in the order in which they sat. + /// + /// + /// The link entity. null if not found. + /// + /// + /// + /// Can be either a non-negative integer or ScriptBaseClass.LINK_THIS (-4). + /// If ScriptBaseClass.LINK_THIS then the entity containing the script is returned. + /// If the linkset has one entity and a linknum of zero is given, then the single entity is returned. If any + /// positive integer is given in this case then null is returned. + /// If the linkset has more than one entity and a linknum greater than zero but equal to or less than the number + /// of entities, then the entity which corresponds to that linknum is returned. + /// Otherwise, if a positive linknum is given which is greater than the number of entities in the linkset, then + /// null is returned. + /// + public ISceneEntity GetLinkEntity(SceneObjectPart part, int linknum) + { + if (linknum < 0) + { + if (linknum == ScriptBaseClass.LINK_THIS) + return part; + else + return null; + } + + int actualPrimCount = part.ParentGroup.PrimCount; + List sittingAvatars = part.ParentGroup.GetSittingAvatars(); + int adjustedPrimCount = actualPrimCount + sittingAvatars.Count; + + // Special case for a single prim. In this case the linknum is zero. However, this will not match a single + // prim that has any avatars sat upon it (in which case the root prim is link 1). + if (linknum == 0) + { + if (actualPrimCount == 1 && sittingAvatars.Count == 0) + return part; + + return null; + } + // Special case to handle a single prim with sitting avatars. GetLinkPart() would only match zero but + // here we must match 1 (ScriptBaseClass.LINK_ROOT). + else if (linknum == ScriptBaseClass.LINK_ROOT && actualPrimCount == 1) + { + if (sittingAvatars.Count > 0) + return part.ParentGroup.RootPart; + else + return null; + } + else if (linknum <= adjustedPrimCount) + { + if (linknum <= actualPrimCount) + { + return part.ParentGroup.GetLinkNumPart(linknum); + } + else + { + return sittingAvatars[linknum - actualPrimCount - 1]; + } + } + else + { + return null; } } @@ -301,77 +615,52 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api } } - protected UUID InventoryKey(string name, int type) + public List GetLinkEntities(int linkType) { - TaskInventoryItem item = m_host.Inventory.GetInventoryItem(name); - - if (item != null && item.Type == type) - return item.AssetID; - else - return UUID.Zero; + return GetLinkEntities(m_host, linkType); } - /// - /// accepts a valid UUID, -or- a name of an inventory item. - /// Returns a valid UUID or UUID.Zero if key invalid and item not found - /// in prim inventory. - /// - /// - /// - protected UUID KeyOrName(string k) + public List GetLinkEntities(SceneObjectPart part, int linkType) { - UUID key; + List ret; - // if we can parse the string as a key, use it. - // else try to locate the name in inventory of object. found returns key, - // not found returns UUID.Zero - if (!UUID.TryParse(k, out key)) + switch (linkType) { - TaskInventoryItem item = m_host.Inventory.GetInventoryItem(k); + case ScriptBaseClass.LINK_SET: + return new List(part.ParentGroup.Parts); - if (item != null) - key = item.AssetID; - else - key = UUID.Zero; - } + case ScriptBaseClass.LINK_ROOT: + return new List() { part.ParentGroup.RootPart }; - return key; - } + case ScriptBaseClass.LINK_ALL_OTHERS: + ret = new List(part.ParentGroup.Parts); - /// - /// Return the UUID of the asset matching the specified key or name - /// and asset type. - /// - /// - /// - /// - protected UUID KeyOrName(string k, AssetType type) - { - UUID key; + if (ret.Contains(part)) + ret.Remove(part); - if (!UUID.TryParse(k, out key)) - { - TaskInventoryItem item = m_host.Inventory.GetInventoryItem(k); - if (item != null && item.Type == (int)type) - key = item.AssetID; - } - else - { - lock (m_host.TaskInventory) - { - foreach (KeyValuePair item in m_host.TaskInventory) - { - if (item.Value.Type == (int)type && item.Value.Name == k) - { - key = item.Value.ItemID; - break; - } - } - } - } + return ret; + + case ScriptBaseClass.LINK_ALL_CHILDREN: + ret = new List(part.ParentGroup.Parts); + + if (ret.Contains(part.ParentGroup.RootPart)) + ret.Remove(part.ParentGroup.RootPart); + + return ret; + + case ScriptBaseClass.LINK_THIS: + return new List() { part }; + + default: + if (linkType < 0) + return new List(); + ISceneEntity target = GetLinkEntity(part, linkType); + if (target == null) + return new List(); - return key; + return new List() { target }; + } } //These are the implementations of the various ll-functions used by the LSL scripts. @@ -430,10 +719,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api public LSL_Float llFrand(double mag) { m_host.AddScriptLPS(1); - lock (Util.RandomClass) - { - return Util.RandomClass.NextDouble() * mag; - } + + return Util.RandomClass.NextDouble() * mag; } public LSL_Integer llFloor(double f) @@ -837,7 +1124,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api { if (channelID == 0) { - LSLError("Cannot use llRegionSay() on channel 0"); + Error("llRegionSay", "Cannot use on channel 0"); return; } @@ -846,6 +1133,9 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api m_host.AddScriptLPS(1); + World.SimChat(Utils.StringToBytes(text), + ChatTypeEnum.Region, channelID, m_host.ParentGroup.RootPart.AbsolutePosition, m_host.Name, m_host.UUID, false); + IWorldComm wComm = m_ScriptEngine.World.RequestModuleInterface(); if (wComm != null) wComm.DeliverMessage(ChatTypeEnum.Region, channelID, m_host.Name, m_host.UUID, text); @@ -866,6 +1156,9 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api UUID TargetID; UUID.TryParse(target, out TargetID); + World.SimChatToAgent(TargetID, Utils.StringToBytes(msg), + channel, m_host.ParentGroup.RootPart.AbsolutePosition, m_host.Name, m_host.UUID, true); + IWorldComm wComm = m_ScriptEngine.World.RequestModuleInterface(); if (wComm != null) wComm.DeliverMessageTo(TargetID, channel, m_host.AbsolutePosition, m_host.Name, m_host.UUID, msg); @@ -1255,12 +1548,10 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api } if ((status & ScriptBaseClass.STATUS_BLOCK_GRAB) == ScriptBaseClass.STATUS_BLOCK_GRAB) - { - if (value != 0) - m_host.SetBlockGrab(true); - else - m_host.SetBlockGrab(false); - } + m_host.BlockGrab = value != 0; + + if ((status & ScriptBaseClass.STATUS_BLOCK_GRAB_OBJECT) == ScriptBaseClass.STATUS_BLOCK_GRAB_OBJECT) + m_host.ParentGroup.BlockGrabOverride = value != 0; if ((status & ScriptBaseClass.STATUS_DIE_AT_EDGE) == ScriptBaseClass.STATUS_DIE_AT_EDGE) { @@ -1321,10 +1612,10 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api return 0; case ScriptBaseClass.STATUS_BLOCK_GRAB: - if (m_host.GetBlockGrab()) - return 1; - else - return 0; + return m_host.BlockGrab ? 1 : 0; + + case ScriptBaseClass.STATUS_BLOCK_GRAB_OBJECT: + return m_host.ParentGroup.BlockGrabOverride ? 1 : 0; case ScriptBaseClass.STATUS_DIE_AT_EDGE: if (m_host.GetDieAtEdge()) @@ -1427,6 +1718,73 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api m_host.SetFaceColorAlpha(face, color, null); } + public void llSetContentType(LSL_Key id, LSL_Integer type) + { + m_host.AddScriptLPS(1); + + if (m_UrlModule == null) + return; + + // Make sure the content type is text/plain to start with + m_UrlModule.HttpContentType(new UUID(id), "text/plain"); + + // Is the object owner online and in the region + ScenePresence agent = World.GetScenePresence(m_host.ParentGroup.OwnerID); + if (agent == null || agent.IsChildAgent) + return; // Fail if the owner is not in the same region + + // Is it the embeded browser? + string userAgent = m_UrlModule.GetHttpHeader(new UUID(id), "user-agent"); + if (userAgent.IndexOf("SecondLife") < 0) + return; // Not the embedded browser. Is this check good enough? + + // Use the IP address of the client and check against the request + // seperate logins from the same IP will allow all of them to get non-text/plain as long + // as the owner is in the region. Same as SL! + string logonFromIPAddress = agent.ControllingClient.RemoteEndPoint.Address.ToString(); + string requestFromIPAddress = m_UrlModule.GetHttpHeader(new UUID(id), "remote_addr"); + //m_log.Debug("IP from header='" + requestFromIPAddress + "' IP from endpoint='" + logonFromIPAddress + "'"); + if (requestFromIPAddress == null || requestFromIPAddress.Trim() == "") + return; + if (logonFromIPAddress == null || logonFromIPAddress.Trim() == "") + return; + + // If the request isnt from the same IP address then the request cannot be from the owner + if (!requestFromIPAddress.Trim().Equals(logonFromIPAddress.Trim())) + return; + + switch (type) + { + case ScriptBaseClass.CONTENT_TYPE_HTML: + m_UrlModule.HttpContentType(new UUID(id), "text/html"); + break; + case ScriptBaseClass.CONTENT_TYPE_XML: + m_UrlModule.HttpContentType(new UUID(id), "application/xml"); + break; + case ScriptBaseClass.CONTENT_TYPE_XHTML: + m_UrlModule.HttpContentType(new UUID(id), "application/xhtml+xml"); + break; + case ScriptBaseClass.CONTENT_TYPE_ATOM: + m_UrlModule.HttpContentType(new UUID(id), "application/atom+xml"); + break; + case ScriptBaseClass.CONTENT_TYPE_JSON: + m_UrlModule.HttpContentType(new UUID(id), "application/json"); + break; + case ScriptBaseClass.CONTENT_TYPE_LLSD: + m_UrlModule.HttpContentType(new UUID(id), "application/llsd+xml"); + break; + case ScriptBaseClass.CONTENT_TYPE_FORM: + m_UrlModule.HttpContentType(new UUID(id), "application/x-www-form-urlencoded"); + break; + case ScriptBaseClass.CONTENT_TYPE_RSS: + m_UrlModule.HttpContentType(new UUID(id), "application/rss+xml"); + break; + default: + m_UrlModule.HttpContentType(new UUID(id), "text/plain"); + break; + } + } + public void SetTexGen(SceneObjectPart part, int face,int style) { Primitive.TextureEntry tex = part.Shape.Textures; @@ -1522,7 +1880,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api if (tex.FaceTextures[i] != null) { tex.FaceTextures[i].Shiny = sval; - tex.FaceTextures[i].Bump = bump;; + tex.FaceTextures[i].Bump = bump; } tex.DefaultTexture.Shiny = sval; tex.DefaultTexture.Bump = bump; @@ -1631,7 +1989,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api texcolor.A = Util.Clip((float)alpha, 0.0f, 1.0f); tex.DefaultTexture.RGBA = texcolor; } - + part.UpdateTextureEntry(tex.GetBytes()); return; } @@ -1703,9 +2061,9 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api part.Shape.LightColorR = Util.Clip((float)color.x, 0.0f, 1.0f); part.Shape.LightColorG = Util.Clip((float)color.y, 0.0f, 1.0f); part.Shape.LightColorB = Util.Clip((float)color.z, 0.0f, 1.0f); - part.Shape.LightIntensity = intensity; - part.Shape.LightRadius = radius; - part.Shape.LightFalloff = falloff; + part.Shape.LightIntensity = Util.Clip((float)intensity, 0.0f, 1.0f); + part.Shape.LightRadius = Util.Clip((float)radius, 0.1f, 20.0f); + part.Shape.LightFalloff = Util.Clip((float)falloff, 0.01f, 2.0f); } else { @@ -1752,7 +2110,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api rgb.x = texcolor.R; rgb.y = texcolor.G; rgb.z = texcolor.B; - + return rgb; } else @@ -1765,7 +2123,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api { m_host.AddScriptLPS(1); SetTexture(m_host, texture, face); - ScriptSleep(200); + ScriptSleep(m_sleepMsOnSetTexture); } public void llSetLinkTexture(int linknumber, string texture, int face) @@ -1777,19 +2135,19 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api foreach (SceneObjectPart part in parts) SetTexture(part, texture, face); - ScriptSleep(200); + ScriptSleep(m_sleepMsOnSetLinkTexture); } protected void SetTexture(SceneObjectPart part, string texture, int face) { UUID textureID = new UUID(); - textureID = InventoryKey(texture, (int)AssetType.Texture); - if (textureID == UUID.Zero) - { - if (!UUID.TryParse(texture, out textureID)) - return; - } + textureID = ScriptUtils.GetAssetIdFromItemName(m_host, texture, (int)AssetType.Texture); + if (textureID == UUID.Zero) + { + if (!UUID.TryParse(texture, out textureID)) + return; + } Primitive.TextureEntry tex = part.Shape.Textures; @@ -1821,7 +2179,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api m_host.AddScriptLPS(1); ScaleTexture(m_host, u, v, face); - ScriptSleep(200); + ScriptSleep(m_sleepMsOnScaleTexture); } protected void ScaleTexture(SceneObjectPart part, double u, double v, int face) @@ -1857,7 +2215,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api { m_host.AddScriptLPS(1); OffsetTexture(m_host, u, v, face); - ScriptSleep(200); + ScriptSleep(m_sleepMsOnOffsetTexture); } protected void OffsetTexture(SceneObjectPart part, double u, double v, int face) @@ -1893,7 +2251,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api { m_host.AddScriptLPS(1); RotateTexture(m_host, rotation, face); - ScriptSleep(200); + ScriptSleep(m_sleepMsOnRotateTexture); } protected void RotateTexture(SceneObjectPart part, double rotation, int face) @@ -1968,7 +2326,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api SetPos(m_host, pos, true); - ScriptSleep(200); + ScriptSleep(m_sleepMsOnSetPos); } /// @@ -1986,8 +2344,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api // IF YOU GET REGION CROSSINGS WORKING WITH THIS FUNCTION, REPLACE THE WORKAROUND. // // This workaround is to prevent silent failure of this function. - // According to the specification on the SL Wiki, providing a position outside of the - if (pos.x < 0 || pos.x > Constants.RegionSize || pos.y < 0 || pos.y > Constants.RegionSize) + // According to the specification on the SL Wiki, providing a position outside of the + if (pos.x < 0 || pos.x > World.RegionInfo.RegionSizeX || pos.y < 0 || pos.y > World.RegionInfo.RegionSizeY) { return 0; } @@ -1997,9 +2355,9 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api m_host.ParentGroup.IsAttachment || // return FALSE if attachment ( pos.x < -10.0 || // return FALSE if more than 10 meters into a west-adjacent region. - pos.x > (Constants.RegionSize + 10) || // return FALSE if more than 10 meters into a east-adjacent region. + pos.x > (World.RegionInfo.RegionSizeX + 10) || // return FALSE if more than 10 meters into a east-adjacent region. pos.y < -10.0 || // return FALSE if more than 10 meters into a south-adjacent region. - pos.y > (Constants.RegionSize + 10) || // return FALSE if more than 10 meters into a north-adjacent region. + pos.y > (World.RegionInfo.RegionSizeY + 10) || // return FALSE if more than 10 meters into a north-adjacent region. pos.z > Constants.RegionHeight // return FALSE if altitude than 4096m ) ) @@ -2151,14 +2509,14 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api } } - ScriptSleep(200); + ScriptSleep(m_sleepMsOnSetRot); } public void llSetLocalRot(LSL_Rotation rot) { m_host.AddScriptLPS(1); SetRot(m_host, rot); - ScriptSleep(200); + ScriptSleep(m_sleepMsOnSetLocalRot); } protected void SetRot(SceneObjectPart part, Quaternion rot) @@ -2195,7 +2553,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api { return llGetRootRotation(); } - + m_host.AddScriptLPS(1); Quaternion q = m_host.GetWorldRotation(); return new LSL_Rotation(q.X, q.Y, q.Z, q.W); @@ -2214,23 +2572,25 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api if ((avatar.AgentControlFlags & (uint)AgentManager.ControlFlags.AGENT_CONTROL_MOUSELOOK) != 0) q = avatar.CameraRotation; // Mouselook else - q = avatar.Rotation; // Currently infrequently updated so may be inaccurate + q = avatar.GetWorldRotation(); // Currently infrequently updated so may be inaccurate } else q = part.ParentGroup.GroupRotation; // Likely never get here but just in case } else q = part.ParentGroup.GroupRotation; // just the group rotation - return new LSL_Rotation(q.X, q.Y, q.Z, q.W); + + return new LSL_Rotation(q); } - q = part.GetWorldRotation(); - return new LSL_Rotation(q.X, q.Y, q.Z, q.W); + + return new LSL_Rotation(part.GetWorldRotation()); } public LSL_Rotation llGetLocalRot() { m_host.AddScriptLPS(1); - return new LSL_Rotation(m_host.RotationOffset.X, m_host.RotationOffset.Y, m_host.RotationOffset.Z, m_host.RotationOffset.W); + + return new LSL_Rotation(m_host.RotationOffset); } public void llSetForce(LSL_Vector force, int local) @@ -2260,6 +2620,32 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api return force; } + public void llSetVelocity(LSL_Vector velocity, int local) + { + m_host.AddScriptLPS(1); + + if (!m_host.ParentGroup.IsDeleted) + { + if (local != 0) + velocity *= llGetRot(); + + m_host.ParentGroup.RootPart.Velocity = velocity; + } + } + + public void llSetAngularVelocity(LSL_Vector angularVelocity, int local) + { + m_host.AddScriptLPS(1); + + if (!m_host.ParentGroup.IsDeleted) + { + if (local != 0) + angularVelocity *= llGetRot(); + + m_host.ParentGroup.RootPart.AngularVelocity = angularVelocity; + } + } + public LSL_Integer llTarget(LSL_Vector position, double range) { m_host.AddScriptLPS(1); @@ -2325,8 +2711,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api public LSL_Vector llGetTorque() { m_host.AddScriptLPS(1); - Vector3 torque = m_host.ParentGroup.GetTorque(); - return new LSL_Vector(torque.X,torque.Y,torque.Z); + + return new LSL_Vector(m_host.ParentGroup.GetTorque()); } public void llSetForceAndTorque(LSL_Vector force, LSL_Vector torque, int local) @@ -2345,26 +2731,28 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api if (m_host.ParentGroup.IsAttachment) { ScenePresence avatar = m_host.ParentGroup.Scene.GetScenePresence(m_host.ParentGroup.AttachedAvatar); - vel = avatar.Velocity; + vel = avatar.GetWorldVelocity(); } else { vel = m_host.Velocity; } - return new LSL_Vector(vel.X, vel.Y, vel.Z); + return new LSL_Vector(vel); } public LSL_Vector llGetAccel() { m_host.AddScriptLPS(1); - return new LSL_Vector(m_host.Acceleration.X, m_host.Acceleration.Y, m_host.Acceleration.Z); + + return new LSL_Vector(m_host.Acceleration); } public LSL_Vector llGetOmega() { m_host.AddScriptLPS(1); - return new LSL_Vector(m_host.AngularVelocity.X, m_host.AngularVelocity.Y, m_host.AngularVelocity.Z); + + return new LSL_Vector(m_host.AngularVelocity); } public LSL_Float llGetTimeOfDay() @@ -2403,9 +2791,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api public void llSound(string sound, double volume, int queue, int loop) { m_host.AddScriptLPS(1); - // This function has been deprecated - // see http://www.lslwiki.net/lslwiki/wakka.php?wakka=llSound - Deprecated("llSound"); + Deprecated("llSound", "Use llPlaySound instead"); } // Xantor 20080528 PlaySound updated so it accepts an objectinventory name -or- a key to a sound @@ -2417,9 +2803,11 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api // send the sound, once, to all clients in range if (m_SoundModule != null) { - m_SoundModule.SendSound(m_host.UUID, - KeyOrName(sound, AssetType.Sound), volume, false, 0, - 0, false, false); + m_SoundModule.SendSound( + m_host.UUID, + ScriptUtils.GetAssetIdFromKeyOrItemName(m_host, sound, AssetType.Sound), + volume, false, m_host.SoundQueueing ? (byte)SoundFlags.Queue : (byte)SoundFlags.None, + 0, false, false); } } @@ -2428,7 +2816,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api m_host.AddScriptLPS(1); if (m_SoundModule != null) { - m_SoundModule.LoopSound(m_host.UUID, KeyOrName(sound), + m_SoundModule.LoopSound(m_host.UUID, ScriptUtils.GetAssetIdFromKeyOrItemName(m_host, sound), volume, 20, false); } } @@ -2438,7 +2826,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api m_host.AddScriptLPS(1); if (m_SoundModule != null) { - m_SoundModule.LoopSound(m_host.UUID, KeyOrName(sound), + m_SoundModule.LoopSound(m_host.UUID, ScriptUtils.GetAssetIdFromKeyOrItemName(m_host, sound), volume, 20, true); } } @@ -2460,7 +2848,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api if (m_SoundModule != null) { m_SoundModule.SendSound(m_host.UUID, - KeyOrName(sound, AssetType.Sound), volume, false, 0, + ScriptUtils.GetAssetIdFromKeyOrItemName(m_host, sound, AssetType.Sound), volume, false, 0, 0, true, false); } } @@ -2472,7 +2860,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api if (m_SoundModule != null) { m_SoundModule.SendSound(m_host.UUID, - KeyOrName(sound, AssetType.Sound), volume, true, 0, 0, + ScriptUtils.GetAssetIdFromKeyOrItemName(m_host, sound, AssetType.Sound), volume, true, 0, 0, false, false); } } @@ -2489,8 +2877,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api { m_host.AddScriptLPS(1); if (m_SoundModule != null) - m_SoundModule.PreloadSound(m_host.UUID, KeyOrName(sound), 0); - ScriptSleep(1000); + m_SoundModule.PreloadSound(m_host.UUID, ScriptUtils.GetAssetIdFromKeyOrItemName(m_host, sound), 0); + ScriptSleep(m_sleepMsOnPreloadSound); } /// @@ -2723,70 +3111,68 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api return src.ToLower(); } - public LSL_Integer llGiveMoney(string destination, int amount) + public void llGiveMoney(string destination, int amount) { - m_host.AddScriptLPS(1); - - if (m_item.PermsGranter == UUID.Zero) - return 0; - - if ((m_item.PermsMask & ScriptBaseClass.PERMISSION_DEBIT) == 0) + Util.FireAndForget(x => { - LSLError("No permissions to give money"); - return 0; - } + m_host.AddScriptLPS(1); - UUID toID = new UUID(); + if (m_item.PermsGranter == UUID.Zero) + return; - if (!UUID.TryParse(destination, out toID)) - { - LSLError("Bad key in llGiveMoney"); - return 0; - } + if ((m_item.PermsMask & ScriptBaseClass.PERMISSION_DEBIT) == 0) + { + Error("llGiveMoney", "No permissions to give money"); + return; + } - IMoneyModule money = World.RequestModuleInterface(); + UUID toID = new UUID(); - if (money == null) - { - NotImplemented("llGiveMoney"); - return 0; - } + if (!UUID.TryParse(destination, out toID)) + { + Error("llGiveMoney", "Bad key in llGiveMoney"); + return; + } - bool result = money.ObjectGiveMoney( - m_host.ParentGroup.RootPart.UUID, m_host.ParentGroup.RootPart.OwnerID, toID, amount); + IMoneyModule money = World.RequestModuleInterface(); - if (result) - return 1; + if (money == null) + { + NotImplemented("llGiveMoney"); + return; + } - return 0; + money.ObjectGiveMoney( + m_host.ParentGroup.RootPart.UUID, m_host.ParentGroup.RootPart.OwnerID, toID, amount); + }, null, "LSL_Api.llGiveMoney"); } public void llMakeExplosion(int particles, double scale, double vel, double lifetime, double arc, string texture, LSL_Vector offset) { m_host.AddScriptLPS(1); - Deprecated("llMakeExplosion"); - ScriptSleep(100); + Deprecated("llMakeExplosion", "Use llParticleSystem instead"); + ScriptSleep(m_sleepMsOnMakeExplosion); } public void llMakeFountain(int particles, double scale, double vel, double lifetime, double arc, int bounce, string texture, LSL_Vector offset, double bounce_offset) { m_host.AddScriptLPS(1); - Deprecated("llMakeFountain"); - ScriptSleep(100); + Deprecated("llMakeFountain", "Use llParticleSystem instead"); + ScriptSleep(m_sleepMsOnMakeFountain); } public void llMakeSmoke(int particles, double scale, double vel, double lifetime, double arc, string texture, LSL_Vector offset) { m_host.AddScriptLPS(1); - Deprecated("llMakeSmoke"); - ScriptSleep(100); + Deprecated("llMakeSmoke", "Use llParticleSystem instead"); + ScriptSleep(m_sleepMsOnMakeSmoke); } public void llMakeFire(int particles, double scale, double vel, double lifetime, double arc, string texture, LSL_Vector offset) { m_host.AddScriptLPS(1); - Deprecated("llMakeFire"); - ScriptSleep(100); + Deprecated("llMakeFire", "Use llParticleSystem instead"); + ScriptSleep(m_sleepMsOnMakeFire); } public void llRezAtRoot(string inventory, LSL_Vector pos, LSL_Vector vel, LSL_Rotation rot, int param) @@ -2807,54 +3193,57 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api if (item == null) { - llSay(0, "Could not find object " + inventory); + Error("llRezAtRoot", "Can't find object '" + inventory + "'"); return; } if (item.InvType != (int)InventoryType.Object) { - llSay(0, "Unable to create requested object. Object is missing from database."); + Error("llRezAtRoot", "Can't create requested object; object is missing from database"); return; } // need the magnitude later // float velmag = (float)Util.GetMagnitude(llvel); - SceneObjectGroup new_group = World.RezObject(m_host, item, pos, rot, vel, param); + List new_groups = World.RezObject(m_host, item, pos, rot, vel, param); // If either of these are null, then there was an unknown error. - if (new_group == null) + if (new_groups == null) return; - // objects rezzed with this method are die_at_edge by default. - new_group.RootPart.SetDieAtEdge(true); + foreach (SceneObjectGroup group in new_groups) + { + // objects rezzed with this method are die_at_edge by default. + group.RootPart.SetDieAtEdge(true); - new_group.ResumeScripts(); + group.ResumeScripts(); - m_ScriptEngine.PostObjectEvent(m_host.LocalId, new EventParams( - "object_rez", new Object[] { - new LSL_String( - new_group.RootPart.UUID.ToString()) }, - new DetectParams[0])); + m_ScriptEngine.PostObjectEvent(m_host.LocalId, new EventParams( + "object_rez", new Object[] { + new LSL_String( + group.RootPart.UUID.ToString()) }, + new DetectParams[0])); - float groupmass = new_group.GetMass(); + float groupmass = group.GetMass(); - PhysicsActor pa = new_group.RootPart.PhysActor; + PhysicsActor pa = group.RootPart.PhysActor; - //Recoil. - if (pa != null && pa.IsPhysical && (Vector3)vel != Vector3.Zero) - { - Vector3 recoil = -vel * groupmass * m_recoilScaleFactor; - if (recoil != Vector3.Zero) + //Recoil. + if (pa != null && pa.IsPhysical && (Vector3)vel != Vector3.Zero) { - llApplyImpulse(recoil, 0); + Vector3 recoil = -vel * groupmass * m_recoilScaleFactor; + if (recoil != Vector3.Zero) + { + llApplyImpulse(recoil, 0); + } } + // Variable script delay? (see (http://wiki.secondlife.com/wiki/LSL_Delay) } - // Variable script delay? (see (http://wiki.secondlife.com/wiki/LSL_Delay) - }); + }, null, "LSL_Api.llRezAtRoot"); //ScriptSleep((int)((groupmass * velmag) / 10)); - ScriptSleep(100); + ScriptSleep(m_sleepMsOnRezAtRoot); } public void llRezObject(string inventory, LSL_Vector pos, LSL_Vector vel, LSL_Rotation rot, int param) @@ -2868,26 +3257,23 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api // Determine where we are looking from LSL_Vector from = llGetPos(); - // Work out the normalised vector from the source to the target - LSL_Vector delta = llVecNorm(target - from); - LSL_Vector angle = new LSL_Vector(0,0,0); - - // Calculate the yaw - // subtracting PI_BY_TWO is required to compensate for the odd SL co-ordinate system - angle.x = llAtan2(delta.z, delta.y) - ScriptBaseClass.PI_BY_TWO; + // normalized direction to target + LSL_Vector dir = llVecNorm(target - from); + // use vertical to help compute left axis + LSL_Vector up = new LSL_Vector(0.0, 0.0, 1.0); + // find normalized left axis parallel to horizon + LSL_Vector left = llVecNorm(LSL_Vector.Cross(up, dir)); + // make up orthogonal to left and dir + up = LSL_Vector.Cross(dir, left); - // Calculate pitch - angle.y = llAtan2(delta.x, llSqrt((delta.y * delta.y) + (delta.z * delta.z))); + // compute rotation based on orthogonal axes + LSL_Rotation rot = new LSL_Rotation(0.0, 0.707107, 0.0, 0.707107) * llAxes2Rot(dir, left, up); - // we need to convert from a vector describing - // the angles of rotation in radians into rotation value - LSL_Rotation rot = llEuler2Rot(angle); - // Per discussion with Melanie, for non-physical objects llLookAt appears to simply // set the rotation of the object, copy that behavior PhysicsActor pa = m_host.PhysActor; - if (strength == 0 || pa == null || !pa.IsPhysical) + if (m_host.ParentGroup.IsAttachment || strength == 0 || pa == null || !pa.IsPhysical) { llSetRot(rot); } @@ -2900,7 +3286,6 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api public void llStopLookAt() { m_host.AddScriptLPS(1); -// NotImplemented("llStopLookAt"); m_host.StopLookAt(); } @@ -2917,7 +3302,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api { // m_log.Info("llSleep snoozing " + sec + "s."); m_host.AddScriptLPS(1); - Thread.Sleep((int)(sec * 1000)); + + Sleep((int)(sec * 1000)); } public LSL_Float llGetMass() @@ -2950,6 +3336,13 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api } } + public LSL_Float llGetMassMKS() + { + // this is what the wiki says it does! + // http://wiki.secondlife.com/wiki/LlGetMassMKS + return llGetMass() * 100.0; + } + public void llCollisionFilter(string name, string id, int accept) { m_host.AddScriptLPS(1); @@ -2958,7 +3351,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api if (!UUID.TryParse(id, out objectID)) objectID = UUID.Zero; - + if (objectID == UUID.Zero && name == "") return; @@ -3026,7 +3419,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api IAttachmentsModule attachmentsModule = m_ScriptEngine.World.AttachmentsModule; if (attachmentsModule != null) - return attachmentsModule.AttachObject(presence, grp, (uint)attachmentPoint, false, false); + return attachmentsModule.AttachObject(presence, grp, (uint)attachmentPoint, false, true, true); else return false; } @@ -3039,7 +3432,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api /// public void DetachFromAvatar() { - Util.FireAndForget(DetachWrapper, m_host); + Util.FireAndForget(DetachWrapper, m_host, "LSL_Api.DetachFromAvatar"); } private void DetachWrapper(object o) @@ -3083,13 +3476,13 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api public void llTakeCamera(string avatar) { m_host.AddScriptLPS(1); - Deprecated("llTakeCamera"); + Deprecated("llTakeCamera", "Use llSetCameraParams instead"); } public void llReleaseCamera(string avatar) { m_host.AddScriptLPS(1); - Deprecated("llReleaseCamera"); + Deprecated("llReleaseCamera", "Use llClearCameraParams instead"); } public LSL_String llGetOwner() @@ -3112,14 +3505,11 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api // TODO: figure out values for client, fromSession, and imSessionID // client.SendInstantMessage(m_host.UUID, fromSession, message, user, imSessionID, m_host.Name, AgentManager.InstantMessageDialog.MessageFromAgent, (uint)Util.UnixTimeSinceEpoch()); - UUID friendTransactionID = UUID.Random(); - - //m_pendingFriendRequests.Add(friendTransactionID, fromAgentID); GridInstantMessage msg = new GridInstantMessage(); - msg.fromAgentID = new Guid(m_host.UUID.ToString()); // fromAgentID.Guid; + msg.fromAgentID = new Guid(m_host.OwnerID.ToString()); // fromAgentID.Guid; msg.toAgentID = new Guid(user); // toAgentID.Guid; - msg.imSessionID = new Guid(friendTransactionID.ToString()); // This is the item we're mucking with here + msg.imSessionID = new Guid(m_host.UUID.ToString()); // This is the item we're mucking with here // m_log.Debug("[Scripting IM]: From:" + msg.fromAgentID.ToString() + " To: " + msg.toAgentID.ToString() + " Session:" + msg.imSessionID.ToString() + " Message:" + message); // m_log.Debug("[Scripting IM]: Filling Session: " + msg.imSessionID.ToString()); msg.timestamp = (uint)Util.UnixTimeSinceEpoch();// timestamp; @@ -3142,20 +3532,22 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api msg.ParentEstateID = 0; //ParentEstateID; msg.Position = new Vector3(m_host.AbsolutePosition); msg.RegionID = World.RegionInfo.RegionID.Guid;//RegionID.Guid; - msg.binaryBucket + + Vector3 pos = m_host.AbsolutePosition; + msg.binaryBucket = Util.StringToBytes256( - "{0}/{1}/{2}/{3}", - World.RegionInfo.RegionName, - (int)Math.Floor(m_host.AbsolutePosition.X), - (int)Math.Floor(m_host.AbsolutePosition.Y), - (int)Math.Floor(m_host.AbsolutePosition.Z)); + "{0}/{1}/{2}/{3}", + World.RegionInfo.RegionName, + (int)Math.Floor(pos.X), + (int)Math.Floor(pos.Y), + (int)Math.Floor(pos.Z)); if (m_TransferModule != null) { m_TransferModule.SendInstantMessage(msg, delegate(bool success) {}); } - - ScriptSleep(2000); + + ScriptSleep(m_sleepMsOnInstantMessage); } public void llEmail(string address, string subject, string message) @@ -3164,12 +3556,36 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api IEmailModule emailModule = m_ScriptEngine.World.RequestModuleInterface(); if (emailModule == null) { - ShoutError("llEmail: email module not configured"); + Error("llEmail", "Email module not configured"); return; } + //Restrict email destination to the avatars registered email address? + //The restriction only applies if the destination address is not local. + if (m_restrictEmail == true && address.Contains(m_internalObjectHost) == false) + { + UserAccount account = + World.UserAccountService.GetUserAccount( + World.RegionInfo.ScopeID, + m_host.OwnerID); + + if (account == null) + { + Error("llEmail", "Can't find user account for '" + m_host.OwnerID.ToString() + "'"); + return; + } + + if (String.IsNullOrEmpty(account.Email)) + { + Error("llEmail", "User account has not registered an email address."); + return; + } + + address = account.Email; + } + emailModule.SendEmail(m_host.UUID, address, subject, message); - llSleep(EMAIL_PAUSE_TIME); + ScriptSleep(m_sleepMsOnEmail); } public void llGetNextEmail(string address, string subject) @@ -3178,7 +3594,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api IEmailModule emailModule = m_ScriptEngine.World.RequestModuleInterface(); if (emailModule == null) { - ShoutError("llGetNextEmail: email module not configured"); + Error("llGetNextEmail", "Email module not configured"); return; } Email email; @@ -3263,23 +3679,20 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api catch (NotImplementedException) { // Currently not implemented in DotNetEngine only XEngine - NotImplemented("llMinEventDelay in DotNetEngine"); + NotImplemented("llMinEventDelay", "In DotNetEngine"); } } - /// - /// llSoundPreload is deprecated. In SL this appears to do absolutely nothing - /// and is documented to have no delay. - /// public void llSoundPreload(string sound) { m_host.AddScriptLPS(1); + Deprecated("llSoundPreload", "Use llPreloadSound instead"); } public void llRotLookAt(LSL_Rotation target, double strength, double damping) { m_host.AddScriptLPS(1); - + // Per discussion with Melanie, for non-physical objects llLookAt appears to simply // set the rotation of the object, copy that behavior PhysicsActor pa = m_host.PhysActor; @@ -3321,7 +3734,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api if (presence != null) { // Do NOT try to parse UUID, animations cannot be triggered by ID - UUID animID = InventoryKey(anim, (int)AssetType.Animation); + UUID animID = ScriptUtils.GetAssetIdFromItemName(m_host, anim, (int)AssetType.Animation); if (animID == UUID.Zero) presence.Animator.AddAnimation(anim, m_host.UUID); else @@ -3343,7 +3756,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api if (presence != null) { - UUID animID = KeyOrName(anim); + UUID animID = ScriptUtils.GetAssetIdFromKeyOrItemName(m_host, anim); if (animID == UUID.Zero) presence.Animator.RemoveAnimation(anim); @@ -3371,6 +3784,9 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api protected void TargetOmega(SceneObjectPart part, LSL_Vector axis, double spinrate, double gain) { + PhysicsActor pa = part.PhysActor; + if ( ( pa == null || !pa.IsPhysical ) && gain == 0.0d ) + spinrate = 0.0d; part.UpdateAngularVelocity(axis * spinrate); } @@ -3415,11 +3831,12 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api implicitPerms = ScriptBaseClass.PERMISSION_TAKE_CONTROLS | ScriptBaseClass.PERMISSION_TRIGGER_ANIMATION | ScriptBaseClass.PERMISSION_CONTROL_CAMERA | + ScriptBaseClass.PERMISSION_TRACK_CAMERA | ScriptBaseClass.PERMISSION_ATTACH; } else { - if (m_host.ParentGroup.GetSittingAvatars().Contains(agentID)) + if (m_host.ParentGroup.GetSittingAvatars().SingleOrDefault(sp => sp.UUID == agentID) != null) { // When agent is sitting, certain permissions are implicit if requested from sitting agent implicitPerms = ScriptBaseClass.PERMISSION_TRIGGER_ANIMATION | @@ -3451,10 +3868,11 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api } ScenePresence presence = World.GetScenePresence(agentID); + if (presence != null) { // If permissions are being requested from an NPC and were not implicitly granted above then - // auto grant all reuqested permissions if the script is owned by the NPC or the NPCs owner + // auto grant all requested permissions if the script is owned by the NPC or the NPCs owner INPCModule npcModule = World.RequestModuleInterface(); if (npcModule != null && npcModule.IsNPC(agentID, World)) { @@ -3570,22 +3988,23 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api public void llCreateLink(string target, int parent) { m_host.AddScriptLPS(1); - UUID targetID; - - if (!UUID.TryParse(target, out targetID)) - return; if ((m_item.PermsMask & ScriptBaseClass.PERMISSION_CHANGE_LINKS) == 0 && !m_automaticLinkPermission) { - ShoutError("Script trying to link but PERMISSION_CHANGE_LINKS permission not set!"); + Error("llCreateLink", "PERMISSION_CHANGE_LINKS permission not set"); return; } - IClientAPI client = null; - ScenePresence sp = World.GetScenePresence(m_item.PermsGranter); - if (sp != null) - client = sp.ControllingClient; + CreateLink(target, parent); + } + + public void CreateLink(string target, int parent) + { + UUID targetID; + + if (!UUID.TryParse(target, out targetID)) + return; SceneObjectPart targetPart = World.GetSceneObjectPart((UUID)targetID); @@ -3620,10 +4039,15 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api parentPrim.HasGroupChanged = true; parentPrim.ScheduleGroupForFullUpdate(); + IClientAPI client = null; + ScenePresence sp = World.GetScenePresence(m_host.OwnerID); + if (sp != null) + client = sp.ControllingClient; + if (client != null) parentPrim.SendPropertiesToClient(client); - ScriptSleep(1000); + ScriptSleep(m_sleepMsOnCreateLink); } public void llBreakLink(int linknum) @@ -3633,10 +4057,15 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api if ((m_item.PermsMask & ScriptBaseClass.PERMISSION_CHANGE_LINKS) == 0 && !m_automaticLinkPermission) { - ShoutError("Script trying to link but PERMISSION_CHANGE_LINKS permission not set!"); + Error("llBreakLink", "PERMISSION_CHANGE_LINKS permission not set"); return; } + BreakLink(linknum); + } + + public void BreakLink(int linknum) + { if (linknum < ScriptBaseClass.LINK_THIS) return; @@ -3712,6 +4141,19 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api public void llBreakAllLinks() { m_host.AddScriptLPS(1); + + if ((m_item.PermsMask & ScriptBaseClass.PERMISSION_CHANGE_LINKS) == 0 + && !m_automaticLinkPermission) + { + Error("llBreakAllLinks", "PERMISSION_CHANGE_LINKS permission not set"); + return; + } + + BreakAllLinks(); + } + + public void BreakAllLinks() + { SceneObjectGroup parentPrim = m_host.ParentGroup; if (parentPrim.AttachmentPoint != 0) return; // Fail silently if attached @@ -3732,47 +4174,12 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api { m_host.AddScriptLPS(1); - if (linknum < 0) - { - if (linknum == ScriptBaseClass.LINK_THIS) - return m_host.UUID.ToString(); - else - return ScriptBaseClass.NULL_KEY; - } - - int actualPrimCount = m_host.ParentGroup.PrimCount; - List sittingAvatarIds = m_host.ParentGroup.GetSittingAvatars(); - int adjustedPrimCount = actualPrimCount + sittingAvatarIds.Count; - - // Special case for a single prim. In this case the linknum is zero. However, this will not match a single - // prim that has any avatars sat upon it (in which case the root prim is link 1). - if (linknum == 0) - { - if (actualPrimCount == 1 && sittingAvatarIds.Count == 0) - return m_host.UUID.ToString(); + ISceneEntity entity = GetLinkEntity(m_host, linknum); - return ScriptBaseClass.NULL_KEY; - } - // Special case to handle a single prim with sitting avatars. GetLinkPart() would only match zero but - // here we must match 1 (ScriptBaseClass.LINK_ROOT). - else if (linknum == 1 && actualPrimCount == 1) - { - if (sittingAvatarIds.Count > 0) - return m_host.ParentGroup.RootPart.UUID.ToString(); - else - return ScriptBaseClass.NULL_KEY; - } - else if (linknum <= adjustedPrimCount) - { - if (linknum <= actualPrimCount) - return m_host.ParentGroup.GetLinkNumPart(linknum).UUID.ToString(); - else - return sittingAvatarIds[linknum - actualPrimCount - 1].ToString(); - } + if (entity != null) + return entity.UUID.ToString(); else - { return ScriptBaseClass.NULL_KEY; - } } /// @@ -3818,55 +4225,12 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api { m_host.AddScriptLPS(1); - if (linknum < 0) - { - if (linknum == ScriptBaseClass.LINK_THIS) - return m_host.Name; - else - return ScriptBaseClass.NULL_KEY; - } - - int actualPrimCount = m_host.ParentGroup.PrimCount; - List sittingAvatarIds = m_host.ParentGroup.GetSittingAvatars(); - int adjustedPrimCount = actualPrimCount + sittingAvatarIds.Count; - - // Special case for a single prim. In this case the linknum is zero. However, this will not match a single - // prim that has any avatars sat upon it (in which case the root prim is link 1). - if (linknum == 0) - { - if (actualPrimCount == 1 && sittingAvatarIds.Count == 0) - return m_host.Name; + ISceneEntity entity = GetLinkEntity(m_host, linknum); - return ScriptBaseClass.NULL_KEY; - } - // Special case to handle a single prim with sitting avatars. GetLinkPart() would only match zero but - // here we must match 1 (ScriptBaseClass.LINK_ROOT). - else if (linknum == 1 && actualPrimCount == 1) - { - if (sittingAvatarIds.Count > 0) - return m_host.ParentGroup.RootPart.Name; - else - return ScriptBaseClass.NULL_KEY; - } - else if (linknum <= adjustedPrimCount) - { - if (linknum <= actualPrimCount) - { - return m_host.ParentGroup.GetLinkNumPart(linknum).Name; - } - else - { - ScenePresence sp = World.GetScenePresence(sittingAvatarIds[linknum - actualPrimCount - 1]); - if (sp != null) - return sp.Name; - else - return ScriptBaseClass.NULL_KEY; - } - } + if (entity != null) + return entity.Name; else - { return ScriptBaseClass.NULL_KEY; - } } public LSL_Integer llGetInventoryNumber(int type) @@ -3931,7 +4295,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api if (!UUID.TryParse(destination, out destId)) { - llSay(0, "Could not parse key " + destination); + Error("llGiveInventory", "Can't parse destination key '" + destination + "'"); return; } @@ -3939,8 +4303,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api if (item == null) { - llSay(0, String.Format("Could not find object '{0}'", inventory)); - throw new Exception(String.Format("The inventory object '{0}' could not be found", inventory)); + Error("llGiveInventory", "Can't find inventory object '" + inventory + "'"); + return; } UUID objId = item.ItemID; @@ -3964,15 +4328,23 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api if (account == null) { - llSay(0, "Can't find destination "+destId.ToString()); - return; + GridUserInfo info = World.GridUserService.GetGridUserInfo(destId.ToString()); + if(info == null || info.Online == false) + { + Error("llGiveInventory", "Can't find destination '" + destId.ToString() + "'"); + return; + } } } // destination is an avatar - InventoryItemBase agentItem = World.MoveTaskInventoryItem(destId, UUID.Zero, m_host, objId); + string message; + InventoryItemBase agentItem = World.MoveTaskInventoryItem(destId, UUID.Zero, m_host, objId, out message); if (agentItem == null) + { + llSay(0, message); return; + } if (m_TransferModule != null) { @@ -3991,7 +4363,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api m_TransferModule.SendInstantMessage(msg, delegate(bool success) {}); } - ScriptSleep(3000); + ScriptSleep(m_sleepMsOnGiveInventory); } } @@ -4054,87 +4426,98 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api UserAccount account; UserInfoCacheEntry ce; - if (!m_userInfoCache.TryGetValue(uuid, out ce)) + + lock (m_userInfoCache) { - account = World.UserAccountService.GetUserAccount(World.RegionInfo.ScopeID, uuid); - if (account == null) + if (!m_userInfoCache.TryGetValue(uuid, out ce)) { - m_userInfoCache[uuid] = null; // Cache negative - return UUID.Zero.ToString(); - } - + account = World.UserAccountService.GetUserAccount(World.RegionInfo.ScopeID, uuid); + if (account == null) + { + m_userInfoCache[uuid] = null; // Cache negative + return UUID.Zero.ToString(); + } - PresenceInfo[] pinfos = World.PresenceService.GetAgents(new string[] { uuid.ToString() }); - if (pinfos != null && pinfos.Length > 0) - { - foreach (PresenceInfo p in pinfos) + PresenceInfo[] pinfos = World.PresenceService.GetAgents(new string[] { uuid.ToString() }); + if (pinfos != null && pinfos.Length > 0) { - if (p.RegionID != UUID.Zero) + foreach (PresenceInfo p in pinfos) { - pinfo = p; + if (p.RegionID != UUID.Zero) + { + pinfo = p; + } } } - } - - ce = new UserInfoCacheEntry(); - ce.time = Util.EnvironmentTickCount(); - ce.account = account; - ce.pinfo = pinfo; - } - else - { - if (ce == null) - return UUID.Zero.ToString(); - account = ce.account; - pinfo = ce.pinfo; - } + ce = new UserInfoCacheEntry(); + ce.time = Util.EnvironmentTickCount(); + ce.account = account; + ce.pinfo = pinfo; - if (Util.EnvironmentTickCount() < ce.time || (Util.EnvironmentTickCount() - ce.time) >= 20000) - { - PresenceInfo[] pinfos = World.PresenceService.GetAgents(new string[] { uuid.ToString() }); - if (pinfos != null && pinfos.Length > 0) + m_userInfoCache[uuid] = ce; + } + else { - foreach (PresenceInfo p in pinfos) + if (ce == null) + return UUID.Zero.ToString(); + + account = ce.account; + + if (Util.EnvironmentTickCount() < ce.time || (Util.EnvironmentTickCount() - ce.time) + >= LlRequestAgentDataCacheTimeoutMs) { - if (p.RegionID != UUID.Zero) + PresenceInfo[] pinfos = World.PresenceService.GetAgents(new string[] { uuid.ToString() }); + if (pinfos != null && pinfos.Length > 0) + { + foreach (PresenceInfo p in pinfos) + { + if (p.RegionID != UUID.Zero) + { + pinfo = p; + } + } + } + else { - pinfo = p; + pinfo = null; } + + ce.time = Util.EnvironmentTickCount(); + ce.pinfo = pinfo; + } + else + { + pinfo = ce.pinfo; } } - else - pinfo = null; - - ce.time = Util.EnvironmentTickCount(); - ce.pinfo = pinfo; } string reply = String.Empty; switch (data) { - case 1: // DATA_ONLINE (0|1) + case ScriptBaseClass.DATA_ONLINE: if (pinfo != null && pinfo.RegionID != UUID.Zero) reply = "1"; else reply = "0"; break; - case 2: // DATA_NAME (First Last) + case ScriptBaseClass.DATA_NAME: // (First Last) reply = account.FirstName + " " + account.LastName; break; - case 3: // DATA_BORN (YYYY-MM-DD) + case ScriptBaseClass.DATA_BORN: // (YYYY-MM-DD) DateTime born = new DateTime(1970, 1, 1, 0, 0, 0, 0); born = born.AddSeconds(account.Created); reply = born.ToString("yyyy-MM-dd"); break; - case 4: // DATA_RATING (0,0,0,0,0,0) + case ScriptBaseClass.DATA_RATING: // (0,0,0,0,0,0) reply = "0,0,0,0,0,0"; break; - case 7: // DATA_USERLEVEL (integer) + case 7: // DATA_USERLEVEL (integer). This is not available in LL and so has no constant. reply = account.UserLevel.ToString(); break; - case 8: // DATA_PAYINFO (0|1|2|3) + case ScriptBaseClass.DATA_PAYINFO: // (0|1|2|3) reply = "0"; break; default: @@ -4150,7 +4533,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api AsyncCommands. DataserverPlugin.DataserverReply(rq.ToString(), reply); - ScriptSleep(100); + ScriptSleep(m_sleepMsOnRequestAgentData); return tid.ToString(); } @@ -4166,10 +4549,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api DataserverPlugin.RegisterRequest(m_host.LocalId, m_item.ItemID, item.AssetID.ToString()); - Vector3 region = new Vector3( - World.RegionInfo.RegionLocX * Constants.RegionSize, - World.RegionInfo.RegionLocY * Constants.RegionSize, - 0); + Vector3 region = new Vector3(World.RegionInfo.WorldLocX, World.RegionInfo.WorldLocY, 0); World.AssetService.Get(item.AssetID.ToString(), this, delegate(string i, object sender, AssetBase a) @@ -4186,12 +4566,12 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api reply); }); - ScriptSleep(1000); + ScriptSleep(m_sleepMsOnRequestInventoryData); return tid.ToString(); } } - ScriptSleep(1000); + ScriptSleep(m_sleepMsOnRequestInventoryData); return String.Empty; } @@ -4211,14 +4591,14 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api if (presence != null) { // agent must be over the owners land - if (m_host.OwnerID == World.LandChannel.GetLandObject( - presence.AbsolutePosition.X, presence.AbsolutePosition.Y).LandData.OwnerID) + if (m_host.OwnerID == World.LandChannel.GetLandObject(presence.AbsolutePosition).LandData.OwnerID) { World.TeleportClientHome(agentId, presence.ControllingClient); } } } - ScriptSleep(5000); + + ScriptSleep(m_sleepMsOnSetDamage); } public void llTeleportAgent(string agent, string destination, LSL_Vector targetPos, LSL_Vector targetLookAt) @@ -4238,8 +4618,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api destination = World.RegionInfo.RegionName; // agent must be over the owners land - if (m_host.OwnerID == World.LandChannel.GetLandObject( - presence.AbsolutePosition.X, presence.AbsolutePosition.Y).LandData.OwnerID) + if (m_host.OwnerID == World.LandChannel.GetLandObject(presence.AbsolutePosition).LandData.OwnerID) { DoLLTeleport(presence, destination, targetPos, targetLookAt); } @@ -4259,7 +4638,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api m_host.AddScriptLPS(1); UUID agentId = new UUID(); - ulong regionHandle = Utils.UIntsToLong((uint)global_coords.x, (uint)global_coords.y); + ulong regionHandle = Util.RegionWorldLocToHandle((uint)global_coords.x, (uint)global_coords.y); if (UUID.TryParse(agent, out agentId)) { @@ -4270,8 +4649,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api if (presence.GodLevel >= 200) return; // agent must be over the owners land - if (m_host.OwnerID == World.LandChannel.GetLandObject( - presence.AbsolutePosition.X, presence.AbsolutePosition.Y).LandData.OwnerID) + if (m_host.OwnerID == World.LandChannel.GetLandObject(presence.AbsolutePosition).LandData.OwnerID) { World.RequestTeleportLocation(presence.ControllingClient, regionHandle, targetPos, targetLookAt, (uint)TeleportFlags.ViaLocation); } @@ -4288,7 +4666,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api private void DoLLTeleport(ScenePresence sp, string destination, Vector3 targetPos, Vector3 targetLookAt) { - UUID assetID = KeyOrName(destination); + UUID assetID = ScriptUtils.GetAssetIdFromKeyOrItemName(m_host, destination); // The destinaion is not an asset ID and also doesn't name a landmark. // Use it as a sim name @@ -4321,22 +4699,22 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api UUID av = new UUID(); if (!UUID.TryParse(agent,out av)) { - LSLError("First parameter to llDialog needs to be a key"); + Error("llTextBox", "First parameter must be a key"); return; } if (message == string.Empty) { - ShoutError("Trying to use llTextBox with empty message."); + Error("llTextBox", "Empty message"); } else if (message.Length > 512) { - ShoutError("Trying to use llTextBox with message over 512 characters."); + Error("llTextBox", "Message more than 512 characters"); } else { dm.SendTextBoxToUser(av, message, chatChannel, m_host.Name, m_host.UUID, m_host.OwnerID); - ScriptSleep(1000); + ScriptSleep(m_sleepMsOnTextBox); } } @@ -4353,9 +4731,9 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api public void llCollisionSound(string impact_sound, double impact_volume) { m_host.AddScriptLPS(1); - + // TODO: Parameter check logic required. - m_host.CollisionSound = KeyOrName(impact_sound, AssetType.Sound); + m_host.CollisionSound = ScriptUtils.GetAssetIdFromKeyOrItemName(m_host, impact_sound, AssetType.Sound); m_host.CollisionSoundVolume = (float)impact_volume; } @@ -4475,7 +4853,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api { if (pushrestricted) { - ILandObject targetlandObj = World.LandChannel.GetLandObject(PusheePos.X, PusheePos.Y); + ILandObject targetlandObj = World.LandChannel.GetLandObject(PusheePos); // We didn't find the parcel but region is push restricted so assume it is NOT ok if (targetlandObj == null) @@ -4490,7 +4868,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api } else { - ILandObject targetlandObj = World.LandChannel.GetLandObject(PusheePos.X, PusheePos.Y); + ILandObject targetlandObj = World.LandChannel.GetLandObject(PusheePos); if (targetlandObj == null) { // We didn't find the parcel but region isn't push restricted so assume it's ok @@ -4520,6 +4898,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api } } } + if (pushAllowed) { float distance = (PusheePos - m_host.AbsolutePosition).Length(); @@ -4548,17 +4927,21 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api applied_linear_impulse *= scaling_factor; } + if (pusheeIsAvatar) { if (pusheeav != null) { - if (pusheeav.PhysicsActor != null) + PhysicsActor pa = pusheeav.PhysicsActor; + + if (pa != null) { if (local != 0) { applied_linear_impulse *= m_host.GetWorldRotation(); } - pusheeav.PhysicsActor.AddForce(applied_linear_impulse, true); + + pa.AddForce(applied_linear_impulse, true); } } } @@ -4666,6 +5049,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api s = Math.Cos(angle * 0.5); t = Math.Sin(angle * 0.5); // temp value to avoid 2 more sin() calcs + axis = LSL_Vector.Norm(axis); x = axis.x * t; y = axis.y * t; z = axis.z * t; @@ -4673,41 +5057,29 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api return new LSL_Rotation(x,y,z,s); } - - // Xantor 29/apr/2008 - // converts a Quaternion to X,Y,Z axis rotations + /// + /// Returns the axis of rotation for a quaternion + /// + /// + /// public LSL_Vector llRot2Axis(LSL_Rotation rot) { m_host.AddScriptLPS(1); - double x,y,z; - - if (rot.s > 1) // normalization needed - { - double length = Math.Sqrt(rot.x * rot.x + rot.y * rot.y + - rot.z * rot.z + rot.s * rot.s); - rot.x /= length; - rot.y /= length; - rot.z /= length; - rot.s /= length; + if (Math.Abs(rot.s) > 1) // normalization needed + rot.Normalize(); - } - - // double angle = 2 * Math.Acos(rot.s); double s = Math.Sqrt(1 - rot.s * rot.s); if (s < 0.001) { - x = 1; - y = z = 0; + return new LSL_Vector(1, 0, 0); } else { - x = rot.x / s; // normalise axis - y = rot.y / s; - z = rot.z / s; + double invS = 1.0 / s; + if (rot.s < 0) invS = -invS; + return new LSL_Vector(rot.x * invS, rot.y * invS, rot.z * invS); } - - return new LSL_Vector(x,y,z); } @@ -4716,18 +5088,12 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api { m_host.AddScriptLPS(1); - if (rot.s > 1) // normalization needed - { - double length = Math.Sqrt(rot.x * rot.x + rot.y * rot.y + - rot.z * rot.z + rot.s * rot.s); - - rot.x /= length; - rot.y /= length; - rot.z /= length; - rot.s /= length; - } + if (Math.Abs(rot.s) > 1) // normalization needed + rot.Normalize(); double angle = 2 * Math.Acos(rot.s); + if (angle > Math.PI) + angle = 2 * Math.PI - angle; return angle; } @@ -4907,8 +5273,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api public LSL_Vector llGetCenterOfMass() { m_host.AddScriptLPS(1); - Vector3 center = m_host.GetGeometricCenter(); - return new LSL_Vector(center.X,center.Y,center.Z); + + return new LSL_Vector(m_host.GetCenterOfMass()); } public LSL_List llListSort(LSL_List src, int stride, int ascending) @@ -5043,10 +5409,13 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api // SL spits out an empty string for types other than key & string // At the time of patching, LSL_Key is currently LSL_String, // so the OR check may be a little redundant, but it's being done - // for completion and should LSL_Key ever be implemented + // for completion and should LSL_Key ever be implemented // as it's own struct + // NOTE: 3rd case is needed because a NULL_KEY comes through as + // type 'obj' and wrongly returns "" else if (!(src.Data[index] is LSL_String || - src.Data[index] is LSL_Key)) + src.Data[index] is LSL_Key || + src.Data[index].ToString() == "00000000-0000-0000-0000-000000000000")) { return ""; } @@ -5179,8 +5548,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api { m_host.AddScriptLPS(1); - return string.Join(", ", - (new List(src.Data)).ConvertAll(o => + return string.Join(", ", + (new List(src.Data)).ConvertAll(o => { return o.ToString(); }).ToArray()); @@ -5254,7 +5623,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api public LSL_List llListRandomize(LSL_List src, int stride) { LSL_List result; - Random rand = new Random(); + BetterRandom rand = new BetterRandom(); int chunkk; int[] chunks; @@ -5270,24 +5639,25 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api // If not, then return the src list. This also // traps those cases where stride > length. - if (src.Length != stride && src.Length%stride == 0) + if (src.Length != stride && src.Length % stride == 0) { chunkk = src.Length/stride; chunks = new int[chunkk]; for (int i = 0; i < chunkk; i++) + { chunks[i] = i; + } // Knuth shuffle the chunkk index - for (int i = chunkk - 1; i >= 1; i--) + for (int i = chunkk - 1; i > 0; i--) { // Elect an unrandomized chunk to swap int index = rand.Next(i + 1); - int tmp; // and swap position with first unrandomized chunk - tmp = chunks[i]; + int tmp = chunks[i]; chunks[i] = chunks[index]; chunks[index] = tmp; } @@ -5300,7 +5670,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api { for (int j = 0; j < stride; j++) { - result.Add(src.Data[chunks[i]*stride+j]); + result.Add(src.Data[chunks[i] * stride + j]); } } } @@ -5417,7 +5787,72 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api public LSL_Vector llGetRegionCorner() { m_host.AddScriptLPS(1); - return new LSL_Vector(World.RegionInfo.RegionLocX * Constants.RegionSize, World.RegionInfo.RegionLocY * Constants.RegionSize, 0); + return new LSL_Vector(World.RegionInfo.WorldLocX, World.RegionInfo.WorldLocY, 0); + } + + public LSL_String llGetEnv(LSL_String name) + { + m_host.AddScriptLPS(1); + if (name == "agent_limit") + { + return World.RegionInfo.RegionSettings.AgentLimit.ToString(); + } + else if (name == "dynamic_pathfinding") + { + return "0"; + } + else if (name == "estate_id") + { + return World.RegionInfo.EstateSettings.EstateID.ToString(); + } + else if (name == "estate_name") + { + return World.RegionInfo.EstateSettings.EstateName; + } + else if (name == "frame_number") + { + return World.Frame.ToString(); + } + else if (name == "region_cpu_ratio") + { + return "1"; + } + else if (name == "region_idle") + { + return "0"; + } + else if (name == "region_product_name") + { + if (World.RegionInfo.RegionType != String.Empty) + return World.RegionInfo.RegionType; + else + return ""; + } + else if (name == "region_product_sku") + { + return "OpenSim"; + } + else if (name == "region_start_time") + { + return World.UnixStartTime.ToString(); + } + else if (name == "sim_channel") + { + return "OpenSim"; + } + else if (name == "sim_version") + { + return World.GetSimulatorVersion(); + } + else if (name == "simulator_hostname") + { + IUrlModule UrlModule = World.RequestModuleInterface(); + return UrlModule.ExternalHostNameForLSL; + } + else + { + return ""; + } } /// @@ -5429,8 +5864,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api public LSL_List llListInsertList(LSL_List dest, LSL_List src, int index) { - LSL_List pref = null; - LSL_List suff = null; + LSL_List pref; + LSL_List suff; m_host.AddScriptLPS(1); @@ -5564,7 +5999,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api LSL_Float mag; if (dir.x > 0) { - mag = (Constants.RegionSize - pos.x) / dir.x; + mag = (World.RegionInfo.RegionSizeX - pos.x) / dir.x; } else { @@ -5575,7 +6010,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api edge.y = pos.y + (dir.y * mag); - if (edge.y > Constants.RegionSize || edge.y < 0) + if (edge.y > World.RegionInfo.RegionSizeY || edge.y < 0) { // Y goes out of bounds first edge.y = dir.y / Math.Abs(dir.y); @@ -5704,12 +6139,21 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api public LSL_String llGetAgentLanguage(string id) { - // This should only return a value if the avatar is in the same region - //ckrinke 1-30-09 : This needs to parse the XMLRPC language field supplied - //by the client at login. Currently returning only en-us until our I18N - //effort gains momentum + // This should only return a value if the avatar is in the same region, but eh. idc. m_host.AddScriptLPS(1); - return "en-us"; + if (World.AgentPreferencesService == null) + { + Error("llGetAgentLanguage", "No AgentPreferencesService present"); + } + else + { + UUID key = new UUID(); + if (UUID.TryParse(id, out key)) + { + return new LSL_String(World.AgentPreferencesService.GetLang(key)); + } + } + return new LSL_String("en-us"); } /// /// http://wiki.secondlife.com/wiki/LlGetAgentList @@ -5739,12 +6183,11 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api } ILandObject land; - Vector3 pos; UUID id = UUID.Zero; + if (parcel || parcelOwned) { - pos = m_host.ParentGroup.RootPart.GetWorldPosition(); - land = World.LandChannel.GetLandObject(pos.X, pos.Y); + land = World.LandChannel.GetLandObject(m_host.ParentGroup.RootPart.GetWorldPosition()); if (land == null) { id = UUID.Zero; @@ -5770,8 +6213,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api { if (!regionWide) { - pos = ssp.AbsolutePosition; - land = World.LandChannel.GetLandObject(pos.X, pos.Y); + land = World.LandChannel.GetLandObject(ssp.AbsolutePosition); if (land != null) { if (parcelOwned && land.LandData.OwnerID == id || @@ -5800,7 +6242,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api { m_host.AddScriptLPS(1); m_host.AdjustSoundGain(volume); - ScriptSleep(100); + ScriptSleep(m_sleepMsOnAdjustSoundVolume); } public void llSetSoundRadius(double radius) @@ -5881,7 +6323,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api if (m_SoundModule != null) { m_SoundModule.TriggerSoundLimited(m_host.UUID, - KeyOrName(sound, AssetType.Sound), volume, + ScriptUtils.GetAssetIdFromKeyOrItemName(m_host, sound, AssetType.Sound), volume, bottom_south_west, top_north_east); } } @@ -5896,7 +6338,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api if (presence != null) { // agent must be over the owners land - ILandObject land = World.LandChannel.GetLandObject(presence.AbsolutePosition.X, presence.AbsolutePosition.Y); + ILandObject land = World.LandChannel.GetLandObject(presence.AbsolutePosition); if (land == null) return; @@ -5906,7 +6348,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api } } } - ScriptSleep(5000); + ScriptSleep(m_sleepMsOnEjectFromLand); } public LSL_Integer llOverMyLand(string id) @@ -5918,19 +6360,18 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api ScenePresence presence = World.GetScenePresence(key); if (presence != null) // object is an avatar { - if (m_host.OwnerID - == World.LandChannel.GetLandObject( - presence.AbsolutePosition.X, presence.AbsolutePosition.Y).LandData.OwnerID) + if (m_host.OwnerID == World.LandChannel.GetLandObject(presence.AbsolutePosition).LandData.OwnerID) return 1; } else // object is not an avatar { SceneObjectPart obj = World.GetSceneObjectPart(key); + if (obj != null) - if (m_host.OwnerID - == World.LandChannel.GetLandObject( - obj.AbsolutePosition.X, obj.AbsolutePosition.Y).LandData.OwnerID) + { + if (m_host.OwnerID == World.LandChannel.GetLandObject(obj.AbsolutePosition).LandData.OwnerID) return 1; + } } } @@ -5962,8 +6403,9 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api } else { - agentSize = new LSL_Vector(0.45, 0.6, avatar.Appearance.AvatarHeight); + agentSize = GetAgentSize(avatar); } + return agentSize; } @@ -5991,10 +6433,11 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api if (UUID.TryParse(id, out key)) { ScenePresence av = World.GetScenePresence(key); + List sittingAvatars = m_host.ParentGroup.GetSittingAvatars(); if (av != null) { - if (llAvatarOnSitTarget() == id) + if (sittingAvatars.Contains(av)) { // if the avatar is sitting on this object, then // we can unsit them. We don't want random scripts unsitting random people @@ -6008,8 +6451,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api // if the land is group owned and the object is group owned by the same group // or // if the object is owned by a person with estate access. - - ILandObject parcel = World.LandChannel.GetLandObject(av.AbsolutePosition.X, av.AbsolutePosition.Y); + ILandObject parcel = World.LandChannel.GetLandObject(av.AbsolutePosition); if (parcel != null) { if (m_host.OwnerID == parcel.LandData.OwnerID || @@ -6021,14 +6463,13 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api } } } - } - } public LSL_Vector llGroundSlope(LSL_Vector offset) { m_host.AddScriptLPS(1); + //Get the slope normal. This gives us the equation of the plane tangent to the slope. LSL_Vector vsn = llGroundNormal(offset); @@ -6039,7 +6480,11 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api vsl.Normalize(); //Normalization might be overkill here - return new LSL_Vector(vsl.X, vsl.Y, vsl.Z); + vsn.x = vsl.X; + vsn.y = vsl.Y; + vsn.z = vsl.Z; + + return vsn; } public LSL_Vector llGroundNormal(LSL_Vector offset) @@ -6089,7 +6534,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api //I believe the crossproduct of two normalized vectors is a normalized vector so //this normalization may be overkill - return new LSL_Vector(vsn.X, vsn.Y, vsn.Z); + return new LSL_Vector(vsn); } public LSL_Vector llGroundContour(LSL_Vector offset) @@ -6105,11 +6550,11 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api return m_host.ParentGroup.AttachmentPoint; } - public LSL_Integer llGetFreeMemory() + public virtual LSL_Integer llGetFreeMemory() { m_host.AddScriptLPS(1); - // Make scripts designed for LSO happy - return 16384; + // Make scripts designed for Mono happy + return 65536; } public LSL_Integer llGetFreeURLs() @@ -6176,7 +6621,11 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api PSYS_SRC_TARGET_KEY = 20, PSYS_SRC_OMEGA = 21, PSYS_SRC_ANGLE_BEGIN = 22, - PSYS_SRC_ANGLE_END = 23 + PSYS_SRC_ANGLE_END = 23, + PSYS_PART_BLEND_FUNC_SOURCE = 24, + PSYS_PART_BLEND_FUNC_DEST = 25, + PSYS_PART_START_GLOW = 26, + PSYS_PART_END_GLOW = 27 } internal Primitive.ParticleSystem.ParticleDataFlags ConvertUINTtoFlags(uint flags) @@ -6202,6 +6651,11 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api ps.BurstRate = 0.1f; ps.PartMaxAge = 10.0f; ps.BurstPartCount = 1; + ps.BlendFuncSource = ScriptBaseClass.PSYS_PART_BF_SOURCE_ALPHA; + ps.BlendFuncDest = ScriptBaseClass.PSYS_PART_BF_ONE_MINUS_SOURCE_ALPHA; + ps.PartStartGlow = 0.0f; + ps.PartEndGlow = 0.0f; + return ps; } @@ -6213,17 +6667,17 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api foreach (SceneObjectPart part in parts) { - SetParticleSystem(part, rules); + SetParticleSystem(part, rules, "llLinkParticleSystem"); } } public void llParticleSystem(LSL_List rules) { m_host.AddScriptLPS(1); - SetParticleSystem(m_host, rules); + SetParticleSystem(m_host, rules, "llParticleSystem"); } - private void SetParticleSystem(SceneObjectPart part, LSL_List rules) + private void SetParticleSystem(SceneObjectPart part, LSL_List rules, string originFunc) { if (rules.Length == 0) { @@ -6236,65 +6690,156 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api LSL_Vector tempv = new LSL_Vector(); float tempf = 0; + int tmpi = 0; for (int i = 0; i < rules.Length; i += 2) { - switch (rules.GetLSLIntegerItem(i)) + int psystype; + try + { + psystype = rules.GetLSLIntegerItem(i); + } + catch (InvalidCastException) + { + Error(originFunc, string.Format("Error running particle system params index #{0}: particle system parameter type must be integer", i)); + return; + } + switch (psystype) { case (int)ScriptBaseClass.PSYS_PART_FLAGS: - prules.PartDataFlags = (Primitive.ParticleSystem.ParticleDataFlags)(uint)rules.GetLSLIntegerItem(i + 1); + try + { + prules.PartDataFlags = (Primitive.ParticleSystem.ParticleDataFlags)(uint)rules.GetLSLIntegerItem(i + 1); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule PSYS_PART_FLAGS: arg #{0} - parameter 1 must be integer", i + 1)); + return; + } break; case (int)ScriptBaseClass.PSYS_PART_START_COLOR: - tempv = rules.GetVector3Item(i + 1); + try + { + tempv = rules.GetVector3Item(i + 1); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule PSYS_PART_START_COLOR: arg #{0} - parameter 1 must be vector", i + 1)); + return; + } prules.PartStartColor.R = (float)tempv.x; prules.PartStartColor.G = (float)tempv.y; prules.PartStartColor.B = (float)tempv.z; break; case (int)ScriptBaseClass.PSYS_PART_START_ALPHA: - tempf = (float)rules.GetLSLFloatItem(i + 1); + try + { + tempf = (float)rules.GetLSLFloatItem(i + 1); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule PSYS_PART_START_ALPHA: arg #{0} - parameter 1 must be float", i + 1)); + return; + } prules.PartStartColor.A = tempf; break; case (int)ScriptBaseClass.PSYS_PART_END_COLOR: - tempv = rules.GetVector3Item(i + 1); + try + { + tempv = rules.GetVector3Item(i + 1); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule PSYS_PART_END_COLOR: arg #{0} - parameter 1 must be vector", i + 1)); + return; + } prules.PartEndColor.R = (float)tempv.x; prules.PartEndColor.G = (float)tempv.y; prules.PartEndColor.B = (float)tempv.z; break; case (int)ScriptBaseClass.PSYS_PART_END_ALPHA: - tempf = (float)rules.GetLSLFloatItem(i + 1); + try + { + tempf = (float)rules.GetLSLFloatItem(i + 1); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule PSYS_PART_END_ALPHA: arg #{0} - parameter 1 must be float", i + 1)); + return; + } prules.PartEndColor.A = tempf; break; case (int)ScriptBaseClass.PSYS_PART_START_SCALE: - tempv = rules.GetVector3Item(i + 1); - prules.PartStartScaleX = (float)tempv.x; - prules.PartStartScaleY = (float)tempv.y; + try + { + tempv = rules.GetVector3Item(i + 1); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule PSYS_PART_START_SCALE: arg #{0} - parameter 1 must be vector", i + 1)); + return; + } + prules.PartStartScaleX = validParticleScale((float)tempv.x); + prules.PartStartScaleY = validParticleScale((float)tempv.y); break; case (int)ScriptBaseClass.PSYS_PART_END_SCALE: - tempv = rules.GetVector3Item(i + 1); - prules.PartEndScaleX = (float)tempv.x; - prules.PartEndScaleY = (float)tempv.y; + try + { + tempv = rules.GetVector3Item(i + 1); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule PSYS_PART_END_SCALE: arg #{0} - parameter 1 must be vector", i + 1)); + return; + } + prules.PartEndScaleX = validParticleScale((float)tempv.x); + prules.PartEndScaleY = validParticleScale((float)tempv.y); break; case (int)ScriptBaseClass.PSYS_PART_MAX_AGE: - tempf = (float)rules.GetLSLFloatItem(i + 1); + try + { + tempf = (float)rules.GetLSLFloatItem(i + 1); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule PSYS_PART_MAX_AGE: arg #{0} - parameter 1 must be float", i + 1)); + return; + } prules.PartMaxAge = tempf; break; case (int)ScriptBaseClass.PSYS_SRC_ACCEL: - tempv = rules.GetVector3Item(i + 1); + try + { + tempv = rules.GetVector3Item(i + 1); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule PSYS_SRC_ACCEL: arg #{0} - parameter 1 must be vector", i + 1)); + return; + } prules.PartAcceleration.X = (float)tempv.x; prules.PartAcceleration.Y = (float)tempv.y; prules.PartAcceleration.Z = (float)tempv.z; break; case (int)ScriptBaseClass.PSYS_SRC_PATTERN: - int tmpi = (int)rules.GetLSLIntegerItem(i + 1); + try + { + tmpi = (int)rules.GetLSLIntegerItem(i + 1); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule PSYS_SRC_PATTERN: arg #{0} - parameter 1 must be integer", i + 1)); + return; + } prules.Pattern = (Primitive.ParticleSystem.SourcePattern)tmpi; break; @@ -6303,47 +6848,171 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api // client tells the difference between the two by looking at the 0x02 bit in // the PartFlags variable. case (int)ScriptBaseClass.PSYS_SRC_INNERANGLE: - tempf = (float)rules.GetLSLFloatItem(i + 1); + try + { + tempf = (float)rules.GetLSLFloatItem(i + 1); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule PSYS_SRC_INNERANGLE: arg #{0} - parameter 1 must be float", i + 1)); + return; + } prules.InnerAngle = (float)tempf; prules.PartFlags &= 0xFFFFFFFD; // Make sure new angle format is off. break; case (int)ScriptBaseClass.PSYS_SRC_OUTERANGLE: - tempf = (float)rules.GetLSLFloatItem(i + 1); - prules.OuterAngle = (float)tempf; - prules.PartFlags &= 0xFFFFFFFD; // Make sure new angle format is off. - break; + try + { + tempf = (float)rules.GetLSLFloatItem(i + 1); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule PSYS_SRC_OUTERANGLE: arg #{0} - parameter 1 must be float", i + 1)); + return; + } + prules.OuterAngle = (float)tempf; + prules.PartFlags &= 0xFFFFFFFD; // Make sure new angle format is off. + break; + + case (int)ScriptBaseClass.PSYS_PART_BLEND_FUNC_SOURCE: + try + { + tmpi = (int)rules.GetLSLIntegerItem(i + 1); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule PSYS_PART_BLEND_FUNC_SOURCE: arg #{0} - parameter 1 must be integer", i + 1)); + return; + } + prules.BlendFuncSource = (byte)tmpi; + break; + + case (int)ScriptBaseClass.PSYS_PART_BLEND_FUNC_DEST: + try + { + tmpi = (int)rules.GetLSLIntegerItem(i + 1); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule PSYS_PART_BLEND_FUNC_DEST: arg #{0} - parameter 1 must be integer", i + 1)); + return; + } + prules.BlendFuncDest = (byte)tmpi; + break; + + case (int)ScriptBaseClass.PSYS_PART_START_GLOW: + try + { + tempf = (float)rules.GetLSLFloatItem(i + 1); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule PSYS_PART_START_GLOW: arg #{0} - parameter 1 must be float", i + 1)); + return; + } + prules.PartStartGlow = (float)tempf; + break; + + case (int)ScriptBaseClass.PSYS_PART_END_GLOW: + try + { + tempf = (float)rules.GetLSLFloatItem(i + 1); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule PSYS_PART_END_GLOW: arg #{0} - parameter 1 must be float", i + 1)); + return; + } + prules.PartEndGlow = (float)tempf; + break; case (int)ScriptBaseClass.PSYS_SRC_TEXTURE: - prules.Texture = KeyOrName(rules.GetLSLStringItem(i + 1)); + try + { + prules.Texture = ScriptUtils.GetAssetIdFromKeyOrItemName(m_host, rules.GetLSLStringItem(i + 1)); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule PSYS_SRC_TEXTURE: arg #{0} - parameter 1 must be string or key", i + 1)); + return; + } break; case (int)ScriptBaseClass.PSYS_SRC_BURST_RATE: - tempf = (float)rules.GetLSLFloatItem(i + 1); + try + { + tempf = (float)rules.GetLSLFloatItem(i + 1); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule PSYS_SRC_BURST_RATE: arg #{0} - parameter 1 must be float", i + 1)); + return; + } prules.BurstRate = (float)tempf; break; case (int)ScriptBaseClass.PSYS_SRC_BURST_PART_COUNT: - prules.BurstPartCount = (byte)(int)rules.GetLSLIntegerItem(i + 1); + try + { + prules.BurstPartCount = (byte)(int)rules.GetLSLIntegerItem(i + 1); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule PSYS_SRC_BURST_PART_COUNT: arg #{0} - parameter 1 must be integer", i + 1)); + return; + } break; case (int)ScriptBaseClass.PSYS_SRC_BURST_RADIUS: - tempf = (float)rules.GetLSLFloatItem(i + 1); + try + { + tempf = (float)rules.GetLSLFloatItem(i + 1); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule PSYS_SRC_BURST_RADIUS: arg #{0} - parameter 1 must be float", i + 1)); + return; + } prules.BurstRadius = (float)tempf; break; case (int)ScriptBaseClass.PSYS_SRC_BURST_SPEED_MIN: - tempf = (float)rules.GetLSLFloatItem(i + 1); + try + { + tempf = (float)rules.GetLSLFloatItem(i + 1); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule PSYS_SRC_BURST_SPEED_MIN: arg #{0} - parameter 1 must be float", i + 1)); + return; + } prules.BurstSpeedMin = (float)tempf; break; case (int)ScriptBaseClass.PSYS_SRC_BURST_SPEED_MAX: - tempf = (float)rules.GetLSLFloatItem(i + 1); + try + { + tempf = (float)rules.GetLSLFloatItem(i + 1); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule PSYS_SRC_BURST_SPEED_MAX: arg #{0} - parameter 1 must be float", i + 1)); + return; + } prules.BurstSpeedMax = (float)tempf; break; case (int)ScriptBaseClass.PSYS_SRC_MAX_AGE: - tempf = (float)rules.GetLSLFloatItem(i + 1); + try + { + tempf = (float)rules.GetLSLFloatItem(i + 1); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule PSYS_SRC_MAX_AGE: arg #{0} - parameter 1 must be float", i + 1)); + return; + } prules.MaxAge = (float)tempf; break; @@ -6361,20 +7030,44 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api case (int)ScriptBaseClass.PSYS_SRC_OMEGA: // AL: This is an assumption, since it is the only thing that would match. - tempv = rules.GetVector3Item(i + 1); + try + { + tempv = rules.GetVector3Item(i + 1); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule PSYS_SRC_OMEGA: arg #{0} - parameter 1 must be vector", i + 1)); + return; + } prules.AngularVelocity.X = (float)tempv.x; prules.AngularVelocity.Y = (float)tempv.y; prules.AngularVelocity.Z = (float)tempv.z; break; case (int)ScriptBaseClass.PSYS_SRC_ANGLE_BEGIN: - tempf = (float)rules.GetLSLFloatItem(i + 1); + try + { + tempf = (float)rules.GetLSLFloatItem(i + 1); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule PSYS_SRC_ANGLE_BEGIN: arg #{0} - parameter 1 must be float", i + 1)); + return; + } prules.InnerAngle = (float)tempf; prules.PartFlags |= 0x02; // Set new angle format. break; case (int)ScriptBaseClass.PSYS_SRC_ANGLE_END: - tempf = (float)rules.GetLSLFloatItem(i + 1); + try + { + tempf = (float)rules.GetLSLFloatItem(i + 1); + } + catch (InvalidCastException) + { + Error(originFunc, string.Format("Error running rule PSYS_SRC_ANGLE_END: arg #{0} - parameter 1 must be float", i + 1)); + return; + } prules.OuterAngle = (float)tempf; prules.PartFlags |= 0x02; // Set new angle format. break; @@ -6389,6 +7082,12 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api part.SendFullUpdateToAllClients(); } + private float validParticleScale(float value) + { + if (value > 4.0f) return 4.0f; + return value; + } + public void llGroundRepel(double height, int water, double tau) { m_host.AddScriptLPS(1); @@ -6460,7 +7159,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api m_host.OwnerID, m_host.Name, destID, (byte)InstantMessageDialog.TaskInventoryOffered, false, string.Format("'{0}'", category), -// We won't go so far as to add a SLURL, but this is the format used by LL as of 2012-10-06 +// We won't go so far as to add a SLURL, but this is the format used by LL as of 2012-10-06 // false, string.Format("'{0}' ( http://slurl.com/secondlife/{1}/{2}/{3}/{4} )", category, World.Name, (int)pos.X, (int)pos.Y, (int)pos.Z), folderID, false, pos, bucket, false); @@ -6575,12 +7274,12 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api public LSL_String llAvatarOnLinkSitTarget(int linknum) { m_host.AddScriptLPS(1); - if(linknum == ScriptBaseClass.LINK_SET || + if(linknum == ScriptBaseClass.LINK_SET || linknum == ScriptBaseClass.LINK_ALL_CHILDREN || linknum == ScriptBaseClass.LINK_ALL_OTHERS) return UUID.Zero.ToString(); - + List parts = GetLinkParts(linknum); - if (parts.Count == 0) return UUID.Zero.ToString(); + if (parts.Count == 0) return UUID.Zero.ToString(); return parts[0].SitTargetAvatar.ToString(); } @@ -6589,7 +7288,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api { m_host.AddScriptLPS(1); UUID key; - ILandObject land = World.LandChannel.GetLandObject(m_host.AbsolutePosition.X, m_host.AbsolutePosition.Y); + ILandObject land = World.LandChannel.GetLandObject(m_host.AbsolutePosition); + if (World.Permissions.CanEditParcelProperties(m_host.OwnerID, land, GroupPowers.LandManageBanned)) { int expires = 0; @@ -6623,7 +7323,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api World.EventManager.TriggerLandObjectUpdated((uint)land.LandData.LocalID, land); } } - ScriptSleep(100); + ScriptSleep(m_sleepMsOnAddToLandPassList); } public void llSetTouchText(string text) @@ -6642,12 +7342,18 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api { m_host.AddScriptLPS(1); m_host.SetCameraEyeOffset(offset); + + if (m_host.ParentGroup.RootPart.GetCameraEyeOffset() == Vector3.Zero) + m_host.ParentGroup.RootPart.SetCameraEyeOffset(offset); } public void llSetCameraAtOffset(LSL_Vector offset) { m_host.AddScriptLPS(1); m_host.SetCameraAtOffset(offset); + + if (m_host.ParentGroup.RootPart.GetCameraAtOffset() == Vector3.Zero) + m_host.ParentGroup.RootPart.SetCameraAtOffset(offset); } public void llSetLinkCamera(LSL_Integer link, LSL_Vector eye, LSL_Vector at) @@ -6722,17 +7428,17 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api UUID av = new UUID(); if (!UUID.TryParse(avatar,out av)) { - LSLError("First parameter to llDialog needs to be a key"); + Error("llDialog", "First parameter must be a key"); return; } if (buttons.Length < 1) { - LSLError("No less than 1 button can be shown"); + Error("llDialog", "At least 1 button must be shown"); return; } if (buttons.Length > 12) { - LSLError("No more than 12 buttons can be shown"); + Error("llDialog", "No more than 12 buttons can be shown"); return; } string[] buts = new string[buttons.Length]; @@ -6740,12 +7446,12 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api { if (buttons.Data[i].ToString() == String.Empty) { - LSLError("button label cannot be blank"); + Error("llDialog", "Button label cannot be blank"); return; } if (buttons.Data[i].ToString().Length > 24) { - LSLError("button label cannot be longer than 24 characters"); + Error("llDialog", "Button label cannot be longer than 24 characters"); return; } buts[i] = buttons.Data[i].ToString(); @@ -6755,7 +7461,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api av, m_host.Name, m_host.UUID, m_host.OwnerID, message, new UUID("00000000-0000-2222-3333-100000001000"), chat_channel, buts); - ScriptSleep(1000); + ScriptSleep(m_sleepMsOnDialog); } public void llVolumeDetect(int detect) @@ -6766,16 +7472,11 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api m_host.ParentGroup.ScriptSetVolumeDetect(detect != 0); } - /// - /// This is a depecated function so this just replicates the result of - /// invoking it in SL - /// public void llRemoteLoadScript(string target, string name, int running, int start_param) { m_host.AddScriptLPS(1); - // Report an error as it does in SL - ShoutError("Deprecated. Please use llRemoteLoadScriptPin instead."); - ScriptSleep(3000); + Deprecated("llRemoteLoadScript", "Use llRemoteLoadScriptPin instead"); + ScriptSleep(m_sleepMsOnRemoteLoadScript); } public void llSetRemoteScriptAccessPin(int pin) @@ -6792,7 +7493,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api if (!UUID.TryParse(target, out destId)) { - llSay(0, "Could not parse key " + target); + Error("llRemoteLoadScriptPin", "Can't parse key '" + target + "'"); return; } @@ -6808,7 +7509,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api // make sure the object is a script if (item == null || item.Type != 10) { - llSay(0, "Could not find script " + name); + Error("llRemoteLoadScriptPin", "Can't find script '" + name + "'"); return; } @@ -6816,14 +7517,14 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api World.RezScriptFromPrim(item.ItemID, m_host, destId, pin, running, start_param); // this will cause the delay even if the script pin or permissions were wrong - seems ok - ScriptSleep(3000); + ScriptSleep(m_sleepMsOnRemoteLoadScriptPin); } public void llOpenRemoteDataChannel() { m_host.AddScriptLPS(1); IXMLRPC xmlrpcMod = m_ScriptEngine.World.RequestModuleInterface(); - if (xmlrpcMod.IsEnabled()) + if (xmlrpcMod != null && xmlrpcMod.IsEnabled()) { UUID channelID = xmlrpcMod.OpenXMLRPCChannel(m_host.LocalId, m_item.ItemID, UUID.Zero); IXmlRpcRouter xmlRpcRouter = m_ScriptEngine.World.RequestModuleInterface(); @@ -6847,14 +7548,16 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api m_ScriptEngine.PostScriptEvent(m_item.ItemID, new EventParams("remote_data", resobj, new DetectParams[0])); } - ScriptSleep(1000); + ScriptSleep(m_sleepMsOnOpenRemoteDataChannel); } public LSL_String llSendRemoteData(string channel, string dest, int idata, string sdata) { m_host.AddScriptLPS(1); IXMLRPC xmlrpcMod = m_ScriptEngine.World.RequestModuleInterface(); - ScriptSleep(3000); + ScriptSleep(m_sleepMsOnSendRemoteData); + if (xmlrpcMod == null) + return ""; return (xmlrpcMod.SendRemoteData(m_host.LocalId, m_item.ItemID, channel, dest, idata, sdata)).ToString(); } @@ -6862,8 +7565,9 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api { m_host.AddScriptLPS(1); IXMLRPC xmlrpcMod = m_ScriptEngine.World.RequestModuleInterface(); - xmlrpcMod.RemoteDataReply(channel, message_id, sdata, idata); - ScriptSleep(3000); + if (xmlrpcMod != null) + xmlrpcMod.RemoteDataReply(channel, message_id, sdata, idata); + ScriptSleep(m_sleepMsOnRemoteDataReply); } public void llCloseRemoteDataChannel(string channel) @@ -6877,8 +7581,9 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api } IXMLRPC xmlrpcMod = m_ScriptEngine.World.RequestModuleInterface(); - xmlrpcMod.CloseXMLRPCChannel((UUID)channel); - ScriptSleep(1000); + if (xmlrpcMod != null) + xmlrpcMod.CloseXMLRPCChannel((UUID)channel); + ScriptSleep(m_sleepMsOnCloseRemoteDataChannel); } public LSL_String llMD5String(string src, int nonce) @@ -6924,13 +7629,13 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api { cut.y = 1f; } - if (cut.y - cut.x < 0.05f) + if (cut.y - cut.x < 0.02f) { - cut.x = cut.y - 0.05f; + cut.x = cut.y - 0.02f; if (cut.x < 0.0f) { cut.x = 0.0f; - cut.y = 0.05f; + cut.y = 0.02f; } } shapeBlock.ProfileBegin = (ushort)(50000 * cut.x); @@ -6952,12 +7657,12 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api hollow = 0.70f; } } - // Otherwise, hollow is limited to 95%. + // Otherwise, hollow is limited to 99%. else { - if (hollow > 0.95f) + if (hollow > 0.99f) { - hollow = 0.95f; + hollow = 0.99f; } } shapeBlock.ProfileHollow = (ushort)(50000 * hollow); @@ -7081,9 +7786,14 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api { dimple.y = 1f; } - if (dimple.y - cut.x < 0.05f) + if (dimple.y - dimple.x < 0.02f) { - dimple.x = cut.y - 0.05f; + dimple.x = dimple.y - 0.02f; + if (dimple.x < 0.0f) + { + dimple.x = 0.0f; + dimple.y = 0.02f; + } } shapeBlock.ProfileBegin = (ushort)(50000 * dimple.x); shapeBlock.ProfileEnd = (ushort)(50000 * (1 - dimple.y)); @@ -7104,17 +7814,17 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api shapeBlock.PathBegin = shapeBlock.ProfileBegin; shapeBlock.PathEnd = shapeBlock.ProfileEnd; - if (holesize.x < 0.05f) + if (holesize.x < 0.01f) { - holesize.x = 0.05f; + holesize.x = 0.01f; } if (holesize.x > 1f) { holesize.x = 1f; } - if (holesize.y < 0.05f) + if (holesize.y < 0.01f) { - holesize.y = 0.05f; + holesize.y = 0.01f; } if (holesize.y > 0.5f) { @@ -7160,13 +7870,13 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api { profilecut.y = 1f; } - if (profilecut.y - profilecut.x < 0.05f) + if (profilecut.y - profilecut.x < 0.02f) { - profilecut.x = profilecut.y - 0.05f; + profilecut.x = profilecut.y - 0.02f; if (profilecut.x < 0.0f) { profilecut.x = 0.0f; - profilecut.y = 0.05f; + profilecut.y = 0.02f; } } shapeBlock.ProfileBegin = (ushort)(50000 * profilecut.x); @@ -7234,9 +7944,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api UUID sculptId; if (!UUID.TryParse(map, out sculptId)) - { - sculptId = InventoryKey(map, (int)AssetType.Texture); - } + sculptId = ScriptUtils.GetAssetIdFromItemName(m_host, map, (int)AssetType.Texture); if (sculptId == UUID.Zero) return; @@ -7266,45 +7974,205 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api { m_host.AddScriptLPS(1); - setLinkPrimParams(ScriptBaseClass.LINK_THIS, rules, "llSetPrimitiveParams"); + SetLinkPrimParams(ScriptBaseClass.LINK_THIS, rules, "llSetPrimitiveParams"); - ScriptSleep(200); + ScriptSleep(m_sleepMsOnSetPrimitiveParams); } public void llSetLinkPrimitiveParams(int linknumber, LSL_List rules) { m_host.AddScriptLPS(1); - setLinkPrimParams(linknumber, rules, "llSetLinkPrimitiveParams"); + SetLinkPrimParams(linknumber, rules, "llSetLinkPrimitiveParams"); - ScriptSleep(200); + ScriptSleep(m_sleepMsOnSetLinkPrimitiveParams); } public void llSetLinkPrimitiveParamsFast(int linknumber, LSL_List rules) { m_host.AddScriptLPS(1); - setLinkPrimParams(linknumber, rules, "llSetLinkPrimitiveParamsFast"); + SetLinkPrimParams(linknumber, rules, "llSetLinkPrimitiveParamsFast"); } - protected void setLinkPrimParams(int linknumber, LSL_List rules, string originFunc) + protected void SetLinkPrimParams(int linknumber, LSL_List rules, string originFunc) { - List parts = GetLinkParts(linknumber); + SetEntityParams(GetLinkEntities(linknumber), rules, originFunc); + } - LSL_List remaining = null; + protected void SetEntityParams(List entities, LSL_List rules, string originFunc) + { + LSL_List remaining = new LSL_List(); uint rulesParsed = 0; - foreach (SceneObjectPart part in parts) - remaining = SetPrimParams(part, rules, originFunc, ref rulesParsed); + foreach (ISceneEntity entity in entities) + { + if (entity is SceneObjectPart) + remaining = SetPrimParams((SceneObjectPart)entity, rules, originFunc, ref rulesParsed); + else + remaining = SetAgentParams((ScenePresence)entity, rules, originFunc, ref rulesParsed); + } - while (remaining != null && remaining.Length > 2) + while (remaining.Length > 2) { - linknumber = remaining.GetLSLIntegerItem(0); + int linknumber; + try + { + linknumber = remaining.GetLSLIntegerItem(0); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_LINK_TARGET: parameter 2 must be integer", rulesParsed)); + return; + } + rules = remaining.GetSublist(1, -1); - parts = GetLinkParts(linknumber); + entities = GetLinkEntities(linknumber); - foreach (SceneObjectPart part in parts) - remaining = SetPrimParams(part, rules, originFunc, ref rulesParsed); + foreach (ISceneEntity entity in entities) + { + if (entity is SceneObjectPart) + remaining = SetPrimParams((SceneObjectPart)entity, rules, originFunc, ref rulesParsed); + else + remaining = SetAgentParams((ScenePresence)entity, rules, originFunc, ref rulesParsed); + } + } + } + + 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 idx = 0; + + while (idx < options.Data.Length) + { + int option = (int)options.GetLSLIntegerItem(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; + } + } } } @@ -7331,29 +8199,48 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api switch (code) { - case (int)ScriptBaseClass.PRIM_POSITION: - case (int)ScriptBaseClass.PRIM_POS_LOCAL: + case ScriptBaseClass.PRIM_POSITION: + case ScriptBaseClass.PRIM_POS_LOCAL: if (remain < 1) - return null; + return new LSL_List(); - v=rules.GetVector3Item(idx++); + try + { + v = rules.GetVector3Item(idx++); + } + catch(InvalidCastException) + { + if(code == ScriptBaseClass.PRIM_POSITION) + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_POSITION: arg #{1} - parameter 1 must be vector", rulesParsed, idx - idxStart - 1)); + else + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_POS_LOCAL: arg #{1} - parameter 1 must be vector", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } positionChanged = true; currentPosition = GetSetPosTarget(part, v, currentPosition); break; - case (int)ScriptBaseClass.PRIM_SIZE: + case ScriptBaseClass.PRIM_SIZE: if (remain < 1) - return null; + return new LSL_List(); v=rules.GetVector3Item(idx++); SetScale(part, v); break; - case (int)ScriptBaseClass.PRIM_ROTATION: + case ScriptBaseClass.PRIM_ROTATION: if (remain < 1) - return null; - - LSL_Rotation q = rules.GetQuaternionItem(idx++); + return new LSL_List(); + LSL_Rotation q; + try + { + q = rules.GetQuaternionItem(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_ROTATION: arg #{1} - parameter 1 must be rotation", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } // try to let this work as in SL... if (part.ParentID == 0) { @@ -7369,11 +8256,19 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api break; - case (int)ScriptBaseClass.PRIM_TYPE: + case ScriptBaseClass.PRIM_TYPE: if (remain < 3) - return null; + return new LSL_List(); - code = (int)rules.GetLSLIntegerItem(idx++); + try + { + code = (int)rules.GetLSLIntegerItem(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_TYPE: arg #{1} - parameter 1 must be integer", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } remain = rules.Length - idx; float hollow; @@ -7388,140 +8283,625 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api switch (code) { - case (int)ScriptBaseClass.PRIM_TYPE_BOX: + case ScriptBaseClass.PRIM_TYPE_BOX: if (remain < 6) - return null; - - face = (int)rules.GetLSLIntegerItem(idx++); - v = rules.GetVector3Item(idx++); // cut - hollow = (float)rules.GetLSLFloatItem(idx++); - twist = rules.GetVector3Item(idx++); - taper_b = rules.GetVector3Item(idx++); - topshear = rules.GetVector3Item(idx++); + return new LSL_List(); + + try + { + face = (int)rules.GetLSLIntegerItem(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_TYPE, PRIM_TYPE_BOX: arg #{1} - parameter 2 must be integer", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + v = rules.GetVector3Item(idx++); // cut + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_TYPE, PRIM_TYPE_BOX: arg #{1} - parameter 3 must be vector", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + hollow = (float)rules.GetLSLFloatItem(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_TYPE, PRIM_TYPE_BOX: arg #{1} - parameter 4 must be float", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + twist = rules.GetVector3Item(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_TYPE, PRIM_TYPE_BOX: arg #{1} - parameter 5 must be vector", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + taper_b = rules.GetVector3Item(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_TYPE, PRIM_TYPE_BOX: arg #{1} - parameter 6 must be vector", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + topshear = rules.GetVector3Item(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_TYPE, PRIM_TYPE_BOX: arg #{1} - parameter 7 must be vector", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } SetPrimitiveShapeParams(part, face, v, hollow, twist, taper_b, topshear, (byte)ProfileShape.Square, (byte)Extrusion.Straight); break; - case (int)ScriptBaseClass.PRIM_TYPE_CYLINDER: + case ScriptBaseClass.PRIM_TYPE_CYLINDER: if (remain < 6) - return null; - - face = (int)rules.GetLSLIntegerItem(idx++); // holeshape - v = rules.GetVector3Item(idx++); // cut - hollow = (float)rules.GetLSLFloatItem(idx++); - twist = rules.GetVector3Item(idx++); - taper_b = rules.GetVector3Item(idx++); - topshear = rules.GetVector3Item(idx++); + return new LSL_List(); + + try + { + face = (int)rules.GetLSLIntegerItem(idx++); // holeshape + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_TYPE, PRIM_TYPE_CYLINDER: arg #{1} - parameter 3 must be integer", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + v = rules.GetVector3Item(idx++); // cut + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_TYPE, PRIM_TYPE_CYLINDER: arg #{1} - parameter 4 must be vector", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + hollow = (float)rules.GetLSLFloatItem(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_TYPE, PRIM_TYPE_CYLINDER: arg #{1} - parameter 5 must be float", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + twist = rules.GetVector3Item(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_TYPE, PRIM_TYPE_CYLINDER: arg #{1} - parameter 6 must be vector", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + taper_b = rules.GetVector3Item(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_TYPE, PRIM_TYPE_CYLINDER: arg #{1} - parameter 7 must be vector", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + topshear = rules.GetVector3Item(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_TYPE, PRIM_TYPE_CYLINDER: arg #{1} - parameter 8 must be vector", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } SetPrimitiveShapeParams(part, face, v, hollow, twist, taper_b, topshear, (byte)ProfileShape.Circle, (byte)Extrusion.Straight); break; - case (int)ScriptBaseClass.PRIM_TYPE_PRISM: + case ScriptBaseClass.PRIM_TYPE_PRISM: if (remain < 6) - return null; - - face = (int)rules.GetLSLIntegerItem(idx++); // holeshape - v = rules.GetVector3Item(idx++); //cut - hollow = (float)rules.GetLSLFloatItem(idx++); - twist = rules.GetVector3Item(idx++); - taper_b = rules.GetVector3Item(idx++); - topshear = rules.GetVector3Item(idx++); + return new LSL_List(); + + try + { + face = (int)rules.GetLSLIntegerItem(idx++); // holeshape + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_TYPE, PRIM_TYPE_PRISM: arg #{1} - parameter 3 must be integer", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + v = rules.GetVector3Item(idx++); //cut + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_TYPE, PRIM_TYPE_PRISM: arg #{1} - parameter 4 must be vector", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + hollow = (float)rules.GetLSLFloatItem(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_TYPE, PRIM_TYPE_PRISM: arg #{1} - parameter 5 must be float", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + twist = rules.GetVector3Item(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_TYPE, PRIM_TYPE_PRISM: arg #{1} - parameter 6 must be vector", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + taper_b = rules.GetVector3Item(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_TYPE, PRIM_TYPE_PRISM: arg #{1} - parameter 7 must be vector", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + topshear = rules.GetVector3Item(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_TYPE, PRIM_TYPE_PRISM: arg #{1} - parameter 8 must be vector", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } SetPrimitiveShapeParams(part, face, v, hollow, twist, taper_b, topshear, (byte)ProfileShape.EquilateralTriangle, (byte)Extrusion.Straight); break; - case (int)ScriptBaseClass.PRIM_TYPE_SPHERE: + case ScriptBaseClass.PRIM_TYPE_SPHERE: if (remain < 5) - return null; - - face = (int)rules.GetLSLIntegerItem(idx++); // holeshape - v = rules.GetVector3Item(idx++); // cut - hollow = (float)rules.GetLSLFloatItem(idx++); - twist = rules.GetVector3Item(idx++); - taper_b = rules.GetVector3Item(idx++); // dimple + return new LSL_List(); + + try + { + face = (int)rules.GetLSLIntegerItem(idx++); // holeshape + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_TYPE, PRIM_TYPE_SPHERE: arg #{1} - parameter 3 must be integer", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + v = rules.GetVector3Item(idx++); // cut + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_TYPE, PRIM_TYPE_SPHERE: arg #{1} - parameter 4 must be vector", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + hollow = (float)rules.GetLSLFloatItem(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_TYPE, PRIM_TYPE_SPHERE: arg #{1} - parameter 5 must be float", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + twist = rules.GetVector3Item(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_TYPE, PRIM_TYPE_SPHERE: arg #{1} - parameter 6 must be vector", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + taper_b = rules.GetVector3Item(idx++); // dimple + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_TYPE, PRIM_TYPE_SPHERE: arg #{1} - parameter 7 must be vector", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } SetPrimitiveShapeParams(part, face, v, hollow, twist, taper_b, (byte)ProfileShape.HalfCircle, (byte)Extrusion.Curve1); break; - case (int)ScriptBaseClass.PRIM_TYPE_TORUS: + case ScriptBaseClass.PRIM_TYPE_TORUS: if (remain < 11) - return null; - - face = (int)rules.GetLSLIntegerItem(idx++); // holeshape - v = rules.GetVector3Item(idx++); //cut - hollow = (float)rules.GetLSLFloatItem(idx++); - twist = rules.GetVector3Item(idx++); - holesize = rules.GetVector3Item(idx++); - topshear = rules.GetVector3Item(idx++); - profilecut = rules.GetVector3Item(idx++); - taper_b = rules.GetVector3Item(idx++); // taper_a - revolutions = (float)rules.GetLSLFloatItem(idx++); - radiusoffset = (float)rules.GetLSLFloatItem(idx++); - skew = (float)rules.GetLSLFloatItem(idx++); + return new LSL_List(); + + try + { + face = (int)rules.GetLSLIntegerItem(idx++); // holeshape + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_TYPE, PRIM_TYPE_TORUS: arg #{1} - parameter 3 must be integer", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + v = rules.GetVector3Item(idx++); //cut + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_TYPE, PRIM_TYPE_TORUS: arg #{1} - parameter 4 must be vector", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + hollow = (float)rules.GetLSLFloatItem(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_TYPE, PRIM_TYPE_TORUS: arg #{1} - parameter 5 must be float", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + twist = rules.GetVector3Item(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_TYPE, PRIM_TYPE_TORUS: arg #{1} - parameter 6 must be vector", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + holesize = rules.GetVector3Item(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_TYPE, PRIM_TYPE_TORUS: arg #{1} - parameter 7 must be vector", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + topshear = rules.GetVector3Item(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_TYPE, PRIM_TYPE_TORUS: arg #{1} - parameter 8 must be vector", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + profilecut = rules.GetVector3Item(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_TYPE, PRIM_TYPE_TORUS: arg #{1} - parameter 9 must be vector", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + taper_b = rules.GetVector3Item(idx++); // taper_a + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_TYPE, PRIM_TYPE_TORUS: arg #{1} - parameter 10 must be vector", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + revolutions = (float)rules.GetLSLFloatItem(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_TYPE, PRIM_TYPE_TORUS: arg #{1} - parameter 11 must be float", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + radiusoffset = (float)rules.GetLSLFloatItem(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_TYPE, PRIM_TYPE_TORUS: arg #{1} - parameter 12 must be float", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + skew = (float)rules.GetLSLFloatItem(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_TYPE, PRIM_TYPE_TORUS: arg #{1} - parameter 13 must be vector", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } SetPrimitiveShapeParams(part, face, v, hollow, twist, holesize, topshear, profilecut, taper_b, revolutions, radiusoffset, skew, (byte)ProfileShape.Circle, (byte)Extrusion.Curve1); break; - case (int)ScriptBaseClass.PRIM_TYPE_TUBE: + case ScriptBaseClass.PRIM_TYPE_TUBE: if (remain < 11) - return null; - - face = (int)rules.GetLSLIntegerItem(idx++); // holeshape - v = rules.GetVector3Item(idx++); //cut - hollow = (float)rules.GetLSLFloatItem(idx++); - twist = rules.GetVector3Item(idx++); - holesize = rules.GetVector3Item(idx++); - topshear = rules.GetVector3Item(idx++); - profilecut = rules.GetVector3Item(idx++); - taper_b = rules.GetVector3Item(idx++); // taper_a - revolutions = (float)rules.GetLSLFloatItem(idx++); - radiusoffset = (float)rules.GetLSLFloatItem(idx++); - skew = (float)rules.GetLSLFloatItem(idx++); + return new LSL_List(); + + try + { + face = (int)rules.GetLSLIntegerItem(idx++); // holeshape + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_TYPE, PRIM_TYPE_TUBE: arg #{1} - parameter 3 must be integer", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + v = rules.GetVector3Item(idx++); //cut + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_TYPE, PRIM_TYPE_TUBE: arg #{1} - parameter 4 must be vector", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + hollow = (float)rules.GetLSLFloatItem(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_TYPE, PRIM_TYPE_TUBE: arg #{1} - parameter 5 must be float", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + twist = rules.GetVector3Item(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_TYPE, PRIM_TYPE_TUBE: arg #{1} - parameter 6 must be vector", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + holesize = rules.GetVector3Item(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_TYPE, PRIM_TYPE_TUBE: arg #{1} - parameter 7 must be vector", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + topshear = rules.GetVector3Item(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_TYPE, PRIM_TYPE_TUBE: arg #{1} - parameter 8 must be vector", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + profilecut = rules.GetVector3Item(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_TYPE, PRIM_TYPE_TUBE: arg #{1} - parameter 9 must be vector", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + taper_b = rules.GetVector3Item(idx++); // taper_a + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_TYPE, PRIM_TYPE_TUBE: arg #{1} - parameter 10 must be vector", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + revolutions = (float)rules.GetLSLFloatItem(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_TYPE, PRIM_TYPE_TUBE: arg #{1} - parameter 11 must be float", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + radiusoffset = (float)rules.GetLSLFloatItem(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_TYPE, PRIM_TYPE_TUBE: arg #{1} - parameter 12 must be float", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + skew = (float)rules.GetLSLFloatItem(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_TYPE, PRIM_TYPE_TUBE: arg #{1} - parameter 13 must be float", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } SetPrimitiveShapeParams(part, face, v, hollow, twist, holesize, topshear, profilecut, taper_b, revolutions, radiusoffset, skew, (byte)ProfileShape.Square, (byte)Extrusion.Curve1); break; - case (int)ScriptBaseClass.PRIM_TYPE_RING: + case ScriptBaseClass.PRIM_TYPE_RING: if (remain < 11) - return null; - - face = (int)rules.GetLSLIntegerItem(idx++); // holeshape - v = rules.GetVector3Item(idx++); //cut - hollow = (float)rules.GetLSLFloatItem(idx++); - twist = rules.GetVector3Item(idx++); - holesize = rules.GetVector3Item(idx++); - topshear = rules.GetVector3Item(idx++); - profilecut = rules.GetVector3Item(idx++); - taper_b = rules.GetVector3Item(idx++); // taper_a - revolutions = (float)rules.GetLSLFloatItem(idx++); - radiusoffset = (float)rules.GetLSLFloatItem(idx++); - skew = (float)rules.GetLSLFloatItem(idx++); + return new LSL_List(); + + try + { + face = (int)rules.GetLSLIntegerItem(idx++); // holeshape + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_TYPE, PRIM_TYPE_RING: arg #{1} - parameter 3 must be integer", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + v = rules.GetVector3Item(idx++); //cut + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_TYPE, PRIM_TYPE_RING: arg #{1} - parameter 4 must be vector", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + hollow = (float)rules.GetLSLFloatItem(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_TYPE, PRIM_TYPE_RING: arg #{1} - parameter 5 must be float", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + twist = rules.GetVector3Item(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_TYPE, PRIM_TYPE_RING: arg #{1} - parameter 6 must be vector", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + holesize = rules.GetVector3Item(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_TYPE, PRIM_TYPE_RING: arg #{1} - parameter 7 must be vector", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + topshear = rules.GetVector3Item(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_TYPE, PRIM_TYPE_RING: arg #{1} - parameter 8 must be vector", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + profilecut = rules.GetVector3Item(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_TYPE, PRIM_TYPE_RING: arg #{1} - parameter 9 must be vector", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + taper_b = rules.GetVector3Item(idx++); // taper_a + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_TYPE, PRIM_TYPE_RING: arg #{1} - parameter 10 must be vector", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + revolutions = (float)rules.GetLSLFloatItem(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_TYPE, PRIM_TYPE_RING: arg #{1} - parameter 11 must be float", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + radiusoffset = (float)rules.GetLSLFloatItem(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_TYPE, PRIM_TYPE_RING: arg #{1} - parameter 12 must be float", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + skew = (float)rules.GetLSLFloatItem(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_TYPE, PRIM_TYPE_RING: arg #{1} - parameter 13 must be float", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } SetPrimitiveShapeParams(part, face, v, hollow, twist, holesize, topshear, profilecut, taper_b, revolutions, radiusoffset, skew, (byte)ProfileShape.EquilateralTriangle, (byte)Extrusion.Curve1); break; - case (int)ScriptBaseClass.PRIM_TYPE_SCULPT: + case ScriptBaseClass.PRIM_TYPE_SCULPT: if (remain < 2) - return null; + return new LSL_List(); string map = rules.Data[idx++].ToString(); - face = (int)rules.GetLSLIntegerItem(idx++); // type + try + { + face = (int)rules.GetLSLIntegerItem(idx++); // type + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_TYPE, PRIM_TYPE_SCULPT: arg #{1} - parameter 4 must be integer", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } SetPrimitiveShapeParams(part, map, face, (byte)Extrusion.Curve1); break; } break; - case (int)ScriptBaseClass.PRIM_TEXTURE: + case ScriptBaseClass.PRIM_TEXTURE: if (remain < 5) - return null; + return new LSL_List(); face=(int)rules.GetLSLIntegerItem(idx++); - string tex=rules.Data[idx++].ToString(); - LSL_Vector repeats=rules.GetVector3Item(idx++); - LSL_Vector offsets=rules.GetVector3Item(idx++); - double rotation=(double)rules.GetLSLFloatItem(idx++); + string tex; + LSL_Vector repeats; + LSL_Vector offsets; + double rotation; + + tex = rules.Data[idx++].ToString(); + try + { + repeats = rules.GetVector3Item(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_TEXTURE: arg #{1} - parameter 3 must be vector", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + offsets = rules.GetVector3Item(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_TEXTURE: arg #{1} - parameter 4 must be vector", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + rotation = (double)rules.GetLSLFloatItem(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_TEXTURE: arg #{1} - parameter 5 must be float", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } SetTexture(part, tex, face); ScaleTexture(part, repeats.x, repeats.y, face); @@ -7530,180 +8910,525 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api break; - case (int)ScriptBaseClass.PRIM_COLOR: + case ScriptBaseClass.PRIM_COLOR: if (remain < 3) - return null; + return new LSL_List(); - face=(int)rules.GetLSLIntegerItem(idx++); - LSL_Vector color=rules.GetVector3Item(idx++); - double alpha=(double)rules.GetLSLFloatItem(idx++); + LSL_Vector color; + double alpha; + + try + { + face = (int)rules.GetLSLIntegerItem(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_COLOR: arg #{1} - parameter 2 must be integer", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + color = rules.GetVector3Item(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_COLOR: arg #{1} - parameter 3 must be vector", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + alpha = (double)rules.GetLSLFloatItem(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_COLOR: arg #{1} - parameter 4 must be float", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } part.SetFaceColorAlpha(face, color, alpha); break; - case (int)ScriptBaseClass.PRIM_FLEXIBLE: + case ScriptBaseClass.PRIM_FLEXIBLE: if (remain < 7) - return null; - - bool flexi = rules.GetLSLIntegerItem(idx++); - int softness = rules.GetLSLIntegerItem(idx++); - float gravity = (float)rules.GetLSLFloatItem(idx++); - float friction = (float)rules.GetLSLFloatItem(idx++); - float wind = (float)rules.GetLSLFloatItem(idx++); - float tension = (float)rules.GetLSLFloatItem(idx++); - LSL_Vector force = rules.GetVector3Item(idx++); + return new LSL_List(); + bool flexi; + int softness; + float gravity; + float friction; + float wind; + float tension; + LSL_Vector force; + + try + { + flexi = rules.GetLSLIntegerItem(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_FLEXIBLE: arg #{1} - parameter 2 must be integer", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + softness = rules.GetLSLIntegerItem(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_FLEXIBLE: arg #{1} - parameter 3 must be integer", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + gravity = (float)rules.GetLSLFloatItem(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_FLEXIBLE: arg #{1} - parameter 4 must be float", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + friction = (float)rules.GetLSLFloatItem(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_FLEXIBLE: arg #{1} - parameter 5 must be float", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + wind = (float)rules.GetLSLFloatItem(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_FLEXIBLE: arg #{1} - parameter 6 must be float", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + tension = (float)rules.GetLSLFloatItem(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_FLEXIBLE: arg #{1} - parameter 7 must be float", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + force = rules.GetVector3Item(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_FLEXIBLE: arg #{1} - parameter 8 must be vector", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } SetFlexi(part, flexi, softness, gravity, friction, wind, tension, force); break; - case (int)ScriptBaseClass.PRIM_POINT_LIGHT: + case ScriptBaseClass.PRIM_POINT_LIGHT: if (remain < 5) - return null; - bool light = rules.GetLSLIntegerItem(idx++); - LSL_Vector lightcolor = rules.GetVector3Item(idx++); - float intensity = (float)rules.GetLSLFloatItem(idx++); - float radius = (float)rules.GetLSLFloatItem(idx++); - float falloff = (float)rules.GetLSLFloatItem(idx++); - - SetPointLight(part, light, lightcolor, intensity, radius, falloff); - - break; - - case (int)ScriptBaseClass.PRIM_GLOW: - if (remain < 2) - return null; - face = rules.GetLSLIntegerItem(idx++); - float glow = (float)rules.GetLSLFloatItem(idx++); - - SetGlow(part, face, glow); - - break; - - case (int)ScriptBaseClass.PRIM_BUMP_SHINY: - if (remain < 3) - return null; - face = (int)rules.GetLSLIntegerItem(idx++); - int shiny = (int)rules.GetLSLIntegerItem(idx++); - Bumpiness bump = (Bumpiness)(int)rules.GetLSLIntegerItem(idx++); - + return new LSL_List(); + bool light; + LSL_Vector lightcolor; + float intensity; + float radius; + float falloff; + + try + { + light = rules.GetLSLIntegerItem(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_POINT_LIGHT: arg #{1} - parameter 2 must be integer", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + lightcolor = rules.GetVector3Item(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_POINT_LIGHT: arg #{1} - parameter 3 must be vector", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + intensity = (float)rules.GetLSLFloatItem(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_POINT_LIGHT: arg #{1} - parameter 4 must be float", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + radius = (float)rules.GetLSLFloatItem(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_POINT_LIGHT: arg #{1} - parameter 5 must be float", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + falloff = (float)rules.GetLSLFloatItem(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_POINT_LIGHT: arg #{1} - parameter 6 must be float", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + + SetPointLight(part, light, lightcolor, intensity, radius, falloff); + + break; + + case ScriptBaseClass.PRIM_GLOW: + if (remain < 2) + return new LSL_List(); + + float glow; + + try + { + face = rules.GetLSLIntegerItem(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_GLOW: arg #{1} - parameter 2 must be integer", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + glow = (float)rules.GetLSLFloatItem(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_GLOW: arg #{1} - parameter 3 must be float", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + + SetGlow(part, face, glow); + + break; + + case ScriptBaseClass.PRIM_BUMP_SHINY: + if (remain < 3) + return new LSL_List(); + + int shiny; + Bumpiness bump; + + try + { + face = (int)rules.GetLSLIntegerItem(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_BUMP_SHINY: arg #{1} - parameter 2 must be integer", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + shiny = (int)rules.GetLSLIntegerItem(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_BUMP_SHINY: arg #{1} - parameter 3 must be integer", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + bump = (Bumpiness)(int)rules.GetLSLIntegerItem(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_BUMP_SHINY: arg #{1} - parameter 4 must be integer", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + SetShiny(part, face, shiny, bump); break; - case (int)ScriptBaseClass.PRIM_FULLBRIGHT: - if (remain < 2) - return null; - face = rules.GetLSLIntegerItem(idx++); - bool st = rules.GetLSLIntegerItem(idx++); - SetFullBright(part, face , st); - break; + case ScriptBaseClass.PRIM_FULLBRIGHT: + if (remain < 2) + return new LSL_List(); + bool st; + + try + { + face = rules.GetLSLIntegerItem(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_FULLBRIGHT: arg #{1} - parameter 2 must be integer", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + st = rules.GetLSLIntegerItem(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_FULLBRIGHT: arg #{1} - parameter 4 must be integer", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + SetFullBright(part, face , st); + break; + + case ScriptBaseClass.PRIM_MATERIAL: + if (remain < 1) + return new LSL_List(); + int mat; + + try + { + mat = rules.GetLSLIntegerItem(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_MATERIAL: arg #{1} - parameter 2 must be integer", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + if (mat < 0 || mat > 7) + return new LSL_List(); - case (int)ScriptBaseClass.PRIM_MATERIAL: - if (remain < 1) - return null; - int mat = rules.GetLSLIntegerItem(idx++); - if (mat < 0 || mat > 7) - return null; + part.Material = Convert.ToByte(mat); + break; - part.Material = Convert.ToByte(mat); - break; + case ScriptBaseClass.PRIM_PHANTOM: + if (remain < 1) + return new LSL_List(); - case (int)ScriptBaseClass.PRIM_PHANTOM: - if (remain < 1) - return null; + string ph = rules.Data[idx++].ToString(); + part.ParentGroup.ScriptSetPhantomStatus(ph.Equals("1")); - string ph = rules.Data[idx++].ToString(); - m_host.ParentGroup.ScriptSetPhantomStatus(ph.Equals("1")); + break; - break; + case ScriptBaseClass.PRIM_PHYSICS: + if (remain < 1) + return new LSL_List(); + string phy = rules.Data[idx++].ToString(); + part.ScriptSetPhysicsStatus(phy.Equals("1")); + break; - case (int)ScriptBaseClass.PRIM_PHYSICS: + case ScriptBaseClass.PRIM_PHYSICS_SHAPE_TYPE: if (remain < 1) - return null; - string phy = rules.Data[idx++].ToString(); - bool physics; + return new LSL_List(); - if (phy.Equals("1")) - physics = true; - else - physics = false; + int shape_type; + + try + { + shape_type = rules.GetLSLIntegerItem(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_PHYSICS_SHAPE_TYPE: arg #{1} - parameter 2 must be integer", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } - part.ScriptSetPhysicsStatus(physics); - break; + ExtraPhysicsData physdata = new ExtraPhysicsData(); + physdata.Density = part.Density; + physdata.Bounce = part.Restitution; + physdata.GravitationModifier = part.GravityModifier; + physdata.PhysShapeType = (PhysShapeType)shape_type; - case (int)ScriptBaseClass.PRIM_TEMP_ON_REZ: + part.UpdateExtraPhysics(physdata); + + break; + + case ScriptBaseClass.PRIM_TEMP_ON_REZ: if (remain < 1) - return null; + return new LSL_List(); string temp = rules.Data[idx++].ToString(); - m_host.ParentGroup.ScriptSetTemporaryStatus(temp.Equals("1")); + part.ParentGroup.ScriptSetTemporaryStatus(temp.Equals("1")); break; - case (int)ScriptBaseClass.PRIM_TEXGEN: + case ScriptBaseClass.PRIM_TEXGEN: if (remain < 2) - return null; + return new LSL_List(); //face,type - face = rules.GetLSLIntegerItem(idx++); - int style = rules.GetLSLIntegerItem(idx++); + int style; + + try + { + face = rules.GetLSLIntegerItem(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_TEXGEN: arg #{1} - parameter 2 must be integer", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + style = rules.GetLSLIntegerItem(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_TEXGEN: arg #{1} - parameter 3 must be integer", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } SetTexGen(part, face, style); break; - case (int)ScriptBaseClass.PRIM_TEXT: + case ScriptBaseClass.PRIM_TEXT: if (remain < 3) - return null; - string primText = rules.GetLSLStringItem(idx++); - LSL_Vector primTextColor = rules.GetVector3Item(idx++); - LSL_Float primTextAlpha = rules.GetLSLFloatItem(idx++); + return new LSL_List(); + string primText; + LSL_Vector primTextColor; + LSL_Float primTextAlpha; + + try + { + primText = rules.GetLSLStringItem(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_TEXT: arg #{1} - parameter 2 must be string", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + primTextColor = rules.GetVector3Item(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_TEXT: arg #{1} - parameter 3 must be vector", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + primTextAlpha = rules.GetLSLFloatItem(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_TEXT: arg #{1} - parameter 4 must be float", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } Vector3 av3 = Util.Clip(primTextColor, 0.0f, 1.0f); part.SetText(primText, av3, Util.Clip((float)primTextAlpha, 0.0f, 1.0f)); break; - case (int)ScriptBaseClass.PRIM_NAME: + + case ScriptBaseClass.PRIM_NAME: if (remain < 1) - return null; - string primName = rules.GetLSLStringItem(idx++); - part.Name = primName; + return new LSL_List(); + try + { + string primName = rules.GetLSLStringItem(idx++); + part.Name = primName; + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_NAME: arg #{1} - parameter 2 must be string", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } break; - case (int)ScriptBaseClass.PRIM_DESC: + case ScriptBaseClass.PRIM_DESC: if (remain < 1) - return null; - string primDesc = rules.GetLSLStringItem(idx++); - part.Description = primDesc; + return new LSL_List(); + try + { + string primDesc = rules.GetLSLStringItem(idx++); + part.Description = primDesc; + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_DESC: arg #{1} - parameter 2 must be string", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } break; - case (int)ScriptBaseClass.PRIM_ROT_LOCAL: + case ScriptBaseClass.PRIM_ROT_LOCAL: if (remain < 1) - return null; - SetRot(part, rules.GetQuaternionItem(idx++)); + return new LSL_List(); + LSL_Rotation rot; + try + { + rot = rules.GetQuaternionItem(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_ROT_LOCAL: arg #{1} - parameter 2 must be rotation", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + SetRot(part, rot); break; - case (int)ScriptBaseClass.PRIM_OMEGA: + + case ScriptBaseClass.PRIM_OMEGA: if (remain < 3) - return null; - LSL_Vector axis = rules.GetVector3Item(idx++); - LSL_Float spinrate = rules.GetLSLFloatItem(idx++); - LSL_Float gain = rules.GetLSLFloatItem(idx++); + return new LSL_List(); + LSL_Vector axis; + LSL_Float spinrate; + LSL_Float gain; + + try + { + axis = rules.GetVector3Item(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_OMEGA: arg #{1} - parameter 2 must be vector", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + spinrate = rules.GetLSLFloatItem(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_OMEGA: arg #{1} - parameter 3 must be float", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + try + { + gain = rules.GetLSLFloatItem(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_OMEGA: arg #{1} - parameter 4 must be float", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } TargetOmega(part, axis, (double)spinrate, (double)gain); break; - case (int)ScriptBaseClass.PRIM_SLICE: + + case ScriptBaseClass.PRIM_SLICE: if (remain < 1) - return null; - LSL_Vector slice = rules.GetVector3Item(idx++); + return new LSL_List(); + LSL_Vector slice; + try + { + slice = rules.GetVector3Item(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_SLICE: arg #{1} - parameter 2 must be vector", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } part.UpdateSlice((float)slice.x, (float)slice.y); break; - case (int)ScriptBaseClass.PRIM_LINK_TARGET: + + case ScriptBaseClass.PRIM_LINK_TARGET: if (remain < 3) // setting to 3 on the basis that parsing any usage of PRIM_LINK_TARGET that has nothing following it is pointless. - return null; + return new LSL_List(); return rules.GetSublist(idx, -1); + + default: + Error(originFunc, string.Format("Error running rule #{0}: arg #{1} - unsupported parameter", rulesParsed, idx - idxStart)); + return new LSL_List(); } } } catch (InvalidCastException e) { - ShoutError(string.Format( - "{0} error running rule #{1}: arg #{2} ", - originFunc, rulesParsed, idx - idxStart) + e.Message); + Error(originFunc, string.Format("Error running rule #{0}: arg #{1} - ", rulesParsed, idx - idxStart) + e.Message); } finally { @@ -7723,7 +9448,118 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api } } } - return null; + + return new LSL_List(); + } + + protected LSL_List SetAgentParams(ScenePresence sp, LSL_List rules, string originFunc, ref uint rulesParsed) + { + int idx = 0; + int idxStart = 0; + + try + { + while (idx < rules.Length) + { + ++rulesParsed; + int code = rules.GetLSLIntegerItem(idx++); + + int remain = rules.Length - idx; + idxStart = idx; + + switch (code) + { + case ScriptBaseClass.PRIM_POSITION: + case ScriptBaseClass.PRIM_POS_LOCAL: + if (remain < 1) + return new LSL_List(); + + try + { + sp.OffsetPosition = rules.GetVector3Item(idx++); + } + catch(InvalidCastException) + { + if (code == ScriptBaseClass.PRIM_POSITION) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_POSITION: arg #{1} - parameter 2 must be vector", rulesParsed, idx - idxStart - 1)); + } + else + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_POS_LOCAL: arg #{1} - parameter 2 must be vector", rulesParsed, idx - idxStart - 1)); + } + return new LSL_List(); + } + break; + + case ScriptBaseClass.PRIM_ROTATION: + if (remain < 1) + return new LSL_List(); + + Quaternion inRot; + + try + { + inRot = rules.GetQuaternionItem(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_ROTATION: arg #{1} - parameter 2 must be rotation", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + + SceneObjectPart parentPart = sp.ParentPart; + + if (parentPart != null) + sp.Rotation = m_host.GetWorldRotation() * inRot; + + break; + + case ScriptBaseClass.PRIM_ROT_LOCAL: + if (remain < 1) + return new LSL_List(); + + try + { + sp.Rotation = rules.GetQuaternionItem(idx++); + } + catch(InvalidCastException) + { + Error(originFunc, string.Format("Error running rule #{0} -> PRIM_ROT_LOCAL: arg #{1} - parameter 2 must be rotation", rulesParsed, idx - idxStart - 1)); + return new LSL_List(); + } + + break; + + case ScriptBaseClass.PRIM_TYPE: + Error(originFunc, "PRIM_TYPE disallowed on agent"); + return new LSL_List(); + + case ScriptBaseClass.PRIM_OMEGA: + Error(originFunc, "PRIM_OMEGA disallowed on agent"); + return new LSL_List(); + + case ScriptBaseClass.PRIM_LINK_TARGET: + if (remain < 3) // setting to 3 on the basis that parsing any usage of PRIM_LINK_TARGET that has nothing following it is pointless. + return new LSL_List(); + + return rules.GetSublist(idx, -1); + + default: + Error(originFunc, + string.Format("Error running rule #{0} on agent: arg #{1} - disallowed on agent", rulesParsed, idx - idxStart)); + return new LSL_List(); + } + } + } + catch (InvalidCastException e) + { + Error( + originFunc, + string.Format("Error running rule #{0}: arg #{1} - ", rulesParsed, idx - idxStart) + e.Message); + } + + return new LSL_List(); } public LSL_String llStringToBase64(string str) @@ -7731,14 +9567,15 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api m_host.AddScriptLPS(1); try { - byte[] encData_byte = new byte[str.Length]; + byte[] encData_byte; encData_byte = Util.UTF8.GetBytes(str); string encodedData = Convert.ToBase64String(encData_byte); return encodedData; } - catch (Exception e) + catch { - throw new Exception("Error in base64Encode" + e.Message); + Error("llBase64ToString", "Error encoding string"); + return String.Empty; } } @@ -7747,26 +9584,28 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api m_host.AddScriptLPS(1); try { - return Util.Base64ToString(str); + byte[] b = Convert.FromBase64String(str); + return Encoding.UTF8.GetString(b); } - catch (Exception e) + catch { - throw new Exception("Error in base64Decode" + e.Message); + Error("llBase64ToString", "Error decoding string"); + return String.Empty; } } public LSL_String llXorBase64Strings(string str1, string str2) { m_host.AddScriptLPS(1); - Deprecated("llXorBase64Strings"); - ScriptSleep(300); + Deprecated("llXorBase64Strings", "Use llXorBase64 instead"); + ScriptSleep(m_sleepMsOnXorBase64Strings); return String.Empty; } public void llRemoteDataSetRegion() { m_host.AddScriptLPS(1); - Deprecated("llRemoteDataSetRegion"); + Deprecated("llRemoteDataSetRegion", "Use llOpenRemoteDataChannel instead"); } public LSL_Float llLog10(double val) @@ -7800,21 +9639,21 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api { m_host.AddScriptLPS(1); - ILandObject land = World.LandChannel.GetLandObject(m_host.AbsolutePosition.X, m_host.AbsolutePosition.Y); + ILandObject land = World.LandChannel.GetLandObject(m_host.AbsolutePosition); if (land.LandData.OwnerID != m_host.OwnerID) return; land.SetMusicUrl(url); - ScriptSleep(2000); + ScriptSleep(m_sleepMsOnSetParcelMusicURL); } public LSL_String llGetParcelMusicURL() { m_host.AddScriptLPS(1); - ILandObject land = World.LandChannel.GetLandObject(m_host.AbsolutePosition.X, m_host.AbsolutePosition.Y); + ILandObject land = World.LandChannel.GetLandObject(m_host.AbsolutePosition); if (land.LandData.OwnerID != m_host.OwnerID) return String.Empty; @@ -7825,8 +9664,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api public LSL_Vector llGetRootPosition() { m_host.AddScriptLPS(1); - return new LSL_Vector(m_host.ParentGroup.AbsolutePosition.X, m_host.ParentGroup.AbsolutePosition.Y, - m_host.ParentGroup.AbsolutePosition.Z); + + return new LSL_Vector(m_host.ParentGroup.AbsolutePosition); } /// @@ -7849,13 +9688,14 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api if ((avatar.AgentControlFlags & (uint)AgentManager.ControlFlags.AGENT_CONTROL_MOUSELOOK) != 0) q = avatar.CameraRotation; // Mouselook else - q = avatar.Rotation; // Currently infrequently updated so may be inaccurate + q = avatar.GetWorldRotation(); // Currently infrequently updated so may be inaccurate else q = m_host.ParentGroup.GroupRotation; // Likely never get here but just in case } else q = m_host.ParentGroup.GroupRotation; // just the group rotation - return new LSL_Rotation(q.X, q.Y, q.Z, q.W); + + return new LSL_Rotation(q); } public LSL_String llGetObjectDesc() @@ -7881,145 +9721,676 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api return DateTime.Now.ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ss.fffffffZ"); } - public LSL_Integer llGetNumberOfPrims() - { - m_host.AddScriptLPS(1); + public LSL_Integer llGetNumberOfPrims() + { + m_host.AddScriptLPS(1); + + return m_host.ParentGroup.PrimCount + m_host.ParentGroup.GetSittingAvatarsCount(); + } + + /// + /// Full implementation of llGetBoundingBox according to SL 2015-04-15. + /// http://wiki.secondlife.com/wiki/LlGetBoundingBox + /// http://lslwiki.net/lslwiki/wakka.php?wakka=llGetBoundingBox + /// Returns local bounding box of avatar without attachments + /// if target is non-seated avatar or prim/mesh in avatar attachment. + /// Returns local bounding box of object including seated avatars + /// if target is seated avatar or prim/mesh in object. + /// Uses meshing of prims for high accuracy + /// or less accurate box models for speed. + /// + public LSL_List llGetBoundingBox(string obj) + { + m_host.AddScriptLPS(1); + + // Get target avatar if non-seated avatar or attachment, or prim and object + UUID objID = UUID.Zero; + UUID.TryParse(obj, out objID); + ScenePresence agent = World.GetScenePresence(objID); + if (agent != null) + { + if (agent.ParentPart != null) + { + objID = agent.ParentPart.UUID; + agent = null; + } + } + SceneObjectGroup group = null; + SceneObjectPart target = World.GetSceneObjectPart(objID); + if (target != null) + { + group = target.ParentGroup; + if (group.IsAttachment) { + objID = group.AttachedAvatar; + agent = World.GetScenePresence(objID); + group = null; + target = null; + } + } + + // Initialize but break if no target + LSL_List result = new LSL_List(); + int groupCount = 0; + int partCount = 0; + int vertexCount = 0; + if (target == null && agent == null) + { + result.Add(new LSL_Vector()); + result.Add(new LSL_Vector()); + if (m_addStatsInGetBoundingBox) + result.Add(new LSL_Vector((float)groupCount, (float)partCount, (float)vertexCount)); + return result; + } + Vector3 minPosition = new Vector3(float.MaxValue, float.MaxValue, float.MaxValue); + Vector3 maxPosition = new Vector3(float.MinValue, float.MinValue, float.MinValue); + + // Try to get a mesher + IRendering primMesher = null; + List renderers = RenderingLoader.ListRenderers(Util.ExecutingDirectory()); + if (renderers.Count > 0) + primMesher = RenderingLoader.LoadRenderer(renderers[0]); + + // Get bounding box of just avatar, seated or not + if (agent != null) + { + bool hasParent = false; + Vector3 lower; + Vector3 upper; + BoundingBoxOfScenePresence(agent, out lower, out upper); + Vector3 offset = Vector3.Zero; + + // Since local bounding box unrotated and untilted, keep it simple + AddBoundingBoxOfSimpleBox(lower, upper, offset, agent.Rotation, hasParent, ref minPosition, ref maxPosition, ref vertexCount); + partCount++; + groupCount++; + + // Return lower and upper bounding box corners + result.Add(new LSL_Vector(minPosition)); + result.Add(new LSL_Vector(maxPosition)); + if (m_addStatsInGetBoundingBox) + result.Add(new LSL_Vector((float)groupCount, (float)partCount, (float)vertexCount)); + return result; + } + // Get bounding box of object including seated avatars + else if (group != null) + { + // Merge bounding boxes of all parts (prims and mesh) + foreach (SceneObjectPart part in group.Parts) + { + bool hasParent = (!part.IsRoot); + // When requested or if no mesher, keep it simple + if (m_useSimpleBoxesInGetBoundingBox || primMesher == null) + { + AddBoundingBoxOfSimpleBox(part.Scale * -0.5f, part.Scale * 0.5f, part.OffsetPosition, part.RotationOffset, hasParent, ref minPosition, ref maxPosition, ref vertexCount); + } + // Do the full mounty + else + { + Primitive omvPrim = part.Shape.ToOmvPrimitive(part.OffsetPosition, part.RotationOffset); + byte[] sculptAsset = null; + if (omvPrim.Sculpt != null) + sculptAsset = World.AssetService.GetData(omvPrim.Sculpt.SculptTexture.ToString()); + + // When part is mesh + // Quirk: Only imports as incompletely populated faceted mesh object, so needs an own handler. + if (omvPrim.Sculpt != null && omvPrim.Sculpt.Type == SculptType.Mesh && sculptAsset != null) + { + AssetMesh meshAsset = new AssetMesh(omvPrim.Sculpt.SculptTexture, sculptAsset); + FacetedMesh mesh = null; + FacetedMesh.TryDecodeFromAsset(omvPrim, meshAsset, DetailLevel.Highest, out mesh); + meshAsset = null; + if (mesh != null) + { + AddBoundingBoxOfFacetedMesh(mesh, omvPrim, hasParent, ref minPosition, ref maxPosition, ref vertexCount); + mesh = null; + } + } + + // When part is sculpt + // Quirk: Generated sculpt mesh is about 2.8% smaller in X and Y than visual sculpt. + else if (omvPrim.Sculpt != null && omvPrim.Sculpt.Type != SculptType.Mesh && sculptAsset != null) + { + IJ2KDecoder imgDecoder = World.RequestModuleInterface(); + if (imgDecoder != null) + { + Image sculpt = imgDecoder.DecodeToImage(sculptAsset); + if (sculpt != null) + { + SimpleMesh mesh = primMesher.GenerateSimpleSculptMesh(omvPrim, (Bitmap)sculpt, DetailLevel.Medium); + sculpt.Dispose(); + if (mesh != null) + { + AddBoundingBoxOfSimpleMesh(mesh, omvPrim, hasParent, ref minPosition, ref maxPosition, ref vertexCount); + mesh = null; + } + } + } + } + + // When part is prim + else if (omvPrim.Sculpt == null) + { + SimpleMesh mesh = primMesher.GenerateSimpleMesh(omvPrim, DetailLevel.Medium); + if (mesh != null) + { + AddBoundingBoxOfSimpleMesh(mesh, omvPrim, hasParent, ref minPosition, ref maxPosition, ref vertexCount); + mesh = null; + } + } + + // When all else fails, try fallback to simple box + else + { + AddBoundingBoxOfSimpleBox(part.Scale * -0.5f, part.Scale * 0.5f, part.OffsetPosition, part.RotationOffset, hasParent, ref minPosition, ref maxPosition, ref vertexCount); + } + } + partCount++; + } + } + + // Merge bounding boxes of seated avatars + foreach (ScenePresence sp in group.GetSittingAvatars()) + { + Vector3 lower; + Vector3 upper; + BoundingBoxOfScenePresence(sp, out lower, out upper); + Vector3 offset = sp.OffsetPosition; + + bool hasParent = true; + // When requested or if no mesher, keep it simple + if (m_useSimpleBoxesInGetBoundingBox || primMesher == null) + { + AddBoundingBoxOfSimpleBox(lower, upper, offset, sp.Rotation, hasParent, ref minPosition, ref maxPosition, ref vertexCount); + } + // Do the full mounty + else + { + // Prim shapes don't do center offsets, so add it here. + offset = offset + (lower + upper) * 0.5f * sp.Rotation; + Primitive omvPrim = MakeOpenMetaversePrim(upper - lower, offset, sp.Rotation, ScriptBaseClass.PRIM_TYPE_SPHERE); + SimpleMesh mesh = primMesher.GenerateSimpleMesh(omvPrim, DetailLevel.Medium); + AddBoundingBoxOfSimpleMesh(mesh, omvPrim, hasParent, ref minPosition, ref maxPosition, ref vertexCount); + mesh = null; + } + partCount++; + } + + groupCount++; + + // Return lower and upper bounding box corners + result.Add(new LSL_Vector(minPosition)); + result.Add(new LSL_Vector(maxPosition)); + if (m_addStatsInGetBoundingBox) + result.Add(new LSL_Vector((float)groupCount, (float)partCount, (float)vertexCount)); + + primMesher = null; + return result; + } + + /// + /// Helper to calculate bounding box of an avatar. + /// + private void BoundingBoxOfScenePresence(ScenePresence sp, out Vector3 lower, out Vector3 upper) + { + // Adjust from OS model + // avatar height = visual height - 0.2, bounding box height = visual height + // to SL model + // avatar height = visual height, bounding box height = visual height + 0.2 + float height = sp.Appearance.AvatarHeight + m_avatarHeightCorrection; + + // According to avatar bounding box in SL 2015-04-18: + // standing = <-0.275,-0.35,-0.1-0.5*h> : <0.275,0.35,0.1+0.5*h> + // groundsitting = <-0.3875,-0.5,-0.05-0.375*h> : <0.3875,0.5,0.5> + // sitting = <-0.5875,-0.35,-0.35-0.375*h> : <0.1875,0.35,-0.25+0.25*h> + + // When avatar is sitting + if (sp.ParentPart != null) + { + lower = new Vector3(m_lABB1SitX0, m_lABB1SitY0, m_lABB1SitZ0 + m_lABB1SitZ1 * height); + upper = new Vector3(m_lABB2SitX0, m_lABB2SitY0, m_lABB2SitZ0 + m_lABB2SitZ1 * height); + } + // When avatar is groundsitting + else if (sp.Animator.Animations.ImplicitDefaultAnimation.AnimID == DefaultAvatarAnimations.AnimsUUID["SIT_GROUND_CONSTRAINED"]) + { + lower = new Vector3(m_lABB1GrsX0, m_lABB1GrsY0, m_lABB1GrsZ0 + m_lABB1GrsZ1 * height); + upper = new Vector3(m_lABB2GrsX0, m_lABB2GrsY0, m_lABB2GrsZ0 + m_lABB2GrsZ1 * height); + } + // When avatar is standing or flying + else + { + lower = new Vector3(m_lABB1StdX0, m_lABB1StdY0, m_lABB1StdZ0 + m_lABB1StdZ1 * height); + upper = new Vector3(m_lABB2StdX0, m_lABB2StdY0, m_lABB2StdZ0 + m_lABB2StdZ1 * height); + } + } + + /// + /// Helper to approximate a part with a simple box. + /// + private void AddBoundingBoxOfSimpleBox(Vector3 corner1, Vector3 corner2, Vector3 offset, Quaternion rotation, bool hasParent, ref Vector3 lower, ref Vector3 upper, ref int count) + { + // Parse the 8 box corners + for (int i = 0; i < 8; i++) + { + // Calculate each box corner + Vector3 position = corner1; + if ((i & 1) != 0) + position.X = corner2.X; + if ((i & 2) != 0) + position.Y = corner2.Y; + if ((i & 4) != 0) + position.Z = corner2.Z; + // Rotate part unless part is root + if (hasParent) + position = position * rotation; + position = position + offset; + // Adjust lower and upper bounding box corners if needed + lower = Vector3.Min(lower, position); + upper = Vector3.Max(upper, position); + count++; + } + } + + /// + /// Helper to parse a meshed prim and needed especially + /// for accuracy with tortured prims and sculpts. + /// + private void AddBoundingBoxOfSimpleMesh(SimpleMesh mesh, Primitive prim, bool hasParent, ref Vector3 lower, ref Vector3 upper, ref int count) + { + // Quirk: A meshed box contains 10 instead of the 8 necessary vertices. + if (mesh != null) + { + // Parse each vertex in mesh + foreach (Vertex vertex in mesh.Vertices) + { + Vector3 position = vertex.Position; + position = position * prim.Scale; + // Rotate part unless part is root + if (hasParent) + position = position * prim.Rotation; + position = position + prim.Position; + // Adjust lower and upper bounding box corners if needed + lower = Vector3.Min(lower, position); + upper = Vector3.Max(upper, position); + count++; + } + } + } + + /// + /// Helper to parse mesh because no method exists + /// to parse mesh assets to SimpleMesh. + /// + private void AddBoundingBoxOfFacetedMesh(FacetedMesh mesh, Primitive prim, bool hasParent, ref Vector3 lower, ref Vector3 upper, ref int count) + { + if (mesh != null) + { + // Parse each face in mesh + // since vertex array isn't populated. + // This parses each unique vertex 3-6 times. + foreach (Face face in mesh.Faces) + { + // Parse each vertex in face + foreach (Vertex vertex in face.Vertices) + { + Vector3 position = vertex.Position; + position = position * prim.Scale; + // Rotate part unless part is root + if (hasParent) + position = position * prim.Rotation; + position = position + prim.Position; + // Adjust lower and upper bounding box corners if needed + lower = Vector3.Min(lower, position); + upper = Vector3.Max(upper, position); + count++; + } + } + } + } + + /// + /// Helper to make up an OpenMetaverse prim + /// needed to create mesh from parts. + /// + private Primitive MakeOpenMetaversePrim(Vector3 scale, Vector3 position, Quaternion rotation, int primType) + { + // Initialize and set common parameters + Primitive prim = new OpenMetaverse.Primitive(); + prim.Scale = scale; + prim.Position = position; + prim.Rotation = rotation; + prim.PrimData.PathShearX = 0.0f; + prim.PrimData.PathShearY = 0.0f; + prim.PrimData.PathBegin = 0.0f; + prim.PrimData.PathEnd = 1.0f; + prim.PrimData.PathScaleX = 1.0f; + prim.PrimData.PathScaleY = 1.0f; + prim.PrimData.PathTaperX = 0.0f; + prim.PrimData.PathTaperY = 0.0f; + prim.PrimData.PathTwistBegin = 0.0f; + prim.PrimData.PathTwist = 0.0f; + prim.PrimData.ProfileBegin = 0.0f; + prim.PrimData.ProfileEnd = 1.0f; + prim.PrimData.ProfileHollow = 0.0f; + prim.PrimData.ProfileCurve = (ProfileCurve)1; + prim.PrimData.ProfileHole = (HoleType)0; + prim.PrimData.PathCurve = (PathCurve)16; + prim.PrimData.PathRadiusOffset = 0.0f; + prim.PrimData.PathRevolutions = 1.0f; + prim.PrimData.PathSkew = 0.0f; + prim.PrimData.PCode = OpenMetaverse.PCode.Prim; + prim.PrimData.State = (byte)0; + + // Set type specific parameters + switch (primType) + { + // Set specific parameters for box + case ScriptBaseClass.PRIM_TYPE_BOX: + prim.PrimData.PathScaleY = 1.0f; + prim.PrimData.ProfileCurve = (ProfileCurve)1; + prim.PrimData.PathCurve = (PathCurve)16; + break; + // Set specific parameters for cylinder + case ScriptBaseClass.PRIM_TYPE_CYLINDER: + prim.PrimData.PathScaleY = 1.0f; + prim.PrimData.ProfileCurve = (ProfileCurve)0; + prim.PrimData.PathCurve = (PathCurve)16; + break; + // Set specific parameters for prism + case ScriptBaseClass.PRIM_TYPE_PRISM: + prim.PrimData.PathScaleY = 1.0f; + prim.PrimData.ProfileCurve = (ProfileCurve)3; + prim.PrimData.PathCurve = (PathCurve)16; + break; + // Set specific parameters for sphere + case ScriptBaseClass.PRIM_TYPE_SPHERE: + prim.PrimData.PathScaleY = 1.0f; + prim.PrimData.ProfileCurve = (ProfileCurve)5; + prim.PrimData.PathCurve = (PathCurve)32; + break; + // Set specific parameters for torus + case ScriptBaseClass.PRIM_TYPE_TORUS: + prim.PrimData.PathScaleY = 0.5f; + prim.PrimData.ProfileCurve = (ProfileCurve)0; + prim.PrimData.PathCurve = (PathCurve)32; + break; + // Set specific parameters for tube + case ScriptBaseClass.PRIM_TYPE_TUBE: + prim.PrimData.PathScaleY = 0.5f; + prim.PrimData.ProfileCurve = (ProfileCurve)1; + prim.PrimData.PathCurve = (PathCurve)32; + break; + // Set specific parameters for ring + case ScriptBaseClass.PRIM_TYPE_RING: + prim.PrimData.PathScaleY = 0.5f; + prim.PrimData.ProfileCurve = (ProfileCurve)3; + prim.PrimData.PathCurve = (PathCurve)32; + break; + // Set specific parameters for sculpt + case ScriptBaseClass.PRIM_TYPE_SCULPT: + prim.PrimData.PathScaleY = 1.0f; + prim.PrimData.ProfileCurve = (ProfileCurve)5; + prim.PrimData.PathCurve = (PathCurve)32; + break; + // Default to specific parameters for box + default: + prim.PrimData.PathScaleY = 1.0f; + prim.PrimData.ProfileCurve = (ProfileCurve)1; + prim.PrimData.PathCurve = (PathCurve)16; + break; + } + + return prim; + } + + /// + /// Implementation of llGetGeometricCenter according to SL 2015-04-30. + /// http://wiki.secondlife.com/wiki/LlGetGeometricCenter + /// Returns the average position offset of all linked parts, + /// including the root prim and seated avatars, + /// relative to the root prim in local coordinates. + /// + public LSL_Vector llGetGeometricCenter() + { + // Subtract whatever position the root prim has to make it zero + Vector3 offset = m_host.ParentGroup.RootPart.OffsetPosition * -1.0f; + + // Add all prim/part position offsets + foreach (SceneObjectPart part in m_host.ParentGroup.Parts) + offset = offset + part.OffsetPosition; + // Add all avatar/scene presence position offsets + foreach (ScenePresence sp in m_host.ParentGroup.GetSittingAvatars()) + offset = offset + sp.OffsetPosition; + + // Calculate and return the average offset + offset = offset / (float)(m_host.ParentGroup.PrimCount + m_host.ParentGroup.GetSittingAvatarsCount()); + return new LSL_Vector(offset); + } + + public LSL_List GetEntityParams(ISceneEntity entity, LSL_List rules) + { + LSL_List result = new LSL_List(); + LSL_List remaining; + + while (true) + { +// m_log.DebugFormat( +// "[LSL API]: GetEntityParams has {0} rules with scene entity named {1}", +// rules.Length, entity != null ? entity.Name : "NULL"); + + if (entity == null) + return result; + + if (entity is SceneObjectPart) + remaining = GetPrimParams((SceneObjectPart)entity, rules, ref result); + else + remaining = GetAgentParams((ScenePresence)entity, rules, ref result); + + if (remaining == null || remaining.Length < 2) + return result; + + int linknumber = remaining.GetLSLIntegerItem(0); + rules = remaining.GetSublist(1, -1); + entity = GetLinkEntity(m_host, linknumber); + } + } + + public LSL_List llGetPrimitiveParams(LSL_List rules) + { + m_host.AddScriptLPS(1); + + return GetEntityParams(m_host, rules); + } + + public LSL_List llGetLinkPrimitiveParams(int linknumber, LSL_List rules) + { + m_host.AddScriptLPS(1); + + return GetEntityParams(GetLinkEntity(m_host, linknumber), rules); + } + + public LSL_Vector GetAgentSize(ScenePresence sp) + { + return new LSL_Vector(0.45, 0.6, sp.Appearance.AvatarHeight); + } + + /// + /// Gets params for a seated avatar in a linkset. + /// + /// + /// + /// + /// + public LSL_List GetAgentParams(ScenePresence sp, LSL_List rules, ref LSL_List res) + { + int idx = 0; + while (idx < rules.Length) + { + int code = (int)rules.GetLSLIntegerItem(idx++); + int remain = rules.Length-idx; + + switch (code) + { + case (int)ScriptBaseClass.PRIM_MATERIAL: + res.Add(new LSL_Integer(ScriptBaseClass.PRIM_MATERIAL_FLESH)); + break; + + case (int)ScriptBaseClass.PRIM_PHYSICS: + res.Add(ScriptBaseClass.FALSE); + break; + + case (int)ScriptBaseClass.PRIM_TEMP_ON_REZ: + res.Add(ScriptBaseClass.FALSE); + break; + + case (int)ScriptBaseClass.PRIM_PHANTOM: + res.Add(ScriptBaseClass.FALSE); + break; + + case (int)ScriptBaseClass.PRIM_POSITION: + res.Add(new LSL_Vector(sp.AbsolutePosition)); + break; + + case (int)ScriptBaseClass.PRIM_SIZE: + res.Add(GetAgentSize(sp)); + break; + + case (int)ScriptBaseClass.PRIM_ROTATION: + res.Add(sp.GetWorldRotation()); + break; + + case (int)ScriptBaseClass.PRIM_TYPE: + res.Add(new LSL_Integer(ScriptBaseClass.PRIM_TYPE_BOX)); + res.Add(new LSL_Integer(ScriptBaseClass.PRIM_HOLE_DEFAULT)); + res.Add(new LSL_Vector(0, 1, 0)); + res.Add(new LSL_Float(0)); + res.Add(new LSL_Vector(0, 0, 0)); + res.Add(new LSL_Vector(1, 1, 0)); + res.Add(new LSL_Vector(0, 0, 0)); + break; + + case (int)ScriptBaseClass.PRIM_TEXTURE: + if (remain < 1) + return new LSL_List(); + + int face = (int)rules.GetLSLIntegerItem(idx++); + if (face > 21) + break; + + res.Add(new LSL_String("")); + res.Add(ScriptBaseClass.ZERO_VECTOR); + res.Add(ScriptBaseClass.ZERO_VECTOR); + res.Add(new LSL_Float(0)); + break; + + case (int)ScriptBaseClass.PRIM_COLOR: + if (remain < 1) + return new LSL_List(); + + face = (int)rules.GetLSLIntegerItem(idx++); + if (face > 21) + break; + + res.Add(ScriptBaseClass.ZERO_VECTOR); + res.Add(new LSL_Float(0)); + break; + + case (int)ScriptBaseClass.PRIM_BUMP_SHINY: + if (remain < 1) + return new LSL_List(); + + face = (int)rules.GetLSLIntegerItem(idx++); + if (face > 21) + break; + + res.Add(ScriptBaseClass.PRIM_SHINY_NONE); + res.Add(ScriptBaseClass.PRIM_BUMP_NONE); + break; + + case (int)ScriptBaseClass.PRIM_FULLBRIGHT: + if (remain < 1) + return new LSL_List(); - return m_host.ParentGroup.PrimCount + m_host.ParentGroup.GetSittingAvatarsCount(); - } + face = (int)rules.GetLSLIntegerItem(idx++); + if (face > 21) + break; - /// - /// A partial implementation. - /// http://lslwiki.net/lslwiki/wakka.php?wakka=llGetBoundingBox - /// So far only valid for standing/flying/ground sitting avatars and single prim objects. - /// If the object has multiple prims and/or a sitting avatar then the bounding - /// box is for the root prim only. - /// - public LSL_List llGetBoundingBox(string obj) - { - m_host.AddScriptLPS(1); - UUID objID = UUID.Zero; - LSL_List result = new LSL_List(); - if (!UUID.TryParse(obj, out objID)) - { - result.Add(new LSL_Vector()); - result.Add(new LSL_Vector()); - return result; - } - ScenePresence presence = World.GetScenePresence(objID); - if (presence != null) - { - if (presence.ParentID == 0) // not sat on an object - { - LSL_Vector lower; - LSL_Vector upper; - if (presence.Animator.Animations.ImplicitDefaultAnimation.AnimID - == DefaultAvatarAnimations.AnimsUUID["SIT_GROUND_CONSTRAINED"]) - { - // This is for ground sitting avatars - float height = presence.Appearance.AvatarHeight / 2.66666667f; - lower = new LSL_Vector(-0.3375f, -0.45f, height * -1.0f); - upper = new LSL_Vector(0.3375f, 0.45f, 0.0f); - } - else - { - // This is for standing/flying avatars - float height = presence.Appearance.AvatarHeight / 2.0f; - lower = new LSL_Vector(-0.225f, -0.3f, height * -1.0f); - upper = new LSL_Vector(0.225f, 0.3f, height + 0.05f); - } - result.Add(lower); - result.Add(upper); - return result; - } - else - { - // sitting on an object so we need the bounding box of that - // which should include the avatar so set the UUID to the - // UUID of the object the avatar is sat on and allow it to fall through - // to processing an object - SceneObjectPart p = World.GetSceneObjectPart(presence.ParentID); - objID = p.UUID; - } - } - SceneObjectPart part = World.GetSceneObjectPart(objID); - // Currently only works for single prims without a sitting avatar - if (part != null) - { - Vector3 halfSize = part.Scale / 2.0f; - LSL_Vector lower = (new LSL_Vector(halfSize)) * -1.0f; - LSL_Vector upper = new LSL_Vector(halfSize); - result.Add(lower); - result.Add(upper); - return result; - } + res.Add(ScriptBaseClass.FALSE); + break; - // Not found so return empty values - result.Add(new LSL_Vector()); - result.Add(new LSL_Vector()); - return result; - } + case (int)ScriptBaseClass.PRIM_FLEXIBLE: + res.Add(ScriptBaseClass.FALSE); + res.Add(new LSL_Integer(0)); + res.Add(new LSL_Float(0)); + res.Add(new LSL_Float(0)); + res.Add(new LSL_Float(0)); + res.Add(new LSL_Float(0)); + res.Add(ScriptBaseClass.ZERO_VECTOR); + break; - public LSL_Vector llGetGeometricCenter() - { - return new LSL_Vector(m_host.GetGeometricCenter().X, m_host.GetGeometricCenter().Y, m_host.GetGeometricCenter().Z); - } + case (int)ScriptBaseClass.PRIM_TEXGEN: + if (remain < 1) + return new LSL_List(); - public LSL_List llGetPrimitiveParams(LSL_List rules) - { - m_host.AddScriptLPS(1); + face = (int)rules.GetLSLIntegerItem(idx++); + if (face > 21) + break; - LSL_List result = new LSL_List(); + res.Add(ScriptBaseClass.PRIM_TEXGEN_DEFAULT); + break; - LSL_List remaining = GetPrimParams(m_host, rules, ref result); + case (int)ScriptBaseClass.PRIM_POINT_LIGHT: + res.Add(ScriptBaseClass.FALSE); + res.Add(ScriptBaseClass.ZERO_VECTOR); + res.Add(ScriptBaseClass.ZERO_VECTOR); + break; - while (remaining != null && remaining.Length > 2) - { - int linknumber = remaining.GetLSLIntegerItem(0); - rules = remaining.GetSublist(1, -1); - List parts = GetLinkParts(linknumber); + case (int)ScriptBaseClass.PRIM_GLOW: + if (remain < 1) + return new LSL_List(); - foreach (SceneObjectPart part in parts) - remaining = GetPrimParams(part, rules, ref result); - } + face = (int)rules.GetLSLIntegerItem(idx++); + if (face > 21) + break; - return result; - } + res.Add(new LSL_Float(0)); + break; - public LSL_List llGetLinkPrimitiveParams(int linknumber, LSL_List rules) - { - m_host.AddScriptLPS(1); + case (int)ScriptBaseClass.PRIM_TEXT: + res.Add(new LSL_String("")); + res.Add(ScriptBaseClass.ZERO_VECTOR); + res.Add(new LSL_Float(1)); + break; - List parts = GetLinkParts(linknumber); + case (int)ScriptBaseClass.PRIM_ROT_LOCAL: + res.Add(new LSL_Rotation(sp.Rotation)); + break; - LSL_List res = new LSL_List(); - LSL_List remaining = null; + case (int)ScriptBaseClass.PRIM_POS_LOCAL: + res.Add(new LSL_Vector(sp.OffsetPosition)); + break; - foreach (SceneObjectPart part in parts) - { - remaining = GetPrimParams(part, rules, ref res); - } + case (int)ScriptBaseClass.PRIM_SLICE: + res.Add(new LSL_Vector(0, 1, 0)); + break; - while (remaining != null && remaining.Length > 2) - { - linknumber = remaining.GetLSLIntegerItem(0); - rules = remaining.GetSublist(1, -1); - parts = GetLinkParts(linknumber); + case (int)ScriptBaseClass.PRIM_LINK_TARGET: + if(remain < 3) + return new LSL_List(); - foreach (SceneObjectPart part in parts) - remaining = GetPrimParams(part, rules, ref res); + return rules.GetSublist(idx, -1); + } } - return res; + return new LSL_List(); } public LSL_List GetPrimParams(SceneObjectPart part, LSL_List rules, ref LSL_List res) { - int idx=0; + int idx = 0; while (idx < rules.Length) { - int code=(int)rules.GetLSLIntegerItem(idx++); - int remain=rules.Length-idx; + int code = (int)rules.GetLSLIntegerItem(idx++); + int remain = rules.Length - idx; switch (code) { @@ -8049,29 +10420,34 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api break; case (int)ScriptBaseClass.PRIM_POSITION: - LSL_Vector v = new LSL_Vector(part.AbsolutePosition.X, - part.AbsolutePosition.Y, - part.AbsolutePosition.Z); + LSL_Vector v = new LSL_Vector(part.AbsolutePosition); + // For some reason, the part.AbsolutePosition.* values do not change if the // linkset is rotated; they always reflect the child prim's world position // as though the linkset is unrotated. This is incompatible behavior with SL's // implementation, so will break scripts imported from there (not to mention it // makes it more difficult to determine a child prim's actual inworld position). - if (part.ParentID != 0) - v = ((v - llGetRootPosition()) * llGetRootRotation()) + llGetRootPosition(); + if (!part.IsRoot) + { + LSL_Vector rootPos = new LSL_Vector(m_host.ParentGroup.AbsolutePosition); + v = ((v - rootPos) * llGetRootRotation()) + rootPos; + } + res.Add(v); break; case (int)ScriptBaseClass.PRIM_SIZE: - res.Add(new LSL_Vector(part.Scale.X, - part.Scale.Y, - part.Scale.Z)); + res.Add(new LSL_Vector(part.Scale)); break; case (int)ScriptBaseClass.PRIM_ROTATION: res.Add(GetPartRot(part)); break; + case (int)ScriptBaseClass.PRIM_PHYSICS_SHAPE_TYPE: + res.Add(new LSL_Integer((int)part.PhysicsShapeType)); + break; + case (int)ScriptBaseClass.PRIM_TYPE: // implementing box PrimitiveBaseShape Shape = part.Shape; @@ -8101,7 +10477,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api break; case ScriptBaseClass.PRIM_TYPE_SCULPT: - res.Add(Shape.SculptTexture.ToString()); + res.Add(new LSL_String(Shape.SculptTexture.ToString())); res.Add(new LSL_Integer(Shape.SculptType)); break; @@ -8133,16 +10509,16 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api res.Add(new LSL_Vector(Shape.PathTaperX / 100.0, Shape.PathTaperY / 100.0, 0)); // float revolutions - res.Add(new LSL_Float(Math.Round(Shape.PathRevolutions * 0.015d, 2, MidpointRounding.AwayFromZero)) + 1.0d); + res.Add(new LSL_Float(Math.Round(Shape.PathRevolutions * 0.015d, 2, MidpointRounding.AwayFromZero)) + 1.0d); // Slightly inaccurate, because an unsigned byte is being used to represent - // the entire range of floating-point values from 1.0 through 4.0 (which is how + // the entire range of floating-point values from 1.0 through 4.0 (which is how // SL does it). // - // Using these formulas to store and retrieve PathRevolutions, it is not - // possible to use all values between 1.00 and 4.00. For instance, you can't + // Using these formulas to store and retrieve PathRevolutions, it is not + // possible to use all values between 1.00 and 4.00. For instance, you can't // represent 1.10. You can represent 1.09 and 1.11, but not 1.10. So, if you // use llSetPrimitiveParams to set revolutions to 1.10 and then retreive them - // with llGetPrimitiveParams, you'll retrieve 1.09. You can also see a similar + // with llGetPrimitiveParams, you'll retrieve 1.09. You can also see a similar // behavior in the viewer as you cannot set 1.10. The viewer jumps to 1.11. // In SL, llSetPrimitveParams and llGetPrimitiveParams can set and get a value // such as 1.10. So, SL must store and retreive the actual user input rather @@ -8159,7 +10535,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api case (int)ScriptBaseClass.PRIM_TEXTURE: if (remain < 1) - return null; + return new LSL_List(); int face = (int)rules.GetLSLIntegerItem(idx++); Primitive.TextureEntry tex = part.Shape.Textures; @@ -8199,7 +10575,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api case (int)ScriptBaseClass.PRIM_COLOR: if (remain < 1) - return null; + return new LSL_List(); face=(int)rules.GetLSLIntegerItem(idx++); @@ -8228,7 +10604,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api case (int)ScriptBaseClass.PRIM_BUMP_SHINY: if (remain < 1) - return null; + return new LSL_List(); face=(int)rules.GetLSLIntegerItem(idx++); @@ -8259,9 +10635,9 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api case (int)ScriptBaseClass.PRIM_FULLBRIGHT: if (remain < 1) - return null; + return new LSL_List(); - face=(int)rules.GetLSLIntegerItem(idx++); + face = (int)rules.GetLSLIntegerItem(idx++); tex = part.Shape.Textures; if (face == ScriptBaseClass.ALL_SIDES) @@ -8301,7 +10677,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api case (int)ScriptBaseClass.PRIM_TEXGEN: if (remain < 1) - return null; + return new LSL_List(); face=(int)rules.GetLSLIntegerItem(idx++); @@ -8342,7 +10718,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api case (int)ScriptBaseClass.PRIM_GLOW: if (remain < 1) - return null; + return new LSL_List(); face=(int)rules.GetLSLIntegerItem(idx++); @@ -8371,7 +10747,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api res.Add(new LSL_Vector(textColor.R, textColor.G, textColor.B)); - res.Add(new LSL_Float(textColor.A)); + res.Add(new LSL_Float(1.0 - textColor.A)); break; case (int)ScriptBaseClass.PRIM_NAME: res.Add(new LSL_String(part.Name)); @@ -8380,7 +10756,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api res.Add(new LSL_String(part.Description)); break; case (int)ScriptBaseClass.PRIM_ROT_LOCAL: - res.Add(new LSL_Rotation(part.RotationOffset.X, part.RotationOffset.Y, part.RotationOffset.Z, part.RotationOffset.W)); + res.Add(new LSL_Rotation(part.RotationOffset)); break; case (int)ScriptBaseClass.PRIM_POS_LOCAL: res.Add(new LSL_Vector(GetPartLocalPos(part))); @@ -8395,27 +10771,29 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api )); break; case (int)ScriptBaseClass.PRIM_LINK_TARGET: - if(remain < 3) - return null; + + // TODO: Should be issuing a runtime script warning in this case. + if (remain < 2) + return new LSL_List(); return rules.GetSublist(idx, -1); } } - return null; + return new LSL_List(); } public LSL_List llGetPrimMediaParams(int face, LSL_List rules) { m_host.AddScriptLPS(1); - ScriptSleep(1000); + ScriptSleep(m_sleepMsOnGetPrimMediaParams); return GetPrimMediaParams(m_host, face, rules); } public LSL_List llGetLinkMedia(LSL_Integer link, LSL_Integer face, LSL_List rules) { m_host.AddScriptLPS(1); - ScriptSleep(1000); + ScriptSleep(m_sleepMsOnGetLinkMedia); if (link == ScriptBaseClass.LINK_ROOT) return GetPrimMediaParams(m_host.ParentGroup.RootPart, face, rules); else if (link == ScriptBaseClass.LINK_THIS) @@ -8535,14 +10913,14 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api public LSL_Integer llSetPrimMediaParams(LSL_Integer face, LSL_List rules) { m_host.AddScriptLPS(1); - ScriptSleep(1000); + ScriptSleep(m_sleepMsOnSetPrimMediaParams); return SetPrimMediaParams(m_host, face, rules); } public LSL_Integer llSetLinkMedia(LSL_Integer link, LSL_Integer face, LSL_List rules) { m_host.AddScriptLPS(1); - ScriptSleep(1000); + ScriptSleep(m_sleepMsOnSetLinkMedia); if (link == ScriptBaseClass.LINK_ROOT) return SetPrimMediaParams(m_host.ParentGroup.RootPart, face, rules); else if (link == ScriptBaseClass.LINK_THIS) @@ -8661,14 +11039,14 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api public LSL_Integer llClearPrimMedia(LSL_Integer face) { m_host.AddScriptLPS(1); - ScriptSleep(1000); + ScriptSleep(m_sleepMsOnClearPrimMedia); return ClearPrimMedia(m_host, face); } public LSL_Integer llClearLinkMedia(LSL_Integer link, LSL_Integer face) { m_host.AddScriptLPS(1); - ScriptSleep(1000); + ScriptSleep(m_sleepMsOnClearLinkMedia); if (link == ScriptBaseClass.LINK_ROOT) return ClearPrimMedia(m_host.ParentGroup.RootPart, face); else if (link == ScriptBaseClass.LINK_THIS) @@ -9307,7 +11685,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api if (item == null) { - llSay(0, "No item name '" + item + "'"); + Error("llGetInventoryCreator", "Can't find item '" + item + "'"); return String.Empty; } @@ -9355,7 +11733,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api case ScriptBaseClass.DATA_SIM_POS: if (info == null) { - ScriptSleep(1000); + ScriptSleep(m_sleepMsOnRequestSimulatorData); return UUID.Zero.ToString(); } @@ -9402,7 +11780,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api case ScriptBaseClass.DATA_SIM_RATING: if (info == null) { - ScriptSleep(1000); + ScriptSleep(m_sleepMsOnRequestSimulatorData); return UUID.Zero.ToString(); } int access = info.Maturity; @@ -9421,7 +11799,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api reply = "OpenSim"; break; default: - ScriptSleep(1000); + ScriptSleep(m_sleepMsOnRequestSimulatorData); return UUID.Zero.ToString(); // Raise no event } UUID rq = UUID.Random(); @@ -9432,7 +11810,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api AsyncCommands. DataserverPlugin.DataserverReply(rq.ToString(), reply); - ScriptSleep(1000); + ScriptSleep(m_sleepMsOnRequestSimulatorData); return tid.ToString(); } catch(Exception) @@ -9441,6 +11819,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api return UUID.Zero.ToString(); } } + public LSL_String llRequestURL() { m_host.AddScriptLPS(1); @@ -9499,7 +11878,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api public LSL_List llListReplaceList(LSL_List dest, LSL_List src, int start, int end) { - LSL_List pref = null; + LSL_List pref; m_host.AddScriptLPS(1); @@ -9576,7 +11955,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api dm.SendUrlToUser( new UUID(avatar_id), m_host.Name, m_host.UUID, m_host.OwnerID, false, message, url); - ScriptSleep(10000); + ScriptSleep(m_sleepMsOnLoadURL); } public void llParcelMediaCommandList(LSL_List commandList) @@ -9588,7 +11967,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api // according to the docs, this command only works if script owner and land owner are the same // lets add estate owners and gods, too, and use the generic permission check. - ILandObject landObject = World.LandChannel.GetLandObject(m_host.AbsolutePosition.X, m_host.AbsolutePosition.Y); + ILandObject landObject = World.LandChannel.GetLandObject(m_host.AbsolutePosition); if (!World.Permissions.CanEditParcelProperties(m_host.OwnerID, landObject, GroupPowers.ChangeMedia)) return; bool update = false; // send a ParcelMediaUpdate (and possibly change the land's media URL)? @@ -9625,7 +12004,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api presence = World.GetScenePresence(agentID); } } - else ShoutError("The argument of PARCEL_MEDIA_COMMAND_AGENT must be a key"); + else Error("llParcelMediaCommandList", "The argument of PARCEL_MEDIA_COMMAND_AGENT must be a key"); ++i; } break; @@ -9656,7 +12035,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api url = (LSL_String)commandList.Data[i + 1]; update = true; } - else ShoutError("The argument of PARCEL_MEDIA_COMMAND_URL must be a string."); + else Error("llParcelMediaCommandList", "The argument of PARCEL_MEDIA_COMMAND_URL must be a string"); ++i; } break; @@ -9669,7 +12048,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api texture = (LSL_String)commandList.Data[i + 1]; update = true; } - else ShoutError("The argument of PARCEL_MEDIA_COMMAND_TEXTURE must be a string or key."); + else Error("llParcelMediaCommandList", "The argument of PARCEL_MEDIA_COMMAND_TEXTURE must be a string or a key"); ++i; } break; @@ -9681,7 +12060,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api { time = (float)(LSL_Float)commandList.Data[i + 1]; } - else ShoutError("The argument of PARCEL_MEDIA_COMMAND_TIME must be a float."); + else Error("llParcelMediaCommandList", "The argument of PARCEL_MEDIA_COMMAND_TIME must be a float"); ++i; } break; @@ -9695,7 +12074,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api update = true; } - else ShoutError("The argument of PARCEL_MEDIA_COMMAND_AUTO_ALIGN must be an integer."); + else Error("llParcelMediaCommandList", "The argument of PARCEL_MEDIA_COMMAND_AUTO_ALIGN must be an integer"); ++i; } break; @@ -9708,7 +12087,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api mediaType = (LSL_String)commandList.Data[i + 1]; update = true; } - else ShoutError("The argument of PARCEL_MEDIA_COMMAND_TYPE must be a string."); + else Error("llParcelMediaCommandList", "The argument of PARCEL_MEDIA_COMMAND_TYPE must be a string"); ++i; } break; @@ -9721,7 +12100,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api description = (LSL_String)commandList.Data[i + 1]; update = true; } - else ShoutError("The argument of PARCEL_MEDIA_COMMAND_DESC must be a string."); + else Error("llParcelMediaCommandList", "The argument of PARCEL_MEDIA_COMMAND_DESC must be a string"); ++i; } break; @@ -9737,15 +12116,15 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api height = (LSL_Integer)commandList.Data[i + 2]; update = true; } - else ShoutError("The second argument of PARCEL_MEDIA_COMMAND_SIZE must be an integer."); + else Error("llParcelMediaCommandList", "The second argument of PARCEL_MEDIA_COMMAND_SIZE must be an integer"); } - else ShoutError("The first argument of PARCEL_MEDIA_COMMAND_SIZE must be an integer."); + else Error("llParcelMediaCommandList", "The first argument of PARCEL_MEDIA_COMMAND_SIZE must be an integer"); i += 2; } break; default: - NotImplemented("llParcelMediaCommandList parameter not supported yet: " + Enum.Parse(typeof(ParcelMediaCommandEnum), commandList.Data[i].ToString()).ToString()); + NotImplemented("llParcelMediaCommandList", "Parameter not supported yet: " + Enum.Parse(typeof(ParcelMediaCommandEnum), commandList.Data[i].ToString()).ToString()); break; }//end switch }//end for @@ -9819,7 +12198,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api time); } } - ScriptSleep(2000); + ScriptSleep(m_sleepMsOnParcelMediaCommandList); } public LSL_List llParcelMediaQuery(LSL_List aList) @@ -9853,13 +12232,13 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api break; default: ParcelMediaCommandEnum mediaCommandEnum = ParcelMediaCommandEnum.Url; - NotImplemented("llParcelMediaQuery parameter do not supported yet: " + Enum.Parse(mediaCommandEnum.GetType() , aList.Data[i].ToString()).ToString()); + NotImplemented("llParcelMediaQuery", "Parameter not supported yet: " + Enum.Parse(mediaCommandEnum.GetType() , aList.Data[i].ToString()).ToString()); break; } } } - ScriptSleep(2000); + ScriptSleep(m_sleepMsOnParcelMediaQuery); return list; } @@ -9868,7 +12247,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api m_host.AddScriptLPS(1); Int64 tmp = 0; Math.DivRem(Convert.ToInt64(Math.Pow(a, b)), c, out tmp); - ScriptSleep(1000); + ScriptSleep(m_sleepMsOnModPow); return Convert.ToInt32(tmp); } @@ -9890,7 +12269,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api if (quick_pay_buttons.Data.Length < 4) { - LSLError("List must have at least 4 elements"); + Error("llSetPayPrice", "List must have at least 4 elements"); return; } m_host.ParentGroup.RootPart.PayPrice[0]=price; @@ -9907,21 +12286,22 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api m_host.AddScriptLPS(1); if (m_item.PermsGranter == UUID.Zero) - return new LSL_Vector(); + return Vector3.Zero; if ((m_item.PermsMask & ScriptBaseClass.PERMISSION_TRACK_CAMERA) == 0) { - ShoutError("No permissions to track the camera"); - return new LSL_Vector(); + Error("llGetCameraPos", "No permissions to track the camera"); + return Vector3.Zero; } ScenePresence presence = World.GetScenePresence(m_host.OwnerID); if (presence != null) { - LSL_Vector pos = new LSL_Vector(presence.CameraPosition.X, presence.CameraPosition.Y, presence.CameraPosition.Z); + LSL_Vector pos = new LSL_Vector(presence.CameraPosition); return pos; } - return new LSL_Vector(); + + return Vector3.Zero; } public LSL_Rotation llGetCameraRot() @@ -9929,42 +12309,35 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api m_host.AddScriptLPS(1); if (m_item.PermsGranter == UUID.Zero) - return new LSL_Rotation(); + return Quaternion.Identity; if ((m_item.PermsMask & ScriptBaseClass.PERMISSION_TRACK_CAMERA) == 0) { - ShoutError("No permissions to track the camera"); - return new LSL_Rotation(); + Error("llGetCameraRot", "No permissions to track the camera"); + return Quaternion.Identity; } ScenePresence presence = World.GetScenePresence(m_host.OwnerID); if (presence != null) { - return new LSL_Rotation(presence.CameraRotation.X, presence.CameraRotation.Y, presence.CameraRotation.Z, presence.CameraRotation.W); + return new LSL_Rotation(presence.CameraRotation); } - return new LSL_Rotation(); + return Quaternion.Identity; } - /// - /// The SL implementation does nothing, it is deprecated - /// This duplicates SL - /// public void llSetPrimURL(string url) { m_host.AddScriptLPS(1); - ScriptSleep(2000); + Deprecated("llSetPrimURL", "Use llSetPrimMediaParams instead"); + ScriptSleep(m_sleepMsOnSetPrimURL); } - /// - /// The SL implementation shouts an error, it is deprecated - /// This duplicates SL - /// public void llRefreshPrimURL() { m_host.AddScriptLPS(1); - ShoutError("llRefreshPrimURL - not yet supported"); - ScriptSleep(20000); + Deprecated("llRefreshPrimURL"); + ScriptSleep(m_sleepMsOnRefreshPrimURL); } public LSL_String llEscapeURL(string url) @@ -10005,14 +12378,14 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api avatar.ControllingClient.SendScriptTeleportRequest(m_host.Name, simname, pos, lookAt); } - ScriptSleep(1000); + ScriptSleep(m_sleepMsOnMapDestination); } public void llAddToLandBanList(string avatar, double hours) { m_host.AddScriptLPS(1); UUID key; - ILandObject land = World.LandChannel.GetLandObject(m_host.AbsolutePosition.X, m_host.AbsolutePosition.Y); + ILandObject land = World.LandChannel.GetLandObject(m_host.AbsolutePosition); if (World.Permissions.CanEditParcelProperties(m_host.OwnerID, land, GroupPowers.LandManageBanned)) { int expires = 0; @@ -10046,14 +12419,14 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api World.EventManager.TriggerLandObjectUpdated((uint)land.LandData.LocalID, land); } } - ScriptSleep(100); + ScriptSleep(m_sleepMsOnAddToLandBanList); } public void llRemoveFromLandPassList(string avatar) { m_host.AddScriptLPS(1); UUID key; - ILandObject land = World.LandChannel.GetLandObject(m_host.AbsolutePosition.X, m_host.AbsolutePosition.Y); + ILandObject land = World.LandChannel.GetLandObject(m_host.AbsolutePosition); if (World.Permissions.CanEditParcelProperties(m_host.OwnerID, land, GroupPowers.LandManageAllowed)) { if (UUID.TryParse(avatar, out key)) @@ -10073,14 +12446,14 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api } } } - ScriptSleep(100); + ScriptSleep(m_sleepMsOnRemoveFromLandPassList); } public void llRemoveFromLandBanList(string avatar) { m_host.AddScriptLPS(1); UUID key; - ILandObject land = World.LandChannel.GetLandObject(m_host.AbsolutePosition.X, m_host.AbsolutePosition.Y); + ILandObject land = World.LandChannel.GetLandObject(m_host.AbsolutePosition); if (World.Permissions.CanEditParcelProperties(m_host.OwnerID, land, GroupPowers.LandManageBanned)) { if (UUID.TryParse(avatar, out key)) @@ -10100,7 +12473,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api } } } - ScriptSleep(100); + ScriptSleep(m_sleepMsOnRemoveFromLandBanList); } public void llSetCameraParams(LSL_List rules) @@ -10128,19 +12501,84 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api SortedDictionary parameters = new SortedDictionary(); object[] data = rules.Data; - for (int i = 0; i < data.Length; ++i) { - int type = Convert.ToInt32(data[i++].ToString()); + for (int i = 0; i < data.Length; ++i) + { + int type; + try + { + type = Convert.ToInt32(data[i++].ToString()); + } + catch + { + Error("llSetCameraParams", string.Format("Invalid camera param type {0}", data[i - 1])); + return; + } if (i >= data.Length) break; // odd number of entries => ignore the last // some special cases: Vector parameters are split into 3 float parameters (with type+1, type+2, type+3) - switch (type) { + switch (type) + { case ScriptBaseClass.CAMERA_FOCUS: case ScriptBaseClass.CAMERA_FOCUS_OFFSET: case ScriptBaseClass.CAMERA_POSITION: LSL_Vector v = (LSL_Vector)data[i]; - parameters.Add(type + 1, (float)v.x); - parameters.Add(type + 2, (float)v.y); - parameters.Add(type + 3, (float)v.z); + try + { + parameters.Add(type + 1, (float)v.x); + } + catch + { + switch(type) + { + case ScriptBaseClass.CAMERA_FOCUS: + Error("llSetCameraParams", "CAMERA_FOCUS: Parameter x is invalid"); + return; + case ScriptBaseClass.CAMERA_FOCUS_OFFSET: + Error("llSetCameraParams", "CAMERA_FOCUS_OFFSET: Parameter x is invalid"); + return; + case ScriptBaseClass.CAMERA_POSITION: + Error("llSetCameraParams", "CAMERA_POSITION: Parameter x is invalid"); + return; + } + } + try + { + parameters.Add(type + 2, (float)v.y); + } + catch + { + switch(type) + { + case ScriptBaseClass.CAMERA_FOCUS: + Error("llSetCameraParams", "CAMERA_FOCUS: Parameter y is invalid"); + return; + case ScriptBaseClass.CAMERA_FOCUS_OFFSET: + Error("llSetCameraParams", "CAMERA_FOCUS_OFFSET: Parameter y is invalid"); + return; + case ScriptBaseClass.CAMERA_POSITION: + Error("llSetCameraParams", "CAMERA_POSITION: Parameter y is invalid"); + return; + } + } + try + { + parameters.Add(type + 3, (float)v.z); + } + catch + { + switch(type) + { + case ScriptBaseClass.CAMERA_FOCUS: + Error("llSetCameraParams", "CAMERA_FOCUS: Parameter z is invalid"); + return; + case ScriptBaseClass.CAMERA_FOCUS_OFFSET: + Error("llSetCameraParams", "CAMERA_FOCUS_OFFSET: Parameter z is invalid"); + return; + case ScriptBaseClass.CAMERA_POSITION: + Error("llSetCameraParams", "CAMERA_POSITION: Parameter z is invalid"); + return; + } + } break; default: // TODO: clean that up as soon as the implicit casts are in @@ -10148,7 +12586,17 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api parameters.Add(type, (float)((LSL_Float)data[i]).value); else if (data[i] is LSL_Integer) parameters.Add(type, (float)((LSL_Integer)data[i]).value); - else parameters.Add(type, Convert.ToSingle(data[i])); + else + { + try + { + parameters.Add(type, Convert.ToSingle(data[i])); + } + catch + { + Error("llSetCameraParams", string.Format("{0}: Parameter is invalid", type)); + } + } break; } } @@ -10264,9 +12712,60 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api IHttpRequestModule httpScriptMod = m_ScriptEngine.World.RequestModuleInterface(); List param = new List(); - foreach (object o in parameters.Data) + bool ok; + Int32 flag; + + for (int i = 0; i < parameters.Data.Length; i += 2) { - param.Add(o.ToString()); + ok = Int32.TryParse(parameters.Data[i].ToString(), out flag); + if (!ok || flag < 0 || + flag > (int)HttpRequestConstants.HTTP_PRAGMA_NO_CACHE) + { + Error("llHTTPRequest", "Parameter " + i.ToString() + " is an invalid flag"); + } + + param.Add(parameters.Data[i].ToString()); //Add parameter flag + + if (flag != (int)HttpRequestConstants.HTTP_CUSTOM_HEADER) + { + param.Add(parameters.Data[i+1].ToString()); //Add parameter value + } + else + { + //Parameters are in pairs and custom header takes + //arguments in pairs so adjust for header marker. + ++i; + + //Maximum of 8 headers are allowed based on the + //Second Life documentation for llHTTPRequest. + for (int count = 1; count <= 8; ++count) + { + //Enough parameters remaining for (another) header? + if (parameters.Data.Length - i < 2) + { + //There must be at least one name/value pair for custom header + if (count == 1) + Error("llHTTPRequest", "Missing name/value for custom header at parameter " + i.ToString()); + break; + } + + if (HttpStandardHeaders.Contains(parameters.Data[i].ToString(), StringComparer.OrdinalIgnoreCase)) + Error("llHTTPRequest", "Name is invalid as a custom header at parameter " + i.ToString()); + + param.Add(parameters.Data[i].ToString()); + param.Add(parameters.Data[i+1].ToString()); + + //Have we reached the end of the list of headers? + //End is marked by a string with a single digit. + if (i+2 >= parameters.Data.Length || + Char.IsDigit(parameters.Data[i].ToString()[0])) + { + break; + } + + i += 2; + } + } } Vector3 position = m_host.AbsolutePosition; @@ -10318,8 +12817,12 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api } } + HttpInitialRequestStatus status; UUID reqID - = httpScriptMod.StartHttpRequest(m_host.LocalId, m_item.ItemID, url, param, httpHeaders, body); + = httpScriptMod.StartHttpRequest(m_host.LocalId, m_item.ItemID, url, param, httpHeaders, body, out status); + + if (status == HttpInitialRequestStatus.DISALLOWED_BY_FILTER) + Error("llHttpRequest", string.Format("Request to {0} disallowed by filter", url)); if (reqID != UUID.Zero) return reqID.ToString(); @@ -10342,7 +12845,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api public void llResetLandBanList() { m_host.AddScriptLPS(1); - LandData land = World.LandChannel.GetLandObject(m_host.AbsolutePosition.X, m_host.AbsolutePosition.Y).LandData; + LandData land = World.LandChannel.GetLandObject(m_host.AbsolutePosition).LandData; if (land.OwnerID == m_host.OwnerID) { foreach (LandAccessEntry entry in land.ParcelAccessList) @@ -10353,13 +12856,13 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api } } } - ScriptSleep(100); + ScriptSleep(m_sleepMsOnResetLandBanList); } public void llResetLandPassList() { m_host.AddScriptLPS(1); - LandData land = World.LandChannel.GetLandObject(m_host.AbsolutePosition.X, m_host.AbsolutePosition.Y).LandData; + LandData land = World.LandChannel.GetLandObject(m_host.AbsolutePosition).LandData; if (land.OwnerID == m_host.OwnerID) { foreach (LandAccessEntry entry in land.ParcelAccessList) @@ -10370,18 +12873,18 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api } } } - ScriptSleep(100); + ScriptSleep(m_sleepMsOnResetLandPassList); } public LSL_Integer llGetParcelPrimCount(LSL_Vector pos, int category, int sim_wide) { m_host.AddScriptLPS(1); - + ILandObject lo = World.LandChannel.GetLandObject((float)pos.x, (float)pos.y); if (lo == null) return 0; - + IPrimCounts pc = lo.PrimCounts; if (sim_wide != ScriptBaseClass.FALSE) @@ -10411,7 +12914,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api else if (category == ScriptBaseClass.PARCEL_COUNT_TEMP) return 0; // counts not implemented yet } - + return 0; } @@ -10428,7 +12931,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api ret.Add(new LSL_Integer(detectedParams.Value)); } } - ScriptSleep(2000); + ScriptSleep(m_sleepMsOnGetParcelPrimOwners); return ret; } @@ -10535,10 +13038,10 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api ret.Add(new LSL_Vector((double)av.AbsolutePosition.X, (double)av.AbsolutePosition.Y, (double)av.AbsolutePosition.Z)); break; case ScriptBaseClass.OBJECT_ROT: - ret.Add(new LSL_Rotation((double)av.Rotation.X, (double)av.Rotation.Y, (double)av.Rotation.Z, (double)av.Rotation.W)); + ret.Add(new LSL_Rotation(av.GetWorldRotation())); break; case ScriptBaseClass.OBJECT_VELOCITY: - ret.Add(new LSL_Vector(av.Velocity.X, av.Velocity.Y, av.Velocity.Z)); + ret.Add(new LSL_Vector(av.GetWorldVelocity())); break; case ScriptBaseClass.OBJECT_OWNER: ret.Add(new LSL_String(id)); @@ -10603,6 +13106,23 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api case ScriptBaseClass.OBJECT_TEMP_ON_REZ: ret.Add(new LSL_Integer(0)); break; + case ScriptBaseClass.OBJECT_RENDER_WEIGHT: + ret.Add(new LSL_Integer(-1)); + break; + case ScriptBaseClass.OBJECT_HOVER_HEIGHT: + ret.Add(new LSL_Float(0)); + break; + case ScriptBaseClass.OBJECT_BODY_SHAPE_TYPE: + LSL_Float shapeType; + if (av.Appearance.VisualParams[(int)AvatarAppearance.VPElement.SHAPE_MALE] != 0) + shapeType = new LSL_Float(1); + else + shapeType = new LSL_Float(0); + ret.Add(shapeType); + break; + case ScriptBaseClass.OBJECT_LAST_OWNER_ID: + ret.Add(new LSL_Key(ScriptBaseClass.NULL_KEY)); + break; default: // Invalid or unhandled constant. ret.Add(new LSL_Integer(ScriptBaseClass.OBJECT_UNKNOWN_DETAIL)); @@ -10630,20 +13150,43 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api ret.Add(new LSL_Vector(obj.AbsolutePosition.X, obj.AbsolutePosition.Y, obj.AbsolutePosition.Z)); break; case ScriptBaseClass.OBJECT_ROT: + Quaternion rot = Quaternion.Identity; + + if (obj.ParentGroup.IsAttachment) { - Quaternion rot = Quaternion.Identity; + ScenePresence sp = World.GetScenePresence(obj.ParentGroup.AttachedAvatar); + if (sp != null) + rot = sp.GetWorldRotation(); + } + else + { if (obj.ParentGroup.RootPart == obj) rot = obj.ParentGroup.GroupRotation; else rot = obj.GetWorldRotation(); - - LSL_Rotation objrot = new LSL_Rotation(rot); - ret.Add(objrot); } + + LSL_Rotation objrot = new LSL_Rotation(rot); + ret.Add(objrot); + break; case ScriptBaseClass.OBJECT_VELOCITY: - ret.Add(new LSL_Vector(obj.Velocity)); + Vector3 vel = Vector3.Zero; + + if (obj.ParentGroup.IsAttachment) + { + ScenePresence sp = World.GetScenePresence(obj.ParentGroup.AttachedAvatar); + + if (sp != null) + vel = sp.GetWorldVelocity(); + } + else + { + vel = obj.Velocity; + } + + ret.Add(vel); break; case ScriptBaseClass.OBJECT_OWNER: ret.Add(new LSL_String(obj.OwnerID.ToString())); @@ -10744,6 +13287,18 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api case ScriptBaseClass.OBJECT_TEMP_ON_REZ: ret.Add(new LSL_Integer(obj.ParentGroup.IsTemporary ? 1 : 0)); break; + case ScriptBaseClass.OBJECT_RENDER_WEIGHT: + ret.Add(new LSL_Integer(0)); + break; + case ScriptBaseClass.OBJECT_HOVER_HEIGHT: + ret.Add(new LSL_Float(0)); + break; + case ScriptBaseClass.OBJECT_BODY_SHAPE_TYPE: + ret.Add(new LSL_Float(-1)); + break; + case ScriptBaseClass.OBJECT_LAST_OWNER_ID: + ret.Add(new LSL_Key(obj.ParentGroup.LastOwnerID.ToString())); + break; default: // Invalid or unhandled constant. ret.Add(new LSL_Integer(ScriptBaseClass.OBJECT_UNKNOWN_DETAIL)); @@ -10754,7 +13309,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api return ret; } } - + return new LSL_List(); } @@ -10768,25 +13323,71 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api return item.ItemID; } - internal void ShoutError(string msg) + /// + /// Reports the script error in the viewer's Script Warning/Error dialog and shouts it on the debug channel. + /// + /// The name of the command that generated the error. + /// The error message to report to the user. + internal void Error(string command, string message) { - llShout(ScriptBaseClass.DEBUG_CHANNEL, msg); + string text = command + ": " + message; + if (text.Length > 1023) + { + text = text.Substring(0, 1023); + } + + World.SimChat(Utils.StringToBytes(text), ChatTypeEnum.DebugChannel, ScriptBaseClass.DEBUG_CHANNEL, + m_host.ParentGroup.RootPart.AbsolutePosition, m_host.Name, m_host.UUID, false); + + IWorldComm wComm = m_ScriptEngine.World.RequestModuleInterface(); + if (wComm != null) + { + wComm.DeliverMessage(ChatTypeEnum.Shout, ScriptBaseClass.DEBUG_CHANNEL, m_host.Name, m_host.UUID, text); + } } - internal void NotImplemented(string command) + /// + /// Reports that the command is not implemented as a script error. + /// + /// The name of the command that is not implemented. + /// Additional information to report to the user. (Optional) + internal void NotImplemented(string command, string message = "") { if (throwErrorOnNotImplemented) - throw new NotImplementedException("Command not implemented: " + command); - } + { + if (message != "") + { + message = " - " + message; + } - internal void Deprecated(string command) - { - throw new ScriptException("Command deprecated: " + command); + throw new NotImplementedException("Command not implemented: " + command + message); + } + else + { + string text = "Command not implemented"; + if (message != "") + { + text = text + " - " + message; + } + + Error(command, text); + } } - internal void LSLError(string msg) + /// + /// Reports that the command is deprecated as a script error. + /// + /// The name of the command that is deprecated. + /// Additional information to report to the user. (Optional) + internal void Deprecated(string command, string message = "") { - throw new ScriptException("LSL Runtime Error: " + msg); + string text = "Command deprecated"; + if (message != "") + { + text = text + " - " + message; + } + + Error(command, text); } public delegate void AssetRequestCallback(UUID assetID, AssetBase asset); @@ -10818,20 +13419,21 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api if (assetID == UUID.Zero) { // => complain loudly, as specified by the LSL docs - ShoutError("Notecard '" + name + "' could not be found."); + Error("llGetNumberOfNotecardLines", "Can't find notecard '" + name + "'"); return UUID.Zero.ToString(); } + string reqIdentifier = UUID.Random().ToString(); + // was: UUID tid = tid = AsyncCommands. - UUID tid = AsyncCommands.DataserverPlugin.RegisterRequest(m_host.LocalId, m_item.ItemID, assetID.ToString()); + UUID tid = AsyncCommands.DataserverPlugin.RegisterRequest(m_host.LocalId, m_item.ItemID, reqIdentifier); if (NotecardCache.IsCached(assetID)) { - AsyncCommands. - DataserverPlugin.DataserverReply(assetID.ToString(), - NotecardCache.GetLines(assetID).ToString()); - ScriptSleep(100); + AsyncCommands.DataserverPlugin.DataserverReply(reqIdentifier, NotecardCache.GetLines(assetID).ToString()); + + ScriptSleep(m_sleepMsOnGetNumberOfNotecardLines); return tid.ToString(); } @@ -10839,19 +13441,15 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api { if (a == null || a.Type != 7) { - ShoutError("Notecard '" + name + "' could not be found."); + Error("llGetNumberOfNotecardLines", "Can't find notecard '" + name + "'"); return; } - string data = Encoding.UTF8.GetString(a.Data); - //m_log.Debug(data); - NotecardCache.Cache(id, data); - AsyncCommands. - DataserverPlugin.DataserverReply(id.ToString(), - NotecardCache.GetLines(id).ToString()); + NotecardCache.Cache(id, a.Data); + AsyncCommands.DataserverPlugin.DataserverReply(reqIdentifier, NotecardCache.GetLines(id).ToString()); }); - ScriptSleep(100); + ScriptSleep(m_sleepMsOnGetNumberOfNotecardLines); return tid.ToString(); } @@ -10872,19 +13470,22 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api if (assetID == UUID.Zero) { // => complain loudly, as specified by the LSL docs - ShoutError("Notecard '" + name + "' could not be found."); + Error("llGetNotecardLine", "Can't find notecard '" + name + "'"); return UUID.Zero.ToString(); } + string reqIdentifier = UUID.Random().ToString(); + // was: UUID tid = tid = AsyncCommands. - UUID tid = AsyncCommands.DataserverPlugin.RegisterRequest(m_host.LocalId, m_item.ItemID, assetID.ToString()); + UUID tid = AsyncCommands.DataserverPlugin.RegisterRequest(m_host.LocalId, m_item.ItemID, reqIdentifier); if (NotecardCache.IsCached(assetID)) { - AsyncCommands.DataserverPlugin.DataserverReply(assetID.ToString(), - NotecardCache.GetLine(assetID, line, m_notecardLineReadCharsMax)); - ScriptSleep(100); + AsyncCommands.DataserverPlugin.DataserverReply( + reqIdentifier, NotecardCache.GetLine(assetID, line, m_notecardLineReadCharsMax)); + + ScriptSleep(m_sleepMsOnGetNotecardLine); return tid.ToString(); } @@ -10892,18 +13493,18 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api { if (a == null || a.Type != 7) { - ShoutError("Notecard '" + name + "' could not be found."); + Error("llGetNotecardLine", "Can't find notecard '" + name + "'"); return; } string data = Encoding.UTF8.GetString(a.Data); //m_log.Debug(data); - NotecardCache.Cache(id, data); - AsyncCommands.DataserverPlugin.DataserverReply(id.ToString(), - NotecardCache.GetLine(id, line, m_notecardLineReadCharsMax)); + NotecardCache.Cache(id, a.Data); + AsyncCommands.DataserverPlugin.DataserverReply( + reqIdentifier, NotecardCache.GetLine(assetID, line, m_notecardLineReadCharsMax)); }); - ScriptSleep(100); + ScriptSleep(m_sleepMsOnGetNotecardLine); return tid.ToString(); } @@ -10916,41 +13517,17 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api if (obj.OwnerID != m_host.OwnerID) return; - uint rulesParsed = 0; - LSL_List remaining = SetPrimParams(obj, rules, originFunc, ref rulesParsed); - - while ((object)remaining != null && remaining.Length > 2) - { - LSL_Integer newLink = remaining.GetLSLIntegerItem(0); - LSL_List newrules = remaining.GetSublist(1, -1); - foreach(SceneObjectPart part in GetLinkParts(obj, newLink)){ - remaining = SetPrimParams(part, newrules, originFunc, ref rulesParsed); - } - } - } - - public LSL_List GetPrimitiveParamsEx(LSL_Key prim, LSL_List rules) - { - SceneObjectPart obj = World.GetSceneObjectPart(new UUID(prim)); - - LSL_List result = new LSL_List(); - - if (obj != null && obj.OwnerID != m_host.OwnerID) - { - LSL_List remaining = GetPrimParams(obj, rules, ref result); + SetEntityParams(new List() { obj }, rules, originFunc); + } - while (remaining != null && remaining.Length > 2) - { - int linknumber = remaining.GetLSLIntegerItem(0); - rules = remaining.GetSublist(1, -1); - List parts = GetLinkParts(linknumber); + public LSL_List GetPrimitiveParamsEx(LSL_Key prim, LSL_List rules) + { + SceneObjectPart obj = World.GetSceneObjectPart(new UUID(prim)); - foreach (SceneObjectPart part in parts) - remaining = GetPrimParams(part, rules, ref result); - } - } + if (obj != null && obj.OwnerID == m_host.OwnerID) + return GetEntityParams(obj, rules); - return result; + return new LSL_List(); } public void print(string str) @@ -11036,7 +13613,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api World.ForEachScenePresence(delegate(ScenePresence sp) { Vector3 ac = sp.AbsolutePosition - rayStart; - Vector3 bc = sp.AbsolutePosition - rayEnd; +// Vector3 bc = sp.AbsolutePosition - rayEnd; double d = Math.Abs(Vector3.Mag(Vector3.Cross(ab, ac)) / Vector3.Distance(rayStart, rayEnd)); @@ -11124,9 +13701,9 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api radius = Math.Abs(maxY); if (Math.Abs(maxZ) > radius) radius = Math.Abs(maxZ); - + radius = radius*1.413f; Vector3 ac = group.AbsolutePosition - rayStart; - Vector3 bc = group.AbsolutePosition - rayEnd; +// Vector3 bc = group.AbsolutePosition - rayEnd; double d = Math.Abs(Vector3.Mag(Vector3.Cross(ab, ac)) / Vector3.Distance(rayStart, rayEnd)); @@ -11139,11 +13716,20 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api if (d2 > 0) return; + ray = new Ray(rayStart, Vector3.Normalize(rayEnd - rayStart)); EntityIntersection intersection = group.TestIntersection(ray, true, false); // Miss. if (!intersection.HitTF) return; + Vector3 b1 = group.AbsolutePosition + new Vector3(minX, minY, minZ); + Vector3 b2 = group.AbsolutePosition + new Vector3(maxX, maxY, maxZ); + //m_log.DebugFormat("[LLCASTRAY]: min<{0},{1},{2}>, max<{3},{4},{5}> = hitp<{6},{7},{8}>", b1.X,b1.Y,b1.Z,b2.X,b2.Y,b2.Z,intersection.ipoint.X,intersection.ipoint.Y,intersection.ipoint.Z); + if (!(intersection.ipoint.X >= b1.X && intersection.ipoint.X <= b2.X && + intersection.ipoint.Y >= b1.Y && intersection.ipoint.Y <= b2.Y && + intersection.ipoint.Z >= b1.Z && intersection.ipoint.Z <= b2.Z)) + return; + ContactResult result = new ContactResult (); result.ConsumerID = group.LocalId; result.Depth = intersection.distance; @@ -11254,155 +13840,1093 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api float wv = Vector3.Dot(w, v); float d = uv * uv - uu * vv; - float cs = (uv * wv - vv * wu) / d; - if (cs < 0 || cs > 1.0) - continue; - float ct = (uv * wu - uu * wv) / d; - if (ct < 0 || (cs + ct) > 1.0) - continue; + float cs = (uv * wv - vv * wu) / d; + if (cs < 0 || cs > 1.0) + continue; + float ct = (uv * wu - uu * wv) / d; + if (ct < 0 || (cs + ct) > 1.0) + continue; + + // Add contact point + ContactResult result = new ContactResult (); + result.ConsumerID = 0; + result.Depth = Vector3.Distance(rayStart, ip); + result.Normal = n; + result.Pos = ip; + + contacts.Add(result); + } + + if (contacts.Count == 0) + return null; + + contacts.Sort(delegate(ContactResult a, ContactResult b) + { + return (int)(a.Depth - b.Depth); + }); + + return contacts[0]; + } + + public LSL_List llCastRay(LSL_Vector start, LSL_Vector end, LSL_List options) + { + // Use llCastRay V3 if configured + if (m_useCastRayV3) + return llCastRayV3(start, end, options); + + LSL_List list = new LSL_List(); + + m_host.AddScriptLPS(1); + + Vector3 rayStart = start; + Vector3 rayEnd = end; + Vector3 dir = rayEnd - rayStart; + + float dist = Vector3.Mag(dir); + + int count = 1; + bool detectPhantom = false; + int dataFlags = 0; + int rejectTypes = 0; + + for (int i = 0; i < options.Length; i += 2) + { + if (options.GetLSLIntegerItem(i) == ScriptBaseClass.RC_MAX_HITS) + count = options.GetLSLIntegerItem(i + 1); + else if (options.GetLSLIntegerItem(i) == ScriptBaseClass.RC_DETECT_PHANTOM) + detectPhantom = (options.GetLSLIntegerItem(i + 1) > 0); + else if (options.GetLSLIntegerItem(i) == ScriptBaseClass.RC_DATA_FLAGS) + dataFlags = options.GetLSLIntegerItem(i + 1); + else if (options.GetLSLIntegerItem(i) == ScriptBaseClass.RC_REJECT_TYPES) + rejectTypes = options.GetLSLIntegerItem(i + 1); + } + + if (count > 16) + count = 16; + + List results = new List(); + + bool checkTerrain = !((rejectTypes & ScriptBaseClass.RC_REJECT_LAND) == ScriptBaseClass.RC_REJECT_LAND); + bool checkAgents = !((rejectTypes & ScriptBaseClass.RC_REJECT_AGENTS) == ScriptBaseClass.RC_REJECT_AGENTS); + bool checkNonPhysical = !((rejectTypes & ScriptBaseClass.RC_REJECT_NONPHYSICAL) == ScriptBaseClass.RC_REJECT_NONPHYSICAL); + bool checkPhysical = !((rejectTypes & ScriptBaseClass.RC_REJECT_PHYSICAL) == ScriptBaseClass.RC_REJECT_PHYSICAL); + + + if (World.SupportsRayCastFiltered()) + { + if (dist == 0) + return list; + + RayFilterFlags rayfilter = RayFilterFlags.ClosestAndBackCull; + if (checkTerrain) + rayfilter |= RayFilterFlags.land; +// if (checkAgents) +// rayfilter |= RayFilterFlags.agent; + if (checkPhysical) + rayfilter |= RayFilterFlags.physical; + if (checkNonPhysical) + rayfilter |= RayFilterFlags.nonphysical; + if (detectPhantom) + rayfilter |= RayFilterFlags.LSLPhantom; + + Vector3 direction = dir * ( 1/dist); + + if(rayfilter == 0) + { + list.Add(new LSL_Integer(0)); + return list; + } + + // get some more contacts to sort ??? + int physcount = 4 * count; + if (physcount > 20) + physcount = 20; + + object physresults; + physresults = World.RayCastFiltered(rayStart, direction, dist, physcount, rayfilter); + + if (physresults == null) + { + list.Add(new LSL_Integer(-3)); // timeout error + return list; + } + + results = (List)physresults; + + // for now physics doesn't detect sitted avatars so do it outside physics + if (checkAgents) + { + ContactResult[] agentHits = AvatarIntersection(rayStart, rayEnd); + foreach (ContactResult r in agentHits) + results.Add(r); + } + + // TODO: Replace this with a better solution. ObjectIntersection can only + // detect nonphysical phantoms. They are detected by virtue of being + // nonphysical (e.g. no PhysActor) so will not conflict with detecting + // physicsl phantoms as done by the physics scene + // We don't want anything else but phantoms here. + if (detectPhantom) + { + ContactResult[] objectHits = ObjectIntersection(rayStart, rayEnd, false, false, true); + foreach (ContactResult r in objectHits) + results.Add(r); + } + } + else + { + if (checkAgents) + { + ContactResult[] agentHits = AvatarIntersection(rayStart, rayEnd); + foreach (ContactResult r in agentHits) + results.Add(r); + } + + if (checkPhysical || checkNonPhysical || detectPhantom) + { + ContactResult[] objectHits = ObjectIntersection(rayStart, rayEnd, checkPhysical, checkNonPhysical, detectPhantom); + for (int iter = 0; iter < objectHits.Length; iter++) + { + // Redistance the Depth because the Scene RayCaster returns distance from center to make the rezzing code simpler. + objectHits[iter].Depth = Vector3.Distance(objectHits[iter].Pos, rayStart); + results.Add(objectHits[iter]); + } + } + } + + if (checkTerrain) + { + ContactResult? groundContact = GroundIntersection(rayStart, rayEnd); + if (groundContact != null) + results.Add((ContactResult)groundContact); + } + + results.Sort(delegate(ContactResult a, ContactResult b) + { + return a.Depth.CompareTo(b.Depth); + }); + + int values = 0; + SceneObjectGroup thisgrp = m_host.ParentGroup; + + foreach (ContactResult result in results) + { + if (result.Depth > dist) + continue; + + // physics ray can return colisions with host prim + if (m_host.LocalId == result.ConsumerID) + continue; + + UUID itemID = UUID.Zero; + int linkNum = 0; + + SceneObjectPart part = World.GetSceneObjectPart(result.ConsumerID); + // It's a prim! + if (part != null) + { + // dont detect members of same object ??? + if (part.ParentGroup == thisgrp) + continue; + + if ((dataFlags & ScriptBaseClass.RC_GET_ROOT_KEY) == ScriptBaseClass.RC_GET_ROOT_KEY) + itemID = part.ParentGroup.UUID; + else + itemID = part.UUID; + + linkNum = part.LinkNum; + } + else + { + ScenePresence sp = World.GetScenePresence(result.ConsumerID); + /// It it a boy? a girl? + if (sp != null) + itemID = sp.UUID; + } + + list.Add(new LSL_String(itemID.ToString())); + list.Add(new LSL_String(result.Pos.ToString())); + + if ((dataFlags & ScriptBaseClass.RC_GET_LINK_NUM) == ScriptBaseClass.RC_GET_LINK_NUM) + list.Add(new LSL_Integer(linkNum)); + + if ((dataFlags & ScriptBaseClass.RC_GET_NORMAL) == ScriptBaseClass.RC_GET_NORMAL) + list.Add(new LSL_Vector(result.Normal)); + + values++; + if (values >= count) + break; + } + + list.Add(new LSL_Integer(values)); + + return list; + } + + /// + /// Implementation of llCastRay similar to SL 2015-04-21. + /// http://wiki.secondlife.com/wiki/LlCastRay + /// Uses pure geometry, bounding shapes, meshing and no physics + /// for prims, sculpts, meshes, avatars and terrain. + /// Implements all flags, reject types and data flags. + /// Can handle both objects/groups and prims/parts, by config. + /// May sometimes be inaccurate owing to calculation precision, + /// meshing detail level and a bug in libopenmetaverse PrimMesher. + /// + public LSL_List llCastRayV3(LSL_Vector start, LSL_Vector end, LSL_List options) + { + m_host.AddScriptLPS(1); + LSL_List result = new LSL_List(); + + // Prepare throttle data + int calledMs = Environment.TickCount; + Stopwatch stopWatch = new Stopwatch(); + stopWatch.Start(); + UUID regionId = World.RegionInfo.RegionID; + UUID userId = UUID.Zero; + int msAvailable = 0; + // Throttle per owner when attachment or "vehicle" (sat upon) + if (m_host.ParentGroup.IsAttachment || m_host.ParentGroup.GetSittingAvatars().Count > 0) + { + userId = m_host.OwnerID; + msAvailable = m_msPerAvatarInCastRay; + } + // Throttle per parcel when not attachment or vehicle + else + { + LandData land = World.GetLandData(m_host.GetWorldPosition()); + if (land != null) + msAvailable = m_msPerRegionInCastRay * land.Area / 65536; + } + // Clamp for "oversized" parcels on varregions + if (msAvailable > m_msMaxInCastRay) + msAvailable = m_msMaxInCastRay; + + // Check throttle data + int fromCalledMs = calledMs - m_msThrottleInCastRay; + lock (m_castRayCalls) + { + for (int i = m_castRayCalls.Count - 1; i >= 0; i--) + { + // Delete old calls from throttle data + if (m_castRayCalls[i].CalledMs < fromCalledMs) + m_castRayCalls.RemoveAt(i); + // Use current region (in multi-region sims) + else if (m_castRayCalls[i].RegionId == regionId) + { + // Reduce available time with recent calls + if (m_castRayCalls[i].UserId == userId) + msAvailable -= m_castRayCalls[i].UsedMs; + } + } + } + + // Return failure if not enough available time + if (msAvailable < m_msMinInCastRay) + { + result.Add(new LSL_Integer(ScriptBaseClass.RCERR_CAST_TIME_EXCEEDED)); + return result; + } + + // Initialize + List rayHits = new List(); + float tol = m_floatToleranceInCastRay; + Vector3 pos1Ray = start; + Vector3 pos2Ray = end; + + // Get input options + int rejectTypes = 0; + int dataFlags = 0; + int maxHits = 1; + bool detectPhantom = false; + for (int i = 0; i < options.Length; i += 2) + { + if (options.GetLSLIntegerItem(i) == ScriptBaseClass.RC_REJECT_TYPES) + rejectTypes = options.GetLSLIntegerItem(i + 1); + else if (options.GetLSLIntegerItem(i) == ScriptBaseClass.RC_DATA_FLAGS) + dataFlags = options.GetLSLIntegerItem(i + 1); + else if (options.GetLSLIntegerItem(i) == ScriptBaseClass.RC_MAX_HITS) + maxHits = options.GetLSLIntegerItem(i + 1); + else if (options.GetLSLIntegerItem(i) == ScriptBaseClass.RC_DETECT_PHANTOM) + detectPhantom = (options.GetLSLIntegerItem(i + 1) != 0); + } + if (maxHits > m_maxHitsInCastRay) + maxHits = m_maxHitsInCastRay; + bool rejectAgents = ((rejectTypes & ScriptBaseClass.RC_REJECT_AGENTS) != 0); + bool rejectPhysical = ((rejectTypes & ScriptBaseClass.RC_REJECT_PHYSICAL) != 0); + bool rejectNonphysical = ((rejectTypes & ScriptBaseClass.RC_REJECT_NONPHYSICAL) != 0); + bool rejectLand = ((rejectTypes & ScriptBaseClass.RC_REJECT_LAND) != 0); + bool getNormal = ((dataFlags & ScriptBaseClass.RC_GET_NORMAL) != 0); + bool getRootKey = ((dataFlags & ScriptBaseClass.RC_GET_ROOT_KEY) != 0); + bool getLinkNum = ((dataFlags & ScriptBaseClass.RC_GET_LINK_NUM) != 0); + + // Calculate some basic parameters + Vector3 vecRay = pos2Ray - pos1Ray; + float rayLength = vecRay.Length(); + + // Try to get a mesher and return failure if none, degenerate ray, or max 0 hits + IRendering primMesher = null; + List renderers = RenderingLoader.ListRenderers(Util.ExecutingDirectory()); + if (renderers.Count < 1 || rayLength < tol || m_maxHitsInCastRay < 1) + { + result.Add(new LSL_Integer(ScriptBaseClass.RCERR_UNKNOWN)); + return result; + } + primMesher = RenderingLoader.LoadRenderer(renderers[0]); + + // Iterate over all objects/groups and prims/parts in region + World.ForEachSOG( + delegate(SceneObjectGroup group) + { + // Check group filters unless part filters are configured + bool isPhysical = (group.RootPart != null && group.RootPart.PhysActor != null && group.RootPart.PhysActor.IsPhysical); + bool isNonphysical = !isPhysical; + bool isPhantom = group.IsPhantom || group.IsVolumeDetect; + bool isAttachment = group.IsAttachment; + bool doGroup = true; + if (isPhysical && rejectPhysical) + doGroup = false; + if (isNonphysical && rejectNonphysical) + doGroup = false; + if (isPhantom && detectPhantom) + doGroup = true; + if (m_filterPartsInCastRay) + doGroup = true; + if (isAttachment && !m_doAttachmentsInCastRay) + doGroup = false; + // Parse object/group if passed filters + if (doGroup) + { + // Iterate over all prims/parts in object/group + foreach(SceneObjectPart part in group.Parts) + { + // Check part filters if configured + if (m_filterPartsInCastRay) + { + isPhysical = (part.PhysActor != null && part.PhysActor.IsPhysical); + isNonphysical = !isPhysical; + isPhantom = ((part.Flags & PrimFlags.Phantom) != 0) || (part.VolumeDetectActive); + bool doPart = true; + if (isPhysical && rejectPhysical) + doPart = false; + if (isNonphysical && rejectNonphysical) + doPart = false; + if (isPhantom && detectPhantom) + doPart = true; + if (!doPart) + continue; + } + + // Parse prim/part and project ray if passed filters + Vector3 scalePart = part.Scale; + Vector3 posPart = part.GetWorldPosition(); + Quaternion rotPart = part.GetWorldRotation(); + Quaternion rotPartInv = Quaternion.Inverse(rotPart); + Vector3 pos1RayProj = ((pos1Ray - posPart) * rotPartInv) / scalePart; + Vector3 pos2RayProj = ((pos2Ray - posPart) * rotPartInv) / scalePart; + + // Filter parts by shape bounding boxes + Vector3 shapeBoxMax = new Vector3(0.5f, 0.5f, 0.5f); + if (!part.Shape.SculptEntry) + shapeBoxMax = shapeBoxMax * (new Vector3(m_primSafetyCoeffX, m_primSafetyCoeffY, m_primSafetyCoeffZ)); + shapeBoxMax = shapeBoxMax + (new Vector3(tol, tol, tol)); + if (RayIntersectsShapeBox(pos1RayProj, pos2RayProj, shapeBoxMax)) + { + // Prepare data needed to check for ray hits + RayTrans rayTrans = new RayTrans(); + rayTrans.PartId = part.UUID; + rayTrans.GroupId = part.ParentGroup.UUID; + rayTrans.Link = group.PrimCount > 1 ? part.LinkNum : 0; + rayTrans.ScalePart = scalePart; + rayTrans.PositionPart = posPart; + rayTrans.RotationPart = rotPart; + rayTrans.ShapeNeedsEnds = true; + rayTrans.Position1Ray = pos1Ray; + rayTrans.Position1RayProj = pos1RayProj; + rayTrans.VectorRayProj = pos2RayProj - pos1RayProj; + + // Get detail level depending on type + int lod = 0; + // Mesh detail level + if (part.Shape.SculptEntry && part.Shape.SculptType == (byte)SculptType.Mesh) + lod = (int)m_meshLodInCastRay; + // Sculpt detail level + else if (part.Shape.SculptEntry && part.Shape.SculptType == (byte)SculptType.Mesh) + lod = (int)m_sculptLodInCastRay; + // Shape detail level + else if (!part.Shape.SculptEntry) + lod = (int)m_primLodInCastRay; + + // Try to get cached mesh if configured + ulong meshKey = 0; + FacetedMesh mesh = null; + if (m_useMeshCacheInCastRay) + { + meshKey = part.Shape.GetMeshKey(Vector3.One, (float)(4 << lod)); + lock (m_cachedMeshes) + { + m_cachedMeshes.TryGetValue(meshKey, out mesh); + } + } + + // Create mesh if no cached mesh + if (mesh == null) + { + // Make an OMV prim to be able to mesh part + Primitive omvPrim = part.Shape.ToOmvPrimitive(posPart, rotPart); + byte[] sculptAsset = null; + if (omvPrim.Sculpt != null) + sculptAsset = World.AssetService.GetData(omvPrim.Sculpt.SculptTexture.ToString()); + + // When part is mesh, get mesh + if (omvPrim.Sculpt != null && omvPrim.Sculpt.Type == SculptType.Mesh && sculptAsset != null) + { + AssetMesh meshAsset = new AssetMesh(omvPrim.Sculpt.SculptTexture, sculptAsset); + FacetedMesh.TryDecodeFromAsset(omvPrim, meshAsset, m_meshLodInCastRay, out mesh); + meshAsset = null; + } + + // When part is sculpt, create mesh + // Quirk: Generated sculpt mesh is about 2.8% smaller in X and Y than visual sculpt. + else if (omvPrim.Sculpt != null && omvPrim.Sculpt.Type != SculptType.Mesh && sculptAsset != null) + { + IJ2KDecoder imgDecoder = World.RequestModuleInterface(); + if (imgDecoder != null) + { + Image sculpt = imgDecoder.DecodeToImage(sculptAsset); + if (sculpt != null) + { + mesh = primMesher.GenerateFacetedSculptMesh(omvPrim, (Bitmap)sculpt, m_sculptLodInCastRay); + sculpt.Dispose(); + } + } + } + + // When part is shape, create mesh + else if (omvPrim.Sculpt == null) + { + if ( + omvPrim.PrimData.PathBegin == 0.0 && omvPrim.PrimData.PathEnd == 1.0 && + omvPrim.PrimData.PathTaperX == 0.0 && omvPrim.PrimData.PathTaperY == 0.0 && + omvPrim.PrimData.PathSkew == 0.0 && + omvPrim.PrimData.PathTwist - omvPrim.PrimData.PathTwistBegin == 0.0 + ) + rayTrans.ShapeNeedsEnds = false; + mesh = primMesher.GenerateFacetedMesh(omvPrim, m_primLodInCastRay); + } + + // Cache mesh if configured + if (m_useMeshCacheInCastRay && mesh != null) + { + lock(m_cachedMeshes) + { + if (!m_cachedMeshes.ContainsKey(meshKey)) + m_cachedMeshes.Add(meshKey, mesh); + } + } + } + // Check mesh for ray hits + AddRayInFacetedMesh(mesh, rayTrans, ref rayHits); + mesh = null; + } + } + } + } + ); + + // Check avatar filter + if (!rejectAgents) + { + // Iterate over all avatars in region + World.ForEachRootScenePresence( + delegate (ScenePresence sp) + { + // Get bounding box + Vector3 lower; + Vector3 upper; + BoundingBoxOfScenePresence(sp, out lower, out upper); + // Parse avatar + Vector3 scalePart = upper - lower; + Vector3 posPart = sp.AbsolutePosition; + Quaternion rotPart = sp.GetWorldRotation(); + Quaternion rotPartInv = Quaternion.Inverse(rotPart); + posPart = posPart + (lower + upper) * 0.5f * rotPart; + // Project ray + Vector3 pos1RayProj = ((pos1Ray - posPart) * rotPartInv) / scalePart; + Vector3 pos2RayProj = ((pos2Ray - posPart) * rotPartInv) / scalePart; + + // Filter avatars by shape bounding boxes + Vector3 shapeBoxMax = new Vector3(0.5f + tol, 0.5f + tol, 0.5f + tol); + if (RayIntersectsShapeBox(pos1RayProj, pos2RayProj, shapeBoxMax)) + { + // Prepare data needed to check for ray hits + RayTrans rayTrans = new RayTrans(); + rayTrans.PartId = sp.UUID; + rayTrans.GroupId = sp.ParentPart != null ? sp.ParentPart.ParentGroup.UUID : sp.UUID; + rayTrans.Link = sp.ParentPart != null ? UUID2LinkNumber(sp.ParentPart, sp.UUID) : 0; + rayTrans.ScalePart = scalePart; + rayTrans.PositionPart = posPart; + rayTrans.RotationPart = rotPart; + rayTrans.ShapeNeedsEnds = false; + rayTrans.Position1Ray = pos1Ray; + rayTrans.Position1RayProj = pos1RayProj; + rayTrans.VectorRayProj = pos2RayProj - pos1RayProj; + + // Try to get cached mesh if configured + PrimitiveBaseShape prim = PrimitiveBaseShape.CreateSphere(); + int lod = (int)m_avatarLodInCastRay; + ulong meshKey = prim.GetMeshKey(Vector3.One, (float)(4 << lod)); + FacetedMesh mesh = null; + if (m_useMeshCacheInCastRay) + { + lock (m_cachedMeshes) + { + m_cachedMeshes.TryGetValue(meshKey, out mesh); + } + } + + // Create mesh if no cached mesh + if (mesh == null) + { + // Make OMV prim and create mesh + prim.Scale = scalePart; + Primitive omvPrim = prim.ToOmvPrimitive(posPart, rotPart); + mesh = primMesher.GenerateFacetedMesh(omvPrim, m_avatarLodInCastRay); + + // Cache mesh if configured + if (m_useMeshCacheInCastRay && mesh != null) + { + lock(m_cachedMeshes) + { + if (!m_cachedMeshes.ContainsKey(meshKey)) + m_cachedMeshes.Add(meshKey, mesh); + } + } + } + + // Check mesh for ray hits + AddRayInFacetedMesh(mesh, rayTrans, ref rayHits); + mesh = null; + } + } + ); + } + + // Check terrain filter + if (!rejectLand) + { + // Parse terrain + + // Mesh terrain and check bounding box + Vector3 lower; + Vector3 upper; + List triangles = TrisFromHeightmapUnderRay(pos1Ray, pos2Ray, out lower, out upper); + lower.Z -= tol; + upper.Z += tol; + if ((pos1Ray.Z >= lower.Z || pos2Ray.Z >= lower.Z) && (pos1Ray.Z <= upper.Z || pos2Ray.Z <= upper.Z)) + { + // Prepare data needed to check for ray hits + RayTrans rayTrans = new RayTrans(); + rayTrans.PartId = UUID.Zero; + rayTrans.GroupId = UUID.Zero; + rayTrans.Link = 0; + rayTrans.ScalePart = new Vector3 (1.0f, 1.0f, 1.0f); + rayTrans.PositionPart = Vector3.Zero; + rayTrans.RotationPart = Quaternion.Identity; + rayTrans.ShapeNeedsEnds = true; + rayTrans.Position1Ray = pos1Ray; + rayTrans.Position1RayProj = pos1Ray; + rayTrans.VectorRayProj = vecRay; + + // Check mesh + AddRayInTris(triangles, rayTrans, ref rayHits); + triangles = null; + } + } + + // Sort hits by ascending distance + rayHits.Sort((s1, s2) => s1.Distance.CompareTo(s2.Distance)); - // Add contact point - ContactResult result = new ContactResult (); - result.ConsumerID = 0; - result.Depth = Vector3.Distance(rayStart, ip); - result.Normal = n; - result.Pos = ip; + // Check excess hits per part and group + for (int t = 0; t < 2; t++) + { + int maxHitsPerType = 0; + UUID id = UUID.Zero; + if (t == 0) + maxHitsPerType = m_maxHitsPerPrimInCastRay; + else + maxHitsPerType = m_maxHitsPerObjectInCastRay; - contacts.Add(result); + // Handle excess hits only when needed + if (maxHitsPerType < m_maxHitsInCastRay) + { + // Find excess hits + Hashtable hits = new Hashtable(); + for (int i = rayHits.Count - 1; i >= 0; i--) + { + if (t == 0) + id = rayHits[i].PartId; + else + id = rayHits[i].GroupId; + if (hits.ContainsKey(id)) + hits[id] = (int)hits[id] + 1; + else + hits[id] = 1; + } + + // Remove excess hits + for (int i = rayHits.Count - 1; i >= 0; i--) + { + if (t == 0) + id = rayHits[i].PartId; + else + id = rayHits[i].GroupId; + int hit = (int)hits[id]; + if (hit > m_maxHitsPerPrimInCastRay) + { + rayHits.RemoveAt(i); + hit--; + hits[id] = hit; + } + } + } } - if (contacts.Count == 0) - return null; + // Parse hits into result list according to data flags + int hitCount = rayHits.Count; + if (hitCount > maxHits) + hitCount = maxHits; + for (int i = 0; i < hitCount; i++) + { + RayHit rayHit = rayHits[i]; + if (getRootKey) + result.Add(new LSL_Key(rayHit.GroupId.ToString())); + else + result.Add(new LSL_Key(rayHit.PartId.ToString())); + result.Add(new LSL_Vector(rayHit.Position)); + if (getLinkNum) + result.Add(new LSL_Integer(rayHit.Link)); + if (getNormal) + result.Add(new LSL_Vector(rayHit.Normal)); + } + result.Add(new LSL_Integer(hitCount)); - contacts.Sort(delegate(ContactResult a, ContactResult b) + // Add to throttle data + stopWatch.Stop(); + CastRayCall castRayCall = new CastRayCall(); + castRayCall.RegionId = regionId; + castRayCall.UserId = userId; + castRayCall.CalledMs = calledMs; + castRayCall.UsedMs = (int)stopWatch.ElapsedMilliseconds; + lock (m_castRayCalls) { - return (int)(a.Depth - b.Depth); - }); + m_castRayCalls.Add(castRayCall); + } - return contacts[0]; + // Return hits + return result; } - public LSL_List llCastRay(LSL_Vector start, LSL_Vector end, LSL_List options) + /// + /// Struct for transmitting parameters required for finding llCastRay ray hits. + /// + public struct RayTrans { - LSL_List list = new LSL_List(); + public UUID PartId; + public UUID GroupId; + public int Link; + public Vector3 ScalePart; + public Vector3 PositionPart; + public Quaternion RotationPart; + public bool ShapeNeedsEnds; + public Vector3 Position1Ray; + public Vector3 Position1RayProj; + public Vector3 VectorRayProj; + } - m_host.AddScriptLPS(1); + /// + /// Struct for llCastRay ray hits. + /// + public struct RayHit + { + public UUID PartId; + public UUID GroupId; + public int Link; + public Vector3 Position; + public Vector3 Normal; + public float Distance; + } - Vector3 rayStart = start; - Vector3 rayEnd = end; - Vector3 dir = rayEnd - rayStart; + /// + /// Struct for llCastRay throttle data. + /// + public struct CastRayCall + { + public UUID RegionId; + public UUID UserId; + public int CalledMs; + public int UsedMs; + } - float dist = Vector3.Mag(dir); + /// + /// Helper to check if a ray intersects a shape bounding box. + /// + private bool RayIntersectsShapeBox(Vector3 pos1RayProj, Vector3 pos2RayProj, Vector3 shapeBoxMax) + { + // Skip if ray can't intersect bounding box; + Vector3 rayBoxProjMin = Vector3.Min(pos1RayProj, pos2RayProj); + Vector3 rayBoxProjMax = Vector3.Max(pos1RayProj, pos2RayProj); + if ( + rayBoxProjMin.X > shapeBoxMax.X || rayBoxProjMin.Y > shapeBoxMax.Y || rayBoxProjMin.Z > shapeBoxMax.Z || + rayBoxProjMax.X < -shapeBoxMax.X || rayBoxProjMax.Y < -shapeBoxMax.Y || rayBoxProjMax.Z < -shapeBoxMax.Z + ) + return false; - int count = 1; - bool detectPhantom = false; - int dataFlags = 0; - int rejectTypes = 0; + // Check if ray intersect any bounding box side + int sign = 0; + float dist = 0.0f; + Vector3 posProj = Vector3.Zero; + Vector3 vecRayProj = pos2RayProj - pos1RayProj; - for (int i = 0; i < options.Length; i += 2) + // Check both X sides unless ray is parallell to them + if (Math.Abs(vecRayProj.X) > m_floatToleranceInCastRay) { - if (options.GetLSLIntegerItem(i) == ScriptBaseClass.RC_MAX_HITS) - count = options.GetLSLIntegerItem(i + 1); - else if (options.GetLSLIntegerItem(i) == ScriptBaseClass.RC_DETECT_PHANTOM) - detectPhantom = (options.GetLSLIntegerItem(i + 1) > 0); - else if (options.GetLSLIntegerItem(i) == ScriptBaseClass.RC_DATA_FLAGS) - dataFlags = options.GetLSLIntegerItem(i + 1); - else if (options.GetLSLIntegerItem(i) == ScriptBaseClass.RC_REJECT_TYPES) - rejectTypes = options.GetLSLIntegerItem(i + 1); + for (sign = -1; sign <= 1; sign += 2) + { + dist = ((float)sign * shapeBoxMax.X - pos1RayProj.X) / vecRayProj.X; + posProj = pos1RayProj + vecRayProj * dist; + if (Math.Abs(posProj.Y) <= shapeBoxMax.Y && Math.Abs(posProj.Z) <= shapeBoxMax.Z) + return true; + } } - if (count > 16) - count = 16; - - List results = new List(); + // Check both Y sides unless ray is parallell to them + if (Math.Abs(vecRayProj.Y) > m_floatToleranceInCastRay) + { + for (sign = -1; sign <= 1; sign += 2) + { + dist = ((float)sign * shapeBoxMax.Y - pos1RayProj.Y) / vecRayProj.Y; + posProj = pos1RayProj + vecRayProj * dist; + if (Math.Abs(posProj.X) <= shapeBoxMax.X && Math.Abs(posProj.Z) <= shapeBoxMax.Z) + return true; + } + } - bool checkTerrain = !((rejectTypes & ScriptBaseClass.RC_REJECT_LAND) == ScriptBaseClass.RC_REJECT_LAND); - bool checkAgents = !((rejectTypes & ScriptBaseClass.RC_REJECT_AGENTS) == ScriptBaseClass.RC_REJECT_AGENTS); - bool checkNonPhysical = !((rejectTypes & ScriptBaseClass.RC_REJECT_NONPHYSICAL) == ScriptBaseClass.RC_REJECT_NONPHYSICAL); - bool checkPhysical = !((rejectTypes & ScriptBaseClass.RC_REJECT_PHYSICAL) == ScriptBaseClass.RC_REJECT_PHYSICAL); + // Check both Z sides unless ray is parallell to them + if (Math.Abs(vecRayProj.Z) > m_floatToleranceInCastRay) + { + for (sign = -1; sign <= 1; sign += 2) + { + dist = ((float)sign * shapeBoxMax.Z - pos1RayProj.Z) / vecRayProj.Z; + posProj = pos1RayProj + vecRayProj * dist; + if (Math.Abs(posProj.X) <= shapeBoxMax.X && Math.Abs(posProj.Y) <= shapeBoxMax.Y) + return true; + } + } + // No hits on bounding box so return false + return false; + } - if (checkTerrain) + /// + /// Helper to parse FacetedMesh for ray hits. + /// + private void AddRayInFacetedMesh(FacetedMesh mesh, RayTrans rayTrans, ref List rayHits) + { + if (mesh != null) { - ContactResult? groundContact = GroundIntersection(rayStart, rayEnd); - if (groundContact != null) - results.Add((ContactResult)groundContact); + foreach (Face face in mesh.Faces) + { + for (int i = 0; i < face.Indices.Count; i += 3) + { + Tri triangle = new Tri(); + triangle.p1 = face.Vertices[face.Indices[i]].Position; + triangle.p2 = face.Vertices[face.Indices[i + 1]].Position; + triangle.p3 = face.Vertices[face.Indices[i + 2]].Position; + AddRayInTri(triangle, rayTrans, ref rayHits); + } + } } + } - if (checkAgents) + /// + /// Helper to parse Tri (triangle) List for ray hits. + /// + private void AddRayInTris(List triangles, RayTrans rayTrans, ref List rayHits) + { + foreach (Tri triangle in triangles) { - ContactResult[] agentHits = AvatarIntersection(rayStart, rayEnd); - foreach (ContactResult r in agentHits) - results.Add(r); + AddRayInTri(triangle, rayTrans, ref rayHits); } + } - if (checkPhysical || checkNonPhysical || detectPhantom) + /// + /// Helper to add ray hit in a Tri (triangle). + /// + private void AddRayInTri(Tri triProj, RayTrans rayTrans, ref List rayHits) + { + // Check for hit in triangle + Vector3 posHitProj; + Vector3 normalProj; + if (HitRayInTri(triProj, rayTrans.Position1RayProj, rayTrans.VectorRayProj, out posHitProj, out normalProj)) { - ContactResult[] objectHits = ObjectIntersection(rayStart, rayEnd, checkPhysical, checkNonPhysical, detectPhantom); - foreach (ContactResult r in objectHits) - results.Add(r); + // Hack to circumvent ghost face bug in PrimMesher by removing hits in (ghost) face plane through shape center + if (Math.Abs(Vector3.Dot(posHitProj, normalProj)) < m_floatToleranceInCastRay && !rayTrans.ShapeNeedsEnds) + return; + + // Transform hit and normal to region coordinate system + Vector3 posHit = rayTrans.PositionPart + (posHitProj * rayTrans.ScalePart) * rayTrans.RotationPart; + Vector3 normal = Vector3.Normalize((normalProj * rayTrans.ScalePart) * rayTrans.RotationPart); + + // Remove duplicate hits at triangle intersections + float distance = Vector3.Distance(rayTrans.Position1Ray, posHit); + for (int i = rayHits.Count - 1; i >= 0; i--) + { + if (rayHits[i].PartId != rayTrans.PartId) + break; + if (Math.Abs(rayHits[i].Distance - distance) < m_floatTolerance2InCastRay) + return; + } + + // Build result data set + RayHit rayHit = new RayHit(); + rayHit.PartId = rayTrans.PartId; + rayHit.GroupId = rayTrans.GroupId; + rayHit.Link = rayTrans.Link; + rayHit.Position = posHit; + rayHit.Normal = normal; + rayHit.Distance = distance; + rayHits.Add(rayHit); } + } - results.Sort(delegate(ContactResult a, ContactResult b) - { - return a.Depth.CompareTo(b.Depth); - }); + /// + /// Helper to find ray hit in triangle + /// + bool HitRayInTri(Tri triProj, Vector3 pos1RayProj, Vector3 vecRayProj, out Vector3 posHitProj, out Vector3 normalProj) + { + float tol = m_floatToleranceInCastRay; + posHitProj = Vector3.Zero; - int values = 0; - SceneObjectGroup thisgrp = m_host.ParentGroup; + // Calculate triangle edge vectors + Vector3 vec1Proj = triProj.p2 - triProj.p1; + Vector3 vec2Proj = triProj.p3 - triProj.p2; + Vector3 vec3Proj = triProj.p1 - triProj.p3; - foreach (ContactResult result in results) - { - if (result.Depth > dist) - continue; + // Calculate triangle normal + normalProj = Vector3.Cross(vec1Proj, vec2Proj); - // physics ray can return colisions with host prim - if (m_host.LocalId == result.ConsumerID) - continue; + // Skip if degenerate triangle or ray parallell with triangle plane + float divisor = Vector3.Dot(vecRayProj, normalProj); + if (Math.Abs(divisor) < tol) + return false; - UUID itemID = UUID.Zero; - int linkNum = 0; + // Skip if exit and not configured to detect + if (divisor > tol && !m_detectExitsInCastRay) + return false; - SceneObjectPart part = World.GetSceneObjectPart(result.ConsumerID); - // It's a prim! - if (part != null) - { - // dont detect members of same object ??? - if (part.ParentGroup == thisgrp) - continue; + // Skip if outside ray ends + float distanceProj = Vector3.Dot(triProj.p1 - pos1RayProj, normalProj) / divisor; + if (distanceProj < -tol || distanceProj > 1 + tol) + return false; - if ((dataFlags & ScriptBaseClass.RC_GET_ROOT_KEY) == ScriptBaseClass.RC_GET_ROOT_KEY) - itemID = part.ParentGroup.UUID; - else - itemID = part.UUID; + // Calculate hit position in triangle + posHitProj = pos1RayProj + vecRayProj * distanceProj; - linkNum = part.LinkNum; + // Skip if outside triangle bounding box + Vector3 triProjMin = Vector3.Min(Vector3.Min(triProj.p1, triProj.p2), triProj.p3); + Vector3 triProjMax = Vector3.Max(Vector3.Max(triProj.p1, triProj.p2), triProj.p3); + if ( + posHitProj.X < triProjMin.X - tol || posHitProj.Y < triProjMin.Y - tol || posHitProj.Z < triProjMin.Z - tol || + posHitProj.X > triProjMax.X + tol || posHitProj.Y > triProjMax.Y + tol || posHitProj.Z > triProjMax.Z + tol + ) + return false; + + // Skip if outside triangle + if ( + Vector3.Dot(Vector3.Cross(vec1Proj, normalProj), posHitProj - triProj.p1) > tol || + Vector3.Dot(Vector3.Cross(vec2Proj, normalProj), posHitProj - triProj.p2) > tol || + Vector3.Dot(Vector3.Cross(vec3Proj, normalProj), posHitProj - triProj.p3) > tol + ) + return false; + + // Return hit + return true; + } + + /// + /// Helper to parse selected parts of HeightMap into a Tri (triangle) List and calculate bounding box. + /// + private List TrisFromHeightmapUnderRay(Vector3 posStart, Vector3 posEnd, out Vector3 lower, out Vector3 upper) + { + // Get bounding X-Y rectangle of terrain under ray + lower = Vector3.Min(posStart, posEnd); + upper = Vector3.Max(posStart, posEnd); + lower.X = (float)Math.Floor(lower.X); + lower.Y = (float)Math.Floor(lower.Y); + float zLower = float.MaxValue; + upper.X = (float)Math.Ceiling(upper.X); + upper.Y = (float)Math.Ceiling(upper.Y); + float zUpper = float.MinValue; + + // Initialize Tri (triangle) List + List triangles = new List(); + + // Set parsing lane direction to major ray X-Y axis + Vector3 vec = posEnd - posStart; + float xAbs = Math.Abs(vec.X); + float yAbs = Math.Abs(vec.Y); + bool bigX = true; + if (yAbs > xAbs) + { + bigX = false; + vec = vec / yAbs; + } + else if (xAbs > yAbs || xAbs > 0.0f) + vec = vec / xAbs; + else + vec = new Vector3(1.0f, 1.0f, 0.0f); + + // Simplify by start parsing in lower end of lane + if ((bigX && vec.X < 0.0f) || (!bigX && vec.Y < 0.0f)) + { + Vector3 posTemp = posStart; + posStart = posEnd; + posEnd = posTemp; + vec = vec * -1.0f; + } + + // First 1x1 rectangle under ray + float xFloorOld = 0.0f; + float yFloorOld = 0.0f; + Vector3 pos = posStart; + float xFloor = (float)Math.Floor(pos.X); + float yFloor = (float)Math.Floor(pos.Y); + AddTrisFromHeightmap(xFloor, yFloor, ref triangles, ref zLower, ref zUpper); + + // Parse every remaining 1x1 rectangle under ray + while (pos != posEnd) + { + // Next 1x1 rectangle under ray + xFloorOld = xFloor; + yFloorOld = yFloor; + pos = pos + vec; + + // Clip position to 1x1 rectangle border + xFloor = (float)Math.Floor(pos.X); + yFloor = (float)Math.Floor(pos.Y); + if (bigX && pos.X > xFloor) + { + pos.Y -= vec.Y * (pos.X - xFloor); + pos.X = xFloor; } - else + else if (!bigX && pos.Y > yFloor) { - ScenePresence sp = World.GetScenePresence(result.ConsumerID); - /// It it a boy? a girl? - if (sp != null) - itemID = sp.UUID; + pos.X -= vec.X * (pos.Y - yFloor); + pos.Y = yFloor; } - list.Add(new LSL_String(itemID.ToString())); - list.Add(new LSL_String(result.Pos.ToString())); + // Last 1x1 rectangle under ray + if ((bigX && pos.X >= posEnd.X) || (!bigX && pos.Y >= posEnd.Y)) + { + pos = posEnd; + xFloor = (float)Math.Floor(pos.X); + yFloor = (float)Math.Floor(pos.Y); + } - if ((dataFlags & ScriptBaseClass.RC_GET_LINK_NUM) == ScriptBaseClass.RC_GET_LINK_NUM) - list.Add(new LSL_Integer(linkNum)); + // Add new 1x1 rectangle in lane + if ((bigX && xFloor != xFloorOld) || (!bigX && yFloor != yFloorOld)) + AddTrisFromHeightmap(xFloor, yFloor, ref triangles, ref zLower, ref zUpper); + // Add last 1x1 rectangle in old lane at lane shift + if (bigX && yFloor != yFloorOld) + AddTrisFromHeightmap(xFloor, yFloorOld, ref triangles, ref zLower, ref zUpper); + if (!bigX && xFloor != xFloorOld) + AddTrisFromHeightmap(xFloorOld, yFloor, ref triangles, ref zLower, ref zUpper); + } - if ((dataFlags & ScriptBaseClass.RC_GET_NORMAL) == ScriptBaseClass.RC_GET_NORMAL) - list.Add(new LSL_Vector(result.Normal.X, result.Normal.Y, result.Normal.Z)); + // Finalize bounding box Z + lower.Z = zLower; + upper.Z = zUpper; - values++; - if (values >= count) - break; - } + // Done and returning Tri (triangle)List + return triangles; + } - list.Add(new LSL_Integer(values)); + /// + /// Helper to add HeightMap squares into Tri (triangle) List and adjust bounding box. + /// + private void AddTrisFromHeightmap(float xPos, float yPos, ref List triangles, ref float zLower, ref float zUpper) + { + int xInt = (int)xPos; + int yInt = (int)yPos; + + // Corner 1 of 1x1 rectangle + int x = Util.Clamp(xInt+1, 0, World.Heightmap.Width - 1); + int y = Util.Clamp(yInt+1, 0, World.Heightmap.Height - 1); + Vector3 pos1 = new Vector3(x, y, (float)World.Heightmap[x, y]); + // Adjust bounding box + zLower = Math.Min(zLower, pos1.Z); + zUpper = Math.Max(zUpper, pos1.Z); + + // Corner 2 of 1x1 rectangle + x = Util.Clamp(xInt, 0, World.Heightmap.Width - 1); + y = Util.Clamp(yInt+1, 0, World.Heightmap.Height - 1); + Vector3 pos2 = new Vector3(x, y, (float)World.Heightmap[x, y]); + // Adjust bounding box + zLower = Math.Min(zLower, pos2.Z); + zUpper = Math.Max(zUpper, pos2.Z); + + // Corner 3 of 1x1 rectangle + x = Util.Clamp(xInt, 0, World.Heightmap.Width - 1); + y = Util.Clamp(yInt, 0, World.Heightmap.Height - 1); + Vector3 pos3 = new Vector3(x, y, (float)World.Heightmap[x, y]); + // Adjust bounding box + zLower = Math.Min(zLower, pos3.Z); + zUpper = Math.Max(zUpper, pos3.Z); + + // Corner 4 of 1x1 rectangle + x = Util.Clamp(xInt+1, 0, World.Heightmap.Width - 1); + y = Util.Clamp(yInt, 0, World.Heightmap.Height - 1); + Vector3 pos4 = new Vector3(x, y, (float)World.Heightmap[x, y]); + // Adjust bounding box + zLower = Math.Min(zLower, pos4.Z); + zUpper = Math.Max(zUpper, pos4.Z); + + // Add triangle 1 + Tri triangle1 = new Tri(); + triangle1.p1 = pos1; + triangle1.p2 = pos2; + triangle1.p3 = pos3; + triangles.Add(triangle1); + + // Add triangle 2 + Tri triangle2 = new Tri(); + triangle2.p1 = pos3; + triangle2.p2 = pos4; + triangle2.p3 = pos1; + triangles.Add(triangle2); + } - return list; + /// + /// Helper to get link number for a UUID. + /// + private int UUID2LinkNumber(SceneObjectPart part, UUID id) + { + SceneObjectGroup group = part.ParentGroup; + if (group != null) + { + // Parse every link for UUID + int linkCount = group.PrimCount + group.GetSittingAvatarsCount(); + for (int link = linkCount; link > 0; link--) + { + ISceneEntity entity = GetLinkEntity(part, link); + // Return link number if UUID match + if (entity != null && entity.UUID == id) + return link; + } + } + // Return link number 0 if no links or UUID matches + return 0; } public LSL_Integer llManageEstateAccess(int action, string avatar) @@ -11477,8 +15001,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api public LSL_Integer llGetMemoryLimit() { m_host.AddScriptLPS(1); - // The value returned for LSO scripts in SL - return 16384; + // The value returned for Mono scripts in SL + return 65536; } public LSL_Integer llSetMemoryLimit(LSL_Integer limit) @@ -11491,15 +15015,15 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api public LSL_Integer llGetSPMaxMemory() { m_host.AddScriptLPS(1); - // The value returned for LSO scripts in SL - return 16384; + // The value returned for Mono scripts in SL + return 65536; } - public LSL_Integer llGetUsedMemory() + public virtual LSL_Integer llGetUsedMemory() { m_host.AddScriptLPS(1); - // The value returned for LSO scripts in SL - return 16384; + // The value returned for Mono scripts in SL + return 65536; } public void llScriptProfiler(LSL_Integer flags) @@ -11514,16 +15038,12 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api // them from this region as they are completed // - public void llGetEnv(LSL_String name) - { - m_host.AddScriptLPS(1); - NotImplemented("llGetEnv"); - } - public void llSetSoundQueueing(int queue) { m_host.AddScriptLPS(1); - NotImplemented("llSetSoundQueueing"); + + if (m_SoundModule != null) + m_SoundModule.SetSoundQueueing(m_host.UUID, queue == ScriptBaseClass.TRUE.value); } public void llCollisionSprite(string impact_sprite) @@ -11538,6 +15058,79 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api NotImplemented("llGodLikeRezObject"); } + public LSL_String llTransferLindenDollars(string destination, int amount) + { + UUID txn = UUID.Random(); + + Util.FireAndForget(delegate(object x) + { + int replycode = 0; + string replydata = destination + "," + amount.ToString(); + + try + { + TaskInventoryItem item = m_item; + if (item == null) + { + replydata = "SERVICE_ERROR"; + return; + } + + m_host.AddScriptLPS(1); + + if (item.PermsGranter == UUID.Zero) + { + replydata = "MISSING_PERMISSION_DEBIT"; + return; + } + + if ((item.PermsMask & ScriptBaseClass.PERMISSION_DEBIT) == 0) + { + replydata = "MISSING_PERMISSION_DEBIT"; + return; + } + + UUID toID = new UUID(); + + if (!UUID.TryParse(destination, out toID)) + { + replydata = "INVALID_AGENT"; + return; + } + + IMoneyModule money = World.RequestModuleInterface(); + + if (money == null) + { + replydata = "TRANSFERS_DISABLED"; + return; + } + + bool result = money.ObjectGiveMoney( + m_host.ParentGroup.RootPart.UUID, m_host.ParentGroup.RootPart.OwnerID, toID, amount); + + if (result) + { + replycode = 1; + return; + } + + replydata = "LINDENDOLLAR_INSUFFICIENTFUNDS"; + } + finally + { + m_ScriptEngine.PostScriptEvent(m_item.ItemID, new EventParams( + "transaction_result", new Object[] { + new LSL_String(txn.ToString()), + new LSL_Integer(replycode), + new LSL_String(replydata) }, + new DetectParams[0])); + } + }, null, "LSL_Api.llTransferLindenDollars"); + + return txn.ToString(); + } + #endregion } @@ -11549,12 +15142,12 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api public DateTime lastRef; } - protected static Dictionary m_Notecards = + private static Dictionary m_Notecards = new Dictionary(); - public static void Cache(UUID assetID, string text) + public static void Cache(UUID assetID, byte[] text) { - CacheCheck(); + CheckCache(); lock (m_Notecards) { @@ -11563,7 +15156,14 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api Notecard nc = new Notecard(); nc.lastRef = DateTime.Now; - nc.text = SLUtil.ParseNotecardToList(text).ToArray(); + try + { + nc.text = SLUtil.ParseNotecardToArray(text); + } + catch(SLUtil.NotANotecardFormatException) + { + nc.text = new string[0]; + } m_Notecards[assetID] = nc; } } @@ -11639,13 +15239,16 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api return line; } - public static void CacheCheck() + public static void CheckCache() { - foreach (UUID key in new List(m_Notecards.Keys)) + lock (m_Notecards) { - Notecard nc = m_Notecards[key]; - if (nc.lastRef.AddSeconds(30) < DateTime.Now) - m_Notecards.Remove(key); + foreach (UUID key in new List(m_Notecards.Keys)) + { + Notecard nc = m_Notecards[key]; + if (nc.lastRef.AddSeconds(30) < DateTime.Now) + m_Notecards.Remove(key); + } } } } diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LS_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LS_Api.cs index ceb4660..e5e43f8 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LS_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LS_Api.cs @@ -30,6 +30,7 @@ using System.Reflection; using System.Collections; using System.Collections.Generic; using System.Runtime.Remoting.Lifetime; +using System.Threading; using OpenMetaverse; using Nini.Config; using OpenSim; @@ -61,9 +62,10 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api internal bool m_LSFunctionsEnabled = false; internal IScriptModuleComms m_comms = null; - public void Initialize(IScriptEngine ScriptEngine, SceneObjectPart host, TaskInventoryItem item) + public void Initialize( + IScriptEngine scriptEngine, SceneObjectPart host, TaskInventoryItem item) { - m_ScriptEngine = ScriptEngine; + m_ScriptEngine = scriptEngine; m_host = host; if (m_ScriptEngine.Config.GetBoolean("AllowLightShareFunctions", false)) @@ -92,10 +94,9 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api get { return m_ScriptEngine.World; } } - // - //Dumps an error message on the debug console. - // - + /// + /// Dumps an error message on the debug console. + /// internal void LSShoutError(string message) { if (message.Length > 1023) @@ -264,175 +265,445 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api int idx = 0; while (idx < rules.Length) { - uint rule = (uint)rules.GetLSLIntegerItem(idx); + uint rule; + + try + { + rule = (uint)rules.GetLSLIntegerItem(idx); + } + catch (InvalidCastException) + { + throw new InvalidCastException(string.Format("Error running rule type: arg #{0} - parameter type must be integer", idx)); + } + LSL_Types.Quaternion iQ; LSL_Types.Vector3 iV; switch (rule) { case (int)ScriptBaseClass.WL_SUN_MOON_POSITION: idx++; - wl.sunMoonPosition = (float)rules.GetLSLFloatItem(idx); + try + { + wl.sunMoonPosition = (float)rules.GetLSLFloatItem(idx); + } + catch (InvalidCastException) + { + throw new InvalidCastException(string.Format("Error running rule WL_SUN_MOON_POSITION: arg #{0} - parameter 1 must be float", idx)); + } break; case (int)ScriptBaseClass.WL_AMBIENT: idx++; - iQ = rules.GetQuaternionItem(idx); + try + { + iQ = rules.GetQuaternionItem(idx); + } + catch (InvalidCastException) + { + throw new InvalidCastException(string.Format("Error running rule WL_AMBIENT: arg #{0} - parameter 1 must be rotation", idx)); + } wl.ambient = new Vector4((float)iQ.x, (float)iQ.y, (float)iQ.z, (float)iQ.s); break; case (int)ScriptBaseClass.WL_BIG_WAVE_DIRECTION: idx++; - iV = rules.GetVector3Item(idx); + try + { + iV = rules.GetVector3Item(idx); + } + catch (InvalidCastException) + { + throw new InvalidCastException(string.Format("Error running rule WL_BIG_WAVE_DIRECTION: arg #{0} - parameter 1 must be vector", idx)); + } wl.bigWaveDirection = new Vector2((float)iV.x, (float)iV.y); break; case (int)ScriptBaseClass.WL_BLUE_DENSITY: idx++; - iQ = rules.GetQuaternionItem(idx); + try + { + iQ = rules.GetQuaternionItem(idx); + } + catch (InvalidCastException) + { + throw new InvalidCastException(string.Format("Error running rule WL_BLUE_DENSITY: arg #{0} - parameter 1 must be rotation", idx)); + } wl.blueDensity = new Vector4((float)iQ.x, (float)iQ.y, (float)iQ.z, (float)iQ.s); break; case (int)ScriptBaseClass.WL_BLUR_MULTIPLIER: idx++; - wl.blurMultiplier = (float)rules.GetLSLFloatItem(idx); + try + { + wl.blurMultiplier = (float)rules.GetLSLFloatItem(idx); + } + catch (InvalidCastException) + { + throw new InvalidCastException(string.Format("Error running rule WL_BLUR_MULTIPLIER: arg #{0} - parameter 1 must be float", idx)); + } break; case (int)ScriptBaseClass.WL_CLOUD_COLOR: idx++; - iQ = rules.GetQuaternionItem(idx); + try + { + iQ = rules.GetQuaternionItem(idx); + } + catch (InvalidCastException) + { + throw new InvalidCastException(string.Format("Error running rule WL_CLOUD_COLOR: arg #{0} - parameter 1 must be rotation", idx)); + } wl.cloudColor = new Vector4((float)iQ.x, (float)iQ.y, (float)iQ.z, (float)iQ.s); break; case (int)ScriptBaseClass.WL_CLOUD_COVERAGE: idx++; - wl.cloudCoverage = (float)rules.GetLSLFloatItem(idx); + try + { + wl.cloudCoverage = (float)rules.GetLSLFloatItem(idx); + } + catch (InvalidCastException) + { + throw new InvalidCastException(string.Format("Error running rule WL_CLOUD_COVERAGE: arg #{0} - parameter 1 must be float", idx)); + } break; case (int)ScriptBaseClass.WL_CLOUD_DETAIL_XY_DENSITY: idx++; - iV = rules.GetVector3Item(idx); + try + { + iV = rules.GetVector3Item(idx); + } + catch (InvalidCastException) + { + throw new InvalidCastException(string.Format("Error running rule WL_CLOUD_DETAIL_XY_DENSITY: arg #{0} - parameter 1 must be vector", idx)); + } wl.cloudDetailXYDensity = iV; break; case (int)ScriptBaseClass.WL_CLOUD_SCALE: idx++; - wl.cloudScale = (float)rules.GetLSLFloatItem(idx); + try + { + wl.cloudScale = (float)rules.GetLSLFloatItem(idx); + } + catch (InvalidCastException) + { + throw new InvalidCastException(string.Format("Error running rule WL_CLOUD_SCALE: arg #{0} - parameter 1 must be float", idx)); + } break; case (int)ScriptBaseClass.WL_CLOUD_SCROLL_X: idx++; - wl.cloudScrollX = (float)rules.GetLSLFloatItem(idx); + try + { + wl.cloudScrollX = (float)rules.GetLSLFloatItem(idx); + } + catch (InvalidCastException) + { + throw new InvalidCastException(string.Format("Error running rule WL_CLOUD_SCROLL_X: arg #{0} - parameter 1 must be float", idx)); + } break; case (int)ScriptBaseClass.WL_CLOUD_SCROLL_X_LOCK: idx++; - wl.cloudScrollXLock = rules.GetLSLIntegerItem(idx).value == 1 ? true : false; + try + { + wl.cloudScrollXLock = rules.GetLSLIntegerItem(idx).value == 1 ? true : false; + } + catch (InvalidCastException) + { + throw new InvalidCastException(string.Format("Error running rule WL_CLOUD_SCROLL_Y_LOCK: arg #{0} - parameter 1 must be integer", idx)); + } break; case (int)ScriptBaseClass.WL_CLOUD_SCROLL_Y: idx++; - wl.cloudScrollY = (float)rules.GetLSLFloatItem(idx); + try + { + wl.cloudScrollY = (float)rules.GetLSLFloatItem(idx); + } + catch (InvalidCastException) + { + throw new InvalidCastException(string.Format("Error running rule WL_CLOUD_SCROLL_Y: arg #{0} - parameter 1 must be float", idx)); + } break; case (int)ScriptBaseClass.WL_CLOUD_SCROLL_Y_LOCK: idx++; - wl.cloudScrollYLock = rules.GetLSLIntegerItem(idx).value == 1 ? true : false; + try + { + wl.cloudScrollYLock = rules.GetLSLIntegerItem(idx).value == 1 ? true : false; + } + catch (InvalidCastException) + { + throw new InvalidCastException(string.Format("Error running rule WL_CLOUD_SCROLL_Y_LOCK: arg #{0} - parameter 1 must be integer", idx)); + } break; case (int)ScriptBaseClass.WL_CLOUD_XY_DENSITY: idx++; - iV = rules.GetVector3Item(idx); + try + { + iV = rules.GetVector3Item(idx); + } + catch (InvalidCastException) + { + throw new InvalidCastException(string.Format("Error running rule WL_CLOUD_XY_DENSITY: arg #{0} - parameter 1 must be vector", idx)); + } wl.cloudXYDensity = iV; break; case (int)ScriptBaseClass.WL_DENSITY_MULTIPLIER: idx++; - wl.densityMultiplier = (float)rules.GetLSLFloatItem(idx); + try + { + wl.densityMultiplier = (float)rules.GetLSLFloatItem(idx); + } + catch (InvalidCastException) + { + throw new InvalidCastException(string.Format("Error running rule WL_DENSITY_MULTIPLIER: arg #{0} - parameter 1 must be float", idx)); + } break; case (int)ScriptBaseClass.WL_DISTANCE_MULTIPLIER: idx++; - wl.distanceMultiplier = (float)rules.GetLSLFloatItem(idx); + try + { + wl.distanceMultiplier = (float)rules.GetLSLFloatItem(idx); + } + catch (InvalidCastException) + { + throw new InvalidCastException(string.Format("Error running rule WL_DISTANCE_MULTIPLIER: arg #{0} - parameter 1 must be float", idx)); + } break; case (int)ScriptBaseClass.WL_DRAW_CLASSIC_CLOUDS: idx++; - wl.drawClassicClouds = rules.GetLSLIntegerItem(idx).value == 1 ? true : false; + try + { + wl.drawClassicClouds = rules.GetLSLIntegerItem(idx).value == 1 ? true : false; + } + catch (InvalidCastException) + { + throw new InvalidCastException(string.Format("Error running rule WL_DRAW_CLASSIC_CLOUDS: arg #{0} - parameter 1 must be integer", idx)); + } break; case (int)ScriptBaseClass.WL_EAST_ANGLE: idx++; - wl.eastAngle = (float)rules.GetLSLFloatItem(idx); + try + { + wl.eastAngle = (float)rules.GetLSLFloatItem(idx); + } + catch (InvalidCastException) + { + throw new InvalidCastException(string.Format("Error running rule WL_EAST_ANGLE: arg #{0} - parameter 1 must be float", idx)); + } break; case (int)ScriptBaseClass.WL_FRESNEL_OFFSET: idx++; - wl.fresnelOffset = (float)rules.GetLSLFloatItem(idx); + try + { + wl.fresnelOffset = (float)rules.GetLSLFloatItem(idx); + } + catch (InvalidCastException) + { + throw new InvalidCastException(string.Format("Error running rule WL_FRESNEL_OFFSET: arg #{0} - parameter 1 must be float", idx)); + } break; case (int)ScriptBaseClass.WL_FRESNEL_SCALE: idx++; - wl.fresnelScale = (float)rules.GetLSLFloatItem(idx); + try + { + wl.fresnelScale = (float)rules.GetLSLFloatItem(idx); + } + catch (InvalidCastException) + { + throw new InvalidCastException(string.Format("Error running rule WL_FRESNEL_SCALE: arg #{0} - parameter 1 must be float", idx)); + } break; case (int)ScriptBaseClass.WL_HAZE_DENSITY: idx++; - wl.hazeDensity = (float)rules.GetLSLFloatItem(idx); + try + { + wl.hazeDensity = (float)rules.GetLSLFloatItem(idx); + } + catch (InvalidCastException) + { + throw new InvalidCastException(string.Format("Error running rule WL_HAZE_DENSITY: arg #{0} - parameter 1 must be float", idx)); + } break; case (int)ScriptBaseClass.WL_HAZE_HORIZON: idx++; - wl.hazeHorizon = (float)rules.GetLSLFloatItem(idx); + try + { + wl.hazeHorizon = (float)rules.GetLSLFloatItem(idx); + } + catch (InvalidCastException) + { + throw new InvalidCastException(string.Format("Error running rule WL_HAZE_HORIZON: arg #{0} - parameter 1 must be float", idx)); + } break; case (int)ScriptBaseClass.WL_HORIZON: idx++; - iQ = rules.GetQuaternionItem(idx); + try + { + iQ = rules.GetQuaternionItem(idx); + } + catch (InvalidCastException) + { + throw new InvalidCastException(string.Format("Error running rule WL_HORIZON: arg #{0} - parameter 1 must be rotation", idx)); + } wl.horizon = new Vector4((float)iQ.x, (float)iQ.y, (float)iQ.z, (float)iQ.s); break; case (int)ScriptBaseClass.WL_LITTLE_WAVE_DIRECTION: idx++; - iV = rules.GetVector3Item(idx); + try + { + iV = rules.GetVector3Item(idx); + } + catch (InvalidCastException) + { + throw new InvalidCastException(string.Format("Error running rule WL_LITTLE_WAVE_DIRECTION: arg #{0} - parameter 1 must be vector", idx)); + } wl.littleWaveDirection = new Vector2((float)iV.x, (float)iV.y); break; case (int)ScriptBaseClass.WL_MAX_ALTITUDE: idx++; - wl.maxAltitude = (ushort)rules.GetLSLIntegerItem(idx).value; + try + { + wl.maxAltitude = (ushort)rules.GetLSLIntegerItem(idx).value; + } + catch (InvalidCastException) + { + throw new InvalidCastException(string.Format("Error running rule WL_MAX_ALTITUDE: arg #{0} - parameter 1 must be integer", idx)); + } break; case (int)ScriptBaseClass.WL_NORMAL_MAP_TEXTURE: idx++; - wl.normalMapTexture = new UUID(rules.GetLSLStringItem(idx).m_string); + try + { + wl.normalMapTexture = new UUID(rules.GetLSLStringItem(idx).m_string); + } + catch (ArgumentException) + { + throw new InvalidCastException(string.Format("Error running rule WL_NORMAL_MAP_TEXTURE: arg #{0} - parameter 1 must be key", idx)); + } break; case (int)ScriptBaseClass.WL_REFLECTION_WAVELET_SCALE: idx++; - iV = rules.GetVector3Item(idx); + try + { + iV = rules.GetVector3Item(idx); + } + catch (InvalidCastException) + { + throw new InvalidCastException(string.Format("Error running rule WL_REFLECTION_WAVELET_SCALE: arg #{0} - parameter 1 must be vector", idx)); + } wl.reflectionWaveletScale = iV; break; case (int)ScriptBaseClass.WL_REFRACT_SCALE_ABOVE: idx++; - wl.refractScaleAbove = (float)rules.GetLSLFloatItem(idx); + try + { + wl.refractScaleAbove = (float)rules.GetLSLFloatItem(idx); + } + catch (InvalidCastException) + { + throw new InvalidCastException(string.Format("Error running rule WL_REFRACT_SCALE_ABOVE: arg #{0} - parameter 1 must be float", idx)); + } break; case (int)ScriptBaseClass.WL_REFRACT_SCALE_BELOW: idx++; - wl.refractScaleBelow = (float)rules.GetLSLFloatItem(idx); + try + { + wl.refractScaleBelow = (float)rules.GetLSLFloatItem(idx); + } + catch (InvalidCastException) + { + throw new InvalidCastException(string.Format("Error running rule WL_REFRACT_SCALE_BELOW: arg #{0} - parameter 1 must be float", idx)); + } break; case (int)ScriptBaseClass.WL_SCENE_GAMMA: idx++; - wl.sceneGamma = (float)rules.GetLSLFloatItem(idx); + try + { + wl.sceneGamma = (float)rules.GetLSLFloatItem(idx); + } + catch (InvalidCastException) + { + throw new InvalidCastException(string.Format("Error running rule WL_SCENE_GAMMA: arg #{0} - parameter 1 must be float", idx)); + } break; case (int)ScriptBaseClass.WL_STAR_BRIGHTNESS: idx++; - wl.starBrightness = (float)rules.GetLSLFloatItem(idx); + try + { + wl.starBrightness = (float)rules.GetLSLFloatItem(idx); + } + catch (InvalidCastException) + { + throw new InvalidCastException(string.Format("Error running rule WL_STAR_BRIGHTNESS: arg #{0} - parameter 1 must be float", idx)); + } break; case (int)ScriptBaseClass.WL_SUN_GLOW_FOCUS: idx++; - wl.sunGlowFocus = (float)rules.GetLSLFloatItem(idx); + try + { + wl.sunGlowFocus = (float)rules.GetLSLFloatItem(idx); + } + catch (InvalidCastException) + { + throw new InvalidCastException(string.Format("Error running rule WL_SUN_GLOW_FOCUS: arg #{0} - parameter 1 must be float", idx)); + } break; case (int)ScriptBaseClass.WL_SUN_GLOW_SIZE: idx++; - wl.sunGlowSize = (float)rules.GetLSLFloatItem(idx); + try + { + wl.sunGlowSize = (float)rules.GetLSLFloatItem(idx); + } + catch (InvalidCastException) + { + throw new InvalidCastException(string.Format("Error running rule WL_SUN_GLOW_SIZE: arg #{0} - parameter 1 must be float", idx)); + } break; case (int)ScriptBaseClass.WL_SUN_MOON_COLOR: idx++; iQ = rules.GetQuaternionItem(idx); - wl.sunMoonColor = new Vector4((float)iQ.x, (float)iQ.y, (float)iQ.z, (float)iQ.s); + try + { + wl.sunMoonColor = new Vector4((float)iQ.x, (float)iQ.y, (float)iQ.z, (float)iQ.s); + } + catch (InvalidCastException) + { + throw new InvalidCastException(string.Format("Error running rule WL_SUN_MOON_COLOR: arg #{0} - parameter 1 must be rotation", idx)); + } break; case (int)ScriptBaseClass.WL_UNDERWATER_FOG_MODIFIER: idx++; - wl.underwaterFogModifier = (float)rules.GetLSLFloatItem(idx); + try + { + wl.underwaterFogModifier = (float)rules.GetLSLFloatItem(idx); + } + catch (InvalidCastException) + { + throw new InvalidCastException(string.Format("Error running rule WL_UNDERWATER_FOG_MODIFIER: arg #{0} - parameter 1 must be float", idx)); + } break; case (int)ScriptBaseClass.WL_WATER_COLOR: idx++; - iV = rules.GetVector3Item(idx); + try + { + iV = rules.GetVector3Item(idx); + } + catch (InvalidCastException) + { + throw new InvalidCastException(string.Format("Error running rule WL_WATER_COLOR: arg #{0} - parameter 1 must be vector", idx)); + } wl.waterColor = iV; break; case (int)ScriptBaseClass.WL_WATER_FOG_DENSITY_EXPONENT: idx++; - wl.waterFogDensityExponent = (float)rules.GetLSLFloatItem(idx); + try + { + wl.waterFogDensityExponent = (float)rules.GetLSLFloatItem(idx); + } + catch (InvalidCastException) + { + throw new InvalidCastException(string.Format("Error running rule WL_WATER_FOG_DENSITY_EXPONENT: arg #{0} - parameter 1 must be float", idx)); + } break; } idx++; } return wl; } + /// /// Set the current Windlight scene /// @@ -445,16 +716,33 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api LSShoutError("LightShare functions are not enabled."); return 0; } - if (!World.RegionInfo.EstateSettings.IsEstateManagerOrOwner(m_host.OwnerID) && World.GetScenePresence(m_host.OwnerID).GodLevel < 200) + + if (!World.RegionInfo.EstateSettings.IsEstateManagerOrOwner(m_host.OwnerID)) { - LSShoutError("lsSetWindlightScene can only be used by estate managers or owners."); - return 0; + ScenePresence sp = World.GetScenePresence(m_host.OwnerID); + + if (sp == null || sp.GodLevel < 200) + { + LSShoutError("lsSetWindlightScene can only be used by estate managers or owners."); + return 0; + } } + int success = 0; m_host.AddScriptLPS(1); + if (LightShareModule.EnableWindlight) { - RegionLightShareData wl = getWindlightProfileFromRules(rules); + RegionLightShareData wl; + try + { + wl = getWindlightProfileFromRules(rules); + } + catch(InvalidCastException e) + { + LSShoutError(e.Message); + return 0; + } wl.valid = true; m_host.ParentGroup.Scene.StoreWindlightProfile(wl); success = 1; @@ -464,8 +752,10 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api LSShoutError("Windlight module is disabled"); return 0; } + return success; } + public void lsClearWindlightScene() { if (!m_LSFunctionsEnabled) @@ -473,17 +763,25 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api LSShoutError("LightShare functions are not enabled."); return; } - if (!World.RegionInfo.EstateSettings.IsEstateManagerOrOwner(m_host.OwnerID) && World.GetScenePresence(m_host.OwnerID).GodLevel < 200) + + if (!World.RegionInfo.EstateSettings.IsEstateManagerOrOwner(m_host.OwnerID)) { - LSShoutError("lsSetWindlightScene can only be used by estate managers or owners."); - return; + ScenePresence sp = World.GetScenePresence(m_host.OwnerID); + + if (sp == null || sp.GodLevel < 200) + { + LSShoutError("lsSetWindlightScene can only be used by estate managers or owners."); + return; + } } m_host.ParentGroup.Scene.RegionInfo.WindlightSettings.valid = false; if (m_host.ParentGroup.Scene.SimulationDataService != null) m_host.ParentGroup.Scene.SimulationDataService.RemoveRegionWindlightSettings(m_host.ParentGroup.Scene.RegionInfo.RegionID); + m_host.ParentGroup.Scene.EventManager.TriggerOnSaveNewWindlightProfile(); } + /// /// Set the current Windlight scene to a target avatar /// @@ -496,16 +794,33 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api LSShoutError("LightShare functions are not enabled."); return 0; } - if (!World.RegionInfo.EstateSettings.IsEstateManagerOrOwner(m_host.OwnerID) && World.GetScenePresence(m_host.OwnerID).GodLevel < 200) + + if (!World.RegionInfo.EstateSettings.IsEstateManagerOrOwner(m_host.OwnerID)) { - LSShoutError("lsSetWindlightSceneTargeted can only be used by estate managers or owners."); - return 0; + ScenePresence sp = World.GetScenePresence(m_host.OwnerID); + + if (sp == null || sp.GodLevel < 200) + { + LSShoutError("lsSetWindlightSceneTargeted can only be used by estate managers or owners."); + return 0; + } } + int success = 0; m_host.AddScriptLPS(1); + if (LightShareModule.EnableWindlight) - { - RegionLightShareData wl = getWindlightProfileFromRules(rules); + { + RegionLightShareData wl; + try + { + wl = getWindlightProfileFromRules(rules); + } + catch(InvalidCastException e) + { + LSShoutError(e.Message); + return 0; + } World.EventManager.TriggerOnSendNewWindlightProfileTargeted(wl, new UUID(target.m_string)); success = 1; } @@ -514,8 +829,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api LSShoutError("Windlight module is disabled"); return 0; } + return success; - } - + } } } diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/MOD_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/MOD_Api.cs index 8f34833..1458c95 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/MOD_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/MOD_Api.cs @@ -30,6 +30,8 @@ using System.Reflection; using System.Collections; using System.Collections.Generic; using System.Runtime.Remoting.Lifetime; +using System.Threading; +using log4net; using OpenMetaverse; using Nini.Config; using OpenSim; @@ -55,15 +57,18 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api [Serializable] public class MOD_Api : MarshalByRefObject, IMOD_Api, IScriptApi { +// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + internal IScriptEngine m_ScriptEngine; internal SceneObjectPart m_host; internal TaskInventoryItem m_item; internal bool m_MODFunctionsEnabled = false; internal IScriptModuleComms m_comms = null; - public void Initialize(IScriptEngine ScriptEngine, SceneObjectPart host, TaskInventoryItem item) + public void Initialize( + IScriptEngine scriptEngine, SceneObjectPart host, TaskInventoryItem item) { - m_ScriptEngine = ScriptEngine; + m_ScriptEngine = scriptEngine; m_host = host; m_item = item; @@ -107,8 +112,10 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api if (message.Length > 1023) message = message.Substring(0, 1023); - World.SimChat(Utils.StringToBytes(message), - ChatTypeEnum.Shout, ScriptBaseClass.DEBUG_CHANNEL, m_host.ParentGroup.RootPart.AbsolutePosition, m_host.Name, m_host.UUID, true); + World.SimChat( + Utils.StringToBytes(message), + ChatTypeEnum.Shout, ScriptBaseClass.DEBUG_CHANNEL, + m_host.ParentGroup.RootPart.AbsolutePosition, m_host.Name, m_host.UUID, false); IWorldComm wComm = m_ScriptEngine.World.RequestModuleInterface(); wComm.DeliverMessage(ChatTypeEnum.Shout, ScriptBaseClass.DEBUG_CHANNEL, m_host.Name, m_host.UUID, message); @@ -122,6 +129,12 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api /// string result of the invocation public void modInvokeN(string fname, params object[] parms) { +// m_log.DebugFormat( +// "[MOD API]: Invoking dynamic function {0}, args '{1}' with {2} return type", +// fname, +// string.Join(",", Array.ConvertAll(parms, o => o.ToString())), +// ((MethodInfo)MethodBase.GetCurrentMethod()).ReturnType); + Type returntype = m_comms.LookupReturnType(fname); if (returntype != typeof(string)) MODError(String.Format("return type mismatch for {0}",fname)); @@ -131,6 +144,12 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api public LSL_String modInvokeS(string fname, params object[] parms) { +// m_log.DebugFormat( +// "[MOD API]: Invoking dynamic function {0}, args '{1}' with {2} return type", +// fname, +// string.Join(",", Array.ConvertAll(parms, o => o.ToString())), +// ((MethodInfo)MethodBase.GetCurrentMethod()).ReturnType); + Type returntype = m_comms.LookupReturnType(fname); if (returntype != typeof(string)) MODError(String.Format("return type mismatch for {0}",fname)); @@ -141,6 +160,12 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api public LSL_Integer modInvokeI(string fname, params object[] parms) { +// m_log.DebugFormat( +// "[MOD API]: Invoking dynamic function {0}, args '{1}' with {2} return type", +// fname, +// string.Join(",", Array.ConvertAll(parms, o => o.ToString())), +// ((MethodInfo)MethodBase.GetCurrentMethod()).ReturnType); + Type returntype = m_comms.LookupReturnType(fname); if (returntype != typeof(int)) MODError(String.Format("return type mismatch for {0}",fname)); @@ -151,6 +176,12 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api public LSL_Float modInvokeF(string fname, params object[] parms) { +// m_log.DebugFormat( +// "[MOD API]: Invoking dynamic function {0}, args '{1}' with {2} return type", +// fname, +// string.Join(",", Array.ConvertAll(parms, o => o.ToString())), +// ((MethodInfo)MethodBase.GetCurrentMethod()).ReturnType); + Type returntype = m_comms.LookupReturnType(fname); if (returntype != typeof(float)) MODError(String.Format("return type mismatch for {0}",fname)); @@ -161,6 +192,12 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api public LSL_Key modInvokeK(string fname, params object[] parms) { +// m_log.DebugFormat( +// "[MOD API]: Invoking dynamic function {0}, args '{1}' with {2} return type", +// fname, +// string.Join(",", Array.ConvertAll(parms, o => o.ToString())), +// ((MethodInfo)MethodBase.GetCurrentMethod()).ReturnType); + Type returntype = m_comms.LookupReturnType(fname); if (returntype != typeof(UUID)) MODError(String.Format("return type mismatch for {0}",fname)); @@ -171,6 +208,12 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api public LSL_Vector modInvokeV(string fname, params object[] parms) { +// m_log.DebugFormat( +// "[MOD API]: Invoking dynamic function {0}, args '{1}' with {2} return type", +// fname, +// string.Join(",", Array.ConvertAll(parms, o => o.ToString())), +// ((MethodInfo)MethodBase.GetCurrentMethod()).ReturnType); + Type returntype = m_comms.LookupReturnType(fname); if (returntype != typeof(OpenMetaverse.Vector3)) MODError(String.Format("return type mismatch for {0}",fname)); @@ -181,6 +224,12 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api public LSL_Rotation modInvokeR(string fname, params object[] parms) { +// m_log.DebugFormat( +// "[MOD API]: Invoking dynamic function {0}, args '{1}' with {2} return type", +// fname, +// string.Join(",", Array.ConvertAll(parms, o => o.ToString())), +// ((MethodInfo)MethodBase.GetCurrentMethod()).ReturnType); + Type returntype = m_comms.LookupReturnType(fname); if (returntype != typeof(OpenMetaverse.Quaternion)) MODError(String.Format("return type mismatch for {0}",fname)); @@ -191,6 +240,12 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api public LSL_List modInvokeL(string fname, params object[] parms) { +// m_log.DebugFormat( +// "[MOD API]: Invoking dynamic function {0}, args '{1}' with {2} return type", +// fname, +// string.Join(",", Array.ConvertAll(parms, o => o.ToString())), +// ((MethodInfo)MethodBase.GetCurrentMethod()).ReturnType); + Type returntype = m_comms.LookupReturnType(fname); if (returntype != typeof(object[])) MODError(String.Format("return type mismatch for {0}",fname)); @@ -211,6 +266,10 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api { llist[i] = new LSL_Float((float)result[i]); } + else if (result[i] is double) + { + llist[i] = new LSL_Float((double)result[i]); + } else if (result[i] is UUID) { llist[i] = new LSL_Key(result[i].ToString()); @@ -248,13 +307,19 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api return ""; } +// m_log.DebugFormat( +// "[MOD API]: Invoking dynamic function {0}, args '{1}' with {2} return type", +// fname, +// string.Join(",", Array.ConvertAll(parms, o => o.ToString())), +// ((MethodInfo)MethodBase.GetCurrentMethod()).ReturnType); + Type[] signature = m_comms.LookupTypeSignature(fname); if (signature.Length != parms.Length) MODError(String.Format("wrong number of parameters to function {0}",fname)); object[] convertedParms = new object[parms.Length]; for (int i = 0; i < parms.Length; i++) - convertedParms[i] = ConvertFromLSL(parms[i],signature[i], fname); + convertedParms[i] = ConvertFromLSL(parms[i], signature[i], fname); // now call the function, the contract with the function is that it will always return // non-null but don't trust it completely @@ -351,7 +416,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api { if (type == typeof(object[])) { - object[] plist = (lslparm as LSL_List).Data; + object[] plist = ((LSL_List)lslparm).Data; object[] result = new object[plist.Length]; for (int i = 0; i < plist.Length; i++) { @@ -379,7 +444,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api } } - MODError(String.Format("{1}: parameter type mismatch; expecting {0}",type.Name, fname)); + MODError(String.Format("{0}: parameter type mismatch; expecting {1}, type(parm)={2}", fname, type.Name, lslparm.GetType())); return null; } diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs index dcc85c4..e799714 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/OSSL_Api.cs @@ -62,6 +62,8 @@ using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list; using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion; using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString; using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3; +using PermissionMask = OpenSim.Framework.PermissionMask; +using OpenSim.Services.Connectors.Hypergrid; namespace OpenSim.Region.ScriptEngine.Shared.Api { @@ -142,16 +144,20 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api protected IUrlModule m_UrlModule = null; - public void Initialize(IScriptEngine ScriptEngine, SceneObjectPart host, TaskInventoryItem item) + public void Initialize( + IScriptEngine scriptEngine, SceneObjectPart host, TaskInventoryItem item) { - m_ScriptEngine = ScriptEngine; + m_ScriptEngine = scriptEngine; m_host = host; m_item = item; m_UrlModule = m_ScriptEngine.World.RequestModuleInterface(); if (m_ScriptEngine.Config.GetBoolean("AllowOSFunctions", false)) + { m_OSFunctionsEnabled = true; + // m_log.Warn("[OSSL] OSSL FUNCTIONS ENABLED"); + } m_ScriptDelayFactor = m_ScriptEngine.Config.GetFloat("ScriptDelayFactor", 1.0f); @@ -245,11 +251,23 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api wComm.DeliverMessage(ChatTypeEnum.Shout, ScriptBaseClass.DEBUG_CHANNEL, m_host.Name, m_host.UUID, message); } + // Returns of the function is allowed. Throws a script exception if not allowed. public void CheckThreatLevel(ThreatLevel level, string function) { if (!m_OSFunctionsEnabled) OSSLError(String.Format("{0} permission denied. All OS functions are disabled.", function)); // throws + string reasonWhyNot = CheckThreatLevelTest(level, function); + if (!String.IsNullOrEmpty(reasonWhyNot)) + { + OSSLError(reasonWhyNot); + } + } + + // Check to see if function is allowed. Returns an empty string if function permitted + // or a string explaining why this function can't be used. + private string CheckThreatLevelTest(ThreatLevel level, string function) + { if (!m_FunctionPerms.ContainsKey(function)) { FunctionPerms perms = new FunctionPerms(); @@ -329,10 +347,10 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api { // Allow / disallow by threat level if (level > m_MaxThreatLevel) - OSSLError( + return String.Format( "{0} permission denied. Allowed threat level is {1} but function threat level is {2}.", - function, m_MaxThreatLevel, level)); + function, m_MaxThreatLevel, level); } else { @@ -342,7 +360,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api if (m_FunctionPerms[function].AllowedOwners.Contains(m_host.OwnerID)) { // prim owner is in the list of allowed owners - return; + return String.Empty; } UUID ownerID = m_item.OwnerID; @@ -350,22 +368,22 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api //OSSL only may be used if object is in the same group as the parcel if (m_FunctionPerms[function].AllowedOwnerClasses.Contains("PARCEL_GROUP_MEMBER")) { - ILandObject land = World.LandChannel.GetLandObject(m_host.AbsolutePosition.X, m_host.AbsolutePosition.Y); + ILandObject land = World.LandChannel.GetLandObject(m_host.AbsolutePosition); if (land.LandData.GroupID == m_item.GroupID && land.LandData.GroupID != UUID.Zero) { - return; + return String.Empty; } } //Only Parcelowners may use the function if (m_FunctionPerms[function].AllowedOwnerClasses.Contains("PARCEL_OWNER")) { - ILandObject land = World.LandChannel.GetLandObject(m_host.AbsolutePosition.X, m_host.AbsolutePosition.Y); + ILandObject land = World.LandChannel.GetLandObject(m_host.AbsolutePosition); if (land.LandData.OwnerID == ownerID) { - return; + return String.Empty; } } @@ -375,7 +393,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api //Only Estate Managers may use the function if (World.RegionInfo.EstateSettings.IsEstateManagerOrOwner(ownerID) && World.RegionInfo.EstateSettings.EstateOwner != ownerID) { - return; + return String.Empty; } } @@ -384,25 +402,24 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api { if (World.RegionInfo.EstateSettings.EstateOwner == ownerID) { - return; + return String.Empty; } } if (!m_FunctionPerms[function].AllowedCreators.Contains(m_item.CreatorID)) - OSSLError( + return( String.Format("{0} permission denied. Script creator is not in the list of users allowed to execute this function and prim owner also has no permission.", function)); if (m_item.CreatorID != ownerID) { if ((m_item.CurrentPermissions & (uint)PermissionMask.Modify) != 0) - OSSLError( - String.Format("{0} permission denied. Script permissions error.", - function)); + return String.Format("{0} permission denied. Script permissions error.", function); } } } + return String.Empty; } internal void OSSLDeprecated(string function, string replacement) @@ -437,7 +454,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api { m_host.AddScriptLPS(1); - if (x > ((int)Constants.RegionSize - 1) || x < 0 || y > ((int)Constants.RegionSize - 1) || y < 0) + if (x > (World.RegionInfo.RegionSizeX - 1) || x < 0 || y > (World.RegionInfo.RegionSizeY - 1) || y < 0) OSSLError("osSetTerrainHeight: Coordinate out of bounds"); if (World.Permissions.CanTerraformLand(m_host.OwnerID, new Vector3(x, y, 0))) @@ -467,7 +484,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api private LSL_Float GetTerrainHeight(int x, int y) { m_host.AddScriptLPS(1); - if (x > ((int)Constants.RegionSize - 1) || x < 0 || y > ((int)Constants.RegionSize - 1) || y < 0) + if (x > (World.RegionInfo.RegionSizeX - 1) || x < 0 || y > (World.RegionInfo.RegionSizeY - 1) || y < 0) OSSLError("osGetTerrainHeight: Coordinate out of bounds"); return World.Heightmap[x, y]; @@ -777,9 +794,11 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api // We will launch the teleport on a new thread so that when the script threads are terminated // before teleport in ScriptInstance.GetXMLState(), we don't end up aborting the one doing the teleporting. - Util.FireAndForget(o => World.RequestTeleportLocation( - presence.ControllingClient, regionName, position, - lookat, (uint)TPFlags.ViaLocation)); + Util.FireAndForget( + o => World.RequestTeleportLocation( + presence.ControllingClient, regionName, position, + lookat, (uint)TPFlags.ViaLocation), + null, "OSSL_Api.TeleportAgentByRegionCoords"); ScriptSleep(5000); @@ -801,7 +820,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api private void TeleportAgent(string agent, int regionX, int regionY, LSL_Types.Vector3 position, LSL_Types.Vector3 lookat, bool relaxRestrictions) { - ulong regionHandle = Util.UIntsToLong(((uint)regionX * (uint)Constants.RegionSize), ((uint)regionY * (uint)Constants.RegionSize)); + // ulong regionHandle = Util.UIntsToLong(((uint)regionX * (uint)Constants.RegionSize), ((uint)regionY * (uint)Constants.RegionSize)); + ulong regionHandle = Util.RegionLocToHandle((uint)regionX, (uint)regionY); m_host.AddScriptLPS(1); UUID agentId = new UUID(); @@ -822,9 +842,11 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api // We will launch the teleport on a new thread so that when the script threads are terminated // before teleport in ScriptInstance.GetXMLState(), we don't end up aborting the one doing the teleporting. - Util.FireAndForget(o => World.RequestTeleportLocation( - presence.ControllingClient, regionHandle, - position, lookat, (uint)TPFlags.ViaLocation)); + Util.FireAndForget( + o => World.RequestTeleportLocation( + presence.ControllingClient, regionHandle, + position, lookat, (uint)TPFlags.ViaLocation), + null, "OSSL_Api.TeleportAgentByRegionName"); ScriptSleep(5000); @@ -859,6 +881,59 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api TeleportAgent(m_host.OwnerID.ToString(), regionX, regionY, position, lookat, true); } + /// + /// Allows a script IN the target prim to force an avatar to sit on it using normal methods + /// as if called by the client. + /// Silent fail if agent (or target if overloaded) not found. + /// Does work if passed key (or keys if overloaded). + /// + /// + public void osForceOtherSit(string avatar) + { + CheckThreatLevel(ThreatLevel.VeryHigh, "osForceOtherSit"); + + m_host.AddScriptLPS(1); + + ForceSit(avatar, m_host.UUID); + } + + /// + /// Overload method of osForceOtherSit(string avatar) to allow a script NOT in the target prim to force + /// an avatar to sit on the target prim using normal methods as if called by the client. + /// + /// + /// + public void osForceOtherSit(string avatar, string target) + { + CheckThreatLevel(ThreatLevel.VeryHigh, "osForceOtherSit"); + + m_host.AddScriptLPS(1); + + UUID targetID = new UUID(target); + + ForceSit(avatar, targetID); + } + + public void ForceSit(string avatar, UUID targetID) + { + UUID agentID; + + if (!UUID.TryParse(avatar, out agentID)) + return; + + ScenePresence presence = World.GetScenePresence(agentID); + + SceneObjectPart part = World.GetSceneObjectPart(targetID); + + if (presence != null && + part != null && + part.SitTargetAvatar == UUID.Zero) + presence.HandleAgentRequestSit(presence.ControllingClient, + agentID, + targetID, + part.SitTargetPosition); + } + // Functions that get information from the agent itself. // // osGetAgentIP - this is used to determine the IP address of @@ -1205,12 +1280,11 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api sunHour += 24.0; World.RegionInfo.RegionSettings.UseEstateSun = useEstateSun; - World.RegionInfo.RegionSettings.SunPosition = sunHour + 6; // LL Region Sun Hour is 6 to 30 - World.RegionInfo.RegionSettings.FixedSun = sunFixed; + World.RegionInfo.RegionSettings.SunPosition = sunHour + 6; // LL Region Sun Hour is 6 to 30 + World.RegionInfo.RegionSettings.FixedSun = sunFixed; World.RegionInfo.RegionSettings.Save(); - World.EventManager.TriggerEstateToolsSunUpdate( - World.RegionInfo.RegionHandle, sunFixed, useEstateSun, (float)sunHour); + World.EventManager.TriggerEstateToolsSunUpdate(World.RegionInfo.RegionHandle); } /// @@ -1233,10 +1307,9 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api World.RegionInfo.EstateSettings.UseGlobalTime = !sunFixed; World.RegionInfo.EstateSettings.SunPosition = sunHour; World.RegionInfo.EstateSettings.FixedSun = sunFixed; - World.RegionInfo.EstateSettings.Save(); + World.EstateDataService.StoreEstateSettings(World.RegionInfo.EstateSettings); - World.EventManager.TriggerEstateToolsSunUpdate( - World.RegionInfo.RegionHandle, sunFixed, World.RegionInfo.RegionSettings.UseEstateSun, (float)sunHour); + World.EventManager.TriggerEstateToolsSunUpdate(World.RegionInfo.RegionHandle); } /// @@ -1492,8 +1565,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api m_host.AddScriptLPS(1); - ILandObject land - = World.LandChannel.GetLandObject(m_host.AbsolutePosition.X, m_host.AbsolutePosition.Y); + ILandObject land = World.LandChannel.GetLandObject(m_host.AbsolutePosition); if (land.LandData.OwnerID != m_host.OwnerID) return; @@ -1509,8 +1581,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api m_host.AddScriptLPS(1); - ILandObject land - = World.LandChannel.GetLandObject(m_host.AbsolutePosition.X, m_host.AbsolutePosition.Y); + ILandObject land = World.LandChannel.GetLandObject(m_host.AbsolutePosition); if (land.LandData.OwnerID != m_host.OwnerID) { @@ -1560,6 +1631,47 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api } } + public LSL_Integer osCheckODE() + { + m_host.AddScriptLPS(1); + LSL_Integer ret = 0; // false + if (m_ScriptEngine.World.PhysicsScene != null) + { + string physEngine = m_ScriptEngine.World.PhysicsScene.EngineType; + if (physEngine == "OpenDynamicsEngine") + { + ret = 1; // true + } + } + return ret; + } + + public string osGetPhysicsEngineType() + { + // High because it can be used to target attacks to known weaknesses + // This would allow a new class of griefer scripts that don't even + // require their user to know what they are doing (see script + // kiddie) + // Because it would be nice if scripts didn't blow up if the information + // about the physics engine, this function returns an empty string if + // the user does not have permission to see it. This as opposed to + // throwing an exception. + m_host.AddScriptLPS(1); + string ret = String.Empty; + if (String.IsNullOrEmpty(CheckThreatLevelTest(ThreatLevel.High, "osGetPhysicsEngineType"))) + { + if (m_ScriptEngine.World.PhysicsScene != null) + { + ret = m_ScriptEngine.World.PhysicsScene.EngineType; + // An old physics engine might have an uninitialized engine type + if (ret == null) + ret = "unknown"; + } + } + + return ret; + } + public string osGetSimulatorVersion() { // High because it can be used to target attacks to known weaknesses @@ -1732,13 +1844,23 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api // Create new asset AssetBase asset = new AssetBase(UUID.Random(), name, (sbyte)AssetType.Notecard, m_host.OwnerID.ToString()); asset.Description = description; + byte[] a; + byte[] b; + byte[] c; + + b = Util.UTF8.GetBytes(data); - int textLength = data.Length; - data - = "Linden text version 2\n{\nLLEmbeddedItems version 1\n{\ncount 0\n}\nText length " - + textLength.ToString() + "\n" + data + "}\n"; + a = Util.UTF8.GetBytes( + "Linden text version 2\n{\nLLEmbeddedItems version 1\n{\ncount 0\n}\nText length " + b.Length.ToString() + "\n"); - asset.Data = Util.UTF8.GetBytes(data); + c = Util.UTF8.GetBytes("}"); + + byte[] d = new byte[a.Length + b.Length + c.Length]; + Buffer.BlockCopy(a, 0, d, 0, a.Length); + Buffer.BlockCopy(b, 0, d, a.Length, b.Length); + Buffer.BlockCopy(c, 0, d, a.Length + b.Length, c.Length); + + asset.Data = d; World.AssetService.Store(asset); // Create Task Entry @@ -1753,8 +1875,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api taskItem.InvType = (int)InventoryType.Notecard; taskItem.OwnerID = m_host.OwnerID; taskItem.CreatorID = m_host.OwnerID; - taskItem.BasePermissions = (uint)PermissionMask.All; - taskItem.CurrentPermissions = (uint)PermissionMask.All; + taskItem.BasePermissions = (uint)PermissionMask.All | (uint)PermissionMask.Export; + taskItem.CurrentPermissions = (uint)PermissionMask.All | (uint)PermissionMask.Export; taskItem.EveryonePermissions = 0; taskItem.NextPermissions = (uint)PermissionMask.All; taskItem.GroupID = m_host.GroupID; @@ -1833,8 +1955,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api if (a == null) return UUID.Zero; - string data = Encoding.UTF8.GetString(a.Data); - NotecardCache.Cache(assetID, data); + NotecardCache.Cache(assetID, a.Data); }; return assetID; @@ -1932,15 +2053,51 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api CheckThreatLevel(ThreatLevel.Low, "osAvatarName2Key"); m_host.AddScriptLPS(1); - UserAccount account = World.UserAccountService.GetUserAccount(World.RegionInfo.ScopeID, firstname, lastname); - if (null == account) + IUserManagement userManager = World.RequestModuleInterface(); + if (userManager == null) { - return UUID.Zero.ToString(); + OSSLShoutError("osAvatarName2Key: UserManagement module not available"); + return string.Empty; + } + + // Check if the user is already cached + + UUID userID = userManager.GetUserIdByName(firstname, lastname); + if (userID != UUID.Zero) + return userID.ToString(); + + // Query for the user + + String realFirstName; String realLastName; String serverURI; + if (Util.ParseForeignAvatarName(firstname, lastname, out realFirstName, out realLastName, out serverURI)) + { + try + { + UserAgentServiceConnector userConnection = new UserAgentServiceConnector(serverURI, true); + + if (userConnection != null) + { + userID = userConnection.GetUUID(realFirstName, realLastName); + if (userID != UUID.Zero) + { + userManager.AddUser(userID, realFirstName, realLastName, serverURI); + return userID.ToString(); + } + } + } + catch (Exception /*e*/) + { + // m_log.Warn("[osAvatarName2Key] UserAgentServiceConnector - Unable to connect to destination grid ", e); + } } else { - return account.PrincipalID.ToString(); + UserAccount account = World.UserAccountService.GetUserAccount(World.RegionInfo.ScopeID, firstname, lastname); + if (account != null) + return account.PrincipalID.ToString(); } + + return UUID.Zero.ToString(); } public string osKey2Name(string id) @@ -1953,19 +2110,27 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api if (UUID.TryParse(id, out key)) { UserAccount account = World.UserAccountService.GetUserAccount(World.RegionInfo.ScopeID, key); - if (null == account) - { - return ""; - } - else - { + if (account != null) return account.Name; + + if (m_ScriptEngine.World.GridUserService != null) + { + GridUserInfo uInfo = m_ScriptEngine.World.GridUserService.GetGridUserInfo(key.ToString()); + + if (uInfo != null) + { + UUID userUUID; String gridURL; String firstName; String lastName; String tmp; + + if (Util.ParseUniversalUserIdentifier(uInfo.UserID, out userUUID, out gridURL, out firstName, out lastName, out tmp)) + { + string grid = new Uri(gridURL).Authority; + return firstName + "." + lastName + " @" + grid; + } + } } } - else - { - return ""; - } + + return ""; } private enum InfoType @@ -2101,9 +2266,14 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api CheckThreatLevel(ThreatLevel.Moderate, "osGetGridHomeURI"); m_host.AddScriptLPS(1); - string HomeURI = String.Empty; IConfigSource config = m_ScriptEngine.ConfigSource; + string HomeURI = Util.GetConfigVarFromSections(config, "HomeURI", + new string[] { "Startup", "Hypergrid" }, String.Empty); + if (!string.IsNullOrEmpty(HomeURI)) + return HomeURI; + + // Legacy. Remove soon! if (config.Configs["LoginService"] != null) HomeURI = config.Configs["LoginService"].GetString("SRV_HomeURI", HomeURI); @@ -2118,9 +2288,14 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api CheckThreatLevel(ThreatLevel.Moderate, "osGetGridGatekeeperURI"); m_host.AddScriptLPS(1); - string gatekeeperURI = String.Empty; IConfigSource config = m_ScriptEngine.ConfigSource; + string gatekeeperURI = Util.GetConfigVarFromSections(config, "GatekeeperURI", + new string[] { "Startup", "Hypergrid" }, String.Empty); + + if (!string.IsNullOrEmpty(gatekeeperURI)) + return gatekeeperURI; + // Legacy. Remove soon! if (config.Configs["GridService"] != null) gatekeeperURI = config.Configs["GridService"].GetString("Gatekeeper", gatekeeperURI); @@ -2144,6 +2319,39 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api return retval; } + public string osGetAvatarHomeURI(string uuid) + { + CheckThreatLevel(ThreatLevel.Low, "osGetAvatarHomeURI"); + m_host.AddScriptLPS(1); + + IUserManagement userManager = m_ScriptEngine.World.RequestModuleInterface(); + string returnValue = ""; + + if (userManager != null) + { + returnValue = userManager.GetUserServerURL(new UUID(uuid), "HomeURI"); + } + + if (returnValue == "") + { + IConfigSource config = m_ScriptEngine.ConfigSource; + returnValue = Util.GetConfigVarFromSections(config, "HomeURI", + new string[] { "Startup", "Hypergrid" }, String.Empty); + + if (!string.IsNullOrEmpty(returnValue)) + return returnValue; + + // Legacy. Remove soon! + if (config.Configs["LoginService"] != null) + returnValue = config.Configs["LoginService"].GetString("SRV_HomeURI", returnValue); + + if (String.IsNullOrEmpty(returnValue)) + returnValue = GridUserInfo(InfoType.Home); + } + + return returnValue; + } + public LSL_String osFormatString(string str, LSL_List strings) { CheckThreatLevel(ThreatLevel.VeryLow, "osFormatString"); @@ -2265,14 +2473,14 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api // on the ILSL_Api interface. LSL_Api LSL_Api = (LSL_Api)m_LSL_Api; LSL_List retVal = new LSL_List(); - LSL_List remaining = null; + LSL_List remaining = new LSL_List(); List parts = LSL_Api.GetLinkParts(linknumber); foreach (SceneObjectPart part in parts) { remaining = LSL_Api.GetPrimParams(part, rules, ref retVal); } - while (remaining != null && remaining.Length > 2) + while (remaining.Length > 2) { linknumber = remaining.GetLSLIntegerItem(0); rules = remaining.GetSublist(1, -1); @@ -2284,6 +2492,36 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api return retVal; } + public void osForceCreateLink(string target, int parent) + { + CheckThreatLevel(ThreatLevel.VeryLow, "osForceCreateLink"); + + m_host.AddScriptLPS(1); + + InitLSL(); + ((LSL_Api)m_LSL_Api).CreateLink(target, parent); + } + + public void osForceBreakLink(int linknum) + { + CheckThreatLevel(ThreatLevel.VeryLow, "osForceBreakLink"); + + m_host.AddScriptLPS(1); + + InitLSL(); + ((LSL_Api)m_LSL_Api).BreakLink(linknum); + } + + public void osForceBreakAllLinks() + { + CheckThreatLevel(ThreatLevel.VeryLow, "osForceBreakAllLinks"); + + m_host.AddScriptLPS(1); + + InitLSL(); + ((LSL_Api)m_LSL_Api).BreakAllLinks(); + } + public LSL_Integer osIsNpc(LSL_Key npc) { CheckThreatLevel(ThreatLevel.None, "osIsNpc"); @@ -2469,13 +2707,10 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api ScenePresence sp = World.GetScenePresence(npcId); if (sp != null) - { - Vector3 pos = sp.AbsolutePosition; - return new LSL_Vector(pos.X, pos.Y, pos.Z); - } + return new LSL_Vector(sp.AbsolutePosition); } - return new LSL_Vector(0, 0, 0); + return Vector3.Zero; } public void osNpcMoveTo(LSL_Key npc, LSL_Vector pos) @@ -2532,21 +2767,18 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api { UUID npcId; if (!UUID.TryParse(npc.m_string, out npcId)) - return new LSL_Rotation(Quaternion.Identity.X, Quaternion.Identity.Y, Quaternion.Identity.Z, Quaternion.Identity.W); + return new LSL_Rotation(Quaternion.Identity); if (!npcModule.CheckPermissions(npcId, m_host.OwnerID)) - return new LSL_Rotation(Quaternion.Identity.X, Quaternion.Identity.Y, Quaternion.Identity.Z, Quaternion.Identity.W); + return new LSL_Rotation(Quaternion.Identity); ScenePresence sp = World.GetScenePresence(npcId); if (sp != null) - { - Quaternion rot = sp.Rotation; - return new LSL_Rotation(rot.X, rot.Y, rot.Z, rot.W); - } + return new LSL_Rotation(sp.GetWorldRotation()); } - return new LSL_Rotation(Quaternion.Identity.X, Quaternion.Identity.Y, Quaternion.Identity.Z, Quaternion.Identity.W); + return Quaternion.Identity; } public void osNpcSetRot(LSL_Key npc, LSL_Rotation rotation) @@ -2824,6 +3056,51 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api return SaveAppearanceToNotecard(avatarId, notecard); } + + /// + /// Get the gender as specified in avatar appearance for a given avatar key + /// + /// + /// "male" or "female" or "unknown" + public LSL_String osGetGender(LSL_Key rawAvatarId) + { + CheckThreatLevel(ThreatLevel.None, "osGetGender"); + m_host.AddScriptLPS(1); + + UUID avatarId; + if (!UUID.TryParse(rawAvatarId, out avatarId)) + return new LSL_String("unknown"); + + ScenePresence sp = World.GetScenePresence(avatarId); + + if (sp == null || sp.IsChildAgent || sp.Appearance == null || sp.Appearance.VisualParams == null) + return new LSL_String("unknown"); + + // find the index of "shape" parameter "male" + int vpShapeMaleIndex = 0; + bool indexFound = false; + VisualParam param = new VisualParam(); + foreach(var vpEntry in VisualParams.Params) + { + param = vpEntry.Value; + if (param.Name == "male" && param.Wearable == "shape") + { + indexFound = true; + break; + } + + if (param.Group == 0) + vpShapeMaleIndex++; + } + + if (!indexFound) + return new LSL_String("unknown"); + + float vpShapeMale = Utils.ByteToFloat(sp.Appearance.VisualParams[vpShapeMaleIndex], param.MinValue, param.MaxValue); + + bool isMale = vpShapeMale > 0.5f; + return new LSL_String(isMale ? "male" : "female"); + } /// /// Get current region's map texture UUID @@ -2887,6 +3164,31 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api return ret; } + public LSL_Vector osGetRegionSize() + { + CheckThreatLevel(ThreatLevel.None, "osGetRegionSize"); + m_host.AddScriptLPS(1); + + bool isMegaregion; + IRegionCombinerModule rcMod = World.RequestModuleInterface(); + if (rcMod != null) + isMegaregion = rcMod.IsRootForMegaregion(World.RegionInfo.RegionID); + else + isMegaregion = false; + + if (isMegaregion) + { + Vector2 size = rcMod.GetSizeOfMegaregion(World.RegionInfo.RegionID); + return new LSL_Vector(size.X, size.Y, Constants.RegionHeight); + } + else + { + Scene scene = m_ScriptEngine.World; + GridRegion region = scene.GridService.GetRegionByUUID(UUID.Zero, World.RegionInfo.RegionID); + return new LSL_Vector((float)region.RegionSizeX, (float)region.RegionSizeX, Constants.RegionHeight); + } + } + public int osGetSimulatorMemory() { CheckThreatLevel(ThreatLevel.Moderate, "osGetSimulatorMemory"); @@ -2925,7 +3227,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api sp.ControllingClient.Kick(alert); // ...and close on our side - sp.Scene.IncomingCloseAgent(sp.UUID, false); + sp.Scene.CloseAgent(sp.UUID, false); } }); } @@ -2976,20 +3278,16 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api UUID avatarId = new UUID(avatar); ScenePresence presence = World.GetScenePresence(avatarId); - Vector3 pos = m_host.GetWorldPosition(); - bool result = World.ScriptDanger(m_host.LocalId, new Vector3((float)pos.X, (float)pos.Y, (float)pos.Z)); - if (result) + + if (presence != null && World.ScriptDanger(m_host.LocalId, m_host.GetWorldPosition())) { - if (presence != null) - { - float health = presence.Health; - health += (float)healing; - if (health >= 100) - { - health = 100; - } - presence.setHealthWithUpdate(health); - } + float health = presence.Health; + health += (float)healing; + + if (health >= 100) + health = 100; + + presence.setHealthWithUpdate(health); } } @@ -3066,8 +3364,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api if (avatar != null && avatar.UUID != m_host.OwnerID) { result.Add(new LSL_String(avatar.UUID.ToString())); - OpenMetaverse.Vector3 ap = avatar.AbsolutePosition; - result.Add(new LSL_Vector(ap.X, ap.Y, ap.Z)); + result.Add(new LSL_Vector(avatar.AbsolutePosition)); result.Add(new LSL_String(avatar.Name)); } }); @@ -3263,7 +3560,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api public void osForceAttachToOtherAvatarFromInventory(string rawAvatarId, string itemName, int attachmentPoint) { - CheckThreatLevel(ThreatLevel.Severe, "osForceAttachToOtherAvatarFromInventory"); + CheckThreatLevel(ThreatLevel.VeryHigh, "osForceAttachToOtherAvatarFromInventory"); m_host.AddScriptLPS(1); @@ -3307,14 +3604,15 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api if (sp == null) return; - InventoryItemBase newItem = World.MoveTaskInventoryItem(sp.UUID, UUID.Zero, m_host, item.ItemID); + string message; + InventoryItemBase newItem = World.MoveTaskInventoryItem(sp.UUID, UUID.Zero, m_host, item.ItemID, out message); if (newItem == null) { m_log.ErrorFormat( - "[OSSL API]: Could not create user inventory item {0} for {1}, attach point {2} in {3}", - itemName, m_host.Name, attachmentPoint, World.Name); - + "[OSSL API]: Could not create user inventory item {0} for {1}, attach point {2} in {3}: {4}", + itemName, m_host.Name, attachmentPoint, World.Name, message); + ((LSL_Api)m_LSL_Api).llSay(0, message); return; } diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/Plugins/SensorRepeat.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/Plugins/SensorRepeat.cs index dd45406..64dc2e2 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/Plugins/SensorRepeat.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/Plugins/SensorRepeat.cs @@ -353,7 +353,12 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins // Position of a sensor in a child prim attached to an avatar // will be still wrong. ScenePresence avatar = m_CmdManager.m_ScriptEngine.World.GetScenePresence(SensePoint.ParentGroup.AttachedAvatar); - q = avatar.Rotation * q; + + // Don't proceed if the avatar for this attachment has since been removed from the scene. + if (avatar == null) + return sensedEntities; + + q = avatar.GetWorldRotation() * q; } LSL_Types.Quaternion r = new LSL_Types.Quaternion(q); @@ -362,7 +367,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins Vector3 ZeroVector = new Vector3(0, 0, 0); - bool nameSearch = (ts.name != null && ts.name != ""); + bool nameSearch = !string.IsNullOrEmpty(ts.name); foreach (EntityBase ent in Entities) { @@ -480,7 +485,12 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins // Position of a sensor in a child prim attached to an avatar // will be still wrong. ScenePresence avatar = m_CmdManager.m_ScriptEngine.World.GetScenePresence(SensePoint.ParentGroup.AttachedAvatar); - q = avatar.Rotation * q; + + // Don't proceed if the avatar for this attachment has since been removed from the scene. + if (avatar == null) + return sensedEntities; + + q = avatar.GetWorldRotation() * q; } LSL_Types.Quaternion r = new LSL_Types.Quaternion(q); @@ -595,7 +605,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins return sensedEntities; senseEntity(sp); } - else if (ts.name != null && ts.name != "") + else if (!string.IsNullOrEmpty(ts.name)) { ScenePresence sp; // Try lookup by name will return if/when found diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/Properties/AssemblyInfo.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/Properties/AssemblyInfo.cs index d173db0..215c087 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/Properties/AssemblyInfo.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/Properties/AssemblyInfo.cs @@ -29,5 +29,5 @@ using System.Runtime.InteropServices; // Build Number // Revision // -[assembly: AssemblyVersion("0.7.5.*")] -[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: AssemblyVersion("0.8.3.*")] + -- cgit v1.1