aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Grid/ScriptEngine/DotNetEngine/ScriptManager.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Grid/ScriptEngine/DotNetEngine/ScriptManager.cs')
-rw-r--r--OpenSim/Grid/ScriptEngine/DotNetEngine/ScriptManager.cs417
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 */
29using System;
30using System.Collections.Generic;
31using System.Text;
32using System.Threading;
33using System.Reflection;
34using System.Runtime.Remoting;
35using System.Runtime.Serialization;
36using System.Runtime.Serialization.Formatters.Binary;
37using OpenSim.Region.Environment.Scenes;
38using OpenSim.Region.Environment.Scenes.Scripting;
39using OpenSim.Grid.ScriptEngine.DotNetEngine.Compiler;
40using OpenSim.Grid.ScriptEngine.DotNetEngine.Compiler.LSL;
41using OpenSim.Region.ScriptEngine.Common;
42using libsecondlife;
43
44
45namespace 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}