diff options
Diffstat (limited to 'OpenSim/Region/ScriptEngine/YEngine/XMRInstCtor.cs')
-rw-r--r-- | OpenSim/Region/ScriptEngine/YEngine/XMRInstCtor.cs | 721 |
1 files changed, 721 insertions, 0 deletions
diff --git a/OpenSim/Region/ScriptEngine/YEngine/XMRInstCtor.cs b/OpenSim/Region/ScriptEngine/YEngine/XMRInstCtor.cs new file mode 100644 index 0000000..ce74d9f --- /dev/null +++ b/OpenSim/Region/ScriptEngine/YEngine/XMRInstCtor.cs | |||
@@ -0,0 +1,721 @@ | |||
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.Yengine; | ||
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.Yengine | ||
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 = YEngine 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(Yengine engine, string scriptBasePath, | ||
76 | int stackSize, int heapSize, ArrayList errors) | ||
77 | { | ||
78 | if(stackSize < 16384) | ||
79 | stackSize = 16384; | ||
80 | if(heapSize < 16384) | ||
81 | heapSize = 16384; | ||
82 | |||
83 | // Save all call parameters in instance vars for easy access. | ||
84 | m_Engine = engine; | ||
85 | m_ScriptBasePath = scriptBasePath; | ||
86 | m_StackSize = stackSize; | ||
87 | m_StackLeft = stackSize; | ||
88 | m_HeapSize = heapSize; | ||
89 | m_CompilerErrors = errors; | ||
90 | m_StateFileName = GetStateFileName(scriptBasePath, m_ItemID); | ||
91 | |||
92 | // Not in any XMRInstQueue. | ||
93 | m_NextInst = this; | ||
94 | m_PrevInst = this; | ||
95 | |||
96 | // Set up list of API calls it has available. | ||
97 | // This also gets the API modules ready to accept setup data, such as | ||
98 | // active listeners being restored. | ||
99 | IScriptApi scriptApi; | ||
100 | ApiManager am = new ApiManager(); | ||
101 | foreach(string api in am.GetApis()) | ||
102 | { | ||
103 | // Instantiate the API for this script instance. | ||
104 | if(api != "LSL") | ||
105 | scriptApi = am.CreateApi(api); | ||
106 | else | ||
107 | scriptApi = m_XMRLSLApi = new XMRLSL_Api(); | ||
108 | |||
109 | // Connect it up to the instance. | ||
110 | InitScriptApi(engine, api, scriptApi); | ||
111 | } | ||
112 | |||
113 | m_XMRLSLApi.InitXMRLSLApi(this); | ||
114 | |||
115 | // Get object loaded, compiling script and reading .state file as | ||
116 | // necessary to restore the state. | ||
117 | suspendOnCheckRunHold = true; | ||
118 | InstantiateScript(); | ||
119 | m_SourceCode = null; | ||
120 | if(m_ObjCode == null) | ||
121 | throw new ArgumentNullException("m_ObjCode"); | ||
122 | if(m_ObjCode.scriptEventHandlerTable == null) | ||
123 | throw new ArgumentNullException("m_ObjCode.scriptEventHandlerTable"); | ||
124 | |||
125 | suspendOnCheckRunHold = false; | ||
126 | suspendOnCheckRunTemp = false; | ||
127 | |||
128 | // Declare which events the script's current state can handle. | ||
129 | int eventMask = GetStateEventFlags(stateCode); | ||
130 | m_Part.SetScriptEvents(m_ItemID, eventMask); | ||
131 | } | ||
132 | |||
133 | private void InitScriptApi(Yengine engine, string api, IScriptApi scriptApi) | ||
134 | { | ||
135 | // Set up m_ApiManager_<APINAME> = instance pointer. | ||
136 | engine.m_XMRInstanceApiCtxFieldInfos[api].SetValue(this, scriptApi); | ||
137 | |||
138 | // Initialize the API instance. | ||
139 | scriptApi.Initialize(m_Engine, m_Part, m_Item); | ||
140 | this.InitApi(api, scriptApi); | ||
141 | } | ||
142 | |||
143 | |||
144 | /* | ||
145 | * Get script object code loaded in memory and all ready to run, | ||
146 | * ready to resume it from where the .state file says it was last | ||
147 | */ | ||
148 | private void InstantiateScript() | ||
149 | { | ||
150 | bool compiledIt = false; | ||
151 | ScriptObjCode objCode; | ||
152 | |||
153 | // If source code string is empty, use the asset ID as the object file name. | ||
154 | // Allow lines of // comments at the beginning (for such as engine selection). | ||
155 | int i, j, len; | ||
156 | if(m_SourceCode == null) | ||
157 | m_SourceCode = String.Empty; | ||
158 | for(len = m_SourceCode.Length; len > 0; --len) | ||
159 | { | ||
160 | if(m_SourceCode[len - 1] > ' ') | ||
161 | break; | ||
162 | } | ||
163 | for(i = 0; i < len; i++) | ||
164 | { | ||
165 | char c = m_SourceCode[i]; | ||
166 | if(c <= ' ') | ||
167 | continue; | ||
168 | if(c != '/') | ||
169 | break; | ||
170 | if((i + 1 >= len) || (m_SourceCode[i + 1] != '/')) | ||
171 | break; | ||
172 | i = m_SourceCode.IndexOf('\n', i); | ||
173 | if(i < 0) | ||
174 | i = len - 1; | ||
175 | } | ||
176 | if((i >= len) || !m_Engine.m_UseSourceHashCode) | ||
177 | { | ||
178 | // Source consists of nothing but // comments and whitespace, | ||
179 | // or we are being forced to use the asset-id as the key, to | ||
180 | // open an already existing object code file. | ||
181 | m_ScriptObjCodeKey = m_Item.AssetID.ToString(); | ||
182 | if(i >= len) | ||
183 | m_SourceCode = ""; | ||
184 | } | ||
185 | else | ||
186 | { | ||
187 | // Make up dictionary key for the object code. | ||
188 | // Use the same object code for identical source code | ||
189 | // regardless of asset ID, so we don't care if they | ||
190 | // copy scripts or not. | ||
191 | byte[] scbytes = System.Text.Encoding.UTF8.GetBytes(m_SourceCode); | ||
192 | StringBuilder sb = new StringBuilder((256 + 5) / 6); | ||
193 | ByteArrayToSixbitStr(sb, System.Security.Cryptography.SHA256.Create().ComputeHash(scbytes)); | ||
194 | m_ScriptObjCodeKey = sb.ToString(); | ||
195 | |||
196 | // But source code can be just a sixbit string itself | ||
197 | // that identifies an already existing object code file. | ||
198 | if(len - i == m_ScriptObjCodeKey.Length) | ||
199 | { | ||
200 | for(j = len; --j >= i;) | ||
201 | { | ||
202 | if(sixbit.IndexOf(m_SourceCode[j]) < 0) | ||
203 | break; | ||
204 | } | ||
205 | if(j < i) | ||
206 | { | ||
207 | m_ScriptObjCodeKey = m_SourceCode.Substring(i, len - i); | ||
208 | m_SourceCode = ""; | ||
209 | } | ||
210 | } | ||
211 | } | ||
212 | |||
213 | // There may already be an ScriptObjCode struct in memory that | ||
214 | // we can use. If not, try to compile it. | ||
215 | lock(m_CompileLock) | ||
216 | { | ||
217 | if(!m_CompiledScriptObjCode.TryGetValue(m_ScriptObjCodeKey, out objCode) || m_ForceRecomp) | ||
218 | { | ||
219 | objCode = TryToCompile(); | ||
220 | compiledIt = true; | ||
221 | } | ||
222 | |||
223 | // Loaded successfully, increment reference count. | ||
224 | // If we just compiled it though, reset count to 0 first as | ||
225 | // this is the one-and-only existance of this objCode struct, | ||
226 | // and we want any old ones for this source code to be garbage | ||
227 | // collected. | ||
228 | |||
229 | if(compiledIt) | ||
230 | { | ||
231 | m_CompiledScriptObjCode[m_ScriptObjCodeKey] = objCode; | ||
232 | objCode.refCount = 0; | ||
233 | } | ||
234 | objCode.refCount++; | ||
235 | |||
236 | // Now set up to decrement ref count on dispose. | ||
237 | m_ObjCode = objCode; | ||
238 | } | ||
239 | |||
240 | try | ||
241 | { | ||
242 | |||
243 | // Fill in script instance from object code | ||
244 | // Script instance is put in a "never-ever-has-run-before" state. | ||
245 | LoadObjCode(); | ||
246 | |||
247 | // Fill in script intial state | ||
248 | // - either as loaded from a .state file | ||
249 | // - or initial default state_entry() event | ||
250 | LoadInitialState(); | ||
251 | } | ||
252 | catch | ||
253 | { | ||
254 | |||
255 | // If any error loading, decrement object code reference count. | ||
256 | DecObjCodeRefCount(); | ||
257 | throw; | ||
258 | } | ||
259 | } | ||
260 | |||
261 | private const string sixbit = "0123456789_abcdefghijklmnopqrstuvwxyz@ABCDEFGHIJKLMNOPQRSTUVWXYZ"; | ||
262 | private static void ByteArrayToSixbitStr(StringBuilder sb, byte[] bytes) | ||
263 | { | ||
264 | int bit = 0; | ||
265 | int val = 0; | ||
266 | foreach(byte b in bytes) | ||
267 | { | ||
268 | val |= (int)((uint)b << bit); | ||
269 | bit += 8; | ||
270 | while(bit >= 6) | ||
271 | { | ||
272 | sb.Append(sixbit[val & 63]); | ||
273 | val >>= 6; | ||
274 | bit -= 6; | ||
275 | } | ||
276 | } | ||
277 | if(bit > 0) | ||
278 | sb.Append(sixbit[val & 63]); | ||
279 | } | ||
280 | |||
281 | // Try to create object code from source code | ||
282 | // If error, just throw exception | ||
283 | private ScriptObjCode TryToCompile() | ||
284 | { | ||
285 | m_CompilerErrors.Clear(); | ||
286 | |||
287 | // If object file exists, create ScriptObjCode directly from that. | ||
288 | // Otherwise, compile the source to create object file then create | ||
289 | // ScriptObjCode from that. | ||
290 | string assetID = m_Item.AssetID.ToString(); | ||
291 | m_CameFrom = "asset://" + assetID; | ||
292 | ScriptObjCode objCode = Compile(); | ||
293 | if(m_CompilerErrors.Count != 0) | ||
294 | throw new Exception("compilation errors"); | ||
295 | |||
296 | if(objCode == null) | ||
297 | throw new Exception("compilation failed"); | ||
298 | |||
299 | return objCode; | ||
300 | } | ||
301 | |||
302 | /* | ||
303 | * Retrieve source from asset server. | ||
304 | */ | ||
305 | private string FetchSource(string cameFrom) | ||
306 | { | ||
307 | m_log.Debug("[YEngine]: fetching source " + cameFrom); | ||
308 | if(!cameFrom.StartsWith("asset://")) | ||
309 | throw new Exception("unable to retrieve source from " + cameFrom); | ||
310 | |||
311 | string assetID = cameFrom.Substring(8); | ||
312 | AssetBase asset = m_Engine.World.AssetService.Get(assetID); | ||
313 | if(asset == null) | ||
314 | throw new Exception("source not found " + cameFrom); | ||
315 | |||
316 | string source = Encoding.UTF8.GetString(asset.Data); | ||
317 | if(EmptySource(source)) | ||
318 | throw new Exception("fetched source empty " + cameFrom); | ||
319 | |||
320 | return source; | ||
321 | } | ||
322 | |||
323 | /* | ||
324 | * Fill in script object initial contents. | ||
325 | * Set the initial state to "default". | ||
326 | */ | ||
327 | private void LoadObjCode() | ||
328 | { | ||
329 | // Script must leave this much stack remaining on calls to CheckRun(). | ||
330 | this.stackLimit = m_StackSize / 2; | ||
331 | |||
332 | // This is how many total heap bytes script is allowed to use. | ||
333 | this.heapLimit = m_HeapSize; | ||
334 | |||
335 | // Allocate global variable arrays. | ||
336 | this.glblVars.AllocVarArrays(m_ObjCode.glblSizes); | ||
337 | |||
338 | // Script can handle these event codes. | ||
339 | m_HaveEventHandlers = new bool[m_ObjCode.scriptEventHandlerTable.GetLength(1)]; | ||
340 | for(int i = m_ObjCode.scriptEventHandlerTable.GetLength(0); --i >= 0;) | ||
341 | { | ||
342 | for(int j = m_ObjCode.scriptEventHandlerTable.GetLength(1); --j >= 0;) | ||
343 | { | ||
344 | if(m_ObjCode.scriptEventHandlerTable[i, j] != null) | ||
345 | { | ||
346 | m_HaveEventHandlers[j] = true; | ||
347 | } | ||
348 | } | ||
349 | } | ||
350 | } | ||
351 | |||
352 | /* | ||
353 | * LoadInitialState() | ||
354 | * if no state XML file exists for the asset, | ||
355 | * post initial default state events | ||
356 | * else | ||
357 | * try to restore from .state file | ||
358 | * If any error, throw exception | ||
359 | */ | ||
360 | private void LoadInitialState() | ||
361 | { | ||
362 | // If no .state file exists, start from default state | ||
363 | // Otherwise, read initial state from the .state file | ||
364 | |||
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 | else | ||
453 | m_CompilerErrors.Add("(0,0) Error compiling, see exception in log"); | ||
454 | } | ||
455 | |||
456 | /** | ||
457 | * @brief Load script state from the given XML doc into the script memory | ||
458 | * <ScriptState Engine="YEngine" Asset=...> | ||
459 | * <Running>...</Running> | ||
460 | * <DoGblInit>...</DoGblInit> | ||
461 | * <Permissions granted=... mask=... /> | ||
462 | * RestoreDetectParams() | ||
463 | * <Plugins> | ||
464 | * ExtractXMLObjectArray("plugin") | ||
465 | * </Plugins> | ||
466 | * <Snapshot> | ||
467 | * MigrateInEventHandler() | ||
468 | * </Snapshot> | ||
469 | * </ScriptState> | ||
470 | */ | ||
471 | private void LoadScriptState(XmlDocument doc) | ||
472 | { | ||
473 | DetectParams[] detParams; | ||
474 | LinkedList<EventParams> eventQueue; | ||
475 | |||
476 | // Everything we know is enclosed in <ScriptState>...</ScriptState> | ||
477 | XmlElement scriptStateN = (XmlElement)doc.SelectSingleNode("ScriptState"); | ||
478 | if(scriptStateN == null) | ||
479 | throw new Exception("no <ScriptState> tag"); | ||
480 | |||
481 | string sen = scriptStateN.GetAttribute("Engine"); | ||
482 | if((sen == null) || (sen != m_Engine.ScriptEngineName)) | ||
483 | throw new Exception("<ScriptState> missing Engine=\"YEngine\" attribute"); | ||
484 | |||
485 | // AssetID is unique for the script source text so make sure the | ||
486 | // state file was written for that source file | ||
487 | string assetID = scriptStateN.GetAttribute("Asset"); | ||
488 | if(assetID != m_Item.AssetID.ToString()) | ||
489 | throw new Exception("<ScriptState> assetID mismatch"); | ||
490 | |||
491 | // Also match the sourceHash in case script was | ||
492 | // loaded via 'xmroption fetchsource' and has changed | ||
493 | string sourceHash = scriptStateN.GetAttribute("SourceHash"); | ||
494 | if((sourceHash == null) || (sourceHash != m_ObjCode.sourceHash)) | ||
495 | throw new Exception("<ScriptState> SourceHash mismatch"); | ||
496 | |||
497 | // Get various attributes | ||
498 | XmlElement runningN = (XmlElement)scriptStateN.SelectSingleNode("Running"); | ||
499 | m_Running = bool.Parse(runningN.InnerText); | ||
500 | |||
501 | XmlElement doGblInitN = (XmlElement)scriptStateN.SelectSingleNode("DoGblInit"); | ||
502 | doGblInit = bool.Parse(doGblInitN.InnerText); | ||
503 | |||
504 | XmlElement permissionsN = (XmlElement)scriptStateN.SelectSingleNode("Permissions"); | ||
505 | m_Item.PermsGranter = new UUID(permissionsN.GetAttribute("granter")); | ||
506 | m_Item.PermsMask = Convert.ToInt32(permissionsN.GetAttribute("mask")); | ||
507 | m_Part.Inventory.UpdateInventoryItem(m_Item, false, false); | ||
508 | |||
509 | // get values used by stuff like llDetectedGrab, etc. | ||
510 | detParams = RestoreDetectParams(scriptStateN.SelectSingleNode("DetectArray")); | ||
511 | |||
512 | // Restore queued events | ||
513 | eventQueue = RestoreEventQueue(scriptStateN.SelectSingleNode("EventQueue")); | ||
514 | |||
515 | // Restore timers and listeners | ||
516 | XmlElement pluginN = (XmlElement)scriptStateN.SelectSingleNode("Plugins"); | ||
517 | Object[] pluginData = ExtractXMLObjectArray(pluginN, "plugin"); | ||
518 | |||
519 | // Script's global variables and stack contents | ||
520 | XmlElement snapshotN = | ||
521 | (XmlElement)scriptStateN.SelectSingleNode("Snapshot"); | ||
522 | |||
523 | Byte[] data = Convert.FromBase64String(snapshotN.InnerText); | ||
524 | MemoryStream ms = new MemoryStream(); | ||
525 | ms.Write(data, 0, data.Length); | ||
526 | ms.Seek(0, SeekOrigin.Begin); | ||
527 | MigrateInEventHandler(ms); | ||
528 | ms.Close(); | ||
529 | |||
530 | // Restore event queues, preserving any events that queued | ||
531 | // whilst we were restoring the state | ||
532 | lock(m_QueueLock) | ||
533 | { | ||
534 | m_DetectParams = detParams; | ||
535 | foreach(EventParams evt in m_EventQueue) | ||
536 | eventQueue.AddLast(evt); | ||
537 | |||
538 | m_EventQueue = eventQueue; | ||
539 | for(int i = m_EventCounts.Length; --i >= 0;) | ||
540 | m_EventCounts[i] = 0; | ||
541 | foreach(EventParams evt in m_EventQueue) | ||
542 | { | ||
543 | ScriptEventCode eventCode = (ScriptEventCode)Enum.Parse(typeof(ScriptEventCode), | ||
544 | evt.EventName); | ||
545 | m_EventCounts[(int)eventCode]++; | ||
546 | } | ||
547 | } | ||
548 | |||
549 | // Requeue timer and listeners (possibly queuing new events) | ||
550 | AsyncCommandManager.CreateFromData(m_Engine, | ||
551 | m_LocalID, m_ItemID, m_Part.UUID, | ||
552 | pluginData); | ||
553 | } | ||
554 | |||
555 | /** | ||
556 | * @brief Read llDetectedGrab, etc, values from XML | ||
557 | * <EventQueue> | ||
558 | * <DetectParams>...</DetectParams> | ||
559 | * . | ||
560 | * . | ||
561 | * . | ||
562 | * </EventQueue> | ||
563 | */ | ||
564 | private LinkedList<EventParams> RestoreEventQueue(XmlNode eventsN) | ||
565 | { | ||
566 | LinkedList<EventParams> eventQueue = new LinkedList<EventParams>(); | ||
567 | if(eventsN != null) | ||
568 | { | ||
569 | XmlNodeList eventL = eventsN.SelectNodes("Event"); | ||
570 | foreach(XmlNode evnt in eventL) | ||
571 | { | ||
572 | string name = ((XmlElement)evnt).GetAttribute("Name"); | ||
573 | object[] parms = ExtractXMLObjectArray(evnt, "param"); | ||
574 | DetectParams[] detects = RestoreDetectParams(evnt); | ||
575 | |||
576 | if(parms == null) | ||
577 | parms = zeroObjectArray; | ||
578 | if(detects == null) | ||
579 | 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) | ||
600 | return null; | ||
601 | |||
602 | List<DetectParams> detected = new List<DetectParams>(); | ||
603 | XmlNodeList detectL = detectedN.SelectNodes("DetectParams"); | ||
604 | |||
605 | DetectParams detprm = new DetectParams(); | ||
606 | foreach(XmlNode detxml in detectL) | ||
607 | { | ||
608 | try | ||
609 | { | ||
610 | detprm.Group = new UUID(detxml.Attributes.GetNamedItem("group").Value); | ||
611 | detprm.Key = new UUID(detxml.Attributes.GetNamedItem("key").Value); | ||
612 | detprm.Owner = new UUID(detxml.Attributes.GetNamedItem("owner").Value); | ||
613 | |||
614 | detprm.LinkNum = Int32.Parse(detxml.Attributes.GetNamedItem("linkNum").Value); | ||
615 | detprm.Type = Int32.Parse(detxml.Attributes.GetNamedItem("type").Value); | ||
616 | |||
617 | detprm.Name = detxml.Attributes.GetNamedItem("name").Value; | ||
618 | |||
619 | detprm.OffsetPos = new LSL_Types.Vector3(detxml.Attributes.GetNamedItem("pos").Value); | ||
620 | detprm.Position = new LSL_Types.Vector3(detxml.Attributes.GetNamedItem("position").Value); | ||
621 | detprm.Velocity = new LSL_Types.Vector3(detxml.Attributes.GetNamedItem("velocity").Value); | ||
622 | |||
623 | detprm.Rotation = new LSL_Types.Quaternion(detxml.Attributes.GetNamedItem("rotation").Value); | ||
624 | |||
625 | detected.Add(detprm); | ||
626 | detprm = new DetectParams(); | ||
627 | } | ||
628 | catch(Exception e) | ||
629 | { | ||
630 | m_log.Warn("[YEngine]: RestoreDetectParams bad XML: " + detxml.ToString()); | ||
631 | m_log.Warn("[YEngine]: ... " + e.ToString()); | ||
632 | } | ||
633 | } | ||
634 | |||
635 | return detected.ToArray(); | ||
636 | } | ||
637 | |||
638 | /** | ||
639 | * @brief Extract elements of an array of objects from an XML parent. | ||
640 | * Each element is of form <tag ...>...</tag> | ||
641 | * @param parent = XML parent to extract them from | ||
642 | * @param tag = what the value's tag is | ||
643 | * @returns object array of the values | ||
644 | */ | ||
645 | private static object[] ExtractXMLObjectArray(XmlNode parent, string tag) | ||
646 | { | ||
647 | List<Object> olist = new List<Object>(); | ||
648 | |||
649 | XmlNodeList itemL = parent.SelectNodes(tag); | ||
650 | foreach(XmlNode item in itemL) | ||
651 | { | ||
652 | olist.Add(ExtractXMLObjectValue(item)); | ||
653 | } | ||
654 | |||
655 | return olist.ToArray(); | ||
656 | } | ||
657 | |||
658 | private static object ExtractXMLObjectValue(XmlNode item) | ||
659 | { | ||
660 | string itemType = item.Attributes.GetNamedItem("type").Value; | ||
661 | |||
662 | if(itemType == "list") | ||
663 | { | ||
664 | return new LSL_List(ExtractXMLObjectArray(item, "item")); | ||
665 | } | ||
666 | |||
667 | if(itemType == "OpenMetaverse.UUID") | ||
668 | { | ||
669 | UUID val = new UUID(); | ||
670 | UUID.TryParse(item.InnerText, out val); | ||
671 | return val; | ||
672 | } | ||
673 | |||
674 | Type itemT = Type.GetType(itemType); | ||
675 | if(itemT == null) | ||
676 | { | ||
677 | Object[] args = new Object[] { item.InnerText }; | ||
678 | |||
679 | string assembly = itemType + ", OpenSim.Region.ScriptEngine.Shared"; | ||
680 | itemT = Type.GetType(assembly); | ||
681 | if(itemT == null) | ||
682 | { | ||
683 | return null; | ||
684 | } | ||
685 | return Activator.CreateInstance(itemT, args); | ||
686 | } | ||
687 | |||
688 | return Convert.ChangeType(item.InnerText, itemT); | ||
689 | } | ||
690 | |||
691 | /* | ||
692 | * Migrate an event handler in from a stream. | ||
693 | * | ||
694 | * Input: | ||
695 | * stream = as generated by MigrateOutEventHandler() | ||
696 | */ | ||
697 | private void MigrateInEventHandler(Stream stream) | ||
698 | { | ||
699 | int mv = stream.ReadByte(); | ||
700 | if(mv != migrationVersion) | ||
701 | throw new Exception("incoming migration version " + mv + " but accept only " + migrationVersion); | ||
702 | |||
703 | stream.ReadByte(); // ignored | ||
704 | |||
705 | /* | ||
706 | * Restore script variables and stack and other state from stream. | ||
707 | * And it also marks us busy (by setting this.eventCode) so we can't be | ||
708 | * started again and this event lost. If it restores this.eventCode = | ||
709 | * None, the the script was idle. | ||
710 | */ | ||
711 | lock(m_RunLock) | ||
712 | { | ||
713 | BinaryReader br = new BinaryReader(stream); | ||
714 | this.MigrateIn(br); | ||
715 | |||
716 | m_RunOnePhase = "MigrateInEventHandler finished"; | ||
717 | CheckRunLockInvariants(true); | ||
718 | } | ||
719 | } | ||
720 | } | ||
721 | } | ||