aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/ScriptEngine/XEngine
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/ScriptEngine/XEngine')
-rw-r--r--OpenSim/Region/ScriptEngine/XEngine/Api/Runtime/XEngineScriptBase.cs61
-rw-r--r--OpenSim/Region/ScriptEngine/XEngine/EventManager.cs10
-rw-r--r--OpenSim/Region/ScriptEngine/XEngine/Properties/AssemblyInfo.cs7
-rw-r--r--OpenSim/Region/ScriptEngine/XEngine/Resources/XEngine.addin.xml13
-rw-r--r--OpenSim/Region/ScriptEngine/XEngine/Tests/XEngineBasicTests.cs (renamed from OpenSim/Region/ScriptEngine/XEngine/Tests/XEngineTest.cs)7
-rw-r--r--OpenSim/Region/ScriptEngine/XEngine/Tests/XEngineCrossingTests.cs195
-rw-r--r--OpenSim/Region/ScriptEngine/XEngine/Tests/XEnginePersistenceTests.cs152
-rwxr-xr-x[-rw-r--r--]OpenSim/Region/ScriptEngine/XEngine/XEngine.cs609
-rw-r--r--OpenSim/Region/ScriptEngine/XEngine/XWorkItem.cs10
9 files changed, 846 insertions, 218 deletions
diff --git a/OpenSim/Region/ScriptEngine/XEngine/Api/Runtime/XEngineScriptBase.cs b/OpenSim/Region/ScriptEngine/XEngine/Api/Runtime/XEngineScriptBase.cs
new file mode 100644
index 0000000..f4211c8
--- /dev/null
+++ b/OpenSim/Region/ScriptEngine/XEngine/Api/Runtime/XEngineScriptBase.cs
@@ -0,0 +1,61 @@
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 OpenSimulator 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
28using System;
29using System.Runtime.Remoting;
30using System.Runtime.Remoting.Lifetime;
31using System.Security.Permissions;
32using System.Threading;
33using System.Reflection;
34using System.Collections;
35using System.Collections.Generic;
36using OpenSim.Region.ScriptEngine.Interfaces;
37using OpenSim.Region.ScriptEngine.Shared;
38using OpenSim.Region.ScriptEngine.Shared.ScriptBase;
39
40namespace OpenSim.Region.ScriptEngine.XEngine.ScriptBase
41{
42 public class XEngineScriptBase : ScriptBaseClass
43 {
44 /// <summary>
45 /// Used for script sleeps when we are using co-operative script termination.
46 /// </summary>
47 /// <remarks>null if co-operative script termination is not active</remarks>
48 WaitHandle m_coopSleepHandle;
49
50 public XEngineScriptBase(WaitHandle coopSleepHandle) : base()
51 {
52 m_coopSleepHandle = coopSleepHandle;
53 }
54
55 public void opensim_reserved_CheckForCoopTermination()
56 {
57 if (m_coopSleepHandle != null && m_coopSleepHandle.WaitOne(0))
58 throw new ScriptCoopStopException();
59 }
60 }
61} \ No newline at end of file
diff --git a/OpenSim/Region/ScriptEngine/XEngine/EventManager.cs b/OpenSim/Region/ScriptEngine/XEngine/EventManager.cs
index 9405075..0ff2da3 100644
--- a/OpenSim/Region/ScriptEngine/XEngine/EventManager.cs
+++ b/OpenSim/Region/ScriptEngine/XEngine/EventManager.cs
@@ -52,7 +52,7 @@ namespace OpenSim.Region.ScriptEngine.XEngine
52 { 52 {
53 myScriptEngine = _ScriptEngine; 53 myScriptEngine = _ScriptEngine;
54 54
55 m_log.Info("[XEngine] Hooking up to server events"); 55// m_log.Info("[XEngine] Hooking up to server events");
56 myScriptEngine.World.EventManager.OnAttach += attach; 56 myScriptEngine.World.EventManager.OnAttach += attach;
57 myScriptEngine.World.EventManager.OnObjectGrab += touch_start; 57 myScriptEngine.World.EventManager.OnObjectGrab += touch_start;
58 myScriptEngine.World.EventManager.OnObjectGrabbing += touch; 58 myScriptEngine.World.EventManager.OnObjectGrabbing += touch;
@@ -62,6 +62,8 @@ namespace OpenSim.Region.ScriptEngine.XEngine
62 myScriptEngine.World.EventManager.OnScriptNotAtTargetEvent += not_at_target; 62 myScriptEngine.World.EventManager.OnScriptNotAtTargetEvent += not_at_target;
63 myScriptEngine.World.EventManager.OnScriptAtRotTargetEvent += at_rot_target; 63 myScriptEngine.World.EventManager.OnScriptAtRotTargetEvent += at_rot_target;
64 myScriptEngine.World.EventManager.OnScriptNotAtRotTargetEvent += not_at_rot_target; 64 myScriptEngine.World.EventManager.OnScriptNotAtRotTargetEvent += not_at_rot_target;
65 myScriptEngine.World.EventManager.OnScriptMovingStartEvent += moving_start;
66 myScriptEngine.World.EventManager.OnScriptMovingEndEvent += moving_end;
65 myScriptEngine.World.EventManager.OnScriptControlEvent += control; 67 myScriptEngine.World.EventManager.OnScriptControlEvent += control;
66 myScriptEngine.World.EventManager.OnScriptColliderStart += collision_start; 68 myScriptEngine.World.EventManager.OnScriptColliderStart += collision_start;
67 myScriptEngine.World.EventManager.OnScriptColliding += collision; 69 myScriptEngine.World.EventManager.OnScriptColliding += collision;
@@ -69,7 +71,7 @@ namespace OpenSim.Region.ScriptEngine.XEngine
69 myScriptEngine.World.EventManager.OnScriptLandColliderStart += land_collision_start; 71 myScriptEngine.World.EventManager.OnScriptLandColliderStart += land_collision_start;
70 myScriptEngine.World.EventManager.OnScriptLandColliding += land_collision; 72 myScriptEngine.World.EventManager.OnScriptLandColliding += land_collision;
71 myScriptEngine.World.EventManager.OnScriptLandColliderEnd += land_collision_end; 73 myScriptEngine.World.EventManager.OnScriptLandColliderEnd += land_collision_end;
72 IMoneyModule money=myScriptEngine.World.RequestModuleInterface<IMoneyModule>(); 74 IMoneyModule money = myScriptEngine.World.RequestModuleInterface<IMoneyModule>();
73 if (money != null) 75 if (money != null)
74 { 76 {
75 money.OnObjectPaid+=HandleObjectPaid; 77 money.OnObjectPaid+=HandleObjectPaid;
@@ -419,14 +421,14 @@ namespace OpenSim.Region.ScriptEngine.XEngine
419 // dataserver: not handled here 421 // dataserver: not handled here
420 // link_message: not handled here 422 // link_message: not handled here
421 423
422 public void moving_start(uint localID, UUID itemID) 424 public void moving_start(uint localID)
423 { 425 {
424 myScriptEngine.PostObjectEvent(localID, new EventParams( 426 myScriptEngine.PostObjectEvent(localID, new EventParams(
425 "moving_start",new object[0], 427 "moving_start",new object[0],
426 new DetectParams[0])); 428 new DetectParams[0]));
427 } 429 }
428 430
429 public void moving_end(uint localID, UUID itemID) 431 public void moving_end(uint localID)
430 { 432 {
431 myScriptEngine.PostObjectEvent(localID, new EventParams( 433 myScriptEngine.PostObjectEvent(localID, new EventParams(
432 "moving_end",new object[0], 434 "moving_end",new object[0],
diff --git a/OpenSim/Region/ScriptEngine/XEngine/Properties/AssemblyInfo.cs b/OpenSim/Region/ScriptEngine/XEngine/Properties/AssemblyInfo.cs
index bd26a8b..665929d 100644
--- a/OpenSim/Region/ScriptEngine/XEngine/Properties/AssemblyInfo.cs
+++ b/OpenSim/Region/ScriptEngine/XEngine/Properties/AssemblyInfo.cs
@@ -1,6 +1,7 @@
1using System.Reflection; 1using System.Reflection;
2using System.Runtime.CompilerServices; 2using System.Runtime.CompilerServices;
3using System.Runtime.InteropServices; 3using System.Runtime.InteropServices;
4using Mono.Addins;
4 5
5// General Information about an assembly is controlled through the following 6// General Information about an assembly is controlled through the following
6// set of attributes. Change these attribute values to modify the information 7// set of attributes. Change these attribute values to modify the information
@@ -29,5 +30,7 @@ using System.Runtime.InteropServices;
29// Build Number 30// Build Number
30// Revision 31// Revision
31// 32//
32[assembly: AssemblyVersion("0.7.5.*")] 33[assembly: AssemblyVersion("0.8.3.*")]
33[assembly: AssemblyFileVersion("1.0.0.0")] 34
35[assembly: Addin("OpenSim.Region.ScriptEngine.XEngine", OpenSim.VersionInfo.VersionNumber)]
36[assembly: AddinDependency("OpenSim.Region.Framework", OpenSim.VersionInfo.VersionNumber)]
diff --git a/OpenSim/Region/ScriptEngine/XEngine/Resources/XEngine.addin.xml b/OpenSim/Region/ScriptEngine/XEngine/Resources/XEngine.addin.xml
deleted file mode 100644
index 96c9c3a..0000000
--- a/OpenSim/Region/ScriptEngine/XEngine/Resources/XEngine.addin.xml
+++ /dev/null
@@ -1,13 +0,0 @@
1<Addin id="OpenSim.Region.ScriptEngine.XEngine" version="0.2">
2 <Runtime>
3 <Import assembly="OpenSim.Region.ScriptEngine.XEngine.dll"/>
4 </Runtime>
5
6 <Dependencies>
7 <Addin id="OpenSim" version="0.5" />
8 </Dependencies>
9
10 <Extension path = "/OpenSim/RegionModules">
11 <RegionModule id="XEngine" type="OpenSim.Region.ScriptEngine.XEngine.XEngine" />
12 </Extension>
13</Addin>
diff --git a/OpenSim/Region/ScriptEngine/XEngine/Tests/XEngineTest.cs b/OpenSim/Region/ScriptEngine/XEngine/Tests/XEngineBasicTests.cs
index 5abfe9a..878e571 100644
--- a/OpenSim/Region/ScriptEngine/XEngine/Tests/XEngineTest.cs
+++ b/OpenSim/Region/ScriptEngine/XEngine/Tests/XEngineBasicTests.cs
@@ -36,15 +36,14 @@ using OpenSim.Region.CoreModules.Scripting.WorldComm;
36using OpenSim.Region.Framework.Scenes; 36using OpenSim.Region.Framework.Scenes;
37using OpenSim.Region.Framework.Interfaces; 37using OpenSim.Region.Framework.Interfaces;
38using OpenSim.Tests.Common; 38using OpenSim.Tests.Common;
39using OpenSim.Tests.Common.Mock;
40 39
41namespace OpenSim.Region.ScriptEngine.XEngine.Tests 40namespace OpenSim.Region.ScriptEngine.XEngine.Tests
42{ 41{
43 /// <summary> 42 /// <summary>
44 /// XEngine tests. 43 /// Basic XEngine tests.
45 /// </summary> 44 /// </summary>
46 [TestFixture] 45 [TestFixture]
47 public class XEngineTest : OpenSimTestCase 46 public class XEngineBasicTests : OpenSimTestCase
48 { 47 {
49 private TestScene m_scene; 48 private TestScene m_scene;
50 private XEngine m_xEngine; 49 private XEngine m_xEngine;
@@ -87,7 +86,7 @@ namespace OpenSim.Region.ScriptEngine.XEngine.Tests
87 public void TestCompileAndStartScript() 86 public void TestCompileAndStartScript()
88 { 87 {
89 TestHelpers.InMethod(); 88 TestHelpers.InMethod();
90// log4net.Config.XmlConfigurator.Configure(); 89 TestHelpers.EnableLogging();
91 90
92 UUID userId = TestHelpers.ParseTail(0x1); 91 UUID userId = TestHelpers.ParseTail(0x1);
93// UUID objectId = TestHelpers.ParseTail(0x100); 92// UUID objectId = TestHelpers.ParseTail(0x100);
diff --git a/OpenSim/Region/ScriptEngine/XEngine/Tests/XEngineCrossingTests.cs b/OpenSim/Region/ScriptEngine/XEngine/Tests/XEngineCrossingTests.cs
new file mode 100644
index 0000000..587695f
--- /dev/null
+++ b/OpenSim/Region/ScriptEngine/XEngine/Tests/XEngineCrossingTests.cs
@@ -0,0 +1,195 @@
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 OpenSimulator 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
28using System;
29using System.Threading;
30using Nini.Config;
31using NUnit.Framework;
32using OpenMetaverse;
33using OpenSim.Framework;
34using OpenSim.Region.CoreModules.Framework.EntityTransfer;
35using OpenSim.Region.CoreModules.ServiceConnectorsOut.Simulation;
36using OpenSim.Region.Framework.Scenes;
37using OpenSim.Region.ScriptEngine.Shared;
38using OpenSim.Tests.Common;
39
40namespace OpenSim.Region.ScriptEngine.XEngine.Tests
41{
42 /// <summary>
43 /// XEngine tests connected with crossing scripts between regions.
44 /// </summary>
45 [TestFixture]
46 public class XEngineCrossingTests : OpenSimTestCase
47 {
48 [TestFixtureSetUp]
49 public void FixtureInit()
50 {
51 // Don't allow tests to be bamboozled by asynchronous events. Execute everything on the same thread.
52 Util.FireAndForgetMethod = FireAndForgetMethod.RegressionTest;
53 }
54
55 [TestFixtureTearDown]
56 public void TearDown()
57 {
58 // We must set this back afterwards, otherwise later tests will fail since they're expecting multiple
59 // threads. Possibly, later tests should be rewritten so none of them require async stuff (which regression
60 // tests really shouldn't).
61 Util.FireAndForgetMethod = Util.DefaultFireAndForgetMethod;
62 }
63
64 /// <summary>
65 /// Test script state preservation when a script crosses between regions on the same simulator.
66 /// </summary>
67 [Test]
68 public void TestScriptCrossOnSameSimulator()
69 {
70 TestHelpers.InMethod();
71// TestHelpers.EnableLogging();
72
73 UUID userId = TestHelpers.ParseTail(0x1);
74 int sceneObjectIdTail = 0x2;
75
76 EntityTransferModule etmA = new EntityTransferModule();
77 EntityTransferModule etmB = new EntityTransferModule();
78 LocalSimulationConnectorModule lscm = new LocalSimulationConnectorModule();
79 XEngine xEngineA = new XEngine();
80 XEngine xEngineB = new XEngine();
81 xEngineA.DebugLevel = 1;
82 xEngineB.DebugLevel = 1;
83
84 IConfigSource configSource = new IniConfigSource();
85
86 IConfig startupConfig = configSource.AddConfig("Startup");
87 startupConfig.Set("DefaultScriptEngine", "XEngine");
88
89 IConfig xEngineConfig = configSource.AddConfig("XEngine");
90 xEngineConfig.Set("Enabled", "true");
91 xEngineConfig.Set("StartDelay", "0");
92
93 // These tests will not run with AppDomainLoading = true, at least on mono. For unknown reasons, the call
94 // to AssemblyResolver.OnAssemblyResolve fails.
95 xEngineConfig.Set("AppDomainLoading", "false");
96
97 IConfig modulesConfig = configSource.AddConfig("Modules");
98 modulesConfig.Set("EntityTransferModule", etmA.Name);
99 modulesConfig.Set("SimulationServices", lscm.Name);
100
101 SceneHelpers sh = new SceneHelpers();
102 TestScene sceneA = sh.SetupScene("sceneA", TestHelpers.ParseTail(0x100), 1000, 1000, configSource);
103 TestScene sceneB = sh.SetupScene("sceneB", TestHelpers.ParseTail(0x200), 1000, 999, configSource);
104
105 SceneHelpers.SetupSceneModules(new Scene[] { sceneA, sceneB }, configSource, lscm);
106 SceneHelpers.SetupSceneModules(sceneA, configSource, etmA, xEngineA);
107 SceneHelpers.SetupSceneModules(sceneB, configSource, etmB, xEngineB);
108 sceneA.StartScripts();
109 sceneB.StartScripts();
110
111 SceneObjectGroup soSceneA = SceneHelpers.AddSceneObject(sceneA, 1, userId, "so1-", sceneObjectIdTail);
112 soSceneA.AbsolutePosition = new Vector3(128, 10, 20);
113
114 // CREATE SCRIPT TODO
115 InventoryItemBase scriptItemSceneA = new InventoryItemBase();
116 // itemTemplate.ID = itemId;
117 scriptItemSceneA.Name = "script1";
118 scriptItemSceneA.Folder = soSceneA.UUID;
119 scriptItemSceneA.InvType = (int)InventoryType.LSL;
120
121 AutoResetEvent chatEvent = new AutoResetEvent(false);
122 OSChatMessage messageReceived = null;
123 sceneA.EventManager.OnChatFromWorld += (s, m) => { messageReceived = m; chatEvent.Set(); };
124
125 sceneA.RezNewScript(userId, scriptItemSceneA,
126@"integer c = 0;
127
128default
129{
130 state_entry()
131 {
132 llSay(0, ""Script running"");
133 }
134
135 changed(integer change)
136 {
137 llSay(0, ""Changed"");
138 }
139
140 touch_start(integer n)
141 {
142 c = c + 1;
143 llSay(0, (string)c);
144 }
145}");
146
147 chatEvent.WaitOne(60000);
148
149 Assert.That(messageReceived, Is.Not.Null, "No chat message received.");
150 Assert.That(messageReceived.Message, Is.EqualTo("Script running"));
151
152 {
153 // XXX: Should not be doing this so directly. Should call some variant of EventManager.touch() instead.
154 DetectParams[] det = new DetectParams[1];
155 det[0] = new DetectParams();
156 det[0].Key = userId;
157 det[0].Populate(sceneA);
158
159 EventParams ep = new EventParams("touch_start", new Object[] { new LSL_Types.LSLInteger(1) }, det);
160
161 xEngineA.PostObjectEvent(soSceneA.LocalId, ep);
162 chatEvent.WaitOne(60000);
163
164 Assert.That(messageReceived.Message, Is.EqualTo("1"));
165 }
166
167 sceneB.EventManager.OnChatFromWorld += (s, m) => { messageReceived = m; chatEvent.Set(); };
168
169 // Cross with a negative value
170 soSceneA.AbsolutePosition = new Vector3(128, -10, 20);
171
172 chatEvent.WaitOne(60000);
173 Assert.That(messageReceived.Message, Is.EqualTo("Changed"));
174
175 // TEST sending event to moved prim and output
176 {
177 SceneObjectGroup soSceneB = sceneB.GetSceneObjectGroup(soSceneA.Name);
178 TaskInventoryItem scriptItemSceneB = soSceneB.RootPart.Inventory.GetInventoryItem(scriptItemSceneA.Name);
179
180 // XXX: Should not be doing this so directly. Should call some variant of EventManager.touch() instead.
181 DetectParams[] det = new DetectParams[1];
182 det[0] = new DetectParams();
183 det[0].Key = userId;
184 det[0].Populate(sceneB);
185
186 EventParams ep = new EventParams("touch_start", new Object[] { new LSL_Types.LSLInteger(1) }, det);
187
188 xEngineB.PostObjectEvent(soSceneB.LocalId, ep);
189 chatEvent.WaitOne(60000);
190
191 Assert.That(messageReceived.Message, Is.EqualTo("2"));
192 }
193 }
194 }
195} \ No newline at end of file
diff --git a/OpenSim/Region/ScriptEngine/XEngine/Tests/XEnginePersistenceTests.cs b/OpenSim/Region/ScriptEngine/XEngine/Tests/XEnginePersistenceTests.cs
new file mode 100644
index 0000000..2ef4058
--- /dev/null
+++ b/OpenSim/Region/ScriptEngine/XEngine/Tests/XEnginePersistenceTests.cs
@@ -0,0 +1,152 @@
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 OpenSimulator 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
28using System;
29using System.Collections.Generic;
30using System.IO;
31using System.Linq;
32using System.Threading;
33using Nini.Config;
34using NUnit.Framework;
35using OpenMetaverse;
36using OpenSim.Framework;
37using OpenSim.Region.CoreModules.Avatar.Attachments;
38using OpenSim.Region.CoreModules.Framework.InventoryAccess;
39using OpenSim.Region.Framework.Scenes;
40using OpenSim.Region.ScriptEngine.XEngine;
41using OpenSim.Services.Interfaces;
42using OpenSim.Tests.Common;
43
44namespace OpenSim.Region.ScriptEngine.Tests
45{
46 [TestFixture]
47 public class XEnginePersistenceTests : OpenSimTestCase
48 {
49 private AutoResetEvent m_chatEvent = new AutoResetEvent(false);
50
51 private void OnChatFromWorld(object sender, OSChatMessage oscm)
52 {
53 // Console.WriteLine("Got chat [{0}]", oscm.Message);
54
55 // m_osChatMessageReceived = oscm;
56 m_chatEvent.Set();
57 }
58
59 private void AddCommonConfig(IConfigSource config, List<object> modules)
60 {
61 config.AddConfig("Modules");
62 config.Configs["Modules"].Set("InventoryAccessModule", "BasicInventoryAccessModule");
63
64 AttachmentsModule attMod = new AttachmentsModule();
65 attMod.DebugLevel = 1;
66 modules.Add(attMod);
67 modules.Add(new BasicInventoryAccessModule());
68 }
69
70 private void AddScriptingConfig(IConfigSource config, XEngine.XEngine xEngine, List<object> modules)
71 {
72 IConfig startupConfig = config.AddConfig("Startup");
73 startupConfig.Set("DefaultScriptEngine", "XEngine");
74
75 IConfig xEngineConfig = config.AddConfig("XEngine");
76 xEngineConfig.Set("Enabled", "true");
77 xEngineConfig.Set("StartDelay", "0");
78
79 // These tests will not run with AppDomainLoading = true, at least on mono. For unknown reasons, the call
80 // to AssemblyResolver.OnAssemblyResolve fails.
81 xEngineConfig.Set("AppDomainLoading", "false");
82
83 modules.Add(xEngine);
84 }
85
86 private Scene CreateScriptingEnabledTestScene(XEngine.XEngine xEngine)
87 {
88 IConfigSource config = new IniConfigSource();
89 List<object> modules = new List<object>();
90
91 AddCommonConfig(config, modules);
92 AddScriptingConfig(config, xEngine, modules);
93
94 Scene scene
95 = new SceneHelpers().SetupScene(
96 "attachments-test-scene", TestHelpers.ParseTail(999), 1000, 1000, config);
97 SceneHelpers.SetupSceneModules(scene, config, modules.ToArray());
98
99 scene.StartScripts();
100
101 return scene;
102 }
103
104 [Test]
105 public void TestScriptedAttachmentPersistence()
106 {
107 TestHelpers.InMethod();
108// TestHelpers.EnableLogging();
109
110 XEngine.XEngine xEngine = new XEngine.XEngine();
111 Scene scene = CreateScriptingEnabledTestScene(xEngine);
112 UserAccount ua1 = UserAccountHelpers.CreateUserWithInventory(scene, 0x1);
113 ScenePresence sp = SceneHelpers.AddScenePresence(scene, ua1);
114
115 SceneObjectGroup so = SceneHelpers.CreateSceneObject(1, sp.UUID, "att-name", 0x10);
116 TaskInventoryHelpers.AddScript(
117 scene.AssetService,
118 so.RootPart,
119 "scriptItem",
120 "default { attach(key id) { if (id != NULL_KEY) { llSay(0, \"Hello World\"); } } }");
121
122 InventoryItemBase userItem = UserInventoryHelpers.AddInventoryItem(scene, so, 0x100, 0x1000);
123
124 // FIXME: Right now, we have to do a tricksy chat listen to make sure we know when the script is running.
125 // In the future, we need to be able to do this programatically more predicably.
126 scene.EventManager.OnChatFromWorld += OnChatFromWorld;
127
128 SceneObjectGroup rezzedSo
129 = scene.AttachmentsModule.RezSingleAttachmentFromInventory(sp, userItem.ID, (uint)AttachmentPoint.Chest);
130 TaskInventoryItem rezzedScriptItem = rezzedSo.RootPart.Inventory.GetInventoryItem("scriptItem");
131
132 // Wait for chat to signal rezzed script has been started.
133 m_chatEvent.WaitOne(60000);
134
135 // Force save
136 xEngine.DoBackup(new Object[] { 0 });
137
138// Console.WriteLine("ItemID {0}", rezzedScriptItem.ItemID);
139//
140// foreach (
141// string s in Directory.EnumerateFileSystemEntries(
142// string.Format("ScriptEngines/{0}", scene.RegionInfo.RegionID)))
143// Console.WriteLine(s);
144
145 Assert.IsFalse(
146 File.Exists(
147 string.Format("ScriptEngines/{0}/{1}.state", scene.RegionInfo.RegionID, rezzedScriptItem.ItemID)));
148
149 scene.AttachmentsModule.DetachSingleAttachmentToInv(sp, rezzedSo);
150 }
151 }
152} \ No newline at end of file
diff --git a/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs b/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs
index 18569ca..78d4ee9 100644..100755
--- a/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs
+++ b/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs
@@ -42,22 +42,26 @@ using OpenMetaverse.StructuredData;
42using log4net; 42using log4net;
43using Nini.Config; 43using Nini.Config;
44using Amib.Threading; 44using Amib.Threading;
45using Mono.Addins;
45using OpenSim.Framework; 46using OpenSim.Framework;
46using OpenSim.Framework.Console; 47using OpenSim.Framework.Console;
47using OpenSim.Region.Framework.Scenes; 48using OpenSim.Region.Framework.Scenes;
48using OpenSim.Region.Framework.Interfaces; 49using OpenSim.Region.Framework.Interfaces;
50using OpenSim.Region.ScriptEngine.Interfaces;
49using OpenSim.Region.ScriptEngine.Shared; 51using OpenSim.Region.ScriptEngine.Shared;
50using OpenSim.Region.ScriptEngine.Shared.CodeTools; 52using OpenSim.Region.ScriptEngine.Shared.CodeTools;
51using OpenSim.Region.ScriptEngine.Shared.Instance; 53using OpenSim.Region.ScriptEngine.Shared.Instance;
52using OpenSim.Region.ScriptEngine.Shared.Api; 54using OpenSim.Region.ScriptEngine.Shared.Api;
53using OpenSim.Region.ScriptEngine.Shared.Api.Plugins; 55using OpenSim.Region.ScriptEngine.Shared.Api.Plugins;
54using OpenSim.Region.ScriptEngine.Interfaces; 56using OpenSim.Region.ScriptEngine.Shared.ScriptBase;
57using OpenSim.Region.ScriptEngine.XEngine.ScriptBase;
55using Timer = OpenSim.Region.ScriptEngine.Shared.Api.Plugins.Timer; 58using Timer = OpenSim.Region.ScriptEngine.Shared.Api.Plugins.Timer;
56 59
57using ScriptCompileQueue = OpenSim.Framework.LocklessQueue<object[]>; 60using ScriptCompileQueue = OpenSim.Framework.LocklessQueue<object[]>;
58 61
59namespace OpenSim.Region.ScriptEngine.XEngine 62namespace OpenSim.Region.ScriptEngine.XEngine
60{ 63{
64 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "XEngine")]
61 public class XEngine : INonSharedRegionModule, IScriptModule, IScriptEngine 65 public class XEngine : INonSharedRegionModule, IScriptModule, IScriptEngine
62 { 66 {
63 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 67 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
@@ -68,7 +72,13 @@ namespace OpenSim.Region.ScriptEngine.XEngine
68 /// <remarks> 72 /// <remarks>
69 /// If DebugLevel >= 1, then we log every time that a script is started. 73 /// If DebugLevel >= 1, then we log every time that a script is started.
70 /// </remarks> 74 /// </remarks>
71// public int DebugLevel { get; set; } 75 public int DebugLevel { get; set; }
76
77 /// <summary>
78 /// A parameter to allow us to notify the log if at least one script has a compilation that is not compatible
79 /// with ScriptStopStrategy.
80 /// </summary>
81 public bool HaveNotifiedLogOfScriptStopMistmatch { get; private set; }
72 82
73 private SmartThreadPool m_ThreadPool; 83 private SmartThreadPool m_ThreadPool;
74 private int m_MaxScriptQueue; 84 private int m_MaxScriptQueue;
@@ -84,6 +94,12 @@ namespace OpenSim.Region.ScriptEngine.XEngine
84 /// </summary> 94 /// </summary>
85 private int m_StartDelay; 95 private int m_StartDelay;
86 96
97 /// <summary>
98 /// Are we stopping scripts co-operatively by inserting checks in them at C# compile time (true) or aborting
99 /// their threads (false)?
100 /// </summary>
101 private bool m_coopTermination;
102
87 private int m_IdleTimeout; 103 private int m_IdleTimeout;
88 private int m_StackSize; 104 private int m_StackSize;
89 private int m_SleepTime; 105 private int m_SleepTime;
@@ -93,7 +109,6 @@ namespace OpenSim.Region.ScriptEngine.XEngine
93 private bool m_InitialStartup = true; 109 private bool m_InitialStartup = true;
94 private int m_ScriptFailCount; // Number of script fails since compile queue was last empty 110 private int m_ScriptFailCount; // Number of script fails since compile queue was last empty
95 private string m_ScriptErrorMessage; 111 private string m_ScriptErrorMessage;
96 private Dictionary<string, string> m_uniqueScripts = new Dictionary<string, string>();
97 private bool m_AppDomainLoading; 112 private bool m_AppDomainLoading;
98 private Dictionary<UUID,ArrayList> m_ScriptErrors = 113 private Dictionary<UUID,ArrayList> m_ScriptErrors =
99 new Dictionary<UUID,ArrayList>(); 114 new Dictionary<UUID,ArrayList>();
@@ -175,6 +190,14 @@ namespace OpenSim.Region.ScriptEngine.XEngine
175 get { return "XEngine"; } 190 get { return "XEngine"; }
176 } 191 }
177 192
193 public string ScriptClassName { get; private set; }
194
195 public string ScriptBaseClassName { get; private set; }
196
197 public ParameterInfo[] ScriptBaseClassParameters { get; private set; }
198
199 public string[] ScriptReferencedAssemblies { get; private set; }
200
178 public Scene World 201 public Scene World
179 { 202 {
180 get { return m_Scene; } 203 get { return m_Scene; }
@@ -229,21 +252,36 @@ namespace OpenSim.Region.ScriptEngine.XEngine
229 252
230 m_ScriptConfig = configSource.Configs["XEngine"]; 253 m_ScriptConfig = configSource.Configs["XEngine"];
231 m_ConfigSource = configSource; 254 m_ConfigSource = configSource;
255
256 string rawScriptStopStrategy = m_ScriptConfig.GetString("ScriptStopStrategy", "co-op");
257
258 m_log.InfoFormat("[XEngine]: Script stop strategy is {0}", rawScriptStopStrategy);
259
260 if (rawScriptStopStrategy == "co-op")
261 {
262 m_coopTermination = true;
263 ScriptClassName = "XEngineScript";
264 ScriptBaseClassName = typeof(XEngineScriptBase).FullName;
265 ScriptBaseClassParameters = typeof(XEngineScriptBase).GetConstructor(new Type[] { typeof(WaitHandle) }).GetParameters();
266 ScriptReferencedAssemblies = new string[] { Path.GetFileName(typeof(XEngineScriptBase).Assembly.Location) };
267 }
268 else
269 {
270 ScriptClassName = "Script";
271 ScriptBaseClassName = typeof(ScriptBaseClass).FullName;
272 }
273
274// Console.WriteLine("ASSEMBLY NAME: {0}", ScriptReferencedAssemblies[0]);
232 } 275 }
233 276
234 public void AddRegion(Scene scene) 277 public void AddRegion(Scene scene)
235 { 278 {
236 if (m_ScriptConfig == null) 279 if (m_ScriptConfig == null)
237 return; 280 return;
281
238 m_ScriptFailCount = 0; 282 m_ScriptFailCount = 0;
239 m_ScriptErrorMessage = String.Empty; 283 m_ScriptErrorMessage = String.Empty;
240 284
241 if (m_ScriptConfig == null)
242 {
243// m_log.ErrorFormat("[XEngine] No script configuration found. Scripts disabled");
244 return;
245 }
246
247 m_Enabled = m_ScriptConfig.GetBoolean("Enabled", true); 285 m_Enabled = m_ScriptConfig.GetBoolean("Enabled", true);
248 286
249 if (!m_Enabled) 287 if (!m_Enabled)
@@ -365,19 +403,19 @@ namespace OpenSim.Region.ScriptEngine.XEngine
365 (module, cmdparams) => HandleScriptsAction(cmdparams, HandleStartScript)); 403 (module, cmdparams) => HandleScriptsAction(cmdparams, HandleStartScript));
366 404
367 MainConsole.Instance.Commands.AddCommand( 405 MainConsole.Instance.Commands.AddCommand(
368 "Scripts", false, "debug scripts log", "debug scripts log <item-id> <log-level>", "Extra debug logging for a script", 406 "Debug", false, "debug scripts log", "debug scripts log <item-id> <log-level>", "Extra debug logging for a particular script.",
369 "Activates or deactivates extra debug logging for the given script.\n" 407 "Activates or deactivates extra debug logging for the given script.\n"
370 + "Level == 0, deactivate extra debug logging.\n" 408 + "Level == 0, deactivate extra debug logging.\n"
371 + "Level >= 1, log state changes.\n" 409 + "Level >= 1, log state changes.\n"
372 + "Level >= 2, log event invocations.\n", 410 + "Level >= 2, log event invocations.\n",
373 HandleDebugScriptLogCommand); 411 HandleDebugScriptLogCommand);
374 412
375// MainConsole.Instance.Commands.AddCommand( 413 MainConsole.Instance.Commands.AddCommand(
376// "Debug", false, "debug xengine", "debug xengine [<level>]", 414 "Debug", false, "debug xengine log", "debug xengine log [<level>]",
377// "Turn on detailed xengine debugging.", 415 "Turn on detailed xengine debugging.",
378// "If level <= 0, then no extra logging is done.\n" 416 "If level <= 0, then no extra logging is done.\n"
379// + "If level >= 1, then we log every time that a script is started.", 417 + "If level >= 1, then we log every time that a script is started.",
380// HandleDebugLevelCommand); 418 HandleDebugLevelCommand);
381 } 419 }
382 420
383 private void HandleDebugScriptLogCommand(string module, string[] args) 421 private void HandleDebugScriptLogCommand(string module, string[] args)
@@ -420,26 +458,26 @@ namespace OpenSim.Region.ScriptEngine.XEngine
420 /// </summary> 458 /// </summary>
421 /// <param name="module"></param> 459 /// <param name="module"></param>
422 /// <param name="args"></param> 460 /// <param name="args"></param>
423// private void HandleDebugLevelCommand(string module, string[] args) 461 private void HandleDebugLevelCommand(string module, string[] args)
424// { 462 {
425// if (args.Length == 3) 463 if (args.Length >= 4)
426// { 464 {
427// int newDebug; 465 int newDebug;
428// if (int.TryParse(args[2], out newDebug)) 466 if (ConsoleUtil.TryParseConsoleNaturalInt(MainConsole.Instance, args[3], out newDebug))
429// { 467 {
430// DebugLevel = newDebug; 468 DebugLevel = newDebug;
431// MainConsole.Instance.OutputFormat("Debug level set to {0}", newDebug); 469 MainConsole.Instance.OutputFormat("Debug level set to {0} in XEngine for region {1}", newDebug, m_Scene.Name);
432// } 470 }
433// } 471 }
434// else if (args.Length == 2) 472 else if (args.Length == 3)
435// { 473 {
436// MainConsole.Instance.OutputFormat("Current debug level is {0}", DebugLevel); 474 MainConsole.Instance.OutputFormat("Current debug level is {0}", DebugLevel);
437// } 475 }
438// else 476 else
439// { 477 {
440// MainConsole.Instance.Output("Usage: debug xengine 0..1"); 478 MainConsole.Instance.Output("Usage: debug xengine log <level>");
441// } 479 }
442// } 480 }
443 481
444 /// <summary> 482 /// <summary>
445 /// Parse the raw item id into a script instance from the command params if it's present. 483 /// Parse the raw item id into a script instance from the command params if it's present.
@@ -459,7 +497,7 @@ namespace OpenSim.Region.ScriptEngine.XEngine
459 /// <param name="instance"></param> 497 /// <param name="instance"></param>
460 /// <param name="keySelector">Basis on which to sort output. Can be null if no sort needs to take place</param> 498 /// <param name="keySelector">Basis on which to sort output. Can be null if no sort needs to take place</param>
461 private void HandleScriptsAction<TKey>( 499 private void HandleScriptsAction<TKey>(
462 string[] cmdparams, Action<IScriptInstance> action, Func<IScriptInstance, TKey> keySelector) 500 string[] cmdparams, Action<IScriptInstance> action, System.Func<IScriptInstance, TKey> keySelector)
463 { 501 {
464 if (!(MainConsole.Instance.ConsoleScene == null || MainConsole.Instance.ConsoleScene == m_Scene)) 502 if (!(MainConsole.Instance.ConsoleScene == null || MainConsole.Instance.ConsoleScene == m_Scene))
465 return; 503 return;
@@ -517,7 +555,7 @@ namespace OpenSim.Region.ScriptEngine.XEngine
517 if (!(MainConsole.Instance.ConsoleScene == null || MainConsole.Instance.ConsoleScene == m_Scene)) 555 if (!(MainConsole.Instance.ConsoleScene == null || MainConsole.Instance.ConsoleScene == m_Scene))
518 return; 556 return;
519 557
520 MainConsole.Instance.OutputFormat(GetStatusReport()); 558 MainConsole.Instance.Output(GetStatusReport());
521 } 559 }
522 560
523 public string GetStatusReport() 561 public string GetStatusReport()
@@ -539,7 +577,6 @@ namespace OpenSim.Region.ScriptEngine.XEngine
539 } 577 }
540 578
541 sb.AppendFormat("Scripts loaded : {0}\n", scriptsLoaded); 579 sb.AppendFormat("Scripts loaded : {0}\n", scriptsLoaded);
542 sb.AppendFormat("Unique scripts : {0}\n", m_uniqueScripts.Count);
543 sb.AppendFormat("Scripts waiting for load : {0}\n", m_CompileQueue.Count); 580 sb.AppendFormat("Scripts waiting for load : {0}\n", m_CompileQueue.Count);
544 sb.AppendFormat("Max threads : {0}\n", m_ThreadPool.MaxThreads); 581 sb.AppendFormat("Max threads : {0}\n", m_ThreadPool.MaxThreads);
545 sb.AppendFormat("Min threads : {0}\n", m_ThreadPool.MinThreads); 582 sb.AppendFormat("Min threads : {0}\n", m_ThreadPool.MinThreads);
@@ -616,7 +653,7 @@ namespace OpenSim.Region.ScriptEngine.XEngine
616 sb.AppendFormat("Containing part UUID: {0}\n", instance.ObjectID); 653 sb.AppendFormat("Containing part UUID: {0}\n", instance.ObjectID);
617 sb.AppendFormat("Position : {0}\n", sop.AbsolutePosition); 654 sb.AppendFormat("Position : {0}\n", sop.AbsolutePosition);
618 655
619 MainConsole.Instance.OutputFormat(sb.ToString()); 656 MainConsole.Instance.Output(sb.ToString());
620 } 657 }
621 658
622 private void HandleSuspendScript(IScriptInstance instance) 659 private void HandleSuspendScript(IScriptInstance instance)
@@ -662,6 +699,8 @@ namespace OpenSim.Region.ScriptEngine.XEngine
662 { 699 {
663 if (instance.Running) 700 if (instance.Running)
664 { 701 {
702 instance.StayStopped = true; // the script was stopped explicitly
703
665 instance.Stop(0); 704 instance.Stop(0);
666 705
667 SceneObjectPart sop = m_Scene.GetSceneObjectPart(instance.ObjectID); 706 SceneObjectPart sop = m_Scene.GetSceneObjectPart(instance.ObjectID);
@@ -685,28 +724,23 @@ namespace OpenSim.Region.ScriptEngine.XEngine
685 { 724 {
686 // Force a final state save 725 // Force a final state save
687 // 726 //
688 if (m_Assemblies.ContainsKey(instance.AssetID)) 727 try
689 { 728 {
690 string assembly = m_Assemblies[instance.AssetID]; 729 if (instance.StatePersistedHere)
691 730 instance.SaveState();
692 try 731 }
693 { 732 catch (Exception e)
694 instance.SaveState(assembly); 733 {
695 } 734 m_log.Error(
696 catch (Exception e) 735 string.Format(
697 { 736 "[XEngine]: Failed final state save for script {0}.{1}, item UUID {2}, prim UUID {3} in {4}. Exception ",
698 m_log.Error( 737 instance.PrimName, instance.ScriptName, instance.ItemID, instance.ObjectID, World.Name)
699 string.Format( 738 , e);
700 "[XEngine]: Failed final state save for script {0}.{1}, item UUID {2}, prim UUID {3} in {4}. Exception ",
701 instance.PrimName, instance.ScriptName, instance.ItemID, instance.ObjectID, World.Name)
702 , e);
703 }
704 } 739 }
705 740
706 // Clear the event queue and abort the instance thread 741 // Clear the event queue and abort the instance thread
707 // 742 //
708 instance.ClearQueue(); 743 instance.Stop(0, true);
709 instance.Stop(0);
710 744
711 // Release events, timer, etc 745 // Release events, timer, etc
712 // 746 //
@@ -804,23 +838,23 @@ namespace OpenSim.Region.ScriptEngine.XEngine
804 lock (m_Scripts) 838 lock (m_Scripts)
805 { 839 {
806 foreach (IScriptInstance instance in m_Scripts.Values) 840 foreach (IScriptInstance instance in m_Scripts.Values)
807 instances.Add(instance); 841 {
842 if (instance.StatePersistedHere)
843 {
844// m_log.DebugFormat(
845// "[XEngine]: Adding script {0}.{1}, item UUID {2}, prim UUID {3} in {4} for state persistence",
846// instance.PrimName, instance.ScriptName, instance.ItemID, instance.ObjectID, World.Name);
847
848 instances.Add(instance);
849 }
850 }
808 } 851 }
809 852
810 foreach (IScriptInstance i in instances) 853 foreach (IScriptInstance i in instances)
811 { 854 {
812 string assembly = String.Empty;
813
814 lock (m_Scripts)
815 {
816 if (!m_Assemblies.ContainsKey(i.AssetID))
817 continue;
818 assembly = m_Assemblies[i.AssetID];
819 }
820
821 try 855 try
822 { 856 {
823 i.SaveState(assembly); 857 i.SaveState();
824 } 858 }
825 catch (Exception e) 859 catch (Exception e)
826 { 860 {
@@ -832,8 +866,6 @@ namespace OpenSim.Region.ScriptEngine.XEngine
832 } 866 }
833 } 867 }
834 868
835 instances.Clear();
836
837 if (saveTime > 0) 869 if (saveTime > 0)
838 m_ThreadPool.QueueWorkItem(new WorkItemCallback(this.DoBackup), 870 m_ThreadPool.QueueWorkItem(new WorkItemCallback(this.DoBackup),
839 new Object[] { saveTime }); 871 new Object[] { saveTime });
@@ -843,6 +875,14 @@ namespace OpenSim.Region.ScriptEngine.XEngine
843 875
844 public void SaveAllState() 876 public void SaveAllState()
845 { 877 {
878 DoBackup(new object[] { 0 });
879 }
880
881 public object DoMaintenance(object p)
882 {
883 object[] parms = (object[])p;
884 int sleepTime = (int)parms[0];
885
846 foreach (IScriptInstance inst in m_Scripts.Values) 886 foreach (IScriptInstance inst in m_Scripts.Values)
847 { 887 {
848 if (inst.EventTime() > m_EventLimit) 888 if (inst.EventTime() > m_EventLimit)
@@ -852,14 +892,6 @@ namespace OpenSim.Region.ScriptEngine.XEngine
852 inst.Start(); 892 inst.Start();
853 } 893 }
854 } 894 }
855 }
856
857 public object DoMaintenance(object p)
858 {
859 object[] parms = (object[])p;
860 int sleepTime = (int)parms[0];
861
862 SaveAllState();
863 895
864 System.Threading.Thread.Sleep(sleepTime); 896 System.Threading.Thread.Sleep(sleepTime);
865 897
@@ -937,8 +969,6 @@ namespace OpenSim.Region.ScriptEngine.XEngine
937 if (restOfFirstLine.StartsWith("c#") 969 if (restOfFirstLine.StartsWith("c#")
938 || restOfFirstLine.StartsWith("vb") 970 || restOfFirstLine.StartsWith("vb")
939 || restOfFirstLine.StartsWith("lsl") 971 || restOfFirstLine.StartsWith("lsl")
940 || restOfFirstLine.StartsWith("js")
941 || restOfFirstLine.StartsWith("yp")
942 || restOfFirstLine.Length == 0) 972 || restOfFirstLine.Length == 0)
943 warnRunningInXEngine = true; 973 warnRunningInXEngine = true;
944 974
@@ -972,12 +1002,6 @@ namespace OpenSim.Region.ScriptEngine.XEngine
972 if (engine != ScriptEngineName) 1002 if (engine != ScriptEngineName)
973 return; 1003 return;
974 1004
975 // If we've seen this exact script text before, use that reference instead
976 if (m_uniqueScripts.ContainsKey(script))
977 script = m_uniqueScripts[script];
978 else
979 m_uniqueScripts[script] = script;
980
981 Object[] parms = new Object[]{localID, itemID, script, startParam, postOnRez, (StateSource)stateSource}; 1005 Object[] parms = new Object[]{localID, itemID, script, startParam, postOnRez, (StateSource)stateSource};
982 1006
983 if (stateSource == (int)StateSource.ScriptedRez) 1007 if (stateSource == (int)StateSource.ScriptedRez)
@@ -991,11 +1015,12 @@ namespace OpenSim.Region.ScriptEngine.XEngine
991 } 1015 }
992 else 1016 else
993 { 1017 {
994 m_CompileQueue.Enqueue(parms);
995 lock (m_CompileDict) 1018 lock (m_CompileDict)
996 {
997 m_CompileDict[itemID] = 0; 1019 m_CompileDict[itemID] = 0;
998 } 1020
1021 // This must occur after the m_CompileDict so that an existing compile thread cannot hit the check
1022 // in DoOnRezScript() before m_CompileDict has been updated.
1023 m_CompileQueue.Enqueue(parms);
999 1024
1000// m_log.DebugFormat("[XEngine]: Added script {0} to compile queue", itemID); 1025// m_log.DebugFormat("[XEngine]: Added script {0} to compile queue", itemID);
1001 1026
@@ -1017,49 +1042,81 @@ namespace OpenSim.Region.ScriptEngine.XEngine
1017 1042
1018 public Object DoOnRezScriptQueue(Object dummy) 1043 public Object DoOnRezScriptQueue(Object dummy)
1019 { 1044 {
1020 if (m_InitialStartup) 1045 try
1021 { 1046 {
1022 // This delay exists to stop mono problems where script compilation and startup would stop the sim 1047 if (m_InitialStartup)
1023 // working properly for the session. 1048 {
1024 System.Threading.Thread.Sleep(m_StartDelay); 1049 // This delay exists to stop mono problems where script compilation and startup would stop the sim
1050 // working properly for the session.
1051 System.Threading.Thread.Sleep(m_StartDelay);
1025 1052
1026 m_log.InfoFormat("[XEngine]: Performing initial script startup on {0}", m_Scene.Name); 1053 m_log.InfoFormat("[XEngine]: Performing initial script startup on {0}", m_Scene.Name);
1027 } 1054 }
1028 1055
1029 object[] o; 1056 object[] o;
1030 1057
1031 int scriptsStarted = 0; 1058 int scriptsStarted = 0;
1032 1059
1033 while (m_CompileQueue.Dequeue(out o)) 1060 while (m_CompileQueue.Dequeue(out o))
1034 {
1035 if (DoOnRezScript(o))
1036 { 1061 {
1037 scriptsStarted++; 1062 try
1063 {
1064 if (DoOnRezScript(o))
1065 {
1066 scriptsStarted++;
1038 1067
1039 if (m_InitialStartup) 1068 if (m_InitialStartup)
1040 if (scriptsStarted % 50 == 0) 1069 if (scriptsStarted % 50 == 0)
1041 m_log.InfoFormat( 1070 m_log.InfoFormat(
1042 "[XEngine]: Started {0} scripts in {1}", scriptsStarted, m_Scene.Name); 1071 "[XEngine]: Started {0} scripts in {1}", scriptsStarted, m_Scene.Name);
1072 }
1073 }
1074 catch (Exception e)
1075 {
1076 m_log.Error(
1077 string.Format(
1078 "[XEngine]: Failure in DoOnRezScriptQueue() for item {0} in {1}. Continuing. Exception ",
1079 o[1], m_Scene.Name),
1080 e);
1081 }
1043 } 1082 }
1044 }
1045 1083
1046 if (m_InitialStartup) 1084 if (m_InitialStartup)
1047 m_log.InfoFormat( 1085 m_log.InfoFormat(
1048 "[XEngine]: Completed starting {0} scripts on {1}", scriptsStarted, m_Scene.Name); 1086 "[XEngine]: Completed starting {0} scripts on {1}", scriptsStarted, m_Scene.Name);
1049 1087
1050 // NOTE: Despite having a lockless queue, this lock is required 1088 }
1051 // to make sure there is never no compile thread while there 1089 catch (Exception e)
1052 // are still scripts to compile. This could otherwise happen 1090 {
1053 // due to a race condition 1091 m_log.Error(
1054 // 1092 string.Format("[XEngine]: Failure in DoOnRezScriptQueue() in {0}. Exception ", m_Scene.Name), e);
1055 lock (m_CompileQueue) 1093 }
1056 m_CurrentCompile = null; 1094 finally
1095 {
1096 // FIXME: On failure we must trigger this even if the compile queue is not actually empty so that the
1097 // RegionReadyModule is not forever waiting. This event really needs a different name.
1098 m_Scene.EventManager.TriggerEmptyScriptCompileQueue(m_ScriptFailCount,
1099 m_ScriptErrorMessage);
1057 1100
1058 m_Scene.EventManager.TriggerEmptyScriptCompileQueue(m_ScriptFailCount, 1101 m_ScriptFailCount = 0;
1059 m_ScriptErrorMessage); 1102 m_InitialStartup = false;
1060 1103
1061 m_ScriptFailCount = 0; 1104 // NOTE: Despite having a lockless queue, this lock is required
1062 m_InitialStartup = false; 1105 // to make sure there is never no compile thread while there
1106 // are still scripts to compile. This could otherwise happen
1107 // due to a race condition
1108 //
1109 lock (m_CompileQueue)
1110 {
1111 m_CurrentCompile = null;
1112
1113 // This is to avoid a situation where the m_CompileQueue while loop above could complete but
1114 // OnRezScript() place a new script on the queue and check m_CurrentCompile = null before we hit
1115 // this section.
1116 if (m_CompileQueue.Count > 0)
1117 m_CurrentCompile = m_ThreadPool.QueueWorkItem(DoOnRezScriptQueue, null);
1118 }
1119 }
1063 1120
1064 return null; 1121 return null;
1065 } 1122 }
@@ -1106,16 +1163,17 @@ namespace OpenSim.Region.ScriptEngine.XEngine
1106 return false; 1163 return false;
1107 } 1164 }
1108 1165
1109 m_log.DebugFormat( 1166 if (DebugLevel > 0)
1110 "[XEngine] Loading script {0}.{1}, item UUID {2}, prim UUID {3} @ {4}.{5}", 1167 m_log.DebugFormat(
1111 part.ParentGroup.RootPart.Name, item.Name, itemID, part.UUID, 1168 "[XEngine]: Loading script {0}.{1}, item UUID {2}, prim UUID {3} @ {4}.{5}",
1112 part.ParentGroup.RootPart.AbsolutePosition, part.ParentGroup.Scene.RegionInfo.RegionName); 1169 part.ParentGroup.RootPart.Name, item.Name, itemID, part.UUID,
1170 part.ParentGroup.RootPart.AbsolutePosition, part.ParentGroup.Scene.RegionInfo.RegionName);
1113 1171
1114 UUID assetID = item.AssetID; 1172 UUID assetID = item.AssetID;
1115 1173
1116 ScenePresence presence = m_Scene.GetScenePresence(item.OwnerID); 1174 ScenePresence presence = m_Scene.GetScenePresence(item.OwnerID);
1117 1175
1118 string assembly = ""; 1176 string assemblyPath = "";
1119 1177
1120 Culture.SetCurrentCulture(); 1178 Culture.SetCurrentCulture();
1121 1179
@@ -1127,11 +1185,16 @@ namespace OpenSim.Region.ScriptEngine.XEngine
1127 { 1185 {
1128 lock (m_AddingAssemblies) 1186 lock (m_AddingAssemblies)
1129 { 1187 {
1130 m_Compiler.PerformScriptCompile(script, assetID.ToString(), item.OwnerID, out assembly, out linemap); 1188 m_Compiler.PerformScriptCompile(script, assetID.ToString(), item.OwnerID, out assemblyPath, out linemap);
1131 if (!m_AddingAssemblies.ContainsKey(assembly)) { 1189
1132 m_AddingAssemblies[assembly] = 1; 1190// m_log.DebugFormat(
1191// "[XENGINE]: Found assembly path {0} onrez {1} in {2}",
1192// assemblyPath, item.ItemID, World.Name);
1193
1194 if (!m_AddingAssemblies.ContainsKey(assemblyPath)) {
1195 m_AddingAssemblies[assemblyPath] = 1;
1133 } else { 1196 } else {
1134 m_AddingAssemblies[assembly]++; 1197 m_AddingAssemblies[assemblyPath]++;
1135 } 1198 }
1136 } 1199 }
1137 1200
@@ -1177,7 +1240,9 @@ namespace OpenSim.Region.ScriptEngine.XEngine
1177 } 1240 }
1178 catch (Exception e) 1241 catch (Exception e)
1179 { 1242 {
1180// m_log.ErrorFormat("[XEngine]: Exception when rezzing script {0}{1}", e.Message, e.StackTrace); 1243// m_log.ErrorFormat(
1244// "[XEngine]: Exception when rezzing script with item ID {0}, {1}{2}",
1245// itemID, e.Message, e.StackTrace);
1181 1246
1182 // try 1247 // try
1183 // { 1248 // {
@@ -1274,19 +1339,144 @@ namespace OpenSim.Region.ScriptEngine.XEngine
1274 m_ScriptFailCount++; 1339 m_ScriptFailCount++;
1275 lock (m_AddingAssemblies) 1340 lock (m_AddingAssemblies)
1276 { 1341 {
1277 m_AddingAssemblies[assembly]--; 1342 m_AddingAssemblies[assemblyPath]--;
1278 } 1343 }
1279 return false; 1344 return false;
1280 } 1345 }
1281 } 1346 }
1282 m_DomainScripts[appDomain].Add(itemID); 1347 m_DomainScripts[appDomain].Add(itemID);
1283 1348
1349 IScript scriptObj = null;
1350 EventWaitHandle coopSleepHandle;
1351 bool coopTerminationForThisScript;
1352
1353 // Set up assembly name to point to the appropriate scriptEngines directory
1354 AssemblyName assemblyName = new AssemblyName(Path.GetFileNameWithoutExtension(assemblyPath));
1355 assemblyName.CodeBase = Path.GetDirectoryName(assemblyPath);
1356
1357 if (m_coopTermination)
1358 {
1359 try
1360 {
1361 coopSleepHandle = new XEngineEventWaitHandle(false, EventResetMode.AutoReset);
1362
1363 scriptObj
1364 = (IScript)m_AppDomains[appDomain].CreateInstanceAndUnwrap(
1365 assemblyName.FullName,
1366 "SecondLife.XEngineScript",
1367 false,
1368 BindingFlags.Default,
1369 null,
1370 new object[] { coopSleepHandle },
1371 null,
1372 null);
1373
1374 coopTerminationForThisScript = true;
1375 }
1376 catch (TypeLoadException)
1377 {
1378 coopSleepHandle = null;
1379
1380 try
1381 {
1382 scriptObj
1383 = (IScript)m_AppDomains[appDomain].CreateInstanceAndUnwrap(
1384 assemblyName.FullName,
1385 "SecondLife.Script",
1386 false,
1387 BindingFlags.Default,
1388 null,
1389 null,
1390 null,
1391 null);
1392 }
1393 catch (Exception e2)
1394 {
1395 m_log.Error(
1396 string.Format(
1397 "[XENGINE]: Could not load previous SecondLife.Script from assembly {0} in {1}. Not starting. Exception ",
1398 assemblyName.FullName, World.Name),
1399 e2);
1400
1401 return false;
1402 }
1403
1404 coopTerminationForThisScript = false;
1405 }
1406 }
1407 else
1408 {
1409 try
1410 {
1411 scriptObj
1412 = (IScript)m_AppDomains[appDomain].CreateInstanceAndUnwrap(
1413 assemblyName.FullName,
1414 "SecondLife.Script",
1415 false,
1416 BindingFlags.Default,
1417 null,
1418 null,
1419 null,
1420 null);
1421
1422 coopSleepHandle = null;
1423 coopTerminationForThisScript = false;
1424 }
1425 catch (TypeLoadException)
1426 {
1427 coopSleepHandle = new XEngineEventWaitHandle(false, EventResetMode.AutoReset);
1428
1429 try
1430 {
1431 scriptObj
1432 = (IScript)m_AppDomains[appDomain].CreateInstanceAndUnwrap(
1433 assemblyName.FullName,
1434 "SecondLife.XEngineScript",
1435 false,
1436 BindingFlags.Default,
1437 null,
1438 new object[] { coopSleepHandle },
1439 null,
1440 null);
1441 }
1442 catch (Exception e2)
1443 {
1444 m_log.Error(
1445 string.Format(
1446 "[XENGINE]: Could not load previous SecondLife.XEngineScript from assembly {0} in {1}. Not starting. Exception ",
1447 assemblyName.FullName, World.Name),
1448 e2);
1449
1450 return false;
1451 }
1452
1453 coopTerminationForThisScript = true;
1454 }
1455 }
1456
1457 if (m_coopTermination != coopTerminationForThisScript && !HaveNotifiedLogOfScriptStopMistmatch)
1458 {
1459 // Notify the log that there is at least one script compile that doesn't match the
1460 // ScriptStopStrategy. Operator has to manually delete old DLLs - we can't do this on Windows
1461 // once the assembly has been loaded evne if the instantiation of a class was unsuccessful.
1462 m_log.WarnFormat(
1463 "[XEngine]: At least one existing compiled script DLL in {0} has {1} as ScriptStopStrategy whereas config setting is {2}."
1464 + "\nContinuing with script compiled strategy but to remove this message please set [XEngine] DeleteScriptsOnStartup = true for one simulator session to remove old script DLLs (script state will not be lost).",
1465 World.Name, coopTerminationForThisScript ? "co-op" : "abort", m_coopTermination ? "co-op" : "abort");
1466
1467 HaveNotifiedLogOfScriptStopMistmatch = true;
1468 }
1469
1284 instance = new ScriptInstance(this, part, 1470 instance = new ScriptInstance(this, part,
1285 itemID, assetID, assembly, 1471 item,
1286 m_AppDomains[appDomain], 1472 startParam, postOnRez,
1287 part.ParentGroup.RootPart.Name, 1473 m_MaxScriptQueue);
1288 item.Name, startParam, postOnRez, 1474
1289 stateSource, m_MaxScriptQueue); 1475 if (
1476 !instance.Load(
1477 scriptObj, coopSleepHandle, assemblyPath,
1478 Path.Combine(ScriptEnginePath, World.RegionInfo.RegionID.ToString()), stateSource, coopTerminationForThisScript))
1479 return false;
1290 1480
1291// if (DebugLevel >= 1) 1481// if (DebugLevel >= 1)
1292// m_log.DebugFormat( 1482// m_log.DebugFormat(
@@ -1317,11 +1507,11 @@ namespace OpenSim.Region.ScriptEngine.XEngine
1317 } 1507 }
1318 1508
1319 if (!m_Assemblies.ContainsKey(assetID)) 1509 if (!m_Assemblies.ContainsKey(assetID))
1320 m_Assemblies[assetID] = assembly; 1510 m_Assemblies[assetID] = assemblyPath;
1321 1511
1322 lock (m_AddingAssemblies) 1512 lock (m_AddingAssemblies)
1323 { 1513 {
1324 m_AddingAssemblies[assembly]--; 1514 m_AddingAssemblies[assemblyPath]--;
1325 } 1515 }
1326 1516
1327 if (instance != null) 1517 if (instance != null)
@@ -1359,11 +1549,7 @@ namespace OpenSim.Region.ScriptEngine.XEngine
1359 m_Scripts.Remove(itemID); 1549 m_Scripts.Remove(itemID);
1360 } 1550 }
1361 1551
1362 instance.ClearQueue(); 1552 instance.Stop(m_WaitForEventCompletionOnScriptStop, true);
1363
1364 instance.Stop(m_WaitForEventCompletionOnScriptStop);
1365
1366// bool objectRemoved = false;
1367 1553
1368 lock (m_PrimObjects) 1554 lock (m_PrimObjects)
1369 { 1555 {
@@ -1376,14 +1562,13 @@ namespace OpenSim.Region.ScriptEngine.XEngine
1376 1562
1377 // If there are no more scripts, remove prim 1563 // If there are no more scripts, remove prim
1378 if (m_PrimObjects[localID].Count == 0) 1564 if (m_PrimObjects[localID].Count == 0)
1379 {
1380 m_PrimObjects.Remove(localID); 1565 m_PrimObjects.Remove(localID);
1381// objectRemoved = true;
1382 }
1383 } 1566 }
1384 } 1567 }
1385 1568
1386 instance.RemoveState(); 1569 if (instance.StatePersistedHere)
1570 instance.RemoveState();
1571
1387 instance.DestroyScriptInstance(); 1572 instance.DestroyScriptInstance();
1388 1573
1389 m_DomainScripts[instance.AppDomain].Remove(instance.ItemID); 1574 m_DomainScripts[instance.AppDomain].Remove(instance.ItemID);
@@ -1489,7 +1674,7 @@ namespace OpenSim.Region.ScriptEngine.XEngine
1489 startInfo.MaxWorkerThreads = maxThreads; 1674 startInfo.MaxWorkerThreads = maxThreads;
1490 startInfo.MinWorkerThreads = minThreads; 1675 startInfo.MinWorkerThreads = minThreads;
1491 startInfo.ThreadPriority = threadPriority;; 1676 startInfo.ThreadPriority = threadPriority;;
1492 startInfo.StackSize = stackSize; 1677 startInfo.MaxStackSize = stackSize;
1493 startInfo.StartSuspended = true; 1678 startInfo.StartSuspended = true;
1494 1679
1495 m_ThreadPool = new SmartThreadPool(startInfo); 1680 m_ThreadPool = new SmartThreadPool(startInfo);
@@ -1516,7 +1701,7 @@ namespace OpenSim.Region.ScriptEngine.XEngine
1516 1701
1517 IScriptInstance instance = (ScriptInstance) parms; 1702 IScriptInstance instance = (ScriptInstance) parms;
1518 1703
1519 //m_log.DebugFormat("[XEngine]: Processing event for {0}", instance); 1704// m_log.DebugFormat("[XEngine]: Processing event for {0}", instance);
1520 1705
1521 return instance.EventProcessor(); 1706 return instance.EventProcessor();
1522 } 1707 }
@@ -1681,9 +1866,7 @@ namespace OpenSim.Region.ScriptEngine.XEngine
1681 public bool GetScriptState(UUID itemID) 1866 public bool GetScriptState(UUID itemID)
1682 { 1867 {
1683 IScriptInstance instance = GetInstance(itemID); 1868 IScriptInstance instance = GetInstance(itemID);
1684 if (instance != null) 1869 return instance != null && instance.Running;
1685 return instance.Running;
1686 return false;
1687 } 1870 }
1688 1871
1689 public void ApiResetScript(UUID itemID) 1872 public void ApiResetScript(UUID itemID)
@@ -1691,6 +1874,12 @@ namespace OpenSim.Region.ScriptEngine.XEngine
1691 IScriptInstance instance = GetInstance(itemID); 1874 IScriptInstance instance = GetInstance(itemID);
1692 if (instance != null) 1875 if (instance != null)
1693 instance.ApiResetScript(); 1876 instance.ApiResetScript();
1877
1878 // Send the new number of threads that are in use by the thread
1879 // pool, I believe that by adding them to the locations where the
1880 // script is changing states that I will catch all changes to the
1881 // thread pool
1882 m_Scene.setThreadCount(m_ThreadPool.InUseThreads);
1694 } 1883 }
1695 1884
1696 public void ResetScript(UUID itemID) 1885 public void ResetScript(UUID itemID)
@@ -1698,6 +1887,12 @@ namespace OpenSim.Region.ScriptEngine.XEngine
1698 IScriptInstance instance = GetInstance(itemID); 1887 IScriptInstance instance = GetInstance(itemID);
1699 if (instance != null) 1888 if (instance != null)
1700 instance.ResetScript(m_WaitForEventCompletionOnScriptStop); 1889 instance.ResetScript(m_WaitForEventCompletionOnScriptStop);
1890
1891 // Send the new number of threads that are in use by the thread
1892 // pool, I believe that by adding them to the locations where the
1893 // script is changing states that I will catch all changes to the
1894 // thread pool
1895 m_Scene.setThreadCount(m_ThreadPool.InUseThreads);
1701 } 1896 }
1702 1897
1703 public void StartScript(UUID itemID) 1898 public void StartScript(UUID itemID)
@@ -1707,6 +1902,12 @@ namespace OpenSim.Region.ScriptEngine.XEngine
1707 instance.Start(); 1902 instance.Start();
1708 else 1903 else
1709 m_runFlags.AddOrUpdate(itemID, true, 240); 1904 m_runFlags.AddOrUpdate(itemID, true, 240);
1905
1906 // Send the new number of threads that are in use by the thread
1907 // pool, I believe that by adding them to the locations where the
1908 // script is changing states that I will catch all changes to the
1909 // thread pool
1910 m_Scene.setThreadCount(m_ThreadPool.InUseThreads);
1710 } 1911 }
1711 1912
1712 public void StopScript(UUID itemID) 1913 public void StopScript(UUID itemID)
@@ -1714,17 +1915,29 @@ namespace OpenSim.Region.ScriptEngine.XEngine
1714 IScriptInstance instance = GetInstance(itemID); 1915 IScriptInstance instance = GetInstance(itemID);
1715 1916
1716 if (instance != null) 1917 if (instance != null)
1918 {
1919 lock (instance.EventQueue)
1920 instance.StayStopped = true; // the script was stopped explicitly
1921
1717 instance.Stop(m_WaitForEventCompletionOnScriptStop); 1922 instance.Stop(m_WaitForEventCompletionOnScriptStop);
1923 }
1718 else 1924 else
1925 {
1926// m_log.DebugFormat("[XENGINE]: Could not find script with ID {0} to stop in {1}", itemID, World.Name);
1719 m_runFlags.AddOrUpdate(itemID, false, 240); 1927 m_runFlags.AddOrUpdate(itemID, false, 240);
1928 }
1929
1930 // Send the new number of threads that are in use by the thread
1931 // pool, I believe that by adding them to the locations where the
1932 // script is changing states that I will catch all changes to the
1933 // thread pool
1934 m_Scene.setThreadCount(m_ThreadPool.InUseThreads);
1720 } 1935 }
1721 1936
1722 public DetectParams GetDetectParams(UUID itemID, int idx) 1937 public DetectParams GetDetectParams(UUID itemID, int idx)
1723 { 1938 {
1724 IScriptInstance instance = GetInstance(itemID); 1939 IScriptInstance instance = GetInstance(itemID);
1725 if (instance != null) 1940 return instance != null ? instance.GetDetectParams(idx) : null;
1726 return instance.GetDetectParams(idx);
1727 return null;
1728 } 1941 }
1729 1942
1730 public void SetMinEventDelay(UUID itemID, double delay) 1943 public void SetMinEventDelay(UUID itemID, double delay)
@@ -1737,9 +1950,7 @@ namespace OpenSim.Region.ScriptEngine.XEngine
1737 public UUID GetDetectID(UUID itemID, int idx) 1950 public UUID GetDetectID(UUID itemID, int idx)
1738 { 1951 {
1739 IScriptInstance instance = GetInstance(itemID); 1952 IScriptInstance instance = GetInstance(itemID);
1740 if (instance != null) 1953 return instance != null ? instance.GetDetectID(idx) : UUID.Zero;
1741 return instance.GetDetectID(idx);
1742 return UUID.Zero;
1743 } 1954 }
1744 1955
1745 public void SetState(UUID itemID, string newState) 1956 public void SetState(UUID itemID, string newState)
@@ -1753,9 +1964,7 @@ namespace OpenSim.Region.ScriptEngine.XEngine
1753 public int GetStartParameter(UUID itemID) 1964 public int GetStartParameter(UUID itemID)
1754 { 1965 {
1755 IScriptInstance instance = GetInstance(itemID); 1966 IScriptInstance instance = GetInstance(itemID);
1756 if (instance == null) 1967 return instance == null ? 0 : instance.StartParam;
1757 return 0;
1758 return instance.StartParam;
1759 } 1968 }
1760 1969
1761 public void OnShutdown() 1970 public void OnShutdown()
@@ -1789,9 +1998,7 @@ namespace OpenSim.Region.ScriptEngine.XEngine
1789 public IScriptApi GetApi(UUID itemID, string name) 1998 public IScriptApi GetApi(UUID itemID, string name)
1790 { 1999 {
1791 IScriptInstance instance = GetInstance(itemID); 2000 IScriptInstance instance = GetInstance(itemID);
1792 if (instance == null) 2001 return instance == null ? null : instance.GetApi(name);
1793 return null;
1794 return instance.GetApi(name);
1795 } 2002 }
1796 2003
1797 public void OnGetScriptRunning(IClientAPI controllingClient, UUID objectID, UUID itemID) 2004 public void OnGetScriptRunning(IClientAPI controllingClient, UUID objectID, UUID itemID)
@@ -2090,7 +2297,8 @@ namespace OpenSim.Region.ScriptEngine.XEngine
2090 catch (IOException ex) 2297 catch (IOException ex)
2091 { 2298 {
2092 // if there already exists a file at that location, it may be locked. 2299 // if there already exists a file at that location, it may be locked.
2093 m_log.ErrorFormat("[XEngine]: Linemap file {0} already exists! {1}", mappath, ex.Message); 2300 m_log.Error(
2301 string.Format("[XEngine]: Linemap file {0} could not be written. Exception ", mappath), ex);
2094 } 2302 }
2095 } 2303 }
2096 } 2304 }
@@ -2116,6 +2324,9 @@ namespace OpenSim.Region.ScriptEngine.XEngine
2116 m_log.ErrorFormat("[XEngine]: Error whilst writing state file {0}, {1}", statepath, ex.Message); 2324 m_log.ErrorFormat("[XEngine]: Error whilst writing state file {0}, {1}", statepath, ex.Message);
2117 } 2325 }
2118 2326
2327// m_log.DebugFormat(
2328// "[XEngine]: Wrote state for script item with ID {0} at {1} in {2}", itemID, statepath, m_Scene.Name);
2329
2119 return true; 2330 return true;
2120 } 2331 }
2121 2332
@@ -2137,7 +2348,6 @@ namespace OpenSim.Region.ScriptEngine.XEngine
2137 2348
2138 public Dictionary<uint, float> GetObjectScriptsExecutionTimes() 2349 public Dictionary<uint, float> GetObjectScriptsExecutionTimes()
2139 { 2350 {
2140 long tickNow = Util.EnvironmentTickCount();
2141 Dictionary<uint, float> topScripts = new Dictionary<uint, float>(); 2351 Dictionary<uint, float> topScripts = new Dictionary<uint, float>();
2142 2352
2143 lock (m_Scripts) 2353 lock (m_Scripts)
@@ -2147,7 +2357,7 @@ namespace OpenSim.Region.ScriptEngine.XEngine
2147 if (!topScripts.ContainsKey(si.LocalID)) 2357 if (!topScripts.ContainsKey(si.LocalID))
2148 topScripts[si.RootLocalID] = 0; 2358 topScripts[si.RootLocalID] = 0;
2149 2359
2150 topScripts[si.RootLocalID] += CalculateAdjustedExectionTime(si, tickNow); 2360 topScripts[si.RootLocalID] += GetExectionTime(si);
2151 } 2361 }
2152 } 2362 }
2153 2363
@@ -2161,7 +2371,6 @@ namespace OpenSim.Region.ScriptEngine.XEngine
2161 return 0.0f; 2371 return 0.0f;
2162 } 2372 }
2163 float time = 0.0f; 2373 float time = 0.0f;
2164 long tickNow = Util.EnvironmentTickCount();
2165 IScriptInstance si; 2374 IScriptInstance si;
2166 // Calculate the time for all scripts that this engine is executing 2375 // Calculate the time for all scripts that this engine is executing
2167 // Ignore any others 2376 // Ignore any others
@@ -2170,36 +2379,15 @@ namespace OpenSim.Region.ScriptEngine.XEngine
2170 si = GetInstance(id); 2379 si = GetInstance(id);
2171 if (si != null && si.Running) 2380 if (si != null && si.Running)
2172 { 2381 {
2173 time += CalculateAdjustedExectionTime(si, tickNow); 2382 time += GetExectionTime(si);
2174 } 2383 }
2175 } 2384 }
2176 return time; 2385 return time;
2177 } 2386 }
2178 2387
2179 private float CalculateAdjustedExectionTime(IScriptInstance si, long tickNow) 2388 private float GetExectionTime(IScriptInstance si)
2180 { 2389 {
2181 long ticksElapsed = tickNow - si.MeasurementPeriodTickStart; 2390 return (float)si.ExecutionTime.GetSumTime().TotalMilliseconds;
2182
2183 // Avoid divide by zero
2184 if (ticksElapsed == 0)
2185 ticksElapsed = 1;
2186
2187 // Scale execution time to the ideal 55 fps frame time for these reasons.
2188 //
2189 // 1) XEngine does not execute scripts per frame, unlike other script engines. Hence, there is no
2190 // 'script execution time per frame', which is the original purpose of this value.
2191 //
2192 // 2) Giving the raw execution times is misleading since scripts start at different times, making
2193 // it impossible to compare scripts.
2194 //
2195 // 3) Scaling the raw execution time to the time that the script has been running is better but
2196 // is still misleading since a script that has just been rezzed may appear to have been running
2197 // for much longer.
2198 //
2199 // 4) Hence, we scale execution time to an idealised frame time (55 fps). This is also not perfect
2200 // since the figure does not represent actual execution time and very hard running scripts will
2201 // never exceed 18ms (though this is a very high number for script execution so is a warning sign).
2202 return ((float)si.MeasurementPeriodExecutionTime / ticksElapsed) * 18.1818f;
2203 } 2391 }
2204 2392
2205 public void SuspendScript(UUID itemID) 2393 public void SuspendScript(UUID itemID)
@@ -2211,6 +2399,12 @@ namespace OpenSim.Region.ScriptEngine.XEngine
2211 instance.Suspend(); 2399 instance.Suspend();
2212// else 2400// else
2213// m_log.DebugFormat("[XEngine]: Could not find script with ID {0} to resume", itemID); 2401// m_log.DebugFormat("[XEngine]: Could not find script with ID {0} to resume", itemID);
2402
2403 // Send the new number of threads that are in use by the thread
2404 // pool, I believe that by adding them to the locations where the
2405 // script is changing states that I will catch all changes to the
2406 // thread pool
2407 m_Scene.setThreadCount(m_ThreadPool.InUseThreads);
2214 } 2408 }
2215 2409
2216 public void ResumeScript(UUID itemID) 2410 public void ResumeScript(UUID itemID)
@@ -2222,6 +2416,12 @@ namespace OpenSim.Region.ScriptEngine.XEngine
2222 instance.Resume(); 2416 instance.Resume();
2223// else 2417// else
2224// m_log.DebugFormat("[XEngine]: Could not find script with ID {0} to resume", itemID); 2418// m_log.DebugFormat("[XEngine]: Could not find script with ID {0} to resume", itemID);
2419
2420 // Send the new number of threads that are in use by the thread
2421 // pool, I believe that by adding them to the locations where the
2422 // script is changing states that I will catch all changes to the
2423 // thread pool
2424 m_Scene.setThreadCount(m_ThreadPool.InUseThreads);
2225 } 2425 }
2226 2426
2227 public bool HasScript(UUID itemID, out bool running) 2427 public bool HasScript(UUID itemID, out bool running)
@@ -2235,5 +2435,30 @@ namespace OpenSim.Region.ScriptEngine.XEngine
2235 running = instance.Running; 2435 running = instance.Running;
2236 return true; 2436 return true;
2237 } 2437 }
2438
2439 public void SleepScript(UUID itemID, int delay)
2440 {
2441 IScriptInstance instance = GetInstance(itemID);
2442 if (instance == null)
2443 return;
2444
2445 instance.ExecutionTimer.Stop();
2446 try
2447 {
2448 if (instance.CoopWaitHandle != null)
2449 {
2450 if (instance.CoopWaitHandle.WaitOne(delay))
2451 throw new ScriptCoopStopException();
2452 }
2453 else
2454 {
2455 Thread.Sleep(delay);
2456 }
2457 }
2458 finally
2459 {
2460 instance.ExecutionTimer.Start();
2461 }
2462 }
2238 } 2463 }
2239} 2464}
diff --git a/OpenSim/Region/ScriptEngine/XEngine/XWorkItem.cs b/OpenSim/Region/ScriptEngine/XEngine/XWorkItem.cs
index 2ac5c31..9d9dee1 100644
--- a/OpenSim/Region/ScriptEngine/XEngine/XWorkItem.cs
+++ b/OpenSim/Region/ScriptEngine/XEngine/XWorkItem.cs
@@ -52,13 +52,17 @@ namespace OpenSim.Region.ScriptEngine.XEngine
52 return wr.Cancel(); 52 return wr.Cancel();
53 } 53 }
54 54
55 public void Abort() 55 public bool Abort()
56 { 56 {
57 wr.Abort(); 57 return wr.Cancel(true);
58 } 58 }
59 59
60 public bool Wait(TimeSpan t) 60 public bool Wait(int t)
61 { 61 {
62 // We use the integer version of WaitAll because the current version of SmartThreadPool has a bug with the
63 // TimeSpan version. The number of milliseconds in TimeSpan is an int64 so when STP casts it down to an
64 // int (32-bit) we can end up with bad values. This occurs on Windows though curiously not on Mono 2.10.8
65 // (or very likely other versions of Mono at least up until 3.0.3).
62 return SmartThreadPool.WaitAll(new IWorkItemResult[] {wr}, t, false); 66 return SmartThreadPool.WaitAll(new IWorkItemResult[] {wr}, t, false);
63 } 67 }
64 } 68 }