aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/ScriptEngine/XMREngine/XMRInstRun.cs
diff options
context:
space:
mode:
authorUbitUmarov2018-02-02 12:49:40 +0000
committerUbitUmarov2018-02-02 12:49:40 +0000
commit83e2fee71be695b78438e0c9dc50b649a539d0e3 (patch)
treeaf213fea5ebfa4e773af6942c753e7f3bba0d83f /OpenSim/Region/ScriptEngine/XMREngine/XMRInstRun.cs
parentMerge branch 'master' into httptests (diff)
downloadopensim-SC-83e2fee71be695b78438e0c9dc50b649a539d0e3.zip
opensim-SC-83e2fee71be695b78438e0c9dc50b649a539d0e3.tar.gz
opensim-SC-83e2fee71be695b78438e0c9dc50b649a539d0e3.tar.bz2
opensim-SC-83e2fee71be695b78438e0c9dc50b649a539d0e3.tar.xz
add experimental script engine XMRengine donated by mrieker (DreamNation) And our Melanie. ***DANGER*** ***TESTONLY*** ***disable HG*** dont leave running when not looking... tp/crossing to Xengine will reset scripts. i do see a few issues but should be testable, so we can decide if we should invest more on it.
Diffstat (limited to '')
-rw-r--r--OpenSim/Region/ScriptEngine/XMREngine/XMRInstRun.cs1051
1 files changed, 1051 insertions, 0 deletions
diff --git a/OpenSim/Region/ScriptEngine/XMREngine/XMRInstRun.cs b/OpenSim/Region/ScriptEngine/XMREngine/XMRInstRun.cs
new file mode 100644
index 0000000..61ae549
--- /dev/null
+++ b/OpenSim/Region/ScriptEngine/XMREngine/XMRInstRun.cs
@@ -0,0 +1,1051 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Threading;
30using System.Reflection;
31using System.Collections;
32using System.Collections.Generic;
33using System.Reflection.Emit;
34using System.Runtime.Remoting.Lifetime;
35using System.Security.Policy;
36using System.IO;
37using System.Xml;
38using System.Text;
39using OpenMetaverse;
40using OpenSim.Framework;
41using OpenSim.Region.Framework.Interfaces;
42using OpenSim.Region.ScriptEngine.Interfaces;
43using OpenSim.Region.ScriptEngine.Shared;
44using OpenSim.Region.ScriptEngine.Shared.Api;
45using OpenSim.Region.ScriptEngine.Shared.ScriptBase;
46using OpenSim.Region.ScriptEngine.XMREngine;
47using OpenSim.Region.Framework.Scenes;
48using log4net;
49
50using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
51using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
52using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
53using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
54using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
55using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
56using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
57
58namespace OpenSim.Region.ScriptEngine.XMREngine
59{
60 public partial class XMRInstance
61 {
62 /************************************************************************************\
63 * This module contains these externally useful methods: *
64 * PostEvent() - queues an event to script and wakes script thread to process it *
65 * RunOne() - runs script for a time slice or until it volunteers to give up cpu *
66 * CallSEH() - runs in the microthread to call the event handler *
67 \************************************************************************************/
68
69 /**
70 * @brief This can be called in any thread (including the script thread itself)
71 * to queue event to script for processing.
72 */
73 public void PostEvent(EventParams evt)
74 {
75 ScriptEventCode evc = (ScriptEventCode)Enum.Parse (typeof (ScriptEventCode),
76 evt.EventName);
77
78 /*
79 * Put event on end of event queue.
80 */
81 bool startIt = false;
82 bool wakeIt = false;
83 lock (m_QueueLock)
84 {
85 bool construct = (m_IState == XMRInstState.CONSTRUCT);
86
87 /*
88 * Ignore event if we don't even have such an handler in any state.
89 * We can't be state-specific here because state might be different
90 * by the time this event is dequeued and delivered to the script.
91 */
92 if (!construct && // make sure m_HaveEventHandlers is filled in
93 ((uint)evc < (uint)m_HaveEventHandlers.Length) &&
94 !m_HaveEventHandlers[(int)evc]) { // don't bother if we don't have such a handler in any state
95 return;
96 }
97
98 /*
99 * Not running means we ignore any incoming events.
100 * But queue if still constructing because m_Running is not yet valid.
101 */
102 if (!m_Running && !construct) {
103 return;
104 }
105
106 /*
107 * Only so many of each event type allowed to queue.
108 */
109 if ((uint)evc < (uint)m_EventCounts.Length) {
110 int maxAllowed = MAXEVENTQUEUE;
111 if (evc == ScriptEventCode.timer) maxAllowed = 1;
112 if (m_EventCounts[(int)evc] >= maxAllowed)
113 {
114 return;
115 }
116 m_EventCounts[(int)evc] ++;
117 }
118
119 /*
120 * Put event on end of instance's event queue.
121 */
122 LinkedListNode<EventParams> lln = new LinkedListNode<EventParams>(evt);
123 switch (evc) {
124
125 /*
126 * These need to go first. The only time we manually
127 * queue them is for the default state_entry() and we
128 * need to make sure they go before any attach() events
129 * so the heapLimit value gets properly initialized.
130 */
131 case ScriptEventCode.state_entry: {
132 m_EventQueue.AddFirst(lln);
133 break;
134 }
135
136 /*
137 * The attach event sneaks to the front of the queue.
138 * This is needed for quantum limiting to work because
139 * we want the attach(NULL_KEY) event to come in front
140 * of all others so the m_DetachQuantum won't run out
141 * before attach(NULL_KEY) is executed.
142 */
143 case ScriptEventCode.attach: {
144 if (evt.Params[0].ToString() == UUID.Zero.ToString())
145 {
146 LinkedListNode<EventParams> lln2 = null;
147 for (lln2 = m_EventQueue.First; lln2 != null; lln2 = lln2.Next) {
148 EventParams evt2 = lln2.Value;
149 ScriptEventCode evc2 = (ScriptEventCode)Enum.Parse (typeof (ScriptEventCode),
150 evt2.EventName);
151 if ((evc2 != ScriptEventCode.state_entry) &&
152 (evc2 != ScriptEventCode.attach)) break;
153 }
154 if (lln2 == null) {
155 m_EventQueue.AddLast(lln);
156 } else {
157 m_EventQueue.AddBefore(lln2, lln);
158 }
159 /* If we're detaching, limit the qantum. This will also
160 * cause the script to self-suspend after running this
161 * event
162 */
163
164 m_DetachReady.Reset();
165 m_DetachQuantum = 100;
166 }
167 else
168 {
169 m_EventQueue.AddLast(lln);
170 }
171 break;
172 }
173
174 /*
175 * All others just go on end in the order queued.
176 */
177 default: {
178 m_EventQueue.AddLast(lln);
179 break;
180 }
181 }
182
183 /*
184 * If instance is idle (ie, not running or waiting to run),
185 * flag it to be on m_StartQueue as we are about to do so.
186 * Flag it now before unlocking so another thread won't try
187 * to do the same thing right now.
188 * Dont' flag it if it's still suspended!
189 */
190 if ((m_IState == XMRInstState.IDLE) && !m_Suspended) {
191 m_IState = XMRInstState.ONSTARTQ;
192 startIt = true;
193 }
194
195 /*
196 * If instance is sleeping (ie, possibly in xmrEventDequeue),
197 * wake it up if event is in the mask.
198 */
199 if ((m_SleepUntil > DateTime.UtcNow) && !m_Suspended) {
200 int evc1 = (int)evc;
201 int evc2 = evc1 - 32;
202 if ((((uint)evc1 < (uint)32) && (((m_SleepEventMask1 >> evc1) & 1) != 0)) ||
203 (((uint)evc2 < (uint)32) && (((m_SleepEventMask2 >> evc2) & 1) != 0))) {
204 wakeIt = true;
205 }
206 }
207 }
208
209 /*
210 * If transitioned from IDLE->ONSTARTQ, actually go insert it
211 * on m_StartQueue and give the RunScriptThread() a wake-up.
212 */
213 if (startIt) {
214 m_Engine.QueueToStart(this);
215 }
216
217 /*
218 * Likewise, if the event mask triggered a wake, wake it up.
219 */
220 if (wakeIt) {
221 m_SleepUntil = DateTime.MinValue;
222 m_Engine.WakeFromSleep(this);
223 }
224 }
225
226 /*
227 * This is called in the script thread to step script until it calls
228 * CheckRun(). It returns what the instance's next state should be,
229 * ONSLEEPQ, ONYIELDQ, SUSPENDED or FINISHED.
230 */
231 public XMRInstState RunOne()
232 {
233 DateTime now = DateTime.UtcNow;
234
235 /*
236 * If script has called llSleep(), don't do any more until time is
237 * up.
238 */
239 m_RunOnePhase = "check m_SleepUntil";
240 if (m_SleepUntil > now)
241 {
242 m_RunOnePhase = "return is sleeping";
243 return XMRInstState.ONSLEEPQ;
244 }
245
246 /*
247 * Also, someone may have called Suspend().
248 */
249 m_RunOnePhase = "check m_SuspendCount";
250 if (m_SuspendCount > 0) {
251 m_RunOnePhase = "return is suspended";
252 return XMRInstState.SUSPENDED;
253 }
254
255 /*
256 * Make sure we aren't being migrated in or out and prevent that
257 * whilst we are in here. If migration has it locked, don't call
258 * back right away, delay a bit so we don't get in infinite loop.
259 */
260 m_RunOnePhase = "lock m_RunLock";
261 if (!Monitor.TryEnter (m_RunLock)) {
262 m_SleepUntil = now.AddMilliseconds(3);
263 m_RunOnePhase = "return was locked";
264 return XMRInstState.ONSLEEPQ;
265 }
266 try
267 {
268 m_RunOnePhase = "check entry invariants";
269 CheckRunLockInvariants(true);
270 Exception e = null;
271
272 /*
273 * Maybe we have been disposed.
274 */
275 m_RunOnePhase = "check disposed";
276 if (microthread == null)
277 {
278 m_RunOnePhase = "return disposed";
279 return XMRInstState.DISPOSED;
280 }
281
282 /*
283 * Do some more of the last event if it didn't finish.
284 */
285 else if (this.eventCode != ScriptEventCode.None)
286 {
287 lock (m_QueueLock)
288 {
289 if (m_DetachQuantum > 0 && --m_DetachQuantum == 0)
290 {
291 m_Suspended = true;
292 m_DetachReady.Set();
293 m_RunOnePhase = "detach quantum went zero";
294 CheckRunLockInvariants(true);
295 return XMRInstState.FINISHED;
296 }
297 }
298
299 m_RunOnePhase = "resume old event handler";
300 m_LastRanAt = now;
301 m_InstEHSlice ++;
302 callMode = CallMode_NORMAL;
303 e = microthread.ResumeEx ();
304 }
305
306 /*
307 * Otherwise, maybe we can dequeue a new event and start
308 * processing it.
309 */
310 else
311 {
312 m_RunOnePhase = "lock event queue";
313 EventParams evt = null;
314 ScriptEventCode evc = ScriptEventCode.None;
315
316 lock (m_QueueLock)
317 {
318
319 /* We can't get here unless the script has been resumed
320 * after creation, then suspended again, and then had
321 * an event posted to it. We just pretend there is no
322 * event int he queue and let the normal mechanics
323 * carry out the suspension. A Resume will handle the
324 * restarting gracefully. This is taking the easy way
325 * out and may be improved in the future.
326 */
327
328 if (m_Suspended)
329 {
330 m_RunOnePhase = "m_Suspended is set";
331 CheckRunLockInvariants(true);
332 return XMRInstState.FINISHED;
333 }
334
335 m_RunOnePhase = "dequeue event";
336 if (m_EventQueue.First != null)
337 {
338 evt = m_EventQueue.First.Value;
339 if (m_DetachQuantum > 0)
340 {
341 evc = (ScriptEventCode)Enum.Parse (typeof (ScriptEventCode),
342 evt.EventName);
343 if (evc != ScriptEventCode.attach)
344 {
345 /*
346 * This is the case where the attach event
347 * has completed and another event is queued
348 * Stop it from running and suspend
349 */
350 m_Suspended = true;
351 m_DetachReady.Set();
352 m_DetachQuantum = 0;
353 m_RunOnePhase = "nothing to do #3";
354 CheckRunLockInvariants(true);
355 return XMRInstState.FINISHED;
356 }
357 }
358 m_EventQueue.RemoveFirst();
359 evc = (ScriptEventCode)Enum.Parse (typeof (ScriptEventCode),
360 evt.EventName);
361 if ((int)evc >= 0) m_EventCounts[(int)evc] --;
362 }
363
364 /*
365 * If there is no event to dequeue, don't run this script
366 * until another event gets queued.
367 */
368 if (evt == null)
369 {
370 if (m_DetachQuantum > 0)
371 {
372 /*
373 * This will happen if the attach event has run
374 * and exited with time slice left.
375 */
376 m_Suspended = true;
377 m_DetachReady.Set();
378 m_DetachQuantum = 0;
379 }
380 m_RunOnePhase = "nothing to do #4";
381 CheckRunLockInvariants(true);
382 return XMRInstState.FINISHED;
383 }
384 }
385
386 /*
387 * Dequeued an event, so start it going until it either
388 * finishes or it calls CheckRun().
389 */
390 m_RunOnePhase = "start event handler";
391 m_DetectParams = evt.DetectParams;
392 m_LastRanAt = now;
393 m_InstEHEvent ++;
394 e = StartEventHandler (evc, evt.Params);
395 }
396 m_RunOnePhase = "done running";
397 m_CPUTime += DateTime.UtcNow.Subtract(now).TotalMilliseconds;
398
399 /*
400 * Maybe it puqued.
401 */
402 if (e != null)
403 {
404 m_RunOnePhase = "handling exception " + e.Message;
405 HandleScriptException(e);
406 m_RunOnePhase = "return had exception " + e.Message;
407 CheckRunLockInvariants(true);
408 return XMRInstState.FINISHED;
409 }
410
411 /*
412 * If event handler completed, get rid of detect params.
413 */
414 if (this.eventCode == ScriptEventCode.None)
415 {
416 m_DetectParams = null;
417 }
418 }
419 finally
420 {
421 m_RunOnePhase += "; checking exit invariants and unlocking";
422 CheckRunLockInvariants(false);
423 Monitor.Exit(m_RunLock);
424 }
425
426 /*
427 * Cycle script through the yield queue and call it back asap.
428 */
429 m_RunOnePhase = "last return";
430 return XMRInstState.ONYIELDQ;
431 }
432
433 /**
434 * @brief Immediately after taking m_RunLock or just before releasing it, check invariants.
435 */
436 private ScriptEventCode lastEventCode = ScriptEventCode.None;
437 private int lastActive = 0;
438 private string lastRunPhase = "";
439
440 public void CheckRunLockInvariants(bool throwIt)
441 {
442 /*
443 * If not executing any event handler, active should be 0 indicating the microthread stack is not in use.
444 * If executing an event handler, active should be -1 indicating stack is in use but suspended.
445 */
446 IScriptUThread uth = microthread;
447 if (uth != null) {
448 int active = uth.Active ();
449 ScriptEventCode ec = this.eventCode;
450 if (((ec == ScriptEventCode.None) && (active != 0)) ||
451 ((ec != ScriptEventCode.None) && (active >= 0))) {
452 Console.WriteLine("CheckRunLockInvariants: script=" + m_DescName);
453 Console.WriteLine("CheckRunLockInvariants: eventcode=" + ec.ToString() + ", active=" + active.ToString());
454 Console.WriteLine("CheckRunLockInvariants: m_RunOnePhase=" + m_RunOnePhase);
455 Console.WriteLine("CheckRunLockInvariants: lastec=" + lastEventCode + ", lastAct=" + lastActive + ", lastPhase=" + lastRunPhase);
456 if (throwIt) {
457 throw new Exception("CheckRunLockInvariants: eventcode=" + ec.ToString() + ", active=" + active.ToString());
458 }
459 }
460 lastEventCode = ec;
461 lastActive = active;
462 lastRunPhase = m_RunOnePhase;
463 }
464 }
465
466 /*
467 * Start event handler.
468 *
469 * Input:
470 * eventCode = code of event to be processed
471 * ehArgs = arguments for the event handler
472 *
473 * Caution:
474 * It is up to the caller to make sure ehArgs[] is correct for
475 * the particular event handler being called. The first thing
476 * a script event handler method does is to unmarshall the args
477 * from ehArgs[] and will throw an array bounds or cast exception
478 * if it can't.
479 */
480 private Exception StartEventHandler (ScriptEventCode eventCode, object[] ehArgs)
481 {
482 /*
483 * We use this.eventCode == ScriptEventCode.None to indicate we are idle.
484 * So trying to execute ScriptEventCode.None might make a mess.
485 */
486 if (eventCode == ScriptEventCode.None) {
487 return new Exception ("Can't process ScriptEventCode.None");
488 }
489
490 /*
491 * Silly to even try if there is no handler defined for this event.
492 */
493 if (((int)eventCode >= 0) && (m_ObjCode.scriptEventHandlerTable[this.stateCode,(int)eventCode] == null)) {
494 return null;
495 }
496
497 /*
498 * The microthread shouldn't be processing any event code.
499 * These are assert checks so we throw them directly as exceptions.
500 */
501 if (this.eventCode != ScriptEventCode.None) {
502 throw new Exception ("still processing event " + this.eventCode.ToString ());
503 }
504 int active = microthread.Active ();
505 if (active != 0) {
506 throw new Exception ("microthread is active " + active.ToString ());
507 }
508
509 /*
510 * Save eventCode so we know what event handler to run in the microthread.
511 * And it also marks us busy so we can't be started again and this event lost.
512 */
513 this.eventCode = eventCode;
514 this.ehArgs = ehArgs;
515
516 /*
517 * This calls ScriptUThread.Main() directly, and returns when Main() [indirectly]
518 * calls Suspend() or when Main() returns, whichever occurs first.
519 * Setting stackFrames = null means run the event handler from the beginning
520 * without doing any stack frame restores first.
521 */
522 this.stackFrames = null;
523 Exception e;
524 e = microthread.StartEx ();
525 return e;
526 }
527
528
529 /**
530 * @brief There was an exception whilst starting/running a script event handler.
531 * Maybe we handle it directly or just print an error message.
532 */
533 private void HandleScriptException(Exception e)
534 {
535 /*
536 * The script threw some kind of exception that was not caught at
537 * script level, so the script is no longer running an event handler.
538 */
539 eventCode = ScriptEventCode.None;
540
541 if (e is ScriptDeleteException)
542 {
543 /*
544 * Script did something like llRemoveInventory(llGetScriptName());
545 * ... to delete itself from the object.
546 */
547 m_SleepUntil = DateTime.MaxValue;
548 Verbose ("[XMREngine]: script self-delete {0}", m_ItemID);
549 m_Part.Inventory.RemoveInventoryItem(m_ItemID);
550 }
551 else if (e is ScriptDieException)
552 {
553 /*
554 * Script did an llDie()
555 */
556 m_RunOnePhase = "dying...";
557 m_SleepUntil = DateTime.MaxValue;
558 m_Engine.World.DeleteSceneObject(m_Part.ParentGroup, false);
559 }
560 else if (e is ScriptResetException)
561 {
562 /*
563 * Script did an llResetScript().
564 */
565 m_RunOnePhase = "resetting...";
566 ResetLocked("HandleScriptResetException");
567 }
568 else
569 {
570 /*
571 * Some general script error.
572 */
573 SendErrorMessage(e);
574 }
575 return;
576 }
577
578 /**
579 * @brief There was an exception running script event handler.
580 * Display error message and disable script (in a way
581 * that the script can be reset to be restarted).
582 */
583 private void SendErrorMessage(Exception e)
584 {
585 StringBuilder msg = new StringBuilder();
586
587 msg.Append ("[XMREngine]: Exception while running ");
588 msg.Append (m_ItemID);
589 msg.Append ('\n');
590
591 /*
592 * Add exception message.
593 */
594 string des = e.Message;
595 des = (des == null) ? "" : (": " + des);
596 msg.Append (e.GetType ().Name + des + "\n");
597
598 /*
599 * Tell script owner what to do.
600 */
601 msg.Append ("Prim: <");
602 msg.Append (m_Part.Name);
603 msg.Append (">, Script: <");
604 msg.Append (m_Item.Name);
605 msg.Append (">, Location: ");
606 msg.Append (m_Engine.World.RegionInfo.RegionName);
607 msg.Append (" <");
608 Vector3 pos = m_Part.AbsolutePosition;
609 msg.Append ((int) Math.Floor (pos.X));
610 msg.Append (',');
611 msg.Append ((int) Math.Floor (pos.Y));
612 msg.Append (',');
613 msg.Append ((int) Math.Floor (pos.Z));
614 msg.Append (">\nScript must be Reset to re-enable.\n");
615
616 /*
617 * Display full exception message in log.
618 */
619 m_log.Info (msg.ToString() + XMRExceptionStackString (e), e);
620
621 /*
622 * Give script owner the stack dump.
623 */
624 msg.Append (XMRExceptionStackString (e));
625
626 /*
627 * Send error message to owner.
628 * Suppress internal code stack trace lines.
629 */
630 string msgst = msg.ToString();
631 if (!msgst.EndsWith ("\n")) msgst += '\n';
632 int j = 0;
633 StringBuilder imstr = new StringBuilder ();
634 for (int i = 0; (i = msgst.IndexOf ('\n', i)) >= 0; j = ++ i) {
635 string line = msgst.Substring (j, i - j);
636 if (line.StartsWith ("at ")) {
637 if (line.StartsWith ("at (wrapper")) continue; // at (wrapper ...
638 int k = line.LastIndexOf (".cs:"); // ... .cs:linenumber
639 if (Int32.TryParse (line.Substring (k + 4), out k)) continue;
640 }
641 this.llOwnerSay (line);
642 imstr.Append (line);
643 imstr.Append ('\n');
644 }
645
646 /*
647 * Send as instant message in case user not online.
648 * Code modelled from llInstantMessage().
649 */
650 IMessageTransferModule transferModule = m_Engine.World.RequestModuleInterface<IMessageTransferModule>();
651 if (transferModule != null) {
652 UUID friendTransactionID = UUID.Random();
653 GridInstantMessage gim = new GridInstantMessage();
654 gim.fromAgentID = new Guid (m_Part.UUID.ToString());
655 gim.toAgentID = new Guid (m_Part.OwnerID.ToString ());
656 gim.imSessionID = new Guid(friendTransactionID.ToString());
657 gim.timestamp = (uint)Util.UnixTimeSinceEpoch();
658 gim.message = imstr.ToString ();
659 gim.dialog = (byte)19; // messgage from script
660 gim.fromGroup = false;
661 gim.offline = (byte)0;
662 gim.ParentEstateID = 0;
663 gim.Position = pos;
664 gim.RegionID = m_Engine.World.RegionInfo.RegionID.Guid;
665 gim.binaryBucket = Util.StringToBytes256(
666 "{0}/{1}/{2}/{3}",
667 m_Engine.World.RegionInfo.RegionName,
668 (int)Math.Floor(pos.X),
669 (int)Math.Floor(pos.Y),
670 (int)Math.Floor(pos.Z));
671 transferModule.SendInstantMessage(gim, delegate(bool success) {});
672 }
673
674 /*
675 * Say script is sleeping for a very long time.
676 * Reset() is able to cancel this sleeping.
677 */
678 m_SleepUntil = DateTime.MaxValue;
679 }
680
681 /**
682 * @brief The user clicked the Reset Script button.
683 * We want to reset the script to a never-has-ever-run-before state.
684 */
685 public void Reset()
686 {
687 checkstate:
688 XMRInstState iState = m_IState;
689 switch (iState) {
690
691 /*
692 * If it's really being constructed now, that's about as reset as we get.
693 */
694 case XMRInstState.CONSTRUCT: {
695 return;
696 }
697
698 /*
699 * If it's idle, that means it is ready to receive a new event.
700 * So we lock the event queue to prevent another thread from taking
701 * it out of idle, verify that it is still in idle then transition
702 * it to resetting so no other thread will touch it.
703 */
704 case XMRInstState.IDLE: {
705 lock (m_QueueLock) {
706 if (m_IState == XMRInstState.IDLE) {
707 m_IState = XMRInstState.RESETTING;
708 break;
709 }
710 }
711 goto checkstate;
712 }
713
714 /*
715 * If it's on the start queue, that means it is about to dequeue an
716 * event and start processing it. So we lock the start queue so it
717 * can't be started and transition it to resetting so no other thread
718 * will touch it.
719 */
720 case XMRInstState.ONSTARTQ: {
721 lock (m_Engine.m_StartQueue) {
722 if (m_IState == XMRInstState.ONSTARTQ) {
723 m_Engine.m_StartQueue.Remove(this);
724 m_IState = XMRInstState.RESETTING;
725 break;
726 }
727 }
728 goto checkstate;
729 }
730
731 /*
732 * If it's running, tell CheckRun() to suspend the thread then go back
733 * to see what it got transitioned to.
734 */
735 case XMRInstState.RUNNING: {
736 suspendOnCheckRunHold = true;
737 lock (m_QueueLock) { }
738 goto checkstate;
739 }
740
741 /*
742 * If it's sleeping, remove it from sleep queue and transition it to
743 * resetting so no other thread will touch it.
744 */
745 case XMRInstState.ONSLEEPQ: {
746 lock (m_Engine.m_SleepQueue) {
747 if (m_IState == XMRInstState.ONSLEEPQ) {
748 m_Engine.m_SleepQueue.Remove(this);
749 m_IState = XMRInstState.RESETTING;
750 break;
751 }
752 }
753 goto checkstate;
754 }
755
756 /*
757 * It was just removed from the sleep queue and is about to be put
758 * on the yield queue (ie, is being woken up).
759 * Let that thread complete transition and try again.
760 */
761 case XMRInstState.REMDFROMSLPQ: {
762 Sleep (10);
763 goto checkstate;
764 }
765
766 /*
767 * If it's yielding, remove it from yield queue and transition it to
768 * resetting so no other thread will touch it.
769 */
770 case XMRInstState.ONYIELDQ: {
771 lock (m_Engine.m_YieldQueue) {
772 if (m_IState == XMRInstState.ONYIELDQ) {
773 m_Engine.m_YieldQueue.Remove(this);
774 m_IState = XMRInstState.RESETTING;
775 break;
776 }
777 }
778 goto checkstate;
779 }
780
781 /*
782 * If it just finished running something, let that thread transition it
783 * to its next state then check again.
784 */
785 case XMRInstState.FINISHED: {
786 Sleep (10);
787 goto checkstate;
788 }
789
790 /*
791 * If it's disposed, that's about as reset as it gets.
792 */
793 case XMRInstState.DISPOSED: {
794 return;
795 }
796
797 /*
798 * Some other thread is already resetting it, let it finish.
799 */
800 case XMRInstState.RESETTING: {
801 return;
802 }
803
804 default: throw new Exception("bad state");
805 }
806
807 /*
808 * This thread transitioned the instance to RESETTING so reset it.
809 */
810 lock (m_RunLock) {
811 CheckRunLockInvariants(true);
812
813 /*
814 * No other thread should have transitioned it from RESETTING.
815 */
816 if (m_IState != XMRInstState.RESETTING) throw new Exception("bad state");
817
818 /*
819 * If the microthread is active, that means it has call frame
820 * context that we don't want. Throw it out and get a fresh one.
821 */
822 if (microthread.Active () < 0) {
823 microthread.Dispose ();
824 microthread = (IScriptUThread)m_Engine.uThreadCtor.Invoke (new object[] { this });
825 }
826
827 /*
828 * Mark it idle now so it can get queued to process new stuff.
829 */
830 m_IState = XMRInstState.IDLE;
831
832 /*
833 * Reset everything and queue up default's start_entry() event.
834 */
835 ClearQueue();
836 ResetLocked("external Reset");
837
838 CheckRunLockInvariants(true);
839 }
840 }
841
842 private void ClearQueueExceptLinkMessages()
843 {
844 lock (m_QueueLock) {
845 EventParams[] linkMessages = new EventParams[m_EventQueue.Count];
846 int n = 0;
847 foreach (EventParams evt2 in m_EventQueue) {
848 if (evt2.EventName == "link_message") {
849 linkMessages[n++] = evt2;
850 }
851 }
852
853 m_EventQueue.Clear();
854 for (int i = m_EventCounts.Length; -- i >= 0;) m_EventCounts[i] = 0;
855
856 for (int i = 0; i < n; i ++) {
857 m_EventQueue.AddLast(linkMessages[i]);
858 }
859
860 m_EventCounts[(int)ScriptEventCode.link_message] = n;
861 }
862 }
863
864 private void ClearQueue()
865 {
866 lock (m_QueueLock)
867 {
868 m_EventQueue.Clear(); // no events queued
869 for (int i = m_EventCounts.Length; -- i >= 0;) m_EventCounts[i] = 0;
870 }
871 }
872
873 /**
874 * @brief The script called llResetScript() while it was running and
875 * has suspended. We want to reset the script to a never-has-
876 * ever-run-before state.
877 *
878 * Caller must have m_RunLock locked so we know script isn't
879 * running.
880 */
881 private void ResetLocked(string from)
882 {
883 m_RunOnePhase = "ResetLocked: releasing controls";
884 ReleaseControls();
885
886 m_RunOnePhase = "ResetLocked: removing script";
887 m_Part.Inventory.GetInventoryItem(m_ItemID).PermsMask = 0;
888 m_Part.Inventory.GetInventoryItem(m_ItemID).PermsGranter = UUID.Zero;
889 IUrlModule urlModule = m_Engine.World.RequestModuleInterface<IUrlModule>();
890 if (urlModule != null)
891 urlModule.ScriptRemoved(m_ItemID);
892
893 AsyncCommandManager.RemoveScript(m_Engine, m_LocalID, m_ItemID);
894
895 m_RunOnePhase = "ResetLocked: clearing current event";
896 this.eventCode = ScriptEventCode.None; // not processing an event
897 m_DetectParams = null; // not processing an event
898 m_SleepUntil = DateTime.MinValue; // not doing llSleep()
899 m_ResetCount ++; // has been reset once more
900
901 /*
902 * Tell next call to 'default state_entry()' to reset all global
903 * vars to their initial values.
904 */
905 doGblInit = true;
906
907 /*
908 * Set script to 'default' state and queue call to its
909 * 'state_entry()' event handler.
910 */
911 m_RunOnePhase = "ResetLocked: posting default:state_entry() event";
912 stateCode = 0;
913 m_Part.SetScriptEvents(m_ItemID, GetStateEventFlags(0));
914 PostEvent(new EventParams("state_entry",
915 zeroObjectArray,
916 zeroDetectParams));
917
918 /*
919 * Tell CheckRun() to let script run.
920 */
921 suspendOnCheckRunHold = false;
922 suspendOnCheckRunTemp = false;
923 m_RunOnePhase = "ResetLocked: reset complete";
924 }
925
926 private void ReleaseControls()
927 {
928 if (m_Part != null)
929 {
930 bool found;
931 int permsMask;
932 UUID permsGranter;
933
934 try {
935 permsGranter = m_Part.TaskInventory[m_ItemID].PermsGranter;
936 permsMask = m_Part.TaskInventory[m_ItemID].PermsMask;
937 found = true;
938 } catch {
939 permsGranter = UUID.Zero;
940 permsMask = 0;
941 found = false;
942 }
943
944 if (found && ((permsMask & ScriptBaseClass.PERMISSION_TAKE_CONTROLS) != 0)) {
945 ScenePresence presence = m_Engine.World.GetScenePresence(permsGranter);
946 if (presence != null) {
947 presence.UnRegisterControlEventsToScript(m_LocalID, m_ItemID);
948 }
949 }
950 }
951 }
952
953 /**
954 * @brief The script code should call this routine whenever it is
955 * convenient to perform a migation or switch microthreads.
956 */
957 public override void CheckRunWork ()
958 {
959 m_CheckRunPhase = "entered";
960
961 /*
962 * Stay stuck in this loop as long as something wants us suspended.
963 */
964 while (suspendOnCheckRunHold || suspendOnCheckRunTemp) {
965 m_CheckRunPhase = "top of while";
966
967 /*
968 * See if MigrateOutEventHandler() has been called.
969 * If so, dump our stack to stackFrames and unwind.
970 */
971 if (this.captureStackFrames) {
972
973 /*
974 * Puque our stack to the output stream.
975 * But otherwise, our state remains intact.
976 */
977 m_CheckRunPhase = "saving";
978 this.callMode = CallMode_SAVE;
979 this.stackFrames = null;
980 throw new StackCaptureException ();
981 }
982
983 /*
984 * We get here when the script state has been read in by MigrateInEventHandler().
985 * Since the stack is completely restored at this point, any subsequent calls
986 * within the functions should do their normal processing instead of trying to
987 * restore their state.
988 */
989 if (this.callMode == CallMode_RESTORE) {
990 stackFramesRestored = true;
991 this.callMode = CallMode_NORMAL;
992 }
993
994 /*
995 * Now we are ready to suspend the microthread.
996 * This is like a longjmp() to the most recent StartEx() or ResumeEx()
997 * with a simultaneous setjmp() so ResumeEx() can longjmp() back here.
998 */
999 m_CheckRunPhase = "suspending";
1000 suspendOnCheckRunTemp = false;
1001 microthread.Hiber ();
1002 m_CheckRunPhase = "resumed";
1003 }
1004
1005 m_CheckRunPhase = "returning";
1006
1007 /*
1008 * Upon return from CheckRun() it should always be the case that the script is
1009 * going to process calls normally, neither saving nor restoring stack frame state.
1010 */
1011 if (callMode != CallMode_NORMAL) throw new Exception ("bad callMode " + callMode);
1012 }
1013
1014 /**
1015 * @brief Allow script to dequeue events.
1016 */
1017 public void ResumeIt()
1018 {
1019 lock (m_QueueLock)
1020 {
1021 m_Suspended = false;
1022 if ((m_EventQueue != null) &&
1023 (m_EventQueue.First != null) &&
1024 (m_IState == XMRInstState.IDLE)) {
1025 m_IState = XMRInstState.ONSTARTQ;
1026 m_Engine.QueueToStart(this);
1027 }
1028 m_HasRun = true;
1029 }
1030 }
1031
1032 /**
1033 * @brief Block script from dequeuing events.
1034 */
1035 public void SuspendIt()
1036 {
1037 lock (m_QueueLock)
1038 {
1039 m_Suspended = true;
1040 }
1041 }
1042 }
1043
1044 /**
1045 * @brief Thrown by CheckRun() to unwind the script stack, capturing frames to
1046 * instance.stackFrames as it unwinds. We don't want scripts to be able
1047 * to intercept this exception as it would block the stack capture
1048 * functionality.
1049 */
1050 public class StackCaptureException : Exception, IXMRUncatchable { }
1051}