From 0081c060d0bb66e842a381e1a15a6cf01c0bd793 Mon Sep 17 00:00:00 2001 From: Tedd Hansen Date: Sat, 12 Jan 2008 14:45:59 +0000 Subject: Set eol --- .../Common/ScriptEngineBase/AppDomainManager.cs | 476 +++++++------- .../ScriptEngine/Common/ScriptEngineBase/Common.cs | 114 ++-- .../Common/ScriptEngineBase/EventManager.cs | 518 +++++++-------- .../Common/ScriptEngineBase/EventQueueManager.cs | 726 ++++++++++----------- .../Common/ScriptEngineBase/LSLLongCmdHandler.cs | 588 ++++++++--------- .../Common/ScriptEngineBase/ScriptEngine.cs | 262 ++++---- .../Common/ScriptEngineBase/ScriptManager.cs | 694 ++++++++++---------- 7 files changed, 1689 insertions(+), 1689 deletions(-) (limited to 'OpenSim/Region/ScriptEngine/Common/ScriptEngineBase') diff --git a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/AppDomainManager.cs b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/AppDomainManager.cs index df00d4e..68ad88d 100644 --- a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/AppDomainManager.cs +++ b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/AppDomainManager.cs @@ -1,239 +1,239 @@ -/* -* Copyright (c) Contributors, http://opensimulator.org/ -* See CONTRIBUTORS.TXT for a full list of copyright holders. -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions are met: -* * Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* * Redistributions in binary form must reproduce the above copyright -* notice, this list of conditions and the following disclaimer in the -* documentation and/or other materials provided with the distribution. -* * Neither the name of the OpenSim Project nor the -* names of its contributors may be used to endorse or promote products -* derived from this software without specific prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS AS IS AND ANY -* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY -* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -* -*/ - - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Reflection; -using OpenSim.Region.ScriptEngine.Common; - -namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase -{ - public class AppDomainManager - { - - // - // This class does AppDomain handling and loading/unloading of scripts in it. - // It is instanced in "ScriptEngine" and controlled from "ScriptManager" - // - // 1. Create a new AppDomain if old one is full (or doesn't exist) - // 2. Load scripts into AppDomain - // 3. Unload scripts from AppDomain (stopping them and marking them as inactive) - // 4. Unload AppDomain completely when all scripts in it has stopped - // - - - private int maxScriptsPerAppDomain = 1; - - /// - /// Internal list of all AppDomains - /// - private List appDomains = new List(); - - /// - /// Structure to keep track of data around AppDomain - /// - private class AppDomainStructure - { - /// - /// The AppDomain itself - /// - public AppDomain CurrentAppDomain; - - /// - /// Number of scripts loaded into AppDomain - /// - public int ScriptsLoaded; - - /// - /// Number of dead scripts - /// - public int ScriptsWaitingUnload; - } - - /// - /// Current AppDomain - /// - private AppDomainStructure currentAD; - - private object getLock = new object(); // Mutex - private object freeLock = new object(); // Mutex - - //private ScriptEngine m_scriptEngine; - //public AppDomainManager(ScriptEngine scriptEngine) - public AppDomainManager() - { - //m_scriptEngine = scriptEngine; - } - - /// - /// Find a free AppDomain, creating one if necessary - /// - /// Free AppDomain - private AppDomainStructure GetFreeAppDomain() - { - Console.WriteLine("Finding free AppDomain"); - lock (getLock) - { - // Current full? - if (currentAD != null && currentAD.ScriptsLoaded >= maxScriptsPerAppDomain) - { - // Add it to AppDomains list and empty current - appDomains.Add(currentAD); - currentAD = null; - } - // No current - if (currentAD == null) - { - // Create a new current AppDomain - currentAD = new AppDomainStructure(); - currentAD.CurrentAppDomain = PrepareNewAppDomain(); - } - - Console.WriteLine("Scripts loaded in this Appdomain: " + currentAD.ScriptsLoaded); - return currentAD; - } // lock - } - - private int AppDomainNameCount; - - /// - /// Create and prepare a new AppDomain for scripts - /// - /// The new AppDomain - private AppDomain PrepareNewAppDomain() - { - // Create and prepare a new AppDomain - AppDomainNameCount++; - // TODO: Currently security match current appdomain - - // Construct and initialize settings for a second AppDomain. - AppDomainSetup ads = new AppDomainSetup(); - ads.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory; - ads.DisallowBindingRedirects = false; - ads.DisallowCodeDownload = true; - ads.LoaderOptimization = LoaderOptimization.MultiDomain; // Sounds good ;) - ads.ShadowCopyFiles = "true"; // Enabled shadowing - ads.ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile; - - AppDomain AD = AppDomain.CreateDomain("ScriptAppDomain_" + AppDomainNameCount, null, ads); - Console.WriteLine("Loading: " + - AssemblyName.GetAssemblyName("OpenSim.Region.ScriptEngine.Common.dll").ToString()); - AD.Load(AssemblyName.GetAssemblyName("OpenSim.Region.ScriptEngine.Common.dll")); - - // Return the new AppDomain - return AD; - } - - /// - /// Unload appdomains that are full and have only dead scripts - /// - private void UnloadAppDomains() - { - lock (freeLock) - { - // Go through all - foreach (AppDomainStructure ads in new ArrayList(appDomains)) - { - // Don't process current AppDomain - if (ads.CurrentAppDomain != currentAD.CurrentAppDomain) - { - // Not current AppDomain - // Is number of unloaded bigger or equal to number of loaded? - if (ads.ScriptsLoaded <= ads.ScriptsWaitingUnload) - { - Console.WriteLine("Found empty AppDomain, unloading"); - // Remove from internal list - appDomains.Remove(ads); -#if DEBUG - long m = GC.GetTotalMemory(true); -#endif - // Unload - AppDomain.Unload(ads.CurrentAppDomain); -#if DEBUG - Console.WriteLine("AppDomain unload freed " + (m - GC.GetTotalMemory(true)) + - " bytes of memory"); -#endif - } - } - } // foreach - } // lock - } - - - public IScript LoadScript(string FileName) - { - // Find next available AppDomain to put it in - AppDomainStructure FreeAppDomain = GetFreeAppDomain(); - - Console.WriteLine("Loading into AppDomain: " + FileName); - IScript mbrt = - (IScript) - FreeAppDomain.CurrentAppDomain.CreateInstanceFromAndUnwrap(FileName, "SecondLife.Script"); - //Console.WriteLine("ScriptEngine AppDomainManager: is proxy={0}", RemotingServices.IsTransparentProxy(mbrt)); - FreeAppDomain.ScriptsLoaded++; - - return mbrt; - } - - - /// - /// Increase "dead script" counter for an AppDomain - /// - /// - //[Obsolete("Needs fixing, needs a real purpose in life!!!")] - public void StopScript(AppDomain ad) - { - lock (freeLock) - { - Console.WriteLine("Stopping script in AppDomain"); - // Check if it is current AppDomain - if (currentAD.CurrentAppDomain == ad) - { - // Yes - increase - currentAD.ScriptsWaitingUnload++; - return; - } - - // Lopp through all AppDomains - foreach (AppDomainStructure ads in new ArrayList(appDomains)) - { - if (ads.CurrentAppDomain == ad) - { - // Found it - ads.ScriptsWaitingUnload++; - break; - } - } // foreach - } // lock - - UnloadAppDomains(); // Outsite lock, has its own GetLock - } - } +/* +* Copyright (c) Contributors, http://opensimulator.org/ +* See CONTRIBUTORS.TXT for a full list of copyright holders. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the OpenSim Project nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS AS IS AND ANY +* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +*/ + + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Reflection; +using OpenSim.Region.ScriptEngine.Common; + +namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase +{ + public class AppDomainManager + { + + // + // This class does AppDomain handling and loading/unloading of scripts in it. + // It is instanced in "ScriptEngine" and controlled from "ScriptManager" + // + // 1. Create a new AppDomain if old one is full (or doesn't exist) + // 2. Load scripts into AppDomain + // 3. Unload scripts from AppDomain (stopping them and marking them as inactive) + // 4. Unload AppDomain completely when all scripts in it has stopped + // + + + private int maxScriptsPerAppDomain = 1; + + /// + /// Internal list of all AppDomains + /// + private List appDomains = new List(); + + /// + /// Structure to keep track of data around AppDomain + /// + private class AppDomainStructure + { + /// + /// The AppDomain itself + /// + public AppDomain CurrentAppDomain; + + /// + /// Number of scripts loaded into AppDomain + /// + public int ScriptsLoaded; + + /// + /// Number of dead scripts + /// + public int ScriptsWaitingUnload; + } + + /// + /// Current AppDomain + /// + private AppDomainStructure currentAD; + + private object getLock = new object(); // Mutex + private object freeLock = new object(); // Mutex + + //private ScriptEngine m_scriptEngine; + //public AppDomainManager(ScriptEngine scriptEngine) + public AppDomainManager() + { + //m_scriptEngine = scriptEngine; + } + + /// + /// Find a free AppDomain, creating one if necessary + /// + /// Free AppDomain + private AppDomainStructure GetFreeAppDomain() + { + Console.WriteLine("Finding free AppDomain"); + lock (getLock) + { + // Current full? + if (currentAD != null && currentAD.ScriptsLoaded >= maxScriptsPerAppDomain) + { + // Add it to AppDomains list and empty current + appDomains.Add(currentAD); + currentAD = null; + } + // No current + if (currentAD == null) + { + // Create a new current AppDomain + currentAD = new AppDomainStructure(); + currentAD.CurrentAppDomain = PrepareNewAppDomain(); + } + + Console.WriteLine("Scripts loaded in this Appdomain: " + currentAD.ScriptsLoaded); + return currentAD; + } // lock + } + + private int AppDomainNameCount; + + /// + /// Create and prepare a new AppDomain for scripts + /// + /// The new AppDomain + private AppDomain PrepareNewAppDomain() + { + // Create and prepare a new AppDomain + AppDomainNameCount++; + // TODO: Currently security match current appdomain + + // Construct and initialize settings for a second AppDomain. + AppDomainSetup ads = new AppDomainSetup(); + ads.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory; + ads.DisallowBindingRedirects = false; + ads.DisallowCodeDownload = true; + ads.LoaderOptimization = LoaderOptimization.MultiDomain; // Sounds good ;) + ads.ShadowCopyFiles = "true"; // Enabled shadowing + ads.ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile; + + AppDomain AD = AppDomain.CreateDomain("ScriptAppDomain_" + AppDomainNameCount, null, ads); + Console.WriteLine("Loading: " + + AssemblyName.GetAssemblyName("OpenSim.Region.ScriptEngine.Common.dll").ToString()); + AD.Load(AssemblyName.GetAssemblyName("OpenSim.Region.ScriptEngine.Common.dll")); + + // Return the new AppDomain + return AD; + } + + /// + /// Unload appdomains that are full and have only dead scripts + /// + private void UnloadAppDomains() + { + lock (freeLock) + { + // Go through all + foreach (AppDomainStructure ads in new ArrayList(appDomains)) + { + // Don't process current AppDomain + if (ads.CurrentAppDomain != currentAD.CurrentAppDomain) + { + // Not current AppDomain + // Is number of unloaded bigger or equal to number of loaded? + if (ads.ScriptsLoaded <= ads.ScriptsWaitingUnload) + { + Console.WriteLine("Found empty AppDomain, unloading"); + // Remove from internal list + appDomains.Remove(ads); +#if DEBUG + long m = GC.GetTotalMemory(true); +#endif + // Unload + AppDomain.Unload(ads.CurrentAppDomain); +#if DEBUG + Console.WriteLine("AppDomain unload freed " + (m - GC.GetTotalMemory(true)) + + " bytes of memory"); +#endif + } + } + } // foreach + } // lock + } + + + public IScript LoadScript(string FileName) + { + // Find next available AppDomain to put it in + AppDomainStructure FreeAppDomain = GetFreeAppDomain(); + + Console.WriteLine("Loading into AppDomain: " + FileName); + IScript mbrt = + (IScript) + FreeAppDomain.CurrentAppDomain.CreateInstanceFromAndUnwrap(FileName, "SecondLife.Script"); + //Console.WriteLine("ScriptEngine AppDomainManager: is proxy={0}", RemotingServices.IsTransparentProxy(mbrt)); + FreeAppDomain.ScriptsLoaded++; + + return mbrt; + } + + + /// + /// Increase "dead script" counter for an AppDomain + /// + /// + //[Obsolete("Needs fixing, needs a real purpose in life!!!")] + public void StopScript(AppDomain ad) + { + lock (freeLock) + { + Console.WriteLine("Stopping script in AppDomain"); + // Check if it is current AppDomain + if (currentAD.CurrentAppDomain == ad) + { + // Yes - increase + currentAD.ScriptsWaitingUnload++; + return; + } + + // Lopp through all AppDomains + foreach (AppDomainStructure ads in new ArrayList(appDomains)) + { + if (ads.CurrentAppDomain == ad) + { + // Found it + ads.ScriptsWaitingUnload++; + break; + } + } // foreach + } // lock + + UnloadAppDomains(); // Outsite lock, has its own GetLock + } + } } \ No newline at end of file diff --git a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/Common.cs b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/Common.cs index b2eea38..bce26ff 100644 --- a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/Common.cs +++ b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/Common.cs @@ -1,58 +1,58 @@ -/* -* Copyright (c) Contributors, http://opensimulator.org/ -* See CONTRIBUTORS.TXT for a full list of copyright holders. -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions are met: -* * Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* * Redistributions in binary form must reproduce the above copyright -* notice, this list of conditions and the following disclaimer in the -* documentation and/or other materials provided with the distribution. -* * Neither the name of the OpenSim Project nor the -* names of its contributors may be used to endorse or promote products -* derived from this software without specific prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS AS IS AND ANY -* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY -* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -* -*/ - - -namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase -{ - public static class Common - { - public static bool debug = true; - public static ScriptEngine mySE; - - // This class just contains some static log stuff used for debugging. - - //public delegate void SendToDebugEventDelegate(string Message); - //public delegate void SendToLogEventDelegate(string Message); - //static public event SendToDebugEventDelegate SendToDebugEvent; - //static public event SendToLogEventDelegate SendToLogEvent; - - public static void SendToDebug(string Message) - { - //if (Debug == true) - mySE.Log.Verbose("ScriptEngine", "Debug: " + Message); - //SendToDebugEvent("\r\n" + DateTime.Now.ToString("[HH:mm:ss] ") + Message); - } - - public static void SendToLog(string Message) - { - //if (Debug == true) - mySE.Log.Verbose("ScriptEngine", "LOG: " + Message); - //SendToLogEvent("\r\n" + DateTime.Now.ToString("[HH:mm:ss] ") + Message); - } - } +/* +* Copyright (c) Contributors, http://opensimulator.org/ +* See CONTRIBUTORS.TXT for a full list of copyright holders. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the OpenSim Project nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS AS IS AND ANY +* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +*/ + + +namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase +{ + public static class Common + { + public static bool debug = true; + public static ScriptEngine mySE; + + // This class just contains some static log stuff used for debugging. + + //public delegate void SendToDebugEventDelegate(string Message); + //public delegate void SendToLogEventDelegate(string Message); + //static public event SendToDebugEventDelegate SendToDebugEvent; + //static public event SendToLogEventDelegate SendToLogEvent; + + public static void SendToDebug(string Message) + { + //if (Debug == true) + mySE.Log.Verbose("ScriptEngine", "Debug: " + Message); + //SendToDebugEvent("\r\n" + DateTime.Now.ToString("[HH:mm:ss] ") + Message); + } + + public static void SendToLog(string Message) + { + //if (Debug == true) + mySE.Log.Verbose("ScriptEngine", "LOG: " + Message); + //SendToLogEvent("\r\n" + DateTime.Now.ToString("[HH:mm:ss] ") + Message); + } + } } \ No newline at end of file diff --git a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventManager.cs b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventManager.cs index 07476ac..6df8343 100644 --- a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventManager.cs +++ b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventManager.cs @@ -1,260 +1,260 @@ -/* -* Copyright (c) Contributors, http://opensimulator.org/ -* See CONTRIBUTORS.TXT for a full list of copyright holders. -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions are met: -* * Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* * Redistributions in binary form must reproduce the above copyright -* notice, this list of conditions and the following disclaimer in the -* documentation and/or other materials provided with the distribution. -* * Neither the name of the OpenSim Project nor the -* names of its contributors may be used to endorse or promote products -* derived from this software without specific prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS AS IS AND ANY -* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY -* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -* -*/ - - -using System; -using libsecondlife; -using OpenSim.Framework; - -namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase -{ - /// - /// Prepares events so they can be directly executed upon a script by EventQueueManager, then queues it. - /// - [Serializable] - public class EventManager : OpenSim.Region.ScriptEngine.Common.ScriptServerInterfaces.RemoteEvents - { - - // - // Class is instanced in "ScriptEngine" and Uses "EventQueueManager" that is also instanced in "ScriptEngine". - // This class needs a bit of explaining: - // - // This class it the link between an event inside OpenSim and the corresponding event in a user script being executed. - // - // For example when an user touches an object then the "myScriptEngine.World.EventManager.OnObjectGrab" event is fired inside OpenSim. - // We hook up to this event and queue a touch_start in EventQueueManager with the proper LSL parameters. - // It will then be delivered to the script by EventQueueManager. - // - // You can check debug C# dump of an LSL script if you need to verify what exact parameters are needed. - // - - - private ScriptEngine myScriptEngine; - //public IScriptHost TEMP_OBJECT_ID; - public EventManager(ScriptEngine _ScriptEngine, bool performHookUp) - { - myScriptEngine = _ScriptEngine; - - // Hook up to events from OpenSim - // We may not want to do it because someone is controlling us and will deliver events to us - if (performHookUp) - { - myScriptEngine.Log.Verbose("ScriptEngine", "Hooking up to server events"); - myScriptEngine.World.EventManager.OnObjectGrab += touch_start; - myScriptEngine.World.EventManager.OnRezScript += OnRezScript; - myScriptEngine.World.EventManager.OnRemoveScript += OnRemoveScript; - // TODO: HOOK ALL EVENTS UP TO SERVER! - } - } - - public void touch_start(uint localID, LLVector3 offsetPos, IClientAPI remoteClient) - { - // Add to queue for all scripts in ObjectID object - myScriptEngine.m_EventQueueManager.AddToObjectQueue(localID, "touch_start", new object[] {(int) 1}); - } - - public void OnRezScript(uint localID, LLUUID itemID, string script) - { - Console.WriteLine("OnRezScript localID: " + localID + " LLUID: " + itemID.ToString() + " Size: " + - script.Length); - myScriptEngine.m_ScriptManager.StartScript(localID, itemID, script); - } - - public void OnRemoveScript(uint localID, LLUUID itemID) - { - Console.WriteLine("OnRemoveScript localID: " + localID + " LLUID: " + itemID.ToString()); - myScriptEngine.m_ScriptManager.StopScript( - localID, - itemID - ); - } - - // TODO: Replace placeholders below - // NOTE! THE PARAMETERS FOR THESE FUNCTIONS ARE NOT CORRECT! - // These needs to be hooked up to OpenSim during init of this class - // then queued in EventQueueManager. - // When queued in EventQueueManager they need to be LSL compatible (name and params) - - public void state_exit(uint localID, LLUUID itemID) - { - myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "state_exit"); - } - - public void touch(uint localID, LLUUID itemID) - { - myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "touch"); - } - - public void touch_end(uint localID, LLUUID itemID) - { - myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "touch_end"); - } - - public void collision_start(uint localID, LLUUID itemID) - { - myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "collision_start"); - } - - public void collision(uint localID, LLUUID itemID) - { - myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "collision"); - } - - public void collision_end(uint localID, LLUUID itemID) - { - myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "collision_end"); - } - - public void land_collision_start(uint localID, LLUUID itemID) - { - myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "land_collision_start"); - } - - public void land_collision(uint localID, LLUUID itemID) - { - myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "land_collision"); - } - - public void land_collision_end(uint localID, LLUUID itemID) - { - myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "land_collision_end"); - } - - // Handled by long commands - public void timer(uint localID, LLUUID itemID) - { - //myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, ""); - } - - public void listen(uint localID, LLUUID itemID) - { - myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "listen"); - } - - public void on_rez(uint localID, LLUUID itemID) - { - myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "on_rez"); - } - - public void sensor(uint localID, LLUUID itemID) - { - myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "sensor"); - } - - public void no_sensor(uint localID, LLUUID itemID) - { - myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "no_sensor"); - } - - public void control(uint localID, LLUUID itemID) - { - myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "control"); - } - - public void money(uint localID, LLUUID itemID) - { - myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "money"); - } - - public void email(uint localID, LLUUID itemID) - { - myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "email"); - } - - public void at_target(uint localID, LLUUID itemID) - { - myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "at_target"); - } - - public void not_at_target(uint localID, LLUUID itemID) - { - myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "not_at_target"); - } - - public void at_rot_target(uint localID, LLUUID itemID) - { - myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "at_rot_target"); - } - - public void not_at_rot_target(uint localID, LLUUID itemID) - { - myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "not_at_rot_target"); - } - - public void run_time_permissions(uint localID, LLUUID itemID) - { - myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "run_time_permissions"); - } - - public void changed(uint localID, LLUUID itemID) - { - myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "changed"); - } - - public void attach(uint localID, LLUUID itemID) - { - myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "attach"); - } - - public void dataserver(uint localID, LLUUID itemID) - { - myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "dataserver"); - } - - public void link_message(uint localID, LLUUID itemID) - { - myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "link_message"); - } - - public void moving_start(uint localID, LLUUID itemID) - { - myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "moving_start"); - } - - public void moving_end(uint localID, LLUUID itemID) - { - myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "moving_end"); - } - - public void object_rez(uint localID, LLUUID itemID) - { - myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "object_rez"); - } - - public void remote_data(uint localID, LLUUID itemID) - { - myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "remote_data"); - } - - // Handled by long commands - public void http_response(uint localID, LLUUID itemID) - { - // myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "http_response"); - } - } +/* +* Copyright (c) Contributors, http://opensimulator.org/ +* See CONTRIBUTORS.TXT for a full list of copyright holders. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the OpenSim Project nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS AS IS AND ANY +* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +*/ + + +using System; +using libsecondlife; +using OpenSim.Framework; + +namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase +{ + /// + /// Prepares events so they can be directly executed upon a script by EventQueueManager, then queues it. + /// + [Serializable] + public class EventManager : OpenSim.Region.ScriptEngine.Common.ScriptServerInterfaces.RemoteEvents + { + + // + // Class is instanced in "ScriptEngine" and Uses "EventQueueManager" that is also instanced in "ScriptEngine". + // This class needs a bit of explaining: + // + // This class it the link between an event inside OpenSim and the corresponding event in a user script being executed. + // + // For example when an user touches an object then the "myScriptEngine.World.EventManager.OnObjectGrab" event is fired inside OpenSim. + // We hook up to this event and queue a touch_start in EventQueueManager with the proper LSL parameters. + // It will then be delivered to the script by EventQueueManager. + // + // You can check debug C# dump of an LSL script if you need to verify what exact parameters are needed. + // + + + private ScriptEngine myScriptEngine; + //public IScriptHost TEMP_OBJECT_ID; + public EventManager(ScriptEngine _ScriptEngine, bool performHookUp) + { + myScriptEngine = _ScriptEngine; + + // Hook up to events from OpenSim + // We may not want to do it because someone is controlling us and will deliver events to us + if (performHookUp) + { + myScriptEngine.Log.Verbose("ScriptEngine", "Hooking up to server events"); + myScriptEngine.World.EventManager.OnObjectGrab += touch_start; + myScriptEngine.World.EventManager.OnRezScript += OnRezScript; + myScriptEngine.World.EventManager.OnRemoveScript += OnRemoveScript; + // TODO: HOOK ALL EVENTS UP TO SERVER! + } + } + + public void touch_start(uint localID, LLVector3 offsetPos, IClientAPI remoteClient) + { + // Add to queue for all scripts in ObjectID object + myScriptEngine.m_EventQueueManager.AddToObjectQueue(localID, "touch_start", new object[] {(int) 1}); + } + + public void OnRezScript(uint localID, LLUUID itemID, string script) + { + Console.WriteLine("OnRezScript localID: " + localID + " LLUID: " + itemID.ToString() + " Size: " + + script.Length); + myScriptEngine.m_ScriptManager.StartScript(localID, itemID, script); + } + + public void OnRemoveScript(uint localID, LLUUID itemID) + { + Console.WriteLine("OnRemoveScript localID: " + localID + " LLUID: " + itemID.ToString()); + myScriptEngine.m_ScriptManager.StopScript( + localID, + itemID + ); + } + + // TODO: Replace placeholders below + // NOTE! THE PARAMETERS FOR THESE FUNCTIONS ARE NOT CORRECT! + // These needs to be hooked up to OpenSim during init of this class + // then queued in EventQueueManager. + // When queued in EventQueueManager they need to be LSL compatible (name and params) + + public void state_exit(uint localID, LLUUID itemID) + { + myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "state_exit"); + } + + public void touch(uint localID, LLUUID itemID) + { + myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "touch"); + } + + public void touch_end(uint localID, LLUUID itemID) + { + myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "touch_end"); + } + + public void collision_start(uint localID, LLUUID itemID) + { + myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "collision_start"); + } + + public void collision(uint localID, LLUUID itemID) + { + myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "collision"); + } + + public void collision_end(uint localID, LLUUID itemID) + { + myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "collision_end"); + } + + public void land_collision_start(uint localID, LLUUID itemID) + { + myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "land_collision_start"); + } + + public void land_collision(uint localID, LLUUID itemID) + { + myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "land_collision"); + } + + public void land_collision_end(uint localID, LLUUID itemID) + { + myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "land_collision_end"); + } + + // Handled by long commands + public void timer(uint localID, LLUUID itemID) + { + //myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, ""); + } + + public void listen(uint localID, LLUUID itemID) + { + myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "listen"); + } + + public void on_rez(uint localID, LLUUID itemID) + { + myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "on_rez"); + } + + public void sensor(uint localID, LLUUID itemID) + { + myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "sensor"); + } + + public void no_sensor(uint localID, LLUUID itemID) + { + myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "no_sensor"); + } + + public void control(uint localID, LLUUID itemID) + { + myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "control"); + } + + public void money(uint localID, LLUUID itemID) + { + myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "money"); + } + + public void email(uint localID, LLUUID itemID) + { + myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "email"); + } + + public void at_target(uint localID, LLUUID itemID) + { + myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "at_target"); + } + + public void not_at_target(uint localID, LLUUID itemID) + { + myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "not_at_target"); + } + + public void at_rot_target(uint localID, LLUUID itemID) + { + myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "at_rot_target"); + } + + public void not_at_rot_target(uint localID, LLUUID itemID) + { + myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "not_at_rot_target"); + } + + public void run_time_permissions(uint localID, LLUUID itemID) + { + myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "run_time_permissions"); + } + + public void changed(uint localID, LLUUID itemID) + { + myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "changed"); + } + + public void attach(uint localID, LLUUID itemID) + { + myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "attach"); + } + + public void dataserver(uint localID, LLUUID itemID) + { + myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "dataserver"); + } + + public void link_message(uint localID, LLUUID itemID) + { + myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "link_message"); + } + + public void moving_start(uint localID, LLUUID itemID) + { + myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "moving_start"); + } + + public void moving_end(uint localID, LLUUID itemID) + { + myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "moving_end"); + } + + public void object_rez(uint localID, LLUUID itemID) + { + myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "object_rez"); + } + + public void remote_data(uint localID, LLUUID itemID) + { + myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "remote_data"); + } + + // Handled by long commands + public void http_response(uint localID, LLUUID itemID) + { + // myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "http_response"); + } + } } \ No newline at end of file diff --git a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueManager.cs b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueManager.cs index ad9ff45..8081e2c 100644 --- a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueManager.cs +++ b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueManager.cs @@ -1,364 +1,364 @@ -/* -* Copyright (c) Contributors, http://opensimulator.org/ -* See CONTRIBUTORS.TXT for a full list of copyright holders. -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions are met: -* * Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* * Redistributions in binary form must reproduce the above copyright -* notice, this list of conditions and the following disclaimer in the -* documentation and/or other materials provided with the distribution. -* * Neither the name of the OpenSim Project nor the -* names of its contributors may be used to endorse or promote products -* derived from this software without specific prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS AS IS AND ANY -* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY -* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -* -*/ - - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Threading; -using libsecondlife; -using OpenSim.Framework; -using OpenSim.Region.Environment.Scenes.Scripting; - -namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase -{ - /// - /// EventQueueManager handles event queues - /// Events are queued and executed in separate thread - /// - [Serializable] - public class EventQueueManager - { - - // - // Class is instanced in "ScriptEngine" and used by "EventManager" also instanced in "ScriptEngine". - // - // Class purpose is to queue and execute functions that are received by "EventManager": - // - allowing "EventManager" to release its event thread immediately, thus not interrupting server execution. - // - allowing us to prioritize and control execution of script functions. - // Class can use multiple threads for simultaneous execution. Mutexes are used for thread safety. - // - // 1. Hold an execution queue for scripts - // 2. Use threads to process queue, each thread executes one script function on each pass. - // 3. Catch any script error and process it - // - // - // Notes: - // * Current execution load balancing is optimized for 1 thread, and can cause unfair execute balancing between scripts. - // Not noticeable unless server is under high load. - // * This class contains the number of threads used for script executions. Since we are not microthreading scripts yet, - // increase number of threads to allow more concurrent script executions in OpenSim. - // - - - /// - /// List of threads processing event queue - /// - private List eventQueueThreads = new List(); - - private object queueLock = new object(); // Mutex lock object - - /// - /// How many ms to sleep if queue is empty - /// - private int nothingToDoSleepms = 50; - - /// - /// How many threads to process queue with - /// - private int numberOfThreads = 2; - - /// - /// Queue containing events waiting to be executed - /// - private Queue eventQueue = new Queue(); - - /// - /// Queue item structure - /// - private struct QueueItemStruct - { - public uint localID; - public LLUUID itemID; - public string functionName; - public object[] param; - } - - /// - /// List of localID locks for mutex processing of script events - /// - private List objectLocks = new List(); - - private object tryLockLock = new object(); // Mutex lock object - - private ScriptEngine m_ScriptEngine; - - public EventQueueManager(ScriptEngine _ScriptEngine) - { - m_ScriptEngine = _ScriptEngine; - - // - // Start event queue processing threads (worker threads) - // - for (int ThreadCount = 0; ThreadCount <= numberOfThreads; ThreadCount++) - { - Thread EventQueueThread = new Thread(EventQueueThreadLoop); - eventQueueThreads.Add(EventQueueThread); - EventQueueThread.IsBackground = true; - EventQueueThread.Priority = ThreadPriority.BelowNormal; - EventQueueThread.Name = "EventQueueManagerThread_" + ThreadCount; - EventQueueThread.Start(); - } - } - - ~EventQueueManager() - { - // Kill worker threads - foreach (Thread EventQueueThread in new ArrayList(eventQueueThreads)) - { - if (EventQueueThread != null && EventQueueThread.IsAlive == true) - { - try - { - EventQueueThread.Abort(); - EventQueueThread.Join(); - } - catch (Exception) - { - //myScriptEngine.Log.Verbose("ScriptEngine", "EventQueueManager Exception killing worker thread: " + e.ToString()); - } - } - } - eventQueueThreads.Clear(); - // Todo: Clean up our queues - eventQueue.Clear(); - } - - /// - /// Queue processing thread loop - /// - private void EventQueueThreadLoop() - { - //myScriptEngine.m_logger.Verbose("ScriptEngine", "EventQueueManager Worker thread spawned"); - try - { - QueueItemStruct BlankQIS = new QueueItemStruct(); - while (true) - { - try - { - QueueItemStruct QIS = BlankQIS; - bool GotItem = false; - - if (eventQueue.Count == 0) - { - // Nothing to do? Sleep a bit waiting for something to do - Thread.Sleep(nothingToDoSleepms); - } - else - { - // Something in queue, process - //myScriptEngine.m_logger.Verbose("ScriptEngine", "Processing event for localID: " + QIS.localID + ", itemID: " + QIS.itemID + ", FunctionName: " + QIS.FunctionName); - - // OBJECT BASED LOCK - TWO THREADS WORKING ON SAME OBJECT IS NOT GOOD - lock (queueLock) - { - GotItem = false; - for (int qc = 0; qc < eventQueue.Count; qc++) - { - // Get queue item - QIS = eventQueue.Dequeue(); - - // Check if object is being processed by someone else - if (TryLock(QIS.localID) == false) - { - // Object is already being processed, requeue it - eventQueue.Enqueue(QIS); - } - else - { - // We have lock on an object and can process it - GotItem = true; - break; - } - } // go through queue - } // lock - - if (GotItem == true) - { - // Execute function - try - { -#if DEBUG - m_ScriptEngine.Log.Debug("ScriptEngine", "Executing event:\r\n" - + "QIS.localID: " + QIS.localID - + ", QIS.itemID: " + QIS.itemID - + ", QIS.functionName: " + QIS.functionName); -#endif - m_ScriptEngine.m_ScriptManager.ExecuteEvent(QIS.localID, QIS.itemID, - QIS.functionName, QIS.param); - } - catch (Exception e) - { - // DISPLAY ERROR INWORLD - string text = "Error executing script function \"" + QIS.functionName + "\":\r\n"; - //if (e.InnerException != null) - //{ - // Send inner exception - text += e.InnerException.Message.ToString(); - //} - //else - //{ - text += "\r\n"; - // Send normal - text += e.Message.ToString(); - //} - try - { - if (text.Length > 1500) - text = text.Substring(0, 1500); - IScriptHost m_host = m_ScriptEngine.World.GetSceneObjectPart(QIS.localID); - //if (m_host != null) - //{ - m_ScriptEngine.World.SimChat(Helpers.StringToField(text), ChatTypeEnum.Say, 0, - m_host.AbsolutePosition, m_host.Name, m_host.UUID); - } - catch - { - //} - //else - //{ - // T oconsole - m_ScriptEngine.Log.Error("ScriptEngine", - "Unable to send text in-world:\r\n" + text); - } - } - finally - { - ReleaseLock(QIS.localID); - } - } - } // Something in queue - } - catch (ThreadAbortException tae) - { - throw tae; - } - catch (Exception e) - { - m_ScriptEngine.Log.Error("ScriptEngine", "Exception in EventQueueThreadLoop: " + e.ToString()); - } - } // while - } // try - catch (ThreadAbortException) - { - //myScriptEngine.Log.Verbose("ScriptEngine", "EventQueueManager Worker thread killed: " + tae.Message); - } - } - - /// - /// Try to get a mutex lock on localID - /// - /// - /// - private bool TryLock(uint localID) - { - lock (tryLockLock) - { - if (objectLocks.Contains(localID) == true) - { - return false; - } - else - { - objectLocks.Add(localID); - return true; - } - } - } - - /// - /// Release mutex lock on localID - /// - /// - private void ReleaseLock(uint localID) - { - lock (tryLockLock) - { - if (objectLocks.Contains(localID) == true) - { - objectLocks.Remove(localID); - } - } - } - - - /// - /// Add event to event execution queue - /// - /// - /// Name of the function, will be state + "_event_" + FunctionName - /// Array of parameters to match event mask - public void AddToObjectQueue(uint localID, string FunctionName, params object[] param) - { - // Determine all scripts in Object and add to their queue - //myScriptEngine.m_logger.Verbose("ScriptEngine", "EventQueueManager Adding localID: " + localID + ", FunctionName: " + FunctionName); - - - // Do we have any scripts in this object at all? If not, return - if (m_ScriptEngine.m_ScriptManager.Scripts.ContainsKey(localID) == false) - { - //Console.WriteLine("Event \"" + FunctionName + "\" for localID: " + localID + ". No scripts found on this localID."); - return; - } - - Dictionary.KeyCollection scriptKeys = - m_ScriptEngine.m_ScriptManager.GetScriptKeys(localID); - - foreach (LLUUID itemID in scriptKeys) - { - // 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, params 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); - } - } - } +/* +* Copyright (c) Contributors, http://opensimulator.org/ +* See CONTRIBUTORS.TXT for a full list of copyright holders. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the OpenSim Project nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS AS IS AND ANY +* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +*/ + + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Threading; +using libsecondlife; +using OpenSim.Framework; +using OpenSim.Region.Environment.Scenes.Scripting; + +namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase +{ + /// + /// EventQueueManager handles event queues + /// Events are queued and executed in separate thread + /// + [Serializable] + public class EventQueueManager + { + + // + // Class is instanced in "ScriptEngine" and used by "EventManager" also instanced in "ScriptEngine". + // + // Class purpose is to queue and execute functions that are received by "EventManager": + // - allowing "EventManager" to release its event thread immediately, thus not interrupting server execution. + // - allowing us to prioritize and control execution of script functions. + // Class can use multiple threads for simultaneous execution. Mutexes are used for thread safety. + // + // 1. Hold an execution queue for scripts + // 2. Use threads to process queue, each thread executes one script function on each pass. + // 3. Catch any script error and process it + // + // + // Notes: + // * Current execution load balancing is optimized for 1 thread, and can cause unfair execute balancing between scripts. + // Not noticeable unless server is under high load. + // * This class contains the number of threads used for script executions. Since we are not microthreading scripts yet, + // increase number of threads to allow more concurrent script executions in OpenSim. + // + + + /// + /// List of threads processing event queue + /// + private List eventQueueThreads = new List(); + + private object queueLock = new object(); // Mutex lock object + + /// + /// How many ms to sleep if queue is empty + /// + private int nothingToDoSleepms = 50; + + /// + /// How many threads to process queue with + /// + private int numberOfThreads = 2; + + /// + /// Queue containing events waiting to be executed + /// + private Queue eventQueue = new Queue(); + + /// + /// Queue item structure + /// + private struct QueueItemStruct + { + public uint localID; + public LLUUID itemID; + public string functionName; + public object[] param; + } + + /// + /// List of localID locks for mutex processing of script events + /// + private List objectLocks = new List(); + + private object tryLockLock = new object(); // Mutex lock object + + private ScriptEngine m_ScriptEngine; + + public EventQueueManager(ScriptEngine _ScriptEngine) + { + m_ScriptEngine = _ScriptEngine; + + // + // Start event queue processing threads (worker threads) + // + for (int ThreadCount = 0; ThreadCount <= numberOfThreads; ThreadCount++) + { + Thread EventQueueThread = new Thread(EventQueueThreadLoop); + eventQueueThreads.Add(EventQueueThread); + EventQueueThread.IsBackground = true; + EventQueueThread.Priority = ThreadPriority.BelowNormal; + EventQueueThread.Name = "EventQueueManagerThread_" + ThreadCount; + EventQueueThread.Start(); + } + } + + ~EventQueueManager() + { + // Kill worker threads + foreach (Thread EventQueueThread in new ArrayList(eventQueueThreads)) + { + if (EventQueueThread != null && EventQueueThread.IsAlive == true) + { + try + { + EventQueueThread.Abort(); + EventQueueThread.Join(); + } + catch (Exception) + { + //myScriptEngine.Log.Verbose("ScriptEngine", "EventQueueManager Exception killing worker thread: " + e.ToString()); + } + } + } + eventQueueThreads.Clear(); + // Todo: Clean up our queues + eventQueue.Clear(); + } + + /// + /// Queue processing thread loop + /// + private void EventQueueThreadLoop() + { + //myScriptEngine.m_logger.Verbose("ScriptEngine", "EventQueueManager Worker thread spawned"); + try + { + QueueItemStruct BlankQIS = new QueueItemStruct(); + while (true) + { + try + { + QueueItemStruct QIS = BlankQIS; + bool GotItem = false; + + if (eventQueue.Count == 0) + { + // Nothing to do? Sleep a bit waiting for something to do + Thread.Sleep(nothingToDoSleepms); + } + else + { + // Something in queue, process + //myScriptEngine.m_logger.Verbose("ScriptEngine", "Processing event for localID: " + QIS.localID + ", itemID: " + QIS.itemID + ", FunctionName: " + QIS.FunctionName); + + // OBJECT BASED LOCK - TWO THREADS WORKING ON SAME OBJECT IS NOT GOOD + lock (queueLock) + { + GotItem = false; + for (int qc = 0; qc < eventQueue.Count; qc++) + { + // Get queue item + QIS = eventQueue.Dequeue(); + + // Check if object is being processed by someone else + if (TryLock(QIS.localID) == false) + { + // Object is already being processed, requeue it + eventQueue.Enqueue(QIS); + } + else + { + // We have lock on an object and can process it + GotItem = true; + break; + } + } // go through queue + } // lock + + if (GotItem == true) + { + // Execute function + try + { +#if DEBUG + m_ScriptEngine.Log.Debug("ScriptEngine", "Executing event:\r\n" + + "QIS.localID: " + QIS.localID + + ", QIS.itemID: " + QIS.itemID + + ", QIS.functionName: " + QIS.functionName); +#endif + m_ScriptEngine.m_ScriptManager.ExecuteEvent(QIS.localID, QIS.itemID, + QIS.functionName, QIS.param); + } + catch (Exception e) + { + // DISPLAY ERROR INWORLD + string text = "Error executing script function \"" + QIS.functionName + "\":\r\n"; + //if (e.InnerException != null) + //{ + // Send inner exception + text += e.InnerException.Message.ToString(); + //} + //else + //{ + text += "\r\n"; + // Send normal + text += e.Message.ToString(); + //} + try + { + if (text.Length > 1500) + text = text.Substring(0, 1500); + IScriptHost m_host = m_ScriptEngine.World.GetSceneObjectPart(QIS.localID); + //if (m_host != null) + //{ + m_ScriptEngine.World.SimChat(Helpers.StringToField(text), ChatTypeEnum.Say, 0, + m_host.AbsolutePosition, m_host.Name, m_host.UUID); + } + catch + { + //} + //else + //{ + // T oconsole + m_ScriptEngine.Log.Error("ScriptEngine", + "Unable to send text in-world:\r\n" + text); + } + } + finally + { + ReleaseLock(QIS.localID); + } + } + } // Something in queue + } + catch (ThreadAbortException tae) + { + throw tae; + } + catch (Exception e) + { + m_ScriptEngine.Log.Error("ScriptEngine", "Exception in EventQueueThreadLoop: " + e.ToString()); + } + } // while + } // try + catch (ThreadAbortException) + { + //myScriptEngine.Log.Verbose("ScriptEngine", "EventQueueManager Worker thread killed: " + tae.Message); + } + } + + /// + /// Try to get a mutex lock on localID + /// + /// + /// + private bool TryLock(uint localID) + { + lock (tryLockLock) + { + if (objectLocks.Contains(localID) == true) + { + return false; + } + else + { + objectLocks.Add(localID); + return true; + } + } + } + + /// + /// Release mutex lock on localID + /// + /// + private void ReleaseLock(uint localID) + { + lock (tryLockLock) + { + if (objectLocks.Contains(localID) == true) + { + objectLocks.Remove(localID); + } + } + } + + + /// + /// Add event to event execution queue + /// + /// + /// Name of the function, will be state + "_event_" + FunctionName + /// Array of parameters to match event mask + public void AddToObjectQueue(uint localID, string FunctionName, params object[] param) + { + // Determine all scripts in Object and add to their queue + //myScriptEngine.m_logger.Verbose("ScriptEngine", "EventQueueManager Adding localID: " + localID + ", FunctionName: " + FunctionName); + + + // Do we have any scripts in this object at all? If not, return + if (m_ScriptEngine.m_ScriptManager.Scripts.ContainsKey(localID) == false) + { + //Console.WriteLine("Event \"" + FunctionName + "\" for localID: " + localID + ". No scripts found on this localID."); + return; + } + + Dictionary.KeyCollection scriptKeys = + m_ScriptEngine.m_ScriptManager.GetScriptKeys(localID); + + foreach (LLUUID itemID in scriptKeys) + { + // 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, params 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); + } + } + } } \ No newline at end of file diff --git a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/LSLLongCmdHandler.cs b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/LSLLongCmdHandler.cs index 94241eb..635c32a 100644 --- a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/LSLLongCmdHandler.cs +++ b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/LSLLongCmdHandler.cs @@ -1,295 +1,295 @@ -/* -* Copyright (c) Contributors, http://opensimulator.org/ -* See CONTRIBUTORS.TXT for a full list of copyright holders. -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions are met: -* * Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* * Redistributions in binary form must reproduce the above copyright -* notice, this list of conditions and the following disclaimer in the -* documentation and/or other materials provided with the distribution. -* * Neither the name of the OpenSim Project nor the -* names of its contributors may be used to endorse or promote products -* derived from this software without specific prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS AS IS AND ANY -* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY -* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -* -*/ - -using System; -using System.Collections.Generic; -using System.Threading; -using libsecondlife; -using OpenSim.Region.Environment.Interfaces; -using OpenSim.Region.Environment.Modules; - -namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase -{ - /// - /// Handles LSL commands that takes long time and returns an event, for example timers, HTTP requests, etc. - /// - public class LSLLongCmdHandler - { - private Thread cmdHandlerThread; - private int cmdHandlerThreadCycleSleepms = 100; - - private ScriptEngine m_ScriptEngine; - - public LSLLongCmdHandler(ScriptEngine _ScriptEngine) - { - m_ScriptEngine = _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(); - Thread.Sleep(25); - // Check HttpRequests - CheckHttpRequests(); - Thread.Sleep(25); - // Check XMLRPCRequests - CheckXMLRPCRequests(); - Thread.Sleep(25); - // Check Listeners - CheckListeners(); - Thread.Sleep(25); - - // Sleep before next cycle - //Thread.Sleep(cmdHandlerThreadCycleSleepms); - } - } - - /// - /// Remove a specific script (and all its pending commands) - /// - /// - /// - public void RemoveScript(uint localID, LLUUID itemID) - { - // Remove a specific script - - // Remove from: Timers - UnSetTimerEvents(localID, itemID); - // Remove from: HttpRequest - IHttpRequests iHttpReq = - m_ScriptEngine.World.RequestModuleInterface(); - iHttpReq.StopHttpRequest(localID, itemID); - } - - #region TIMER - - // - // TIMER - // - private class TimerClass - { - public uint localID; - public LLUUID itemID; - public double interval; - public DateTime next; - } - - private List Timers = new List(); - private object TimerListLock = 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 (TimerListLock) - { - Timers.Add(ts); - } - } - - public void UnSetTimerEvents(uint m_localID, LLUUID m_itemID) - { - // Remove from timer - lock (TimerListLock) - { - 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; - - lock (TimerListLock) - { - // Go through all timers - foreach (TimerClass ts in Timers) - { - // Time has passed? - if (ts.next.ToUniversalTime() < DateTime.Now.ToUniversalTime()) - { - // Add it to queue - m_ScriptEngine.m_EventQueueManager.AddToScriptQueue(ts.localID, ts.itemID, "timer", - new object[] {}); - // set next interval - - - ts.next = DateTime.Now.ToUniversalTime().AddSeconds(ts.interval); - } - } - } // lock - } - - #endregion - - #region HTTP REQUEST - - public void CheckHttpRequests() - { - if (m_ScriptEngine.World == null) - return; - - IHttpRequests iHttpReq = - m_ScriptEngine.World.RequestModuleInterface(); - - HttpRequestClass httpInfo = null; - - if (iHttpReq != null) - httpInfo = iHttpReq.GetNextCompletedRequest(); - - while (httpInfo != null) - { - //Console.WriteLine("PICKED HTTP REQ:" + httpInfo.response_body + httpInfo.status); - - // Deliver data to prim's remote_data handler - // - // TODO: Returning null for metadata, since the lsl function - // only returns the byte for HTTP_BODY_TRUNCATED, which is not - // implemented here yet anyway. Should be fixed if/when maxsize - // is supported - - object[] resobj = new object[] - { - httpInfo.reqID.ToString(), httpInfo.status, null, httpInfo.response_body - }; - - m_ScriptEngine.m_EventQueueManager.AddToScriptQueue( - httpInfo.localID, httpInfo.itemID, "http_response", resobj - ); - - httpInfo.Stop(); - httpInfo = null; - - httpInfo = iHttpReq.GetNextCompletedRequest(); - } - } - - #endregion - - public void CheckXMLRPCRequests() - { - if (m_ScriptEngine.World == null) - return; - - IXMLRPC xmlrpc = m_ScriptEngine.World.RequestModuleInterface(); - - if (xmlrpc != null) - { - while (xmlrpc.hasRequests()) - { - RPCRequestInfo rInfo = xmlrpc.GetNextRequest(); - //Console.WriteLine("PICKED REQUEST"); - - //Deliver data to prim's remote_data handler - object[] resobj = new object[] - { - 2, rInfo.GetChannelKey().ToString(), rInfo.GetMessageID().ToString(), "", - rInfo.GetIntValue(), - rInfo.GetStrVal() - }; - m_ScriptEngine.m_EventQueueManager.AddToScriptQueue( - rInfo.GetLocalID(), rInfo.GetItemID(), "remote_data", resobj - ); - } - } - } - - public void CheckListeners() - { - if (m_ScriptEngine.World == null) - return; - IWorldComm comms = m_ScriptEngine.World.RequestModuleInterface(); - - while (comms.HasMessages()) - { - ListenerInfo lInfo = comms.GetNextMessage(); - - //Deliver data to prim's listen handler - object[] resobj = new object[] - { - lInfo.GetChannel(), lInfo.GetName(), lInfo.GetID().ToString(), lInfo.GetMessage() - }; - - m_ScriptEngine.m_EventQueueManager.AddToScriptQueue( - lInfo.GetLocalID(), lInfo.GetItemID(), "listen", resobj - ); - } - } - } +/* +* Copyright (c) Contributors, http://opensimulator.org/ +* See CONTRIBUTORS.TXT for a full list of copyright holders. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the OpenSim Project nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS AS IS AND ANY +* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +*/ + +using System; +using System.Collections.Generic; +using System.Threading; +using libsecondlife; +using OpenSim.Region.Environment.Interfaces; +using OpenSim.Region.Environment.Modules; + +namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase +{ + /// + /// Handles LSL commands that takes long time and returns an event, for example timers, HTTP requests, etc. + /// + public class LSLLongCmdHandler + { + private Thread cmdHandlerThread; + private int cmdHandlerThreadCycleSleepms = 100; + + private ScriptEngine m_ScriptEngine; + + public LSLLongCmdHandler(ScriptEngine _ScriptEngine) + { + m_ScriptEngine = _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(); + Thread.Sleep(25); + // Check HttpRequests + CheckHttpRequests(); + Thread.Sleep(25); + // Check XMLRPCRequests + CheckXMLRPCRequests(); + Thread.Sleep(25); + // Check Listeners + CheckListeners(); + Thread.Sleep(25); + + // Sleep before next cycle + //Thread.Sleep(cmdHandlerThreadCycleSleepms); + } + } + + /// + /// Remove a specific script (and all its pending commands) + /// + /// + /// + public void RemoveScript(uint localID, LLUUID itemID) + { + // Remove a specific script + + // Remove from: Timers + UnSetTimerEvents(localID, itemID); + // Remove from: HttpRequest + IHttpRequests iHttpReq = + m_ScriptEngine.World.RequestModuleInterface(); + iHttpReq.StopHttpRequest(localID, itemID); + } + + #region TIMER + + // + // TIMER + // + private class TimerClass + { + public uint localID; + public LLUUID itemID; + public double interval; + public DateTime next; + } + + private List Timers = new List(); + private object TimerListLock = 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 (TimerListLock) + { + Timers.Add(ts); + } + } + + public void UnSetTimerEvents(uint m_localID, LLUUID m_itemID) + { + // Remove from timer + lock (TimerListLock) + { + 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; + + lock (TimerListLock) + { + // Go through all timers + foreach (TimerClass ts in Timers) + { + // Time has passed? + if (ts.next.ToUniversalTime() < DateTime.Now.ToUniversalTime()) + { + // Add it to queue + m_ScriptEngine.m_EventQueueManager.AddToScriptQueue(ts.localID, ts.itemID, "timer", + new object[] {}); + // set next interval + + + ts.next = DateTime.Now.ToUniversalTime().AddSeconds(ts.interval); + } + } + } // lock + } + + #endregion + + #region HTTP REQUEST + + public void CheckHttpRequests() + { + if (m_ScriptEngine.World == null) + return; + + IHttpRequests iHttpReq = + m_ScriptEngine.World.RequestModuleInterface(); + + HttpRequestClass httpInfo = null; + + if (iHttpReq != null) + httpInfo = iHttpReq.GetNextCompletedRequest(); + + while (httpInfo != null) + { + //Console.WriteLine("PICKED HTTP REQ:" + httpInfo.response_body + httpInfo.status); + + // Deliver data to prim's remote_data handler + // + // TODO: Returning null for metadata, since the lsl function + // only returns the byte for HTTP_BODY_TRUNCATED, which is not + // implemented here yet anyway. Should be fixed if/when maxsize + // is supported + + object[] resobj = new object[] + { + httpInfo.reqID.ToString(), httpInfo.status, null, httpInfo.response_body + }; + + m_ScriptEngine.m_EventQueueManager.AddToScriptQueue( + httpInfo.localID, httpInfo.itemID, "http_response", resobj + ); + + httpInfo.Stop(); + httpInfo = null; + + httpInfo = iHttpReq.GetNextCompletedRequest(); + } + } + + #endregion + + public void CheckXMLRPCRequests() + { + if (m_ScriptEngine.World == null) + return; + + IXMLRPC xmlrpc = m_ScriptEngine.World.RequestModuleInterface(); + + if (xmlrpc != null) + { + while (xmlrpc.hasRequests()) + { + RPCRequestInfo rInfo = xmlrpc.GetNextRequest(); + //Console.WriteLine("PICKED REQUEST"); + + //Deliver data to prim's remote_data handler + object[] resobj = new object[] + { + 2, rInfo.GetChannelKey().ToString(), rInfo.GetMessageID().ToString(), "", + rInfo.GetIntValue(), + rInfo.GetStrVal() + }; + m_ScriptEngine.m_EventQueueManager.AddToScriptQueue( + rInfo.GetLocalID(), rInfo.GetItemID(), "remote_data", resobj + ); + } + } + } + + public void CheckListeners() + { + if (m_ScriptEngine.World == null) + return; + IWorldComm comms = m_ScriptEngine.World.RequestModuleInterface(); + + while (comms.HasMessages()) + { + ListenerInfo lInfo = comms.GetNextMessage(); + + //Deliver data to prim's listen handler + object[] resobj = new object[] + { + lInfo.GetChannel(), lInfo.GetName(), lInfo.GetID().ToString(), lInfo.GetMessage() + }; + + m_ScriptEngine.m_EventQueueManager.AddToScriptQueue( + lInfo.GetLocalID(), lInfo.GetItemID(), "listen", resobj + ); + } + } + } } \ No newline at end of file diff --git a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/ScriptEngine.cs b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/ScriptEngine.cs index 39d0bc3..da0baba 100644 --- a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/ScriptEngine.cs +++ b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/ScriptEngine.cs @@ -1,132 +1,132 @@ -/* -* Copyright (c) Contributors, http://opensimulator.org/ -* See CONTRIBUTORS.TXT for a full list of copyright holders. -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions are met: -* * Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* * Redistributions in binary form must reproduce the above copyright -* notice, this list of conditions and the following disclaimer in the -* documentation and/or other materials provided with the distribution. -* * Neither the name of the OpenSim Project nor the -* names of its contributors may be used to endorse or promote products -* derived from this software without specific prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS AS IS AND ANY -* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY -* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -* -*/ - - -using System; -using Nini.Config; -using OpenSim.Framework.Console; -using OpenSim.Region.Environment.Interfaces; -using OpenSim.Region.Environment.Scenes; -using OpenSim.Region.ScriptEngine.Common; -using OpenSim.Region.ScriptEngine.Common.ScriptEngineBase; - -namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase -{ - /// - /// This is the root object for ScriptEngine. Objects access each other trough this class. - /// - /// - [Serializable] - public abstract class ScriptEngine : IRegionModule, OpenSim.Region.ScriptEngine.Common.ScriptServerInterfaces.ScriptEngine - { - public Scene World; - public EventManager m_EventManager; // Handles and queues incoming events from OpenSim - public EventQueueManager m_EventQueueManager; // Executes events - public ScriptManager m_ScriptManager; // Load, unload and execute scripts - public AppDomainManager m_AppDomainManager; - public LSLLongCmdHandler m_LSLLongCmdHandler; - - public ScriptManager GetScriptManager() - { - return _GetScriptManager(); - } - public abstract ScriptManager _GetScriptManager(); - - private LogBase m_log; - - public ScriptEngine() - { - //Common.SendToDebug("ScriptEngine Object Initialized"); - Common.mySE = this; - } - - public LogBase Log - { - get { return m_log; } - } - - public void InitializeEngine(Scene Sceneworld, LogBase logger, bool HookUpToServer, ScriptManager newScriptManager) - { - World = Sceneworld; - m_log = logger; - - Log.Verbose("ScriptEngine", "DotNet & LSL ScriptEngine initializing"); - - //m_logger.Status("ScriptEngine", "InitializeEngine"); - - // Create all objects we'll be using - m_EventQueueManager = new EventQueueManager(this); - m_EventManager = new EventManager(this, HookUpToServer); - m_ScriptManager = newScriptManager; - //m_ScriptManager = new ScriptManager(this); - m_AppDomainManager = new AppDomainManager(); - m_LSLLongCmdHandler = 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? - } - - public void Shutdown() - { - // We are shutting down - } - - ScriptServerInterfaces.RemoteEvents ScriptServerInterfaces.ScriptEngine.EventManager() - { - return this.m_EventManager; - } - - - #region IRegionModule - - public abstract void Initialise(Scene scene, IConfigSource config); - - public void PostInitialise() - { - } - - public void Close() - { - } - - public string Name - { - get { return "DotNetEngine"; } - } - - public bool IsSharedModule - { - get { return false; } - } - - - - #endregion - - } +/* +* Copyright (c) Contributors, http://opensimulator.org/ +* See CONTRIBUTORS.TXT for a full list of copyright holders. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the OpenSim Project nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS AS IS AND ANY +* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +*/ + + +using System; +using Nini.Config; +using OpenSim.Framework.Console; +using OpenSim.Region.Environment.Interfaces; +using OpenSim.Region.Environment.Scenes; +using OpenSim.Region.ScriptEngine.Common; +using OpenSim.Region.ScriptEngine.Common.ScriptEngineBase; + +namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase +{ + /// + /// This is the root object for ScriptEngine. Objects access each other trough this class. + /// + /// + [Serializable] + public abstract class ScriptEngine : IRegionModule, OpenSim.Region.ScriptEngine.Common.ScriptServerInterfaces.ScriptEngine + { + public Scene World; + public EventManager m_EventManager; // Handles and queues incoming events from OpenSim + public EventQueueManager m_EventQueueManager; // Executes events + public ScriptManager m_ScriptManager; // Load, unload and execute scripts + public AppDomainManager m_AppDomainManager; + public LSLLongCmdHandler m_LSLLongCmdHandler; + + public ScriptManager GetScriptManager() + { + return _GetScriptManager(); + } + public abstract ScriptManager _GetScriptManager(); + + private LogBase m_log; + + public ScriptEngine() + { + //Common.SendToDebug("ScriptEngine Object Initialized"); + Common.mySE = this; + } + + public LogBase Log + { + get { return m_log; } + } + + public void InitializeEngine(Scene Sceneworld, LogBase logger, bool HookUpToServer, ScriptManager newScriptManager) + { + World = Sceneworld; + m_log = logger; + + Log.Verbose("ScriptEngine", "DotNet & LSL ScriptEngine initializing"); + + //m_logger.Status("ScriptEngine", "InitializeEngine"); + + // Create all objects we'll be using + m_EventQueueManager = new EventQueueManager(this); + m_EventManager = new EventManager(this, HookUpToServer); + m_ScriptManager = newScriptManager; + //m_ScriptManager = new ScriptManager(this); + m_AppDomainManager = new AppDomainManager(); + m_LSLLongCmdHandler = 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? + } + + public void Shutdown() + { + // We are shutting down + } + + ScriptServerInterfaces.RemoteEvents ScriptServerInterfaces.ScriptEngine.EventManager() + { + return this.m_EventManager; + } + + + #region IRegionModule + + public abstract void Initialise(Scene scene, IConfigSource config); + + public void PostInitialise() + { + } + + public void Close() + { + } + + public string Name + { + get { return "DotNetEngine"; } + } + + public bool IsSharedModule + { + get { return false; } + } + + + + #endregion + + } } \ No newline at end of file diff --git a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/ScriptManager.cs b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/ScriptManager.cs index e98ff81..8f74620 100644 --- a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/ScriptManager.cs +++ b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/ScriptManager.cs @@ -1,348 +1,348 @@ -/* -* Copyright (c) Contributors, http://opensimulator.org/ -* See CONTRIBUTORS.TXT for a full list of copyright holders. -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions are met: -* * Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* * Redistributions in binary form must reproduce the above copyright -* notice, this list of conditions and the following disclaimer in the -* documentation and/or other materials provided with the distribution. -* * Neither the name of the OpenSim Project nor the -* names of its contributors may be used to endorse or promote products -* derived from this software without specific prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS AS IS AND ANY -* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY -* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -* -*/ - - -using System; -using System.Collections.Generic; -using System.IO; -using System.Reflection; -using System.Runtime.Serialization.Formatters.Binary; -using System.Threading; -using libsecondlife; -using OpenSim.Framework; -using OpenSim.Region.Environment.Scenes; -using OpenSim.Region.ScriptEngine.Common; - -namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase -{ - /// - /// 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 abstract class ScriptManager - { - #region Declares - - private Thread scriptLoadUnloadThread; - private int scriptLoadUnloadThread_IdleSleepms = 100; - private Queue LUQueue = new Queue(); - - - // Load/Unload structure - private struct LUStruct - { - public uint localID; - public LLUUID itemID; - public string script; - public LUType Action; - } - - private enum LUType - { - Unknown = 0, - Load = 1, - Unload = 2 - } - - // Object> - // IMPORTANT: Types and MemberInfo-derived objects require a LOT of memory. - // Instead use RuntimeTypeHandle, RuntimeFieldHandle and RunTimeHandle (IntPtr) instead! - public Dictionary> Scripts = - new Dictionary>(); - - public Scene World - { - get { return m_scriptEngine.World; } - } - - #endregion - - #region Object init/shutdown - - public ScriptEngineBase.ScriptEngine m_scriptEngine; - - public ScriptManager(ScriptEngineBase.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(); - } - - ~ScriptManager() - { - // Abort load/unload thread - try - { - if (scriptLoadUnloadThread != null) - { - if (scriptLoadUnloadThread.IsAlive == true) - { - scriptLoadUnloadThread.Abort(); - scriptLoadUnloadThread.Join(); - } - } - } - catch - { - } - } - - #endregion - - #region Load / Unload scripts (Thread loop) - - private void ScriptLoadUnloadThreadLoop() - { - try - { - while (true) - { - if (LUQueue.Count == 0) - Thread.Sleep(scriptLoadUnloadThread_IdleSleepms); - if (LUQueue.Count > 0) - { - LUStruct item = LUQueue.Dequeue(); - lock (startStopLock) // Lock so we have only 1 thread working on loading/unloading of scripts - { - 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 Start/Stop/Reset script - - private readonly 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); - } - - // Create a new instance of the compiler (reuse) - //private Compiler.LSL.Compiler LSLCompiler = new Compiler.LSL.Compiler(); - - public abstract void _StartScript(uint localID, LLUUID itemID, string Script); - - public abstract void _StopScript(uint localID, LLUUID itemID); - - - #endregion - - #region Perform event execution in script - - /// - /// 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); - //ScriptBaseInterface Script = (ScriptBaseInterface)GetScript(localID, itemID); - IScript Script = GetScript(localID, itemID); - if (Script == 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); - } - - #endregion - - #region Internal functions to keep track of script - - public Dictionary.KeyCollection GetScriptKeys(uint localID) - { - if (Scripts.ContainsKey(localID) == false) - return null; - - Dictionary Obj; - Scripts.TryGetValue(localID, out Obj); - - return Obj.Keys; - } - - public IScript 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 - IScript Script; - Obj.TryGetValue(itemID, out Script); - - return Script; - } - - public void SetScript(uint localID, LLUUID itemID, IScript 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); - } - - public 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 - - - public void ResetScript(uint localID, LLUUID itemID) - { - string script = GetScript(localID, itemID).Source; - StopScript(localID, itemID); - StartScript(localID, itemID, script); - } - - - #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? - } - - #endregion - } +/* +* Copyright (c) Contributors, http://opensimulator.org/ +* See CONTRIBUTORS.TXT for a full list of copyright holders. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the OpenSim Project nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS AS IS AND ANY +* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +*/ + + +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Runtime.Serialization.Formatters.Binary; +using System.Threading; +using libsecondlife; +using OpenSim.Framework; +using OpenSim.Region.Environment.Scenes; +using OpenSim.Region.ScriptEngine.Common; + +namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase +{ + /// + /// 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 abstract class ScriptManager + { + #region Declares + + private Thread scriptLoadUnloadThread; + private int scriptLoadUnloadThread_IdleSleepms = 100; + private Queue LUQueue = new Queue(); + + + // Load/Unload structure + private struct LUStruct + { + public uint localID; + public LLUUID itemID; + public string script; + public LUType Action; + } + + private enum LUType + { + Unknown = 0, + Load = 1, + Unload = 2 + } + + // Object> + // IMPORTANT: Types and MemberInfo-derived objects require a LOT of memory. + // Instead use RuntimeTypeHandle, RuntimeFieldHandle and RunTimeHandle (IntPtr) instead! + public Dictionary> Scripts = + new Dictionary>(); + + public Scene World + { + get { return m_scriptEngine.World; } + } + + #endregion + + #region Object init/shutdown + + public ScriptEngineBase.ScriptEngine m_scriptEngine; + + public ScriptManager(ScriptEngineBase.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(); + } + + ~ScriptManager() + { + // Abort load/unload thread + try + { + if (scriptLoadUnloadThread != null) + { + if (scriptLoadUnloadThread.IsAlive == true) + { + scriptLoadUnloadThread.Abort(); + scriptLoadUnloadThread.Join(); + } + } + } + catch + { + } + } + + #endregion + + #region Load / Unload scripts (Thread loop) + + private void ScriptLoadUnloadThreadLoop() + { + try + { + while (true) + { + if (LUQueue.Count == 0) + Thread.Sleep(scriptLoadUnloadThread_IdleSleepms); + if (LUQueue.Count > 0) + { + LUStruct item = LUQueue.Dequeue(); + lock (startStopLock) // Lock so we have only 1 thread working on loading/unloading of scripts + { + 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 Start/Stop/Reset script + + private readonly 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); + } + + // Create a new instance of the compiler (reuse) + //private Compiler.LSL.Compiler LSLCompiler = new Compiler.LSL.Compiler(); + + public abstract void _StartScript(uint localID, LLUUID itemID, string Script); + + public abstract void _StopScript(uint localID, LLUUID itemID); + + + #endregion + + #region Perform event execution in script + + /// + /// 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); + //ScriptBaseInterface Script = (ScriptBaseInterface)GetScript(localID, itemID); + IScript Script = GetScript(localID, itemID); + if (Script == 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); + } + + #endregion + + #region Internal functions to keep track of script + + public Dictionary.KeyCollection GetScriptKeys(uint localID) + { + if (Scripts.ContainsKey(localID) == false) + return null; + + Dictionary Obj; + Scripts.TryGetValue(localID, out Obj); + + return Obj.Keys; + } + + public IScript 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 + IScript Script; + Obj.TryGetValue(itemID, out Script); + + return Script; + } + + public void SetScript(uint localID, LLUUID itemID, IScript 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); + } + + public 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 + + + public void ResetScript(uint localID, LLUUID itemID) + { + string script = GetScript(localID, itemID).Source; + StopScript(localID, itemID); + StartScript(localID, itemID, script); + } + + + #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? + } + + #endregion + } } \ No newline at end of file -- cgit v1.1