XMRInstRun.cs 44 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060
  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. using System;
  28. using System.Threading;
  29. using System.Collections.Generic;
  30. using System.Text;
  31. using OpenMetaverse;
  32. using OpenSim.Framework;
  33. using OpenSim.Region.Framework.Interfaces;
  34. using OpenSim.Region.ScriptEngine.Shared;
  35. using OpenSim.Region.ScriptEngine.Shared.Api;
  36. using OpenSim.Region.ScriptEngine.Shared.ScriptBase;
  37. using OpenSim.Region.Framework.Scenes;
  38. using log4net;
  39. using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
  40. using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
  41. using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
  42. using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
  43. using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
  44. using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
  45. using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
  46. namespace OpenSim.Region.ScriptEngine.Yengine
  47. {
  48. public partial class XMRInstance
  49. {
  50. /************************************************************************************\
  51. * This module contains these externally useful methods: *
  52. * PostEvent() - queues an event to script and wakes script thread to process it *
  53. * RunOne() - runs script for a time slice or until it volunteers to give up cpu *
  54. * CallSEH() - runs in the microthread to call the event handler *
  55. \************************************************************************************/
  56. /**
  57. * @brief This can be called in any thread (including the script thread itself)
  58. * to queue event to script for processing.
  59. */
  60. public void PostEvent(EventParams evt)
  61. {
  62. ScriptEventCode evc = (ScriptEventCode)Enum.Parse(typeof(ScriptEventCode), evt.EventName);
  63. // Put event on end of event queue.
  64. bool startIt = false;
  65. bool wakeIt = false;
  66. lock(m_QueueLock)
  67. {
  68. bool construct = (m_IState == XMRInstState.CONSTRUCT);
  69. // Ignore event if we don't even have such an handler in any state.
  70. // We can't be state-specific here because state might be different
  71. // by the time this event is dequeued and delivered to the script.
  72. if(!construct && // make sure m_HaveEventHandlers is filled in
  73. ((uint)evc < (uint)m_HaveEventHandlers.Length) &&
  74. !m_HaveEventHandlers[(int)evc]) // don't bother if we don't have such a handler in any state
  75. return;
  76. // Not running means we ignore any incoming events.
  77. // But queue if still constructing because m_Running is not yet valid.
  78. if(!m_Running && !construct)
  79. {
  80. if(m_IState == XMRInstState.SUSPENDED)
  81. {
  82. if(evc == ScriptEventCode.state_entry && m_EventQueue.Count == 0)
  83. {
  84. LinkedListNode<EventParams> llns = new LinkedListNode<EventParams>(evt);
  85. m_EventQueue.AddFirst(llns);
  86. }
  87. }
  88. return;
  89. }
  90. if(m_minEventDelay != 0)
  91. {
  92. switch (evc)
  93. {
  94. // ignore some events by time set by llMinEventDelay
  95. case ScriptEventCode.collision:
  96. case ScriptEventCode.land_collision:
  97. case ScriptEventCode.listen:
  98. case ScriptEventCode.not_at_target:
  99. case ScriptEventCode.not_at_rot_target:
  100. case ScriptEventCode.no_sensor:
  101. case ScriptEventCode.sensor:
  102. case ScriptEventCode.timer:
  103. case ScriptEventCode.touch:
  104. {
  105. double now = Util.GetTimeStamp();
  106. if (now < m_nextEventTime)
  107. return;
  108. m_nextEventTime = now + m_minEventDelay;
  109. break;
  110. }
  111. case ScriptEventCode.changed:
  112. {
  113. const int canignore = ~(CHANGED_SCALE | CHANGED_POSITION);
  114. int change = (int)evt.Params[0];
  115. if(change == 0) // what?
  116. return;
  117. if((change & canignore) == 0)
  118. {
  119. double now = Util.GetTimeStamp();
  120. if (now < m_nextEventTime)
  121. return;
  122. m_nextEventTime = now + m_minEventDelay;
  123. }
  124. break;
  125. }
  126. default:
  127. break;
  128. }
  129. }
  130. // Only so many of each event type allowed to queue.
  131. if((uint)evc < (uint)m_EventCounts.Length)
  132. {
  133. if(evc == ScriptEventCode.timer)
  134. {
  135. if(m_EventCounts[(int)evc] >= 1)
  136. return;
  137. }
  138. else if(m_EventCounts[(int)evc] >= MAXEVENTQUEUE)
  139. return;
  140. m_EventCounts[(int)evc]++;
  141. }
  142. // Put event on end of instance's event queue.
  143. LinkedListNode<EventParams> lln = new LinkedListNode<EventParams>(evt);
  144. switch(evc)
  145. {
  146. // These need to go first. The only time we manually
  147. // queue them is for the default state_entry() and we
  148. // need to make sure they go before any attach() events
  149. // so the heapLimit value gets properly initialized.
  150. case ScriptEventCode.state_entry:
  151. m_EventQueue.AddFirst(lln);
  152. break;
  153. // The attach event sneaks to the front of the queue.
  154. // This is needed for quantum limiting to work because
  155. // we want the attach(NULL_KEY) event to come in front
  156. // of all others so the m_DetachQuantum won't run out
  157. // before attach(NULL_KEY) is executed.
  158. case ScriptEventCode.attach:
  159. if(evt.Params[0].ToString() == UUID.Zero.ToString())
  160. {
  161. LinkedListNode<EventParams> lln2 = null;
  162. for(lln2 = m_EventQueue.First; lln2 != null; lln2 = lln2.Next)
  163. {
  164. EventParams evt2 = lln2.Value;
  165. ScriptEventCode evc2 = (ScriptEventCode)Enum.Parse(typeof(ScriptEventCode), evt2.EventName);
  166. if((evc2 != ScriptEventCode.state_entry) && (evc2 != ScriptEventCode.attach))
  167. break;
  168. }
  169. if(lln2 == null)
  170. m_EventQueue.AddLast(lln);
  171. else
  172. m_EventQueue.AddBefore(lln2, lln);
  173. // If we're detaching, limit the qantum. This will also
  174. // cause the script to self-suspend after running this
  175. // event
  176. m_DetachReady.Reset();
  177. m_DetachQuantum = 100;
  178. }
  179. else
  180. m_EventQueue.AddLast(lln);
  181. break;
  182. // All others just go on end in the order queued.
  183. default:
  184. m_EventQueue.AddLast(lln);
  185. break;
  186. }
  187. // If instance is idle (ie, not running or waiting to run),
  188. // flag it to be on m_StartQueue as we are about to do so.
  189. // Flag it now before unlocking so another thread won't try
  190. // to do the same thing right now.
  191. // Dont' flag it if it's still suspended!
  192. if((m_IState == XMRInstState.IDLE) && !m_Suspended)
  193. {
  194. m_IState = XMRInstState.ONSTARTQ;
  195. startIt = true;
  196. }
  197. // If instance is sleeping (ie, possibly in xmrEventDequeue),
  198. // wake it up if event is in the mask.
  199. if((m_SleepUntil > DateTime.UtcNow) && !m_Suspended)
  200. {
  201. int evc1 = (int)evc;
  202. int evc2 = evc1 - 32;
  203. if((((uint)evc1 < (uint)32) && (((m_SleepEventMask1 >> evc1) & 1) != 0)) ||
  204. (((uint)evc2 < (uint)32) && (((m_SleepEventMask2 >> evc2) & 1) != 0)))
  205. wakeIt = true;
  206. }
  207. }
  208. // If transitioned from IDLE->ONSTARTQ, actually go insert it
  209. // on m_StartQueue and give the RunScriptThread() a wake-up.
  210. if(startIt)
  211. m_Engine.QueueToStart(this);
  212. // Likewise, if the event mask triggered a wake, wake it up.
  213. if(wakeIt)
  214. {
  215. m_SleepUntil = DateTime.MinValue;
  216. m_Engine.WakeFromSleep(this);
  217. }
  218. }
  219. public void CancelEvent(string eventName)
  220. {
  221. ScriptEventCode evc;
  222. try
  223. {
  224. evc = (ScriptEventCode)Enum.Parse(typeof(ScriptEventCode), eventName);
  225. }
  226. catch
  227. {
  228. return;
  229. }
  230. lock (m_QueueLock)
  231. {
  232. if(m_EventQueue.Count == 0)
  233. return;
  234. LinkedListNode<EventParams> lln2 = null;
  235. for (lln2 = m_EventQueue.First; lln2 != null; lln2 = lln2.Next)
  236. {
  237. EventParams evt2 = lln2.Value;
  238. if(evt2.EventName.Equals(eventName))
  239. {
  240. m_EventQueue.Remove(lln2);
  241. if (evc >= 0 && m_EventCounts[(int)evc] > 0)
  242. m_EventCounts[(int)evc]--;
  243. }
  244. }
  245. }
  246. }
  247. // This is called in the script thread to step script until it calls
  248. // CheckRun(). It returns what the instance's next state should be,
  249. // ONSLEEPQ, ONYIELDQ, SUSPENDED or FINISHED.
  250. public XMRInstState RunOne()
  251. {
  252. DateTime now = DateTime.UtcNow;
  253. m_SliceStart = Util.GetTimeStampMS();
  254. // If script has called llSleep(), don't do any more until time is up.
  255. m_RunOnePhase = "check m_SleepUntil";
  256. if(m_SleepUntil > now)
  257. {
  258. m_RunOnePhase = "return is sleeping";
  259. return XMRInstState.ONSLEEPQ;
  260. }
  261. // Also, someone may have called Suspend().
  262. m_RunOnePhase = "check m_SuspendCount";
  263. if(m_SuspendCount > 0)
  264. {
  265. m_RunOnePhase = "return is suspended";
  266. return XMRInstState.SUSPENDED;
  267. }
  268. // Make sure we aren't being migrated in or out and prevent that
  269. // whilst we are in here. If migration has it locked, don't call
  270. // back right away, delay a bit so we don't get in infinite loop.
  271. m_RunOnePhase = "lock m_RunLock";
  272. if(!Monitor.TryEnter(m_RunLock))
  273. {
  274. m_SleepUntil = now.AddMilliseconds(15);
  275. m_RunOnePhase = "return was locked";
  276. return XMRInstState.ONSLEEPQ;
  277. }
  278. try
  279. {
  280. m_RunOnePhase = "check entry invariants";
  281. CheckRunLockInvariants(true);
  282. Exception e = null;
  283. // Maybe it has been Disposed()
  284. if(m_Part == null || m_Part.Inventory == null)
  285. {
  286. m_RunOnePhase = "runone saw it disposed";
  287. return XMRInstState.DISPOSED;
  288. }
  289. if(!m_Running)
  290. {
  291. m_RunOnePhase = "return is not running";
  292. return XMRInstState.SUSPENDED;
  293. }
  294. // Do some more of the last event if it didn't finish.
  295. if(this.eventCode != ScriptEventCode.None)
  296. {
  297. lock(m_QueueLock)
  298. {
  299. if(m_DetachQuantum > 0 && --m_DetachQuantum == 0)
  300. {
  301. m_Suspended = true;
  302. m_DetachReady.Set();
  303. m_RunOnePhase = "detach quantum went zero";
  304. CheckRunLockInvariants(true);
  305. return XMRInstState.FINISHED;
  306. }
  307. }
  308. m_RunOnePhase = "resume old event handler";
  309. m_LastRanAt = now;
  310. m_InstEHSlice++;
  311. callMode = CallMode_NORMAL;
  312. e = ResumeEx();
  313. }
  314. // Otherwise, maybe we can dequeue a new event and start
  315. // processing it.
  316. else
  317. {
  318. m_RunOnePhase = "lock event queue";
  319. EventParams evt = null;
  320. ScriptEventCode evc = ScriptEventCode.None;
  321. lock(m_QueueLock)
  322. {
  323. // We can't get here unless the script has been resumed
  324. // after creation, then suspended again, and then had
  325. // an event posted to it. We just pretend there is no
  326. // event int he queue and let the normal mechanics
  327. // carry out the suspension. A Resume will handle the
  328. // restarting gracefully. This is taking the easy way
  329. // out and may be improved in the future.
  330. if(m_Suspended)
  331. {
  332. m_RunOnePhase = "m_Suspended is set";
  333. CheckRunLockInvariants(true);
  334. return XMRInstState.FINISHED;
  335. }
  336. m_RunOnePhase = "dequeue event";
  337. if(m_EventQueue.First != null)
  338. {
  339. evt = m_EventQueue.First.Value;
  340. evc = (ScriptEventCode)Enum.Parse(typeof(ScriptEventCode), evt.EventName);
  341. if (m_DetachQuantum > 0)
  342. {
  343. if(evc != ScriptEventCode.attach)
  344. {
  345. // This is the case where the attach event
  346. // has completed and another event is queued
  347. // Stop it from running and suspend
  348. m_Suspended = true;
  349. m_DetachReady.Set();
  350. m_DetachQuantum = 0;
  351. m_RunOnePhase = "nothing to do #3";
  352. CheckRunLockInvariants(true);
  353. return XMRInstState.FINISHED;
  354. }
  355. }
  356. m_EventQueue.RemoveFirst();
  357. if((int)evc >= 0)
  358. m_EventCounts[(int)evc]--;
  359. }
  360. // If there is no event to dequeue, don't run this script
  361. // until another event gets queued.
  362. if(evt == null)
  363. {
  364. if(m_DetachQuantum > 0)
  365. {
  366. // This will happen if the attach event has run
  367. // and exited with time slice left.
  368. m_Suspended = true;
  369. m_DetachReady.Set();
  370. m_DetachQuantum = 0;
  371. }
  372. m_RunOnePhase = "nothing to do #4";
  373. CheckRunLockInvariants(true);
  374. return XMRInstState.FINISHED;
  375. }
  376. }
  377. // Dequeued an event, so start it going until it either
  378. // finishes or it calls CheckRun().
  379. m_RunOnePhase = "start event handler";
  380. m_DetectParams = evt.DetectParams;
  381. m_LastRanAt = now;
  382. m_InstEHEvent++;
  383. e = StartEventHandler(evc, evt.Params);
  384. }
  385. m_RunOnePhase = "done running";
  386. m_CPUTime += DateTime.UtcNow.Subtract(now).TotalMilliseconds;
  387. // Maybe it puqued.
  388. if(e != null)
  389. {
  390. m_RunOnePhase = "handling exception " + e.Message;
  391. HandleScriptException(e);
  392. m_RunOnePhase = "return had exception " + e.Message;
  393. CheckRunLockInvariants(true);
  394. return XMRInstState.FINISHED;
  395. }
  396. // If event handler completed, get rid of detect params.
  397. if(this.eventCode == ScriptEventCode.None)
  398. m_DetectParams = null;
  399. }
  400. finally
  401. {
  402. m_RunOnePhase += "; checking exit invariants and unlocking";
  403. CheckRunLockInvariants(false);
  404. Monitor.Exit(m_RunLock);
  405. }
  406. // Cycle script through the yield queue and call it back asap.
  407. m_RunOnePhase = "last return";
  408. return XMRInstState.ONYIELDQ;
  409. }
  410. /**
  411. * @brief Immediately after taking m_RunLock or just before releasing it, check invariants.
  412. */
  413. private ScriptEventCode lastEventCode = ScriptEventCode.None;
  414. private bool lastActive = false;
  415. private string lastRunPhase = "";
  416. public void CheckRunLockInvariants(bool throwIt)
  417. {
  418. // If not executing any event handler, there shouldn't be any saved stack frames.
  419. // If executing an event handler, there should be some saved stack frames.
  420. bool active = (stackFrames != null);
  421. ScriptEventCode ec = this.eventCode;
  422. if(((ec == ScriptEventCode.None) && active) ||
  423. ((ec != ScriptEventCode.None) && !active))
  424. {
  425. m_log.Error("CheckRunLockInvariants: script=" + m_DescName);
  426. m_log.Error("CheckRunLockInvariants: eventcode=" + ec.ToString() + ", active=" + active.ToString());
  427. m_log.Error("CheckRunLockInvariants: m_RunOnePhase=" + m_RunOnePhase);
  428. m_log.Error("CheckRunLockInvariants: lastec=" + lastEventCode + ", lastAct=" + lastActive + ", lastPhase=" + lastRunPhase);
  429. if(throwIt)
  430. throw new Exception("CheckRunLockInvariants: eventcode=" + ec.ToString() + ", active=" + active.ToString());
  431. }
  432. lastEventCode = ec;
  433. lastActive = active;
  434. lastRunPhase = m_RunOnePhase;
  435. }
  436. /*
  437. * Start event handler.
  438. *
  439. * Input:
  440. * newEventCode = code of event to be processed
  441. * newEhArgs = arguments for the event handler
  442. *
  443. * Caution:
  444. * It is up to the caller to make sure ehArgs[] is correct for
  445. * the particular event handler being called. The first thing
  446. * a script event handler method does is to unmarshall the args
  447. * from ehArgs[] and will throw an array bounds or cast exception
  448. * if it can't.
  449. */
  450. private Exception StartEventHandler(ScriptEventCode newEventCode, object[] newEhArgs)
  451. {
  452. // We use this.eventCode == ScriptEventCode.None to indicate we are idle.
  453. // So trying to execute ScriptEventCode.None might make a mess.
  454. if(newEventCode == ScriptEventCode.None)
  455. return new Exception("Can't process ScriptEventCode.None");
  456. // Silly to even try if there is no handler defined for this event.
  457. if(((int)newEventCode >= 0) && (m_ObjCode.scriptEventHandlerTable[this.stateCode, (int)newEventCode] == null))
  458. return null;
  459. // The microthread shouldn't be processing any event code.
  460. // These are assert checks so we throw them directly as exceptions.
  461. if(this.eventCode != ScriptEventCode.None)
  462. throw new Exception("still processing event " + this.eventCode.ToString());
  463. // Save eventCode so we know what event handler to run in the microthread.
  464. // And it also marks us busy so we can't be started again and this event lost.
  465. this.eventCode = newEventCode;
  466. this.ehArgs = newEhArgs;
  467. // This calls ScriptUThread.Main() directly, and returns when Main() [indirectly]
  468. // calls Suspend() or when Main() returns, whichever occurs first.
  469. // Setting stackFrames = null means run the event handler from the beginning
  470. // without doing any stack frame restores first.
  471. this.stackFrames = null;
  472. return StartEx();
  473. }
  474. /**
  475. * @brief There was an exception whilst starting/running a script event handler.
  476. * Maybe we handle it directly or just print an error message.
  477. */
  478. private void HandleScriptException(Exception e)
  479. {
  480. // The script threw some kind of exception that was not caught at
  481. // script level, so the script is no longer running an event handler.
  482. ScriptEventCode curevent = eventCode;
  483. eventCode = ScriptEventCode.None;
  484. stackFrames = null;
  485. if(m_Part == null || m_Part.Inventory == null)
  486. {
  487. //we are gone and don't know it still
  488. m_SleepUntil = DateTime.MaxValue;
  489. return;
  490. }
  491. if (e is ScriptDeleteException)
  492. {
  493. // Script did something like llRemoveInventory(llGetScriptName());
  494. // ... to delete itself from the object.
  495. m_SleepUntil = DateTime.MaxValue;
  496. Verbose("[YEngine]: script self-delete {0}", m_ItemID);
  497. m_Part.Inventory.RemoveInventoryItem(m_ItemID);
  498. }
  499. else if(e is ScriptDieException)
  500. {
  501. // Script did an llDie()
  502. m_RunOnePhase = "dying...";
  503. m_SleepUntil = DateTime.MaxValue;
  504. m_Engine.World.DeleteSceneObject(m_Part.ParentGroup, false);
  505. }
  506. else if (e is ScriptResetException)
  507. {
  508. // Script did an llResetScript().
  509. m_RunOnePhase = "resetting...";
  510. ResetLocked("HandleScriptResetException");
  511. }
  512. else if (e is ScriptException)
  513. {
  514. // Some general script error.
  515. SendScriptErrorMessage(e, curevent);
  516. }
  517. else
  518. {
  519. // Some general script error.
  520. SendErrorMessage(e);
  521. }
  522. }
  523. private void SendScriptErrorMessage(Exception e, ScriptEventCode ev)
  524. {
  525. StringBuilder msg = new StringBuilder();
  526. bool toowner = false;
  527. msg.Append("YEngine: ");
  528. if (e.Message != null)
  529. {
  530. string text = e.Message;
  531. if (text.StartsWith("(OWNER)"))
  532. {
  533. text = text.Substring(7);
  534. toowner = true;
  535. }
  536. msg.Append(text);
  537. }
  538. msg.Append(" (script: ");
  539. msg.Append(m_Item.Name);
  540. msg.Append(" event: ");
  541. msg.Append(ev.ToString());
  542. msg.Append(" primID: ");
  543. msg.Append(m_Part.UUID.ToString());
  544. msg.Append(" at: <");
  545. Vector3 pos = m_Part.AbsolutePosition;
  546. msg.Append((int)Math.Floor(pos.X));
  547. msg.Append(',');
  548. msg.Append((int)Math.Floor(pos.Y));
  549. msg.Append(',');
  550. msg.Append((int)Math.Floor(pos.Z));
  551. msg.Append(">) Script must be Reset to re-enable.\n");
  552. string msgst = msg.ToString();
  553. if (msgst.Length > 1000)
  554. msgst = msgst.Substring(0, 1000);
  555. if (toowner)
  556. {
  557. ScenePresence sp = m_Engine.World.GetScenePresence(m_Part.OwnerID);
  558. if (sp != null && !sp.IsNPC)
  559. m_Engine.World.SimChatToAgent(m_Part.OwnerID, Utils.StringToBytes(msgst), 0x7FFFFFFF, m_Part.AbsolutePosition,
  560. m_Part.Name, m_Part.UUID, false);
  561. }
  562. else
  563. m_Engine.World.SimChat(Utils.StringToBytes(msgst),
  564. ChatTypeEnum.DebugChannel, 0x7FFFFFFF,
  565. m_Part.AbsolutePosition,
  566. m_Part.Name, m_Part.UUID, false);
  567. m_log.Debug(string.Format(
  568. "[SCRIPT ERROR]: {0} (at event {1}, part {2} {3} at {4} in {5}",
  569. (e.Message == null)? "" : e.Message,
  570. ev.ToString(),
  571. m_Part.Name,
  572. m_Part.UUID,
  573. m_Part.AbsolutePosition,
  574. m_Part.ParentGroup.Scene.Name));
  575. m_SleepUntil = DateTime.MaxValue;
  576. }
  577. /**
  578. * @brief There was an exception running script event handler.
  579. * Display error message and disable script (in a way
  580. * that the script can be reset to be restarted).
  581. */
  582. private void SendErrorMessage(Exception e)
  583. {
  584. StringBuilder msg = new StringBuilder();
  585. msg.Append("[YEngine]: Exception while running ");
  586. msg.Append(m_ItemID);
  587. msg.Append('\n');
  588. // Add exception message.
  589. string des = e.Message;
  590. des = (des == null) ? "" : (": " + des);
  591. msg.Append(e.GetType().Name + des + "\n");
  592. // Tell script owner what to do.
  593. msg.Append("Prim: <");
  594. msg.Append(m_Part.Name);
  595. msg.Append(">, Script: <");
  596. msg.Append(m_Item.Name);
  597. msg.Append(">, Location: ");
  598. msg.Append(m_Engine.World.RegionInfo.RegionName);
  599. msg.Append(" <");
  600. Vector3 pos = m_Part.AbsolutePosition;
  601. msg.Append((int)Math.Floor(pos.X));
  602. msg.Append(',');
  603. msg.Append((int)Math.Floor(pos.Y));
  604. msg.Append(',');
  605. msg.Append((int)Math.Floor(pos.Z));
  606. msg.Append(">\nScript must be Reset to re-enable.\n");
  607. // Display full exception message in log.
  608. m_log.Info(msg.ToString() + XMRExceptionStackString(e), e);
  609. // Give script owner the stack dump.
  610. msg.Append(XMRExceptionStackString(e));
  611. // Send error message to owner.
  612. // Suppress internal code stack trace lines.
  613. string msgst = msg.ToString();
  614. if(!msgst.EndsWith("\n"))
  615. msgst += '\n';
  616. int j = 0;
  617. StringBuilder imstr = new StringBuilder();
  618. for(int i = 0; (i = msgst.IndexOf('\n', i)) >= 0; j = ++i)
  619. {
  620. string line = msgst.Substring(j, i - j);
  621. if(line.StartsWith("at "))
  622. {
  623. if(line.StartsWith("at (wrapper"))
  624. continue; // at (wrapper ...
  625. int k = line.LastIndexOf(".cs:"); // ... .cs:linenumber
  626. if(Int32.TryParse(line.Substring(k + 4), out k))
  627. continue;
  628. }
  629. this.llOwnerSay(line);
  630. }
  631. // Say script is sleeping for a very long time.
  632. // Reset() is able to cancel this sleeping.
  633. m_SleepUntil = DateTime.MaxValue;
  634. }
  635. /**
  636. * @brief The user clicked the Reset Script button.
  637. * We want to reset the script to a never-has-ever-run-before state.
  638. */
  639. public void Reset()
  640. {
  641. checkstate:
  642. XMRInstState iState = m_IState;
  643. switch(iState)
  644. {
  645. // If it's really being constructed now, that's about as reset as we get.
  646. case XMRInstState.CONSTRUCT:
  647. return;
  648. // If it's idle, that means it is ready to receive a new event.
  649. // So we lock the event queue to prevent another thread from taking
  650. // it out of idle, verify that it is still in idle then transition
  651. // it to resetting so no other thread will touch it.
  652. case XMRInstState.IDLE:
  653. lock(m_QueueLock)
  654. {
  655. if(m_IState == XMRInstState.IDLE)
  656. {
  657. m_IState = XMRInstState.RESETTING;
  658. break;
  659. }
  660. }
  661. goto checkstate;
  662. // If it's on the start queue, that means it is about to dequeue an
  663. // event and start processing it. So we lock the start queue so it
  664. // can't be started and transition it to resetting so no other thread
  665. // will touch it.
  666. case XMRInstState.ONSTARTQ:
  667. lock(m_Engine.m_StartQueue)
  668. {
  669. if(m_IState == XMRInstState.ONSTARTQ)
  670. {
  671. m_Engine.m_StartQueue.Remove(this);
  672. m_IState = XMRInstState.RESETTING;
  673. break;
  674. }
  675. }
  676. goto checkstate;
  677. // If it's running, tell CheckRun() to suspend the thread then go back
  678. // to see what it got transitioned to.
  679. case XMRInstState.RUNNING:
  680. suspendOnCheckRunHold = true;
  681. lock(m_QueueLock)
  682. {
  683. }
  684. goto checkstate;
  685. // If it's sleeping, remove it from sleep queue and transition it to
  686. // resetting so no other thread will touch it.
  687. case XMRInstState.ONSLEEPQ:
  688. lock(m_Engine.m_SleepQueue)
  689. {
  690. if(m_IState == XMRInstState.ONSLEEPQ)
  691. {
  692. m_Engine.m_SleepQueue.Remove(this);
  693. m_IState = XMRInstState.RESETTING;
  694. break;
  695. }
  696. }
  697. goto checkstate;
  698. // It was just removed from the sleep queue and is about to be put
  699. // on the yield queue (ie, is being woken up).
  700. // Let that thread complete transition and try again.
  701. case XMRInstState.REMDFROMSLPQ:
  702. Sleep(10);
  703. goto checkstate;
  704. // If it's yielding, remove it from yield queue and transition it to
  705. // resetting so no other thread will touch it.
  706. case XMRInstState.ONYIELDQ:
  707. lock(m_Engine.m_YieldQueue)
  708. {
  709. if(m_IState == XMRInstState.ONYIELDQ)
  710. {
  711. m_Engine.m_YieldQueue.Remove(this);
  712. m_IState = XMRInstState.RESETTING;
  713. break;
  714. }
  715. }
  716. goto checkstate;
  717. // If it just finished running something, let that thread transition it
  718. // to its next state then check again.
  719. case XMRInstState.FINISHED:
  720. Sleep(10);
  721. goto checkstate;
  722. // If it's disposed, that's about as reset as it gets.
  723. case XMRInstState.DISPOSED:
  724. return;
  725. // Some other thread is already resetting it, let it finish.
  726. case XMRInstState.RESETTING:
  727. return;
  728. case XMRInstState.SUSPENDED:
  729. break;
  730. default:
  731. throw new Exception("bad state");
  732. }
  733. // This thread transitioned the instance to RESETTING so reset it.
  734. lock(m_RunLock)
  735. {
  736. CheckRunLockInvariants(true);
  737. // No other thread should have transitioned it from RESETTING.
  738. if (m_IState != XMRInstState.SUSPENDED)
  739. {
  740. if (m_IState != XMRInstState.RESETTING)
  741. throw new Exception("bad state");
  742. m_IState = XMRInstState.IDLE;
  743. }
  744. // Reset everything and queue up default's start_entry() event.
  745. ClearQueue();
  746. ResetLocked("external Reset");
  747. // Mark it idle now so it can get queued to process new stuff.
  748. CheckRunLockInvariants(true);
  749. }
  750. }
  751. private void ClearQueueExceptLinkMessages()
  752. {
  753. lock(m_QueueLock)
  754. {
  755. EventParams[] linkMessages = new EventParams[m_EventQueue.Count];
  756. int n = 0;
  757. foreach(EventParams evt2 in m_EventQueue)
  758. {
  759. if(evt2.EventName == "link_message")
  760. linkMessages[n++] = evt2;
  761. }
  762. m_EventQueue.Clear();
  763. for(int i = m_EventCounts.Length; --i >= 0;)
  764. m_EventCounts[i] = 0;
  765. for(int i = 0; i < n; i++)
  766. m_EventQueue.AddLast(linkMessages[i]);
  767. m_EventCounts[(int)ScriptEventCode.link_message] = n;
  768. }
  769. }
  770. private void ClearQueue()
  771. {
  772. lock(m_QueueLock)
  773. {
  774. m_EventQueue.Clear(); // no events queued
  775. for(int i = m_EventCounts.Length; --i >= 0;)
  776. m_EventCounts[i] = 0;
  777. }
  778. }
  779. /**
  780. * @brief The script called llResetScript() while it was running and
  781. * has suspended. We want to reset the script to a never-has-
  782. * ever-run-before state.
  783. *
  784. * Caller must have m_RunLock locked so we know script isn't
  785. * running.
  786. */
  787. private void ResetLocked(string from)
  788. {
  789. m_RunOnePhase = "ResetLocked: releasing controls";
  790. ReleaseControlsOrPermissions(true);
  791. m_Part.CollisionSound = UUID.Zero;
  792. if (m_XMRLSLApi != null)
  793. m_XMRLSLApi.llResetTime();
  794. m_RunOnePhase = "ResetLocked: removing script";
  795. IUrlModule urlModule = m_Engine.World.RequestModuleInterface<IUrlModule>();
  796. if(urlModule != null)
  797. urlModule.ScriptRemoved(m_ItemID);
  798. AsyncCommandManager.RemoveScript(m_Engine, m_LocalID, m_ItemID);
  799. m_RunOnePhase = "ResetLocked: clearing current event";
  800. this.eventCode = ScriptEventCode.None; // not processing an event
  801. m_DetectParams = null; // not processing an event
  802. m_SleepUntil = DateTime.MinValue; // not doing llSleep()
  803. m_ResetCount++; // has been reset once more
  804. m_localsHeapUsed = 0;
  805. m_arraysHeapUsed = 0;
  806. glblVars.Clear();
  807. // Tell next call to 'default state_entry()' to reset all global
  808. // vars to their initial values.
  809. doGblInit = true;
  810. // Throw away all its stack frames.
  811. // If the script is resetting itself, there shouldn't be any stack frames.
  812. // If the script is being reset by something else, we throw them away cuz we want to start from the beginning of an event handler.
  813. stackFrames = null;
  814. // Set script to 'default' state and queue call to its
  815. // 'state_entry()' event handler.
  816. m_RunOnePhase = "ResetLocked: posting default:state_entry() event";
  817. stateCode = 0;
  818. m_Part.RemoveScriptTargets(m_ItemID);
  819. m_Part.SetScriptEvents(m_ItemID, GetStateEventFlags(0));
  820. PostEvent(new EventParams("state_entry",
  821. zeroObjectArray,
  822. zeroDetectParams));
  823. // Tell CheckRun() to let script run.
  824. suspendOnCheckRunHold = false;
  825. suspendOnCheckRunTemp = false;
  826. m_RunOnePhase = "ResetLocked: reset complete";
  827. }
  828. private void ReleaseControlsOrPermissions(bool fullPermissions)
  829. {
  830. if(m_Part != null && m_Part.TaskInventory != null)
  831. {
  832. int permsMask;
  833. UUID permsGranter;
  834. m_Part.TaskInventory.LockItemsForWrite(true);
  835. if (!m_Part.TaskInventory.TryGetValue(m_ItemID, out TaskInventoryItem item))
  836. {
  837. m_Part.TaskInventory.LockItemsForWrite(false);
  838. return;
  839. }
  840. permsGranter = item.PermsGranter;
  841. permsMask = item.PermsMask;
  842. if(fullPermissions)
  843. {
  844. item.PermsGranter = UUID.Zero;
  845. item.PermsMask = 0;
  846. }
  847. else
  848. item.PermsMask = permsMask & ~(ScriptBaseClass.PERMISSION_TAKE_CONTROLS | ScriptBaseClass.PERMISSION_CONTROL_CAMERA);
  849. m_Part.TaskInventory.LockItemsForWrite(false);
  850. if ((permsMask & ScriptBaseClass.PERMISSION_TAKE_CONTROLS) != 0)
  851. {
  852. ScenePresence presence = m_Engine.World.GetScenePresence(permsGranter);
  853. if (presence != null)
  854. presence.UnRegisterControlEventsToScript(m_LocalID, m_ItemID);
  855. }
  856. }
  857. }
  858. /**
  859. * @brief The script code should call this routine whenever it is
  860. * convenient to perform a migation or switch microthreads.
  861. */
  862. public override void CheckRunWork()
  863. {
  864. if(!suspendOnCheckRunHold && !suspendOnCheckRunTemp)
  865. {
  866. if(Util.GetTimeStampMS() - m_SliceStart < 60.0)
  867. return;
  868. suspendOnCheckRunTemp = true;
  869. }
  870. m_CheckRunPhase = "entered";
  871. // Stay stuck in this loop as long as something wants us suspended.
  872. while(suspendOnCheckRunHold || suspendOnCheckRunTemp)
  873. {
  874. m_CheckRunPhase = "top of while";
  875. suspendOnCheckRunTemp = false;
  876. switch(this.callMode)
  877. {
  878. // Now we are ready to suspend or resume.
  879. case CallMode_NORMAL:
  880. m_CheckRunPhase = "suspending";
  881. callMode = XMRInstance.CallMode_SAVE;
  882. stackFrames = null;
  883. throw new StackHibernateException(); // does not return
  884. // We get here when the script state has been read in by MigrateInEventHandler().
  885. // Since the stack is completely restored at this point, any subsequent calls
  886. // within the functions should do their normal processing instead of trying to
  887. // restore their state.
  888. // the stack has been restored as a result of calling ResumeEx()
  889. // tell script code to process calls normally
  890. case CallMode_RESTORE:
  891. this.callMode = CallMode_NORMAL;
  892. break;
  893. default:
  894. throw new Exception("callMode=" + callMode);
  895. }
  896. m_CheckRunPhase = "resumed";
  897. }
  898. m_CheckRunPhase = "returning";
  899. // Upon return from CheckRun() it should always be the case that the script is
  900. // going to process calls normally, neither saving nor restoring stack frame state.
  901. if(callMode != CallMode_NORMAL)
  902. throw new Exception("bad callMode " + callMode);
  903. }
  904. /**
  905. * @brief Allow script to dequeue events.
  906. */
  907. public void ResumeIt()
  908. {
  909. lock(m_QueueLock)
  910. {
  911. m_SuspendCount = 0;
  912. m_Suspended = false;
  913. m_DetachQuantum = 0;
  914. m_DetachReady.Set();
  915. if ((m_EventQueue != null) &&
  916. (m_EventQueue.First != null) &&
  917. (m_IState == XMRInstState.IDLE))
  918. {
  919. m_IState = XMRInstState.ONSTARTQ;
  920. m_Engine.QueueToStart(this);
  921. }
  922. m_HasRun = true;
  923. }
  924. }
  925. /**
  926. * @brief Block script from dequeuing events.
  927. */
  928. public void SuspendIt()
  929. {
  930. lock(m_QueueLock)
  931. {
  932. m_SuspendCount = 1;
  933. m_Suspended = true;
  934. }
  935. }
  936. }
  937. /**
  938. * @brief Thrown by CheckRun() to unwind the script stack, capturing frames to
  939. * instance.stackFrames as it unwinds. We don't want scripts to be able
  940. * to intercept this exception as it would block the stack capture
  941. * functionality.
  942. */
  943. public class StackCaptureException: Exception, IXMRUncatchable
  944. {
  945. }
  946. }