aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/ScriptEngine/YEngine/XMREngine.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/ScriptEngine/YEngine/XMREngine.cs')
-rw-r--r--OpenSim/Region/ScriptEngine/YEngine/XMREngine.cs1901
1 files changed, 1901 insertions, 0 deletions
diff --git a/OpenSim/Region/ScriptEngine/YEngine/XMREngine.cs b/OpenSim/Region/ScriptEngine/YEngine/XMREngine.cs
new file mode 100644
index 0000000..45e4469
--- /dev/null
+++ b/OpenSim/Region/ScriptEngine/YEngine/XMREngine.cs
@@ -0,0 +1,1901 @@
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// based on YEngine from Mike Rieker (Dreamnation) and Melanie Thielker
29// but with several changes to be more cross platform.
30
31using log4net;
32using Mono.Addins;
33using Nini.Config;
34using OpenSim.Framework;
35using OpenSim.Framework.Console;
36using OpenSim.Framework.Monitoring;
37using OpenSim.Region.ClientStack.Linden;
38using OpenSim.Region.Framework.Interfaces;
39using OpenSim.Region.Framework.Scenes;
40using OpenSim.Region.ScriptEngine.Interfaces;
41using OpenSim.Region.ScriptEngine.Shared;
42using OpenSim.Region.ScriptEngine.Shared.Api;
43using OpenMetaverse;
44using System;
45using System.Collections;
46using System.Collections.Generic;
47using System.IO;
48using System.Reflection;
49using System.Reflection.Emit;
50using System.Text;
51using System.Threading;
52using System.Timers;
53using System.Xml;
54
55using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
56using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
57using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
58using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
59using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
60using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
61using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
62
63[assembly: Addin("YEngine", OpenSim.VersionInfo.VersionNumber)]
64[assembly: AddinDependency("OpenSim.Region.Framework", OpenSim.VersionInfo.VersionNumber)]
65
66namespace OpenSim.Region.ScriptEngine.Yengine
67{
68 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "YEngine")]
69 public partial class Yengine: INonSharedRegionModule, IScriptEngine,
70 IScriptModule
71 {
72 public static readonly DetectParams[] zeroDetectParams = new DetectParams[0];
73 private static ArrayList noScriptErrors = new ArrayList();
74 public static readonly ILog m_log =
75 LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
76 private static readonly string[] scriptReferencedAssemblies = new string[0];
77
78 private bool m_LateInit;
79 private bool m_TraceCalls;
80 public bool m_Verbose;
81 public bool m_ScriptDebug;
82 public bool m_ScriptDebugSaveSource;
83 public bool m_ScriptDebugSaveIL;
84 public Scene m_Scene;
85 private IConfigSource m_ConfigSource;
86 private IConfig m_Config;
87 private string m_ScriptBasePath;
88 private bool m_Enabled = false;
89 public bool m_StartProcessing = false;
90 public bool m_UseSourceHashCode = false;
91 private Dictionary<UUID, ArrayList> m_ScriptErrors =
92 new Dictionary<UUID, ArrayList>();
93 private Dictionary<UUID, List<UUID>> m_ObjectItemList =
94 new Dictionary<UUID, List<UUID>>();
95 private Dictionary<UUID, XMRInstance[]> m_ObjectInstArray =
96 new Dictionary<UUID, XMRInstance[]>();
97 public Dictionary<string, FieldInfo> m_XMRInstanceApiCtxFieldInfos =
98 new Dictionary<string, FieldInfo>();
99 public int m_StackSize;
100 private int m_HeapSize;
101 private Thread m_SleepThread = null;
102
103 private bool m_Exiting = false;
104
105 private int m_MaintenanceInterval = 10;
106 private System.Timers.Timer m_MaintenanceTimer;
107 public int numThreadScriptWorkers;
108
109 private object m_FrameUpdateLock = new object();
110 private event ThreadStart m_FrameUpdateList = null;
111
112 // Various instance lists:
113 // m_InstancesDict = all known instances
114 // find an instance given its itemID
115 // m_StartQueue = instances that have just had event queued to them
116 // m_YieldQueue = instances that are ready to run right now
117 // m_SleepQueue = instances that have m_SleepUntil valid
118 // sorted by ascending m_SleepUntil
119 private Dictionary<UUID, XMRInstance> m_InstancesDict =
120 new Dictionary<UUID, XMRInstance>();
121 public Queue<ThreadStart> m_ThunkQueue = new Queue<ThreadStart>();
122 public XMRInstQueue m_StartQueue = new XMRInstQueue();
123 public XMRInstQueue m_YieldQueue = new XMRInstQueue();
124 public XMRInstQueue m_SleepQueue = new XMRInstQueue();
125 private string m_LockedDict = "nobody";
126
127 public Yengine()
128 {
129 }
130
131 public string Name
132 {
133 get
134 {
135 return "YEngine";
136 }
137 }
138
139 public Type ReplaceableInterface
140 {
141 get
142 {
143 return null;
144 }
145 }
146
147 public string ScriptEnginePath
148 {
149 get
150 {
151 return m_ScriptBasePath;
152 }
153 }
154
155 public string ScriptClassName
156 {
157 get
158 {
159 return "YEngineScript";
160 }
161 }
162
163 public string ScriptBaseClassName
164 {
165 get
166 {
167 return typeof(XMRInstance).FullName;
168 }
169 }
170
171 public ParameterInfo[] ScriptBaseClassParameters
172 {
173 get
174 {
175 return typeof(XMRInstance).GetConstructor(new Type[] { typeof(WaitHandle) }).GetParameters();
176 }
177 }
178
179 public string[] ScriptReferencedAssemblies
180 {
181 get
182 {
183 return scriptReferencedAssemblies;
184 }
185 }
186
187 public void Initialise(IConfigSource config)
188 {
189 TraceCalls("[YEngine]: Initialize entry");
190 m_ConfigSource = config;
191
192 ////foreach (IConfig icfg in config.Configs) {
193 //// m_log.Debug("[YEngine]: Initialise: configs[" + icfg.Name + "]");
194 //// foreach (string key in icfg.GetKeys ()) {
195 //// m_log.Debug("[YEngine]: Initialise: " + key + "=" + icfg.GetExpanded (key));
196 //// }
197 ////}
198
199 m_Enabled = false;
200 m_Config = config.Configs["YEngine"];
201 if(m_Config == null)
202 {
203 m_log.Info("[YEngine]: no config, assuming disabled");
204 return;
205 }
206 m_Enabled = m_Config.GetBoolean("Enabled", false);
207 m_log.InfoFormat("[YEngine]: config enabled={0}", m_Enabled);
208 if(!m_Enabled)
209 return;
210
211 m_UseSourceHashCode = m_Config.GetBoolean("UseSourceHashCode", false);
212 numThreadScriptWorkers = m_Config.GetInt("NumThreadScriptWorkers", 1);
213
214 m_TraceCalls = m_Config.GetBoolean("TraceCalls", false);
215 m_Verbose = m_Config.GetBoolean("Verbose", false);
216 m_ScriptDebug = m_Config.GetBoolean("ScriptDebug", false);
217 m_ScriptDebugSaveSource = m_Config.GetBoolean("ScriptDebugSaveSource", false);
218 m_ScriptDebugSaveIL = m_Config.GetBoolean("ScriptDebugSaveIL", false);
219
220 m_StackSize = m_Config.GetInt("ScriptStackSize", 2048) << 10;
221 m_HeapSize = m_Config.GetInt("ScriptHeapSize", 1024) << 10;
222
223 // Verify that our ScriptEventCode's match OpenSim's scriptEvent's.
224 bool err = false;
225 for(int i = 0; i < 32; i++)
226 {
227 string mycode = "undefined";
228 string oscode = "undefined";
229 try
230 {
231 mycode = ((ScriptEventCode)i).ToString();
232 Convert.ToInt32(mycode);
233 mycode = "undefined";
234 }
235 catch { }
236 try
237 {
238 oscode = ((OpenSim.Region.Framework.Scenes.scriptEvents)(1 << i)).ToString();
239 Convert.ToInt32(oscode);
240 oscode = "undefined";
241 }
242 catch { }
243 if(mycode != oscode)
244 {
245 m_log.ErrorFormat("[YEngine]: {0} mycode={1}, oscode={2}", i, mycode, oscode);
246 err = true;
247 }
248 }
249 if(err)
250 {
251 m_Enabled = false;
252 return;
253 }
254
255 m_SleepThread = StartMyThread(RunSleepThread, "Yengine sleep", ThreadPriority.Normal);
256 for(int i = 0; i < numThreadScriptWorkers; i++)
257 StartThreadWorker(i);
258
259 m_log.InfoFormat("[YEngine]: Enabled, {0}.{1} Meg (0x{2}) stacks",
260 (m_StackSize >> 20).ToString(),
261 (((m_StackSize % 0x100000) * 1000)
262 >> 20).ToString("D3"),
263 m_StackSize.ToString("X"));
264
265 m_log.InfoFormat("[YEngine]: ... {0}.{1} Meg (0x{2}) heaps",
266 (m_HeapSize >> 20).ToString(),
267 (((m_HeapSize % 0x100000) * 1000)
268 >> 20).ToString("D3"),
269 m_HeapSize.ToString("X"));
270
271 m_MaintenanceInterval = m_Config.GetInt("MaintenanceInterval", 10);
272
273 if(m_MaintenanceInterval > 0)
274 {
275 m_MaintenanceTimer = new System.Timers.Timer(m_MaintenanceInterval * 60000);
276 m_MaintenanceTimer.Elapsed += DoMaintenance;
277 m_MaintenanceTimer.Start();
278 }
279
280 MainConsole.Instance.Commands.AddCommand("yeng", false,
281 "yeng",
282 "yeng [...|help|...] ...",
283 "Run YEngine script engine commands",
284 RunTest);
285
286 TraceCalls("[YEngine]: Initialize successful");
287 }
288
289 public void AddRegion(Scene scene)
290 {
291 if(!m_Enabled)
292 return;
293
294 TraceCalls("[YEngine]: YEngine.AddRegion({0})", scene.RegionInfo.RegionName);
295
296 m_Scene = scene;
297
298 m_Scene.RegisterModuleInterface<IScriptModule>(this);
299
300 m_ScriptBasePath = m_Config.GetString("ScriptBasePath", "ScriptEngines");
301 m_ScriptBasePath = Path.Combine(m_ScriptBasePath, "Yengine");
302 m_ScriptBasePath = Path.Combine(m_ScriptBasePath, scene.RegionInfo.RegionID.ToString());
303
304 Directory.CreateDirectory(m_ScriptBasePath);
305
306 m_Scene.EventManager.OnRezScript += OnRezScript;
307
308 m_Scene.StackModuleInterface<IScriptModule>(this);
309 }
310
311 private void OneTimeLateInitialization()
312 {
313 // Build list of defined APIs and their 'this' types and define a field in XMRInstanceSuperType.
314 ApiManager am = new ApiManager();
315 Dictionary<string, Type> apiCtxTypes = new Dictionary<string, Type>();
316 foreach(string api in am.GetApis())
317 {
318 m_log.Debug("[YEngine]: adding api " + api);
319 IScriptApi scriptApi = am.CreateApi(api);
320 Type apiCtxType = scriptApi.GetType();
321 if(api == "LSL")
322 apiCtxType = typeof(XMRLSL_Api);
323 apiCtxTypes[api] = apiCtxType;
324 }
325
326 if(ScriptCodeGen.xmrInstSuperType == null) // Only create type once!
327 {
328 // Start creating type XMRInstanceSuperType that contains a field
329 // m_ApiManager_<APINAME> that points to the per-instance context
330 // struct for that API, ie, the 'this' value passed to all methods
331 // in that API. It is in essence:
332
333 // public class XMRInstanceSuperType : XMRInstance {
334 // public XMRLSL_Api m_ApiManager_LSL; // 'this' value for all ll...() functions
335 // public MOD_Api m_ApiManager_MOD; // 'this' value for all mod...() functions
336 // public OSSL_Api m_ApiManager_OSSL; // 'this' value for all os...() functions
337 // ....
338 // }
339 AssemblyName assemblyName = new AssemblyName();
340 assemblyName.Name = "XMRInstanceSuperAssembly";
341 AssemblyBuilder assemblyBuilder = Thread.GetDomain().DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
342 ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("XMRInstanceSuperModule");
343 TypeBuilder typeBuilder = moduleBuilder.DefineType("XMRInstanceSuperType", TypeAttributes.Public | TypeAttributes.Class);
344 typeBuilder.SetParent(typeof(XMRInstance));
345
346 foreach(string apiname in apiCtxTypes.Keys)
347 {
348 string fieldName = "m_ApiManager_" + apiname;
349 typeBuilder.DefineField(fieldName, apiCtxTypes[apiname], FieldAttributes.Public);
350 }
351
352 // Finalize definition of XMRInstanceSuperType.
353 // Give the compiler a short name to reference it by,
354 // otherwise it will try to use the AssemblyQualifiedName
355 // and fail miserably.
356 ScriptCodeGen.xmrInstSuperType = typeBuilder.CreateType();
357 ScriptObjWriter.DefineInternalType("xmrsuper", ScriptCodeGen.xmrInstSuperType);
358 }
359
360 // Tell the compiler about all the constants and methods for each API.
361 // We also tell the compiler how to get the per-instance context for each API
362 // by reading the corresponding m_ApiManager_<APINAME> field of XMRInstanceSuperType.
363 foreach(KeyValuePair<string, Type> kvp in apiCtxTypes)
364 {
365 // get API name and the corresponding per-instance context type
366 string api = kvp.Key;
367 Type apiCtxType = kvp.Value;
368
369 // give script compiler an abbreviated name for the API context type
370 ScriptObjWriter.DefineInternalType("apimanager_" + api, apiCtxType);
371
372 // this field tells the compiled code where the per-instance API context object is
373 // eg, for the OSSL API, it is in ((XMRInstanceSuperType)inst).m_ApiManager_OSSL
374 string fieldName = "m_ApiManager_" + api;
375 FieldInfo fieldInfo = ScriptCodeGen.xmrInstSuperType.GetField(fieldName);
376 m_XMRInstanceApiCtxFieldInfos[api] = fieldInfo;
377
378 // now tell the compiler about the constants and methods for the API
379 ScriptConst.AddInterfaceConstants(null, apiCtxType.GetFields());
380 TokenDeclInline.AddInterfaceMethods(null, apiCtxType.GetMethods(), fieldInfo);
381 }
382
383 // Add sim-specific APIs to the compiler.
384 IScriptModuleComms comms = m_Scene.RequestModuleInterface<IScriptModuleComms>();
385 if(comms != null)
386 {
387 // Add methods to list of built-in functions.
388 Delegate[] methods = comms.GetScriptInvocationList();
389 foreach(Delegate m in methods)
390 {
391 MethodInfo mi = m.Method;
392 try
393 {
394 CommsCallCodeGen cccg = new CommsCallCodeGen(mi, comms, m_XMRInstanceApiCtxFieldInfos["MOD"]);
395 Verbose("[YEngine]: added comms function " + cccg.fullName);
396 }
397 catch(Exception e)
398 {
399 m_log.Error("[YEngine]: failed to add comms function " + mi.Name);
400 m_log.Error("[YEngine]: - " + e.ToString());
401 }
402 }
403
404 // Add constants to list of built-in constants.
405 Dictionary<string, object> consts = comms.GetConstants();
406 foreach(KeyValuePair<string, object> kvp in consts)
407 {
408 try
409 {
410 ScriptConst sc = ScriptConst.AddConstant(kvp.Key, kvp.Value);
411 Verbose("[YEngine]: added comms constant " + sc.name);
412 }
413 catch(Exception e)
414 {
415 m_log.Error("[YEngine]: failed to add comms constant " + kvp.Key);
416 m_log.Error("[YEngine]: - " + e.Message);
417 }
418 }
419 }
420 else
421 {
422 Verbose("[YEngine]: comms not enabled");
423 }
424 }
425
426 /**
427 * @brief Generate code for the calls to the comms functions.
428 * It is a tRUlY EvIL interface.
429 * To call the function we must call an XMRInstanceSuperType.m_ApiManager_MOD.modInvoker?()
430 * method passing it the name of the function as a string and the script
431 * argument list wrapped up in an object[] array. The modInvoker?() methods
432 * do some sick type conversions (with corresponding mallocs) so we can't
433 * call the methods directly.
434 */
435 private class CommsCallCodeGen: TokenDeclInline
436 {
437 private static Type[] modInvokerArgTypes = new Type[] { typeof(string), typeof(object[]) };
438 public static FieldInfo xmrInstModApiCtxField;
439
440 private MethodInfo modInvokerMeth;
441 private string methName;
442
443 /**
444 * @brief Constructor
445 * @param mi = method to make available to scripts
446 * mi.Name = name that is used by scripts
447 * mi.GetParameters() = parameter list as defined by module
448 * includes the 'UUID host','UUID script' parameters that script does not see
449 * allowed types for script-visible parameters are as follows:
450 * Single -> float
451 * Int32 -> integer
452 * OpenMetaverse.UUID -> key
453 * Object[] -> list
454 * OpenMetaverse.Quaternion -> rotation
455 * String -> string
456 * OpenMetaverse.Vector3 -> vector
457 * mi.ReturnType = return type as defined by module
458 * types are same as allowed for parameters
459 * @param comms = comms module the method came from
460 * @param apictxfi = what field in XMRInstanceSuperType the 'this' value is for this method
461 */
462 public CommsCallCodeGen(MethodInfo mi, IScriptModuleComms comms, FieldInfo apictxfi)
463 : base(null, false, NameArgSig(mi), RetType(mi))
464 {
465 methName = mi.Name;
466 string modInvokerName = comms.LookupModInvocation(methName);
467 if(modInvokerName == null)
468 throw new Exception("cannot find comms method " + methName);
469 modInvokerMeth = typeof(MOD_Api).GetMethod(modInvokerName, modInvokerArgTypes);
470 xmrInstModApiCtxField = apictxfi;
471 }
472
473 // script-visible name(argtype,...) signature string
474 private static string NameArgSig(MethodInfo mi)
475 {
476 StringBuilder sb = new StringBuilder();
477 sb.Append(mi.Name);
478 sb.Append('(');
479 ParameterInfo[] mps = mi.GetParameters();
480 for(int i = 2; i < mps.Length; i++)
481 {
482 ParameterInfo pi = mps[i];
483 if(i > 2)
484 sb.Append(',');
485 sb.Append(ParamType(pi.ParameterType));
486 }
487 sb.Append(')');
488 return sb.ToString();
489 }
490
491 // script-visible return type
492 // note that although we support void, the comms stuff does not
493 private static TokenType RetType(MethodInfo mi)
494 {
495 Type rt = mi.ReturnType;
496 if(rt == typeof(float))
497 return new TokenTypeFloat(null);
498 if(rt == typeof(int))
499 return new TokenTypeInt(null);
500 if(rt == typeof(object[]))
501 return new TokenTypeList(null);
502 if(rt == typeof(OpenMetaverse.UUID))
503 return new TokenTypeKey(null);
504 if(rt == typeof(OpenMetaverse.Quaternion))
505 return new TokenTypeRot(null);
506 if(rt == typeof(string))
507 return new TokenTypeStr(null);
508 if(rt == typeof(OpenMetaverse.Vector3))
509 return new TokenTypeVec(null);
510 if(rt == null || rt == typeof(void))
511 return new TokenTypeVoid(null);
512 throw new Exception("unsupported return type " + rt.Name);
513 }
514
515 // script-visible parameter type
516 private static string ParamType(Type t)
517 {
518 if(t == typeof(float))
519 return "float";
520 if(t == typeof(int))
521 return "integer";
522 if(t == typeof(OpenMetaverse.UUID))
523 return "key";
524 if(t == typeof(object[]))
525 return "list";
526 if(t == typeof(OpenMetaverse.Quaternion))
527 return "rotation";
528 if(t == typeof(string))
529 return "string";
530 if(t == typeof(OpenMetaverse.Vector3))
531 return "vector";
532 throw new Exception("unsupported parameter type " + t.Name);
533 }
534
535 /**
536 * @brief Called by the compiler to generate a call to the comms function.
537 * @param scg = which script is being compiled
538 * @param errorAt = where in the source code the call is being made (for error messages)
539 * @param result = a temp location to put the return value in if any
540 * @param args = array of script-visible arguments being passed to the function
541 */
542 public override void CodeGen(ScriptCodeGen scg, Token errorAt, CompValuTemp result, CompValu[] args)
543 {
544 // Set up 'this' pointer for modInvoker?() = value from ApiManager.CreateApi("MOD").
545 scg.PushXMRInst();
546 scg.ilGen.Emit(errorAt, OpCodes.Castclass, xmrInstModApiCtxField.DeclaringType);
547 scg.ilGen.Emit(errorAt, OpCodes.Ldfld, xmrInstModApiCtxField);
548
549 // Set up 'fname' argument to modInvoker?() = name of the function to be called.
550 scg.ilGen.Emit(errorAt, OpCodes.Ldstr, methName);
551
552 // Set up 'parms' argument to modInvoker?() = object[] of the script-visible parameters,
553 // in their LSL-wrapped form. Of course, the modInvoker?() method will malloc yet another
554 // object[] and type-convert these parameters one-by-one with another round of unwrapping
555 // and wrapping.
556 // Types allowed in this object[]:
557 // LSL_Float, LSL_Integer, LSL_Key, LSL_List, LSL_Rotation, LSL_String, LSL_Vector
558 int nargs = args.Length;
559 scg.ilGen.Emit(errorAt, OpCodes.Ldc_I4, nargs);
560 scg.ilGen.Emit(errorAt, OpCodes.Newarr, typeof(object));
561
562 for(int i = 0; i < nargs; i++)
563 {
564 scg.ilGen.Emit(errorAt, OpCodes.Dup);
565 scg.ilGen.Emit(errorAt, OpCodes.Ldc_I4, i);
566
567 // get location and type of argument
568 CompValu arg = args[i];
569 TokenType argtype = arg.type;
570
571 // if already in a form acceptable to modInvoker?(),
572 // just push it to the stack and convert to object
573 // by boxing it if necessary
574
575 // but if something like a double, int, string, etc
576 // push to stack converting to the LSL-wrapped type
577 // then convert to object by boxing if necessary
578
579 Type boxit = null;
580 if(argtype is TokenTypeLSLFloat)
581 {
582 args[i].PushVal(scg, errorAt);
583 boxit = typeof(LSL_Float);
584 }
585 else if(argtype is TokenTypeLSLInt)
586 {
587 args[i].PushVal(scg, errorAt);
588 boxit = typeof(LSL_Integer);
589 }
590 else if(argtype is TokenTypeLSLKey)
591 {
592 args[i].PushVal(scg, errorAt);
593 boxit = typeof(LSL_Key);
594 }
595 else if(argtype is TokenTypeList)
596 {
597 args[i].PushVal(scg, errorAt);
598 boxit = typeof(LSL_List);
599 }
600 else if(argtype is TokenTypeRot)
601 {
602 args[i].PushVal(scg, errorAt);
603 boxit = typeof(LSL_Rotation);
604 }
605 else if(argtype is TokenTypeLSLString)
606 {
607 args[i].PushVal(scg, errorAt);
608 boxit = typeof(LSL_String);
609 }
610 else if(argtype is TokenTypeVec)
611 {
612 args[i].PushVal(scg, errorAt);
613 boxit = typeof(LSL_Vector);
614 }
615 else if(argtype is TokenTypeFloat)
616 {
617 args[i].PushVal(scg, errorAt, new TokenTypeLSLFloat(argtype));
618 boxit = typeof(LSL_Float);
619 }
620 else if(argtype is TokenTypeInt)
621 {
622 args[i].PushVal(scg, errorAt, new TokenTypeLSLInt(argtype));
623 boxit = typeof(LSL_Integer);
624 }
625 else if(argtype is TokenTypeKey)
626 {
627 args[i].PushVal(scg, errorAt, new TokenTypeLSLKey(argtype));
628 boxit = typeof(LSL_Key);
629 }
630 else if(argtype is TokenTypeStr)
631 {
632 args[i].PushVal(scg, errorAt, new TokenTypeLSLString(argtype));
633 boxit = typeof(LSL_String);
634 }
635 else
636 throw new Exception("unsupported arg type " + argtype.GetType().Name);
637
638 if(boxit.IsValueType)
639 scg.ilGen.Emit(errorAt, OpCodes.Box, boxit);
640
641 // pop the object into the object[]
642 scg.ilGen.Emit(errorAt, OpCodes.Stelem, typeof(object));
643 }
644
645 // Call the modInvoker?() method.
646 // It leaves an LSL-wrapped type on the stack.
647 if(modInvokerMeth.IsVirtual)
648 scg.ilGen.Emit(errorAt, OpCodes.Callvirt, modInvokerMeth);
649 else
650 scg.ilGen.Emit(errorAt, OpCodes.Call, modInvokerMeth);
651
652
653 // The 3rd arg to Pop() is the type on the stack,
654 // ie, what modInvoker?() actually returns.
655 // The Pop() method will wrap/unwrap as needed.
656 Type retSysType = modInvokerMeth.ReturnType;
657 if(retSysType == null)
658 retSysType = typeof(void);
659 TokenType retTokType = TokenType.FromSysType(errorAt, retSysType);
660 result.Pop(scg, errorAt, retTokType);
661 }
662 }
663
664 /**
665 * @brief Called late in shutdown procedure,
666 * after the 'Shutting down..." message.
667 */
668 public void RemoveRegion(Scene scene)
669 {
670 if(!m_Enabled)
671 return;
672
673 TraceCalls("[YEngine]: YEngine.RemoveRegion({0})", scene.RegionInfo.RegionName);
674
675 if(m_MaintenanceTimer != null)
676 {
677 m_MaintenanceTimer.Stop();
678 m_MaintenanceTimer.Dispose();
679 }
680
681 // Write script states out to .state files so it will be
682 // available when the region is restarted.
683 DoMaintenance(null, null);
684
685 // Stop executing script threads and wait for final
686 // one to finish (ie, script gets to CheckRun() call).
687 m_Exiting = true;
688 if(m_SleepThread != null)
689 {
690 lock(m_SleepQueue)
691 Monitor.PulseAll(m_SleepQueue);
692
693 if(!m_SleepThread.Join(250))
694 m_SleepThread.Abort();
695 m_SleepThread = null;
696 }
697
698 StopThreadWorkers();
699
700 m_Scene.EventManager.OnFrame -= OnFrame;
701 m_Scene.EventManager.OnRezScript -= OnRezScript;
702 m_Scene.EventManager.OnRemoveScript -= OnRemoveScript;
703 m_Scene.EventManager.OnScriptReset -= OnScriptReset;
704 m_Scene.EventManager.OnStartScript -= OnStartScript;
705 m_Scene.EventManager.OnStopScript -= OnStopScript;
706 m_Scene.EventManager.OnGetScriptRunning -= OnGetScriptRunning;
707 m_Scene.EventManager.OnShutdown -= OnShutdown;
708
709 m_Enabled = false;
710 m_Scene = null;
711 }
712
713 public void RegionLoaded(Scene scene)
714 {
715 if(!m_Enabled)
716 return;
717
718 TraceCalls("[YEngine]: YEngine.RegionLoaded({0})", scene.RegionInfo.RegionName);
719
720 m_Scene.EventManager.OnFrame += OnFrame;
721 m_Scene.EventManager.OnRemoveScript += OnRemoveScript;
722 m_Scene.EventManager.OnScriptReset += OnScriptReset;
723 m_Scene.EventManager.OnStartScript += OnStartScript;
724 m_Scene.EventManager.OnStopScript += OnStopScript;
725 m_Scene.EventManager.OnGetScriptRunning += OnGetScriptRunning;
726 m_Scene.EventManager.OnShutdown += OnShutdown;
727
728 InitEvents();
729 }
730
731 public void StartProcessing()
732 {
733 m_log.Debug("[YEngine]: StartProcessing entry");
734 m_StartProcessing = true;
735 ResumeThreads();
736 m_log.Debug("[YEngine]: StartProcessing return");
737 m_Scene.EventManager.TriggerEmptyScriptCompileQueue(0, "");
738 }
739
740 public void Close()
741 {
742 TraceCalls("[YEngine]: YEngine.Close()");
743 }
744
745 private void RunTest(string module, string[] args)
746 {
747 if(args.Length < 2)
748 {
749 m_log.Info("[YEngine]: missing command, try 'yeng help'");
750 return;
751 }
752
753 m_log.Info("[YEngine]: " + m_Scene.RegionInfo.RegionName);
754
755 switch(args[1])
756 {
757 case "cvv":
758 m_log.InfoFormat("[YEngine]: compiled version value = {0}",
759 ScriptCodeGen.COMPILED_VERSION_VALUE);
760 break;
761
762 case "help":
763 case "?":
764 m_log.Info("[YEngine]: yeng reset [-help ...]");
765 m_log.Info("[YEngine]: yeng resume - resume script processing");
766 m_log.Info("[YEngine]: yeng suspend - suspend script processing");
767 m_log.Info("[YEngine]: yeng ls [-help ...]");
768 m_log.Info("[YEngine]: yeng cvv - show compiler version value");
769 m_log.Info("[YEngine]: yeng mvv [<newvalue>] - show migration version value");
770 m_log.Info("[YEngine]: yeng tracecalls [yes | no]");
771 m_log.Info("[YEngine]: yeng verbose [yes | no]");
772 m_log.Info("[YEngine]: yeng pev [-help ...] - post event");
773 break;
774
775 case "ls":
776 XmrTestLs(args, 2);
777 break;
778
779 case "mvv":
780 m_log.InfoFormat("[YEngine]: migration version value = {0}",
781 XMRInstance.migrationVersion);
782 break;
783
784 case "pev":
785 XmrTestPev(args, 2);
786 break;
787
788 case "reset":
789 XmrTestReset(args, 2);
790 break;
791
792 case "resume":
793 m_log.Info("[YEngine]: resuming scripts");
794 ResumeThreads();
795 break;
796
797 case "suspend":
798 m_log.Info("[YEngine]: suspending scripts");
799 SuspendThreads();
800 break;
801
802 case "tracecalls":
803 if(args.Length > 2)
804 m_TraceCalls = (args[2][0] & 1) != 0;
805 m_log.Info("[YEngine]: tracecalls " + (m_TraceCalls ? "yes" : "no"));
806 break;
807
808 case "verbose":
809 if(args.Length > 2)
810 m_Verbose = (args[2][0] & 1) != 0;
811 m_log.Info("[YEngine]: verbose " + (m_Verbose ? "yes" : "no"));
812 break;
813
814 default:
815 m_log.Error("[YEngine]: unknown command " + args[1] + ", try 'yeng help'");
816 break;
817 }
818 }
819
820 // Not required when not using IScriptInstance
821 //
822 public IScriptWorkItem QueueEventHandler(object parms)
823 {
824 return null;
825 }
826
827 public Scene World
828 {
829 get
830 {
831 return m_Scene;
832 }
833 }
834
835 public IScriptModule ScriptModule
836 {
837 get
838 {
839 return this;
840 }
841 }
842
843 public void SaveAllState()
844 {
845 m_log.Error("[YEngine]: YEngine.SaveAllState() called!!");
846 }
847
848#pragma warning disable 0067
849 public event ScriptRemoved OnScriptRemoved;
850 public event ObjectRemoved OnObjectRemoved;
851#pragma warning restore 0067
852
853 // Events targeted at a specific script
854 // ... like listen() for an llListen() call
855 //
856 public bool PostScriptEvent(UUID itemID, EventParams parms)
857 {
858 XMRInstance instance = GetInstance(itemID);
859 if(instance == null)
860 return false;
861
862 TraceCalls("[YEngine]: YEngine.PostScriptEvent({0},{1})", itemID.ToString(), parms.EventName);
863
864 instance.PostEvent(parms);
865 return true;
866 }
867
868 // Events targeted at all scripts in the given prim.
869 // localID = which prim
870 // parms = event to post
871 //
872 public bool PostObjectEvent(uint localID, EventParams parms)
873 {
874 SceneObjectPart part = m_Scene.GetSceneObjectPart(localID);
875
876 if(part == null)
877 return false;
878
879 TraceCalls("[YEngine]: YEngine.PostObjectEvent({0},{1})", localID.ToString(), parms.EventName);
880
881 // In SecondLife, attach events go to all scripts of all prims
882 // in a linked object. So here we duplicate that functionality,
883 // as all we ever get is a single attach event for the whole
884 // object.
885 if(parms.EventName == "attach")
886 {
887 bool posted = false;
888 foreach(SceneObjectPart primpart in part.ParentGroup.Parts)
889 posted |= PostPrimEvent(primpart, parms);
890
891 return posted;
892 }
893
894 // Other events go to just the scripts in that prim.
895 return PostPrimEvent(part, parms);
896 }
897
898 private bool PostPrimEvent(SceneObjectPart part, EventParams parms)
899 {
900 UUID partUUID = part.UUID;
901
902 // Get list of script instances running in the object.
903 XMRInstance[] objInstArray;
904 lock(m_InstancesDict)
905 {
906 if(!m_ObjectInstArray.TryGetValue(partUUID, out objInstArray))
907 return false;
908
909 if(objInstArray == null)
910 {
911 objInstArray = RebuildObjectInstArray(partUUID);
912 m_ObjectInstArray[partUUID] = objInstArray;
913 }
914 }
915
916 // Post event to all script instances in the object.
917 if(objInstArray.Length <= 0)
918 return false;
919 foreach(XMRInstance inst in objInstArray)
920 inst.PostEvent(parms);
921
922 return true;
923 }
924
925 public DetectParams GetDetectParams(UUID itemID, int number)
926 {
927 XMRInstance instance = GetInstance(itemID);
928 if(instance == null)
929 return null;
930 return instance.GetDetectParams(number);
931 }
932
933 public void SetMinEventDelay(UUID itemID, double delay)
934 {
935 }
936
937 public int GetStartParameter(UUID itemID)
938 {
939 XMRInstance instance = GetInstance(itemID);
940 if(instance == null)
941 return 0;
942 return instance.StartParam;
943 }
944
945 // This is the "set running" method
946 //
947 public void SetScriptState(UUID itemID, bool state, bool self)
948 {
949 SetScriptState(itemID, state);
950 }
951 public void SetScriptState(UUID itemID, bool state)
952 {
953 XMRInstance instance = GetInstance(itemID);
954 if(instance != null)
955 instance.Running = state;
956 }
957
958 // Control display of the "running" checkbox
959 //
960 public bool GetScriptState(UUID itemID)
961 {
962 XMRInstance instance = GetInstance(itemID);
963 if(instance == null)
964 return false;
965 return instance.Running;
966 }
967
968 public void SetState(UUID itemID, string newState)
969 {
970 TraceCalls("[YEngine]: YEngine.SetState({0},{1})", itemID.ToString(), newState);
971 }
972
973 public void ApiResetScript(UUID itemID)
974 {
975 XMRInstance instance = GetInstance(itemID);
976 if(instance != null)
977 instance.ApiReset();
978
979 }
980
981 public void ResetScript(UUID itemID)
982 {
983 XMRInstance instance = GetInstance(itemID);
984 if(instance != null)
985 {
986 IUrlModule urlModule = m_Scene.RequestModuleInterface<IUrlModule>();
987 if(urlModule != null)
988 urlModule.ScriptRemoved(itemID);
989
990 instance.Reset();
991 }
992 }
993
994 public IConfig Config
995 {
996 get
997 {
998 return m_Config;
999 }
1000 }
1001
1002 public IConfigSource ConfigSource
1003 {
1004 get
1005 {
1006 return m_ConfigSource;
1007 }
1008 }
1009
1010 public string ScriptEngineName
1011 {
1012 get
1013 {
1014 return "YEngine";
1015 }
1016 }
1017
1018 public IScriptApi GetApi(UUID itemID, string name)
1019 {
1020 FieldInfo fi;
1021 if(!m_XMRInstanceApiCtxFieldInfos.TryGetValue(name, out fi))
1022 return null;
1023 XMRInstance inst = GetInstance(itemID);
1024 if(inst == null)
1025 return null;
1026 return (IScriptApi)fi.GetValue(inst);
1027 }
1028
1029 /**
1030 * @brief Get script's current state as an XML string
1031 * - called by "Take", "Take Copy" and when object deleted (ie, moved to Trash)
1032 * This includes the .state file
1033 */
1034 public string GetXMLState(UUID itemID)
1035 {
1036 XMRInstance instance = GetInstance(itemID);
1037 if(instance == null)
1038 return String.Empty;
1039
1040 TraceCalls("[YEngine]: YEngine.GetXMLState({0})", itemID.ToString());
1041
1042 if(!instance.m_HasRun)
1043 return String.Empty;
1044
1045 XmlDocument doc = new XmlDocument();
1046
1047 /*
1048 * Set up <State Engine="YEngine" UUID="itemID" Asset="assetID"> tag.
1049 */
1050 XmlElement stateN = doc.CreateElement("", "State", "");
1051 doc.AppendChild(stateN);
1052
1053 XmlAttribute engineA = doc.CreateAttribute("", "Engine", "");
1054 engineA.Value = ScriptEngineName;
1055 stateN.Attributes.Append(engineA);
1056
1057 XmlAttribute uuidA = doc.CreateAttribute("", "UUID", "");
1058 uuidA.Value = itemID.ToString();
1059 stateN.Attributes.Append(uuidA);
1060
1061 XmlAttribute assetA = doc.CreateAttribute("", "Asset", "");
1062 string assetID = instance.AssetID.ToString();
1063 assetA.Value = assetID;
1064 stateN.Attributes.Append(assetA);
1065
1066 // Get <ScriptState>...</ScriptState> item that hold's script's state.
1067 // This suspends the script if necessary then takes a snapshot.
1068 XmlElement scriptStateN = instance.GetExecutionState(doc);
1069 stateN.AppendChild(scriptStateN);
1070
1071 return doc.OuterXml;
1072 }
1073
1074 // Set script's current state from an XML string
1075 // - called just before a script is instantiated
1076 // So we write the .state file so the .state file will be seen when
1077 // the script is instantiated.
1078 public bool SetXMLState(UUID itemID, string xml)
1079 {
1080 XmlDocument doc = new XmlDocument();
1081
1082 try
1083 {
1084 doc.LoadXml(xml);
1085 }
1086 catch
1087 {
1088 return false;
1089 }
1090 TraceCalls("[YEngine]: YEngine.SetXMLState({0})", itemID.ToString());
1091
1092 // Make sure <State Engine="YEngine"> so we know it is in our
1093 // format.
1094 XmlElement stateN = (XmlElement)doc.SelectSingleNode("State");
1095 if(stateN == null)
1096 return false;
1097
1098 if(stateN.GetAttribute("Engine") != ScriptEngineName)
1099 return false;
1100
1101 // <ScriptState>...</ScriptState> contains contents of .state file.
1102 XmlElement scriptStateN = (XmlElement)stateN.SelectSingleNode("ScriptState");
1103 if(scriptStateN == null)
1104 return false;
1105
1106 string sen = stateN.GetAttribute("Engine");
1107 if((sen == null) || (sen != ScriptEngineName))
1108 return false;
1109
1110 XmlAttribute assetA = doc.CreateAttribute("", "Asset", "");
1111 assetA.Value = stateN.GetAttribute("Asset");
1112 scriptStateN.Attributes.Append(assetA);
1113
1114 // Write out the .state file with the <ScriptState ...>...</ScriptState> XML text
1115 string statePath = XMRInstance.GetStateFileName(m_ScriptBasePath, itemID);
1116 FileStream ss = File.Create(statePath);
1117 StreamWriter sw = new StreamWriter(ss);
1118 sw.Write(scriptStateN.OuterXml);
1119 sw.Close();
1120 ss.Close();
1121
1122 return true;
1123 }
1124
1125 public bool PostScriptEvent(UUID itemID, string name, Object[] p)
1126 {
1127 if(!m_Enabled)
1128 return false;
1129
1130 TraceCalls("[YEngine]: YEngine.PostScriptEvent({0},{1})", itemID.ToString(), name);
1131
1132 return PostScriptEvent(itemID, new EventParams(name, p, zeroDetectParams));
1133 }
1134
1135 public bool PostObjectEvent(UUID itemID, string name, Object[] p)
1136 {
1137 if(!m_Enabled)
1138 return false;
1139
1140 TraceCalls("[YEngine]: YEngine.PostObjectEvent({0},{1})", itemID.ToString(), name);
1141
1142 SceneObjectPart part = m_Scene.GetSceneObjectPart(itemID);
1143 if(part == null)
1144 return false;
1145
1146 return PostObjectEvent(part.LocalId, new EventParams(name, p, zeroDetectParams));
1147 }
1148
1149 // about the 3523rd entrypoint for a script to put itself to sleep
1150 public void SleepScript(UUID itemID, int delay)
1151 {
1152 XMRInstance instance = GetInstance(itemID);
1153 if(instance != null)
1154 instance.Sleep(delay);
1155 }
1156
1157 // Get a script instance loaded, compiling it if necessary
1158 //
1159 // localID = the object as a whole, may contain many scripts
1160 // itemID = this instance of the script in this object
1161 // script = script source code
1162 // startParam = value passed to 'on_rez' event handler
1163 // postOnRez = true to post an 'on_rez' event to script on load
1164 // defEngine = default script engine
1165 // stateSource = post this event to script on load
1166
1167 public void OnRezScript(uint localID, UUID itemID, string script,
1168 int startParam, bool postOnRez, string defEngine, int stateSource)
1169 {
1170 SceneObjectPart part = m_Scene.GetSceneObjectPart(localID);
1171 TaskInventoryItem item = part.Inventory.GetInventoryItem(itemID);
1172
1173 if(!m_LateInit)
1174 {
1175 m_LateInit = true;
1176 OneTimeLateInitialization();
1177 }
1178
1179 TraceCalls("[YEngine]: OnRezScript(...,{0},...)", itemID.ToString());
1180
1181 // Assume script uses the default engine, whatever that is.
1182 string engineName = defEngine;
1183
1184 // Very first line might contain "//" scriptengine ":".
1185 string firstline = "";
1186 if(script.StartsWith("//"))
1187 {
1188 int lineEnd = script.IndexOf('\n');
1189 if(lineEnd > 1)
1190 firstline = script.Substring(0, lineEnd).Trim();
1191 int colon = firstline.IndexOf(':');
1192 if(colon >= 2)
1193 {
1194 engineName = firstline.Substring(2, colon - 2).Trim();
1195 if(engineName == "")
1196 engineName = defEngine;
1197 }
1198 }
1199
1200 // Make sure the default or requested engine is us.
1201 if(engineName != ScriptEngineName)
1202 {
1203 // Not us, if requested engine exists, silently ignore script and let
1204 // requested engine handle it.
1205 IScriptModule[] engines = m_Scene.RequestModuleInterfaces<IScriptModule>();
1206 foreach(IScriptModule eng in engines)
1207 {
1208 if(eng.ScriptEngineName == engineName)
1209 return;
1210 }
1211
1212 // Requested engine not defined, warn on console.
1213 // Then we try to handle it if we're the default engine, else we ignore it.
1214 m_log.Warn("[YEngine]: " + itemID.ToString() + " requests undefined/disabled engine " + engineName);
1215 m_log.Info("[YEngine]: - " + part.GetWorldPosition());
1216 m_log.Info("[YEngine]: first line: " + firstline);
1217 if(defEngine != ScriptEngineName)
1218 {
1219 m_log.Info("[YEngine]: leaving it to the default script engine (" + defEngine + ") to process it");
1220 return;
1221 }
1222 m_log.Info("[YEngine]: will attempt to processing it anyway as default script engine");
1223 }
1224
1225 // Put on object/instance lists.
1226 XMRInstance instance = (XMRInstance)Activator.CreateInstance(ScriptCodeGen.xmrInstSuperType);
1227 instance.m_LocalID = localID;
1228 instance.m_ItemID = itemID;
1229 instance.m_SourceCode = script;
1230 instance.m_StartParam = startParam;
1231 instance.m_PostOnRez = postOnRez;
1232 instance.m_StateSource = (StateSource)stateSource;
1233 instance.m_Part = part;
1234 instance.m_PartUUID = part.UUID;
1235 instance.m_Item = item;
1236 instance.m_DescName = part.Name + ":" + item.Name;
1237 instance.m_IState = XMRInstState.CONSTRUCT;
1238
1239 lock(m_InstancesDict)
1240 {
1241 m_LockedDict = "RegisterInstance";
1242
1243 // Insert on internal list of all scripts being handled by this engine instance.
1244 m_InstancesDict[instance.m_ItemID] = instance;
1245
1246 // Insert on internal list of all scripts being handled by this engine instance
1247 // that are part of the object.
1248 List<UUID> itemIDList;
1249 if(!m_ObjectItemList.TryGetValue(instance.m_PartUUID, out itemIDList))
1250 {
1251 itemIDList = new List<UUID>();
1252 m_ObjectItemList[instance.m_PartUUID] = itemIDList;
1253 }
1254 if(!itemIDList.Contains(instance.m_ItemID))
1255 {
1256 itemIDList.Add(instance.m_ItemID);
1257 m_ObjectInstArray[instance.m_PartUUID] = null;
1258 }
1259
1260 m_LockedDict = "~RegisterInstance";
1261 }
1262
1263 // Compile and load it.
1264 lock(m_ScriptErrors)
1265 m_ScriptErrors.Remove(instance.m_ItemID);
1266
1267 LoadThreadWork(instance);
1268 }
1269
1270 /**
1271 * @brief This routine instantiates one script.
1272 */
1273 private void LoadThreadWork(XMRInstance instance)
1274 {
1275 // Compile and load the script in memory.
1276 ArrayList errors = new ArrayList();
1277 Exception initerr = null;
1278 try
1279 {
1280 instance.Initialize(this, m_ScriptBasePath, m_StackSize, m_HeapSize, errors);
1281 }
1282 catch(Exception e1)
1283 {
1284 initerr = e1;
1285 }
1286 if(initerr != null && !instance.m_ForceRecomp && initerr is CVVMismatchException)
1287 {
1288 UUID itemID = instance.m_ItemID;
1289 Verbose("[YEngine]: {0}/{2} first load failed ({1}), retrying after recompile",
1290 itemID.ToString(), initerr.Message, instance.m_Item.AssetID.ToString());
1291 Verbose("[YEngine]:\n{0}", initerr.ToString());
1292 initerr = null;
1293 errors = new ArrayList();
1294 instance.m_ForceRecomp = true;
1295 try
1296 {
1297 instance.Initialize(this, m_ScriptBasePath, m_StackSize, m_HeapSize, errors);
1298 }
1299 catch(Exception e2)
1300 {
1301 initerr = e2;
1302 }
1303 }
1304 if(initerr != null)
1305 {
1306 UUID itemID = instance.m_ItemID;
1307 Verbose("[YEngine]: Error starting script {0}/{2}: {1}",
1308 itemID.ToString(), initerr.Message, instance.m_Item.AssetID.ToString());
1309 if(initerr.Message != "compilation errors")
1310 {
1311 Verbose("[YEngine]: - " + instance.m_Part.GetWorldPosition() + " " + instance.m_DescName);
1312 Verbose("[YEngine]: exception:\n{0}", initerr.ToString());
1313 }
1314
1315 OnRemoveScript(0, itemID);
1316
1317 // Post errors where GetScriptErrors() can see them.
1318 if(errors.Count == 0)
1319 errors.Add(initerr.Message);
1320 else
1321 {
1322 foreach(Object err in errors)
1323 {
1324 if(m_ScriptDebug)
1325 m_log.DebugFormat("[YEngine]: {0}", err.ToString());
1326 }
1327 }
1328 lock(m_ScriptErrors)
1329 m_ScriptErrors[instance.m_ItemID] = errors;
1330
1331 return;
1332 }
1333
1334 // Tell GetScriptErrors() that we have finished compiling/loading
1335 // successfully (by posting a 0 element array).
1336 lock(m_ScriptErrors)
1337 {
1338 if(instance.m_IState != XMRInstState.CONSTRUCT)
1339 throw new Exception("bad state");
1340 m_ScriptErrors[instance.m_ItemID] = noScriptErrors;
1341 }
1342
1343 // Transition from CONSTRUCT->ONSTARTQ and give to RunScriptThread().
1344 // Put it on the start queue so it will run any queued event handlers,
1345 // such as state_entry() or on_rez(). If there aren't any queued, it
1346 // will just go to idle state when RunOne() tries to dequeue an event.
1347 lock(instance.m_QueueLock)
1348 {
1349 if(instance.m_IState != XMRInstState.CONSTRUCT)
1350 throw new Exception("bad state");
1351 instance.m_IState = XMRInstState.ONSTARTQ;
1352 if(!instance.m_Running)
1353 instance.EmptyEventQueues();
1354 }
1355 QueueToStart(instance);
1356 }
1357
1358 public void OnRemoveScript(uint localID, UUID itemID)
1359 {
1360 TraceCalls("[YEngine]: OnRemoveScript(...,{0})", itemID.ToString());
1361
1362 // Remove from our list of known scripts.
1363 // After this, no more events can queue because we won't be
1364 // able to translate the itemID to an XMRInstance pointer.
1365 XMRInstance instance = null;
1366 lock(m_InstancesDict)
1367 {
1368 m_LockedDict = "OnRemoveScript:" + itemID.ToString();
1369
1370 // Tell the instance to free off everything it can.
1371 if(!m_InstancesDict.TryGetValue(itemID, out instance))
1372 {
1373 m_LockedDict = "~OnRemoveScript";
1374 return;
1375 }
1376
1377 // Tell it to stop executing anything.
1378 instance.suspendOnCheckRunHold = true;
1379
1380 // Remove it from our list of known script instances
1381 // mostly so no more events can queue to it.
1382 m_InstancesDict.Remove(itemID);
1383
1384 List<UUID> itemIDList;
1385 if(m_ObjectItemList.TryGetValue(instance.m_PartUUID, out itemIDList))
1386 {
1387 itemIDList.Remove(itemID);
1388 if(itemIDList.Count == 0)
1389 {
1390 m_ObjectItemList.Remove(instance.m_PartUUID);
1391 m_ObjectInstArray.Remove(instance.m_PartUUID);
1392 }
1393 else
1394 m_ObjectInstArray[instance.m_PartUUID] = null;
1395 }
1396
1397 // Delete the .state file as any needed contents were fetched with GetXMLState()
1398 // and stored on the database server.
1399 string stateFileName = XMRInstance.GetStateFileName(m_ScriptBasePath, itemID);
1400 File.Delete(stateFileName);
1401
1402 ScriptRemoved handlerScriptRemoved = OnScriptRemoved;
1403 if(handlerScriptRemoved != null)
1404 handlerScriptRemoved(itemID);
1405
1406 m_LockedDict = "~~OnRemoveScript";
1407 }
1408
1409 // Free off its stack and fun things like that.
1410 // If it is running, abort it.
1411 instance.Dispose();
1412 }
1413
1414 public void OnScriptReset(uint localID, UUID itemID)
1415 {
1416 TraceCalls("[YEngine]: YEngine.OnScriptReset({0},{1})", localID.ToString(), itemID.ToString());
1417 ResetScript(itemID);
1418 }
1419
1420 public void OnStartScript(uint localID, UUID itemID)
1421 {
1422 XMRInstance instance = GetInstance(itemID);
1423 if(instance != null)
1424 instance.Running = true;
1425 }
1426
1427 public void OnStopScript(uint localID, UUID itemID)
1428 {
1429 XMRInstance instance = GetInstance(itemID);
1430 if(instance != null)
1431 instance.Running = false;
1432 }
1433
1434 public void OnGetScriptRunning(IClientAPI controllingClient,
1435 UUID objectID, UUID itemID)
1436 {
1437 XMRInstance instance = GetInstance(itemID);
1438 if(instance != null)
1439 {
1440 TraceCalls("[YEngine]: YEngine.OnGetScriptRunning({0},{1})", objectID.ToString(), itemID.ToString());
1441
1442 IEventQueue eq = World.RequestModuleInterface<IEventQueue>();
1443 if(eq == null)
1444 {
1445 controllingClient.SendScriptRunningReply(objectID, itemID,
1446 instance.Running);
1447 }
1448 else
1449 {
1450 eq.Enqueue(EventQueueHelper.ScriptRunningReplyEvent(objectID,
1451 itemID, instance.Running, true),
1452 controllingClient.AgentId);
1453 }
1454 }
1455 }
1456
1457 public bool HasScript(UUID itemID, out bool running)
1458 {
1459 XMRInstance instance = GetInstance(itemID);
1460 if(instance == null)
1461 {
1462 running = true;
1463 return false;
1464 }
1465 running = instance.Running;
1466 return true;
1467 }
1468
1469 /**
1470 * @brief Called once per frame update to see if scripts have
1471 * any such work to do.
1472 */
1473 private void OnFrame()
1474 {
1475 if(m_FrameUpdateList != null)
1476 {
1477 ThreadStart frameupdates;
1478 lock(m_FrameUpdateLock)
1479 {
1480 frameupdates = m_FrameUpdateList;
1481 m_FrameUpdateList = null;
1482 }
1483 frameupdates();
1484 }
1485 }
1486
1487 /**
1488 * @brief Add a one-shot delegate to list of things to do
1489 * synchronized with frame updates.
1490 */
1491 public void AddOnFrameUpdate(ThreadStart thunk)
1492 {
1493 lock(m_FrameUpdateLock)
1494 m_FrameUpdateList += thunk;
1495 }
1496
1497 /**
1498 * @brief Gets called early as part of shutdown,
1499 * right after "Persisting changed objects" message.
1500 */
1501 public void OnShutdown()
1502 {
1503 TraceCalls("[YEngine]: YEngine.OnShutdown()");
1504 }
1505
1506 /**
1507 * @brief Queue an instance to the StartQueue so it will run.
1508 * This queue is used for instances that have just had
1509 * an event queued to them when they were previously
1510 * idle. It must only be called by the thread that
1511 * transitioned the thread to XMRInstState.ONSTARTQ so
1512 * we don't get two threads trying to queue the same
1513 * instance to the m_StartQueue at the same time.
1514 */
1515 public void QueueToStart(XMRInstance inst)
1516 {
1517 if(inst.m_IState != XMRInstState.ONSTARTQ)
1518 throw new Exception("bad state");
1519
1520 lock(m_StartQueue)
1521 m_StartQueue.InsertTail(inst);
1522
1523 WakeUpOne();
1524 }
1525
1526 /**
1527 * @brief A script may be sleeping, in which case we wake it.
1528 */
1529 public void WakeFromSleep(XMRInstance inst)
1530 {
1531 // Remove from sleep queue unless someone else already woke it.
1532 lock(m_SleepQueue)
1533 {
1534 if(inst.m_IState != XMRInstState.ONSLEEPQ)
1535 return;
1536
1537 m_SleepQueue.Remove(inst);
1538 inst.m_IState = XMRInstState.REMDFROMSLPQ;
1539 }
1540
1541 // Put on end of list of scripts that are ready to run.
1542 lock(m_YieldQueue)
1543 {
1544 inst.m_IState = XMRInstState.ONYIELDQ;
1545 m_YieldQueue.InsertTail(inst);
1546 }
1547
1548 // Make sure the OS thread is running so it will see the script.
1549 WakeUpOne();
1550 }
1551
1552 /**
1553 * @brief An instance has just finished running for now,
1554 * figure out what to do with it next.
1555 * @param inst = instance in question, not on any queue at the moment
1556 * @param newIState = its new state
1557 * @returns with instance inserted onto proper queue (if any)
1558 */
1559 public void HandleNewIState(XMRInstance inst, XMRInstState newIState)
1560 {
1561 // RunOne() should have left the instance in RUNNING state.
1562 if(inst.m_IState != XMRInstState.RUNNING)
1563 throw new Exception("bad state");
1564
1565 // Now see what RunOne() wants us to do with the instance next.
1566 switch(newIState)
1567 {
1568 // Instance has set m_SleepUntil to when it wants to sleep until.
1569 // So insert instance in sleep queue by ascending wake time.
1570 // Then wake the timer thread if this is the new first entry
1571 // so it will reset its timer.
1572 case XMRInstState.ONSLEEPQ:
1573 lock(m_SleepQueue)
1574 {
1575 XMRInstance after;
1576
1577 inst.m_IState = XMRInstState.ONSLEEPQ;
1578 for(after = m_SleepQueue.PeekHead(); after != null; after = after.m_NextInst)
1579 {
1580 if(after.m_SleepUntil > inst.m_SleepUntil)
1581 break;
1582 }
1583 m_SleepQueue.InsertBefore(inst, after);
1584 if(m_SleepQueue.PeekHead() == inst)
1585 Monitor.Pulse(m_SleepQueue);
1586 }
1587 break;
1588
1589 // Instance just took a long time to run and got wacked by the
1590 // slicer. So put on end of yield queue to let someone else
1591 // run. If there is no one else, it will run again right away.
1592 case XMRInstState.ONYIELDQ:
1593 lock(m_YieldQueue)
1594 {
1595 inst.m_IState = XMRInstState.ONYIELDQ;
1596 m_YieldQueue.InsertTail(inst);
1597 }
1598 break;
1599
1600 // Instance finished executing an event handler. So if there is
1601 // another event queued for it, put it on the start queue so it
1602 // will process the new event. Otherwise, mark it idle and the
1603 // next event to queue to it will start it up.
1604 case XMRInstState.FINISHED:
1605 Monitor.Enter(inst.m_QueueLock);
1606 if(!inst.m_Suspended && (inst.m_EventQueue.Count > 0))
1607 {
1608 inst.m_IState = XMRInstState.ONSTARTQ;
1609 Monitor.Exit(inst.m_QueueLock);
1610 lock(m_StartQueue)
1611 m_StartQueue.InsertTail(inst);
1612 }
1613 else
1614 {
1615 inst.m_IState = XMRInstState.IDLE;
1616 Monitor.Exit(inst.m_QueueLock);
1617 }
1618 break;
1619
1620 // Its m_SuspendCount > 0.
1621 // Don't put it on any queue and it won't run.
1622 // Since it's not IDLE, even queuing an event won't start it.
1623 case XMRInstState.SUSPENDED:
1624 inst.m_IState = XMRInstState.SUSPENDED;
1625 break;
1626
1627 // It has been disposed of.
1628 // Just set the new state and all refs should theoretically drop off
1629 // as the instance is no longer in any list.
1630 case XMRInstState.DISPOSED:
1631 inst.m_IState = XMRInstState.DISPOSED;
1632 break;
1633
1634 // RunOne returned something bad.
1635 default:
1636 throw new Exception("bad new state");
1637 }
1638 }
1639
1640 /**
1641 * @brief Thread that moves instances from the Sleep queue to the Yield queue.
1642 */
1643 private void RunSleepThread()
1644 {
1645 double deltaTS;
1646 int deltaMS;
1647 XMRInstance inst;
1648
1649 while(true)
1650 {
1651 lock(m_SleepQueue)
1652 {
1653 // Wait here until there is a script on the timer queue that has expired.
1654 while(true)
1655 {
1656 UpdateMyThread();
1657 if(m_Exiting)
1658 {
1659 MyThreadExiting();
1660 return;
1661 }
1662 inst = m_SleepQueue.PeekHead();
1663 if(inst == null)
1664 {
1665 Monitor.Wait(m_SleepQueue, Watchdog.DEFAULT_WATCHDOG_TIMEOUT_MS / 2);
1666 continue;
1667 }
1668 if(inst.m_IState != XMRInstState.ONSLEEPQ)
1669 throw new Exception("bad state");
1670 deltaTS = (inst.m_SleepUntil - DateTime.UtcNow).TotalMilliseconds;
1671 if(deltaTS <= 0.0)
1672 break;
1673 deltaMS = Int32.MaxValue;
1674 if(deltaTS < Int32.MaxValue)
1675 deltaMS = (int)deltaTS;
1676 if(deltaMS > Watchdog.DEFAULT_WATCHDOG_TIMEOUT_MS / 2)
1677 deltaMS = Watchdog.DEFAULT_WATCHDOG_TIMEOUT_MS / 2;
1678
1679 Monitor.Wait(m_SleepQueue, deltaMS);
1680 }
1681
1682 // Remove the expired entry from the timer queue.
1683 m_SleepQueue.RemoveHead();
1684 inst.m_IState = XMRInstState.REMDFROMSLPQ;
1685 }
1686
1687 // Post the script to the yield queue so it will run and wake a script thread to run it.
1688 lock(m_YieldQueue)
1689 {
1690 inst.m_IState = XMRInstState.ONYIELDQ;
1691 m_YieldQueue.InsertTail(inst);
1692 }
1693 WakeUpOne();
1694 }
1695 }
1696
1697 public void Suspend(UUID itemID, int ms)
1698 {
1699 XMRInstance instance = GetInstance(itemID);
1700 if(instance != null)
1701 instance.Sleep(ms);
1702 }
1703
1704 public void Die(UUID itemID)
1705 {
1706 XMRInstance instance = GetInstance(itemID);
1707 if(instance != null)
1708 {
1709 TraceCalls("[YEngine]: YEngine.Die({0})", itemID.ToString());
1710 instance.Die();
1711 }
1712 }
1713
1714 /**
1715 * @brief Get specific script instance for which OnRezScript()
1716 * has been called for an YEngine script, and that
1717 * OnRemoveScript() has not been called since.
1718 * @param itemID = as passed to OnRezScript() identifying a specific script instance
1719 * @returns null: not one of our scripts (maybe XEngine etc)
1720 * else: points to the script instance
1721 */
1722 public XMRInstance GetInstance(UUID itemID)
1723 {
1724 XMRInstance instance;
1725 lock(m_InstancesDict)
1726 {
1727 if(!m_InstancesDict.TryGetValue(itemID, out instance))
1728 instance = null;
1729 }
1730 return instance;
1731 }
1732
1733 // Called occasionally to write script state to .state file so the
1734 // script will restart from its last known state if the region crashes
1735 // and gets restarted.
1736 private void DoMaintenance(object source, ElapsedEventArgs e)
1737 {
1738 XMRInstance[] instanceArray;
1739
1740 lock(m_InstancesDict)
1741 instanceArray = System.Linq.Enumerable.ToArray(m_InstancesDict.Values);
1742
1743 foreach(XMRInstance ins in instanceArray)
1744 {
1745 // Don't save attachments
1746 if(ins.m_Part.ParentGroup.IsAttachment)
1747 continue;
1748 ins.GetExecutionState(new XmlDocument());
1749 }
1750 }
1751
1752 /**
1753 * @brief Retrieve errors generated by a previous call to OnRezScript().
1754 * We are guaranteed this routine will not be called before the
1755 * corresponding OnRezScript() has returned. It blocks until the
1756 * compile has completed.
1757 */
1758 public ArrayList GetScriptErrors(UUID itemID)
1759 {
1760 ArrayList errors;
1761
1762 lock(m_ScriptErrors)
1763 {
1764 while(!m_ScriptErrors.TryGetValue(itemID, out errors))
1765 {
1766 Monitor.Wait(m_ScriptErrors);
1767 }
1768 m_ScriptErrors.Remove(itemID);
1769 }
1770 return errors;
1771 }
1772
1773 /**
1774 * @brief Return a list of all script execution times.
1775 */
1776 public Dictionary<uint, float> GetObjectScriptsExecutionTimes()
1777 {
1778 Dictionary<uint, float> topScripts = new Dictionary<uint, float>();
1779 lock(m_InstancesDict)
1780 {
1781 foreach(XMRInstance instance in m_InstancesDict.Values)
1782 {
1783 uint rootLocalID = instance.m_Part.ParentGroup.LocalId;
1784 float oldTotal;
1785 if(!topScripts.TryGetValue(rootLocalID, out oldTotal))
1786 oldTotal = 0;
1787
1788 topScripts[rootLocalID] = (float)instance.m_CPUTime + oldTotal;
1789 }
1790 }
1791 return topScripts;
1792 }
1793
1794 /**
1795 * @brief A float the value is a representative execution time in
1796 * milliseconds of all scripts in the link set.
1797 * @param itemIDs = list of scripts in the link set
1798 * @returns milliseconds for all those scripts
1799 */
1800 public float GetScriptExecutionTime(List<UUID> itemIDs)
1801 {
1802 if((itemIDs == null) || (itemIDs.Count == 0))
1803 return 0;
1804
1805 float time = 0;
1806 foreach(UUID itemID in itemIDs)
1807 {
1808 XMRInstance instance = GetInstance(itemID);
1809 if((instance != null) && instance.Running)
1810 time += (float)instance.m_CPUTime;
1811 }
1812 return time;
1813 }
1814
1815 /**
1816 * @brief Block script from dequeuing events.
1817 */
1818 public void SuspendScript(UUID itemID)
1819 {
1820 XMRInstance instance = GetInstance(itemID);
1821 if(instance != null)
1822 {
1823 TraceCalls("[YEngine]: YEngine.SuspendScript({0})", itemID.ToString());
1824 instance.SuspendIt();
1825 }
1826 }
1827
1828 /**
1829 * @brief Allow script to dequeue events.
1830 */
1831 public void ResumeScript(UUID itemID)
1832 {
1833 XMRInstance instance = GetInstance(itemID);
1834 if(instance != null)
1835 {
1836 TraceCalls("[YEngine]: YEngine.ResumeScript({0})", itemID.ToString());
1837 instance.ResumeIt();
1838 }
1839 else
1840 {
1841 // probably an XEngine script
1842 }
1843 }
1844
1845 /**
1846 * @brief Rebuild m_ObjectInstArray[partUUID] from m_ObjectItemList[partUUID]
1847 * @param partUUID = which object in scene to rebuild for
1848 */
1849 private XMRInstance[] RebuildObjectInstArray(UUID partUUID)
1850 {
1851 List<UUID> itemIDList = m_ObjectItemList[partUUID];
1852 int n = 0;
1853 foreach(UUID itemID in itemIDList)
1854 {
1855 if(m_InstancesDict.ContainsKey(itemID))
1856 n++;
1857 }
1858
1859 XMRInstance[] a = new XMRInstance[n];
1860 n = 0;
1861 foreach(UUID itemID in itemIDList)
1862 {
1863 if(m_InstancesDict.TryGetValue(itemID, out a[n]))
1864 n++;
1865 }
1866 m_ObjectInstArray[partUUID] = a;
1867 return a;
1868 }
1869
1870 public void TraceCalls(string format, params object[] args)
1871 {
1872 if(m_TraceCalls)
1873 m_log.DebugFormat(format, args);
1874 }
1875 public void Verbose(string format, params object[] args)
1876 {
1877 if(m_Verbose)
1878 m_log.DebugFormat(format, args);
1879 }
1880
1881 /**
1882 * @brief Manage our threads.
1883 */
1884 public static Thread StartMyThread(ThreadStart start, string name, ThreadPriority priority)
1885 {
1886 m_log.Debug("[YEngine]: starting thread " + name);
1887 Thread thread = WorkManager.StartThread(start, name, priority, true, false, false);
1888 return thread;
1889 }
1890
1891 public static void UpdateMyThread()
1892 {
1893 Watchdog.UpdateThread();
1894 }
1895
1896 public static void MyThreadExiting()
1897 {
1898 Watchdog.RemoveThread(true);
1899 }
1900 }
1901}