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