diff options
Diffstat (limited to 'OpenSim/Region/ScriptEngine/XMREngine/XMRInstCtor.cs')
-rw-r--r-- | OpenSim/Region/ScriptEngine/XMREngine/XMRInstCtor.cs | 777 |
1 files changed, 777 insertions, 0 deletions
diff --git a/OpenSim/Region/ScriptEngine/XMREngine/XMRInstCtor.cs b/OpenSim/Region/ScriptEngine/XMREngine/XMRInstCtor.cs new file mode 100644 index 0000000..7ae8c47 --- /dev/null +++ b/OpenSim/Region/ScriptEngine/XMREngine/XMRInstCtor.cs | |||
@@ -0,0 +1,777 @@ | |||
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 OpenSimulator 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 | using System; | ||
29 | using System.Threading; | ||
30 | using System.Reflection; | ||
31 | using System.Collections; | ||
32 | using System.Collections.Generic; | ||
33 | using System.Runtime.Remoting.Lifetime; | ||
34 | using System.Security.Policy; | ||
35 | using System.IO; | ||
36 | using System.Xml; | ||
37 | using System.Text; | ||
38 | using OpenMetaverse; | ||
39 | using OpenSim.Framework; | ||
40 | using OpenSim.Region.ScriptEngine.Interfaces; | ||
41 | using OpenSim.Region.ScriptEngine.Shared; | ||
42 | using OpenSim.Region.ScriptEngine.Shared.Api; | ||
43 | using OpenSim.Region.ScriptEngine.Shared.ScriptBase; | ||
44 | using OpenSim.Region.ScriptEngine.XMREngine; | ||
45 | using OpenSim.Region.Framework.Scenes; | ||
46 | using log4net; | ||
47 | |||
48 | using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat; | ||
49 | using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger; | ||
50 | using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString; | ||
51 | using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list; | ||
52 | using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion; | ||
53 | using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString; | ||
54 | using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3; | ||
55 | |||
56 | namespace OpenSim.Region.ScriptEngine.XMREngine | ||
57 | { | ||
58 | public partial class XMRInstance | ||
59 | { | ||
60 | /****************************************************************************\ | ||
61 | * The only method of interest to outside this module is the Initializer. * | ||
62 | * * | ||
63 | * The rest of this module contains support routines for the Initializer. * | ||
64 | \****************************************************************************/ | ||
65 | |||
66 | /** | ||
67 | * @brief Initializer, loads script in memory and all ready for running. | ||
68 | * @param engine = XMREngine instance this is part of | ||
69 | * @param scriptBasePath = directory name where files are | ||
70 | * @param stackSize = number of bytes to allocate for stacks | ||
71 | * @param errors = return compiler errors in this array | ||
72 | * @param forceRecomp = force recompile | ||
73 | * Throws exception if any error, so it was successful if it returns. | ||
74 | */ | ||
75 | public void Initialize(XMREngine engine, string scriptBasePath, | ||
76 | int stackSize, int heapSize, ArrayList errors) | ||
77 | { | ||
78 | if (stackSize < 16384) stackSize = 16384; | ||
79 | if (heapSize < 16384) heapSize = 16384; | ||
80 | |||
81 | // Save all call parameters in instance vars for easy access. | ||
82 | m_Engine = engine; | ||
83 | m_ScriptBasePath = scriptBasePath; | ||
84 | m_StackSize = stackSize; | ||
85 | m_HeapSize = heapSize; | ||
86 | m_CompilerErrors = errors; | ||
87 | m_StateFileName = GetStateFileName(scriptBasePath, m_ItemID); | ||
88 | |||
89 | // Not in any XMRInstQueue. | ||
90 | m_NextInst = this; | ||
91 | m_PrevInst = this; | ||
92 | |||
93 | // Set up list of API calls it has available. | ||
94 | // This also gets the API modules ready to accept setup data, such as | ||
95 | // active listeners being restored. | ||
96 | IScriptApi scriptApi; | ||
97 | ApiManager am = new ApiManager(); | ||
98 | foreach (string api in am.GetApis()) | ||
99 | { | ||
100 | // Instantiate the API for this script instance. | ||
101 | if (api != "LSL") { | ||
102 | scriptApi = am.CreateApi(api); | ||
103 | } else { | ||
104 | scriptApi = m_XMRLSLApi = new XMRLSL_Api(); | ||
105 | } | ||
106 | |||
107 | // Connect it up to the instance. | ||
108 | InitScriptApi (engine, api, scriptApi); | ||
109 | } | ||
110 | |||
111 | m_XMRLSLApi.InitXMRLSLApi(this); | ||
112 | |||
113 | // Get object loaded, compiling script and reading .state file as | ||
114 | // necessary to restore the state. | ||
115 | suspendOnCheckRunHold = true; | ||
116 | InstantiateScript(); | ||
117 | m_SourceCode = null; | ||
118 | if (m_ObjCode == null) throw new ArgumentNullException ("m_ObjCode"); | ||
119 | if (m_ObjCode.scriptEventHandlerTable == null) | ||
120 | throw new ArgumentNullException ("m_ObjCode.scriptEventHandlerTable"); | ||
121 | |||
122 | suspendOnCheckRunHold = false; | ||
123 | suspendOnCheckRunTemp = false; | ||
124 | |||
125 | // Declare which events the script's current state can handle. | ||
126 | int eventMask = GetStateEventFlags(stateCode); | ||
127 | m_Part.SetScriptEvents(m_ItemID, eventMask); | ||
128 | } | ||
129 | |||
130 | private void InitScriptApi (XMREngine engine, string api, IScriptApi scriptApi) | ||
131 | { | ||
132 | // Set up m_ApiManager_<APINAME> = instance pointer. | ||
133 | engine.m_XMRInstanceApiCtxFieldInfos[api].SetValue (this, scriptApi); | ||
134 | |||
135 | // Initialize the API instance. | ||
136 | scriptApi.Initialize(m_Engine, m_Part, m_Item); | ||
137 | this.InitApi (api, scriptApi); | ||
138 | } | ||
139 | |||
140 | /* | ||
141 | * Get script object code loaded in memory and all ready to run, | ||
142 | * ready to resume it from where the .state file says it was last | ||
143 | */ | ||
144 | private void InstantiateScript() | ||
145 | { | ||
146 | bool compiledIt = false; | ||
147 | ScriptObjCode objCode; | ||
148 | |||
149 | // If source code string is empty, use the asset ID as the object file name. | ||
150 | // Allow lines of // comments at the beginning (for such as engine selection). | ||
151 | int i, j, len; | ||
152 | if (m_SourceCode == null) m_SourceCode = String.Empty; | ||
153 | for (len = m_SourceCode.Length; len > 0; --len) | ||
154 | { | ||
155 | if (m_SourceCode[len-1] > ' ') | ||
156 | break; | ||
157 | } | ||
158 | for (i = 0; i < len; i ++) | ||
159 | { | ||
160 | char c = m_SourceCode[i]; | ||
161 | if (c <= ' ') | ||
162 | continue; | ||
163 | if (c != '/') | ||
164 | break; | ||
165 | if ((i + 1 >= len) || (m_SourceCode[i+1] != '/')) | ||
166 | break; | ||
167 | i = m_SourceCode.IndexOf ('\n', i); | ||
168 | if (i < 0) | ||
169 | i = len - 1; | ||
170 | } | ||
171 | if ((i >= len) || !m_Engine.m_UseSourceHashCode) | ||
172 | { | ||
173 | // Source consists of nothing but // comments and whitespace, | ||
174 | // or we are being forced to use the asset-id as the key, to | ||
175 | // open an already existing object code file. | ||
176 | m_ScriptObjCodeKey = m_Item.AssetID.ToString (); | ||
177 | if (i >= len) | ||
178 | m_SourceCode = ""; | ||
179 | } | ||
180 | else | ||
181 | { | ||
182 | // Make up dictionary key for the object code. | ||
183 | // Use the same object code for identical source code | ||
184 | // regardless of asset ID, so we don't care if they | ||
185 | // copy scripts or not. | ||
186 | byte[] scbytes = System.Text.Encoding.UTF8.GetBytes (m_SourceCode); | ||
187 | StringBuilder sb = new StringBuilder ((256 + 5) / 6); | ||
188 | ByteArrayToSixbitStr (sb, System.Security.Cryptography.SHA256.Create ().ComputeHash (scbytes)); | ||
189 | m_ScriptObjCodeKey = sb.ToString (); | ||
190 | |||
191 | // But source code can be just a sixbit string itself | ||
192 | // that identifies an already existing object code file. | ||
193 | if (len - i == m_ScriptObjCodeKey.Length) | ||
194 | { | ||
195 | for (j = len; -- j >= i;) | ||
196 | { | ||
197 | if (sixbit.IndexOf (m_SourceCode[j]) < 0) | ||
198 | break; | ||
199 | } | ||
200 | if (j < i) | ||
201 | { | ||
202 | m_ScriptObjCodeKey = m_SourceCode.Substring (i, len - i); | ||
203 | m_SourceCode = ""; | ||
204 | } | ||
205 | } | ||
206 | } | ||
207 | |||
208 | // There may already be an ScriptObjCode struct in memory that | ||
209 | // we can use. If not, try to compile it. | ||
210 | lock (m_CompileLock) | ||
211 | { | ||
212 | if (!m_CompiledScriptObjCode.TryGetValue (m_ScriptObjCodeKey, out objCode) || m_ForceRecomp) | ||
213 | { | ||
214 | objCode = TryToCompile (); | ||
215 | compiledIt = true; | ||
216 | } | ||
217 | |||
218 | // Loaded successfully, increment reference count. | ||
219 | |||
220 | // If we just compiled it though, reset count to 0 first as | ||
221 | // this is the one-and-only existance of this objCode struct, | ||
222 | // and we want any old ones for this source code to be garbage | ||
223 | // collected. | ||
224 | |||
225 | if (compiledIt) | ||
226 | { | ||
227 | m_CompiledScriptObjCode[m_ScriptObjCodeKey] = objCode; | ||
228 | objCode.refCount = 0; | ||
229 | } | ||
230 | objCode.refCount ++; | ||
231 | |||
232 | // Now set up to decrement ref count on dispose. | ||
233 | m_ObjCode = objCode; | ||
234 | } | ||
235 | |||
236 | try | ||
237 | { | ||
238 | // Fill in script instance from object code | ||
239 | // Script instance is put in a "never-ever-has-run-before" state. | ||
240 | LoadObjCode(); | ||
241 | |||
242 | // Fill in script intial state | ||
243 | // - either as loaded from a .state file | ||
244 | // - or initial default state_entry() event | ||
245 | LoadInitialState(); | ||
246 | } | ||
247 | catch | ||
248 | { | ||
249 | // If any error loading, decrement object code reference count. | ||
250 | DecObjCodeRefCount (); | ||
251 | throw; | ||
252 | } | ||
253 | } | ||
254 | |||
255 | private const string sixbit = "0123456789_abcdefghijklmnopqrstuvwxyz@ABCDEFGHIJKLMNOPQRSTUVWXYZ"; | ||
256 | private static void ByteArrayToSixbitStr (StringBuilder sb, byte[] bytes) | ||
257 | { | ||
258 | int bit = 0; | ||
259 | int val = 0; | ||
260 | foreach (byte b in bytes) | ||
261 | { | ||
262 | val |= (int)((uint)b << bit); | ||
263 | bit += 8; | ||
264 | while (bit >= 6) | ||
265 | { | ||
266 | sb.Append (sixbit[val&63]); | ||
267 | val >>= 6; | ||
268 | bit -= 6; | ||
269 | } | ||
270 | } | ||
271 | if (bit > 0) | ||
272 | sb.Append (sixbit[val&63]); | ||
273 | } | ||
274 | |||
275 | /* | ||
276 | * Try to create object code from source code | ||
277 | * If error, just throw exception | ||
278 | */ | ||
279 | private ScriptObjCode TryToCompile () | ||
280 | { | ||
281 | m_CompilerErrors.Clear(); | ||
282 | |||
283 | // If object file exists, create ScriptObjCode directly from that. | ||
284 | // Otherwise, compile the source to create object file then create | ||
285 | // ScriptObjCode from that. | ||
286 | |||
287 | string assetID = m_Item.AssetID.ToString(); | ||
288 | m_CameFrom = "asset://" + assetID; | ||
289 | ScriptObjCode objCode = Compile (); | ||
290 | if (m_CompilerErrors.Count != 0) | ||
291 | throw new Exception ("compilation errors"); | ||
292 | |||
293 | if (objCode == null) | ||
294 | throw new Exception ("compilation failed"); | ||
295 | |||
296 | return objCode; | ||
297 | } | ||
298 | |||
299 | /* | ||
300 | * Retrieve source from asset server. | ||
301 | */ | ||
302 | private string FetchSource (string cameFrom) | ||
303 | { | ||
304 | m_log.Debug ("[XMREngine]: fetching source " + cameFrom); | ||
305 | if (!cameFrom.StartsWith ("asset://")) | ||
306 | throw new Exception ("unable to retrieve source from " + cameFrom); | ||
307 | |||
308 | string assetID = cameFrom.Substring (8); | ||
309 | AssetBase asset = m_Engine.World.AssetService.Get(assetID); | ||
310 | if (asset == null) | ||
311 | throw new Exception ("source not found " + cameFrom); | ||
312 | |||
313 | string source = Encoding.UTF8.GetString (asset.Data); | ||
314 | if (EmptySource (source)) | ||
315 | throw new Exception ("fetched source empty " + cameFrom); | ||
316 | |||
317 | return source; | ||
318 | } | ||
319 | |||
320 | /* | ||
321 | * Fill in script object initial contents. | ||
322 | * Set the initial state to "default". | ||
323 | */ | ||
324 | private void LoadObjCode () | ||
325 | { | ||
326 | // Script must leave this much stack remaining on calls to CheckRun(). | ||
327 | |||
328 | this.stackLimit = m_StackSize / 2; | ||
329 | |||
330 | // This is how many total heap bytes script is allowed to use. | ||
331 | this.heapLimit = m_HeapSize; | ||
332 | |||
333 | // Allocate global variable arrays. | ||
334 | this.glblVars.AllocVarArrays (m_ObjCode.glblSizes); | ||
335 | |||
336 | // Script can handle these event codes. | ||
337 | m_HaveEventHandlers = new bool[m_ObjCode.scriptEventHandlerTable.GetLength(1)]; | ||
338 | for (int i = m_ObjCode.scriptEventHandlerTable.GetLength(0); -- i >= 0;) | ||
339 | { | ||
340 | for (int j = m_ObjCode.scriptEventHandlerTable.GetLength(1); -- j >= 0;) | ||
341 | { | ||
342 | if (m_ObjCode.scriptEventHandlerTable[i,j] != null) | ||
343 | { | ||
344 | m_HaveEventHandlers[j] = true; | ||
345 | } | ||
346 | } | ||
347 | } | ||
348 | |||
349 | // Set up microthread object which actually calls the script event handler functions. | ||
350 | this.microthread = (IScriptUThread)m_Engine.uThreadCtor.Invoke (new object[] { this }); | ||
351 | } | ||
352 | |||
353 | /* | ||
354 | * LoadInitialState() | ||
355 | * if no state XML file exists for the asset, | ||
356 | * post initial default state events | ||
357 | * else | ||
358 | * try to restore from .state file | ||
359 | * If any error, throw exception | ||
360 | */ | ||
361 | private void LoadInitialState() | ||
362 | { | ||
363 | // If no .state file exists, start from default state | ||
364 | // Otherwise, read initial state from the .state file | ||
365 | if (!File.Exists(m_StateFileName)) | ||
366 | { | ||
367 | m_Running = true; // event processing is enabled | ||
368 | eventCode = ScriptEventCode.None; // not processing any event | ||
369 | |||
370 | // default state_entry() must initialize global variables | ||
371 | doGblInit = true; | ||
372 | stateCode = 0; | ||
373 | |||
374 | PostEvent(new EventParams("state_entry", | ||
375 | zeroObjectArray, | ||
376 | zeroDetectParams)); | ||
377 | } | ||
378 | else | ||
379 | { | ||
380 | FileStream fs = File.Open(m_StateFileName, | ||
381 | FileMode.Open, | ||
382 | FileAccess.Read); | ||
383 | StreamReader ss = new StreamReader(fs); | ||
384 | string xml = ss.ReadToEnd(); | ||
385 | ss.Close(); | ||
386 | fs.Close(); | ||
387 | |||
388 | XmlDocument doc = new XmlDocument(); | ||
389 | doc.LoadXml(xml); | ||
390 | LoadScriptState(doc); | ||
391 | } | ||
392 | |||
393 | // Post event(s) saying what caused the script to start. | ||
394 | if (m_PostOnRez) | ||
395 | { | ||
396 | PostEvent(new EventParams("on_rez", | ||
397 | new Object[] { m_StartParam }, | ||
398 | zeroDetectParams)); | ||
399 | } | ||
400 | |||
401 | switch (m_StateSource) | ||
402 | { | ||
403 | case StateSource.AttachedRez: | ||
404 | // PostEvent(new EventParams("attach", | ||
405 | // new object[] { m_Part.ParentGroup.AttachedAvatar.ToString() }, | ||
406 | // zeroDetectParams)); | ||
407 | break; | ||
408 | |||
409 | case StateSource.PrimCrossing: | ||
410 | PostEvent(new EventParams("changed", | ||
411 | sbcCR, | ||
412 | zeroDetectParams)); | ||
413 | break; | ||
414 | |||
415 | case StateSource.Teleporting: | ||
416 | PostEvent(new EventParams("changed", | ||
417 | sbcCR, | ||
418 | zeroDetectParams)); | ||
419 | PostEvent(new EventParams("changed", | ||
420 | sbcCT, | ||
421 | zeroDetectParams)); | ||
422 | break; | ||
423 | |||
424 | case StateSource.RegionStart: | ||
425 | PostEvent(new EventParams("changed", | ||
426 | sbcCRS, | ||
427 | zeroDetectParams)); | ||
428 | break; | ||
429 | } | ||
430 | } | ||
431 | |||
432 | private static Object[] sbcCRS = new Object[] { ScriptBaseClass.CHANGED_REGION_START }; | ||
433 | private static Object[] sbcCR = new Object[] { ScriptBaseClass.CHANGED_REGION }; | ||
434 | private static Object[] sbcCT = new Object[] { ScriptBaseClass.CHANGED_TELEPORT }; | ||
435 | |||
436 | /** | ||
437 | * @brief Save compilation error messages for later retrieval | ||
438 | * via GetScriptErrors(). | ||
439 | */ | ||
440 | private void ErrorHandler(Token token, string message) | ||
441 | { | ||
442 | if (token != null) | ||
443 | { | ||
444 | string srcloc = token.SrcLoc; | ||
445 | if (srcloc.StartsWith (m_CameFrom)) | ||
446 | srcloc = srcloc.Substring (m_CameFrom.Length); | ||
447 | |||
448 | m_CompilerErrors.Add(srcloc + " Error: " + message); | ||
449 | } | ||
450 | else if (message != null) | ||
451 | m_CompilerErrors.Add("(0,0) Error: " + message); | ||
452 | |||
453 | else | ||
454 | m_CompilerErrors.Add("(0,0) Error compiling, see exception in log"); | ||
455 | } | ||
456 | |||
457 | /** | ||
458 | * @brief Load script state from the given XML doc into the script memory | ||
459 | * <ScriptState Engine="XMREngine" Asset=...> | ||
460 | * <Running>...</Running> | ||
461 | * <DoGblInit>...</DoGblInit> | ||
462 | * <Permissions granted=... mask=... /> | ||
463 | * RestoreDetectParams() | ||
464 | * <Plugins> | ||
465 | * ExtractXMLObjectArray("plugin") | ||
466 | * </Plugins> | ||
467 | * <Snapshot> | ||
468 | * MigrateInEventHandler() | ||
469 | * </Snapshot> | ||
470 | * </ScriptState> | ||
471 | */ | ||
472 | private void LoadScriptState(XmlDocument doc) | ||
473 | { | ||
474 | DetectParams[] detParams; | ||
475 | LinkedList<EventParams> eventQueue; | ||
476 | |||
477 | // Everything we know is enclosed in <ScriptState>...</ScriptState> | ||
478 | XmlElement scriptStateN = (XmlElement)doc.SelectSingleNode("ScriptState"); | ||
479 | if (scriptStateN == null) | ||
480 | throw new Exception("no <ScriptState> tag"); | ||
481 | |||
482 | string sen = scriptStateN.GetAttribute("Engine"); | ||
483 | if ((sen == null) || (sen != m_Engine.ScriptEngineName)) | ||
484 | throw new Exception("<ScriptState> missing Engine=\"XMREngine\" attribute"); | ||
485 | |||
486 | |||
487 | // AssetID is unique for the script source text so make sure the | ||
488 | // state file was written for that source file | ||
489 | string assetID = scriptStateN.GetAttribute("Asset"); | ||
490 | if (assetID != m_Item.AssetID.ToString()) | ||
491 | throw new Exception("<ScriptState> assetID mismatch"); | ||
492 | |||
493 | // Also match the sourceHash in case script was | ||
494 | // loaded via 'xmroption fetchsource' and has changed | ||
495 | string sourceHash = scriptStateN.GetAttribute ("SourceHash"); | ||
496 | if ((sourceHash == null) || (sourceHash != m_ObjCode.sourceHash)) | ||
497 | throw new Exception ("<ScriptState> SourceHash mismatch"); | ||
498 | |||
499 | // Get various attributes | ||
500 | XmlElement runningN = (XmlElement)scriptStateN.SelectSingleNode("Running"); | ||
501 | m_Running = bool.Parse(runningN.InnerText); | ||
502 | |||
503 | XmlElement doGblInitN = (XmlElement)scriptStateN.SelectSingleNode("DoGblInit"); | ||
504 | doGblInit = bool.Parse(doGblInitN.InnerText); | ||
505 | |||
506 | XmlElement permissionsN = (XmlElement)scriptStateN.SelectSingleNode("Permissions"); | ||
507 | m_Item.PermsGranter = new UUID(permissionsN.GetAttribute("granter")); | ||
508 | m_Item.PermsMask = Convert.ToInt32(permissionsN.GetAttribute("mask")); | ||
509 | m_Part.Inventory.UpdateInventoryItem(m_Item, false, false); | ||
510 | |||
511 | // get values used by stuff like llDetectedGrab, etc. | ||
512 | detParams = RestoreDetectParams(scriptStateN.SelectSingleNode("DetectArray")); | ||
513 | |||
514 | // Restore queued events | ||
515 | eventQueue = RestoreEventQueue(scriptStateN.SelectSingleNode("EventQueue")); | ||
516 | |||
517 | // Restore timers and listeners | ||
518 | XmlElement pluginN = (XmlElement)scriptStateN.SelectSingleNode("Plugins"); | ||
519 | Object[] pluginData = ExtractXMLObjectArray(pluginN, "plugin"); | ||
520 | |||
521 | // Script's global variables and stack contents | ||
522 | XmlElement snapshotN = (XmlElement)scriptStateN.SelectSingleNode("Snapshot"); | ||
523 | |||
524 | Byte[] data = Convert.FromBase64String(snapshotN.InnerText); | ||
525 | MemoryStream ms = new MemoryStream(); | ||
526 | ms.Write(data, 0, data.Length); | ||
527 | ms.Seek(0, SeekOrigin.Begin); | ||
528 | MigrateInEventHandler(ms); | ||
529 | ms.Close(); | ||
530 | |||
531 | // Restore event queues, preserving any events that queued | ||
532 | // whilst we were restoring the state | ||
533 | lock (m_QueueLock) | ||
534 | { | ||
535 | m_DetectParams = detParams; | ||
536 | foreach (EventParams evt in m_EventQueue) | ||
537 | eventQueue.AddLast (evt); | ||
538 | |||
539 | m_EventQueue = eventQueue; | ||
540 | for (int i = m_EventCounts.Length; -- i >= 0;) | ||
541 | m_EventCounts[i] = 0; | ||
542 | |||
543 | foreach (EventParams evt in m_EventQueue) | ||
544 | { | ||
545 | ScriptEventCode eventCode = (ScriptEventCode)Enum.Parse(typeof(ScriptEventCode), | ||
546 | evt.EventName); | ||
547 | m_EventCounts[(int)eventCode]++; | ||
548 | } | ||
549 | } | ||
550 | |||
551 | // Requeue timer and listeners (possibly queuing new events) | ||
552 | AsyncCommandManager.CreateFromData(m_Engine, | ||
553 | m_LocalID, m_ItemID, m_Part.UUID, | ||
554 | pluginData); | ||
555 | } | ||
556 | |||
557 | /** | ||
558 | * @brief Read llDetectedGrab, etc, values from XML | ||
559 | * <EventQueue> | ||
560 | * <DetectParams>...</DetectParams> | ||
561 | * . | ||
562 | * . | ||
563 | * . | ||
564 | * </EventQueue> | ||
565 | */ | ||
566 | private LinkedList<EventParams> RestoreEventQueue(XmlNode eventsN) | ||
567 | { | ||
568 | LinkedList<EventParams> eventQueue = new LinkedList<EventParams>(); | ||
569 | if (eventsN != null) | ||
570 | { | ||
571 | XmlNodeList eventL = eventsN.SelectNodes("Event"); | ||
572 | foreach (XmlNode evnt in eventL) | ||
573 | { | ||
574 | string name = ((XmlElement)evnt).GetAttribute("Name"); | ||
575 | object[] parms = ExtractXMLObjectArray(evnt, "param"); | ||
576 | DetectParams[] detects = RestoreDetectParams(evnt); | ||
577 | |||
578 | if (parms == null) parms = zeroObjectArray; | ||
579 | if (detects == null) detects = zeroDetectParams; | ||
580 | |||
581 | EventParams evt = new EventParams(name, parms, detects); | ||
582 | eventQueue.AddLast(evt); | ||
583 | } | ||
584 | } | ||
585 | return eventQueue; | ||
586 | } | ||
587 | |||
588 | /** | ||
589 | * @brief Read llDetectedGrab, etc, values from XML | ||
590 | * <DetectArray> | ||
591 | * <DetectParams>...</DetectParams> | ||
592 | * . | ||
593 | * . | ||
594 | * . | ||
595 | * </DetectArray> | ||
596 | */ | ||
597 | private DetectParams[] RestoreDetectParams(XmlNode detectedN) | ||
598 | { | ||
599 | if (detectedN == null) return null; | ||
600 | |||
601 | List<DetectParams> detected = new List<DetectParams>(); | ||
602 | XmlNodeList detectL = detectedN.SelectNodes("DetectParams"); | ||
603 | |||
604 | DetectParams detprm = new DetectParams(); | ||
605 | foreach (XmlNode detxml in detectL) | ||
606 | { | ||
607 | try | ||
608 | { | ||
609 | detprm.Group = new UUID(detxml.Attributes.GetNamedItem("group").Value); | ||
610 | detprm.Key = new UUID(detxml.Attributes.GetNamedItem("key").Value); | ||
611 | detprm.Owner = new UUID(detxml.Attributes.GetNamedItem("owner").Value); | ||
612 | |||
613 | detprm.LinkNum = Int32.Parse(detxml.Attributes.GetNamedItem("linkNum").Value); | ||
614 | detprm.Type = Int32.Parse(detxml.Attributes.GetNamedItem("type").Value); | ||
615 | |||
616 | detprm.Name = detxml.Attributes.GetNamedItem("name").Value; | ||
617 | |||
618 | detprm.OffsetPos = new LSL_Types.Vector3(detxml.Attributes.GetNamedItem("pos").Value); | ||
619 | detprm.Position = new LSL_Types.Vector3(detxml.Attributes.GetNamedItem("position").Value); | ||
620 | detprm.Velocity = new LSL_Types.Vector3(detxml.Attributes.GetNamedItem("velocity").Value); | ||
621 | |||
622 | detprm.Rotation = new LSL_Types.Quaternion(detxml.Attributes.GetNamedItem("rotation").Value); | ||
623 | |||
624 | detected.Add(detprm); | ||
625 | detprm = new DetectParams(); | ||
626 | } | ||
627 | catch (Exception e) | ||
628 | { | ||
629 | m_log.Warn("[XMREngine]: RestoreDetectParams bad XML: " + detxml.ToString()); | ||
630 | m_log.Warn("[XMREngine]: ... " + e.ToString()); | ||
631 | } | ||
632 | } | ||
633 | |||
634 | return detected.ToArray(); | ||
635 | } | ||
636 | |||
637 | /** | ||
638 | * @brief Extract elements of an array of objects from an XML parent. | ||
639 | * Each element is of form <tag ...>...</tag> | ||
640 | * @param parent = XML parent to extract them from | ||
641 | * @param tag = what the value's tag is | ||
642 | * @returns object array of the values | ||
643 | */ | ||
644 | private static object[] ExtractXMLObjectArray(XmlNode parent, string tag) | ||
645 | { | ||
646 | List<Object> olist = new List<Object>(); | ||
647 | |||
648 | XmlNodeList itemL = parent.SelectNodes(tag); | ||
649 | foreach (XmlNode item in itemL) | ||
650 | olist.Add(ExtractXMLObjectValue(item)); | ||
651 | |||
652 | return olist.ToArray(); | ||
653 | } | ||
654 | |||
655 | private static object ExtractXMLObjectValue(XmlNode item) | ||
656 | { | ||
657 | string itemType = item.Attributes.GetNamedItem("type").Value; | ||
658 | |||
659 | if (itemType == "list") | ||
660 | return new LSL_List(ExtractXMLObjectArray(item, "item")); | ||
661 | |||
662 | if (itemType == "OpenMetaverse.UUID") | ||
663 | { | ||
664 | UUID val = new UUID(); | ||
665 | UUID.TryParse(item.InnerText, out val); | ||
666 | return val; | ||
667 | } | ||
668 | |||
669 | Type itemT = Type.GetType(itemType); | ||
670 | if (itemT == null) | ||
671 | { | ||
672 | Object[] args = new Object[] { item.InnerText }; | ||
673 | |||
674 | string assembly = itemType + ", OpenSim.Region.ScriptEngine.Shared"; | ||
675 | itemT = Type.GetType(assembly); | ||
676 | if (itemT == null) | ||
677 | return null; | ||
678 | |||
679 | return Activator.CreateInstance(itemT, args); | ||
680 | } | ||
681 | |||
682 | return Convert.ChangeType(item.InnerText, itemT); | ||
683 | } | ||
684 | |||
685 | /* | ||
686 | * Migrate an event handler in from a stream. | ||
687 | * | ||
688 | * Input: | ||
689 | * stream = as generated by MigrateOutEventHandler() | ||
690 | */ | ||
691 | private void MigrateInEventHandler (Stream stream) | ||
692 | { | ||
693 | miehexcep = null; | ||
694 | |||
695 | // do all the work in the MigrateInEventHandlerThread() method below | ||
696 | miehstream = stream; | ||
697 | |||
698 | XMRScriptThread cst = m_Engine.CurrentScriptThread (); | ||
699 | if (cst != null) | ||
700 | { | ||
701 | // in case we are getting called inside some LSL Api function | ||
702 | MigrateInEventHandlerThread (); | ||
703 | } | ||
704 | else | ||
705 | { | ||
706 | // some other thread, do migration via a script thread | ||
707 | m_Engine.QueueToTrunk(this.MigrateInEventHandlerThread); | ||
708 | |||
709 | // wait for it to complete | ||
710 | lock (miehdone) | ||
711 | { | ||
712 | while (miehstream != null) | ||
713 | Monitor.Wait(miehdone); | ||
714 | } | ||
715 | } | ||
716 | |||
717 | // maybe it threw up | ||
718 | if (miehexcep != null) | ||
719 | throw miehexcep; | ||
720 | } | ||
721 | |||
722 | private Exception miehexcep; | ||
723 | private object miehdone = new object (); | ||
724 | private Stream miehstream; | ||
725 | private void MigrateInEventHandlerThread () | ||
726 | { | ||
727 | try | ||
728 | { | ||
729 | int mv = miehstream.ReadByte (); | ||
730 | if (mv != migrationVersion) | ||
731 | throw new Exception ("incoming migration version " + mv + " but accept only " + migrationVersion); | ||
732 | |||
733 | miehstream.ReadByte (); // ignored | ||
734 | |||
735 | // Restore script variables and stack and other state from stream. | ||
736 | // And it also marks us busy (by setting this.eventCode) so we can't be | ||
737 | // started again and this event lost. | ||
738 | |||
739 | BinaryReader br = new BinaryReader (miehstream); | ||
740 | this.MigrateIn (br); | ||
741 | |||
742 | // If eventCode is None, it means the script was idle when migrated. | ||
743 | |||
744 | if (this.eventCode != ScriptEventCode.None) | ||
745 | { | ||
746 | // So microthread.Start() calls XMRScriptUThread.Main() which calls the | ||
747 | // event handler function. The event handler function sees the stack | ||
748 | // frames in this.stackFrames and restores its args and locals, then calls | ||
749 | // whatever it was calling when the snapshot was taken. That function also | ||
750 | // sees this.stackFrames and restores its args and locals, and so on... | ||
751 | // Eventually it gets to the point of calling CheckRun() which sees we are | ||
752 | // doing a restore and it suspends, returning here with the microthread | ||
753 | // stack all restored. It shouldn't ever throw an exception. | ||
754 | |||
755 | this.stackFramesRestored = false; | ||
756 | Exception te = microthread.StartEx (); | ||
757 | if (te != null) throw te; | ||
758 | if (!this.stackFramesRestored) | ||
759 | throw new Exception ("migrate in did not complete"); | ||
760 | } | ||
761 | } | ||
762 | catch (Exception e) | ||
763 | { | ||
764 | miehexcep = e; | ||
765 | } | ||
766 | finally | ||
767 | { | ||
768 | // Wake the MigrateInEventHandler() method above. | ||
769 | lock (miehdone) | ||
770 | { | ||
771 | miehstream = null; | ||
772 | Monitor.Pulse (miehdone); | ||
773 | } | ||
774 | } | ||
775 | } | ||
776 | } | ||
777 | } | ||