From 1a47ff8094ee414a47aebd310826906d89428a09 Mon Sep 17 00:00:00 2001 From: Teravus Ovares Date: Fri, 30 May 2008 12:27:06 +0000 Subject: * This is Melanie's XEngine script engine. I've not tested this real well, however, it's confirmed to compile and OpenSimulator to run successfully without this script engine active. --- OpenSim/Region/ScriptEngine/XEngine/XEngine.cs | 1435 ++++++++++++++++++++++++ 1 file changed, 1435 insertions(+) create mode 100644 OpenSim/Region/ScriptEngine/XEngine/XEngine.cs (limited to 'OpenSim/Region/ScriptEngine/XEngine/XEngine.cs') diff --git a/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs b/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs new file mode 100644 index 0000000..3ca03b2 --- /dev/null +++ b/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs @@ -0,0 +1,1435 @@ +using System; +using System.IO; +using System.Threading; +using System.Collections; +using System.Collections.Generic; +using System.Security.Policy; +using System.Reflection; +using System.Xml; +using libsecondlife; +using log4net; +using Nini.Config; +using Amib.Threading; +using OpenSim.Framework; +using OpenSim.Region.Environment; +using OpenSim.Region.Environment.Scenes; +using OpenSim.Region.Environment.Interfaces; +using OpenSim.Region.ScriptEngine.XEngine.Script; + +namespace OpenSim.Region.ScriptEngine.XEngine +{ + public class XEngine : IRegionModule + { + private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private SmartThreadPool m_ThreadPool; + private int m_MaxScriptQueue; + private Scene m_Scene; + private IConfig m_ScriptConfig; + private Compiler m_Compiler; + private EventManager m_EventManager; + private int m_EventLimit; + private bool m_KillTimedOutScripts; + + private static List m_ScriptEngines = + new List(); + public AsyncCommandManager m_ASYNCLSLCommandManager; + + // Maps the local id to the script inventory items in it + + private Dictionary > m_PrimObjects = + new Dictionary >(); + + // Maps the LLUUID above to the script instance + + private Dictionary m_Scripts = + new Dictionary(); + + // Maps the asset ID to the assembly + + private Dictionary m_Assemblies = + new Dictionary(); + + // This will list AppDomains by script asset + + private Dictionary m_AppDomains = + new Dictionary(); + + // List the scripts running in each appdomain + + private Dictionary > m_DomainScripts = + new Dictionary >(); + + public string ScriptEngineName + { + get { return "XEngine"; } + } + + public Scene World + { + get { return m_Scene; } + } + + public ILog Log + { + get { return m_log; } + } + + public static List ScriptEngines + { + get { return m_ScriptEngines; } + } + + private struct RezScriptParms + { + uint LocalID; + LLUUID ItemID; + string Script; + } + + public IConfig ScriptConfigSource + { + get { return m_ScriptConfig; } + } + + // + // IRegionModule functions + // + public void Initialise(Scene scene, IConfigSource configSource) + { + AppDomain.CurrentDomain.AssemblyResolve += + OnAssemblyResolve; + + m_log.InfoFormat("[XEngine] Initializing scripts in region {0}", + scene.RegionInfo.RegionName); + m_Scene=scene; + + m_ScriptConfig = configSource.Configs["XEngine"]; + + if(m_ScriptConfig == null) + { + m_log.ErrorFormat("[XEngine] No script configuration found. Scripts disabled"); + return; + } + + int minThreads = m_ScriptConfig.GetInt("MinThreads", 2); + int maxThreads = m_ScriptConfig.GetInt("MaxThreads", 2); + int idleTimeout = m_ScriptConfig.GetInt("IdleTimeout", 60); + string priority = m_ScriptConfig.GetString("Priority", "BelowNormal"); + int maxScriptQueue = m_ScriptConfig.GetInt("MaxScriptEventQueue",300); + int stackSize = m_ScriptConfig.GetInt("ThreadStackSize", 262144); + int sleepTime = m_ScriptConfig.GetInt("MaintenanceInterval", + 10)*1000; + m_EventLimit = m_ScriptConfig.GetInt("EventLimit", 30); + m_KillTimedOutScripts = m_ScriptConfig.GetBoolean( + "KillTimedOutScripts", false); + int saveTime = m_ScriptConfig.GetInt("SaveInterval", 300)*1000; + + ThreadPriority prio = ThreadPriority.BelowNormal; + switch(priority) + { + case "Lowest": + prio=ThreadPriority.Lowest; + break; + case "BelowNormal": + prio=ThreadPriority.BelowNormal; + break; + case "Normal": + prio=ThreadPriority.Normal; + break; + case "AboveNormal": + prio=ThreadPriority.AboveNormal; + break; + case "Highest": + prio=ThreadPriority.Highest; + break; + default: + m_log.ErrorFormat("[XEngine] Invalid thread priority: '"+ + priority+"'. Assuming BelowNormal"); + break; + } + + lock(m_ScriptEngines) + { + m_ScriptEngines.Add(this); + } + + m_EventManager = new EventManager(this); + m_ASYNCLSLCommandManager = new AsyncCommandManager(this); + + StartEngine(minThreads, maxThreads, idleTimeout, prio, + maxScriptQueue, stackSize); + + m_Compiler = new Compiler(this); + + m_Scene.EventManager.OnRezScript += OnRezScript; + m_Scene.EventManager.OnRemoveScript += OnRemoveScript; + m_Scene.EventManager.OnScriptReset += OnScriptReset; + + if(sleepTime > 0) + { + m_ThreadPool.QueueWorkItem(new WorkItemCallback( + this.DoMaintenance), new Object[] + { sleepTime }); + } + + if(saveTime > 0) + { + m_ThreadPool.QueueWorkItem(new WorkItemCallback( + this.DoBackup), new Object[] { saveTime }); + } + } + + public void PostInitialise() + { + m_ThreadPool.Start(); + } + + public void Close() + { + lock(m_ScriptEngines) + { + if(m_ScriptEngines.Contains(this)) + m_ScriptEngines.Remove(this); + } + } + + public object DoBackup(object o) + { + Object[] p = (Object[])o; + int saveTime = (int)p[0]; + + System.Threading.Thread.Sleep(saveTime); + +// m_log.Debug("[XEngine] Backing up script states"); + + List instances = new List(); + + lock(m_Scripts) + { + foreach (XScriptInstance instance in m_Scripts.Values) + instances.Add(instance); + } + + foreach (XScriptInstance i in instances) + { + string assembly = String.Empty; + + lock(m_Scripts) + { + if(!m_Assemblies.ContainsKey(i.AssetID)) + continue; + assembly = m_Assemblies[i.AssetID]; + } + + i.SaveState(assembly); + } + + instances.Clear(); + + m_ThreadPool.QueueWorkItem(new WorkItemCallback( + this.DoBackup), new Object[] { saveTime }); + + return 0; + } + + public object DoMaintenance(object p) + { + object[] parms = (object[])p; + int sleepTime = (int)parms[0]; + + foreach (XScriptInstance inst in m_Scripts.Values) + { + if(inst.EventTime() > m_EventLimit) + { + inst.Stop(100); + if(!m_KillTimedOutScripts) + inst.Start(); + } + } + + System.Threading.Thread.Sleep(sleepTime); + + m_ThreadPool.QueueWorkItem(new WorkItemCallback( + this.DoMaintenance), new Object[] + { sleepTime }); + + return 0; + } + + public string Name + { + get { return "XEngine"; } + } + + public bool IsSharedModule + { + get { return false; } + } + + // + // XEngine functions + // + public int MaxScriptQueue + { + get { return m_MaxScriptQueue; } + } + + // + // Hooks + // + public void OnRezScript(uint localID, LLUUID itemID, string script) + { + m_ThreadPool.QueueWorkItem(new WorkItemCallback( + this.DoOnRezScript), new Object[] + { localID, itemID, script}); + } + + private object DoOnRezScript(object parm) + { + Object[] p = (Object[])parm; + uint localID = (uint)p[0]; + LLUUID itemID = (LLUUID)p[1]; + string script =(string)p[2]; + + // Get the asset ID of the script, so we can check if we + // already have it. + + SceneObjectPart part = m_Scene.GetSceneObjectPart(localID); + if(part == null) + return false; + + TaskInventoryItem item = part.GetInventoryItem(itemID); + if(item == null) + return false; + + LLUUID assetID=item.AssetID; + +// m_log.DebugFormat("[XEngine] Compiling script {0} ({1})", +// item.Name, itemID.ToString()); + + string assembly=""; + try + { + assembly=m_Compiler.PerformScriptCompile(script, + assetID.ToString()); + m_log.DebugFormat("[XEngine] Loaded script {0}.{1}", + part.ParentGroup.RootPart.Name, item.Name); + } + catch (Exception e) + { + try + { + // DISPLAY ERROR INWORLD + string text = "Error compiling script:\r\n" + e.Message.ToString(); + if (text.Length > 1400) + text = text.Substring(0, 1400); + World.SimChat(Helpers.StringToField(text), + ChatTypeEnum.DebugChannel, 2147483647, + part.AbsolutePosition, + part.Name, part.UUID, false); + } + catch (Exception e2) // LEGIT: User Scripting + { + m_log.Error("[XEngine]: "+ + "Error displaying error in-world: " + + e2.ToString()); + m_log.Error("[XEngine]: " + + "Errormessage: Error compiling script:\r\n" + + e.Message.ToString()); + } + + return false; + } + + lock(m_Scripts) + { + // Create the object record + + if(!m_PrimObjects.ContainsKey(localID)) + m_PrimObjects[localID] = new List(); + + if(!m_PrimObjects[localID].Contains(itemID)) + m_PrimObjects[localID].Add(itemID); + + if(!m_Assemblies.ContainsKey(assetID)) + m_Assemblies[assetID] = assembly; + + if((!m_Scripts.ContainsKey(itemID)) || + (m_Scripts[itemID].AssetID != assetID)) + { + LLUUID appDomain=assetID; + + if(part.ParentGroup.RootPart.m_IsAttachment) + appDomain=part.ParentGroup.RootPart.UUID; + + if(!m_AppDomains.ContainsKey(appDomain)) + { + try + { + AppDomainSetup appSetup = new AppDomainSetup(); +// appSetup.ApplicationBase = Path.Combine( +// "ScriptEngines", +// m_Scene.RegionInfo.RegionID.ToString()); + + Evidence baseEvidence = + AppDomain.CurrentDomain.Evidence; + Evidence evidence = new Evidence(baseEvidence); + + m_AppDomains[appDomain] = + AppDomain.CreateDomain( + m_Scene.RegionInfo.RegionID.ToString(), + evidence, appSetup); + + m_AppDomains[appDomain].AssemblyResolve += + new ResolveEventHandler( + AssemblyResolver.OnAssemblyResolve); + m_DomainScripts[appDomain] = new List(); + } + catch (Exception e) + { + m_log.Error("[XEngine] Exception creating app domain:\n"+e.ToString()); + return false; + } + } + m_DomainScripts[appDomain].Add(itemID); + + + XScriptInstance instance = new XScriptInstance(this,localID, + part.UUID, itemID, assetID, assembly, + m_AppDomains[appDomain]); + + instance.AppDomain = appDomain; + + m_Scripts[itemID] = instance; + } + } + return true; + } + + public void OnRemoveScript(uint localID, LLUUID itemID) + { + lock(m_Scripts) + { + // Do we even have it? + if(!m_Scripts.ContainsKey(itemID)) + return; + + m_ASYNCLSLCommandManager.RemoveScript(localID, itemID); + + XScriptInstance instance=m_Scripts[itemID]; + m_Scripts.Remove(itemID); + + instance.ClearQueue(); + instance.Stop(0); + + SceneObjectPart part = + m_Scene.GetSceneObjectPart(localID); + + if(part != null) + part.RemoveScriptEvents(itemID); + + // Remove the script from it's prim + if(m_PrimObjects.ContainsKey(localID)) + { + // Remove inventory item record + if(m_PrimObjects[localID].Contains(itemID)) + m_PrimObjects[localID].Remove(itemID); + + // If there are no more scripts, remove prim + if(m_PrimObjects[localID].Count == 0) + { + m_PrimObjects.Remove(localID); + } + } + + m_DomainScripts[instance.AppDomain].Remove(instance.ItemID); + if(m_DomainScripts[instance.AppDomain].Count == 0) + { + m_DomainScripts.Remove(instance.AppDomain); + UnloadAppDomain(instance.AppDomain); + } + + instance = null; + + CleanAssemblies(); + } + } + + public void OnScriptReset(uint localID, LLUUID itemID) + { + ResetScript(itemID); + } + + private void CleanAssemblies() + { + List assetIDList = new List(m_Assemblies.Keys); + + foreach (XScriptInstance i in m_Scripts.Values) + { + if(assetIDList.Contains(i.AssetID)) + assetIDList.Remove(i.AssetID); + } + + foreach (LLUUID assetID in assetIDList) + { +// m_log.DebugFormat("[XEngine] Removing unreferenced assembly {0}", m_Assemblies[assetID]); + try + { + if(File.Exists(m_Assemblies[assetID])) + File.Delete(m_Assemblies[assetID]); + if(File.Exists(m_Assemblies[assetID]+".state")) + File.Delete(m_Assemblies[assetID]+".state"); + if(File.Exists(m_Assemblies[assetID]+".mdb")) + File.Delete(m_Assemblies[assetID]+".mdb"); + } + catch (Exception e) + { + } + m_Assemblies.Remove(assetID); + } + } + + private void UnloadAppDomain(LLUUID id) + { + if(m_AppDomains.ContainsKey(id)) + { + AppDomain domain=m_AppDomains[id]; + m_AppDomains.Remove(id); + + AppDomain.Unload(domain); + domain = null; +// m_log.DebugFormat("[XEngine] Unloaded app domain {0}", id.ToString()); + } + } + + // + // Start processing + // + private void StartEngine(int minThreads, int maxThreads, + int idleTimeout, ThreadPriority threadPriority, + int maxScriptQueue, int stackSize) + { + m_MaxScriptQueue=maxScriptQueue; + + STPStartInfo startInfo = new STPStartInfo(); + startInfo.IdleTimeout = idleTimeout; + startInfo.MaxWorkerThreads = maxThreads; + startInfo.MinWorkerThreads = minThreads; + startInfo.ThreadPriority = threadPriority; + startInfo.StackSize = stackSize; + startInfo.StartSuspended = true; + + m_ThreadPool = new SmartThreadPool(startInfo); + } + + // + // Used by script instances to queue event handler jobs + // + public IWorkItemResult QueueEventHandler(object parms) + { + return m_ThreadPool.QueueWorkItem(new WorkItemCallback( + this.ProcessEventHandler), parms); + } + + // + // The main script engine worker + // + private object ProcessEventHandler(object parms) + { + XScriptInstance instance=(XScriptInstance)parms; + + return instance.EventProcessor(); + } + + // + // Post event to an entire prim + // + public bool PostObjectEvent(uint localID, XEventParams p) + { + bool result = false; + + if(!m_PrimObjects.ContainsKey(localID)) + return false; + + foreach (LLUUID itemID in m_PrimObjects[localID]) + { + if(m_Scripts.ContainsKey(itemID)) + { + XScriptInstance instance = m_Scripts[itemID]; + if(instance != null) + { + instance.PostEvent(p); + result = true; + } + } + } + return result; + } + + // + // Post an event to a single script + // + public bool PostScriptEvent(LLUUID itemID, XEventParams p) + { + if(m_Scripts.ContainsKey(itemID)) + { + XScriptInstance instance = m_Scripts[itemID]; + if(instance != null) + instance.PostEvent(p); + return true; + } + return false; + } + + public Assembly OnAssemblyResolve(object sender, + ResolveEventArgs args) + { + if(!(sender is System.AppDomain)) + return null; + + string[] pathList=new string[] {"bin", "ScriptEngines", + Path.Combine("ScriptEngines", + m_Scene.RegionInfo.RegionID.ToString())}; + + string assemblyName = args.Name; + if(assemblyName.IndexOf(",") != -1) + assemblyName=args.Name.Substring(0, args.Name.IndexOf(",")); + + foreach (string s in pathList) + { + string path=Path.Combine(Directory.GetCurrentDirectory(), + Path.Combine(s, assemblyName))+".dll"; + + if(File.Exists(path)) + return Assembly.LoadFrom(path); + } + + return null; + } + + private XScriptInstance GetInstance(LLUUID itemID) + { + XScriptInstance instance; + lock(m_Scripts) + { + if(!m_Scripts.ContainsKey(itemID)) + return null; + instance = m_Scripts[itemID]; + } + return instance; + } + + public void SetScriptState(LLUUID itemID, bool running) + { + XScriptInstance instance = GetInstance(itemID); + if(instance != null) + { + if(running) + instance.Start(); + else + instance.Stop(500); + } + } + + public bool GetScriptState(LLUUID itemID) + { + XScriptInstance instance = GetInstance(itemID); + if(instance != null) + return instance.Running; + return false; + } + + public void ResetScript(LLUUID itemID) + { + XScriptInstance instance = GetInstance(itemID); + if(instance != null) + instance.ResetScript(); + } + + public XDetectParams GetDetectParams(LLUUID itemID, int idx) + { + XScriptInstance instance = GetInstance(itemID); + if(instance != null) + return instance.GetDetectParams(idx); + return new XDetectParams(); + } + + public LLUUID GetDetectID(LLUUID itemID, int idx) + { + XScriptInstance instance = GetInstance(itemID); + if(instance != null) + return instance.GetDetectID(idx); + return LLUUID.Zero; + } + } + + public struct XDetectParams + { + public LLUUID Key; + public LSL_Types.Vector3 OffsetPos; + } + + public class XEventParams + { + public XEventParams(string eventName, Object[] eventParams, XDetectParams[] detectParams) + { + EventName=eventName; + Params=eventParams; + DetectParams=detectParams; + } + + public string EventName; + public Object[] Params; + public XDetectParams[] DetectParams; + } + + public class XScriptInstance + { + private XEngine m_Engine; + private IWorkItemResult m_CurrentResult=null; + private Queue m_EventQueue=new Queue(32); + private bool m_RunEvents=false; + private LLUUID m_ItemID; + private uint m_LocalID; + private LLUUID m_ObjectID; + private LLUUID m_AssetID; + private IScript m_Script; + private LSL_ScriptCommands m_LSLCommands; + private OSSL_ScriptCommands m_OSSLCommands; + private Executor m_Executor; + private LLUUID m_AppDomain; + private XDetectParams[] m_DetectParams; + private bool m_TimerQueued; + private DateTime m_EventStart; + private bool m_InEvent; + + // Script state + private string m_State="default"; + + public Object[] PluginData = new Object[0]; + + public bool Running + { + get { return m_RunEvents; } + } + + public string State + { + get { return m_State; } + set { m_State = value; } + } + + public XEngine Engine + { + get { return m_Engine; } + } + + public LLUUID AppDomain + { + get { return m_AppDomain; } + set { m_AppDomain = value; } + } + + public LLUUID ItemID + { + get { return m_ItemID; } + } + + public LLUUID ObjectID + { + get { return m_ObjectID; } + } + + public uint LocalID + { + get { return m_LocalID; } + } + + public LLUUID AssetID + { + get { return m_AssetID; } + } + + public Queue EventQueue + { + get { return m_EventQueue; } + } + + public void ClearQueue() + { + m_TimerQueued = false; + m_EventQueue.Clear(); + } + + public XScriptInstance(XEngine engine, uint localID, LLUUID objectID, + LLUUID itemID, LLUUID assetID, string assembly, AppDomain dom) + { + m_Engine=engine; + + m_LocalID = localID; + m_ObjectID = objectID; + m_ItemID = itemID; + m_AssetID = assetID; + + SceneObjectPart part=engine.World.GetSceneObjectPart(localID); + if(part == null) + { + engine.Log.Error("[XEngine] SceneObjectPart unavailable. Script NOT started."); + return; + } + + m_LSLCommands = new LSL_ScriptCommands(engine, this, part, localID, + itemID); + m_OSSLCommands = new OSSL_ScriptCommands(engine, this, part, + localID, itemID); + + try + { + m_Script = (IScript)dom.CreateInstanceAndUnwrap( + Path.GetFileNameWithoutExtension(assembly), + "SecondLife.Script"); + } + catch (Exception e) + { + m_Engine.Log.ErrorFormat("[XEngine] Error loading assembly {0}\n"+e.ToString(), assembly); + } + + try + { + m_Script.Start(m_LSLCommands, m_OSSLCommands); + + m_Executor = new Executor(m_Script); + +// m_Engine.Log.Debug("[XEngine] Script instance created"); + + part.SetScriptEvents(m_ItemID, + (int)m_Executor.GetStateEventFlags()); + } + catch (Exception e) + { + m_Engine.Log.Error("Error loading script instance\n"+e.ToString()); + } + + string savedState = assembly+".state"; + if(File.Exists(savedState)) + { + string xml = String.Empty; + + try + { + FileInfo fi = new FileInfo(savedState); + int size=(int)fi.Length; + if(size < 130000) + { + using (FileStream fs = File.Open(savedState, + FileMode.Open, FileAccess.Read, FileShare.None)) + { + System.Text.ASCIIEncoding enc = + new System.Text.ASCIIEncoding(); + + Byte[] data=new Byte[size]; + fs.Read(data, 0, size); + + xml=enc.GetString(data); + + ScriptSerializer.Deserialize(xml, this); + + m_Engine.m_ASYNCLSLCommandManager.CreateFromData( + m_LocalID, m_ItemID, m_ObjectID, + PluginData); + } + } + else + { + m_Engine.Log.Error("Unable to load script state: Memory limit exceeded"); + PostEvent(new XEventParams("state_entry", + new Object[0], new XDetectParams[0])); + } + } + catch (Exception e) + { + m_Engine.Log.ErrorFormat("Unable to load script state from xml: {0}\n"+e.ToString(), xml); + PostEvent(new XEventParams("state_entry", + new Object[0], new XDetectParams[0])); + } + } + else + { + PostEvent(new XEventParams("state_entry", + new Object[0], new XDetectParams[0])); + } + Start(); + } + + public void VarDump(Dictionary vars) + { + Console.WriteLine("Variable dump for script {0}", m_ItemID.ToString()); + foreach (KeyValuePair v in vars) + { + Console.WriteLine("Variable: {0} = '{1}'", v. Key, + v.Value.ToString()); + } + } + + public void Start() + { + lock(m_EventQueue) + { + if(Running) + return; + + m_RunEvents=true; + + if(m_EventQueue.Count > 0) + { + if(m_CurrentResult == null) + m_CurrentResult=m_Engine.QueueEventHandler(this); + } + } + } + + public bool Stop(int timeout) + { + IWorkItemResult result; + + lock(m_EventQueue) + { + if(!Running) + return true; + + if(m_CurrentResult == null) + { + m_RunEvents=false; + return true; + } + + if(m_CurrentResult.Cancel()) + { + m_CurrentResult=null; + m_RunEvents=false; + return true; + } + + result=m_CurrentResult; + m_RunEvents=false; + } + + if(SmartThreadPool.WaitAll(new IWorkItemResult[] {result}, new TimeSpan((long)timeout*100000), false)) + { + return true; + } + + lock(m_EventQueue) + { + if(m_CurrentResult != null) + m_CurrentResult.Abort(); + else + return true; + } + + return true; + } + + public void SetState(string state) + { + PostEvent(new XEventParams("state_exit", new Object[0], + new XDetectParams[0])); + PostEvent(new XEventParams("state", new Object[] { state }, + new XDetectParams[0])); + PostEvent(new XEventParams("state_entry", new Object[0], + new XDetectParams[0])); + } + + public void PostEvent(XEventParams data) + { + lock(m_EventQueue) + { + if(m_EventQueue.Count >= m_Engine.MaxScriptQueue) + return; + + m_EventQueue.Enqueue(data); + if(data.EventName == "timer") + { + if(m_TimerQueued) + return; + m_TimerQueued = true; + } + + if(!m_RunEvents) + return; + + if(m_CurrentResult == null) + { + m_CurrentResult=m_Engine.QueueEventHandler(this); + } + } + } + + public object EventProcessor() + { + XEventParams data=null; + + lock(m_EventQueue) + { + data=(XEventParams)m_EventQueue.Dequeue(); + if(data == null) // Shouldn't happen + { + m_CurrentResult=null; + return 0; + } + if(data.EventName == "timer") + m_TimerQueued = false; + } + + m_DetectParams=data.DetectParams; + + if(data.EventName == "state") // Hardcoded state change + { + m_State=data.Params[0].ToString(); + m_Engine.m_ASYNCLSLCommandManager.RemoveScript( + m_LocalID, m_ItemID); + + SceneObjectPart part=m_Engine.World.GetSceneObjectPart( + m_LocalID); + if(part != null) + { + part.SetScriptEvents(m_ItemID, + (int)m_Executor.GetStateEventFlags()); + } + } + else + { +// m_Engine.Log.DebugFormat("[XEngine] Processed event {0}", data.EventName); + SceneObjectPart part=m_Engine.World.GetSceneObjectPart( + m_LocalID); + try + { + m_EventStart = DateTime.Now; + m_InEvent = true; + m_Executor.ExecuteEvent(data.EventName, data.Params); + m_InEvent = false; + } + catch (Exception e) + { + m_InEvent = false; + if(e is System.Threading.ThreadAbortException) + { + lock(m_EventQueue) + { + if((m_EventQueue.Count > 0) && m_RunEvents) + { + m_CurrentResult=m_Engine.QueueEventHandler(this); + } + else + { + m_CurrentResult=null; + } + } + + m_DetectParams=null; + + return 0; + } + + try + { + // DISPLAY ERROR INWORLD + string text = "Runtime error:\n" + e.ToString(); + if (text.Length > 1400) + text = text.Substring(0, 1400); + m_Engine.World.SimChat(Helpers.StringToField(text), + ChatTypeEnum.DebugChannel, 2147483647, + part.AbsolutePosition, + part.Name, part.UUID, false); + } + catch (Exception e2) // LEGIT: User Scripting + { + m_Engine.Log.Error("[XEngine]: "+ + "Error displaying error in-world: " + + e2.ToString()); + m_Engine.Log.Error("[XEngine]: " + + "Errormessage: Error compiling script:\r\n" + + e.ToString()); + } + } + } + + lock(m_EventQueue) + { + if((m_EventQueue.Count > 0) && m_RunEvents) + { + m_CurrentResult=m_Engine.QueueEventHandler(this); + } + else + { + m_CurrentResult=null; + } + } + + m_DetectParams=null; + + return 0; + } + + public int EventTime() + { + if(!m_InEvent) + return 0; + + return (DateTime.Now - m_EventStart).Seconds; + } + + public void ResetScript() + { + Stop(0); + m_Engine.m_ASYNCLSLCommandManager.RemoveScript(m_LocalID, m_ItemID); + m_EventQueue.Clear(); + m_Script.ResetVars(); + m_State = "default"; + Start(); + PostEvent(new XEventParams("state_entry", + new Object[0], new XDetectParams[0])); + } + + public Dictionary GetVars() + { + return m_Script.GetVars(); + } + + public void SetVars(Dictionary vars) + { + m_Script.SetVars(vars); + } + + public XDetectParams GetDetectParams(int idx) + { + if(idx < 0 || idx >= m_DetectParams.Length) + return new XDetectParams(); + + return m_DetectParams[idx]; + } + + public LLUUID GetDetectID(int idx) + { + if(idx < 0 || idx >= m_DetectParams.Length) + return LLUUID.Zero; + + return m_DetectParams[idx].Key; + } + + public void SaveState(string assembly) + { + PluginData = + m_Engine.m_ASYNCLSLCommandManager.GetSerializationData( + m_ItemID); + + string xml=ScriptSerializer.Serialize(this); + + try + { + FileStream fs = File.Create(assembly+".state"); + System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding(); + Byte[] buf=enc.GetBytes(xml); + fs.Write(buf, 0, buf.Length); + fs.Close(); + } + catch(Exception) + { + return; + } + } + } + + public class ScriptSerializer + { + public static string Serialize(XScriptInstance instance) + { + instance.Stop(50); + + XmlDocument xmldoc = new XmlDocument(); + + XmlNode xmlnode = xmldoc.CreateNode(XmlNodeType.XmlDeclaration, + "", ""); + xmldoc.AppendChild(xmlnode); + + XmlElement rootElement = xmldoc.CreateElement("", "ScriptState", + ""); + xmldoc.AppendChild(rootElement); + + XmlElement state = xmldoc.CreateElement("", "State", ""); + state.AppendChild(xmldoc.CreateTextNode(instance.State)); + + rootElement.AppendChild(state); + + Dictionary vars = instance.GetVars(); + + XmlElement variables = xmldoc.CreateElement("", "Variables", ""); + + foreach (KeyValuePair var in vars) + WriteTypedValue(xmldoc, variables, "Variable", var.Key, + var.Value); + + rootElement.AppendChild(variables); + + XmlElement queue = xmldoc.CreateElement("", "Queue", ""); + + int count = instance.EventQueue.Count; + + while(count > 0) + { + XEventParams ep = (XEventParams)instance.EventQueue.Dequeue(); + instance.EventQueue.Enqueue(ep); + count--; + + XmlElement item = xmldoc.CreateElement("", "Item", ""); + XmlAttribute itemEvent = xmldoc.CreateAttribute("", "event", + ""); + itemEvent.Value=ep.EventName; + item.Attributes.Append(itemEvent); + + XmlElement parms = xmldoc.CreateElement("", "Params", ""); + + foreach (Object o in ep.Params) + WriteTypedValue(xmldoc, parms, "Param", String.Empty, o); + + item.AppendChild(parms); + + XmlElement detect = xmldoc.CreateElement("", "Detected", ""); + + foreach (XDetectParams det in ep.DetectParams) + { + XmlElement objectElem = xmldoc.CreateElement("", "Object", + ""); + XmlAttribute pos = xmldoc.CreateAttribute("", "pos", ""); + pos.Value=det.OffsetPos.ToString(); + objectElem.Attributes.Append(pos); + objectElem.AppendChild( + xmldoc.CreateTextNode(det.Key.ToString())); + + detect.AppendChild(objectElem); + } + + item.AppendChild(detect); + + queue.AppendChild(item); + } + + rootElement.AppendChild(queue); + + XmlNode plugins = xmldoc.CreateElement("", "Plugins", ""); + if(instance.PluginData.Length > 0) + DumpList(xmldoc, plugins, + new LSL_Types.list(instance.PluginData)); + + rootElement.AppendChild(plugins); + + instance.Start(); + + return xmldoc.InnerXml; + } + + public static void Deserialize(string xml, XScriptInstance instance) + { + XmlDocument doc = new XmlDocument(); + + Dictionary vars = instance.GetVars(); + + instance.PluginData = new Object[0]; + + doc.LoadXml(xml); + + XmlNodeList rootL = doc.GetElementsByTagName("ScriptState"); + if(rootL.Count != 1) + { + return; + } + XmlNode rootNode = rootL[0]; + + if(rootNode != null) + { + object varValue; + XmlNodeList partL = rootNode.ChildNodes; + + foreach (XmlNode part in partL) + { + switch(part.Name) + { + case "State": + instance.State=part.InnerText; + break; + case "Variables": + XmlNodeList varL = part.ChildNodes; + foreach (XmlNode var in varL) + { + string varName; + varValue=ReadTypedValue(var, out varName); + + if(vars.ContainsKey(varName)) + vars[varName] = varValue; + } + instance.SetVars(vars); + break; + case "Queue": + XmlNodeList itemL = part.ChildNodes; + foreach (XmlNode item in itemL) + { + List parms = new List(); + List detected = + new List(); + + string eventName = + item.Attributes.GetNamedItem("event").Value; + XmlNodeList eventL = item.ChildNodes; + foreach (XmlNode evt in eventL) + { + switch(evt.Name) + { + case "Params": + XmlNodeList prms = evt.ChildNodes; + foreach (XmlNode pm in prms) + parms.Add(ReadTypedValue(pm)); + + break; + case "Detected": + XmlNodeList detL = evt.ChildNodes; + foreach (XmlNode det in detL) + { + string vect = + det.Attributes.GetNamedItem( + "pos").Value; + LSL_Types.Vector3 v = + new LSL_Types.Vector3(vect); + LLUUID uuid = new LLUUID(); + LLUUID.TryParse(det.InnerText, + out uuid); + + XDetectParams d; + d.Key = uuid; + d.OffsetPos = v; + + detected.Add(d); + } + break; + } + } + XEventParams ep = new XEventParams( + eventName, parms.ToArray(), + detected.ToArray()); + instance.EventQueue.Enqueue(ep); + } + break; + case "Plugins": + instance.PluginData = ReadList(part).Data; + break; + } + } + } + } + + private static void DumpList(XmlDocument doc, XmlNode parent, + LSL_Types.list l) + { + foreach (Object o in l.Data) + WriteTypedValue(doc, parent, "ListItem", "", o); + } + + private static LSL_Types.list ReadList(XmlNode parent) + { + List olist = new List(); + + XmlNodeList itemL = parent.ChildNodes; + foreach(XmlNode item in itemL) + olist.Add(ReadTypedValue(item)); + + return new LSL_Types.list(olist.ToArray()); + } + + private static void WriteTypedValue(XmlDocument doc, XmlNode parent, + string tag, string name, object value) + { + Type t=value.GetType(); + XmlAttribute typ = doc.CreateAttribute("", "type", ""); + XmlNode n = doc.CreateElement("", tag, ""); + + if(value is LSL_Types.list) + { + typ.Value = "list"; + n.Attributes.Append(typ); + + DumpList(doc, n, (LSL_Types.list) value); + + if(name != String.Empty) + { + XmlAttribute nam = doc.CreateAttribute("", "name", ""); + nam.Value = name; + n.Attributes.Append(nam); + } + + parent.AppendChild(n); + return; + } + + n.AppendChild(doc.CreateTextNode(value.ToString())); + + typ.Value = t.ToString(); + n.Attributes.Append(typ); + if(name != String.Empty) + { + XmlAttribute nam = doc.CreateAttribute("", "name", ""); + nam.Value = name; + n.Attributes.Append(nam); + } + + parent.AppendChild(n); + } + + private static object ReadTypedValue(XmlNode tag, out string name) + { + name = tag.Attributes.GetNamedItem("name").Value; + + return ReadTypedValue(tag); + } + + private static object ReadTypedValue(XmlNode tag) + { + Object varValue; + string assembly; + + string itemType = tag.Attributes.GetNamedItem("type").Value; + + if(itemType == "list") + return ReadList(tag); + + if(itemType == "libsecondlife.LLUUID") + { + LLUUID val = new LLUUID(); + LLUUID.TryParse(tag.InnerText, out val); + + return val; + } + + Type itemT = Type.GetType(itemType); + if(itemT == null) + { + Object[] args = + new Object[] { tag.InnerText }; + + assembly = itemType+", OpenSim.Region.ScriptEngine.XEngine.Script"; + itemT = Type.GetType(assembly); + if(itemT == null) + return null; + + varValue = Activator.CreateInstance(itemT, args); + + if(varValue == null) + return null; + } + else + { + varValue = Convert.ChangeType(tag.InnerText, itemT); + } + return varValue; + } + } +} -- cgit v1.1