ScriptInstance.cs 40 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112
  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.Collections;
  29. using System.Collections.Generic;
  30. using System.Globalization;
  31. using System.IO;
  32. using System.Reflection;
  33. using System.Runtime.Remoting;
  34. using System.Runtime.Remoting.Lifetime;
  35. using System.Security.Policy;
  36. using System.Text;
  37. using System.Threading;
  38. using System.Xml;
  39. using OpenMetaverse;
  40. using log4net;
  41. using Nini.Config;
  42. using Amib.Threading;
  43. using OpenSim.Framework;
  44. using OpenSim.Region.CoreModules;
  45. using OpenSim.Region.Framework.Scenes;
  46. using OpenSim.Region.Framework.Interfaces;
  47. using OpenSim.Region.ScriptEngine.Shared;
  48. using OpenSim.Region.ScriptEngine.Shared.Api;
  49. using OpenSim.Region.ScriptEngine.Shared.Api.Runtime;
  50. using OpenSim.Region.ScriptEngine.Shared.ScriptBase;
  51. using OpenSim.Region.ScriptEngine.Shared.CodeTools;
  52. using OpenSim.Region.ScriptEngine.Interfaces;
  53. namespace OpenSim.Region.ScriptEngine.Shared.Instance
  54. {
  55. public class ScriptInstance : MarshalByRefObject, IScriptInstance
  56. {
  57. private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
  58. /// <summary>
  59. /// The current work item if an event for this script is running or waiting to run,
  60. /// </summary>
  61. /// <remarks>
  62. /// Null if there is no running or waiting to run event. Must be changed only under an EventQueue lock.
  63. /// </remarks>
  64. private IScriptWorkItem m_CurrentWorkItem;
  65. private IScript m_Script;
  66. private DetectParams[] m_DetectParams;
  67. private bool m_TimerQueued;
  68. private DateTime m_EventStart;
  69. private bool m_InEvent;
  70. private string m_Assembly;
  71. private string m_CurrentEvent = String.Empty;
  72. private bool m_InSelfDelete;
  73. private int m_MaxScriptQueue;
  74. private bool m_SaveState = true;
  75. private int m_ControlEventsInQueue;
  76. private int m_LastControlLevel;
  77. private bool m_CollisionInQueue;
  78. // The following is for setting a minimum delay between events
  79. private double m_minEventDelay;
  80. private long m_eventDelayTicks;
  81. private long m_nextEventTimeTicks;
  82. private bool m_startOnInit = true;
  83. private UUID m_AttachedAvatar;
  84. private StateSource m_stateSource;
  85. private bool m_postOnRez;
  86. private bool m_startedFromSavedState;
  87. private UUID m_CurrentStateHash;
  88. private UUID m_RegionID;
  89. public int DebugLevel { get; set; }
  90. public Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>> LineMap { get; set; }
  91. private Dictionary<string,IScriptApi> m_Apis = new Dictionary<string,IScriptApi>();
  92. public Object[] PluginData = new Object[0];
  93. /// <summary>
  94. /// Used by llMinEventDelay to suppress events happening any faster than this speed.
  95. /// This currently restricts all events in one go. Not sure if each event type has
  96. /// its own check so take the simple route first.
  97. /// </summary>
  98. public double MinEventDelay
  99. {
  100. get { return m_minEventDelay; }
  101. set
  102. {
  103. if (value > 0.001)
  104. m_minEventDelay = value;
  105. else
  106. m_minEventDelay = 0.0;
  107. m_eventDelayTicks = (long)(m_minEventDelay * 10000000L);
  108. m_nextEventTimeTicks = DateTime.Now.Ticks;
  109. }
  110. }
  111. public bool Running { get; set; }
  112. public bool Suspended
  113. {
  114. get { return m_Suspended; }
  115. set
  116. {
  117. // Need to do this inside a lock in order to avoid races with EventProcessor()
  118. lock (m_Script)
  119. {
  120. bool wasSuspended = m_Suspended;
  121. m_Suspended = value;
  122. if (wasSuspended && !m_Suspended)
  123. {
  124. lock (EventQueue)
  125. {
  126. // Need to place ourselves back in a work item if there are events to process
  127. if (EventQueue.Count > 0 && Running && !ShuttingDown)
  128. m_CurrentWorkItem = Engine.QueueEventHandler(this);
  129. }
  130. }
  131. }
  132. }
  133. }
  134. private bool m_Suspended;
  135. public bool ShuttingDown { get; set; }
  136. public string State { get; set; }
  137. public IScriptEngine Engine { get; private set; }
  138. public UUID AppDomain { get; set; }
  139. public string PrimName { get; private set; }
  140. public string ScriptName { get; private set; }
  141. public UUID ItemID { get; private set; }
  142. public UUID ObjectID { get; private set; }
  143. public uint LocalID { get; private set; }
  144. public UUID RootObjectID { get; private set; }
  145. public uint RootLocalID { get; private set; }
  146. public UUID AssetID { get; private set; }
  147. public Queue EventQueue { get; private set; }
  148. public long EventsQueued
  149. {
  150. get
  151. {
  152. lock (EventQueue)
  153. return EventQueue.Count;
  154. }
  155. }
  156. public long EventsProcessed { get; private set; }
  157. public int StartParam { get; set; }
  158. public TaskInventoryItem ScriptTask { get; private set; }
  159. public DateTime TimeStarted { get; private set; }
  160. public long MeasurementPeriodTickStart { get; private set; }
  161. public long MeasurementPeriodExecutionTime { get; private set; }
  162. public static readonly long MaxMeasurementPeriod = 30 * TimeSpan.TicksPerMinute;
  163. public void ClearQueue()
  164. {
  165. m_TimerQueued = false;
  166. EventQueue.Clear();
  167. }
  168. public ScriptInstance(IScriptEngine engine, SceneObjectPart part,
  169. UUID itemID, UUID assetID, string assembly,
  170. AppDomain dom, string primName, string scriptName,
  171. int startParam, bool postOnRez, StateSource stateSource,
  172. int maxScriptQueue)
  173. {
  174. State = "default";
  175. EventQueue = new Queue(32);
  176. Engine = engine;
  177. LocalID = part.LocalId;
  178. ObjectID = part.UUID;
  179. RootLocalID = part.ParentGroup.LocalId;
  180. RootObjectID = part.ParentGroup.UUID;
  181. ItemID = itemID;
  182. AssetID = assetID;
  183. PrimName = primName;
  184. ScriptName = scriptName;
  185. m_Assembly = assembly;
  186. StartParam = startParam;
  187. m_MaxScriptQueue = maxScriptQueue;
  188. m_stateSource = stateSource;
  189. m_postOnRez = postOnRez;
  190. m_AttachedAvatar = part.ParentGroup.AttachedAvatar;
  191. m_RegionID = part.ParentGroup.Scene.RegionInfo.RegionID;
  192. if (part != null)
  193. {
  194. lock (part.TaskInventory)
  195. {
  196. if (part.TaskInventory.ContainsKey(ItemID))
  197. {
  198. ScriptTask = part.TaskInventory[ItemID];
  199. }
  200. }
  201. }
  202. ApiManager am = new ApiManager();
  203. foreach (string api in am.GetApis())
  204. {
  205. m_Apis[api] = am.CreateApi(api);
  206. m_Apis[api].Initialize(engine, part, ScriptTask);
  207. }
  208. try
  209. {
  210. if (dom != System.AppDomain.CurrentDomain)
  211. m_Script = (IScript)dom.CreateInstanceAndUnwrap(
  212. Path.GetFileNameWithoutExtension(assembly),
  213. "SecondLife.Script");
  214. else
  215. m_Script = (IScript)Assembly.Load(
  216. Path.GetFileNameWithoutExtension(assembly)).CreateInstance(
  217. "SecondLife.Script");
  218. //ILease lease = (ILease)RemotingServices.GetLifetimeService(m_Script as ScriptBaseClass);
  219. //RemotingServices.GetLifetimeService(m_Script as ScriptBaseClass);
  220. // lease.Register(this);
  221. }
  222. catch (Exception e)
  223. {
  224. m_log.ErrorFormat(
  225. "[SCRIPT INSTANCE]: Error loading assembly {0}. Exception {1}{2}",
  226. assembly, e.Message, e.StackTrace);
  227. }
  228. try
  229. {
  230. foreach (KeyValuePair<string,IScriptApi> kv in m_Apis)
  231. {
  232. m_Script.InitApi(kv.Key, kv.Value);
  233. }
  234. // // m_log.Debug("[Script] Script instance created");
  235. part.SetScriptEvents(ItemID,
  236. (int)m_Script.GetStateEventFlags(State));
  237. }
  238. catch (Exception e)
  239. {
  240. m_log.ErrorFormat(
  241. "[SCRIPT INSTANCE]: Error loading script instance from assembly {0}. Exception {1}{2}",
  242. assembly, e.Message, e.StackTrace);
  243. return;
  244. }
  245. m_SaveState = true;
  246. string savedState = Path.Combine(Path.GetDirectoryName(assembly),
  247. ItemID.ToString() + ".state");
  248. if (File.Exists(savedState))
  249. {
  250. string xml = String.Empty;
  251. try
  252. {
  253. FileInfo fi = new FileInfo(savedState);
  254. int size = (int)fi.Length;
  255. if (size < 512000)
  256. {
  257. using (FileStream fs = File.Open(savedState,
  258. FileMode.Open, FileAccess.Read, FileShare.None))
  259. {
  260. Byte[] data = new Byte[size];
  261. fs.Read(data, 0, size);
  262. xml = Encoding.UTF8.GetString(data);
  263. ScriptSerializer.Deserialize(xml, this);
  264. AsyncCommandManager.CreateFromData(Engine,
  265. LocalID, ItemID, ObjectID,
  266. PluginData);
  267. // m_log.DebugFormat("[Script] Successfully retrieved state for script {0}.{1}", PrimName, m_ScriptName);
  268. part.SetScriptEvents(ItemID,
  269. (int)m_Script.GetStateEventFlags(State));
  270. if (!Running)
  271. m_startOnInit = false;
  272. Running = false;
  273. // we get new rez events on sim restart, too
  274. // but if there is state, then we fire the change
  275. // event
  276. // We loaded state, don't force a re-save
  277. m_SaveState = false;
  278. m_startedFromSavedState = true;
  279. }
  280. }
  281. else
  282. {
  283. m_log.WarnFormat(
  284. "[SCRIPT INSTANCE]: Unable to load script state file {0} for script {1} {2} in {3} {4} (assembly {5}). Memory limit exceeded",
  285. savedState, ScriptName, ItemID, PrimName, ObjectID, assembly);
  286. }
  287. }
  288. catch (Exception e)
  289. {
  290. m_log.ErrorFormat(
  291. "[SCRIPT INSTANCE]: Unable to load script state file {0} for script {1} {2} in {3} {4} (assembly {5}). XML is {6}. Exception {7}{8}",
  292. savedState, ScriptName, ItemID, PrimName, ObjectID, assembly, xml, e.Message, e.StackTrace);
  293. }
  294. }
  295. // else
  296. // {
  297. // ScenePresence presence = Engine.World.GetScenePresence(part.OwnerID);
  298. // if (presence != null && (!postOnRez))
  299. // presence.ControllingClient.SendAgentAlertMessage("Compile successful", false);
  300. // }
  301. }
  302. public void Init()
  303. {
  304. if (ShuttingDown)
  305. return;
  306. if (m_startedFromSavedState)
  307. {
  308. if (m_startOnInit)
  309. Start();
  310. if (m_postOnRez)
  311. {
  312. PostEvent(new EventParams("on_rez",
  313. new Object[] {new LSL_Types.LSLInteger(StartParam)}, new DetectParams[0]));
  314. }
  315. if (m_stateSource == StateSource.AttachedRez)
  316. {
  317. PostEvent(new EventParams("attach",
  318. new object[] { new LSL_Types.LSLString(m_AttachedAvatar.ToString()) }, new DetectParams[0]));
  319. }
  320. else if (m_stateSource == StateSource.RegionStart)
  321. {
  322. //m_log.Debug("[Script] Posted changed(CHANGED_REGION_RESTART) to script");
  323. PostEvent(new EventParams("changed",
  324. new Object[] { new LSL_Types.LSLInteger((int)Changed.REGION_RESTART) }, new DetectParams[0]));
  325. }
  326. else if (m_stateSource == StateSource.PrimCrossing || m_stateSource == StateSource.Teleporting)
  327. {
  328. // CHANGED_REGION
  329. PostEvent(new EventParams("changed",
  330. new Object[] { new LSL_Types.LSLInteger((int)Changed.REGION) }, new DetectParams[0]));
  331. // CHANGED_TELEPORT
  332. if (m_stateSource == StateSource.Teleporting)
  333. PostEvent(new EventParams("changed",
  334. new Object[] { new LSL_Types.LSLInteger((int)Changed.TELEPORT) }, new DetectParams[0]));
  335. }
  336. }
  337. else
  338. {
  339. if (m_startOnInit)
  340. Start();
  341. PostEvent(new EventParams("state_entry",
  342. new Object[0], new DetectParams[0]));
  343. if (m_postOnRez)
  344. {
  345. PostEvent(new EventParams("on_rez",
  346. new Object[] {new LSL_Types.LSLInteger(StartParam)}, new DetectParams[0]));
  347. }
  348. if (m_stateSource == StateSource.AttachedRez)
  349. {
  350. PostEvent(new EventParams("attach",
  351. new object[] { new LSL_Types.LSLString(m_AttachedAvatar.ToString()) }, new DetectParams[0]));
  352. }
  353. }
  354. }
  355. private void ReleaseControls()
  356. {
  357. SceneObjectPart part = Engine.World.GetSceneObjectPart(LocalID);
  358. if (part != null)
  359. {
  360. int permsMask;
  361. UUID permsGranter;
  362. lock (part.TaskInventory)
  363. {
  364. if (!part.TaskInventory.ContainsKey(ItemID))
  365. return;
  366. permsGranter = part.TaskInventory[ItemID].PermsGranter;
  367. permsMask = part.TaskInventory[ItemID].PermsMask;
  368. }
  369. if ((permsMask & ScriptBaseClass.PERMISSION_TAKE_CONTROLS) != 0)
  370. {
  371. ScenePresence presence = Engine.World.GetScenePresence(permsGranter);
  372. if (presence != null)
  373. presence.UnRegisterControlEventsToScript(LocalID, ItemID);
  374. }
  375. }
  376. }
  377. public void DestroyScriptInstance()
  378. {
  379. ReleaseControls();
  380. AsyncCommandManager.RemoveScript(Engine, LocalID, ItemID);
  381. }
  382. public void RemoveState()
  383. {
  384. string savedState = Path.Combine(Path.GetDirectoryName(m_Assembly),
  385. ItemID.ToString() + ".state");
  386. try
  387. {
  388. File.Delete(savedState);
  389. }
  390. catch(Exception)
  391. {
  392. }
  393. }
  394. public void VarDump(Dictionary<string, object> vars)
  395. {
  396. // m_log.Info("Variable dump for script "+ ItemID.ToString());
  397. // foreach (KeyValuePair<string, object> v in vars)
  398. // {
  399. // m_log.Info("Variable: "+v.Key+" = "+v.Value.ToString());
  400. // }
  401. }
  402. public void Start()
  403. {
  404. lock (EventQueue)
  405. {
  406. if (Running)
  407. return;
  408. Running = true;
  409. TimeStarted = DateTime.Now;
  410. MeasurementPeriodTickStart = Util.EnvironmentTickCount();
  411. MeasurementPeriodExecutionTime = 0;
  412. if (EventQueue.Count > 0)
  413. {
  414. if (m_CurrentWorkItem == null)
  415. m_CurrentWorkItem = Engine.QueueEventHandler(this);
  416. // else
  417. // m_log.Error("[Script] Tried to start a script that was already queued");
  418. }
  419. }
  420. }
  421. public bool Stop(int timeout)
  422. {
  423. // m_log.DebugFormat(
  424. // "[SCRIPT INSTANCE]: Stopping script {0} {1} in {2} {3} with timeout {4} {5} {6}",
  425. // ScriptName, ItemID, PrimName, ObjectID, timeout, m_InSelfDelete, DateTime.Now.Ticks);
  426. IScriptWorkItem workItem;
  427. lock (EventQueue)
  428. {
  429. if (!Running)
  430. return true;
  431. // If we're not running or waiting to run an event then we can safely stop.
  432. if (m_CurrentWorkItem == null)
  433. {
  434. Running = false;
  435. return true;
  436. }
  437. // If we are waiting to run an event then we can try to cancel it.
  438. if (m_CurrentWorkItem.Cancel())
  439. {
  440. m_CurrentWorkItem = null;
  441. Running = false;
  442. return true;
  443. }
  444. workItem = m_CurrentWorkItem;
  445. Running = false;
  446. }
  447. // Wait for the current event to complete.
  448. if (!m_InSelfDelete && workItem.Wait(new TimeSpan((long)timeout * 100000)))
  449. {
  450. return true;
  451. }
  452. lock (EventQueue)
  453. {
  454. workItem = m_CurrentWorkItem;
  455. }
  456. if (workItem == null)
  457. return true;
  458. // If the event still hasn't stopped and we the stop isn't the result of script or object removal, then
  459. // forcibly abort the work item (this aborts the underlying thread).
  460. if (!m_InSelfDelete)
  461. {
  462. m_log.DebugFormat(
  463. "[SCRIPT INSTANCE]: Aborting unstopped script {0} {1} in prim {2}, localID {3}, timeout was {4} ms",
  464. ScriptName, ItemID, PrimName, LocalID, timeout);
  465. workItem.Abort();
  466. }
  467. lock (EventQueue)
  468. {
  469. m_CurrentWorkItem = null;
  470. }
  471. return true;
  472. }
  473. public void SetState(string state)
  474. {
  475. if (state == State)
  476. return;
  477. PostEvent(new EventParams("state_exit", new Object[0],
  478. new DetectParams[0]));
  479. PostEvent(new EventParams("state", new Object[] { state },
  480. new DetectParams[0]));
  481. PostEvent(new EventParams("state_entry", new Object[0],
  482. new DetectParams[0]));
  483. throw new EventAbortException();
  484. }
  485. /// <summary>
  486. /// Post an event to this script instance.
  487. /// </summary>
  488. /// <remarks>
  489. /// The request to run the event is sent
  490. /// </remarks>
  491. /// <param name="data"></param>
  492. public void PostEvent(EventParams data)
  493. {
  494. // m_log.DebugFormat("[Script] Posted event {2} in state {3} to {0}.{1}",
  495. // PrimName, ScriptName, data.EventName, State);
  496. if (!Running)
  497. return;
  498. // If min event delay is set then ignore any events untill the time has expired
  499. // This currently only allows 1 event of any type in the given time period.
  500. // This may need extending to allow for a time for each individual event type.
  501. if (m_eventDelayTicks != 0)
  502. {
  503. if (DateTime.Now.Ticks < m_nextEventTimeTicks)
  504. return;
  505. m_nextEventTimeTicks = DateTime.Now.Ticks + m_eventDelayTicks;
  506. }
  507. lock (EventQueue)
  508. {
  509. if (EventQueue.Count >= m_MaxScriptQueue)
  510. return;
  511. if (data.EventName == "timer")
  512. {
  513. if (m_TimerQueued)
  514. return;
  515. m_TimerQueued = true;
  516. }
  517. if (data.EventName == "control")
  518. {
  519. int held = ((LSL_Types.LSLInteger)data.Params[1]).value;
  520. // int changed = ((LSL_Types.LSLInteger)data.Params[2]).value;
  521. // If the last message was a 0 (nothing held)
  522. // and this one is also nothing held, drop it
  523. //
  524. if (m_LastControlLevel == held && held == 0)
  525. return;
  526. // If there is one or more queued, then queue
  527. // only changed ones, else queue unconditionally
  528. //
  529. if (m_ControlEventsInQueue > 0)
  530. {
  531. if (m_LastControlLevel == held)
  532. return;
  533. }
  534. m_LastControlLevel = held;
  535. m_ControlEventsInQueue++;
  536. }
  537. if (data.EventName == "collision")
  538. {
  539. if (m_CollisionInQueue)
  540. return;
  541. if (data.DetectParams == null)
  542. return;
  543. m_CollisionInQueue = true;
  544. }
  545. EventQueue.Enqueue(data);
  546. if (m_CurrentWorkItem == null)
  547. {
  548. m_CurrentWorkItem = Engine.QueueEventHandler(this);
  549. }
  550. }
  551. }
  552. /// <summary>
  553. /// Process the next event queued for this script
  554. /// </summary>
  555. /// <returns></returns>
  556. public object EventProcessor()
  557. {
  558. // We check here as the thread stopping this instance from running may itself hold the m_Script lock.
  559. if (!Running)
  560. return 0;
  561. lock (m_Script)
  562. {
  563. // m_log.DebugFormat("[XEngine]: EventProcessor() invoked for {0}.{1}", PrimName, ScriptName);
  564. if (Suspended)
  565. return 0;
  566. EventParams data = null;
  567. lock (EventQueue)
  568. {
  569. data = (EventParams)EventQueue.Dequeue();
  570. if (data == null) // Shouldn't happen
  571. {
  572. if (EventQueue.Count > 0 && Running && !ShuttingDown)
  573. {
  574. m_CurrentWorkItem = Engine.QueueEventHandler(this);
  575. }
  576. else
  577. {
  578. m_CurrentWorkItem = null;
  579. }
  580. return 0;
  581. }
  582. if (data.EventName == "timer")
  583. m_TimerQueued = false;
  584. if (data.EventName == "control")
  585. {
  586. if (m_ControlEventsInQueue > 0)
  587. m_ControlEventsInQueue--;
  588. }
  589. if (data.EventName == "collision")
  590. m_CollisionInQueue = false;
  591. }
  592. SceneObjectPart part = Engine.World.GetSceneObjectPart(LocalID);
  593. if (DebugLevel >= 2)
  594. m_log.DebugFormat(
  595. "[SCRIPT INSTANCE]: Processing event {0} for {1}/{2}({3})/{4}({5}) @ {6}/{7}",
  596. data.EventName,
  597. ScriptName,
  598. part.Name,
  599. part.LocalId,
  600. part.ParentGroup.Name,
  601. part.ParentGroup.UUID,
  602. part.AbsolutePosition,
  603. part.ParentGroup.Scene.Name);
  604. m_DetectParams = data.DetectParams;
  605. if (data.EventName == "state") // Hardcoded state change
  606. {
  607. State = data.Params[0].ToString();
  608. if (DebugLevel >= 1)
  609. m_log.DebugFormat(
  610. "[SCRIPT INSTANCE]: Changing state to {0} for {1}/{2}({3})/{4}({5}) @ {6}/{7}",
  611. State,
  612. ScriptName,
  613. part.Name,
  614. part.LocalId,
  615. part.ParentGroup.Name,
  616. part.ParentGroup.UUID,
  617. part.AbsolutePosition,
  618. part.ParentGroup.Scene.Name);
  619. AsyncCommandManager.RemoveScript(Engine,
  620. LocalID, ItemID);
  621. if (part != null)
  622. {
  623. part.SetScriptEvents(ItemID,
  624. (int)m_Script.GetStateEventFlags(State));
  625. }
  626. }
  627. else
  628. {
  629. if (Engine.World.PipeEventsForScript(LocalID) ||
  630. data.EventName == "control") // Don't freeze avies!
  631. {
  632. // m_log.DebugFormat("[Script] Delivered event {2} in state {3} to {0}.{1}",
  633. // PrimName, ScriptName, data.EventName, State);
  634. try
  635. {
  636. m_CurrentEvent = data.EventName;
  637. m_EventStart = DateTime.Now;
  638. m_InEvent = true;
  639. int start = Util.EnvironmentTickCount();
  640. // Reset the measurement period when we reach the end of the current one.
  641. if (start - MeasurementPeriodTickStart > MaxMeasurementPeriod)
  642. MeasurementPeriodTickStart = start;
  643. m_Script.ExecuteEvent(State, data.EventName, data.Params);
  644. MeasurementPeriodExecutionTime += Util.EnvironmentTickCount() - start;
  645. m_InEvent = false;
  646. m_CurrentEvent = String.Empty;
  647. if (m_SaveState)
  648. {
  649. // This will be the very first event we deliver
  650. // (state_entry) in default state
  651. //
  652. SaveState(m_Assembly);
  653. m_SaveState = false;
  654. }
  655. }
  656. catch (Exception e)
  657. {
  658. // m_log.DebugFormat(
  659. // "[SCRIPT] Exception in script {0} {1}: {2}{3}",
  660. // ScriptName, ItemID, e.Message, e.StackTrace);
  661. m_InEvent = false;
  662. m_CurrentEvent = String.Empty;
  663. if ((!(e is TargetInvocationException) || (!(e.InnerException is SelfDeleteException) && !(e.InnerException is ScriptDeleteException))) && !(e is ThreadAbortException))
  664. {
  665. try
  666. {
  667. // DISPLAY ERROR INWORLD
  668. string text = FormatException(e);
  669. if (text.Length > 1000)
  670. text = text.Substring(0, 1000);
  671. Engine.World.SimChat(Utils.StringToBytes(text),
  672. ChatTypeEnum.DebugChannel, 2147483647,
  673. part.AbsolutePosition,
  674. part.Name, part.UUID, false);
  675. m_log.DebugFormat(
  676. "[SCRIPT INSTANCE]: Runtime error in script {0}, part {1} {2} at {3} in {4}, displayed error {5}, actual exception {6}",
  677. ScriptName,
  678. PrimName,
  679. part.UUID,
  680. part.AbsolutePosition,
  681. part.ParentGroup.Scene.Name,
  682. text.Replace("\n", "\\n"),
  683. e.InnerException);
  684. }
  685. catch (Exception)
  686. {
  687. }
  688. // catch (Exception e2) // LEGIT: User Scripting
  689. // {
  690. // m_log.Error("[SCRIPT]: "+
  691. // "Error displaying error in-world: " +
  692. // e2.ToString());
  693. // m_log.Error("[SCRIPT]: " +
  694. // "Errormessage: Error compiling script:\r\n" +
  695. // e.ToString());
  696. // }
  697. }
  698. else if ((e is TargetInvocationException) && (e.InnerException is SelfDeleteException))
  699. {
  700. m_InSelfDelete = true;
  701. if (part != null)
  702. Engine.World.DeleteSceneObject(part.ParentGroup, false);
  703. }
  704. else if ((e is TargetInvocationException) && (e.InnerException is ScriptDeleteException))
  705. {
  706. m_InSelfDelete = true;
  707. if (part != null)
  708. part.Inventory.RemoveInventoryItem(ItemID);
  709. }
  710. }
  711. }
  712. }
  713. // If there are more events and we are currently running and not shutting down, then ask the
  714. // script engine to run the next event.
  715. lock (EventQueue)
  716. {
  717. EventsProcessed++;
  718. if (EventQueue.Count > 0 && Running && !ShuttingDown)
  719. {
  720. m_CurrentWorkItem = Engine.QueueEventHandler(this);
  721. }
  722. else
  723. {
  724. m_CurrentWorkItem = null;
  725. }
  726. }
  727. m_DetectParams = null;
  728. return 0;
  729. }
  730. }
  731. public int EventTime()
  732. {
  733. if (!m_InEvent)
  734. return 0;
  735. return (DateTime.Now - m_EventStart).Seconds;
  736. }
  737. public void ResetScript(int timeout)
  738. {
  739. if (m_Script == null)
  740. return;
  741. bool running = Running;
  742. RemoveState();
  743. ReleaseControls();
  744. Stop(timeout);
  745. SceneObjectPart part = Engine.World.GetSceneObjectPart(LocalID);
  746. part.Inventory.GetInventoryItem(ItemID).PermsMask = 0;
  747. part.Inventory.GetInventoryItem(ItemID).PermsGranter = UUID.Zero;
  748. AsyncCommandManager.RemoveScript(Engine, LocalID, ItemID);
  749. EventQueue.Clear();
  750. m_Script.ResetVars();
  751. State = "default";
  752. part.SetScriptEvents(ItemID,
  753. (int)m_Script.GetStateEventFlags(State));
  754. if (running)
  755. Start();
  756. m_SaveState = true;
  757. PostEvent(new EventParams("state_entry",
  758. new Object[0], new DetectParams[0]));
  759. }
  760. public void ApiResetScript()
  761. {
  762. // bool running = Running;
  763. RemoveState();
  764. ReleaseControls();
  765. m_Script.ResetVars();
  766. SceneObjectPart part = Engine.World.GetSceneObjectPart(LocalID);
  767. part.Inventory.GetInventoryItem(ItemID).PermsMask = 0;
  768. part.Inventory.GetInventoryItem(ItemID).PermsGranter = UUID.Zero;
  769. AsyncCommandManager.RemoveScript(Engine, LocalID, ItemID);
  770. EventQueue.Clear();
  771. m_Script.ResetVars();
  772. State = "default";
  773. part.SetScriptEvents(ItemID,
  774. (int)m_Script.GetStateEventFlags(State));
  775. if (m_CurrentEvent != "state_entry")
  776. {
  777. m_SaveState = true;
  778. PostEvent(new EventParams("state_entry",
  779. new Object[0], new DetectParams[0]));
  780. throw new EventAbortException();
  781. }
  782. }
  783. public Dictionary<string, object> GetVars()
  784. {
  785. if (m_Script != null)
  786. return m_Script.GetVars();
  787. else
  788. return new Dictionary<string, object>();
  789. }
  790. public void SetVars(Dictionary<string, object> vars)
  791. {
  792. m_Script.SetVars(vars);
  793. }
  794. public DetectParams GetDetectParams(int idx)
  795. {
  796. if (m_DetectParams == null)
  797. return null;
  798. if (idx < 0 || idx >= m_DetectParams.Length)
  799. return null;
  800. return m_DetectParams[idx];
  801. }
  802. public UUID GetDetectID(int idx)
  803. {
  804. if (m_DetectParams == null)
  805. return UUID.Zero;
  806. if (idx < 0 || idx >= m_DetectParams.Length)
  807. return UUID.Zero;
  808. return m_DetectParams[idx].Key;
  809. }
  810. public void SaveState(string assembly)
  811. {
  812. // If we're currently in an event, just tell it to save upon return
  813. //
  814. if (m_InEvent)
  815. {
  816. m_SaveState = true;
  817. return;
  818. }
  819. PluginData = AsyncCommandManager.GetSerializationData(Engine, ItemID);
  820. string xml = ScriptSerializer.Serialize(this);
  821. // Compare hash of the state we just just created with the state last written to disk
  822. // If the state is different, update the disk file.
  823. UUID hash = UUID.Parse(Utils.MD5String(xml));
  824. if (hash != m_CurrentStateHash)
  825. {
  826. try
  827. {
  828. FileStream fs = File.Create(Path.Combine(Path.GetDirectoryName(assembly), ItemID.ToString() + ".state"));
  829. Byte[] buf = Util.UTF8NoBomEncoding.GetBytes(xml);
  830. fs.Write(buf, 0, buf.Length);
  831. fs.Close();
  832. }
  833. catch(Exception)
  834. {
  835. // m_log.Error("Unable to save xml\n"+e.ToString());
  836. }
  837. //if (!File.Exists(Path.Combine(Path.GetDirectoryName(assembly), ItemID.ToString() + ".state")))
  838. //{
  839. // throw new Exception("Completed persistence save, but no file was created");
  840. //}
  841. m_CurrentStateHash = hash;
  842. }
  843. }
  844. public IScriptApi GetApi(string name)
  845. {
  846. if (m_Apis.ContainsKey(name))
  847. {
  848. // m_log.DebugFormat("[SCRIPT INSTANCE]: Found api {0} in {1}@{2}", name, ScriptName, PrimName);
  849. return m_Apis[name];
  850. }
  851. // m_log.DebugFormat("[SCRIPT INSTANCE]: Did not find api {0} in {1}@{2}", name, ScriptName, PrimName);
  852. return null;
  853. }
  854. public override string ToString()
  855. {
  856. return String.Format("{0} {1} on {2}", ScriptName, ItemID, PrimName);
  857. }
  858. string FormatException(Exception e)
  859. {
  860. if (e.InnerException == null) // Not a normal runtime error
  861. return e.ToString();
  862. string message = "Runtime error:\n" + e.InnerException.StackTrace;
  863. string[] lines = message.Split(new char[] {'\n'});
  864. foreach (string line in lines)
  865. {
  866. if (line.Contains("SecondLife.Script"))
  867. {
  868. int idx = line.IndexOf(':');
  869. if (idx != -1)
  870. {
  871. string val = line.Substring(idx+1);
  872. int lineNum = 0;
  873. if (int.TryParse(val, out lineNum))
  874. {
  875. KeyValuePair<int, int> pos =
  876. Compiler.FindErrorPosition(
  877. lineNum, 0, LineMap);
  878. int scriptLine = pos.Key;
  879. int col = pos.Value;
  880. if (scriptLine == 0)
  881. scriptLine++;
  882. if (col == 0)
  883. col++;
  884. message = string.Format("Runtime error:\n" +
  885. "({0}): {1}", scriptLine - 1,
  886. e.InnerException.Message);
  887. return message;
  888. }
  889. }
  890. }
  891. }
  892. // m_log.ErrorFormat("Scripting exception:");
  893. // m_log.ErrorFormat(e.ToString());
  894. return e.ToString();
  895. }
  896. public string GetAssemblyName()
  897. {
  898. return m_Assembly;
  899. }
  900. public string GetXMLState()
  901. {
  902. bool run = Running;
  903. Stop(100);
  904. Running = run;
  905. // We should not be doing this, but since we are about to
  906. // dispose this, it really doesn't make a difference
  907. // This is meant to work around a Windows only race
  908. //
  909. m_InEvent = false;
  910. // Force an update of the in-memory plugin data
  911. //
  912. PluginData = AsyncCommandManager.GetSerializationData(Engine, ItemID);
  913. return ScriptSerializer.Serialize(this);
  914. }
  915. public UUID RegionID
  916. {
  917. get { return m_RegionID; }
  918. }
  919. public void Suspend()
  920. {
  921. Suspended = true;
  922. }
  923. public void Resume()
  924. {
  925. Suspended = false;
  926. }
  927. }
  928. }