diff --git a/OpenSim/Framework/ISceneObject.cs b/OpenSim/Framework/ISceneObject.cs
index afac9b84f2..754b77b4a2 100644
--- a/OpenSim/Framework/ISceneObject.cs
+++ b/OpenSim/Framework/ISceneObject.cs
@@ -32,6 +32,8 @@ namespace OpenSim.Framework
{
public interface ISceneObject
{
+ string Name { get; }
+
UUID UUID { get; }
///
diff --git a/OpenSim/Region/CoreModules/Avatar/Attachments/AttachmentsModule.cs b/OpenSim/Region/CoreModules/Avatar/Attachments/AttachmentsModule.cs
index a3368fbb1c..ff7a062c9c 100644
--- a/OpenSim/Region/CoreModules/Avatar/Attachments/AttachmentsModule.cs
+++ b/OpenSim/Region/CoreModules/Avatar/Attachments/AttachmentsModule.cs
@@ -255,6 +255,8 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
public void CopyAttachments(AgentData ad, IScenePresence sp)
{
+// m_log.DebugFormat("[ATTACHMENTS MODULE]: Copying attachment data into {0} in {1}", sp.Name, m_scene.Name);
+
if (ad.AttachmentObjects != null && ad.AttachmentObjects.Count > 0)
{
lock (sp.AttachmentsSyncLock)
@@ -265,6 +267,11 @@ namespace OpenSim.Region.CoreModules.Avatar.Attachments
{
((SceneObjectGroup)so).LocalId = 0;
((SceneObjectGroup)so).RootPart.ClearUpdateSchedule();
+
+// m_log.DebugFormat(
+// "[ATTACHMENTS MODULE]: Copying script state with {0} bytes for object {1} for {2} in {3}",
+// ad.AttachmentObjectStates[i].Length, so.Name, sp.Name, m_scene.Name);
+
so.SetState(ad.AttachmentObjectStates[i++], m_scene);
m_scene.IncomingCreateObject(Vector3.Zero, so);
}
diff --git a/OpenSim/Region/ScriptEngine/Interfaces/IScriptInstance.cs b/OpenSim/Region/ScriptEngine/Interfaces/IScriptInstance.cs
index b9a217b649..1097efb856 100644
--- a/OpenSim/Region/ScriptEngine/Interfaces/IScriptInstance.cs
+++ b/OpenSim/Region/ScriptEngine/Interfaces/IScriptInstance.cs
@@ -222,7 +222,7 @@ namespace OpenSim.Region.ScriptEngine.Interfaces
void SetVars(Dictionary vars);
DetectParams GetDetectParams(int idx);
UUID GetDetectID(int idx);
- void SaveState(string assembly);
+ void SaveState();
void DestroyScriptInstance();
IScriptApi GetApi(string name);
diff --git a/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs b/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs
index 637c5562ba..b983be93b6 100644
--- a/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs
+++ b/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs
@@ -71,7 +71,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
private bool m_TimerQueued;
private DateTime m_EventStart;
private bool m_InEvent;
- private string m_Assembly;
+ private string m_assemblyPath;
+ private string m_dataPath;
private string m_CurrentEvent = String.Empty;
private bool m_InSelfDelete;
private int m_MaxScriptQueue;
@@ -244,12 +245,16 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
///
///
///
+ ///
+ /// Path for all script associated data (state, etc.). In a multi-region set up
+ /// with all scripts loading into the same AppDomain this may not be the same place as the DLL itself.
+ ///
///
/// false if load failed, true if suceeded
- public bool Load(AppDomain dom, Assembly scriptAssembly, StateSource stateSource)
+ public bool Load(AppDomain dom, Assembly scriptAssembly, string dataPath, StateSource stateSource)
{
- //m_Assembly = scriptAssembly.CodeBase;
- m_Assembly = scriptAssembly.Location;
+ m_assemblyPath = scriptAssembly.Location;
+ m_dataPath = dataPath;
m_stateSource = stateSource;
try
@@ -270,14 +275,10 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
constructorParams = null;
}
-// m_log.DebugFormat(
-// "[SCRIP
-// scriptType.FullName, m_Assembly, Engine.World.Name);
-
if (dom != System.AppDomain.CurrentDomain)
m_Script
= (IScript)dom.CreateInstanceAndUnwrap(
- Path.GetFileNameWithoutExtension(m_Assembly),
+ Path.GetFileNameWithoutExtension(m_assemblyPath),
scriptType.FullName,
false,
BindingFlags.Default,
@@ -305,7 +306,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
{
m_log.ErrorFormat(
"[SCRIPT INSTANCE]: Not starting script {0} (id {1}) in part {2} (id {3}) in object {4} in {5}. Error loading assembly {6}. Exception {7}{8}",
- ScriptTask.Name, ScriptTask.ItemID, Part.Name, Part.UUID, Part.ParentGroup.Name, Engine.World.Name, m_Assembly, e.Message, e.StackTrace);
+ ScriptTask.Name, ScriptTask.ItemID, Part.Name, Part.UUID, Part.ParentGroup.Name, Engine.World.Name, scriptAssembly.Location, e.Message, e.StackTrace);
return false;
}
@@ -340,10 +341,14 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
m_SaveState = true;
- string savedState = Path.Combine(Path.GetDirectoryName(m_Assembly), ItemID.ToString() + ".state");
+ string savedState = Path.Combine(m_dataPath, ItemID.ToString() + ".state");
if (File.Exists(savedState))
{
+// m_log.DebugFormat(
+// "[SCRIPT INSTANCE]: Found state for script {0} for {1} ({2}) at {3} in {4}",
+// ItemID, savedState, Part.Name, Part.ParentGroup.Name, Part.ParentGroup.Scene.Name);
+
string xml = String.Empty;
try
@@ -385,12 +390,12 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
m_startedFromSavedState = true;
}
}
- else
- {
- m_log.WarnFormat(
- "[SCRIPT INSTANCE]: Not starting script {0} (id {1}) in part {2} (id {3}) in object {4} in {5}. Unable to load script state file {6}. Memory limit exceeded.",
- ScriptTask.Name, ScriptTask.ItemID, Part.Name, Part.UUID, Part.ParentGroup.Name, Engine.World.Name, savedState);
- }
+// else
+// {
+// m_log.WarnFormat(
+// "[SCRIPT INSTANCE]: Not starting script {0} (id {1}) in part {2} (id {3}) in object {4} in {5}. Unable to load script state file {6}. Memory limit exceeded.",
+// ScriptTask.Name, ScriptTask.ItemID, Part.Name, Part.UUID, Part.ParentGroup.Name, Engine.World.Name, savedState);
+// }
}
catch (Exception e)
{
@@ -401,11 +406,9 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
}
// else
// {
-// ScenePresence presence = Engine.World.GetScenePresence(part.OwnerID);
-
-// if (presence != null && (!postOnRez))
-// presence.ControllingClient.SendAgentAlertMessage("Compile successful", false);
-
+// m_log.DebugFormat(
+// "[SCRIPT INSTANCE]: Did not find state for script {0} for {1} ({2}) at {3} in {4}",
+// ItemID, savedState, Part.Name, Part.ParentGroup.Name, Part.ParentGroup.Scene.Name);
// }
return true;
@@ -498,8 +501,11 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
public void RemoveState()
{
- string savedState = Path.Combine(Path.GetDirectoryName(m_Assembly),
- ItemID.ToString() + ".state");
+ string savedState = Path.Combine(m_dataPath, ItemID.ToString() + ".state");
+
+// m_log.DebugFormat(
+// "[SCRIPT INSTANCE]: Deleting state {0} for script {1} (id {2}) in part {3} (id {4}) in object {5} in {6}.",
+// savedState, ScriptTask.Name, ScriptTask.ItemID, Part.Name, Part.UUID, Part.ParentGroup.Name, Engine.World.Name);
try
{
@@ -822,8 +828,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
if (Engine.World.PipeEventsForScript(LocalID) ||
data.EventName == "control") // Don't freeze avies!
{
- // m_log.DebugFormat("[Script] Delivered event {2} in state {3} to {0}.{1}",
- // PrimName, ScriptName, data.EventName, State);
+// m_log.DebugFormat("[Script] Delivered event {2} in state {3} to {0}.{1}",
+// PrimName, ScriptName, data.EventName, State);
try
{
@@ -849,7 +855,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
// This will be the very first event we deliver
// (state_entry) in default state
//
- SaveState(m_Assembly);
+ SaveState();
m_SaveState = false;
}
@@ -1043,7 +1049,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
return m_DetectParams[idx].Key;
}
- public void SaveState(string assembly)
+ public void SaveState()
{
// If we're currently in an event, just tell it to save upon return
//
@@ -1065,7 +1071,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
{
try
{
- using (FileStream fs = File.Create(Path.Combine(Path.GetDirectoryName(assembly), ItemID.ToString() + ".state")))
+ using (FileStream fs = File.Create(Path.Combine(m_dataPath, ItemID.ToString() + ".state")))
{
Byte[] buf = Util.UTF8NoBomEncoding.GetBytes(xml);
fs.Write(buf, 0, buf.Length);
@@ -1149,7 +1155,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
public string GetAssemblyName()
{
- return m_Assembly;
+ return m_assemblyPath;
}
public string GetXMLState()
diff --git a/OpenSim/Region/ScriptEngine/XEngine/Tests/XEngineBasicTests.cs b/OpenSim/Region/ScriptEngine/XEngine/Tests/XEngineBasicTests.cs
index fe15157af5..878e571617 100644
--- a/OpenSim/Region/ScriptEngine/XEngine/Tests/XEngineBasicTests.cs
+++ b/OpenSim/Region/ScriptEngine/XEngine/Tests/XEngineBasicTests.cs
@@ -40,10 +40,10 @@ using OpenSim.Tests.Common;
namespace OpenSim.Region.ScriptEngine.XEngine.Tests
{
///
- /// XEngine tests.
+ /// Basic XEngine tests.
///
[TestFixture]
- public class XEngineTest : OpenSimTestCase
+ public class XEngineBasicTests : OpenSimTestCase
{
private TestScene m_scene;
private XEngine m_xEngine;
diff --git a/OpenSim/Region/ScriptEngine/XEngine/Tests/XEngineCrossingTests.cs b/OpenSim/Region/ScriptEngine/XEngine/Tests/XEngineCrossingTests.cs
new file mode 100644
index 0000000000..587695fc1a
--- /dev/null
+++ b/OpenSim/Region/ScriptEngine/XEngine/Tests/XEngineCrossingTests.cs
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) Contributors, http://opensimulator.org/
+ * See CONTRIBUTORS.TXT for a full list of copyright holders.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the OpenSimulator Project nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+using System;
+using System.Threading;
+using Nini.Config;
+using NUnit.Framework;
+using OpenMetaverse;
+using OpenSim.Framework;
+using OpenSim.Region.CoreModules.Framework.EntityTransfer;
+using OpenSim.Region.CoreModules.ServiceConnectorsOut.Simulation;
+using OpenSim.Region.Framework.Scenes;
+using OpenSim.Region.ScriptEngine.Shared;
+using OpenSim.Tests.Common;
+
+namespace OpenSim.Region.ScriptEngine.XEngine.Tests
+{
+ ///
+ /// XEngine tests connected with crossing scripts between regions.
+ ///
+ [TestFixture]
+ public class XEngineCrossingTests : OpenSimTestCase
+ {
+ [TestFixtureSetUp]
+ public void FixtureInit()
+ {
+ // Don't allow tests to be bamboozled by asynchronous events. Execute everything on the same thread.
+ Util.FireAndForgetMethod = FireAndForgetMethod.RegressionTest;
+ }
+
+ [TestFixtureTearDown]
+ public void TearDown()
+ {
+ // We must set this back afterwards, otherwise later tests will fail since they're expecting multiple
+ // threads. Possibly, later tests should be rewritten so none of them require async stuff (which regression
+ // tests really shouldn't).
+ Util.FireAndForgetMethod = Util.DefaultFireAndForgetMethod;
+ }
+
+ ///
+ /// Test script state preservation when a script crosses between regions on the same simulator.
+ ///
+ [Test]
+ public void TestScriptCrossOnSameSimulator()
+ {
+ TestHelpers.InMethod();
+// TestHelpers.EnableLogging();
+
+ UUID userId = TestHelpers.ParseTail(0x1);
+ int sceneObjectIdTail = 0x2;
+
+ EntityTransferModule etmA = new EntityTransferModule();
+ EntityTransferModule etmB = new EntityTransferModule();
+ LocalSimulationConnectorModule lscm = new LocalSimulationConnectorModule();
+ XEngine xEngineA = new XEngine();
+ XEngine xEngineB = new XEngine();
+ xEngineA.DebugLevel = 1;
+ xEngineB.DebugLevel = 1;
+
+ IConfigSource configSource = new IniConfigSource();
+
+ IConfig startupConfig = configSource.AddConfig("Startup");
+ startupConfig.Set("DefaultScriptEngine", "XEngine");
+
+ IConfig xEngineConfig = configSource.AddConfig("XEngine");
+ xEngineConfig.Set("Enabled", "true");
+ xEngineConfig.Set("StartDelay", "0");
+
+ // These tests will not run with AppDomainLoading = true, at least on mono. For unknown reasons, the call
+ // to AssemblyResolver.OnAssemblyResolve fails.
+ xEngineConfig.Set("AppDomainLoading", "false");
+
+ IConfig modulesConfig = configSource.AddConfig("Modules");
+ modulesConfig.Set("EntityTransferModule", etmA.Name);
+ modulesConfig.Set("SimulationServices", lscm.Name);
+
+ SceneHelpers sh = new SceneHelpers();
+ TestScene sceneA = sh.SetupScene("sceneA", TestHelpers.ParseTail(0x100), 1000, 1000, configSource);
+ TestScene sceneB = sh.SetupScene("sceneB", TestHelpers.ParseTail(0x200), 1000, 999, configSource);
+
+ SceneHelpers.SetupSceneModules(new Scene[] { sceneA, sceneB }, configSource, lscm);
+ SceneHelpers.SetupSceneModules(sceneA, configSource, etmA, xEngineA);
+ SceneHelpers.SetupSceneModules(sceneB, configSource, etmB, xEngineB);
+ sceneA.StartScripts();
+ sceneB.StartScripts();
+
+ SceneObjectGroup soSceneA = SceneHelpers.AddSceneObject(sceneA, 1, userId, "so1-", sceneObjectIdTail);
+ soSceneA.AbsolutePosition = new Vector3(128, 10, 20);
+
+ // CREATE SCRIPT TODO
+ InventoryItemBase scriptItemSceneA = new InventoryItemBase();
+ // itemTemplate.ID = itemId;
+ scriptItemSceneA.Name = "script1";
+ scriptItemSceneA.Folder = soSceneA.UUID;
+ scriptItemSceneA.InvType = (int)InventoryType.LSL;
+
+ AutoResetEvent chatEvent = new AutoResetEvent(false);
+ OSChatMessage messageReceived = null;
+ sceneA.EventManager.OnChatFromWorld += (s, m) => { messageReceived = m; chatEvent.Set(); };
+
+ sceneA.RezNewScript(userId, scriptItemSceneA,
+@"integer c = 0;
+
+default
+{
+ state_entry()
+ {
+ llSay(0, ""Script running"");
+ }
+
+ changed(integer change)
+ {
+ llSay(0, ""Changed"");
+ }
+
+ touch_start(integer n)
+ {
+ c = c + 1;
+ llSay(0, (string)c);
+ }
+}");
+
+ chatEvent.WaitOne(60000);
+
+ Assert.That(messageReceived, Is.Not.Null, "No chat message received.");
+ Assert.That(messageReceived.Message, Is.EqualTo("Script running"));
+
+ {
+ // XXX: Should not be doing this so directly. Should call some variant of EventManager.touch() instead.
+ DetectParams[] det = new DetectParams[1];
+ det[0] = new DetectParams();
+ det[0].Key = userId;
+ det[0].Populate(sceneA);
+
+ EventParams ep = new EventParams("touch_start", new Object[] { new LSL_Types.LSLInteger(1) }, det);
+
+ xEngineA.PostObjectEvent(soSceneA.LocalId, ep);
+ chatEvent.WaitOne(60000);
+
+ Assert.That(messageReceived.Message, Is.EqualTo("1"));
+ }
+
+ sceneB.EventManager.OnChatFromWorld += (s, m) => { messageReceived = m; chatEvent.Set(); };
+
+ // Cross with a negative value
+ soSceneA.AbsolutePosition = new Vector3(128, -10, 20);
+
+ chatEvent.WaitOne(60000);
+ Assert.That(messageReceived.Message, Is.EqualTo("Changed"));
+
+ // TEST sending event to moved prim and output
+ {
+ SceneObjectGroup soSceneB = sceneB.GetSceneObjectGroup(soSceneA.Name);
+ TaskInventoryItem scriptItemSceneB = soSceneB.RootPart.Inventory.GetInventoryItem(scriptItemSceneA.Name);
+
+ // XXX: Should not be doing this so directly. Should call some variant of EventManager.touch() instead.
+ DetectParams[] det = new DetectParams[1];
+ det[0] = new DetectParams();
+ det[0].Key = userId;
+ det[0].Populate(sceneB);
+
+ EventParams ep = new EventParams("touch_start", new Object[] { new LSL_Types.LSLInteger(1) }, det);
+
+ xEngineB.PostObjectEvent(soSceneB.LocalId, ep);
+ chatEvent.WaitOne(60000);
+
+ Assert.That(messageReceived.Message, Is.EqualTo("2"));
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs b/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs
index ccd5d990fd..71ed9891c4 100644
--- a/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs
+++ b/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs
@@ -716,22 +716,17 @@ namespace OpenSim.Region.ScriptEngine.XEngine
{
// Force a final state save
//
- if (m_Assemblies.ContainsKey(instance.AssetID))
+ try
{
- string assembly = m_Assemblies[instance.AssetID];
-
- try
- {
- instance.SaveState(assembly);
- }
- catch (Exception e)
- {
- m_log.Error(
- string.Format(
- "[XEngine]: Failed final state save for script {0}.{1}, item UUID {2}, prim UUID {3} in {4}. Exception ",
- instance.PrimName, instance.ScriptName, instance.ItemID, instance.ObjectID, World.Name)
- , e);
- }
+ instance.SaveState();
+ }
+ catch (Exception e)
+ {
+ m_log.Error(
+ string.Format(
+ "[XEngine]: Failed final state save for script {0}.{1}, item UUID {2}, prim UUID {3} in {4}. Exception ",
+ instance.PrimName, instance.ScriptName, instance.ItemID, instance.ObjectID, World.Name)
+ , e);
}
// Clear the event queue and abort the instance thread
@@ -840,18 +835,9 @@ namespace OpenSim.Region.ScriptEngine.XEngine
foreach (IScriptInstance i in instances)
{
- string assembly = String.Empty;
-
- lock (m_Scripts)
- {
- if (!m_Assemblies.ContainsKey(i.AssetID))
- continue;
- assembly = m_Assemblies[i.AssetID];
- }
-
try
{
- i.SaveState(assembly);
+ i.SaveState();
}
catch (Exception e)
{
@@ -1180,7 +1166,11 @@ namespace OpenSim.Region.ScriptEngine.XEngine
lock (m_AddingAssemblies)
{
m_Compiler.PerformScriptCompile(script, assetID.ToString(), item.OwnerID, out assemblyPath, out linemap);
-
+
+// m_log.DebugFormat(
+// "[XENGINE]: Found assembly path {0} onrez {1} in {2}",
+// assemblyPath, item.ItemID, World.Name);
+
if (!m_AddingAssemblies.ContainsKey(assemblyPath)) {
m_AddingAssemblies[assemblyPath] = 1;
} else {
@@ -1373,7 +1363,9 @@ namespace OpenSim.Region.ScriptEngine.XEngine
startParam, postOnRez,
m_MaxScriptQueue);
- if (!instance.Load(m_AppDomains[appDomain], scriptAssembly, stateSource))
+ if (!instance.Load(
+ m_AppDomains[appDomain], scriptAssembly,
+ Path.Combine(ScriptEnginePath, World.RegionInfo.RegionID.ToString()), stateSource))
return false;
// if (DebugLevel >= 1)
@@ -1604,7 +1596,7 @@ namespace OpenSim.Region.ScriptEngine.XEngine
IScriptInstance instance = (ScriptInstance) parms;
- //m_log.DebugFormat("[XEngine]: Processing event for {0}", instance);
+// m_log.DebugFormat("[XEngine]: Processing event for {0}", instance);
return instance.EventProcessor();
}
@@ -2200,6 +2192,9 @@ namespace OpenSim.Region.ScriptEngine.XEngine
m_log.ErrorFormat("[XEngine]: Error whilst writing state file {0}, {1}", statepath, ex.Message);
}
+// m_log.DebugFormat(
+// "[XEngine]: Wrote state for script item with ID {0} at {1} in {2}", itemID, statepath, m_Scene.Name);
+
return true;
}