aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/ScriptEngine/Common/ScriptEngineBase')
-rw-r--r--OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/AppDomainManager.cs238
-rw-r--r--OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/Common.cs57
-rw-r--r--OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventManager.cs259
-rw-r--r--OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueManager.cs363
-rw-r--r--OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/LSLLongCmdHandler.cs295
-rw-r--r--OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/ScriptEngine.cs131
-rw-r--r--OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/ScriptManager.cs347
7 files changed, 1690 insertions, 0 deletions
diff --git a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/AppDomainManager.cs b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/AppDomainManager.cs
new file mode 100644
index 0000000..4eea69a
--- /dev/null
+++ b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/AppDomainManager.cs
@@ -0,0 +1,238 @@
1/*
2* Copyright (c) Contributors, http://opensimulator.org/
3* See CONTRIBUTORS.TXT for a full list of copyright holders.
4*
5* Redistribution and use in source and binary forms, with or without
6* modification, are permitted provided that the following conditions are met:
7* * Redistributions of source code must retain the above copyright
8* notice, this list of conditions and the following disclaimer.
9* * Redistributions in binary form must reproduce the above copyright
10* notice, this list of conditions and the following disclaimer in the
11* documentation and/or other materials provided with the distribution.
12* * Neither the name of the OpenSim Project nor the
13* names of its contributors may be used to endorse or promote products
14* derived from this software without specific prior written permission.
15*
16* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS AS IS AND ANY
17* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*
27*/
28
29using System;
30using System.Collections;
31using System.Collections.Generic;
32using System.Reflection;
33using OpenSim.Region.ScriptEngine.Common;
34
35namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
36{
37 public class AppDomainManager
38 {
39
40 //
41 // This class does AppDomain handling and loading/unloading of scripts in it.
42 // It is instanced in "ScriptEngine" and controlled from "ScriptManager"
43 //
44 // 1. Create a new AppDomain if old one is full (or doesn't exist)
45 // 2. Load scripts into AppDomain
46 // 3. Unload scripts from AppDomain (stopping them and marking them as inactive)
47 // 4. Unload AppDomain completely when all scripts in it has stopped
48 //
49
50
51 private int maxScriptsPerAppDomain = 1;
52
53 /// <summary>
54 /// Internal list of all AppDomains
55 /// </summary>
56 private List<AppDomainStructure> appDomains = new List<AppDomainStructure>();
57
58 /// <summary>
59 /// Structure to keep track of data around AppDomain
60 /// </summary>
61 private class AppDomainStructure
62 {
63 /// <summary>
64 /// The AppDomain itself
65 /// </summary>
66 public AppDomain CurrentAppDomain;
67
68 /// <summary>
69 /// Number of scripts loaded into AppDomain
70 /// </summary>
71 public int ScriptsLoaded;
72
73 /// <summary>
74 /// Number of dead scripts
75 /// </summary>
76 public int ScriptsWaitingUnload;
77 }
78
79 /// <summary>
80 /// Current AppDomain
81 /// </summary>
82 private AppDomainStructure currentAD;
83
84 private object getLock = new object(); // Mutex
85 private object freeLock = new object(); // Mutex
86
87 //private ScriptEngine m_scriptEngine;
88 //public AppDomainManager(ScriptEngine scriptEngine)
89 public AppDomainManager()
90 {
91 //m_scriptEngine = scriptEngine;
92 }
93
94 /// <summary>
95 /// Find a free AppDomain, creating one if necessary
96 /// </summary>
97 /// <returns>Free AppDomain</returns>
98 private AppDomainStructure GetFreeAppDomain()
99 {
100 Console.WriteLine("Finding free AppDomain");
101 lock (getLock)
102 {
103 // Current full?
104 if (currentAD != null && currentAD.ScriptsLoaded >= maxScriptsPerAppDomain)
105 {
106 // Add it to AppDomains list and empty current
107 appDomains.Add(currentAD);
108 currentAD = null;
109 }
110 // No current
111 if (currentAD == null)
112 {
113 // Create a new current AppDomain
114 currentAD = new AppDomainStructure();
115 currentAD.CurrentAppDomain = PrepareNewAppDomain();
116 }
117
118 Console.WriteLine("Scripts loaded in this Appdomain: " + currentAD.ScriptsLoaded);
119 return currentAD;
120 } // lock
121 }
122
123 private int AppDomainNameCount;
124
125 /// <summary>
126 /// Create and prepare a new AppDomain for scripts
127 /// </summary>
128 /// <returns>The new AppDomain</returns>
129 private AppDomain PrepareNewAppDomain()
130 {
131 // Create and prepare a new AppDomain
132 AppDomainNameCount++;
133 // TODO: Currently security match current appdomain
134
135 // Construct and initialize settings for a second AppDomain.
136 AppDomainSetup ads = new AppDomainSetup();
137 ads.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory;
138 ads.DisallowBindingRedirects = false;
139 ads.DisallowCodeDownload = true;
140 ads.LoaderOptimization = LoaderOptimization.MultiDomain; // Sounds good ;)
141 ads.ShadowCopyFiles = "true"; // Enabled shadowing
142 ads.ConfigurationFile = AppDomain.CurrentDomain.SetupInformation.ConfigurationFile;
143
144 AppDomain AD = AppDomain.CreateDomain("ScriptAppDomain_" + AppDomainNameCount, null, ads);
145 Console.WriteLine("Loading: " +
146 AssemblyName.GetAssemblyName("OpenSim.Region.ScriptEngine.Common.dll").ToString());
147 AD.Load(AssemblyName.GetAssemblyName("OpenSim.Region.ScriptEngine.Common.dll"));
148
149 // Return the new AppDomain
150 return AD;
151 }
152
153 /// <summary>
154 /// Unload appdomains that are full and have only dead scripts
155 /// </summary>
156 private void UnloadAppDomains()
157 {
158 lock (freeLock)
159 {
160 // Go through all
161 foreach (AppDomainStructure ads in new ArrayList(appDomains))
162 {
163 // Don't process current AppDomain
164 if (ads.CurrentAppDomain != currentAD.CurrentAppDomain)
165 {
166 // Not current AppDomain
167 // Is number of unloaded bigger or equal to number of loaded?
168 if (ads.ScriptsLoaded <= ads.ScriptsWaitingUnload)
169 {
170 Console.WriteLine("Found empty AppDomain, unloading");
171 // Remove from internal list
172 appDomains.Remove(ads);
173#if DEBUG
174 long m = GC.GetTotalMemory(true);
175#endif
176 // Unload
177 AppDomain.Unload(ads.CurrentAppDomain);
178#if DEBUG
179 Console.WriteLine("AppDomain unload freed " + (m - GC.GetTotalMemory(true)) +
180 " bytes of memory");
181#endif
182 }
183 }
184 } // foreach
185 } // lock
186 }
187
188
189 public IScript LoadScript(string FileName)
190 {
191 // Find next available AppDomain to put it in
192 AppDomainStructure FreeAppDomain = GetFreeAppDomain();
193
194 Console.WriteLine("Loading into AppDomain: " + FileName);
195 IScript mbrt =
196 (IScript)
197 FreeAppDomain.CurrentAppDomain.CreateInstanceFromAndUnwrap(FileName, "SecondLife.Script");
198 //Console.WriteLine("ScriptEngine AppDomainManager: is proxy={0}", RemotingServices.IsTransparentProxy(mbrt));
199 FreeAppDomain.ScriptsLoaded++;
200
201 return mbrt;
202 }
203
204
205 /// <summary>
206 /// Increase "dead script" counter for an AppDomain
207 /// </summary>
208 /// <param name="ad"></param>
209 //[Obsolete("Needs fixing, needs a real purpose in life!!!")]
210 public void StopScript(AppDomain ad)
211 {
212 lock (freeLock)
213 {
214 Console.WriteLine("Stopping script in AppDomain");
215 // Check if it is current AppDomain
216 if (currentAD.CurrentAppDomain == ad)
217 {
218 // Yes - increase
219 currentAD.ScriptsWaitingUnload++;
220 return;
221 }
222
223 // Lopp through all AppDomains
224 foreach (AppDomainStructure ads in new ArrayList(appDomains))
225 {
226 if (ads.CurrentAppDomain == ad)
227 {
228 // Found it
229 ads.ScriptsWaitingUnload++;
230 break;
231 }
232 } // foreach
233 } // lock
234
235 UnloadAppDomains(); // Outsite lock, has its own GetLock
236 }
237 }
238} \ No newline at end of file
diff --git a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/Common.cs b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/Common.cs
new file mode 100644
index 0000000..00ea078
--- /dev/null
+++ b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/Common.cs
@@ -0,0 +1,57 @@
1/*
2* Copyright (c) Contributors, http://opensimulator.org/
3* See CONTRIBUTORS.TXT for a full list of copyright holders.
4*
5* Redistribution and use in source and binary forms, with or without
6* modification, are permitted provided that the following conditions are met:
7* * Redistributions of source code must retain the above copyright
8* notice, this list of conditions and the following disclaimer.
9* * Redistributions in binary form must reproduce the above copyright
10* notice, this list of conditions and the following disclaimer in the
11* documentation and/or other materials provided with the distribution.
12* * Neither the name of the OpenSim Project nor the
13* names of its contributors may be used to endorse or promote products
14* derived from this software without specific prior written permission.
15*
16* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*
27*/
28/* Original code: Tedd Hansen */
29namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
30{
31 public static class Common
32 {
33 public static bool debug = true;
34 public static ScriptEngine mySE;
35
36 // This class just contains some static log stuff used for debugging.
37
38 //public delegate void SendToDebugEventDelegate(string Message);
39 //public delegate void SendToLogEventDelegate(string Message);
40 //static public event SendToDebugEventDelegate SendToDebugEvent;
41 //static public event SendToLogEventDelegate SendToLogEvent;
42
43 public static void SendToDebug(string Message)
44 {
45 //if (Debug == true)
46 mySE.Log.Verbose("ScriptEngine", "Debug: " + Message);
47 //SendToDebugEvent("\r\n" + DateTime.Now.ToString("[HH:mm:ss] ") + Message);
48 }
49
50 public static void SendToLog(string Message)
51 {
52 //if (Debug == true)
53 mySE.Log.Verbose("ScriptEngine", "LOG: " + Message);
54 //SendToLogEvent("\r\n" + DateTime.Now.ToString("[HH:mm:ss] ") + Message);
55 }
56 }
57} \ No newline at end of file
diff --git a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventManager.cs b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventManager.cs
new file mode 100644
index 0000000..a5ad911
--- /dev/null
+++ b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventManager.cs
@@ -0,0 +1,259 @@
1/*
2* Copyright (c) Contributors, http://opensimulator.org/
3* See CONTRIBUTORS.TXT for a full list of copyright holders.
4*
5* Redistribution and use in source and binary forms, with or without
6* modification, are permitted provided that the following conditions are met:
7* * Redistributions of source code must retain the above copyright
8* notice, this list of conditions and the following disclaimer.
9* * Redistributions in binary form must reproduce the above copyright
10* notice, this list of conditions and the following disclaimer in the
11* documentation and/or other materials provided with the distribution.
12* * Neither the name of the OpenSim Project nor the
13* names of its contributors may be used to endorse or promote products
14* derived from this software without specific prior written permission.
15*
16* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*
27*/
28/* Original code: Tedd Hansen */
29using System;
30using libsecondlife;
31using OpenSim.Framework;
32
33namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
34{
35 /// <summary>
36 /// Prepares events so they can be directly executed upon a script by EventQueueManager, then queues it.
37 /// </summary>
38 [Serializable]
39 public class EventManager : OpenSim.Region.ScriptEngine.Common.ScriptServerInterfaces.RemoteEvents
40 {
41
42 //
43 // Class is instanced in "ScriptEngine" and Uses "EventQueueManager" that is also instanced in "ScriptEngine".
44 // This class needs a bit of explaining:
45 //
46 // This class it the link between an event inside OpenSim and the corresponding event in a user script being executed.
47 //
48 // For example when an user touches an object then the "myScriptEngine.World.EventManager.OnObjectGrab" event is fired inside OpenSim.
49 // We hook up to this event and queue a touch_start in EventQueueManager with the proper LSL parameters.
50 // It will then be delivered to the script by EventQueueManager.
51 //
52 // You can check debug C# dump of an LSL script if you need to verify what exact parameters are needed.
53 //
54
55
56 private ScriptEngine myScriptEngine;
57 //public IScriptHost TEMP_OBJECT_ID;
58 public EventManager(ScriptEngine _ScriptEngine, bool performHookUp)
59 {
60 myScriptEngine = _ScriptEngine;
61
62 // Hook up to events from OpenSim
63 // We may not want to do it because someone is controlling us and will deliver events to us
64 if (performHookUp)
65 {
66 myScriptEngine.Log.Verbose("ScriptEngine", "Hooking up to server events");
67 myScriptEngine.World.EventManager.OnObjectGrab += touch_start;
68 myScriptEngine.World.EventManager.OnRezScript += OnRezScript;
69 myScriptEngine.World.EventManager.OnRemoveScript += OnRemoveScript;
70 // TODO: HOOK ALL EVENTS UP TO SERVER!
71 }
72 }
73
74 public void touch_start(uint localID, LLVector3 offsetPos, IClientAPI remoteClient)
75 {
76 // Add to queue for all scripts in ObjectID object
77 myScriptEngine.m_EventQueueManager.AddToObjectQueue(localID, "touch_start", new object[] {(int) 1});
78 }
79
80 public void OnRezScript(uint localID, LLUUID itemID, string script)
81 {
82 Console.WriteLine("OnRezScript localID: " + localID + " LLUID: " + itemID.ToString() + " Size: " +
83 script.Length);
84 myScriptEngine.m_ScriptManager.StartScript(localID, itemID, script);
85 }
86
87 public void OnRemoveScript(uint localID, LLUUID itemID)
88 {
89 Console.WriteLine("OnRemoveScript localID: " + localID + " LLUID: " + itemID.ToString());
90 myScriptEngine.m_ScriptManager.StopScript(
91 localID,
92 itemID
93 );
94 }
95
96 // TODO: Replace placeholders below
97 // NOTE! THE PARAMETERS FOR THESE FUNCTIONS ARE NOT CORRECT!
98 // These needs to be hooked up to OpenSim during init of this class
99 // then queued in EventQueueManager.
100 // When queued in EventQueueManager they need to be LSL compatible (name and params)
101
102 public void state_exit(uint localID, LLUUID itemID)
103 {
104 myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "state_exit");
105 }
106
107 public void touch(uint localID, LLUUID itemID)
108 {
109 myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "touch");
110 }
111
112 public void touch_end(uint localID, LLUUID itemID)
113 {
114 myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "touch_end");
115 }
116
117 public void collision_start(uint localID, LLUUID itemID)
118 {
119 myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "collision_start");
120 }
121
122 public void collision(uint localID, LLUUID itemID)
123 {
124 myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "collision");
125 }
126
127 public void collision_end(uint localID, LLUUID itemID)
128 {
129 myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "collision_end");
130 }
131
132 public void land_collision_start(uint localID, LLUUID itemID)
133 {
134 myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "land_collision_start");
135 }
136
137 public void land_collision(uint localID, LLUUID itemID)
138 {
139 myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "land_collision");
140 }
141
142 public void land_collision_end(uint localID, LLUUID itemID)
143 {
144 myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "land_collision_end");
145 }
146
147 // Handled by long commands
148 public void timer(uint localID, LLUUID itemID)
149 {
150 //myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "");
151 }
152
153 public void listen(uint localID, LLUUID itemID)
154 {
155 myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "listen");
156 }
157
158 public void on_rez(uint localID, LLUUID itemID)
159 {
160 myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "on_rez");
161 }
162
163 public void sensor(uint localID, LLUUID itemID)
164 {
165 myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "sensor");
166 }
167
168 public void no_sensor(uint localID, LLUUID itemID)
169 {
170 myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "no_sensor");
171 }
172
173 public void control(uint localID, LLUUID itemID)
174 {
175 myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "control");
176 }
177
178 public void money(uint localID, LLUUID itemID)
179 {
180 myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "money");
181 }
182
183 public void email(uint localID, LLUUID itemID)
184 {
185 myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "email");
186 }
187
188 public void at_target(uint localID, LLUUID itemID)
189 {
190 myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "at_target");
191 }
192
193 public void not_at_target(uint localID, LLUUID itemID)
194 {
195 myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "not_at_target");
196 }
197
198 public void at_rot_target(uint localID, LLUUID itemID)
199 {
200 myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "at_rot_target");
201 }
202
203 public void not_at_rot_target(uint localID, LLUUID itemID)
204 {
205 myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "not_at_rot_target");
206 }
207
208 public void run_time_permissions(uint localID, LLUUID itemID)
209 {
210 myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "run_time_permissions");
211 }
212
213 public void changed(uint localID, LLUUID itemID)
214 {
215 myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "changed");
216 }
217
218 public void attach(uint localID, LLUUID itemID)
219 {
220 myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "attach");
221 }
222
223 public void dataserver(uint localID, LLUUID itemID)
224 {
225 myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "dataserver");
226 }
227
228 public void link_message(uint localID, LLUUID itemID)
229 {
230 myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "link_message");
231 }
232
233 public void moving_start(uint localID, LLUUID itemID)
234 {
235 myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "moving_start");
236 }
237
238 public void moving_end(uint localID, LLUUID itemID)
239 {
240 myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "moving_end");
241 }
242
243 public void object_rez(uint localID, LLUUID itemID)
244 {
245 myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "object_rez");
246 }
247
248 public void remote_data(uint localID, LLUUID itemID)
249 {
250 myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "remote_data");
251 }
252
253 // Handled by long commands
254 public void http_response(uint localID, LLUUID itemID)
255 {
256 // myScriptEngine.m_EventQueueManager.AddToScriptQueue(localID, itemID, "http_response");
257 }
258 }
259} \ No newline at end of file
diff --git a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueManager.cs b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueManager.cs
new file mode 100644
index 0000000..62194df
--- /dev/null
+++ b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/EventQueueManager.cs
@@ -0,0 +1,363 @@
1/*
2* Copyright (c) Contributors, http://opensimulator.org/
3* See CONTRIBUTORS.TXT for a full list of copyright holders.
4*
5* Redistribution and use in source and binary forms, with or without
6* modification, are permitted provided that the following conditions are met:
7* * Redistributions of source code must retain the above copyright
8* notice, this list of conditions and the following disclaimer.
9* * Redistributions in binary form must reproduce the above copyright
10* notice, this list of conditions and the following disclaimer in the
11* documentation and/or other materials provided with the distribution.
12* * Neither the name of the OpenSim Project nor the
13* names of its contributors may be used to endorse or promote products
14* derived from this software without specific prior written permission.
15*
16* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*
27*/
28/* Original code: Tedd Hansen */
29using System;
30using System.Collections;
31using System.Collections.Generic;
32using System.Threading;
33using libsecondlife;
34using OpenSim.Framework;
35using OpenSim.Region.Environment.Scenes.Scripting;
36
37namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
38{
39 /// <summary>
40 /// EventQueueManager handles event queues
41 /// Events are queued and executed in separate thread
42 /// </summary>
43 [Serializable]
44 public class EventQueueManager
45 {
46
47 //
48 // Class is instanced in "ScriptEngine" and used by "EventManager" also instanced in "ScriptEngine".
49 //
50 // Class purpose is to queue and execute functions that are received by "EventManager":
51 // - allowing "EventManager" to release its event thread immediately, thus not interrupting server execution.
52 // - allowing us to prioritize and control execution of script functions.
53 // Class can use multiple threads for simultaneous execution. Mutexes are used for thread safety.
54 //
55 // 1. Hold an execution queue for scripts
56 // 2. Use threads to process queue, each thread executes one script function on each pass.
57 // 3. Catch any script error and process it
58 //
59 //
60 // Notes:
61 // * Current execution load balancing is optimized for 1 thread, and can cause unfair execute balancing between scripts.
62 // Not noticeable unless server is under high load.
63 // * This class contains the number of threads used for script executions. Since we are not microthreading scripts yet,
64 // increase number of threads to allow more concurrent script executions in OpenSim.
65 //
66
67
68 /// <summary>
69 /// List of threads processing event queue
70 /// </summary>
71 private List<Thread> eventQueueThreads = new List<Thread>();
72
73 private object queueLock = new object(); // Mutex lock object
74
75 /// <summary>
76 /// How many ms to sleep if queue is empty
77 /// </summary>
78 private int nothingToDoSleepms = 50;
79
80 /// <summary>
81 /// How many threads to process queue with
82 /// </summary>
83 private int numberOfThreads = 2;
84
85 /// <summary>
86 /// Queue containing events waiting to be executed
87 /// </summary>
88 private Queue<QueueItemStruct> eventQueue = new Queue<QueueItemStruct>();
89
90 /// <summary>
91 /// Queue item structure
92 /// </summary>
93 private struct QueueItemStruct
94 {
95 public uint localID;
96 public LLUUID itemID;
97 public string functionName;
98 public object[] param;
99 }
100
101 /// <summary>
102 /// List of localID locks for mutex processing of script events
103 /// </summary>
104 private List<uint> objectLocks = new List<uint>();
105
106 private object tryLockLock = new object(); // Mutex lock object
107
108 private ScriptEngine m_ScriptEngine;
109
110 public EventQueueManager(ScriptEngine _ScriptEngine)
111 {
112 m_ScriptEngine = _ScriptEngine;
113
114 //
115 // Start event queue processing threads (worker threads)
116 //
117 for (int ThreadCount = 0; ThreadCount <= numberOfThreads; ThreadCount++)
118 {
119 Thread EventQueueThread = new Thread(EventQueueThreadLoop);
120 eventQueueThreads.Add(EventQueueThread);
121 EventQueueThread.IsBackground = true;
122 EventQueueThread.Priority = ThreadPriority.BelowNormal;
123 EventQueueThread.Name = "EventQueueManagerThread_" + ThreadCount;
124 EventQueueThread.Start();
125 }
126 }
127
128 ~EventQueueManager()
129 {
130 // Kill worker threads
131 foreach (Thread EventQueueThread in new ArrayList(eventQueueThreads))
132 {
133 if (EventQueueThread != null && EventQueueThread.IsAlive == true)
134 {
135 try
136 {
137 EventQueueThread.Abort();
138 EventQueueThread.Join();
139 }
140 catch (Exception)
141 {
142 //myScriptEngine.Log.Verbose("ScriptEngine", "EventQueueManager Exception killing worker thread: " + e.ToString());
143 }
144 }
145 }
146 eventQueueThreads.Clear();
147 // Todo: Clean up our queues
148 eventQueue.Clear();
149 }
150
151 /// <summary>
152 /// Queue processing thread loop
153 /// </summary>
154 private void EventQueueThreadLoop()
155 {
156 //myScriptEngine.m_logger.Verbose("ScriptEngine", "EventQueueManager Worker thread spawned");
157 try
158 {
159 QueueItemStruct BlankQIS = new QueueItemStruct();
160 while (true)
161 {
162 try
163 {
164 QueueItemStruct QIS = BlankQIS;
165 bool GotItem = false;
166
167 if (eventQueue.Count == 0)
168 {
169 // Nothing to do? Sleep a bit waiting for something to do
170 Thread.Sleep(nothingToDoSleepms);
171 }
172 else
173 {
174 // Something in queue, process
175 //myScriptEngine.m_logger.Verbose("ScriptEngine", "Processing event for localID: " + QIS.localID + ", itemID: " + QIS.itemID + ", FunctionName: " + QIS.FunctionName);
176
177 // OBJECT BASED LOCK - TWO THREADS WORKING ON SAME OBJECT IS NOT GOOD
178 lock (queueLock)
179 {
180 GotItem = false;
181 for (int qc = 0; qc < eventQueue.Count; qc++)
182 {
183 // Get queue item
184 QIS = eventQueue.Dequeue();
185
186 // Check if object is being processed by someone else
187 if (TryLock(QIS.localID) == false)
188 {
189 // Object is already being processed, requeue it
190 eventQueue.Enqueue(QIS);
191 }
192 else
193 {
194 // We have lock on an object and can process it
195 GotItem = true;
196 break;
197 }
198 } // go through queue
199 } // lock
200
201 if (GotItem == true)
202 {
203 // Execute function
204 try
205 {
206#if DEBUG
207 m_ScriptEngine.Log.Debug("ScriptEngine", "Executing event:\r\n"
208 + "QIS.localID: " + QIS.localID
209 + ", QIS.itemID: " + QIS.itemID
210 + ", QIS.functionName: " + QIS.functionName);
211#endif
212 m_ScriptEngine.m_ScriptManager.ExecuteEvent(QIS.localID, QIS.itemID,
213 QIS.functionName, QIS.param);
214 }
215 catch (Exception e)
216 {
217 // DISPLAY ERROR INWORLD
218 string text = "Error executing script function \"" + QIS.functionName + "\":\r\n";
219 //if (e.InnerException != null)
220 //{
221 // Send inner exception
222 text += e.InnerException.Message.ToString();
223 //}
224 //else
225 //{
226 text += "\r\n";
227 // Send normal
228 text += e.Message.ToString();
229 //}
230 try
231 {
232 if (text.Length > 1500)
233 text = text.Substring(0, 1500);
234 IScriptHost m_host = m_ScriptEngine.World.GetSceneObjectPart(QIS.localID);
235 //if (m_host != null)
236 //{
237 m_ScriptEngine.World.SimChat(Helpers.StringToField(text), ChatTypeEnum.Say, 0,
238 m_host.AbsolutePosition, m_host.Name, m_host.UUID);
239 }
240 catch
241 {
242 //}
243 //else
244 //{
245 // T oconsole
246 m_ScriptEngine.Log.Error("ScriptEngine",
247 "Unable to send text in-world:\r\n" + text);
248 }
249 }
250 finally
251 {
252 ReleaseLock(QIS.localID);
253 }
254 }
255 } // Something in queue
256 }
257 catch (ThreadAbortException tae)
258 {
259 throw tae;
260 }
261 catch (Exception e)
262 {
263 m_ScriptEngine.Log.Error("ScriptEngine", "Exception in EventQueueThreadLoop: " + e.ToString());
264 }
265 } // while
266 } // try
267 catch (ThreadAbortException)
268 {
269 //myScriptEngine.Log.Verbose("ScriptEngine", "EventQueueManager Worker thread killed: " + tae.Message);
270 }
271 }
272
273 /// <summary>
274 /// Try to get a mutex lock on localID
275 /// </summary>
276 /// <param name="localID"></param>
277 /// <returns></returns>
278 private bool TryLock(uint localID)
279 {
280 lock (tryLockLock)
281 {
282 if (objectLocks.Contains(localID) == true)
283 {
284 return false;
285 }
286 else
287 {
288 objectLocks.Add(localID);
289 return true;
290 }
291 }
292 }
293
294 /// <summary>
295 /// Release mutex lock on localID
296 /// </summary>
297 /// <param name="localID"></param>
298 private void ReleaseLock(uint localID)
299 {
300 lock (tryLockLock)
301 {
302 if (objectLocks.Contains(localID) == true)
303 {
304 objectLocks.Remove(localID);
305 }
306 }
307 }
308
309
310 /// <summary>
311 /// Add event to event execution queue
312 /// </summary>
313 /// <param name="localID"></param>
314 /// <param name="FunctionName">Name of the function, will be state + "_event_" + FunctionName</param>
315 /// <param name="param">Array of parameters to match event mask</param>
316 public void AddToObjectQueue(uint localID, string FunctionName, params object[] param)
317 {
318 // Determine all scripts in Object and add to their queue
319 //myScriptEngine.m_logger.Verbose("ScriptEngine", "EventQueueManager Adding localID: " + localID + ", FunctionName: " + FunctionName);
320
321
322 // Do we have any scripts in this object at all? If not, return
323 if (m_ScriptEngine.m_ScriptManager.Scripts.ContainsKey(localID) == false)
324 {
325 //Console.WriteLine("Event \"" + FunctionName + "\" for localID: " + localID + ". No scripts found on this localID.");
326 return;
327 }
328
329 Dictionary<LLUUID, IScript>.KeyCollection scriptKeys =
330 m_ScriptEngine.m_ScriptManager.GetScriptKeys(localID);
331
332 foreach (LLUUID itemID in scriptKeys)
333 {
334 // Add to each script in that object
335 // TODO: Some scripts may not subscribe to this event. Should we NOT add it? Does it matter?
336 AddToScriptQueue(localID, itemID, FunctionName, param);
337 }
338 }
339
340 /// <summary>
341 /// Add event to event execution queue
342 /// </summary>
343 /// <param name="localID"></param>
344 /// <param name="itemID"></param>
345 /// <param name="FunctionName">Name of the function, will be state + "_event_" + FunctionName</param>
346 /// <param name="param">Array of parameters to match event mask</param>
347 public void AddToScriptQueue(uint localID, LLUUID itemID, string FunctionName, params object[] param)
348 {
349 lock (queueLock)
350 {
351 // Create a structure and add data
352 QueueItemStruct QIS = new QueueItemStruct();
353 QIS.localID = localID;
354 QIS.itemID = itemID;
355 QIS.functionName = FunctionName;
356 QIS.param = param;
357
358 // Add it to queue
359 eventQueue.Enqueue(QIS);
360 }
361 }
362 }
363} \ No newline at end of file
diff --git a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/LSLLongCmdHandler.cs b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/LSLLongCmdHandler.cs
new file mode 100644
index 0000000..94241eb
--- /dev/null
+++ b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/LSLLongCmdHandler.cs
@@ -0,0 +1,295 @@
1/*
2* Copyright (c) Contributors, http://opensimulator.org/
3* See CONTRIBUTORS.TXT for a full list of copyright holders.
4*
5* Redistribution and use in source and binary forms, with or without
6* modification, are permitted provided that the following conditions are met:
7* * Redistributions of source code must retain the above copyright
8* notice, this list of conditions and the following disclaimer.
9* * Redistributions in binary form must reproduce the above copyright
10* notice, this list of conditions and the following disclaimer in the
11* documentation and/or other materials provided with the distribution.
12* * Neither the name of the OpenSim Project nor the
13* names of its contributors may be used to endorse or promote products
14* derived from this software without specific prior written permission.
15*
16* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS AS IS AND ANY
17* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*
27*/
28
29using System;
30using System.Collections.Generic;
31using System.Threading;
32using libsecondlife;
33using OpenSim.Region.Environment.Interfaces;
34using OpenSim.Region.Environment.Modules;
35
36namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
37{
38 /// <summary>
39 /// Handles LSL commands that takes long time and returns an event, for example timers, HTTP requests, etc.
40 /// </summary>
41 public class LSLLongCmdHandler
42 {
43 private Thread cmdHandlerThread;
44 private int cmdHandlerThreadCycleSleepms = 100;
45
46 private ScriptEngine m_ScriptEngine;
47
48 public LSLLongCmdHandler(ScriptEngine _ScriptEngine)
49 {
50 m_ScriptEngine = _ScriptEngine;
51
52 // Start the thread that will be doing the work
53 cmdHandlerThread = new Thread(CmdHandlerThreadLoop);
54 cmdHandlerThread.Name = "CmdHandlerThread";
55 cmdHandlerThread.Priority = ThreadPriority.BelowNormal;
56 cmdHandlerThread.IsBackground = true;
57 cmdHandlerThread.Start();
58 }
59
60 ~LSLLongCmdHandler()
61 {
62 // Shut down thread
63 try
64 {
65 if (cmdHandlerThread != null)
66 {
67 if (cmdHandlerThread.IsAlive == true)
68 {
69 cmdHandlerThread.Abort();
70 cmdHandlerThread.Join();
71 }
72 }
73 }
74 catch
75 {
76 }
77 }
78
79 private void CmdHandlerThreadLoop()
80 {
81 while (true)
82 {
83 // Check timers
84 CheckTimerEvents();
85 Thread.Sleep(25);
86 // Check HttpRequests
87 CheckHttpRequests();
88 Thread.Sleep(25);
89 // Check XMLRPCRequests
90 CheckXMLRPCRequests();
91 Thread.Sleep(25);
92 // Check Listeners
93 CheckListeners();
94 Thread.Sleep(25);
95
96 // Sleep before next cycle
97 //Thread.Sleep(cmdHandlerThreadCycleSleepms);
98 }
99 }
100
101 /// <summary>
102 /// Remove a specific script (and all its pending commands)
103 /// </summary>
104 /// <param name="m_localID"></param>
105 /// <param name="m_itemID"></param>
106 public void RemoveScript(uint localID, LLUUID itemID)
107 {
108 // Remove a specific script
109
110 // Remove from: Timers
111 UnSetTimerEvents(localID, itemID);
112 // Remove from: HttpRequest
113 IHttpRequests iHttpReq =
114 m_ScriptEngine.World.RequestModuleInterface<IHttpRequests>();
115 iHttpReq.StopHttpRequest(localID, itemID);
116 }
117
118 #region TIMER
119
120 //
121 // TIMER
122 //
123 private class TimerClass
124 {
125 public uint localID;
126 public LLUUID itemID;
127 public double interval;
128 public DateTime next;
129 }
130
131 private List<TimerClass> Timers = new List<TimerClass>();
132 private object TimerListLock = new object();
133
134 public void SetTimerEvent(uint m_localID, LLUUID m_itemID, double sec)
135 {
136 Console.WriteLine("SetTimerEvent");
137
138 // Always remove first, in case this is a re-set
139 UnSetTimerEvents(m_localID, m_itemID);
140 if (sec == 0) // Disabling timer
141 return;
142
143 // Add to timer
144 TimerClass ts = new TimerClass();
145 ts.localID = m_localID;
146 ts.itemID = m_itemID;
147 ts.interval = sec;
148 ts.next = DateTime.Now.ToUniversalTime().AddSeconds(ts.interval);
149 lock (TimerListLock)
150 {
151 Timers.Add(ts);
152 }
153 }
154
155 public void UnSetTimerEvents(uint m_localID, LLUUID m_itemID)
156 {
157 // Remove from timer
158 lock (TimerListLock)
159 {
160 List<TimerClass> NewTimers = new List<TimerClass>();
161 foreach (TimerClass ts in Timers)
162 {
163 if (ts.localID != m_localID && ts.itemID != m_itemID)
164 {
165 NewTimers.Add(ts);
166 }
167 }
168 Timers.Clear();
169 Timers = NewTimers;
170 }
171 }
172
173 public void CheckTimerEvents()
174 {
175 // Nothing to do here?
176 if (Timers.Count == 0)
177 return;
178
179 lock (TimerListLock)
180 {
181 // Go through all timers
182 foreach (TimerClass ts in Timers)
183 {
184 // Time has passed?
185 if (ts.next.ToUniversalTime() < DateTime.Now.ToUniversalTime())
186 {
187 // Add it to queue
188 m_ScriptEngine.m_EventQueueManager.AddToScriptQueue(ts.localID, ts.itemID, "timer",
189 new object[] {});
190 // set next interval
191
192
193 ts.next = DateTime.Now.ToUniversalTime().AddSeconds(ts.interval);
194 }
195 }
196 } // lock
197 }
198
199 #endregion
200
201 #region HTTP REQUEST
202
203 public void CheckHttpRequests()
204 {
205 if (m_ScriptEngine.World == null)
206 return;
207
208 IHttpRequests iHttpReq =
209 m_ScriptEngine.World.RequestModuleInterface<IHttpRequests>();
210
211 HttpRequestClass httpInfo = null;
212
213 if (iHttpReq != null)
214 httpInfo = iHttpReq.GetNextCompletedRequest();
215
216 while (httpInfo != null)
217 {
218 //Console.WriteLine("PICKED HTTP REQ:" + httpInfo.response_body + httpInfo.status);
219
220 // Deliver data to prim's remote_data handler
221 //
222 // TODO: Returning null for metadata, since the lsl function
223 // only returns the byte for HTTP_BODY_TRUNCATED, which is not
224 // implemented here yet anyway. Should be fixed if/when maxsize
225 // is supported
226
227 object[] resobj = new object[]
228 {
229 httpInfo.reqID.ToString(), httpInfo.status, null, httpInfo.response_body
230 };
231
232 m_ScriptEngine.m_EventQueueManager.AddToScriptQueue(
233 httpInfo.localID, httpInfo.itemID, "http_response", resobj
234 );
235
236 httpInfo.Stop();
237 httpInfo = null;
238
239 httpInfo = iHttpReq.GetNextCompletedRequest();
240 }
241 }
242
243 #endregion
244
245 public void CheckXMLRPCRequests()
246 {
247 if (m_ScriptEngine.World == null)
248 return;
249
250 IXMLRPC xmlrpc = m_ScriptEngine.World.RequestModuleInterface<IXMLRPC>();
251
252 if (xmlrpc != null)
253 {
254 while (xmlrpc.hasRequests())
255 {
256 RPCRequestInfo rInfo = xmlrpc.GetNextRequest();
257 //Console.WriteLine("PICKED REQUEST");
258
259 //Deliver data to prim's remote_data handler
260 object[] resobj = new object[]
261 {
262 2, rInfo.GetChannelKey().ToString(), rInfo.GetMessageID().ToString(), "",
263 rInfo.GetIntValue(),
264 rInfo.GetStrVal()
265 };
266 m_ScriptEngine.m_EventQueueManager.AddToScriptQueue(
267 rInfo.GetLocalID(), rInfo.GetItemID(), "remote_data", resobj
268 );
269 }
270 }
271 }
272
273 public void CheckListeners()
274 {
275 if (m_ScriptEngine.World == null)
276 return;
277 IWorldComm comms = m_ScriptEngine.World.RequestModuleInterface<IWorldComm>();
278
279 while (comms.HasMessages())
280 {
281 ListenerInfo lInfo = comms.GetNextMessage();
282
283 //Deliver data to prim's listen handler
284 object[] resobj = new object[]
285 {
286 lInfo.GetChannel(), lInfo.GetName(), lInfo.GetID().ToString(), lInfo.GetMessage()
287 };
288
289 m_ScriptEngine.m_EventQueueManager.AddToScriptQueue(
290 lInfo.GetLocalID(), lInfo.GetItemID(), "listen", resobj
291 );
292 }
293 }
294 }
295} \ No newline at end of file
diff --git a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/ScriptEngine.cs b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/ScriptEngine.cs
new file mode 100644
index 0000000..9637252
--- /dev/null
+++ b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/ScriptEngine.cs
@@ -0,0 +1,131 @@
1/*
2* Copyright (c) Contributors, http://opensimulator.org/
3* See CONTRIBUTORS.TXT for a full list of copyright holders.
4*
5* Redistribution and use in source and binary forms, with or without
6* modification, are permitted provided that the following conditions are met:
7* * Redistributions of source code must retain the above copyright
8* notice, this list of conditions and the following disclaimer.
9* * Redistributions in binary form must reproduce the above copyright
10* notice, this list of conditions and the following disclaimer in the
11* documentation and/or other materials provided with the distribution.
12* * Neither the name of the OpenSim Project nor the
13* names of its contributors may be used to endorse or promote products
14* derived from this software without specific prior written permission.
15*
16* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*
27*/
28/* Original code: Tedd Hansen */
29using System;
30using Nini.Config;
31using OpenSim.Framework.Console;
32using OpenSim.Region.Environment.Interfaces;
33using OpenSim.Region.Environment.Scenes;
34using OpenSim.Region.ScriptEngine.Common;
35using OpenSim.Region.ScriptEngine.Common.ScriptEngineBase;
36
37namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
38{
39 /// <summary>
40 /// This is the root object for ScriptEngine. Objects access each other trough this class.
41 /// </summary>
42 ///
43 [Serializable]
44 public abstract class ScriptEngine : IRegionModule, OpenSim.Region.ScriptEngine.Common.ScriptServerInterfaces.ScriptEngine
45 {
46 public Scene World;
47 public EventManager m_EventManager; // Handles and queues incoming events from OpenSim
48 public EventQueueManager m_EventQueueManager; // Executes events
49 public ScriptManager m_ScriptManager; // Load, unload and execute scripts
50 public AppDomainManager m_AppDomainManager;
51 public LSLLongCmdHandler m_LSLLongCmdHandler;
52
53 public ScriptManager GetScriptManager()
54 {
55 return _GetScriptManager();
56 }
57 public abstract ScriptManager _GetScriptManager();
58
59 private LogBase m_log;
60
61 public ScriptEngine()
62 {
63 //Common.SendToDebug("ScriptEngine Object Initialized");
64 Common.mySE = this;
65 }
66
67 public LogBase Log
68 {
69 get { return m_log; }
70 }
71
72 public void InitializeEngine(Scene Sceneworld, LogBase logger, bool HookUpToServer, ScriptManager newScriptManager)
73 {
74 World = Sceneworld;
75 m_log = logger;
76
77 Log.Verbose("ScriptEngine", "DotNet & LSL ScriptEngine initializing");
78
79 //m_logger.Status("ScriptEngine", "InitializeEngine");
80
81 // Create all objects we'll be using
82 m_EventQueueManager = new EventQueueManager(this);
83 m_EventManager = new EventManager(this, HookUpToServer);
84 m_ScriptManager = newScriptManager;
85 //m_ScriptManager = new ScriptManager(this);
86 m_AppDomainManager = new AppDomainManager();
87 m_LSLLongCmdHandler = new LSLLongCmdHandler(this);
88
89 // Should we iterate the region for scripts that needs starting?
90 // Or can we assume we are loaded before anything else so we can use proper events?
91 }
92
93 public void Shutdown()
94 {
95 // We are shutting down
96 }
97
98 ScriptServerInterfaces.RemoteEvents ScriptServerInterfaces.ScriptEngine.EventManager()
99 {
100 return this.m_EventManager;
101 }
102
103
104 #region IRegionModule
105
106 public abstract void Initialise(Scene scene, IConfigSource config);
107
108 public void PostInitialise()
109 {
110 }
111
112 public void Close()
113 {
114 }
115
116 public string Name
117 {
118 get { return "DotNetEngine"; }
119 }
120
121 public bool IsSharedModule
122 {
123 get { return false; }
124 }
125
126
127
128 #endregion
129
130 }
131} \ No newline at end of file
diff --git a/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/ScriptManager.cs b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/ScriptManager.cs
new file mode 100644
index 0000000..55c9545
--- /dev/null
+++ b/OpenSim/Region/ScriptEngine/Common/ScriptEngineBase/ScriptManager.cs
@@ -0,0 +1,347 @@
1/*
2* Copyright (c) Contributors, http://opensimulator.org/
3* See CONTRIBUTORS.TXT for a full list of copyright holders.
4*
5* Redistribution and use in source and binary forms, with or without
6* modification, are permitted provided that the following conditions are met:
7* * Redistributions of source code must retain the above copyright
8* notice, this list of conditions and the following disclaimer.
9* * Redistributions in binary form must reproduce the above copyright
10* notice, this list of conditions and the following disclaimer in the
11* documentation and/or other materials provided with the distribution.
12* * Neither the name of the OpenSim Project nor the
13* names of its contributors may be used to endorse or promote products
14* derived from this software without specific prior written permission.
15*
16* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26*
27*/
28/* Original code: Tedd Hansen */
29using System;
30using System.Collections.Generic;
31using System.IO;
32using System.Reflection;
33using System.Runtime.Serialization.Formatters.Binary;
34using System.Threading;
35using libsecondlife;
36using OpenSim.Framework;
37using OpenSim.Region.Environment.Scenes;
38using OpenSim.Region.ScriptEngine.Common;
39
40namespace OpenSim.Region.ScriptEngine.Common.ScriptEngineBase
41{
42 /// <summary>
43 /// Loads scripts
44 /// Compiles them if necessary
45 /// Execute functions for EventQueueManager (Sends them to script on other AppDomain for execution)
46 /// </summary>
47 ///
48
49 // 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.
50 // * Keeps track of running scripts
51 // * Compiles script if necessary (through "Compiler")
52 // * Loads script (through "AppDomainManager" called from for example "EventQueueManager")
53 // * Executes functions inside script (called from for example "EventQueueManager" class)
54 // * Unloads script (through "AppDomainManager" called from for example "EventQueueManager")
55 // * Dedicated load/unload thread, and queues loading/unloading.
56 // This so that scripts starting or stopping will not slow down other theads or whole system.
57 //
58 [Serializable]
59 public abstract class ScriptManager
60 {
61 #region Declares
62
63 private Thread scriptLoadUnloadThread;
64 private int scriptLoadUnloadThread_IdleSleepms = 100;
65 private Queue<LUStruct> LUQueue = new Queue<LUStruct>();
66
67
68 // Load/Unload structure
69 private struct LUStruct
70 {
71 public uint localID;
72 public LLUUID itemID;
73 public string script;
74 public LUType Action;
75 }
76
77 private enum LUType
78 {
79 Unknown = 0,
80 Load = 1,
81 Unload = 2
82 }
83
84 // Object<string, Script<string, script>>
85 // IMPORTANT: Types and MemberInfo-derived objects require a LOT of memory.
86 // Instead use RuntimeTypeHandle, RuntimeFieldHandle and RunTimeHandle (IntPtr) instead!
87 public Dictionary<uint, Dictionary<LLUUID, IScript>> Scripts =
88 new Dictionary<uint, Dictionary<LLUUID, IScript>>();
89
90 public Scene World
91 {
92 get { return m_scriptEngine.World; }
93 }
94
95 #endregion
96
97 #region Object init/shutdown
98
99 public ScriptEngineBase.ScriptEngine m_scriptEngine;
100
101 public ScriptManager(ScriptEngineBase.ScriptEngine scriptEngine)
102 {
103 m_scriptEngine = scriptEngine;
104 AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
105 scriptLoadUnloadThread = new Thread(ScriptLoadUnloadThreadLoop);
106 scriptLoadUnloadThread.Name = "ScriptLoadUnloadThread";
107 scriptLoadUnloadThread.IsBackground = true;
108 scriptLoadUnloadThread.Priority = ThreadPriority.BelowNormal;
109 scriptLoadUnloadThread.Start();
110 }
111
112 ~ScriptManager()
113 {
114 // Abort load/unload thread
115 try
116 {
117 if (scriptLoadUnloadThread != null)
118 {
119 if (scriptLoadUnloadThread.IsAlive == true)
120 {
121 scriptLoadUnloadThread.Abort();
122 scriptLoadUnloadThread.Join();
123 }
124 }
125 }
126 catch
127 {
128 }
129 }
130
131 #endregion
132
133 #region Load / Unload scripts (Thread loop)
134
135 private void ScriptLoadUnloadThreadLoop()
136 {
137 try
138 {
139 while (true)
140 {
141 if (LUQueue.Count == 0)
142 Thread.Sleep(scriptLoadUnloadThread_IdleSleepms);
143 if (LUQueue.Count > 0)
144 {
145 LUStruct item = LUQueue.Dequeue();
146 lock (startStopLock) // Lock so we have only 1 thread working on loading/unloading of scripts
147 {
148 if (item.Action == LUType.Unload)
149 {
150 _StopScript(item.localID, item.itemID);
151 }
152 if (item.Action == LUType.Load)
153 {
154 _StartScript(item.localID, item.itemID, item.script);
155 }
156 }
157 }
158 }
159 }
160 catch (ThreadAbortException tae)
161 {
162 string a = tae.ToString();
163 a = "";
164 // Expected
165 }
166 }
167
168 #endregion
169
170 #region Helper functions
171
172 private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
173 {
174 //Console.WriteLine("ScriptManager.CurrentDomain_AssemblyResolve: " + args.Name);
175 return Assembly.GetExecutingAssembly().FullName == args.Name ? Assembly.GetExecutingAssembly() : null;
176 }
177
178 #endregion
179
180
181
182 #region Start/Stop/Reset script
183
184 private readonly Object startStopLock = new Object();
185
186 /// <summary>
187 /// Fetches, loads and hooks up a script to an objects events
188 /// </summary>
189 /// <param name="itemID"></param>
190 /// <param name="localID"></param>
191 public void StartScript(uint localID, LLUUID itemID, string Script)
192 {
193 LUStruct ls = new LUStruct();
194 ls.localID = localID;
195 ls.itemID = itemID;
196 ls.script = Script;
197 ls.Action = LUType.Load;
198 LUQueue.Enqueue(ls);
199 }
200
201 /// <summary>
202 /// Disables and unloads a script
203 /// </summary>
204 /// <param name="localID"></param>
205 /// <param name="itemID"></param>
206 public void StopScript(uint localID, LLUUID itemID)
207 {
208 LUStruct ls = new LUStruct();
209 ls.localID = localID;
210 ls.itemID = itemID;
211 ls.Action = LUType.Unload;
212 LUQueue.Enqueue(ls);
213 }
214
215 // Create a new instance of the compiler (reuse)
216 //private Compiler.LSL.Compiler LSLCompiler = new Compiler.LSL.Compiler();
217
218 public abstract void _StartScript(uint localID, LLUUID itemID, string Script);
219
220 public abstract void _StopScript(uint localID, LLUUID itemID);
221
222
223 #endregion
224
225 #region Perform event execution in script
226
227 /// <summary>
228 /// Execute a LL-event-function in Script
229 /// </summary>
230 /// <param name="localID">Object the script is located in</param>
231 /// <param name="itemID">Script ID</param>
232 /// <param name="FunctionName">Name of function</param>
233 /// <param name="args">Arguments to pass to function</param>
234 internal void ExecuteEvent(uint localID, LLUUID itemID, string FunctionName, object[] args)
235 {
236#if DEBUG
237 Console.WriteLine("ScriptEngine: Inside ExecuteEvent for event " + FunctionName);
238#endif
239 // Execute a function in the script
240 //m_scriptEngine.Log.Verbose("ScriptEngine", "Executing Function localID: " + localID + ", itemID: " + itemID + ", FunctionName: " + FunctionName);
241 //ScriptBaseInterface Script = (ScriptBaseInterface)GetScript(localID, itemID);
242 IScript Script = GetScript(localID, itemID);
243 if (Script == null)
244 return;
245#if DEBUG
246 Console.WriteLine("ScriptEngine: Executing event: " + FunctionName);
247#endif
248 // Must be done in correct AppDomain, so leaving it up to the script itself
249 Script.Exec.ExecuteEvent(FunctionName, args);
250 }
251
252 #endregion
253
254 #region Internal functions to keep track of script
255
256 public Dictionary<LLUUID, IScript>.KeyCollection GetScriptKeys(uint localID)
257 {
258 if (Scripts.ContainsKey(localID) == false)
259 return null;
260
261 Dictionary<LLUUID, IScript> Obj;
262 Scripts.TryGetValue(localID, out Obj);
263
264 return Obj.Keys;
265 }
266
267 public IScript GetScript(uint localID, LLUUID itemID)
268 {
269 if (Scripts.ContainsKey(localID) == false)
270 return null;
271
272 Dictionary<LLUUID, IScript> Obj;
273 Scripts.TryGetValue(localID, out Obj);
274 if (Obj.ContainsKey(itemID) == false)
275 return null;
276
277 // Get script
278 IScript Script;
279 Obj.TryGetValue(itemID, out Script);
280
281 return Script;
282 }
283
284 public void SetScript(uint localID, LLUUID itemID, IScript Script)
285 {
286 // Create object if it doesn't exist
287 if (Scripts.ContainsKey(localID) == false)
288 {
289 Scripts.Add(localID, new Dictionary<LLUUID, IScript>());
290 }
291
292 // Delete script if it exists
293 Dictionary<LLUUID, IScript> Obj;
294 Scripts.TryGetValue(localID, out Obj);
295 if (Obj.ContainsKey(itemID) == true)
296 Obj.Remove(itemID);
297
298 // Add to object
299 Obj.Add(itemID, Script);
300 }
301
302 public void RemoveScript(uint localID, LLUUID itemID)
303 {
304 // Don't have that object?
305 if (Scripts.ContainsKey(localID) == false)
306 return;
307
308 // Delete script if it exists
309 Dictionary<LLUUID, IScript> Obj;
310 Scripts.TryGetValue(localID, out Obj);
311 if (Obj.ContainsKey(itemID) == true)
312 Obj.Remove(itemID);
313 }
314
315 #endregion
316
317
318 public void ResetScript(uint localID, LLUUID itemID)
319 {
320 string script = GetScript(localID, itemID).Source;
321 StopScript(localID, itemID);
322 StartScript(localID, itemID, script);
323 }
324
325
326 #region Script serialization/deserialization
327
328 public void GetSerializedScript(uint localID, LLUUID itemID)
329 {
330 // Serialize the script and return it
331 // Should not be a problem
332 FileStream fs = File.Create("SERIALIZED_SCRIPT_" + itemID);
333 BinaryFormatter b = new BinaryFormatter();
334 b.Serialize(fs, GetScript(localID, itemID));
335 fs.Close();
336 }
337
338 public void PutSerializedScript(uint localID, LLUUID itemID)
339 {
340 // Deserialize the script and inject it into an AppDomain
341
342 // How to inject into an AppDomain?
343 }
344
345 #endregion
346 }
347} \ No newline at end of file