aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs
diff options
context:
space:
mode:
authorMelanie Thielker2008-08-27 22:38:36 +0000
committerMelanie Thielker2008-08-27 22:38:36 +0000
commit6e3367d68ca6e0e632078dc02f52b03bd034afce (patch)
tree787b31ac8ce1d29b40869aafa1bebd476e86979a /OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs
parentRefactor Executor into the script app domain and IScript. This changes (diff)
downloadopensim-SC-6e3367d68ca6e0e632078dc02f52b03bd034afce.zip
opensim-SC-6e3367d68ca6e0e632078dc02f52b03bd034afce.tar.gz
opensim-SC-6e3367d68ca6e0e632078dc02f52b03bd034afce.tar.bz2
opensim-SC-6e3367d68ca6e0e632078dc02f52b03bd034afce.tar.xz
Refactor XScriptInstance to IScriptInstance and move into Shared/. Now
engines that want to use the XEngine's instance handling and state persistence can do so. IScriptInstance is optional, but it does require the SmartThreadPool if it is used.
Diffstat (limited to 'OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs')
-rw-r--r--OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs660
1 files changed, 660 insertions, 0 deletions
diff --git a/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs b/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs
new file mode 100644
index 0000000..30c0274
--- /dev/null
+++ b/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs
@@ -0,0 +1,660 @@
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 OpenSim Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.IO;
30using System.Threading;
31using System.Collections;
32using System.Collections.Generic;
33using System.Security.Policy;
34using System.Reflection;
35using System.Globalization;
36using System.Xml;
37using libsecondlife;
38using log4net;
39using Nini.Config;
40using Amib.Threading;
41using OpenSim.Framework;
42using OpenSim.Region.Environment;
43using OpenSim.Region.Environment.Scenes;
44using OpenSim.Region.Environment.Interfaces;
45using OpenSim.Region.ScriptEngine.Shared;
46using OpenSim.Region.ScriptEngine.Shared.Api;
47using OpenSim.Region.ScriptEngine.Shared.ScriptBase;
48using OpenSim.Region.ScriptEngine.Shared.CodeTools;
49using OpenSim.Region.ScriptEngine.Interfaces;
50
51namespace OpenSim.Region.ScriptEngine.Shared.Instance
52{
53 public class ScriptInstance : IScriptInstance
54 {
55 private IScriptEngine m_Engine;
56 private IWorkItemResult m_CurrentResult=null;
57 private Queue m_EventQueue = new Queue(32);
58 private bool m_RunEvents = false;
59 private LLUUID m_ItemID;
60 private uint m_LocalID;
61 private LLUUID m_ObjectID;
62 private LLUUID m_AssetID;
63 private IScript m_Script;
64 private LLUUID m_AppDomain;
65 private DetectParams[] m_DetectParams;
66 private bool m_TimerQueued;
67 private DateTime m_EventStart;
68 private bool m_InEvent;
69 private string m_PrimName;
70 private string m_ScriptName;
71 private string m_Assembly;
72 private int m_StartParam = 0;
73 private string m_CurrentEvent = String.Empty;
74 private bool m_InSelfDelete = false;
75
76 private Dictionary<string,IScriptApi> m_Apis = new Dictionary<string,IScriptApi>();
77
78 // Script state
79 private string m_State="default";
80
81 public Object[] PluginData = new Object[0];
82
83 public bool Running
84 {
85 get { return m_RunEvents; }
86 set { m_RunEvents = value; }
87 }
88
89 public string State
90 {
91 get { return m_State; }
92 set { m_State = value; }
93 }
94
95 public IScriptEngine Engine
96 {
97 get { return m_Engine; }
98 }
99
100 public LLUUID AppDomain
101 {
102 get { return m_AppDomain; }
103 set { m_AppDomain = value; }
104 }
105
106 public string PrimName
107 {
108 get { return m_PrimName; }
109 }
110
111 public string ScriptName
112 {
113 get { return m_ScriptName; }
114 }
115
116 public LLUUID ItemID
117 {
118 get { return m_ItemID; }
119 }
120
121 public LLUUID ObjectID
122 {
123 get { return m_ObjectID; }
124 }
125
126 public uint LocalID
127 {
128 get { return m_LocalID; }
129 }
130
131 public LLUUID AssetID
132 {
133 get { return m_AssetID; }
134 }
135
136 public Queue EventQueue
137 {
138 get { return m_EventQueue; }
139 }
140
141 public void ClearQueue()
142 {
143 m_TimerQueued = false;
144 m_EventQueue.Clear();
145 }
146
147 public int StartParam
148 {
149 get { return m_StartParam; }
150 set { m_StartParam = value; }
151 }
152
153 public ScriptInstance(IScriptEngine engine, uint localID,
154 LLUUID objectID, LLUUID itemID, LLUUID assetID, string assembly,
155 AppDomain dom, string primName, string scriptName,
156 int startParam, bool postOnRez, StateSource stateSource)
157 {
158 m_Engine = engine;
159
160 m_LocalID = localID;
161 m_ObjectID = objectID;
162 m_ItemID = itemID;
163 m_AssetID = assetID;
164 m_PrimName = primName;
165 m_ScriptName = scriptName;
166 m_Assembly = assembly;
167 m_StartParam = startParam;
168
169 ApiManager am = new ApiManager();
170
171 SceneObjectPart part=engine.World.GetSceneObjectPart(localID);
172 if (part == null)
173 {
174 engine.Log.Error("[Script] SceneObjectPart unavailable. Script NOT started.");
175 return;
176 }
177
178 foreach (string api in am.GetApis())
179 {
180 m_Apis[api] = am.CreateApi(api);
181 m_Apis[api].Initialize(engine, part, localID, itemID);
182 }
183
184 try
185 {
186 m_Script = (IScript)dom.CreateInstanceAndUnwrap(
187 Path.GetFileNameWithoutExtension(assembly),
188 "SecondLife.Script");
189 }
190 catch (Exception e)
191 {
192 m_Engine.Log.ErrorFormat("[Script] Error loading assembly {0}\n"+e.ToString(), assembly);
193 }
194
195 try
196 {
197 foreach (KeyValuePair<string,IScriptApi> kv in m_Apis)
198 {
199 m_Script.InitApi(kv.Key, kv.Value);
200 }
201
202// m_Engine.Log.Debug("[Script] Script instance created");
203
204 part.SetScriptEvents(m_ItemID,
205 (int)m_Script.GetStateEventFlags(State));
206 }
207 catch (Exception e)
208 {
209 m_Engine.Log.Error("[Script] Error loading script instance\n"+e.ToString());
210 return;
211 }
212
213 string savedState = Path.Combine(Path.GetDirectoryName(assembly),
214 m_ItemID.ToString() + ".state");
215 if (File.Exists(savedState))
216 {
217 string xml = String.Empty;
218
219 try
220 {
221 FileInfo fi = new FileInfo(savedState);
222 int size=(int)fi.Length;
223 if (size < 512000)
224 {
225 using (FileStream fs = File.Open(savedState,
226 FileMode.Open, FileAccess.Read, FileShare.None))
227 {
228 System.Text.ASCIIEncoding enc =
229 new System.Text.ASCIIEncoding();
230
231 Byte[] data = new Byte[size];
232 fs.Read(data, 0, size);
233
234 xml = enc.GetString(data);
235
236 ScriptSerializer.Deserialize(xml, this);
237
238 AsyncCommandManager async = (AsyncCommandManager)m_Engine.AsyncCommands;
239 async.CreateFromData(
240 m_LocalID, m_ItemID, m_ObjectID,
241 PluginData);
242
243 m_Engine.Log.DebugFormat("[Script] Successfully retrieved state for script {0}.{1}", m_PrimName, m_ScriptName);
244
245 if (m_RunEvents)
246 {
247 m_RunEvents = false;
248 Start();
249 if (postOnRez)
250 PostEvent(new EventParams("on_rez",
251 new Object[] {new LSL_Types.LSLInteger(startParam)}, new DetectParams[0]));
252 }
253
254 // we get new rez events on sim restart, too
255 // but if there is state, then we fire the change
256 // event
257 if (stateSource == StateSource.NewRez)
258 {
259// m_Engine.Log.Debug("[Script] Posted changed(CHANGED_REGION_RESTART) to script");
260 PostEvent(new EventParams("changed",
261 new Object[] {new LSL_Types.LSLInteger(256)}, new DetectParams[0]));
262 }
263 }
264 }
265 else
266 {
267 m_Engine.Log.Error("[Script] Unable to load script state: Memory limit exceeded");
268 Start();
269 PostEvent(new EventParams("state_entry",
270 new Object[0], new DetectParams[0]));
271 if (postOnRez)
272 PostEvent(new EventParams("on_rez",
273 new Object[] {new LSL_Types.LSLInteger(startParam)}, new DetectParams[0]));
274
275 }
276 }
277 catch (Exception e)
278 {
279 m_Engine.Log.ErrorFormat("[Script] Unable to load script state from xml: {0}\n"+e.ToString(), xml);
280 Start();
281 PostEvent(new EventParams("state_entry",
282 new Object[0], new DetectParams[0]));
283 if (postOnRez)
284 PostEvent(new EventParams("on_rez",
285 new Object[] {new LSL_Types.LSLInteger(startParam)}, new DetectParams[0]));
286 }
287 }
288 else
289 {
290// m_Engine.Log.ErrorFormat("[Script] Unable to load script state, file not found");
291 Start();
292 PostEvent(new EventParams("state_entry",
293 new Object[0], new DetectParams[0]));
294
295 if (postOnRez)
296 PostEvent(new EventParams("on_rez",
297 new Object[] {new LSL_Types.LSLInteger(startParam)}, new DetectParams[0]));
298 }
299 }
300
301 public void RemoveState()
302 {
303 string savedState = Path.Combine(Path.GetDirectoryName(m_Assembly),
304 m_ItemID.ToString() + ".state");
305
306 try
307 {
308 File.Delete(savedState);
309 }
310 catch(Exception)
311 {
312 }
313 }
314
315 public void VarDump(Dictionary<string, object> vars)
316 {
317 Console.WriteLine("Variable dump for script {0}", m_ItemID.ToString());
318 foreach (KeyValuePair<string, object> v in vars)
319 {
320 Console.WriteLine("Variable: {0} = '{1}'", v. Key,
321 v.Value.ToString());
322 }
323 }
324
325 public void Start()
326 {
327 lock (m_EventQueue)
328 {
329 if (Running)
330 return;
331
332 m_RunEvents = true;
333
334 if (m_EventQueue.Count > 0)
335 {
336 if (m_CurrentResult == null)
337 m_CurrentResult = m_Engine.QueueEventHandler(this);
338 else
339 m_Engine.Log.Error("[Script] Tried to start a script that was already queued");
340 }
341 }
342 }
343
344 public bool Stop(int timeout)
345 {
346 IWorkItemResult result;
347
348 lock (m_EventQueue)
349 {
350 if (!Running)
351 return true;
352
353 if (m_CurrentResult == null)
354 {
355 m_RunEvents = false;
356 return true;
357 }
358
359 if (m_CurrentResult.Cancel())
360 {
361 m_CurrentResult = null;
362 m_RunEvents = false;
363 return true;
364 }
365
366 result = m_CurrentResult;
367 m_RunEvents = false;
368 }
369
370 if (SmartThreadPool.WaitAll(new IWorkItemResult[] {result}, new TimeSpan((long)timeout * 100000), false))
371 {
372 return true;
373 }
374
375 lock (m_EventQueue)
376 {
377 result = m_CurrentResult;
378 }
379
380 if (result == null)
381 return true;
382
383 if (!m_InSelfDelete)
384 result.Abort();
385
386 lock (m_EventQueue)
387 {
388 m_CurrentResult = null;
389 }
390
391 return true;
392 }
393
394 public void SetState(string state)
395 {
396 PostEvent(new EventParams("state_exit", new Object[0],
397 new DetectParams[0]));
398 PostEvent(new EventParams("state", new Object[] { state },
399 new DetectParams[0]));
400 PostEvent(new EventParams("state_entry", new Object[0],
401 new DetectParams[0]));
402 }
403
404 public void PostEvent(EventParams data)
405 {
406// m_Engine.Log.DebugFormat("[Script] Posted event {2} in state {3} to {0}.{1}",
407// m_PrimName, m_ScriptName, data.EventName, m_State);
408
409 if (!Running)
410 return;
411
412 lock (m_EventQueue)
413 {
414 if (m_EventQueue.Count >= m_Engine.MaxScriptQueue)
415 return;
416
417 m_EventQueue.Enqueue(data);
418 if (data.EventName == "timer")
419 {
420 if (m_TimerQueued)
421 return;
422 m_TimerQueued = true;
423 }
424
425 if (!m_RunEvents)
426 return;
427
428 if (m_CurrentResult == null)
429 {
430 m_CurrentResult = m_Engine.QueueEventHandler(this);
431 }
432 }
433 }
434
435 public object EventProcessor()
436 {
437 EventParams data = null;
438
439 lock (m_EventQueue)
440 {
441 data = (EventParams) m_EventQueue.Dequeue();
442 if (data == null) // Shouldn't happen
443 {
444 m_CurrentResult = null;
445 return 0;
446 }
447 if (data.EventName == "timer")
448 m_TimerQueued = false;
449 }
450
451 m_DetectParams = data.DetectParams;
452
453 if (data.EventName == "state") // Hardcoded state change
454 {
455// m_Engine.Log.DebugFormat("[Script] Script {0}.{1} state set to {2}",
456// m_PrimName, m_ScriptName, data.Params[0].ToString());
457 m_State=data.Params[0].ToString();
458 AsyncCommandManager async = (AsyncCommandManager)m_Engine.AsyncCommands;
459 async.RemoveScript(
460 m_LocalID, m_ItemID);
461
462 SceneObjectPart part = m_Engine.World.GetSceneObjectPart(
463 m_LocalID);
464 if (part != null)
465 {
466 part.SetScriptEvents(m_ItemID,
467 (int)m_Script.GetStateEventFlags(State));
468 }
469 }
470 else
471 {
472 SceneObjectPart part = m_Engine.World.GetSceneObjectPart(
473 m_LocalID);
474// m_Engine.Log.DebugFormat("[Script] Delivered event {2} in state {3} to {0}.{1}",
475// m_PrimName, m_ScriptName, data.EventName, m_State);
476
477 try
478 {
479 m_CurrentEvent = data.EventName;
480 m_EventStart = DateTime.Now;
481 m_InEvent = true;
482
483 m_Script.ExecuteEvent(State, data.EventName, data.Params);
484
485 m_InEvent = false;
486 m_CurrentEvent = String.Empty;
487 }
488 catch (Exception e)
489 {
490 m_InEvent = false;
491 m_CurrentEvent = String.Empty;
492
493 if (!(e is TargetInvocationException) || (!(e.InnerException is EventAbortException) && (!(e.InnerException is SelfDeleteException))))
494 {
495 if (e is System.Threading.ThreadAbortException)
496 {
497 lock (m_EventQueue)
498 {
499 if ((m_EventQueue.Count > 0) && m_RunEvents)
500 {
501 m_CurrentResult=m_Engine.QueueEventHandler(this);
502 }
503 else
504 {
505 m_CurrentResult = null;
506 }
507 }
508
509 m_DetectParams = null;
510
511 return 0;
512 }
513
514 try
515 {
516 // DISPLAY ERROR INWORLD
517 string text = "Runtime error:\n" + e.ToString();
518 if (text.Length > 1400)
519 text = text.Substring(0, 1400);
520 m_Engine.World.SimChat(Helpers.StringToField(text),
521 ChatTypeEnum.DebugChannel, 2147483647,
522 part.AbsolutePosition,
523 part.Name, part.UUID, false);
524 }
525 catch (Exception e2) // LEGIT: User Scripting
526 {
527 m_Engine.Log.Error("[Script]: "+
528 "Error displaying error in-world: " +
529 e2.ToString());
530 m_Engine.Log.Error("[Script]: " +
531 "Errormessage: Error compiling script:\r\n" +
532 e.ToString());
533 }
534 }
535 else if ((e is TargetInvocationException) && (e.InnerException is SelfDeleteException))
536 {
537 m_InSelfDelete = true;
538 if (part != null && part.ParentGroup != null)
539 m_Engine.World.DeleteSceneObject(part.ParentGroup);
540 }
541 }
542 }
543
544 lock (m_EventQueue)
545 {
546 if ((m_EventQueue.Count > 0) && m_RunEvents)
547 {
548 m_CurrentResult = m_Engine.QueueEventHandler(this);
549 }
550 else
551 {
552 m_CurrentResult = null;
553 }
554 }
555
556 m_DetectParams = null;
557
558 return 0;
559 }
560
561 public int EventTime()
562 {
563 if (!m_InEvent)
564 return 0;
565
566 return (DateTime.Now - m_EventStart).Seconds;
567 }
568
569 public void ResetScript()
570 {
571 bool running = Running;
572
573 RemoveState();
574
575 Stop(0);
576 SceneObjectPart part=m_Engine.World.GetSceneObjectPart(m_LocalID);
577 part.GetInventoryItem(m_ItemID).PermsMask = 0;
578 part.GetInventoryItem(m_ItemID).PermsGranter = LLUUID.Zero;
579 AsyncCommandManager async = (AsyncCommandManager)m_Engine.AsyncCommands;
580 async.RemoveScript(m_LocalID, m_ItemID);
581 m_EventQueue.Clear();
582 m_Script.ResetVars();
583 m_State = "default";
584 if (running)
585 Start();
586 PostEvent(new EventParams("state_entry",
587 new Object[0], new DetectParams[0]));
588 }
589
590 public void ApiResetScript()
591 {
592 // bool running = Running;
593
594 RemoveState();
595
596 m_Script.ResetVars();
597 SceneObjectPart part=m_Engine.World.GetSceneObjectPart(m_LocalID);
598 part.GetInventoryItem(m_ItemID).PermsMask = 0;
599 part.GetInventoryItem(m_ItemID).PermsGranter = LLUUID.Zero;
600 AsyncCommandManager async = (AsyncCommandManager)m_Engine.AsyncCommands;
601 async.RemoveScript(m_LocalID, m_ItemID);
602 if (m_CurrentEvent != "state_entry")
603 {
604 PostEvent(new EventParams("state_entry",
605 new Object[0], new DetectParams[0]));
606 }
607 }
608
609 public Dictionary<string, object> GetVars()
610 {
611 return m_Script.GetVars();
612 }
613
614 public void SetVars(Dictionary<string, object> vars)
615 {
616 m_Script.SetVars(vars);
617 }
618
619 public DetectParams GetDetectParams(int idx)
620 {
621 if (idx < 0 || idx >= m_DetectParams.Length)
622 return null;
623
624 return m_DetectParams[idx];
625 }
626
627 public LLUUID GetDetectID(int idx)
628 {
629 if (idx < 0 || idx >= m_DetectParams.Length)
630 return LLUUID.Zero;
631
632 return m_DetectParams[idx].Key;
633 }
634
635 public void SaveState(string assembly)
636 {
637 AsyncCommandManager async = (AsyncCommandManager)m_Engine.AsyncCommands;
638 PluginData = async.GetSerializationData(m_ItemID);
639
640 string xml = ScriptSerializer.Serialize(this);
641
642 try
643 {
644 FileStream fs = File.Create(Path.Combine(Path.GetDirectoryName(assembly), m_ItemID.ToString() + ".state"));
645 System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding();
646 Byte[] buf = enc.GetBytes(xml);
647 fs.Write(buf, 0, buf.Length);
648 fs.Close();
649 }
650 catch(Exception e)
651 {
652 Console.WriteLine("Unable to save xml\n"+e.ToString());
653 }
654 if (!File.Exists(Path.Combine(Path.GetDirectoryName(assembly), m_ItemID.ToString() + ".state")))
655 {
656 throw new Exception("Completed persistence save, but no file was created");
657 }
658 }
659 }
660}