From bacbade369a5244f9bcc611488b59f3bd4c8a564 Mon Sep 17 00:00:00 2001 From: Tedd Hansen Date: Sat, 12 Jan 2008 14:30:22 +0000 Subject: Major reorganizing of DotNetEngine. Moved common script engine parts to ScriptEngine.Common, only .Net-specific code in DotNetEngine. AppDomains, event handling, event execution queue and multithreading, script load/unload queue, etc has been moved to ScriptEngine.Common. Loads of things has been put into interfaces instead of the specific class. We are now one step closer to ScriptServer, and its very easy to implement new script languages. Just a few lines required to make them a OpenSim script module with all its glory. --- .../ScriptEngine/DotNetEngine/ScriptManager.cs | 438 ++++----------------- 1 file changed, 72 insertions(+), 366 deletions(-) (limited to 'OpenSim/Region/ScriptEngine/DotNetEngine/ScriptManager.cs') diff --git a/OpenSim/Region/ScriptEngine/DotNetEngine/ScriptManager.cs b/OpenSim/Region/ScriptEngine/DotNetEngine/ScriptManager.cs index 223bb8f..0c28ac7 100644 --- a/OpenSim/Region/ScriptEngine/DotNetEngine/ScriptManager.cs +++ b/OpenSim/Region/ScriptEngine/DotNetEngine/ScriptManager.cs @@ -35,421 +35,127 @@ using System.Threading; using libsecondlife; using OpenSim.Framework; using OpenSim.Region.Environment.Scenes; -using OpenSim.Region.ScriptEngine.DotNetEngine.Compiler; +using OpenSim.Region.ScriptEngine.Common; using OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.LSL; namespace OpenSim.Region.ScriptEngine.DotNetEngine { - /// - /// Loads scripts - /// Compiles them if necessary - /// Execute functions for EventQueueManager (Sends them to script on other AppDomain for execution) - /// - /// - - // This class is as close as you get to the script without being inside script class. It handles all the dirty work for other classes. - // * Keeps track of running scripts - // * Compiles script if necessary (through "Compiler") - // * Loads script (through "AppDomainManager" called from for example "EventQueueManager") - // * Executes functions inside script (called from for example "EventQueueManager" class) - // * Unloads script (through "AppDomainManager" called from for example "EventQueueManager") - // * Dedicated load/unload thread, and queues loading/unloading. - // This so that scripts starting or stopping will not slow down other theads or whole system. - // - [Serializable] - public class ScriptManager + public class ScriptManager : OpenSim.Region.ScriptEngine.Common.ScriptEngineBase.ScriptManager { - #region Declares - - private Thread scriptLoadUnloadThread; - private int scriptLoadUnloadThread_IdleSleepms = 100; - private Queue LUQueue = new Queue(); - - - // Load/Unload structure - private struct LUStruct + public ScriptManager(Common.ScriptEngineBase.ScriptEngine scriptEngine) + : base(scriptEngine) { - public uint localID; - public LLUUID itemID; - public string script; - public LUType Action; - } - - private enum LUType - { - Unknown = 0, - Load = 1, - Unload = 2 - } + base.m_scriptEngine = scriptEngine; - // Object> - // IMPORTANT: Types and MemberInfo-derived objects require a LOT of memory. - // Instead use RuntimeTypeHandle, RuntimeFieldHandle and RunTimeHandle (IntPtr) instead! - internal Dictionary> Scripts = - new Dictionary>(); - - public Scene World - { - get { return m_scriptEngine.World; } } - #endregion - - #region Object init/shutdown - - private ScriptEngine m_scriptEngine; + // KEEP TRACK OF SCRIPTS + //internal Dictionary> Scripts = new Dictionary>(); + // LOAD SCRIPT + // UNLOAD SCRIPT + // PROVIDE SCRIPT WITH ITS INTERFACE TO OpenSim - public ScriptManager(ScriptEngine scriptEngine) - { - m_scriptEngine = scriptEngine; - AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve); - scriptLoadUnloadThread = new Thread(ScriptLoadUnloadThreadLoop); - scriptLoadUnloadThread.Name = "ScriptLoadUnloadThread"; - scriptLoadUnloadThread.IsBackground = true; - scriptLoadUnloadThread.Priority = ThreadPriority.BelowNormal; - scriptLoadUnloadThread.Start(); - } + private Compiler.LSL.Compiler LSLCompiler = new Compiler.LSL.Compiler(); - ~ScriptManager() + public override void _StartScript(uint localID, LLUUID itemID, string Script) { - // Abort load/unload thread - try - { - if (scriptLoadUnloadThread != null) - { - if (scriptLoadUnloadThread.IsAlive == true) - { - scriptLoadUnloadThread.Abort(); - scriptLoadUnloadThread.Join(); - } - } - } - catch - { - } - } + //IScriptHost root = host.GetRoot(); + Console.WriteLine("ScriptManager StartScript: localID: " + localID + ", itemID: " + itemID); - #endregion + // We will initialize and start the script. + // It will be up to the script itself to hook up the correct events. + string ScriptSource = ""; - #region Load / Unload scripts (Thread loop) + SceneObjectPart m_host = World.GetSceneObjectPart(localID); - private void ScriptLoadUnloadThreadLoop() - { try { - while (true) - { - if (LUQueue.Count == 0) - Thread.Sleep(scriptLoadUnloadThread_IdleSleepms); - if (LUQueue.Count > 0) - { - LUStruct item = LUQueue.Dequeue(); - if (item.Action == LUType.Unload) - _StopScript(item.localID, item.itemID); - if (item.Action == LUType.Load) - _StartScript(item.localID, item.itemID, item.script); - } - } - } - catch (ThreadAbortException tae) - { - string a = tae.ToString(); - a = ""; - // Expected - } - } - - #endregion - - #region Helper functions - - private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) - { - //Console.WriteLine("ScriptManager.CurrentDomain_AssemblyResolve: " + args.Name); - return Assembly.GetExecutingAssembly().FullName == args.Name ? Assembly.GetExecutingAssembly() : null; - } - - #endregion - - #region Internal functions to keep track of script - - internal Dictionary.KeyCollection GetScriptKeys(uint localID) - { - if (Scripts.ContainsKey(localID) == false) - return null; - - Dictionary Obj; - Scripts.TryGetValue(localID, out Obj); - - return Obj.Keys; - } - - internal LSL_BaseClass GetScript(uint localID, LLUUID itemID) - { - if (Scripts.ContainsKey(localID) == false) - return null; - - Dictionary Obj; - Scripts.TryGetValue(localID, out Obj); - if (Obj.ContainsKey(itemID) == false) - return null; - - // Get script - LSL_BaseClass Script; - Obj.TryGetValue(itemID, out Script); - - return Script; - } - - internal void SetScript(uint localID, LLUUID itemID, LSL_BaseClass Script) - { - // Create object if it doesn't exist - if (Scripts.ContainsKey(localID) == false) - { - Scripts.Add(localID, new Dictionary()); - } - - // Delete script if it exists - Dictionary Obj; - Scripts.TryGetValue(localID, out Obj); - if (Obj.ContainsKey(itemID) == true) - Obj.Remove(itemID); - - // Add to object - Obj.Add(itemID, Script); - } - - internal void RemoveScript(uint localID, LLUUID itemID) - { - // Don't have that object? - if (Scripts.ContainsKey(localID) == false) - return; - - // Delete script if it exists - Dictionary Obj; - Scripts.TryGetValue(localID, out Obj); - if (Obj.ContainsKey(itemID) == true) - Obj.Remove(itemID); - } - - #endregion - - #region Start/Stop/Reset script - - private Object startStopLock = new Object(); - - /// - /// Fetches, loads and hooks up a script to an objects events - /// - /// - /// - public void StartScript(uint localID, LLUUID itemID, string Script) - { - LUStruct ls = new LUStruct(); - ls.localID = localID; - ls.itemID = itemID; - ls.script = Script; - ls.Action = LUType.Load; - LUQueue.Enqueue(ls); - } - - /// - /// Disables and unloads a script - /// - /// - /// - public void StopScript(uint localID, LLUUID itemID) - { - LUStruct ls = new LUStruct(); - ls.localID = localID; - ls.itemID = itemID; - ls.Action = LUType.Unload; - LUQueue.Enqueue(ls); - } - - public void ResetScript(uint localID, LLUUID itemID) - { - string script = GetScript(localID, itemID).SourceCode; - StopScript(localID, itemID); - StartScript(localID, itemID, script); - } - - // Create a new instance of the compiler (reuse) - private Compiler.LSL.Compiler LSLCompiler = new Compiler.LSL.Compiler(); - - private void _StartScript(uint localID, LLUUID itemID, string Script) - { - lock (startStopLock) - { - //IScriptHost root = host.GetRoot(); - Console.WriteLine("ScriptManager StartScript: localID: " + localID + ", itemID: " + itemID); - - // We will initialize and start the script. - // It will be up to the script itself to hook up the correct events. - string ScriptSource = ""; - - SceneObjectPart m_host = World.GetSceneObjectPart(localID); - - try - { - if (!Script.EndsWith("dll")) - { - // Compile (We assume LSL) - ScriptSource = LSLCompiler.CompileFromLSLText(Script); - //Console.WriteLine("Compilation of " + FileName + " done"); - // * Insert yield into code - ScriptSource = ProcessYield(ScriptSource); - } - else - { - ScriptSource = Script; - } + // Compile (We assume LSL) + ScriptSource = LSLCompiler.CompileFromLSLText(Script); #if DEBUG - long before; - before = GC.GetTotalMemory(true); + long before; + before = GC.GetTotalMemory(true); #endif - LSL_BaseClass CompiledScript; - CompiledScript = m_scriptEngine.m_AppDomainManager.LoadScript(ScriptSource); + IScript CompiledScript; + CompiledScript = m_scriptEngine.m_AppDomainManager.LoadScript(ScriptSource); #if DEBUG - Console.WriteLine("Script " + itemID + " occupies {0} bytes", GC.GetTotalMemory(true) - before); + Console.WriteLine("Script " + itemID + " occupies {0} bytes", GC.GetTotalMemory(true) - before); #endif - CompiledScript.SourceCode = ScriptSource; - // Add it to our script memstruct - SetScript(localID, itemID, CompiledScript); + CompiledScript.Source = ScriptSource; + // Add it to our script memstruct + SetScript(localID, itemID, CompiledScript); - // We need to give (untrusted) assembly a private instance of BuiltIns - // this private copy will contain Read-Only FullitemID so that it can bring that on to the server whenever needed. + // We need to give (untrusted) assembly a private instance of BuiltIns + // this private copy will contain Read-Only FullitemID so that it can bring that on to the server whenever needed. - LSL_BuiltIn_Commands LSLB = new LSL_BuiltIn_Commands(m_scriptEngine, m_host, localID, itemID); + LSL_BuiltIn_Commands LSLB = new LSL_BuiltIn_Commands(m_scriptEngine, m_host, localID, itemID); - // Start the script - giving it BuiltIns - CompiledScript.Start(LSLB); + // Start the script - giving it BuiltIns + CompiledScript.Start(LSLB); - // Fire the first start-event - m_scriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "state_entry", new object[] {}); - } - catch (Exception e) - { - //m_scriptEngine.Log.Error("ScriptEngine", "Error compiling script: " + e.ToString()); - try - { - // DISPLAY ERROR INWORLD - string text = "Error compiling script:\r\n" + e.Message.ToString(); - if (text.Length > 1500) - text = text.Substring(0, 1500); - World.SimChat(Helpers.StringToField(text), ChatTypeEnum.Say, 0, m_host.AbsolutePosition, - m_host.Name, m_host.UUID); - } - catch (Exception e2) - { - m_scriptEngine.Log.Error("ScriptEngine", "Error displaying error in-world: " + e2.ToString()); - m_scriptEngine.Log.Error("ScriptEngine", - "Errormessage: Error compiling script:\r\n" + e.Message.ToString()); - } - } + // Fire the first start-event + m_scriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "state_entry", new object[] { }); } - } - - private void _StopScript(uint localID, LLUUID itemID) - { - lock (startStopLock) + catch (Exception e) { - // Stop script - Console.WriteLine("Stop script localID: " + localID + " LLUID: " + itemID.ToString()); - - - // Stop long command on script - m_scriptEngine.m_LSLLongCmdHandler.RemoveScript(localID, itemID); - - LSL_BaseClass LSLBC = GetScript(localID, itemID); - if (LSLBC == null) - return; - - // TEMP: First serialize it - //GetSerializedScript(localID, itemID); - - + //m_scriptEngine.Log.Error("ScriptEngine", "Error compiling script: " + e.ToString()); try { - // Get AppDomain - AppDomain ad = LSLBC.Exec.GetAppDomain(); - // Tell script not to accept new requests - GetScript(localID, itemID).Exec.StopScript(); - // Remove from internal structure - RemoveScript(localID, itemID); - // Tell AppDomain that we have stopped script - m_scriptEngine.m_AppDomainManager.StopScript(ad); + // DISPLAY ERROR INWORLD + string text = "Error compiling script:\r\n" + e.Message.ToString(); + if (text.Length > 1500) + text = text.Substring(0, 1500); + World.SimChat(Helpers.StringToField(text), ChatTypeEnum.Say, 0, m_host.AbsolutePosition, + m_host.Name, m_host.UUID); } - catch (Exception e) + catch (Exception e2) { - Console.WriteLine("Exception stopping script localID: " + localID + " LLUID: " + itemID.ToString() + - ": " + e.ToString()); + m_scriptEngine.Log.Error("ScriptEngine", "Error displaying error in-world: " + e2.ToString()); + m_scriptEngine.Log.Error("ScriptEngine", + "Errormessage: Error compiling script:\r\n" + e.Message.ToString()); } } } - private string ProcessYield(string FileName) + public override void _StopScript(uint localID, LLUUID itemID) { - // TODO: Create a new assembly and copy old but insert Yield Code - //return TempDotNetMicroThreadingCodeInjector.TestFix(FileName); - return FileName; - } + // Stop script + Console.WriteLine("Stop script localID: " + localID + " LLUID: " + itemID.ToString()); - #endregion - #region Perform event execution in script + // Stop long command on script + m_scriptEngine.m_LSLLongCmdHandler.RemoveScript(localID, itemID); - /// - /// Execute a LL-event-function in Script - /// - /// Object the script is located in - /// Script ID - /// Name of function - /// Arguments to pass to function - internal void ExecuteEvent(uint localID, LLUUID itemID, string FunctionName, object[] args) - { -#if DEBUG - Console.WriteLine("ScriptEngine: Inside ExecuteEvent for event " + FunctionName); -#endif - // Execute a function in the script - //m_scriptEngine.Log.Verbose("ScriptEngine", "Executing Function localID: " + localID + ", itemID: " + itemID + ", FunctionName: " + FunctionName); - LSL_BaseClass Script = m_scriptEngine.m_ScriptManager.GetScript(localID, itemID); - if (Script == null) + IScript LSLBC = GetScript(localID, itemID); + if (LSLBC == null) return; -#if DEBUG - Console.WriteLine("ScriptEngine: Executing event: " + FunctionName); -#endif - // Must be done in correct AppDomain, so leaving it up to the script itself - Script.Exec.ExecuteEvent(FunctionName, args); - } + // TEMP: First serialize it + //GetSerializedScript(localID, itemID); - #endregion - #region Script serialization/deserialization - - public void GetSerializedScript(uint localID, LLUUID itemID) - { - // Serialize the script and return it - // Should not be a problem - FileStream fs = File.Create("SERIALIZED_SCRIPT_" + itemID); - BinaryFormatter b = new BinaryFormatter(); - b.Serialize(fs, GetScript(localID, itemID)); - fs.Close(); - } - - public void PutSerializedScript(uint localID, LLUUID itemID) - { - // Deserialize the script and inject it into an AppDomain - - // How to inject into an AppDomain? + try + { + // Get AppDomain + AppDomain ad = LSLBC.Exec.GetAppDomain(); + // Tell script not to accept new requests + GetScript(localID, itemID).Exec.StopScript(); + // Remove from internal structure + RemoveScript(localID, itemID); + // Tell AppDomain that we have stopped script + m_scriptEngine.m_AppDomainManager.StopScript(ad); + } + catch (Exception e) + { + Console.WriteLine("Exception stopping script localID: " + localID + " LLUID: " + itemID.ToString() + + ": " + e.ToString()); + } } - #endregion } } \ No newline at end of file -- cgit v1.1