diff options
Diffstat (limited to '')
-rw-r--r-- | OpenSim/Region/ScriptEngine/DotNetEngine/ScriptManager.cs | 460 |
1 files changed, 452 insertions, 8 deletions
diff --git a/OpenSim/Region/ScriptEngine/DotNetEngine/ScriptManager.cs b/OpenSim/Region/ScriptEngine/DotNetEngine/ScriptManager.cs index 8ff3bfd..12a8fe4 100644 --- a/OpenSim/Region/ScriptEngine/DotNetEngine/ScriptManager.cs +++ b/OpenSim/Region/ScriptEngine/DotNetEngine/ScriptManager.cs | |||
@@ -34,22 +34,66 @@ using OpenSim.Region.Environment.Scenes; | |||
34 | using OpenSim.Region.ScriptEngine.Common; | 34 | using OpenSim.Region.ScriptEngine.Common; |
35 | using OpenSim.Region.ScriptEngine.Shared; | 35 | using OpenSim.Region.ScriptEngine.Shared; |
36 | using OpenSim.Region.ScriptEngine.Shared.Api; | 36 | using OpenSim.Region.ScriptEngine.Shared.Api; |
37 | using OpenSim.Region.ScriptEngine.Common.ScriptEngineBase; | 37 | using System.Collections.Generic; |
38 | using System.IO; | ||
39 | using System.Runtime.Serialization.Formatters.Binary; | ||
40 | using System.Threading; | ||
38 | 41 | ||
39 | namespace OpenSim.Region.ScriptEngine.DotNetEngine | 42 | namespace OpenSim.Region.ScriptEngine.DotNetEngine |
40 | { | 43 | { |
41 | public class ScriptManager : Common.ScriptEngineBase.ScriptManager | 44 | public class ScriptManager |
42 | { | 45 | { |
43 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | 46 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); |
44 | 47 | ||
45 | public ScriptManager(Common.ScriptEngineBase.ScriptEngine scriptEngine) | 48 | #region Declares |
46 | : base(scriptEngine) | 49 | |
50 | private Thread scriptLoadUnloadThread; | ||
51 | private static Thread staticScriptLoadUnloadThread; | ||
52 | // private int scriptLoadUnloadThread_IdleSleepms; | ||
53 | private Queue<LUStruct> LUQueue = new Queue<LUStruct>(); | ||
54 | private static bool PrivateThread; | ||
55 | private int LoadUnloadMaxQueueSize; | ||
56 | private Object scriptLock = new Object(); | ||
57 | private bool m_started = false; | ||
58 | private Dictionary<IScript, DetectParams[]> detparms = new Dictionary<IScript, DetectParams[]>(); | ||
59 | |||
60 | // Load/Unload structure | ||
61 | private struct LUStruct | ||
62 | { | ||
63 | public uint localID; | ||
64 | public UUID itemID; | ||
65 | public string script; | ||
66 | public LUType Action; | ||
67 | public int startParam; | ||
68 | public bool postOnRez; | ||
69 | } | ||
70 | |||
71 | private enum LUType | ||
72 | { | ||
73 | Unknown = 0, | ||
74 | Load = 1, | ||
75 | Unload = 2 | ||
76 | } | ||
77 | |||
78 | // Xantor 20080525: Keep a list of compiled scripts this session for reuse | ||
79 | public Dictionary<UUID, String> scriptList = new Dictionary<UUID, string>(); | ||
80 | |||
81 | // Object<string, Script<string, script>> | ||
82 | // IMPORTANT: Types and MemberInfo-derived objects require a LOT of memory. | ||
83 | // Instead use RuntimeTypeHandle, RuntimeFieldHandle and RunTimeHandle (IntPtr) instead! | ||
84 | public Dictionary<uint, Dictionary<UUID, IScript>> Scripts = | ||
85 | new Dictionary<uint, Dictionary<UUID, IScript>>(); | ||
86 | |||
87 | |||
88 | public Scene World | ||
47 | { | 89 | { |
48 | base.m_scriptEngine = scriptEngine; | 90 | get { return m_scriptEngine.World; } |
49 | } | 91 | } |
92 | |||
93 | #endregion | ||
50 | private Compiler.LSL.Compiler LSLCompiler; | 94 | private Compiler.LSL.Compiler LSLCompiler; |
51 | 95 | ||
52 | public override void Initialize() | 96 | public void Initialize() |
53 | { | 97 | { |
54 | // Create our compiler | 98 | // Create our compiler |
55 | LSLCompiler = new Compiler.LSL.Compiler(m_scriptEngine); | 99 | LSLCompiler = new Compiler.LSL.Compiler(m_scriptEngine); |
@@ -62,7 +106,7 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine | |||
62 | // PROVIDE SCRIPT WITH ITS INTERFACE TO OpenSim | 106 | // PROVIDE SCRIPT WITH ITS INTERFACE TO OpenSim |
63 | 107 | ||
64 | 108 | ||
65 | public override void _StartScript(uint localID, UUID itemID, string Script, int startParam, bool postOnRez) | 109 | public void _StartScript(uint localID, UUID itemID, string Script, int startParam, bool postOnRez) |
66 | { | 110 | { |
67 | m_log.DebugFormat( | 111 | m_log.DebugFormat( |
68 | "[{0}]: ScriptManager StartScript: localID: {1}, itemID: {2}", | 112 | "[{0}]: ScriptManager StartScript: localID: {1}, itemID: {2}", |
@@ -173,7 +217,7 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine | |||
173 | } | 217 | } |
174 | } | 218 | } |
175 | 219 | ||
176 | public override void _StopScript(uint localID, UUID itemID) | 220 | public void _StopScript(uint localID, UUID itemID) |
177 | { | 221 | { |
178 | IScript LSLBC = GetScript(localID, itemID); | 222 | IScript LSLBC = GetScript(localID, itemID); |
179 | if (LSLBC == null) | 223 | if (LSLBC == null) |
@@ -202,5 +246,405 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine | |||
202 | ": " + e.ToString()); | 246 | ": " + e.ToString()); |
203 | } | 247 | } |
204 | } | 248 | } |
249 | |||
250 | public void ReadConfig() | ||
251 | { | ||
252 | // scriptLoadUnloadThread_IdleSleepms = m_scriptEngine.ScriptConfigSource.GetInt("ScriptLoadUnloadLoopms", 30); | ||
253 | // TODO: Requires sharing of all ScriptManagers to single thread | ||
254 | PrivateThread = true; // m_scriptEngine.ScriptConfigSource.GetBoolean("PrivateScriptLoadUnloadThread", false); | ||
255 | LoadUnloadMaxQueueSize = m_scriptEngine.ScriptConfigSource.GetInt("LoadUnloadMaxQueueSize", 100); | ||
256 | } | ||
257 | |||
258 | #region Object init/shutdown | ||
259 | |||
260 | public ScriptEngine m_scriptEngine; | ||
261 | |||
262 | public ScriptManager(ScriptEngine scriptEngine) | ||
263 | { | ||
264 | m_scriptEngine = scriptEngine; | ||
265 | } | ||
266 | public void Setup() | ||
267 | { | ||
268 | ReadConfig(); | ||
269 | Initialize(); | ||
270 | } | ||
271 | public void Start() | ||
272 | { | ||
273 | m_started = true; | ||
274 | |||
275 | |||
276 | AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve); | ||
277 | |||
278 | // | ||
279 | // CREATE THREAD | ||
280 | // Private or shared | ||
281 | // | ||
282 | if (PrivateThread) | ||
283 | { | ||
284 | // Assign one thread per region | ||
285 | //scriptLoadUnloadThread = StartScriptLoadUnloadThread(); | ||
286 | } | ||
287 | else | ||
288 | { | ||
289 | // Shared thread - make sure one exist, then assign it to the private | ||
290 | if (staticScriptLoadUnloadThread == null) | ||
291 | { | ||
292 | //staticScriptLoadUnloadThread = StartScriptLoadUnloadThread(); | ||
293 | } | ||
294 | scriptLoadUnloadThread = staticScriptLoadUnloadThread; | ||
295 | } | ||
296 | } | ||
297 | |||
298 | // TODO: unused | ||
299 | // private static int privateThreadCount = 0; | ||
300 | // private Thread StartScriptLoadUnloadThread() | ||
301 | // { | ||
302 | // Thread t = new Thread(ScriptLoadUnloadThreadLoop); | ||
303 | // string name = "ScriptLoadUnloadThread:"; | ||
304 | // if (PrivateThread) | ||
305 | // { | ||
306 | // name += "Private:" + privateThreadCount; | ||
307 | // privateThreadCount++; | ||
308 | // } | ||
309 | // else | ||
310 | // { | ||
311 | // name += "Shared"; | ||
312 | // } | ||
313 | // t.Name = name; | ||
314 | // t.IsBackground = true; | ||
315 | // t.Priority = ThreadPriority.Normal; | ||
316 | // t.Start(); | ||
317 | // OpenSim.Framework.ThreadTracker.Add(t); | ||
318 | // return t; | ||
319 | // } | ||
320 | |||
321 | ~ScriptManager() | ||
322 | { | ||
323 | // Abort load/unload thread | ||
324 | try | ||
325 | { | ||
326 | //PleaseShutdown = true; | ||
327 | //Thread.Sleep(100); | ||
328 | if (scriptLoadUnloadThread != null && scriptLoadUnloadThread.IsAlive == true) | ||
329 | { | ||
330 | scriptLoadUnloadThread.Abort(); | ||
331 | //scriptLoadUnloadThread.Join(); | ||
332 | } | ||
333 | } | ||
334 | catch | ||
335 | { | ||
336 | } | ||
337 | } | ||
338 | |||
339 | #endregion | ||
340 | |||
341 | #region Load / Unload scripts (Thread loop) | ||
342 | |||
343 | // TODO: unused | ||
344 | // private void ScriptLoadUnloadThreadLoop() | ||
345 | // { | ||
346 | // try | ||
347 | // { | ||
348 | // while (true) | ||
349 | // { | ||
350 | // if (LUQueue.Count == 0) | ||
351 | // Thread.Sleep(scriptLoadUnloadThread_IdleSleepms); | ||
352 | // //if (PleaseShutdown) | ||
353 | // // return; | ||
354 | // DoScriptLoadUnload(); | ||
355 | // } | ||
356 | // } | ||
357 | // catch (ThreadAbortException tae) | ||
358 | // { | ||
359 | // string a = tae.ToString(); | ||
360 | // a = String.Empty; | ||
361 | // // Expected | ||
362 | // } | ||
363 | // } | ||
364 | |||
365 | public void DoScriptLoadUnload() | ||
366 | { | ||
367 | if (!m_started) | ||
368 | return; | ||
369 | |||
370 | lock (LUQueue) | ||
371 | { | ||
372 | if (LUQueue.Count > 0) | ||
373 | { | ||
374 | m_scriptEngine.Log.InfoFormat("[{0}]: Loading script", m_scriptEngine.ScriptEngineName); | ||
375 | LUStruct item = LUQueue.Dequeue(); | ||
376 | |||
377 | if (item.Action == LUType.Unload) | ||
378 | { | ||
379 | _StopScript(item.localID, item.itemID); | ||
380 | RemoveScript(item.localID, item.itemID); | ||
381 | } | ||
382 | else if (item.Action == LUType.Load) | ||
383 | { | ||
384 | _StartScript(item.localID, item.itemID, item.script, item.startParam, item.postOnRez); | ||
385 | } | ||
386 | } | ||
387 | } | ||
388 | } | ||
389 | |||
390 | #endregion | ||
391 | |||
392 | #region Helper functions | ||
393 | |||
394 | private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) | ||
395 | { | ||
396 | //Console.WriteLine("ScriptManager.CurrentDomain_AssemblyResolve: " + args.Name); | ||
397 | return Assembly.GetExecutingAssembly().FullName == args.Name ? Assembly.GetExecutingAssembly() : null; | ||
398 | } | ||
399 | |||
400 | #endregion | ||
401 | |||
402 | |||
403 | |||
404 | #region Start/Stop/Reset script | ||
405 | |||
406 | // private readonly Object startStopLock = new Object(); | ||
407 | |||
408 | /// <summary> | ||
409 | /// Fetches, loads and hooks up a script to an objects events | ||
410 | /// </summary> | ||
411 | /// <param name="itemID"></param> | ||
412 | /// <param name="localID"></param> | ||
413 | public void StartScript(uint localID, UUID itemID, string Script, int startParam, bool postOnRez) | ||
414 | { | ||
415 | lock (LUQueue) | ||
416 | { | ||
417 | if ((LUQueue.Count >= LoadUnloadMaxQueueSize) && m_started) | ||
418 | { | ||
419 | m_scriptEngine.Log.Error("[" + m_scriptEngine.ScriptEngineName + "]: ERROR: Load/unload queue item count is at " + LUQueue.Count + ". Config variable \"LoadUnloadMaxQueueSize\" is set to " + LoadUnloadMaxQueueSize + ", so ignoring new script."); | ||
420 | return; | ||
421 | } | ||
422 | |||
423 | LUStruct ls = new LUStruct(); | ||
424 | ls.localID = localID; | ||
425 | ls.itemID = itemID; | ||
426 | ls.script = Script; | ||
427 | ls.Action = LUType.Load; | ||
428 | ls.startParam = startParam; | ||
429 | ls.postOnRez = postOnRez; | ||
430 | LUQueue.Enqueue(ls); | ||
431 | m_scriptEngine.Log.InfoFormat("[{0}]: Queued script for load", m_scriptEngine.ScriptEngineName); | ||
432 | } | ||
433 | } | ||
434 | |||
435 | /// <summary> | ||
436 | /// Disables and unloads a script | ||
437 | /// </summary> | ||
438 | /// <param name="localID"></param> | ||
439 | /// <param name="itemID"></param> | ||
440 | public void StopScript(uint localID, UUID itemID) | ||
441 | { | ||
442 | LUStruct ls = new LUStruct(); | ||
443 | ls.localID = localID; | ||
444 | ls.itemID = itemID; | ||
445 | ls.Action = LUType.Unload; | ||
446 | ls.startParam = 0; | ||
447 | ls.postOnRez = false; | ||
448 | lock (LUQueue) | ||
449 | { | ||
450 | LUQueue.Enqueue(ls); | ||
451 | } | ||
452 | } | ||
453 | |||
454 | // Create a new instance of the compiler (reuse) | ||
455 | //private Compiler.LSL.Compiler LSLCompiler = new Compiler.LSL.Compiler(); | ||
456 | |||
457 | |||
458 | #endregion | ||
459 | |||
460 | #region Perform event execution in script | ||
461 | |||
462 | /// <summary> | ||
463 | /// Execute a LL-event-function in Script | ||
464 | /// </summary> | ||
465 | /// <param name="localID">Object the script is located in</param> | ||
466 | /// <param name="itemID">Script ID</param> | ||
467 | /// <param name="FunctionName">Name of function</param> | ||
468 | /// <param name="args">Arguments to pass to function</param> | ||
469 | internal void ExecuteEvent(uint localID, UUID itemID, string FunctionName, DetectParams[] qParams, object[] args) | ||
470 | { | ||
471 | //cfk 2-7-08 dont need this right now and the default Linux build has DEBUG defined | ||
472 | ///#if DEBUG | ||
473 | /// Console.WriteLine("ScriptEngine: Inside ExecuteEvent for event " + FunctionName); | ||
474 | ///#endif | ||
475 | // Execute a function in the script | ||
476 | //m_scriptEngine.Log.Info("[" + ScriptEngineName + "]: Executing Function localID: " + localID + ", itemID: " + itemID + ", FunctionName: " + FunctionName); | ||
477 | //ScriptBaseInterface Script = (ScriptBaseInterface)GetScript(localID, itemID); | ||
478 | IScript Script = GetScript(localID, itemID); | ||
479 | if (Script == null) | ||
480 | { | ||
481 | return; | ||
482 | } | ||
483 | //cfk 2-7-08 dont need this right now and the default Linux build has DEBUG defined | ||
484 | ///#if DEBUG | ||
485 | /// Console.WriteLine("ScriptEngine: Executing event: " + FunctionName); | ||
486 | ///#endif | ||
487 | // Must be done in correct AppDomain, so leaving it up to the script itself | ||
488 | detparms[Script] = qParams; | ||
489 | Script.Exec.ExecuteEvent(FunctionName, args); | ||
490 | detparms.Remove(Script); | ||
491 | } | ||
492 | |||
493 | public uint GetLocalID(UUID itemID) | ||
494 | { | ||
495 | foreach (KeyValuePair<uint, Dictionary<UUID, IScript> > k in Scripts) | ||
496 | { | ||
497 | if (k.Value.ContainsKey(itemID)) | ||
498 | return k.Key; | ||
499 | } | ||
500 | return 0; | ||
501 | } | ||
502 | |||
503 | public int GetStateEventFlags(uint localID, UUID itemID) | ||
504 | { | ||
505 | // Console.WriteLine("GetStateEventFlags for <" + localID + "," + itemID + ">"); | ||
506 | try | ||
507 | { | ||
508 | IScript Script = GetScript(localID, itemID); | ||
509 | if (Script == null) | ||
510 | { | ||
511 | return 0; | ||
512 | } | ||
513 | ExecutorBase.scriptEvents evflags = Script.Exec.GetStateEventFlags(); | ||
514 | return (int)evflags; | ||
515 | } | ||
516 | catch (Exception) | ||
517 | { | ||
518 | } | ||
519 | |||
520 | return 0; | ||
521 | } | ||
522 | |||
523 | |||
524 | #endregion | ||
525 | |||
526 | #region Internal functions to keep track of script | ||
527 | |||
528 | public List<UUID> GetScriptKeys(uint localID) | ||
529 | { | ||
530 | if (Scripts.ContainsKey(localID) == false) | ||
531 | return new List<UUID>(); | ||
532 | |||
533 | Dictionary<UUID, IScript> Obj; | ||
534 | Scripts.TryGetValue(localID, out Obj); | ||
535 | |||
536 | return new List<UUID>(Obj.Keys); | ||
537 | } | ||
538 | |||
539 | public IScript GetScript(uint localID, UUID itemID) | ||
540 | { | ||
541 | lock (scriptLock) | ||
542 | { | ||
543 | IScript Script = null; | ||
544 | |||
545 | if (Scripts.ContainsKey(localID) == false) | ||
546 | return null; | ||
547 | |||
548 | Dictionary<UUID, IScript> Obj; | ||
549 | Scripts.TryGetValue(localID, out Obj); | ||
550 | if (Obj.ContainsKey(itemID) == false) | ||
551 | return null; | ||
552 | |||
553 | // Get script | ||
554 | Obj.TryGetValue(itemID, out Script); | ||
555 | return Script; | ||
556 | } | ||
557 | } | ||
558 | |||
559 | public void SetScript(uint localID, UUID itemID, IScript Script) | ||
560 | { | ||
561 | lock (scriptLock) | ||
562 | { | ||
563 | // Create object if it doesn't exist | ||
564 | if (Scripts.ContainsKey(localID) == false) | ||
565 | { | ||
566 | Scripts.Add(localID, new Dictionary<UUID, IScript>()); | ||
567 | } | ||
568 | |||
569 | // Delete script if it exists | ||
570 | Dictionary<UUID, IScript> Obj; | ||
571 | Scripts.TryGetValue(localID, out Obj); | ||
572 | if (Obj.ContainsKey(itemID) == true) | ||
573 | Obj.Remove(itemID); | ||
574 | |||
575 | // Add to object | ||
576 | Obj.Add(itemID, Script); | ||
577 | } | ||
578 | } | ||
579 | |||
580 | public void RemoveScript(uint localID, UUID itemID) | ||
581 | { | ||
582 | if (localID == 0) | ||
583 | localID = GetLocalID(itemID); | ||
584 | |||
585 | // Don't have that object? | ||
586 | if (Scripts.ContainsKey(localID) == false) | ||
587 | return; | ||
588 | |||
589 | // Delete script if it exists | ||
590 | Dictionary<UUID, IScript> Obj; | ||
591 | Scripts.TryGetValue(localID, out Obj); | ||
592 | if (Obj.ContainsKey(itemID) == true) | ||
593 | Obj.Remove(itemID); | ||
594 | } | ||
595 | |||
596 | #endregion | ||
597 | |||
598 | |||
599 | public void ResetScript(uint localID, UUID itemID) | ||
600 | { | ||
601 | IScript s = GetScript(localID, itemID); | ||
602 | string script = s.Source; | ||
603 | StopScript(localID, itemID); | ||
604 | SceneObjectPart part = World.GetSceneObjectPart(localID); | ||
605 | part.GetInventoryItem(itemID).PermsMask = 0; | ||
606 | part.GetInventoryItem(itemID).PermsGranter = UUID.Zero; | ||
607 | StartScript(localID, itemID, script, s.StartParam, false); | ||
608 | } | ||
609 | |||
610 | |||
611 | #region Script serialization/deserialization | ||
612 | |||
613 | public void GetSerializedScript(uint localID, UUID itemID) | ||
614 | { | ||
615 | // Serialize the script and return it | ||
616 | // Should not be a problem | ||
617 | FileStream fs = File.Create("SERIALIZED_SCRIPT_" + itemID); | ||
618 | BinaryFormatter b = new BinaryFormatter(); | ||
619 | b.Serialize(fs, GetScript(localID, itemID)); | ||
620 | fs.Close(); | ||
621 | } | ||
622 | |||
623 | public void PutSerializedScript(uint localID, UUID itemID) | ||
624 | { | ||
625 | // Deserialize the script and inject it into an AppDomain | ||
626 | |||
627 | // How to inject into an AppDomain? | ||
628 | } | ||
629 | |||
630 | #endregion | ||
631 | |||
632 | ///// <summary> | ||
633 | ///// If set to true then threads and stuff should try to make a graceful exit | ||
634 | ///// </summary> | ||
635 | //public bool PleaseShutdown | ||
636 | //{ | ||
637 | // get { return _PleaseShutdown; } | ||
638 | // set { _PleaseShutdown = value; } | ||
639 | //} | ||
640 | //private bool _PleaseShutdown = false; | ||
641 | |||
642 | public DetectParams[] GetDetectParams(IScript script) | ||
643 | { | ||
644 | if (detparms.ContainsKey(script)) | ||
645 | return detparms[script]; | ||
646 | |||
647 | return null; | ||
648 | } | ||
205 | } | 649 | } |
206 | } | 650 | } |