/* * Start event handler. * * Input: * eventCode = code of event to be processed * ehArgs = arguments for the event handler * * Caution: * It is up to the caller to make sure ehArgs[] is correct for * the particular event handler being called. The first thing * a script event handler method does is to unmarshall the args * from ehArgs[] and will throw an array bounds or cast exception * if it can't. */ private Exception StartEventHandler(ScriptEventCode eventCode, object[] ehArgs) { // We use this.eventCode == ScriptEventCode.None to indicate we are idle. // So trying to execute ScriptEventCode.None might make a mess. if (eventCode == ScriptEventCode.None) { return(new Exception("Can't process ScriptEventCode.None")); } // Silly to even try if there is no handler defined for this event. if (((int)eventCode >= 0) && (m_ObjCode.scriptEventHandlerTable[this.stateCode, (int)eventCode] == null)) { return(null); } // The microthread shouldn't be processing any event code. // These are assert checks so we throw them directly as exceptions. if (this.eventCode != ScriptEventCode.None) { throw new Exception("still processing event " + this.eventCode.ToString()); } // Save eventCode so we know what event handler to run in the microthread. // And it also marks us busy so we can't be started again and this event lost. this.eventCode = eventCode; this.ehArgs = ehArgs; // This calls ScriptUThread.Main() directly, and returns when Main() [indirectly] // calls Suspend() or when Main() returns, whichever occurs first. // Setting stackFrames = null means run the event handler from the beginning // without doing any stack frame restores first. this.stackFrames = null; return(StartEx()); }
public void CheckRunLockInvariants(bool throwIt) { /* * If not executing any event handler, there shouldn't be any saved stack frames. * If executing an event handler, there should be some saved stack frames. */ bool active = (stackFrames != null); ScriptEventCode ec = this.eventCode; if (((ec == ScriptEventCode.None) && active) || ((ec != ScriptEventCode.None) && !active)) { m_log.Error("CheckRunLockInvariants: script=" + m_DescName); m_log.Error("CheckRunLockInvariants: eventcode=" + ec.ToString() + ", active=" + active.ToString()); m_log.Error("CheckRunLockInvariants: m_RunOnePhase=" + m_RunOnePhase); m_log.Error("CheckRunLockInvariants: lastec=" + lastEventCode + ", lastAct=" + lastActive + ", lastPhase=" + lastRunPhase); if (throwIt) { throw new Exception("CheckRunLockInvariants: eventcode=" + ec.ToString() + ", active=" + active.ToString()); } } lastEventCode = ec; lastActive = active; lastRunPhase = m_RunOnePhase; }
/** * @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 There was an exception whilst starting/running a script event handler. * Maybe we handle it directly or just print an error message. */ private void HandleScriptException(Exception e) { // The script threw some kind of exception that was not caught at // script level, so the script is no longer running an event handler. ScriptEventCode curevent = eventCode; eventCode = ScriptEventCode.None; stackFrames = null; if (m_Part == null || m_Part.Inventory == null) { //we are gone and don't know it still m_SleepUntil = DateTime.MaxValue; return; } if (e is ScriptDeleteException) { // Script did something like llRemoveInventory(llGetScriptName()); // ... to delete itself from the object. m_SleepUntil = DateTime.MaxValue; Verbose("[YEngine]: script self-delete {0}", m_ItemID); m_Part.Inventory.RemoveInventoryItem(m_ItemID); } else if (e is ScriptDieException) { // Script did an llDie() m_RunOnePhase = "dying..."; m_SleepUntil = DateTime.MaxValue; m_Engine.World.DeleteSceneObject(m_Part.ParentGroup, false); } else if (e is ScriptResetException) { // Script did an llResetScript(). m_RunOnePhase = "resetting..."; ResetLocked("HandleScriptResetException"); } else if (e is ScriptException) { // Some general script error. SendScriptErrorMessage(e, curevent); } else { // Some general script error. SendErrorMessage(e); } }
private void SendScriptErrorMessage(Exception e, ScriptEventCode ev) { StringBuilder msg = new StringBuilder(); msg.Append("YEngine: "); if (e.Message != null) { msg.Append(e.Message); } msg.Append(" (script: "); msg.Append(m_Item.Name); msg.Append(" event: "); msg.Append(ev.ToString()); msg.Append(" primID: "); msg.Append(m_Part.UUID.ToString()); msg.Append(" at: <"); Vector3 pos = m_Part.AbsolutePosition; msg.Append((int)Math.Floor(pos.X)); msg.Append(','); msg.Append((int)Math.Floor(pos.Y)); msg.Append(','); msg.Append((int)Math.Floor(pos.Z)); msg.Append(">) Script must be Reset to re-enable.\n"); string msgst = msg.ToString(); if (msgst.Length > 1000) { msgst = msgst.Substring(0, 1000); } m_Engine.World.SimChat(Utils.StringToBytes(msgst), ChatTypeEnum.DebugChannel, 2147483647, m_Part.AbsolutePosition, m_Part.Name, m_Part.UUID, false); m_log.Debug(string.Format( "[SCRIPT ERROR]: {0} (at event {1}, part {2} {3} at {4} in {5}", (e.Message == null)? "" : e.Message, ev.ToString(), m_Part.Name, m_Part.UUID, m_Part.AbsolutePosition, m_Part.ParentGroup.Scene.Name)); m_SleepUntil = DateTime.MaxValue; }
private void processXstate(XmlDocument doc) { XmlNodeList rootL = doc.GetElementsByTagName("ScriptState"); if (rootL.Count != 1) { throw new Exception("Xstate <ScriptState> missing"); } XmlNode rootNode = rootL[0]; if (rootNode == null) { throw new Exception("Xstate root missing"); } string stateName = ""; bool running = false; UUID permsGranter = UUID.Zero; int permsMask = 0; double minEventDelay = 0.0; Object[] pluginData = new Object[0]; LinkedList <EventParams> eventQueue = new LinkedList <EventParams>(); Dictionary <string, int> intNames = new Dictionary <string, int>(); Dictionary <string, int> doubleNames = new Dictionary <string, int>(); Dictionary <string, int> stringNames = new Dictionary <string, int>(); Dictionary <string, int> vectorNames = new Dictionary <string, int>(); Dictionary <string, int> rotationNames = new Dictionary <string, int>(); Dictionary <string, int> listNames = new Dictionary <string, int>(); int nn = m_ObjCode.globalVarNames.Count; int[] ints = null; double[] doubles = null; string[] strings = null; LSL_Vector[] vectors = null; LSL_Rotation[] rotations = null; LSL_List[] lists = null; if (nn > 0) { if (m_ObjCode.globalVarNames.ContainsKey("iarIntegers")) { getvarNames(m_ObjCode.globalVarNames["iarIntegers"], intNames); ints = new int[m_ObjCode.globalVarNames["iarIntegers"].Count]; } if (m_ObjCode.globalVarNames.ContainsKey("iarFloats")) { getvarNames(m_ObjCode.globalVarNames["iarFloats"], doubleNames); doubles = new double[m_ObjCode.globalVarNames["iarFloats"].Count]; } if (m_ObjCode.globalVarNames.ContainsKey("iarVectors")) { getvarNames(m_ObjCode.globalVarNames["iarVectors"], vectorNames); vectors = new LSL_Vector[m_ObjCode.globalVarNames["iarVectors"].Count]; } if (m_ObjCode.globalVarNames.ContainsKey("iarRotations")) { getvarNames(m_ObjCode.globalVarNames["iarRotations"], rotationNames); rotations = new LSL_Rotation[m_ObjCode.globalVarNames["iarRotations"].Count]; } if (m_ObjCode.globalVarNames.ContainsKey("iarStrings")) { getvarNames(m_ObjCode.globalVarNames["iarStrings"], stringNames); strings = new string[m_ObjCode.globalVarNames["iarStrings"].Count]; } if (m_ObjCode.globalVarNames.ContainsKey("iarLists")) { getvarNames(m_ObjCode.globalVarNames["iarLists"], listNames); lists = new LSL_List[m_ObjCode.globalVarNames["iarLists"].Count]; } } int heapsz = 0; try { XmlNodeList partL = rootNode.ChildNodes; foreach (XmlNode part in partL) { switch (part.Name) { case "State": stateName = part.InnerText; break; case "Running": running = bool.Parse(part.InnerText); break; case "Variables": int indx; XmlNodeList varL = part.ChildNodes; foreach (XmlNode var in varL) { string varName; object o = ReadXTypedValue(var, out varName); Type otype = o.GetType(); if (otype == typeof(LSL_Integer)) { if (intNames.TryGetValue(varName, out indx)) { ints[indx] = ((LSL_Integer)o); } continue; } if (otype == typeof(LSL_Float)) { if (doubleNames.TryGetValue(varName, out indx)) { doubles[indx] = ((LSL_Float)o); } continue; } if (otype == typeof(LSL_String)) { if (stringNames.TryGetValue(varName, out indx)) { strings[indx] = ((LSL_String)o); heapsz += ((LSL_String)o).Length; } continue; } if (otype == typeof(LSL_Rotation)) { if (rotationNames.TryGetValue(varName, out indx)) { rotations[indx] = ((LSL_Rotation)o); } continue; } if (otype == typeof(LSL_Vector)) { if (vectorNames.TryGetValue(varName, out indx)) { vectors[indx] = ((LSL_Vector)o); } continue; } if (otype == typeof(LSL_Key)) { if (stringNames.TryGetValue(varName, out indx)) { strings[indx] = ((LSL_Key)o); heapsz += ((LSL_String)o).Length; } continue; } if (otype == typeof(UUID)) { if (stringNames.TryGetValue(varName, out indx)) { LSL_String id = ((UUID)o).ToString(); strings[indx] = (id); heapsz += id.Length; } continue; } if (otype == typeof(LSL_List)) { if (listNames.TryGetValue(varName, out indx)) { LSL_List lo = (LSL_List)o; lists[indx] = (lo); heapsz += lo.Size; } continue; } } break; case "Queue": XmlNodeList itemL = part.ChildNodes; foreach (XmlNode item in itemL) { List <Object> parms = new List <Object>(); List <DetectParams> detected = new List <DetectParams>(); string eventName = item.Attributes.GetNamedItem("event").Value; XmlNodeList eventL = item.ChildNodes; foreach (XmlNode evt in eventL) { switch (evt.Name) { case "Params": XmlNodeList prms = evt.ChildNodes; foreach (XmlNode pm in prms) { parms.Add(ReadXTypedValue(pm)); } break; case "Detected": XmlNodeList detL = evt.ChildNodes; foreach (XmlNode det in detL) { string vect = det.Attributes.GetNamedItem("pos").Value; LSL_Vector v = new LSL_Vector(vect); int d_linkNum = 0; UUID d_group = UUID.Zero; string d_name = String.Empty; UUID d_owner = UUID.Zero; LSL_Vector d_position = new LSL_Vector(); LSL_Rotation d_rotation = new LSL_Rotation(); int d_type = 0; LSL_Vector d_velocity = new LSL_Vector(); try { string tmp; tmp = det.Attributes.GetNamedItem("linkNum").Value; int.TryParse(tmp, out d_linkNum); tmp = det.Attributes.GetNamedItem("group").Value; UUID.TryParse(tmp, out d_group); d_name = det.Attributes.GetNamedItem("name").Value; tmp = det.Attributes.GetNamedItem("owner").Value; UUID.TryParse(tmp, out d_owner); tmp = det.Attributes.GetNamedItem("position").Value; d_position = new LSL_Types.Vector3(tmp); tmp = det.Attributes.GetNamedItem("rotation").Value; d_rotation = new LSL_Rotation(tmp); tmp = det.Attributes.GetNamedItem("type").Value; int.TryParse(tmp, out d_type); tmp = det.Attributes.GetNamedItem("velocity").Value; d_velocity = new LSL_Vector(tmp); } catch (Exception) // Old version XML { } UUID uuid = new UUID(); UUID.TryParse(det.InnerText, out uuid); DetectParams d = new DetectParams(); d.Key = uuid; d.OffsetPos = v; d.LinkNum = d_linkNum; d.Group = d_group; d.Name = d_name; d.Owner = d_owner; d.Position = d_position; d.Rotation = d_rotation; d.Type = d_type; d.Velocity = d_velocity; detected.Add(d); } break; } } EventParams ep = new EventParams( eventName, parms.ToArray(), detected.ToArray()); eventQueue.AddLast(ep); } break; case "Plugins": List <Object> olist = new List <Object>(); XmlNodeList itemLP = part.ChildNodes; foreach (XmlNode item in itemLP) { olist.Add(ReadXTypedValue(item)); } pluginData = olist.ToArray(); break; case "Permissions": string tmpPerm; int mask = 0; tmpPerm = part.Attributes.GetNamedItem("mask").Value; if (tmpPerm != null) { int.TryParse(tmpPerm, out mask); if (mask != 0) { tmpPerm = part.Attributes.GetNamedItem("granter").Value; if (tmpPerm != null) { UUID granter = new UUID(); UUID.TryParse(tmpPerm, out granter); if (granter != UUID.Zero) { permsMask = mask; permsGranter = granter; } } } } break; case "MinEventDelay": double.TryParse(part.InnerText, out minEventDelay); break; } } } catch { throw new Exception("Xstate fail decode"); } int k = 0; stateCode = 0; foreach (string sn in m_ObjCode.stateNames) { if (stateName == sn) { stateCode = k; break; } k++; } eventCode = ScriptEventCode.None; m_Running = running; doGblInit = false; m_Item.PermsGranter = permsGranter; m_Item.PermsMask = permsMask; m_Part.Inventory.UpdateInventoryItem(m_Item, false, false); lock (m_RunLock) { glblVars.iarIntegers = ints; glblVars.iarFloats = doubles; glblVars.iarVectors = vectors; glblVars.iarRotations = rotations; glblVars.iarStrings = strings; glblVars.iarLists = lists; AddHeapUse(heapsz); CheckRunLockInvariants(true); } lock (m_QueueLock) { m_DetectParams = null; foreach (EventParams evt in m_EventQueue) { eventQueue.AddLast(evt); } m_EventQueue = eventQueue; for (int i = m_EventCounts.Length; --i >= 0;) { m_EventCounts[i] = 0; } foreach (EventParams evt in m_EventQueue) { ScriptEventCode eventCode = (ScriptEventCode)Enum.Parse(typeof(ScriptEventCode), evt.EventName); m_EventCounts[(int)eventCode]++; } } AsyncCommandManager.CreateFromData(m_Engine, m_LocalID, m_ItemID, m_Part.UUID, pluginData); }
/** * @brief Load script state from the given XML doc into the script memory * <ScriptState Engine="YEngine" Asset=...> * <Running>...</Running> * <DoGblInit>...</DoGblInit> * <Permissions granted=... mask=... /> * RestoreDetectParams() * <Plugins> * ExtractXMLObjectArray("plugin") * </Plugins> * <Snapshot> * MigrateInEventHandler() * </Snapshot> * </ScriptState> */ private void LoadScriptState(XmlDocument doc) { // Everything we know is enclosed in <ScriptState>...</ScriptState> XmlElement scriptStateN = (XmlElement)doc.SelectSingleNode("ScriptState"); if (scriptStateN == null) { throw new Exception("no <ScriptState> tag"); } XmlElement XvariablesN = null; string sen = scriptStateN.GetAttribute("Engine"); if ((sen == null) || (sen != m_Engine.ScriptEngineName)) { XvariablesN = (XmlElement)scriptStateN.SelectSingleNode("Variables"); if (XvariablesN == null) { throw new Exception("<ScriptState> missing Engine=\"YEngine\" attribute"); } processXstate(doc); return; } // AssetID is unique for the script source text so make sure the // state file was written for that source file string assetID = scriptStateN.GetAttribute("Asset"); if (assetID != m_Item.AssetID.ToString()) { throw new Exception("<ScriptState> assetID mismatch"); } // Also match the sourceHash in case script was // loaded via 'xmroption fetchsource' and has changed string sourceHash = scriptStateN.GetAttribute("SourceHash"); if ((sourceHash == null) || (sourceHash != m_ObjCode.sourceHash)) { throw new Exception("<ScriptState> SourceHash mismatch"); } // Get various attributes XmlElement runningN = (XmlElement)scriptStateN.SelectSingleNode("Running"); m_Running = bool.Parse(runningN.InnerText); XmlElement doGblInitN = (XmlElement)scriptStateN.SelectSingleNode("DoGblInit"); doGblInit = bool.Parse(doGblInitN.InnerText); XmlElement permissionsN = (XmlElement)scriptStateN.SelectSingleNode("Permissions"); m_Item.PermsGranter = new UUID(permissionsN.GetAttribute("granter")); m_Item.PermsMask = Convert.ToInt32(permissionsN.GetAttribute("mask")); m_Part.Inventory.UpdateInventoryItem(m_Item, false, false); // get values used by stuff like llDetectedGrab, etc. DetectParams[] detParams = RestoreDetectParams(scriptStateN.SelectSingleNode("DetectArray")); // Restore queued events LinkedList <EventParams> eventQueue = RestoreEventQueue(scriptStateN.SelectSingleNode("EventQueue")); // Restore timers and listeners XmlElement pluginN = (XmlElement)scriptStateN.SelectSingleNode("Plugins"); Object[] pluginData = ExtractXMLObjectArray(pluginN, "plugin"); // Script's global variables and stack contents XmlElement snapshotN = (XmlElement)scriptStateN.SelectSingleNode("Snapshot"); Byte[] data = Convert.FromBase64String(snapshotN.InnerText); MemoryStream ms = new MemoryStream(); ms.Write(data, 0, data.Length); ms.Seek(0, SeekOrigin.Begin); MigrateInEventHandler(ms); ms.Close(); // Restore event queues, preserving any events that queued // whilst we were restoring the state lock (m_QueueLock) { m_DetectParams = detParams; foreach (EventParams evt in m_EventQueue) { eventQueue.AddLast(evt); } m_EventQueue = eventQueue; for (int i = m_EventCounts.Length; --i >= 0;) { m_EventCounts[i] = 0; } foreach (EventParams evt in m_EventQueue) { ScriptEventCode eventCode = (ScriptEventCode)Enum.Parse(typeof(ScriptEventCode), evt.EventName); m_EventCounts[(int)eventCode]++; } } // Requeue timer and listeners (possibly queuing new events) AsyncCommandManager.CreateFromData(m_Engine, m_LocalID, m_ItemID, m_Part.UUID, pluginData); }
/************************************************************************************\ * This module contains these externally useful methods: * * PostEvent() - queues an event to script and wakes script thread to process it * * RunOne() - runs script for a time slice or until it volunteers to give up cpu * * CallSEH() - runs in the microthread to call the event handler * \************************************************************************************/ /** * @brief This can be called in any thread (including the script thread itself) * to queue event to script for processing. */ public void PostEvent(EventParams evt) { ScriptEventCode evc = (ScriptEventCode)Enum.Parse(typeof(ScriptEventCode), evt.EventName); // Put event on end of event queue. bool startIt = false; bool wakeIt = false; lock (m_QueueLock) { bool construct = (m_IState == XMRInstState.CONSTRUCT); // Ignore event if we don't even have such an handler in any state. // We can't be state-specific here because state might be different // by the time this event is dequeued and delivered to the script. if (!construct && // make sure m_HaveEventHandlers is filled in ((uint)evc < (uint)m_HaveEventHandlers.Length) && !m_HaveEventHandlers[(int)evc]) // don't bother if we don't have such a handler in any state { return; } // Not running means we ignore any incoming events. // But queue if still constructing because m_Running is not yet valid. if (!m_Running && !construct) { return; } // Only so many of each event type allowed to queue. if ((uint)evc < (uint)m_EventCounts.Length) { if (evc == ScriptEventCode.timer) { if (m_EventCounts[(int)evc] >= 1) { return; } } else if (m_EventCounts[(int)evc] >= MAXEVENTQUEUE) { return; } m_EventCounts[(int)evc]++; } // Put event on end of instance's event queue. LinkedListNode <EventParams> lln = new LinkedListNode <EventParams>(evt); switch (evc) { // These need to go first. The only time we manually // queue them is for the default state_entry() and we // need to make sure they go before any attach() events // so the heapLimit value gets properly initialized. case ScriptEventCode.state_entry: m_EventQueue.AddFirst(lln); break; // The attach event sneaks to the front of the queue. // This is needed for quantum limiting to work because // we want the attach(NULL_KEY) event to come in front // of all others so the m_DetachQuantum won't run out // before attach(NULL_KEY) is executed. case ScriptEventCode.attach: if (evt.Params[0].ToString() == UUID.Zero.ToString()) { LinkedListNode <EventParams> lln2 = null; for (lln2 = m_EventQueue.First; lln2 != null; lln2 = lln2.Next) { EventParams evt2 = lln2.Value; ScriptEventCode evc2 = (ScriptEventCode)Enum.Parse(typeof(ScriptEventCode), evt2.EventName); if ((evc2 != ScriptEventCode.state_entry) && (evc2 != ScriptEventCode.attach)) { break; } } if (lln2 == null) { m_EventQueue.AddLast(lln); } else { m_EventQueue.AddBefore(lln2, lln); } // If we're detaching, limit the qantum. This will also // cause the script to self-suspend after running this // event m_DetachReady.Reset(); m_DetachQuantum = 100; } else { m_EventQueue.AddLast(lln); } break; // All others just go on end in the order queued. default: m_EventQueue.AddLast(lln); break; } // If instance is idle (ie, not running or waiting to run), // flag it to be on m_StartQueue as we are about to do so. // Flag it now before unlocking so another thread won't try // to do the same thing right now. // Dont' flag it if it's still suspended! if ((m_IState == XMRInstState.IDLE) && !m_Suspended) { m_IState = XMRInstState.ONSTARTQ; startIt = true; } // If instance is sleeping (ie, possibly in xmrEventDequeue), // wake it up if event is in the mask. if ((m_SleepUntil > DateTime.UtcNow) && !m_Suspended) { int evc1 = (int)evc; int evc2 = evc1 - 32; if ((((uint)evc1 < (uint)32) && (((m_SleepEventMask1 >> evc1) & 1) != 0)) || (((uint)evc2 < (uint)32) && (((m_SleepEventMask2 >> evc2) & 1) != 0))) { wakeIt = true; } } } // If transitioned from IDLE->ONSTARTQ, actually go insert it // on m_StartQueue and give the RunScriptThread() a wake-up. if (startIt) { m_Engine.QueueToStart(this); } // Likewise, if the event mask triggered a wake, wake it up. if (wakeIt) { m_SleepUntil = DateTime.MinValue; m_Engine.WakeFromSleep(this); } }
// This is called in the script thread to step script until it calls // CheckRun(). It returns what the instance's next state should be, // ONSLEEPQ, ONYIELDQ, SUSPENDED or FINISHED. public XMRInstState RunOne() { DateTime now = DateTime.UtcNow; m_SliceStart = Util.GetTimeStampMS(); // If script has called llSleep(), don't do any more until time is up. m_RunOnePhase = "check m_SleepUntil"; if (m_SleepUntil > now) { m_RunOnePhase = "return is sleeping"; return(XMRInstState.ONSLEEPQ); } // Also, someone may have called Suspend(). m_RunOnePhase = "check m_SuspendCount"; if (m_SuspendCount > 0) { m_RunOnePhase = "return is suspended"; return(XMRInstState.SUSPENDED); } // Make sure we aren't being migrated in or out and prevent that // whilst we are in here. If migration has it locked, don't call // back right away, delay a bit so we don't get in infinite loop. m_RunOnePhase = "lock m_RunLock"; if (!Monitor.TryEnter(m_RunLock)) { m_SleepUntil = now.AddMilliseconds(3); m_RunOnePhase = "return was locked"; return(XMRInstState.ONSLEEPQ); } try { m_RunOnePhase = "check entry invariants"; CheckRunLockInvariants(true); Exception e = null; // Maybe it has been Disposed() if (m_Part == null) { m_RunOnePhase = "runone saw it disposed"; return(XMRInstState.DISPOSED); } // Do some more of the last event if it didn't finish. if (this.eventCode != ScriptEventCode.None) { lock (m_QueueLock) { if (m_DetachQuantum > 0 && --m_DetachQuantum == 0) { m_Suspended = true; m_DetachReady.Set(); m_RunOnePhase = "detach quantum went zero"; CheckRunLockInvariants(true); return(XMRInstState.FINISHED); } } m_RunOnePhase = "resume old event handler"; m_LastRanAt = now; m_InstEHSlice++; callMode = CallMode_NORMAL; e = ResumeEx(); } // Otherwise, maybe we can dequeue a new event and start // processing it. else { m_RunOnePhase = "lock event queue"; EventParams evt = null; ScriptEventCode evc = ScriptEventCode.None; lock (m_QueueLock) { // We can't get here unless the script has been resumed // after creation, then suspended again, and then had // an event posted to it. We just pretend there is no // event int he queue and let the normal mechanics // carry out the suspension. A Resume will handle the // restarting gracefully. This is taking the easy way // out and may be improved in the future. if (m_Suspended) { m_RunOnePhase = "m_Suspended is set"; CheckRunLockInvariants(true); return(XMRInstState.FINISHED); } m_RunOnePhase = "dequeue event"; if (m_EventQueue.First != null) { evt = m_EventQueue.First.Value; if (m_DetachQuantum > 0) { evc = (ScriptEventCode)Enum.Parse(typeof(ScriptEventCode), evt.EventName); if (evc != ScriptEventCode.attach) { // This is the case where the attach event // has completed and another event is queued // Stop it from running and suspend m_Suspended = true; m_DetachReady.Set(); m_DetachQuantum = 0; m_RunOnePhase = "nothing to do #3"; CheckRunLockInvariants(true); return(XMRInstState.FINISHED); } } m_EventQueue.RemoveFirst(); evc = (ScriptEventCode)Enum.Parse(typeof(ScriptEventCode), evt.EventName); if ((int)evc >= 0) { m_EventCounts[(int)evc]--; } } // If there is no event to dequeue, don't run this script // until another event gets queued. if (evt == null) { if (m_DetachQuantum > 0) { // This will happen if the attach event has run // and exited with time slice left. m_Suspended = true; m_DetachReady.Set(); m_DetachQuantum = 0; } m_RunOnePhase = "nothing to do #4"; CheckRunLockInvariants(true); return(XMRInstState.FINISHED); } } // Dequeued an event, so start it going until it either // finishes or it calls CheckRun(). m_RunOnePhase = "start event handler"; m_DetectParams = evt.DetectParams; m_LastRanAt = now; m_InstEHEvent++; e = StartEventHandler(evc, evt.Params); } m_RunOnePhase = "done running"; m_CPUTime += DateTime.UtcNow.Subtract(now).TotalMilliseconds; // Maybe it puqued. if (e != null) { m_RunOnePhase = "handling exception " + e.Message; HandleScriptException(e); m_RunOnePhase = "return had exception " + e.Message; CheckRunLockInvariants(true); return(XMRInstState.FINISHED); } // If event handler completed, get rid of detect params. if (this.eventCode == ScriptEventCode.None) { m_DetectParams = null; } } finally { m_RunOnePhase += "; checking exit invariants and unlocking"; CheckRunLockInvariants(false); Monitor.Exit(m_RunLock); } // Cycle script through the yield queue and call it back asap. m_RunOnePhase = "last return"; return(XMRInstState.ONYIELDQ); }
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 <EventParams> 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<n> 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<n> 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 } } } }
private void SendScriptErrorMessage(Exception e, ScriptEventCode ev) { StringBuilder msg = new StringBuilder(); bool toowner = false; msg.Append("YEngine: "); if (e.Message != null) { string text = e.Message; if (text.StartsWith("(OWNER)")) { text = text.Substring(7); toowner = true; } msg.Append(text); } msg.Append(" (script: "); msg.Append(m_Item.Name); msg.Append(" event: "); msg.Append(ev.ToString()); msg.Append(" primID: "); msg.Append(m_Part.UUID.ToString()); msg.Append(" at: <"); Vector3 pos = m_Part.AbsolutePosition; msg.Append((int)Math.Floor(pos.X)); msg.Append(','); msg.Append((int)Math.Floor(pos.Y)); msg.Append(','); msg.Append((int)Math.Floor(pos.Z)); msg.Append(">) Script must be Reset to re-enable.\n"); string msgst = msg.ToString(); if (msgst.Length > 1000) { msgst = msgst.Substring(0, 1000); } if (toowner) { ScenePresence sp = m_Engine.World.GetScenePresence(m_Part.OwnerID); if (sp != null && !sp.IsNPC) { m_Engine.World.SimChatToAgent(m_Part.OwnerID, Utils.StringToBytes(msgst), 0x7FFFFFFF, m_Part.AbsolutePosition, m_Part.Name, m_Part.UUID, false); } } else { m_Engine.World.SimChat(Utils.StringToBytes(msgst), ChatTypeEnum.DebugChannel, 0x7FFFFFFF, m_Part.AbsolutePosition, m_Part.Name, m_Part.UUID, false); } m_log.Debug(string.Format( "[SCRIPT ERROR]: {0} (at event {1}, part {2} {3} at {4} in {5}", (e.Message == null)? "" : e.Message, ev.ToString(), m_Part.Name, m_Part.UUID, m_Part.AbsolutePosition, m_Part.ParentGroup.Scene.Name)); m_SleepUntil = DateTime.MaxValue; }