diff options
Diffstat (limited to 'OpenSim/Region/ScriptEngine/DotNetEngine/ScriptManager.cs')
-rw-r--r-- | OpenSim/Region/ScriptEngine/DotNetEngine/ScriptManager.cs | 438 |
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; | |||
35 | using libsecondlife; | 35 | using libsecondlife; |
36 | using OpenSim.Framework; | 36 | using OpenSim.Framework; |
37 | using OpenSim.Region.Environment.Scenes; | 37 | using OpenSim.Region.Environment.Scenes; |
38 | using OpenSim.Region.ScriptEngine.DotNetEngine.Compiler; | 38 | using OpenSim.Region.ScriptEngine.Common; |
39 | using OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.LSL; | 39 | using OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.LSL; |
40 | 40 | ||
41 | namespace OpenSim.Region.ScriptEngine.DotNetEngine | 41 | namespace 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 |