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