aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/ScriptEngine/DotNetEngine/ScriptManager.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/ScriptEngine/DotNetEngine/ScriptManager.cs')
-rw-r--r--OpenSim/Region/ScriptEngine/DotNetEngine/ScriptManager.cs438
1 files changed, 72 insertions, 366 deletions
diff --git a/OpenSim/Region/ScriptEngine/DotNetEngine/ScriptManager.cs b/OpenSim/Region/ScriptEngine/DotNetEngine/ScriptManager.cs
index 223bb8f..0c28ac7 100644
--- a/OpenSim/Region/ScriptEngine/DotNetEngine/ScriptManager.cs
+++ b/OpenSim/Region/ScriptEngine/DotNetEngine/ScriptManager.cs
@@ -35,421 +35,127 @@ using System.Threading;
35using libsecondlife; 35using libsecondlife;
36using OpenSim.Framework; 36using OpenSim.Framework;
37using OpenSim.Region.Environment.Scenes; 37using OpenSim.Region.Environment.Scenes;
38using OpenSim.Region.ScriptEngine.DotNetEngine.Compiler; 38using OpenSim.Region.ScriptEngine.Common;
39using OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.LSL; 39using OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.LSL;
40 40
41namespace OpenSim.Region.ScriptEngine.DotNetEngine 41namespace OpenSim.Region.ScriptEngine.DotNetEngine
42{ 42{
43 /// <summary> 43 public class ScriptManager : OpenSim.Region.ScriptEngine.Common.ScriptEngineBase.ScriptManager
44 /// Loads scripts
45 /// Compiles them if necessary
46 /// Execute functions for EventQueueManager (Sends them to script on other AppDomain for execution)
47 /// </summary>
48 ///
49
50 // This class is as close as you get to the script without being inside script class. It handles all the dirty work for other classes.
51 // * Keeps track of running scripts
52 // * Compiles script if necessary (through "Compiler")
53 // * Loads script (through "AppDomainManager" called from for example "EventQueueManager")
54 // * Executes functions inside script (called from for example "EventQueueManager" class)
55 // * Unloads script (through "AppDomainManager" called from for example "EventQueueManager")
56 // * Dedicated load/unload thread, and queues loading/unloading.
57 // This so that scripts starting or stopping will not slow down other theads or whole system.
58 //
59 [Serializable]
60 public class ScriptManager
61 { 44 {
62 #region Declares 45 public ScriptManager(Common.ScriptEngineBase.ScriptEngine scriptEngine)
63 46 : base(scriptEngine)
64 private Thread scriptLoadUnloadThread;
65 private int scriptLoadUnloadThread_IdleSleepms = 100;
66 private Queue<LUStruct> LUQueue = new Queue<LUStruct>();
67
68
69 // Load/Unload structure
70 private struct LUStruct
71 { 47 {
72 public uint localID; 48 base.m_scriptEngine = scriptEngine;
73 public LLUUID itemID;
74 public string script;
75 public LUType Action;
76 }
77
78 private enum LUType
79 {
80 Unknown = 0,
81 Load = 1,
82 Unload = 2
83 }
84 49
85 // Object<string, Script<string, script>>
86 // IMPORTANT: Types and MemberInfo-derived objects require a LOT of memory.
87 // Instead use RuntimeTypeHandle, RuntimeFieldHandle and RunTimeHandle (IntPtr) instead!
88 internal Dictionary<uint, Dictionary<LLUUID, LSL_BaseClass>> Scripts =
89 new Dictionary<uint, Dictionary<LLUUID, LSL_BaseClass>>();
90
91 public Scene World
92 {
93 get { return m_scriptEngine.World; }
94 } 50 }
95 51
96 #endregion 52 // KEEP TRACK OF SCRIPTS <int id, whatever script>
97 53 //internal Dictionary<uint, Dictionary<LLUUID, LSL_BaseClass>> Scripts = new Dictionary<uint, Dictionary<LLUUID, LSL_BaseClass>>();
98 #region Object init/shutdown 54 // LOAD SCRIPT
99 55 // UNLOAD SCRIPT
100 private ScriptEngine m_scriptEngine; 56 // PROVIDE SCRIPT WITH ITS INTERFACE TO OpenSim
101 57
102 public ScriptManager(ScriptEngine scriptEngine) 58 private Compiler.LSL.Compiler LSLCompiler = new Compiler.LSL.Compiler();
103 {
104 m_scriptEngine = scriptEngine;
105 AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
106 scriptLoadUnloadThread = new Thread(ScriptLoadUnloadThreadLoop);
107 scriptLoadUnloadThread.Name = "ScriptLoadUnloadThread";
108 scriptLoadUnloadThread.IsBackground = true;
109 scriptLoadUnloadThread.Priority = ThreadPriority.BelowNormal;
110 scriptLoadUnloadThread.Start();
111 }
112 59
113 ~ScriptManager() 60 public override void _StartScript(uint localID, LLUUID itemID, string Script)
114 { 61 {
115 // Abort load/unload thread 62 //IScriptHost root = host.GetRoot();
116 try 63 Console.WriteLine("ScriptManager StartScript: localID: " + localID + ", itemID: " + itemID);
117 {
118 if (scriptLoadUnloadThread != null)
119 {
120 if (scriptLoadUnloadThread.IsAlive == true)
121 {
122 scriptLoadUnloadThread.Abort();
123 scriptLoadUnloadThread.Join();
124 }
125 }
126 }
127 catch
128 {
129 }
130 }
131 64
132 #endregion 65 // We will initialize and start the script.
66 // It will be up to the script itself to hook up the correct events.
67 string ScriptSource = "";
133 68
134 #region Load / Unload scripts (Thread loop) 69 SceneObjectPart m_host = World.GetSceneObjectPart(localID);
135 70
136 private void ScriptLoadUnloadThreadLoop()
137 {
138 try 71 try
139 { 72 {
140 while (true) 73 // Compile (We assume LSL)
141 { 74 ScriptSource = LSLCompiler.CompileFromLSLText(Script);
142 if (LUQueue.Count == 0)
143 Thread.Sleep(scriptLoadUnloadThread_IdleSleepms);
144 if (LUQueue.Count > 0)
145 {
146 LUStruct item = LUQueue.Dequeue();
147 if (item.Action == LUType.Unload)
148 _StopScript(item.localID, item.itemID);
149 if (item.Action == LUType.Load)
150 _StartScript(item.localID, item.itemID, item.script);
151 }
152 }
153 }
154 catch (ThreadAbortException tae)
155 {
156 string a = tae.ToString();
157 a = "";
158 // Expected
159 }
160 }
161
162 #endregion
163
164 #region Helper functions
165
166 private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
167 {
168 //Console.WriteLine("ScriptManager.CurrentDomain_AssemblyResolve: " + args.Name);
169 return Assembly.GetExecutingAssembly().FullName == args.Name ? Assembly.GetExecutingAssembly() : null;
170 }
171
172 #endregion
173
174 #region Internal functions to keep track of script
175
176 internal Dictionary<LLUUID, LSL_BaseClass>.KeyCollection GetScriptKeys(uint localID)
177 {
178 if (Scripts.ContainsKey(localID) == false)
179 return null;
180
181 Dictionary<LLUUID, LSL_BaseClass> Obj;
182 Scripts.TryGetValue(localID, out Obj);
183
184 return Obj.Keys;
185 }
186
187 internal LSL_BaseClass GetScript(uint localID, LLUUID itemID)
188 {
189 if (Scripts.ContainsKey(localID) == false)
190 return null;
191
192 Dictionary<LLUUID, LSL_BaseClass> Obj;
193 Scripts.TryGetValue(localID, out Obj);
194 if (Obj.ContainsKey(itemID) == false)
195 return null;
196
197 // Get script
198 LSL_BaseClass Script;
199 Obj.TryGetValue(itemID, out Script);
200
201 return Script;
202 }
203
204 internal void SetScript(uint localID, LLUUID itemID, LSL_BaseClass Script)
205 {
206 // Create object if it doesn't exist
207 if (Scripts.ContainsKey(localID) == false)
208 {
209 Scripts.Add(localID, new Dictionary<LLUUID, LSL_BaseClass>());
210 }
211
212 // Delete script if it exists
213 Dictionary<LLUUID, LSL_BaseClass> Obj;
214 Scripts.TryGetValue(localID, out Obj);
215 if (Obj.ContainsKey(itemID) == true)
216 Obj.Remove(itemID);
217
218 // Add to object
219 Obj.Add(itemID, Script);
220 }
221
222 internal void RemoveScript(uint localID, LLUUID itemID)
223 {
224 // Don't have that object?
225 if (Scripts.ContainsKey(localID) == false)
226 return;
227
228 // Delete script if it exists
229 Dictionary<LLUUID, LSL_BaseClass> Obj;
230 Scripts.TryGetValue(localID, out Obj);
231 if (Obj.ContainsKey(itemID) == true)
232 Obj.Remove(itemID);
233 }
234
235 #endregion
236
237 #region Start/Stop/Reset script
238
239 private Object startStopLock = new Object();
240
241 /// <summary>
242 /// Fetches, loads and hooks up a script to an objects events
243 /// </summary>
244 /// <param name="itemID"></param>
245 /// <param name="localID"></param>
246 public void StartScript(uint localID, LLUUID itemID, string Script)
247 {
248 LUStruct ls = new LUStruct();
249 ls.localID = localID;
250 ls.itemID = itemID;
251 ls.script = Script;
252 ls.Action = LUType.Load;
253 LUQueue.Enqueue(ls);
254 }
255
256 /// <summary>
257 /// Disables and unloads a script
258 /// </summary>
259 /// <param name="localID"></param>
260 /// <param name="itemID"></param>
261 public void StopScript(uint localID, LLUUID itemID)
262 {
263 LUStruct ls = new LUStruct();
264 ls.localID = localID;
265 ls.itemID = itemID;
266 ls.Action = LUType.Unload;
267 LUQueue.Enqueue(ls);
268 }
269
270 public void ResetScript(uint localID, LLUUID itemID)
271 {
272 string script = GetScript(localID, itemID).SourceCode;
273 StopScript(localID, itemID);
274 StartScript(localID, itemID, script);
275 }
276
277 // Create a new instance of the compiler (reuse)
278 private Compiler.LSL.Compiler LSLCompiler = new Compiler.LSL.Compiler();
279
280 private void _StartScript(uint localID, LLUUID itemID, string Script)
281 {
282 lock (startStopLock)
283 {
284 //IScriptHost root = host.GetRoot();
285 Console.WriteLine("ScriptManager StartScript: localID: " + localID + ", itemID: " + itemID);
286
287 // We will initialize and start the script.
288 // It will be up to the script itself to hook up the correct events.
289 string ScriptSource = "";
290
291 SceneObjectPart m_host = World.GetSceneObjectPart(localID);
292
293 try
294 {
295 if (!Script.EndsWith("dll"))
296 {
297 // Compile (We assume LSL)
298 ScriptSource = LSLCompiler.CompileFromLSLText(Script);
299 //Console.WriteLine("Compilation of " + FileName + " done");
300 // * Insert yield into code
301 ScriptSource = ProcessYield(ScriptSource);
302 }
303 else
304 {
305 ScriptSource = Script;
306 }
307 75
308#if DEBUG 76#if DEBUG
309 long before; 77 long before;
310 before = GC.GetTotalMemory(true); 78 before = GC.GetTotalMemory(true);
311#endif 79#endif
312 80
313 LSL_BaseClass CompiledScript; 81 IScript CompiledScript;
314 CompiledScript = m_scriptEngine.m_AppDomainManager.LoadScript(ScriptSource); 82 CompiledScript = m_scriptEngine.m_AppDomainManager.LoadScript(ScriptSource);
315 83
316#if DEBUG 84#if DEBUG
317 Console.WriteLine("Script " + itemID + " occupies {0} bytes", GC.GetTotalMemory(true) - before); 85 Console.WriteLine("Script " + itemID + " occupies {0} bytes", GC.GetTotalMemory(true) - before);
318#endif 86#endif
319 87
320 CompiledScript.SourceCode = ScriptSource; 88 CompiledScript.Source = ScriptSource;
321 // Add it to our script memstruct 89 // Add it to our script memstruct
322 SetScript(localID, itemID, CompiledScript); 90 SetScript(localID, itemID, CompiledScript);
323 91
324 // We need to give (untrusted) assembly a private instance of BuiltIns 92 // We need to give (untrusted) assembly a private instance of BuiltIns
325 // this private copy will contain Read-Only FullitemID so that it can bring that on to the server whenever needed. 93 // this private copy will contain Read-Only FullitemID so that it can bring that on to the server whenever needed.
326 94
327 95
328 LSL_BuiltIn_Commands LSLB = new LSL_BuiltIn_Commands(m_scriptEngine, m_host, localID, itemID); 96 LSL_BuiltIn_Commands LSLB = new LSL_BuiltIn_Commands(m_scriptEngine, m_host, localID, itemID);
329 97
330 // Start the script - giving it BuiltIns 98 // Start the script - giving it BuiltIns
331 CompiledScript.Start(LSLB); 99 CompiledScript.Start(LSLB);
332 100
333 // Fire the first start-event 101 // Fire the first start-event
334 m_scriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "state_entry", new object[] {}); 102 m_scriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "state_entry", new object[] { });
335 }
336 catch (Exception e)
337 {
338 //m_scriptEngine.Log.Error("ScriptEngine", "Error compiling script: " + e.ToString());
339 try
340 {
341 // DISPLAY ERROR INWORLD
342 string text = "Error compiling script:\r\n" + e.Message.ToString();
343 if (text.Length > 1500)
344 text = text.Substring(0, 1500);
345 World.SimChat(Helpers.StringToField(text), ChatTypeEnum.Say, 0, m_host.AbsolutePosition,
346 m_host.Name, m_host.UUID);
347 }
348 catch (Exception e2)
349 {
350 m_scriptEngine.Log.Error("ScriptEngine", "Error displaying error in-world: " + e2.ToString());
351 m_scriptEngine.Log.Error("ScriptEngine",
352 "Errormessage: Error compiling script:\r\n" + e.Message.ToString());
353 }
354 }
355 } 103 }
356 } 104 catch (Exception e)
357
358 private void _StopScript(uint localID, LLUUID itemID)
359 {
360 lock (startStopLock)
361 { 105 {
362 // Stop script 106 //m_scriptEngine.Log.Error("ScriptEngine", "Error compiling script: " + e.ToString());
363 Console.WriteLine("Stop script localID: " + localID + " LLUID: " + itemID.ToString());
364
365
366 // Stop long command on script
367 m_scriptEngine.m_LSLLongCmdHandler.RemoveScript(localID, itemID);
368
369 LSL_BaseClass LSLBC = GetScript(localID, itemID);
370 if (LSLBC == null)
371 return;
372
373 // TEMP: First serialize it
374 //GetSerializedScript(localID, itemID);
375
376
377 try 107 try
378 { 108 {
379 // Get AppDomain 109 // DISPLAY ERROR INWORLD
380 AppDomain ad = LSLBC.Exec.GetAppDomain(); 110 string text = "Error compiling script:\r\n" + e.Message.ToString();
381 // Tell script not to accept new requests 111 if (text.Length > 1500)
382 GetScript(localID, itemID).Exec.StopScript(); 112 text = text.Substring(0, 1500);
383 // Remove from internal structure 113 World.SimChat(Helpers.StringToField(text), ChatTypeEnum.Say, 0, m_host.AbsolutePosition,
384 RemoveScript(localID, itemID); 114 m_host.Name, m_host.UUID);
385 // Tell AppDomain that we have stopped script
386 m_scriptEngine.m_AppDomainManager.StopScript(ad);
387 } 115 }
388 catch (Exception e) 116 catch (Exception e2)
389 { 117 {
390 Console.WriteLine("Exception stopping script localID: " + localID + " LLUID: " + itemID.ToString() + 118 m_scriptEngine.Log.Error("ScriptEngine", "Error displaying error in-world: " + e2.ToString());
391 ": " + e.ToString()); 119 m_scriptEngine.Log.Error("ScriptEngine",
120 "Errormessage: Error compiling script:\r\n" + e.Message.ToString());
392 } 121 }
393 } 122 }
394 } 123 }
395 124
396 private string ProcessYield(string FileName) 125 public override void _StopScript(uint localID, LLUUID itemID)
397 { 126 {
398 // TODO: Create a new assembly and copy old but insert Yield Code 127 // Stop script
399 //return TempDotNetMicroThreadingCodeInjector.TestFix(FileName); 128 Console.WriteLine("Stop script localID: " + localID + " LLUID: " + itemID.ToString());
400 return FileName;
401 }
402 129
403 #endregion
404 130
405 #region Perform event execution in script 131 // Stop long command on script
132 m_scriptEngine.m_LSLLongCmdHandler.RemoveScript(localID, itemID);
406 133
407 /// <summary> 134 IScript LSLBC = GetScript(localID, itemID);
408 /// Execute a LL-event-function in Script 135 if (LSLBC == null)
409 /// </summary>
410 /// <param name="localID">Object the script is located in</param>
411 /// <param name="itemID">Script ID</param>
412 /// <param name="FunctionName">Name of function</param>
413 /// <param name="args">Arguments to pass to function</param>
414 internal void ExecuteEvent(uint localID, LLUUID itemID, string FunctionName, object[] args)
415 {
416#if DEBUG
417 Console.WriteLine("ScriptEngine: Inside ExecuteEvent for event " + FunctionName);
418#endif
419 // Execute a function in the script
420 //m_scriptEngine.Log.Verbose("ScriptEngine", "Executing Function localID: " + localID + ", itemID: " + itemID + ", FunctionName: " + FunctionName);
421 LSL_BaseClass Script = m_scriptEngine.m_ScriptManager.GetScript(localID, itemID);
422 if (Script == null)
423 return; 136 return;
424 137
425#if DEBUG 138 // TEMP: First serialize it
426 Console.WriteLine("ScriptEngine: Executing event: " + FunctionName); 139 //GetSerializedScript(localID, itemID);
427#endif
428 // Must be done in correct AppDomain, so leaving it up to the script itself
429 Script.Exec.ExecuteEvent(FunctionName, args);
430 }
431 140
432 #endregion
433 141
434 #region Script serialization/deserialization 142 try
435 143 {
436 public void GetSerializedScript(uint localID, LLUUID itemID) 144 // Get AppDomain
437 { 145 AppDomain ad = LSLBC.Exec.GetAppDomain();
438 // Serialize the script and return it 146 // Tell script not to accept new requests
439 // Should not be a problem 147 GetScript(localID, itemID).Exec.StopScript();
440 FileStream fs = File.Create("SERIALIZED_SCRIPT_" + itemID); 148 // Remove from internal structure
441 BinaryFormatter b = new BinaryFormatter(); 149 RemoveScript(localID, itemID);
442 b.Serialize(fs, GetScript(localID, itemID)); 150 // Tell AppDomain that we have stopped script
443 fs.Close(); 151 m_scriptEngine.m_AppDomainManager.StopScript(ad);
444 } 152 }
445 153 catch (Exception e)
446 public void PutSerializedScript(uint localID, LLUUID itemID) 154 {
447 { 155 Console.WriteLine("Exception stopping script localID: " + localID + " LLUID: " + itemID.ToString() +
448 // Deserialize the script and inject it into an AppDomain 156 ": " + e.ToString());
449 157 }
450 // How to inject into an AppDomain?
451 } 158 }
452 159
453 #endregion
454 } 160 }
455} \ No newline at end of file 161} \ No newline at end of file