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; } } }