XMRInstRun.cs 42 KB

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