From 2129d941acbc5f83aca4dc4c30a62d3226888136 Mon Sep 17 00:00:00 2001 From: UbitUmarov Date: Fri, 23 Feb 2018 14:52:34 +0000 Subject: rename XMREngine as Yengine (still not all done), big mess source formating changes, move state files to proper folder, fix a source file locking on errors, more changes for cross platform including from Mike,... yes yes i know a messy commit --- .../Region/ScriptEngine/YEngine/XMRInstBackend.cs | 652 +++++++++++++++++++++ 1 file changed, 652 insertions(+) create mode 100644 OpenSim/Region/ScriptEngine/YEngine/XMRInstBackend.cs (limited to 'OpenSim/Region/ScriptEngine/YEngine/XMRInstBackend.cs') diff --git a/OpenSim/Region/ScriptEngine/YEngine/XMRInstBackend.cs b/OpenSim/Region/ScriptEngine/YEngine/XMRInstBackend.cs new file mode 100644 index 0000000..fb5c75e --- /dev/null +++ b/OpenSim/Region/ScriptEngine/YEngine/XMRInstBackend.cs @@ -0,0 +1,652 @@ +/* + * Copyright (c) Contributors, http://opensimulator.org/ + * See CONTRIBUTORS.TXT for a full list of copyright holders. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the OpenSimulator Project nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +using System; +using System.Threading; +using System.Collections.Generic; +using OpenMetaverse; +using OpenSim.Framework; +using OpenSim.Region.ScriptEngine.Shared; +using OpenSim.Region.ScriptEngine.Shared.Api; +using log4net; + +using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat; +using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger; +using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString; +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; + +namespace OpenSim.Region.ScriptEngine.Yengine +{ + /****************************************************\ + * This file contains routines called by scripts. * + \****************************************************/ + + public class XMRLSL_Api: LSL_Api + { + public AsyncCommandManager acm; + private XMRInstance inst; + + public void InitXMRLSLApi(XMRInstance i) + { + acm = AsyncCommands; + inst = i; + } + + protected override void ScriptSleep(int ms) + { + inst.Sleep(ms); + } + + public override void llSleep(double sec) + { + inst.Sleep((int)(sec * 1000.0)); + } + + public override void llDie() + { + inst.Die(); + } + + /** + * @brief Seat avatar on prim. + * @param owner = true: owner of prim script is running in + * false: avatar that has given ANIMATION permission on the prim + * @returns 0: successful + * -1: no permission to animate + * -2: no av granted perms + * -3: av not in region + */ + /* engines should not have own API + public int xmrSeatAvatar (bool owner) + { + // Get avatar to be seated and make sure they have given us ANIMATION permission + + UUID avuuid; + if (owner) { + avuuid = inst.m_Part.OwnerID; + } else { + if ((m_item.PermsMask & ScriptBaseClass.PERMISSION_TRIGGER_ANIMATION) == 0) { + return -1; + } + avuuid = m_item.PermsGranter; + } + if (avuuid == UUID.Zero) { + return -2; + } + + ScenePresence presence = World.GetScenePresence (avuuid); + if (presence == null) { + return -3; + } + + // remoteClient = not used by ScenePresence.HandleAgentRequestSit() + // agentID = not used by ScenePresence.HandleAgentRequestSit() + // targetID = UUID of prim to sit on + // offset = offset of sitting position + + presence.HandleAgentRequestSit (null, UUID.Zero, m_host.UUID, OpenMetaverse.Vector3.Zero); + return 0; + } + */ + /** + * @brief llTeleportAgent() is broken in that if you pass it a landmark, + * it still subjects the position to spawn points, as it always + * calls RequestTeleportLocation() with TeleportFlags.ViaLocation. + * See llTeleportAgent() and CheckAndAdjustTelehub(). + * + * @param agent = what agent to teleport + * @param landmark = inventory name or UUID of a landmark object + * @param lookat = looking direction after teleport + */ + /* engines should not have own API + public void xmrTeleportAgent2Landmark (string agent, string landmark, LSL_Vector lookat) + { + // find out about agent to be teleported + UUID agentId; + if (!UUID.TryParse (agent, out agentId)) throw new ApplicationException ("bad agent uuid"); + + ScenePresence presence = World.GetScenePresence (agentId); + if (presence == null) throw new ApplicationException ("agent not present in scene"); + if (presence.IsNPC) throw new ApplicationException ("agent is an NPC"); + if (presence.IsGod) throw new ApplicationException ("agent is a god"); + + // prim must be owned by land owner or prim must be attached to agent + if (m_host.ParentGroup.AttachmentPoint == 0) { + if (m_host.OwnerID != World.LandChannel.GetLandObject (presence.AbsolutePosition).LandData.OwnerID) { + throw new ApplicationException ("prim not owned by land's owner"); + } + } else { + if (m_host.OwnerID != presence.UUID) throw new ApplicationException ("prim not attached to agent"); + } + + // find landmark in inventory or by UUID + UUID assetID = ScriptUtils.GetAssetIdFromKeyOrItemName (m_host, landmark); + if (assetID == UUID.Zero) throw new ApplicationException ("no such landmark"); + + // read it in and make sure it is a landmark + AssetBase lma = World.AssetService.Get (assetID.ToString ()); + if ((lma == null) || (lma.Type != (sbyte)AssetType.Landmark)) throw new ApplicationException ("not a landmark"); + + // parse the record + AssetLandmark lm = new AssetLandmark (lma); + + // the regionhandle (based on region's world X,Y) might be out of date + // re-read the handle so we can pass it to RequestTeleportLocation() + var region = World.GridService.GetRegionByUUID (World.RegionInfo.ScopeID, lm.RegionID); + if (region == null) throw new ApplicationException ("no such region"); + + // finally ready to teleport + World.RequestTeleportLocation (presence.ControllingClient, + region.RegionHandle, + lm.Position, + lookat, + (uint)TeleportFlags.ViaLandmark); + } + */ + /** + * @brief Allow any member of group given by config SetParcelMusicURLGroup to set music URL. + * Code modelled after llSetParcelMusicURL(). + * @param newurl = new URL to set (or "" to leave it alone) + * @returns previous URL string + */ + /* engines should not have own API + public string xmrSetParcelMusicURLGroup (string newurl) + { + string groupname = m_ScriptEngine.Config.GetString ("SetParcelMusicURLGroup", ""); + if (groupname == "") throw new ApplicationException ("no SetParcelMusicURLGroup config param set"); + + IGroupsModule igm = World.RequestModuleInterface (); + if (igm == null) throw new ApplicationException ("no GroupsModule loaded"); + + GroupRecord grouprec = igm.GetGroupRecord (groupname); + if (grouprec == null) throw new ApplicationException ("no such group " + groupname); + + GroupMembershipData gmd = igm.GetMembershipData (grouprec.GroupID, m_host.OwnerID); + if (gmd == null) throw new ApplicationException ("not a member of group " + groupname); + + ILandObject land = World.LandChannel.GetLandObject (m_host.AbsolutePosition); + if (land == null) throw new ApplicationException ("no land at " + m_host.AbsolutePosition.ToString ()); + string oldurl = land.GetMusicUrl (); + if (oldurl == null) oldurl = ""; + if ((newurl != null) && (newurl != "")) land.SetMusicUrl (newurl); + return oldurl; + } + */ + } + + public partial class XMRInstance + { + /** + * @brief The script is calling llReset(). + * We throw an exception to unwind the script out to its main + * causing all the finally's to execute and it will also set + * eventCode = None to indicate event handler has completed. + */ + public void ApiReset() + { + ClearQueueExceptLinkMessages(); + throw new ScriptResetException(); + } + + /** + * @brief The script is calling one of the llDetected...(int number) + * functions. Return corresponding DetectParams pointer. + */ + public DetectParams GetDetectParams(int number) + { + DetectParams dp = null; + if((number >= 0) && (m_DetectParams != null) && (number < m_DetectParams.Length)) + dp = m_DetectParams[number]; + + return dp; + } + + /** + * @brief Script is calling llDie, so flag the run loop to delete script + * once we are off the microthread stack, and throw an exception + * to unwind the stack asap. + */ + public void Die() + { + // llDie doesn't work in attachments! + if(m_Part.ParentGroup.IsAttachment || m_DetachQuantum > 0) + return; + + throw new ScriptDieException(); + } + + /** + * @brief Called by script to sleep for the given number of milliseconds. + */ + public void Sleep(int ms) + { + lock(m_QueueLock) + { + /* + * Say how long to sleep. + */ + m_SleepUntil = DateTime.UtcNow + TimeSpan.FromMilliseconds(ms); + + /* + * Don't wake on any events. + */ + m_SleepEventMask1 = 0; + m_SleepEventMask2 = 0; + } + + /* + * The compiler follows all calls to llSleep() with a call to CheckRun(). + * So tell CheckRun() to suspend the microthread. + */ + suspendOnCheckRunTemp = true; + } + + /** + * Block script execution until an event is queued or a timeout is reached. + * @param timeout = maximum number of seconds to wait + * @param returnMask = if event is queued that matches these mask bits, + * the script is woken, that event is dequeued and + * returned to the caller. The event handler is not + * executed. + * @param backgroundMask = if any of these events are queued while waiting, + * execute their event handlers. When any such event + * handler exits, continue waiting for events or the + * timeout. + * @returns empty list: no event was queued that matched returnMask and the timeout was reached + * or a background event handler changed state (eg, via 'state' statement) + * else: list giving parameters of the event: + * [0] = event code (integer) + * [1..n] = call parameters to the event, if any + * Notes: + * 1) Scrips should use XMREVENTMASKn_ symbols for the mask arguments, + * where n is 1 or 2 for mask1 or mask2 arguments. + * The list[0] return argument can be decoded by using XMREVENTCODE_ symbols. + * 2) If all masks are zero, the call ends up acting like llSleep. + * 3) If an event is enabled in both returnMask and backgroundMask, the returnMask bit + * action takes precedence, ie, the event is returned. This allows a simple specification + * of -1 for both backgroundMask arguments to indicate that all events not listed in + * the returnMask argumetns should be handled in the background. + * 4) Any events not listed in either returnMask or backgroundMask arguments will be + * queued for later processing (subject to normal queue limits). + * 5) Background event handlers execute as calls from within xmrEventDequeue, they do + * not execute as separate threads. Thus any background event handlers must return + * before the call to xmrEventDequeue will return. + * 6) If a background event handler changes state (eg, via 'state' statement), the state + * is immediately changed and the script-level xmrEventDequeue call does not return. + * 7) For returned events, the detect parameters are overwritten by the returned event. + * For background events, the detect parameters are saved and restored. + * 8) Scripts must contain dummy event handler definitions for any event types that may + * be returned by xmrEventDequeue, to let the runtime know that the script is capable + * of processing that event type. Otherwise, the event may not be queued to the script. + */ + private static LSL_List emptyList = new LSL_List(new object[0]); + + public override LSL_List xmrEventDequeue(double timeout, int returnMask1, int returnMask2, + int backgroundMask1, int backgroundMask2) + { + DateTime sleepUntil = DateTime.UtcNow + TimeSpan.FromMilliseconds(timeout * 1000.0); + EventParams evt = null; + int callNo, evc2; + int evc1 = 0; + int mask1 = returnMask1 | backgroundMask1; // codes 00..31 + int mask2 = returnMask2 | backgroundMask2; // codes 32..63 + LinkedListNode lln = null; + object[] sv; + ScriptEventCode evc = ScriptEventCode.None; + + callNo = -1; + try + { + if(callMode == CallMode_NORMAL) + goto findevent; + + /* + * Stack frame is being restored as saved via CheckRun...(). + * Restore necessary values then jump to __call label to resume processing. + */ + sv = RestoreStackFrame("xmrEventDequeue", out callNo); + sleepUntil = DateTime.Parse((string)sv[0]); + returnMask1 = (int)sv[1]; + returnMask2 = (int)sv[2]; + mask1 = (int)sv[3]; + mask2 = (int)sv[4]; + switch(callNo) + { + case 0: + goto __call0; + case 1: + { + evc1 = (int)sv[5]; + evc = (ScriptEventCode)(int)sv[6]; + DetectParams[] detprms = ObjArrToDetPrms((object[])sv[7]); + object[] ehargs = (object[])sv[8]; + evt = new EventParams(evc.ToString(), ehargs, detprms); + goto __call1; + } + } + throw new ScriptBadCallNoException(callNo); + + /* + * Find first event that matches either the return or background masks. + */ + findevent: + Monitor.Enter(m_QueueLock); + for(lln = m_EventQueue.First; lln != null; lln = lln.Next) + { + evt = lln.Value; + evc = (ScriptEventCode)Enum.Parse(typeof(ScriptEventCode), evt.EventName); + evc1 = (int)evc; + evc2 = evc1 - 32; + if((((uint)evc1 < (uint)32) && (((mask1 >> evc1) & 1) != 0)) || + (((uint)evc2 < (uint)32) && (((mask2 >> evc2) & 1) != 0))) + goto remfromq; + } + + /* + * Nothing found, sleep while one comes in. + */ + m_SleepUntil = sleepUntil; + m_SleepEventMask1 = mask1; + m_SleepEventMask2 = mask2; + Monitor.Exit(m_QueueLock); + suspendOnCheckRunTemp = true; + callNo = 0; + __call0: + CheckRunQuick(); + goto checktmo; + + /* + * Found one, remove it from queue. + */ + remfromq: + m_EventQueue.Remove(lln); + if((uint)evc1 < (uint)m_EventCounts.Length) + m_EventCounts[evc1]--; + + Monitor.Exit(m_QueueLock); + m_InstEHEvent++; + + /* + * See if returnable or background event. + */ + if((((uint)evc1 < (uint)32) && (((returnMask1 >> evc1) & 1) != 0)) || + (((uint)evc2 < (uint)32) && (((returnMask2 >> evc2) & 1) != 0))) + { + /* + * Returnable event, return its parameters in a list. + * Also set the detect parameters to what the event has. + */ + int plen = evt.Params.Length; + object[] plist = new object[plen + 1]; + plist[0] = (LSL_Integer)evc1; + for(int i = 0; i < plen;) + { + object ob = evt.Params[i]; + if(ob is int) + ob = (LSL_Integer)(int)ob; + else if(ob is double) + ob = (LSL_Float)(double)ob; + else if(ob is string) + ob = (LSL_String)(string)ob; + plist[++i] = ob; + } + m_DetectParams = evt.DetectParams; + return new LSL_List(plist); + } + + /* + * It is a background event, simply call its event handler, + * then check event queue again. + */ + callNo = 1; + __call1: + ScriptEventHandler seh = m_ObjCode.scriptEventHandlerTable[stateCode, evc1]; + if(seh == null) + goto checktmo; + + DetectParams[] saveDetParams = this.m_DetectParams; + object[] saveEHArgs = this.ehArgs; + ScriptEventCode saveEventCode = this.eventCode; + + this.m_DetectParams = evt.DetectParams; + this.ehArgs = evt.Params; + this.eventCode = evc; + + try + { + seh(this); + } + finally + { + this.m_DetectParams = saveDetParams; + this.ehArgs = saveEHArgs; + this.eventCode = saveEventCode; + } + + /* + * Keep waiting until we find a returnable event or timeout. + */ + checktmo: + if(DateTime.UtcNow < sleepUntil) + goto findevent; + + /* + * We timed out, return an empty list. + */ + return emptyList; + } + finally + { + if(callMode != CallMode_NORMAL) + { + + /* + * Stack frame is being saved by CheckRun...(). + * Save everything we need at the __call labels so we can restore it + * when we need to. + */ + sv = CaptureStackFrame("xmrEventDequeue", callNo, 9); + sv[0] = sleepUntil.ToString(); // needed at __call0,__call1 + sv[1] = returnMask1; // needed at __call0,__call1 + sv[2] = returnMask2; // needed at __call0,__call1 + sv[3] = mask1; // needed at __call0,__call1 + sv[4] = mask2; // needed at __call0,__call1 + if(callNo == 1) + { + sv[5] = evc1; // needed at __call1 + sv[6] = (int)evc; // needed at __call1 + sv[7] = DetPrmsToObjArr(evt.DetectParams); // needed at __call1 + sv[8] = evt.Params; // needed at __call1 + } + } + } + } + + /** + * @brief Enqueue an event + * @param ev = as returned by xmrEventDequeue saying which event type to queue + * and what argument list to pass to it. The llDetect...() parameters + * are as currently set for the script (use xmrEventLoadDets to set how + * you want them to be different). + */ + public override void xmrEventEnqueue(LSL_List ev) + { + object[] data = ev.Data; + ScriptEventCode evc = (ScriptEventCode)ListInt(data[0]); + + int nargs = data.Length - 1; + object[] args = new object[nargs]; + Array.Copy(data, 1, args, 0, nargs); + + PostEvent(new EventParams(evc.ToString(), args, m_DetectParams)); + } + + /** + * @brief Save current detect params into a list + * @returns a list containing current detect param values + */ + private const int saveDPVer = 1; + + public override LSL_List xmrEventSaveDets() + { + object[] obs = DetPrmsToObjArr(m_DetectParams); + return new LSL_List(obs); + } + + private static object[] DetPrmsToObjArr(DetectParams[] dps) + { + int len = dps.Length; + object[] obs = new object[len * 16 + 1]; + int j = 0; + obs[j++] = (LSL_Integer)saveDPVer; + for(int i = 0; i < len; i++) + { + DetectParams dp = dps[i]; + obs[j++] = (LSL_String)dp.Key.ToString(); // UUID + obs[j++] = dp.OffsetPos; // vector + obs[j++] = (LSL_Integer)dp.LinkNum; // integer + obs[j++] = (LSL_String)dp.Group.ToString(); // UUID + obs[j++] = (LSL_String)dp.Name; // string + obs[j++] = (LSL_String)dp.Owner.ToString(); // UUID + obs[j++] = dp.Position; // vector + obs[j++] = dp.Rotation; // rotation + obs[j++] = (LSL_Integer)dp.Type; // integer + obs[j++] = dp.Velocity; // vector + obs[j++] = dp.TouchST; // vector + obs[j++] = dp.TouchNormal; // vector + obs[j++] = dp.TouchBinormal; // vector + obs[j++] = dp.TouchPos; // vector + obs[j++] = dp.TouchUV; // vector + obs[j++] = (LSL_Integer)dp.TouchFace; // integer + } + return obs; + } + + /** + * @brief Load current detect params from a list + * @param dpList = as returned by xmrEventSaveDets() + */ + public override void xmrEventLoadDets(LSL_List dpList) + { + m_DetectParams = ObjArrToDetPrms(dpList.Data); + } + + private static DetectParams[] ObjArrToDetPrms(object[] objs) + { + int j = 0; + if((objs.Length % 16 != 1) || (ListInt(objs[j++]) != saveDPVer)) + throw new Exception("invalid detect param format"); + + int len = objs.Length / 16; + DetectParams[] dps = new DetectParams[len]; + + for(int i = 0; i < len; i++) + { + DetectParams dp = new DetectParams(); + + dp.Key = new UUID(ListStr(objs[j++])); + dp.OffsetPos = (LSL_Vector)objs[j++]; + dp.LinkNum = ListInt(objs[j++]); + dp.Group = new UUID(ListStr(objs[j++])); + dp.Name = ListStr(objs[j++]); + dp.Owner = new UUID(ListStr(objs[j++])); + dp.Position = (LSL_Vector)objs[j++]; + dp.Rotation = (LSL_Rotation)objs[j++]; + dp.Type = ListInt(objs[j++]); + dp.Velocity = (LSL_Vector)objs[j++]; + + SurfaceTouchEventArgs stea = new SurfaceTouchEventArgs(); + + stea.STCoord = LSLVec2OMVec((LSL_Vector)objs[j++]); + stea.Normal = LSLVec2OMVec((LSL_Vector)objs[j++]); + stea.Binormal = LSLVec2OMVec((LSL_Vector)objs[j++]); + stea.Position = LSLVec2OMVec((LSL_Vector)objs[j++]); + stea.UVCoord = LSLVec2OMVec((LSL_Vector)objs[j++]); + stea.FaceIndex = ListInt(objs[j++]); + + dp.SurfaceTouchArgs = stea; + + dps[i] = dp; + } + return dps; + } + + /** + * @brief The script is executing a 'state ;' command. + * Tell outer layers to cancel any event triggers, like llListen(), + * then tell outer layers which events the new state has handlers for. + * We also clear the event queue as per http://wiki.secondlife.com/wiki/State + */ + public override void StateChange() + { + /* + * Cancel any llListen()s etc. + * But llSetTimerEvent() should persist. + */ + object[] timers = m_XMRLSLApi.acm.TimerPlugin.GetSerializationData(m_ItemID); + AsyncCommandManager.RemoveScript(m_Engine, m_LocalID, m_ItemID); + m_XMRLSLApi.acm.TimerPlugin.CreateFromData(m_LocalID, m_ItemID, UUID.Zero, timers); + + /* + * Tell whoever cares which event handlers the new state has. + */ + m_Part.SetScriptEvents(m_ItemID, GetStateEventFlags(stateCode)); + + /* + * Clear out any old events from the queue. + */ + lock(m_QueueLock) + { + m_EventQueue.Clear(); + for(int i = m_EventCounts.Length; --i >= 0;) + m_EventCounts[i] = 0; + } + } + } + + /** + * @brief Thrown by things like llResetScript() to unconditionally + * unwind as script and reset it to the default state_entry + * handler. We don't want script-level try/catch to intercept + * these so scripts can't interfere with the behavior. + */ + public class ScriptResetException: Exception, IXMRUncatchable + { + } + + /** + * @brief Thrown by things like llDie() to unconditionally unwind as + * script. We don't want script-level try/catch to intercept + * these so scripts can't interfere with the behavior. + */ + public class ScriptDieException: Exception, IXMRUncatchable + { + } +} -- cgit v1.1