ScriptInstance.cs 38 KB

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