diff --git a/OpenSim/Region/ScriptEngine/Common/Executor.cs b/OpenSim/Region/ScriptEngine/Common/Executor.cs index cadd55c2c5..363d81e4ff 100644 --- a/OpenSim/Region/ScriptEngine/Common/Executor.cs +++ b/OpenSim/Region/ScriptEngine/Common/Executor.cs @@ -23,7 +23,7 @@ namespace OpenSim.Region.ScriptEngine.Common // Object never expires public override Object InitializeLifetimeService() { - Console.WriteLine("Executor: InitializeLifetimeService()"); + //Console.WriteLine("Executor: InitializeLifetimeService()"); // return null; ILease lease = (ILease)base.InitializeLifetimeService(); @@ -45,54 +45,60 @@ namespace OpenSim.Region.ScriptEngine.Common { // IMPORTANT: Types and MemberInfo-derived objects require a LOT of memory. // Instead use RuntimeTypeHandle, RuntimeFieldHandle and RunTimeHandle (IntPtr) instead! - - if (m_Running == false) + try { - // Script is inactive, do not execute! - return; - } + if (m_Running == false) + { + // Script is inactive, do not execute! + return; + } - string EventName = m_Script.State() + "_event_" + FunctionName; + string EventName = m_Script.State() + "_event_" + FunctionName; - //type.InvokeMember(EventName, BindingFlags.InvokeMethod, null, m_Script, args); + //type.InvokeMember(EventName, BindingFlags.InvokeMethod, null, m_Script, args); - Console.WriteLine("ScriptEngine Executor.ExecuteEvent: \"" + EventName + "\""); + Console.WriteLine("ScriptEngine Executor.ExecuteEvent: \"" + EventName + "\""); - if (Events.ContainsKey(EventName) == false) - { - // Not found, create - Type type = m_Script.GetType(); + if (Events.ContainsKey(EventName) == false) + { + // Not found, create + Type type = m_Script.GetType(); + try + { + MethodInfo mi = type.GetMethod(EventName); + Events.Add(EventName, mi); + } + catch (Exception e) + { + // Event name not found, cache it as not found + Events.Add(EventName, null); + } + } + + // Get event + MethodInfo ev = null; + Events.TryGetValue(EventName, out ev); + + if (ev == null) // No event by that name! + { + Console.WriteLine("ScriptEngine Can not find any event named: \"" + EventName + "\""); + return; + } + + // Found try { - MethodInfo mi = type.GetMethod(EventName); - Events.Add(EventName, mi); + // Invoke it + ev.Invoke(m_Script, args); + } catch (Exception e) { - // Event name not found, cache it as not found - Events.Add(EventName, null); + // TODO: Send to correct place + Console.WriteLine("ScriptEngine Exception attempting to executing script function: " + e.ToString()); } } - - // Get event - MethodInfo ev = null; - Events.TryGetValue(EventName, out ev); - - if (ev == null) // No event by that name! - return; - - // Found - try - { - // Invoke it - ev.Invoke(m_Script, args); - - } - catch (Exception e) - { - // TODO: Send to correct place - Console.WriteLine("ScriptEngine Exception attempting to executing script function: " + e.ToString()); - } + catch { } } diff --git a/OpenSim/Region/ScriptEngine/DotNetEngine/Compiler/LSL/Compiler.cs b/OpenSim/Region/ScriptEngine/DotNetEngine/Compiler/LSL/Compiler.cs index b58e996144..f24eb63cb9 100644 --- a/OpenSim/Region/ScriptEngine/DotNetEngine/Compiler/LSL/Compiler.cs +++ b/OpenSim/Region/ScriptEngine/DotNetEngine/Compiler/LSL/Compiler.cs @@ -51,6 +51,14 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.LSL // Output assembly name ScriptCompileCounter++; string OutFile = Path.Combine("ScriptEngines", "Script_" + ScriptCompileCounter + ".dll"); + try + { + System.IO.File.Delete(OutFile); + } + catch (Exception e) + { + Console.WriteLine("Exception attempting to delete old compiled script: " + e.ToString()); + } //string OutFile = Path.Combine("ScriptEngines", "SecondLife.Script.dll"); // DEBUG - write source to disk diff --git a/OpenSim/Region/ScriptEngine/DotNetEngine/Compiler/LSL/LSL_BaseClass.cs b/OpenSim/Region/ScriptEngine/DotNetEngine/Compiler/LSL/LSL_BaseClass.cs index cb0f9bad27..9af9c82b41 100644 --- a/OpenSim/Region/ScriptEngine/DotNetEngine/Compiler/LSL/LSL_BaseClass.cs +++ b/OpenSim/Region/ScriptEngine/DotNetEngine/Compiler/LSL/LSL_BaseClass.cs @@ -15,7 +15,7 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.LSL // Object never expires public override Object InitializeLifetimeService() { - Console.WriteLine("LSL_BaseClass: InitializeLifetimeService()"); + //Console.WriteLine("LSL_BaseClass: InitializeLifetimeService()"); // return null; ILease lease = (ILease)base.InitializeLifetimeService(); diff --git a/OpenSim/Region/ScriptEngine/DotNetEngine/Compiler/Server_API/LSL_BuiltIn_Commands.cs b/OpenSim/Region/ScriptEngine/DotNetEngine/Compiler/Server_API/LSL_BuiltIn_Commands.cs index 187ac59fc3..bfee3e5a98 100644 --- a/OpenSim/Region/ScriptEngine/DotNetEngine/Compiler/Server_API/LSL_BuiltIn_Commands.cs +++ b/OpenSim/Region/ScriptEngine/DotNetEngine/Compiler/Server_API/LSL_BuiltIn_Commands.cs @@ -19,15 +19,20 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine.Compiler { private System.Text.ASCIIEncoding enc = new System.Text.ASCIIEncoding(); - private ScriptManager m_manager; + private ScriptEngine m_ScriptEngine; private IScriptHost m_host; + private uint m_localID; + private LLUUID m_itemID; - public LSL_BuiltIn_Commands(ScriptManager manager, IScriptHost host) + public LSL_BuiltIn_Commands(ScriptEngine ScriptEngine, IScriptHost host, uint localID, LLUUID itemID) { - m_manager = manager; + m_ScriptEngine = ScriptEngine; m_host = host; + m_localID = localID; + m_itemID = itemID; + - MainLog.Instance.Notice("ScriptEngine", "LSL_BaseClass.Start() called. Hosted by [" + m_host.Name + ":" + m_host.UUID + "@" + m_host.AbsolutePosition + "]"); + //MainLog.Instance.Notice("ScriptEngine", "LSL_BaseClass.Start() called. Hosted by [" + m_host.Name + ":" + m_host.UUID + "@" + m_host.AbsolutePosition + "]"); } @@ -39,7 +44,7 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine.Compiler // Object never expires public override Object InitializeLifetimeService() { - Console.WriteLine("LSL_BuiltIn_Commands: InitializeLifetimeService()"); + //Console.WriteLine("LSL_BuiltIn_Commands: InitializeLifetimeService()"); // return null; ILease lease = (ILease)base.InitializeLifetimeService(); @@ -55,7 +60,7 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine.Compiler public Scene World { - get { return m_manager.World; } + get { return m_ScriptEngine.World; } } //These are the implementations of the various ll-functions used by the LSL scripts. @@ -258,7 +263,11 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine.Compiler public void llRezObject(string inventory, LSL_Types.Vector3 pos, LSL_Types.Quaternion rot, int param) { } public void llLookAt(LSL_Types.Vector3 target, double strength, double damping) { } public void llStopLookAt() { } - public void llSetTimerEvent(double sec) { } + public void llSetTimerEvent(double sec) + { + // Setting timer repeat + m_ScriptEngine.myLSLLongCmdHandler.SetTimerEvent(m_localID, m_itemID, sec); + } public void llSleep(double sec) { System.Threading.Thread.Sleep((int)(sec * 1000)); } public double llGetMass() { return 0; } public void llCollisionFilter(string name, string id, int accept) { } @@ -416,7 +425,7 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine.Compiler public LSL_Types.Vector3 llGroundContour(LSL_Types.Vector3 offset) { return new LSL_Types.Vector3(); } public int llGetAttached() { return 0; } public int llGetFreeMemory() { return 0; } - public string llGetRegionName() { return m_manager.RegionName; } + public string llGetRegionName() { return World.RegionInfo.RegionName; } public double llGetRegionTimeDilation() { return 1.0f; } public double llGetRegionFPS() { return 10.0f; } public void llParticleSystem(List rules) { } diff --git a/OpenSim/Region/ScriptEngine/DotNetEngine/EventManager.cs b/OpenSim/Region/ScriptEngine/DotNetEngine/EventManager.cs index 986d333628..9c8c29a2e1 100644 --- a/OpenSim/Region/ScriptEngine/DotNetEngine/EventManager.cs +++ b/OpenSim/Region/ScriptEngine/DotNetEngine/EventManager.cs @@ -77,7 +77,6 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine //); Console.WriteLine("OnRezScript localID: " + localID + " LLUID: " + itemID.ToString() + " Size: " + script.Length); myScriptEngine.myScriptManager.StartScript(localID, itemID, script); - myScriptEngine.myEventQueueManager.AddToObjectQueue(localID, "state_entry", new object[] { }); } public void OnRemoveScript(uint localID, LLUUID itemID) { diff --git a/OpenSim/Region/ScriptEngine/DotNetEngine/EventQueueManager.cs b/OpenSim/Region/ScriptEngine/DotNetEngine/EventQueueManager.cs index 1cab01e1fc..c62e86238d 100644 --- a/OpenSim/Region/ScriptEngine/DotNetEngine/EventQueueManager.cs +++ b/OpenSim/Region/ScriptEngine/DotNetEngine/EventQueueManager.cs @@ -226,6 +226,7 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine } } + /// /// Add event to event execution queue /// @@ -237,35 +238,45 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine // Determine all scripts in Object and add to their queue //myScriptEngine.m_logger.Verbose("ScriptEngine", "EventQueueManager Adding localID: " + localID + ", FunctionName: " + FunctionName); - lock (QueueLock) + + // Do we have any scripts in this object at all? If not, return + if (myScriptEngine.myScriptManager.Scripts.ContainsKey(localID) == false) { + //Console.WriteLine("Event \"" + FunctionName + "\" for localID: " + localID + ". No scripts found on this localID."); + return; + } - // Do we have any scripts in this object at all? If not, return - if (myScriptEngine.myScriptManager.Scripts.ContainsKey(localID) == false) - { - //Console.WriteLine("Event \"" + FunctionName + "\" for localID: " + localID + ". No scripts found on this localID."); - return; - } - - foreach (LLUUID itemID in myScriptEngine.myScriptManager.GetScriptKeys(localID)) - { - // Add to each script in that object - // TODO: Some scripts may not subscribe to this event. Should we NOT add it? Does it matter? - - // Create a structure and add data - QueueItemStruct QIS = new QueueItemStruct(); - QIS.localID = localID; - QIS.itemID = itemID; - QIS.FunctionName = FunctionName; - QIS.param = param; - - // Add it to queue - EventQueue.Enqueue(QIS); - - } + foreach (LLUUID itemID in new System.Collections.ArrayList(myScriptEngine.myScriptManager.GetScriptKeys(localID))) + { + // Add to each script in that object + // TODO: Some scripts may not subscribe to this event. Should we NOT add it? Does it matter? + AddToScriptQueue(localID, itemID, FunctionName, param); } } + /// + /// Add event to event execution queue + /// + /// + /// + /// Name of the function, will be state + "_event_" + FunctionName + /// Array of parameters to match event mask + public void AddToScriptQueue(uint localID, LLUUID itemID, string FunctionName, object[] param) + { + lock (QueueLock) + { + // Create a structure and add data + QueueItemStruct QIS = new QueueItemStruct(); + QIS.localID = localID; + QIS.itemID = itemID; + QIS.FunctionName = FunctionName; + QIS.param = param; + + // Add it to queue + EventQueue.Enqueue(QIS); + } + } + } } diff --git a/OpenSim/Region/ScriptEngine/DotNetEngine/LSLLongCmdHandler.cs b/OpenSim/Region/ScriptEngine/DotNetEngine/LSLLongCmdHandler.cs new file mode 100644 index 0000000000..e2c039c964 --- /dev/null +++ b/OpenSim/Region/ScriptEngine/DotNetEngine/LSLLongCmdHandler.cs @@ -0,0 +1,145 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; +using libsecondlife; + +namespace OpenSim.Region.ScriptEngine.DotNetEngine +{ + /// + /// Handles LSL commands that takes long time and returns an event, for example timers, HTTP requests, etc. + /// + class LSLLongCmdHandler + { + private Thread CmdHandlerThread; + private int CmdHandlerThreadCycleSleepms = 100; + + private ScriptEngine myScriptEngine; + public LSLLongCmdHandler(ScriptEngine _ScriptEngine) + { + myScriptEngine = _ScriptEngine; + + // Start the thread that will be doing the work + CmdHandlerThread = new Thread(CmdHandlerThreadLoop); + CmdHandlerThread.Name = "CmdHandlerThread"; + CmdHandlerThread.Priority = ThreadPriority.BelowNormal; + CmdHandlerThread.IsBackground = true; + CmdHandlerThread.Start(); + } + ~LSLLongCmdHandler() + { + // Shut down thread + try + { + if (CmdHandlerThread != null) + { + if (CmdHandlerThread.IsAlive == true) + { + CmdHandlerThread.Abort(); + CmdHandlerThread.Join(); + } + } + } + catch { } + } + + private void CmdHandlerThreadLoop() + { + while (true) + { + // Check timers + CheckTimerEvents(); + + // Sleep before next cycle + Thread.Sleep(CmdHandlerThreadCycleSleepms); + } + } + + /// + /// Remove a specific script (and all its pending commands) + /// + /// + /// + public void RemoveScript(uint m_localID, LLUUID m_itemID) + { + // Remove a specific script + + // Remove from: Timers + UnSetTimerEvents(m_localID, m_itemID); + } + + + // + // TIMER + // + private class TimerClass + { + public uint localID; + public LLUUID itemID; + public double interval; + public DateTime next; + } + private List Timers = new List(); + private object ListLock = new object(); + public void SetTimerEvent(uint m_localID, LLUUID m_itemID, double sec) + { + Console.WriteLine("SetTimerEvent"); + + // Always remove first, in case this is a re-set + UnSetTimerEvents(m_localID, m_itemID); + if (sec == 0) // Disabling timer + return; + + // Add to timer + TimerClass ts = new TimerClass(); + ts.localID = m_localID; + ts.itemID = m_itemID; + ts.interval = sec; + ts.next = DateTime.Now.ToUniversalTime().AddSeconds(ts.interval); + lock (ListLock) + { + Timers.Add(ts); + } + } + public void UnSetTimerEvents(uint m_localID, LLUUID m_itemID) + { + // Remove from timer + lock (ListLock) + { + List NewTimers = new List(); + foreach (TimerClass ts in Timers) + { + if (ts.localID != m_localID && ts.itemID != m_itemID) + { + NewTimers.Add(ts); + } + } + Timers.Clear(); + Timers = NewTimers; + } + } + public void CheckTimerEvents() + { + // Nothing to do here? + if (Timers.Count == 0) + return; + + // Go through all timers + foreach (TimerClass ts in Timers) + { + // Time has passed? + if (ts.next.ToUniversalTime() < DateTime.Now.ToUniversalTime()) + { + // Add it to queue + //Console.WriteLine("Enqueue timer event: " + ts.next.ToUniversalTime().ToString("HH:mm:ss") + " > " + DateTime.Now.ToUniversalTime().ToString("HH:mm:ss")); + myScriptEngine.myEventQueueManager.AddToScriptQueue(ts.localID, ts.itemID, "timer", new object[] { }); + // set next interval + + + ts.next = DateTime.Now.ToUniversalTime().AddSeconds(ts.interval); + } + } + } + + } +} diff --git a/OpenSim/Region/ScriptEngine/DotNetEngine/ScriptEngine.cs b/OpenSim/Region/ScriptEngine/DotNetEngine/ScriptEngine.cs index 77bd409d2f..d0823d059b 100644 --- a/OpenSim/Region/ScriptEngine/DotNetEngine/ScriptEngine.cs +++ b/OpenSim/Region/ScriptEngine/DotNetEngine/ScriptEngine.cs @@ -48,6 +48,7 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine internal EventQueueManager myEventQueueManager; // Executes events internal ScriptManager myScriptManager; // Load, unload and execute scripts internal AppDomainManager myAppDomainManager; + internal LSLLongCmdHandler myLSLLongCmdHandler; private OpenSim.Framework.Console.LogBase m_log; @@ -77,6 +78,7 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine myEventManager = new EventManager(this); myScriptManager = new ScriptManager(this); myAppDomainManager = new AppDomainManager(); + myLSLLongCmdHandler = new LSLLongCmdHandler(this); // Should we iterate the region for scripts that needs starting? // Or can we assume we are loaded before anything else so we can use proper events? diff --git a/OpenSim/Region/ScriptEngine/DotNetEngine/ScriptManager.cs b/OpenSim/Region/ScriptEngine/DotNetEngine/ScriptManager.cs index 9621e56f60..29171a19cb 100644 --- a/OpenSim/Region/ScriptEngine/DotNetEngine/ScriptManager.cs +++ b/OpenSim/Region/ScriptEngine/DotNetEngine/ScriptManager.cs @@ -249,6 +249,8 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine { + + // Create a new instance of the compiler (currently we don't want reuse) OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.LSL.Compiler LSLCompiler = new OpenSim.Region.ScriptEngine.DotNetEngine.Compiler.LSL.Compiler(); // Compile (We assume LSL) @@ -272,11 +274,15 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine // 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(this, World.GetSceneObjectPart(localID)); + LSL_BuiltIn_Commands LSLB = new LSL_BuiltIn_Commands(m_scriptEngine, World.GetSceneObjectPart(localID), localID, itemID); // Start the script - giving it BuiltIns CompiledScript.Start(LSLB); + // Fire the first start-event + m_scriptEngine.myEventQueueManager.AddToObjectQueue(localID, "state_entry", new object[] { }); + + } catch (Exception e) { @@ -291,6 +297,9 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine // Stop script Console.WriteLine("Stop script localID: " + localID + " LLUID: " + itemID.ToString()); + // Stop long command on script + m_scriptEngine.myLSLLongCmdHandler.RemoveScript(localID, itemID); + // Get AppDomain AppDomain ad = GetScript(localID, itemID).Exec.GetAppDomain(); // Tell script not to accept new requests @@ -328,12 +337,5 @@ namespace OpenSim.Region.ScriptEngine.DotNetEngine } - public string RegionName - { - get - { - return World.RegionInfo.RegionName; - } - } } }