diff options
Diffstat (limited to 'OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/ScriptManager.cs')
-rw-r--r-- | OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/ScriptManager.cs | 347 |
1 files changed, 347 insertions, 0 deletions
diff --git a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/ScriptManager.cs b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/ScriptManager.cs new file mode 100644 index 0000000..55c9545 --- /dev/null +++ b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/ScriptManager.cs | |||
@@ -0,0 +1,347 @@ | |||
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.IO; | ||
32 | using System.Reflection; | ||
33 | using System.Runtime.Serialization.Formatters.Binary; | ||
34 | using System.Threading; | ||
35 | using libsecondlife; | ||
36 | using OpenSim.Framework; | ||
37 | using OpenSim.Region.Environment.Scenes; | ||
38 | using OpenSim.Region.ScriptEngine.Common; | ||
39 | |||
40 | namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase | ||
41 | { | ||
42 | /// <summary> | ||
43 | /// Loads scripts | ||
44 | /// Compiles them if necessary | ||
45 | /// Execute functions for EventQueueManager (Sends them to script on other AppDomain for execution) | ||
46 | /// </summary> | ||
47 | /// | ||
48 | |||
49 | // 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. | ||
50 | // * Keeps track of running scripts | ||
51 | // * Compiles script if necessary (through "Compiler") | ||
52 | // * Loads script (through "AppDomainManager" called from for example "EventQueueManager") | ||
53 | // * Executes functions inside script (called from for example "EventQueueManager" class) | ||
54 | // * Unloads script (through "AppDomainManager" called from for example "EventQueueManager") | ||
55 | // * Dedicated load/unload thread, and queues loading/unloading. | ||
56 | // This so that scripts starting or stopping will not slow down other theads or whole system. | ||
57 | // | ||
58 | [Serializable] | ||
59 | public abstract class ScriptManager | ||
60 | { | ||
61 | #region Declares | ||
62 | |||
63 | private Thread scriptLoadUnloadThread; | ||
64 | private int scriptLoadUnloadThread_IdleSleepms = 100; | ||
65 | private Queue<LUStruct> LUQueue = new Queue<LUStruct>(); | ||
66 | |||
67 | |||
68 | // Load/Unload structure | ||
69 | private struct LUStruct | ||
70 | { | ||
71 | public uint localID; | ||
72 | public LLUUID itemID; | ||
73 | public string script; | ||
74 | public LUType Action; | ||
75 | } | ||
76 | |||
77 | private enum LUType | ||
78 | { | ||
79 | Unknown = 0, | ||
80 | Load = 1, | ||
81 | Unload = 2 | ||
82 | } | ||
83 | |||
84 | // Object<string, Script<string, script>> | ||
85 | // IMPORTANT: Types and MemberInfo-derived objects require a LOT of memory. | ||
86 | // Instead use RuntimeTypeHandle, RuntimeFieldHandle and RunTimeHandle (IntPtr) instead! | ||
87 | public Dictionary<uint, Dictionary<LLUUID, IScript>> Scripts = | ||
88 | new Dictionary<uint, Dictionary<LLUUID, IScript>>(); | ||
89 | |||
90 | public Scene World | ||
91 | { | ||
92 | get { return m_scriptEngine.World; } | ||
93 | } | ||
94 | |||
95 | #endregion | ||
96 | |||
97 | #region Object init/shutdown | ||
98 | |||
99 | public ScriptEngineBase.ScriptEngine m_scriptEngine; | ||
100 | |||
101 | public ScriptManager(ScriptEngineBase.ScriptEngine scriptEngine) | ||
102 | { | ||
103 | m_scriptEngine = scriptEngine; | ||
104 | AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve); | ||
105 | scriptLoadUnloadThread = new Thread(ScriptLoadUnloadThreadLoop); | ||
106 | scriptLoadUnloadThread.Name = "ScriptLoadUnloadThread"; | ||
107 | scriptLoadUnloadThread.IsBackground = true; | ||
108 | scriptLoadUnloadThread.Priority = ThreadPriority.BelowNormal; | ||
109 | scriptLoadUnloadThread.Start(); | ||
110 | } | ||
111 | |||
112 | ~ScriptManager() | ||
113 | { | ||
114 | // Abort load/unload thread | ||
115 | try | ||
116 | { | ||
117 | if (scriptLoadUnloadThread != null) | ||
118 | { | ||
119 | if (scriptLoadUnloadThread.IsAlive == true) | ||
120 | { | ||
121 | scriptLoadUnloadThread.Abort(); | ||
122 | scriptLoadUnloadThread.Join(); | ||
123 | } | ||
124 | } | ||
125 | } | ||
126 | catch | ||
127 | { | ||
128 | } | ||
129 | } | ||
130 | |||
131 | #endregion | ||
132 | |||
133 | #region Load / Unload scripts (Thread loop) | ||
134 | |||
135 | private void ScriptLoadUnloadThreadLoop() | ||
136 | { | ||
137 | try | ||
138 | { | ||
139 | while (true) | ||
140 | { | ||
141 | if (LUQueue.Count == 0) | ||
142 | Thread.Sleep(scriptLoadUnloadThread_IdleSleepms); | ||
143 | if (LUQueue.Count > 0) | ||
144 | { | ||
145 | LUStruct item = LUQueue.Dequeue(); | ||
146 | lock (startStopLock) // Lock so we have only 1 thread working on loading/unloading of scripts | ||
147 | { | ||
148 | if (item.Action == LUType.Unload) | ||
149 | { | ||
150 | _StopScript(item.localID, item.itemID); | ||
151 | } | ||
152 | if (item.Action == LUType.Load) | ||
153 | { | ||
154 | _StartScript(item.localID, item.itemID, item.script); | ||
155 | } | ||
156 | } | ||
157 | } | ||
158 | } | ||
159 | } | ||
160 | catch (ThreadAbortException tae) | ||
161 | { | ||
162 | string a = tae.ToString(); | ||
163 | a = ""; | ||
164 | // Expected | ||
165 | } | ||
166 | } | ||
167 | |||
168 | #endregion | ||
169 | |||
170 | #region Helper functions | ||
171 | |||
172 | private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) | ||
173 | { | ||
174 | //Console.WriteLine("ScriptManager.CurrentDomain_AssemblyResolve: " + args.Name); | ||
175 | return Assembly.GetExecutingAssembly().FullName == args.Name ? Assembly.GetExecutingAssembly() : null; | ||
176 | } | ||
177 | |||
178 | #endregion | ||
179 | |||
180 | |||
181 | |||
182 | #region Start/Stop/Reset script | ||
183 | |||
184 | private readonly Object startStopLock = new Object(); | ||
185 | |||
186 | /// <summary> | ||
187 | /// Fetches, loads and hooks up a script to an objects events | ||
188 | /// </summary> | ||
189 | /// <param name="itemID"></param> | ||
190 | /// <param name="localID"></param> | ||
191 | public void StartScript(uint localID, LLUUID itemID, string Script) | ||
192 | { | ||
193 | LUStruct ls = new LUStruct(); | ||
194 | ls.localID = localID; | ||
195 | ls.itemID = itemID; | ||
196 | ls.script = Script; | ||
197 | ls.Action = LUType.Load; | ||
198 | LUQueue.Enqueue(ls); | ||
199 | } | ||
200 | |||
201 | /// <summary> | ||
202 | /// Disables and unloads a script | ||
203 | /// </summary> | ||
204 | /// <param name="localID"></param> | ||
205 | /// <param name="itemID"></param> | ||
206 | public void StopScript(uint localID, LLUUID itemID) | ||
207 | { | ||
208 | LUStruct ls = new LUStruct(); | ||
209 | ls.localID = localID; | ||
210 | ls.itemID = itemID; | ||
211 | ls.Action = LUType.Unload; | ||
212 | LUQueue.Enqueue(ls); | ||
213 | } | ||
214 | |||
215 | // Create a new instance of the compiler (reuse) | ||
216 | //private Compiler.LSL.Compiler LSLCompiler = new Compiler.LSL.Compiler(); | ||
217 | |||
218 | public abstract void _StartScript(uint localID, LLUUID itemID, string Script); | ||
219 | |||
220 | public abstract void _StopScript(uint localID, LLUUID itemID); | ||
221 | |||
222 | |||
223 | #endregion | ||
224 | |||
225 | #region Perform event execution in script | ||
226 | |||
227 | /// <summary> | ||
228 | /// Execute a LL-event-function in Script | ||
229 | /// </summary> | ||
230 | /// <param name="localID">Object the script is located in</param> | ||
231 | /// <param name="itemID">Script ID</param> | ||
232 | /// <param name="FunctionName">Name of function</param> | ||
233 | /// <param name="args">Arguments to pass to function</param> | ||
234 | internal void ExecuteEvent(uint localID, LLUUID itemID, string FunctionName, object[] args) | ||
235 | { | ||
236 | #if DEBUG | ||
237 | Console.WriteLine("ScriptEngine: Inside ExecuteEvent for event " + FunctionName); | ||
238 | #endif | ||
239 | // Execute a function in the script | ||
240 | //m_scriptEngine.Log.Verbose("ScriptEngine", "Executing Function localID: " + localID + ", itemID: " + itemID + ", FunctionName: " + FunctionName); | ||
241 | //ScriptBaseInterface Script = (ScriptBaseInterface)GetScript(localID, itemID); | ||
242 | IScript Script = GetScript(localID, itemID); | ||
243 | if (Script == null) | ||
244 | return; | ||
245 | #if DEBUG | ||
246 | Console.WriteLine("ScriptEngine: Executing event: " + FunctionName); | ||
247 | #endif | ||
248 | // Must be done in correct AppDomain, so leaving it up to the script itself | ||
249 | Script.Exec.ExecuteEvent(FunctionName, args); | ||
250 | } | ||
251 | |||
252 | #endregion | ||
253 | |||
254 | #region Internal functions to keep track of script | ||
255 | |||
256 | public Dictionary<LLUUID, IScript>.KeyCollection GetScriptKeys(uint localID) | ||
257 | { | ||
258 | if (Scripts.ContainsKey(localID) == false) | ||
259 | return null; | ||
260 | |||
261 | Dictionary<LLUUID, IScript> Obj; | ||
262 | Scripts.TryGetValue(localID, out Obj); | ||
263 | |||
264 | return Obj.Keys; | ||
265 | } | ||
266 | |||
267 | public IScript GetScript(uint localID, LLUUID itemID) | ||
268 | { | ||
269 | if (Scripts.ContainsKey(localID) == false) | ||
270 | return null; | ||
271 | |||
272 | Dictionary<LLUUID, IScript> Obj; | ||
273 | Scripts.TryGetValue(localID, out Obj); | ||
274 | if (Obj.ContainsKey(itemID) == false) | ||
275 | return null; | ||
276 | |||
277 | // Get script | ||
278 | IScript Script; | ||
279 | Obj.TryGetValue(itemID, out Script); | ||
280 | |||
281 | return Script; | ||
282 | } | ||
283 | |||
284 | public void SetScript(uint localID, LLUUID itemID, IScript Script) | ||
285 | { | ||
286 | // Create object if it doesn't exist | ||
287 | if (Scripts.ContainsKey(localID) == false) | ||
288 | { | ||
289 | Scripts.Add(localID, new Dictionary<LLUUID, IScript>()); | ||
290 | } | ||
291 | |||
292 | // Delete script if it exists | ||
293 | Dictionary<LLUUID, IScript> Obj; | ||
294 | Scripts.TryGetValue(localID, out Obj); | ||
295 | if (Obj.ContainsKey(itemID) == true) | ||
296 | Obj.Remove(itemID); | ||
297 | |||
298 | // Add to object | ||
299 | Obj.Add(itemID, Script); | ||
300 | } | ||
301 | |||
302 | public void RemoveScript(uint localID, LLUUID itemID) | ||
303 | { | ||
304 | // Don't have that object? | ||
305 | if (Scripts.ContainsKey(localID) == false) | ||
306 | return; | ||
307 | |||
308 | // Delete script if it exists | ||
309 | Dictionary<LLUUID, IScript> Obj; | ||
310 | Scripts.TryGetValue(localID, out Obj); | ||
311 | if (Obj.ContainsKey(itemID) == true) | ||
312 | Obj.Remove(itemID); | ||
313 | } | ||
314 | |||
315 | #endregion | ||
316 | |||
317 | |||
318 | public void ResetScript(uint localID, LLUUID itemID) | ||
319 | { | ||
320 | string script = GetScript(localID, itemID).Source; | ||
321 | StopScript(localID, itemID); | ||
322 | StartScript(localID, itemID, script); | ||
323 | } | ||
324 | |||
325 | |||
326 | #region Script serialization/deserialization | ||
327 | |||
328 | public void GetSerializedScript(uint localID, LLUUID itemID) | ||
329 | { | ||
330 | // Serialize the script and return it | ||
331 | // Should not be a problem | ||
332 | FileStream fs = File.Create("SERIALIZED_SCRIPT_" + itemID); | ||
333 | BinaryFormatter b = new BinaryFormatter(); | ||
334 | b.Serialize(fs, GetScript(localID, itemID)); | ||
335 | fs.Close(); | ||
336 | } | ||
337 | |||
338 | public void PutSerializedScript(uint localID, LLUUID itemID) | ||
339 | { | ||
340 | // Deserialize the script and inject it into an AppDomain | ||
341 | |||
342 | // How to inject into an AppDomain? | ||
343 | } | ||
344 | |||
345 | #endregion | ||
346 | } | ||
347 | } \ No newline at end of file | ||