diff options
Diffstat (limited to 'OpenSim/Region/ScriptEngine/YEngine/XMREngine.cs')
-rw-r--r-- | OpenSim/Region/ScriptEngine/YEngine/XMREngine.cs | 1901 |
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 | |||
31 | using log4net; | ||
32 | using Mono.Addins; | ||
33 | using Nini.Config; | ||
34 | using OpenSim.Framework; | ||
35 | using OpenSim.Framework.Console; | ||
36 | using OpenSim.Framework.Monitoring; | ||
37 | using OpenSim.Region.ClientStack.Linden; | ||
38 | using OpenSim.Region.Framework.Interfaces; | ||
39 | using OpenSim.Region.Framework.Scenes; | ||
40 | using OpenSim.Region.ScriptEngine.Interfaces; | ||
41 | using OpenSim.Region.ScriptEngine.Shared; | ||
42 | using OpenSim.Region.ScriptEngine.Shared.Api; | ||
43 | using OpenMetaverse; | ||
44 | using System; | ||
45 | using System.Collections; | ||
46 | using System.Collections.Generic; | ||
47 | using System.IO; | ||
48 | using System.Reflection; | ||
49 | using System.Reflection.Emit; | ||
50 | using System.Text; | ||
51 | using System.Threading; | ||
52 | using System.Timers; | ||
53 | using System.Xml; | ||
54 | |||
55 | using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat; | ||
56 | using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger; | ||
57 | using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString; | ||
58 | using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list; | ||
59 | using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion; | ||
60 | using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString; | ||
61 | using 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 | |||
66 | namespace 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 | } | ||