diff --git a/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs b/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs
index 437d150097..202d1993f8 100644
--- a/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs
+++ b/OpenSim/ApplicationPlugins/RemoteController/RemoteAdminPlugin.cs
@@ -1493,6 +1493,8 @@ namespace OpenSim.ApplicationPlugins.RemoteController
/// profile url
/// - noassets
/// true if no assets should be saved
+ /// - all
+ /// true to save all the regions in the simulator
/// - perm
/// C and/or T
///
@@ -1549,6 +1551,11 @@ namespace OpenSim.ApplicationPlugins.RemoteController
options["checkPermissions"] = (string)requestData["perm"];
}
+ if ((string)requestData["all"] == "true")
+ {
+ options["all"] = (string)requestData["all"];
+ }
+
IRegionArchiverModule archiver = scene.RequestModuleInterface();
if (archiver != null)
diff --git a/OpenSim/Framework/RegionInfo.cs b/OpenSim/Framework/RegionInfo.cs
index da87b05508..fc64ff9cee 100644
--- a/OpenSim/Framework/RegionInfo.cs
+++ b/OpenSim/Framework/RegionInfo.cs
@@ -706,27 +706,27 @@ namespace OpenSim.Framework
config.Set("ExternalHostName", m_externalHostName);
- if (m_nonphysPrimMin != 0)
+ if (m_nonphysPrimMin > 0)
config.Set("NonphysicalPrimMax", m_nonphysPrimMin);
- if (m_nonphysPrimMax != 0)
+ if (m_nonphysPrimMax > 0)
config.Set("NonphysicalPrimMax", m_nonphysPrimMax);
- if (m_physPrimMin != 0)
+ if (m_physPrimMin > 0)
config.Set("PhysicalPrimMax", m_physPrimMin);
- if (m_physPrimMax != 0)
+ if (m_physPrimMax > 0)
config.Set("PhysicalPrimMax", m_physPrimMax);
config.Set("ClampPrimSize", m_clampPrimSize.ToString());
- if (m_objectCapacity != 0)
+ if (m_objectCapacity > 0)
config.Set("MaxPrims", m_objectCapacity);
- if (m_linksetCapacity != 0)
+ if (m_linksetCapacity > 0)
config.Set("LinksetPrims", m_linksetCapacity);
- if (m_agentCapacity != 0)
+ if (m_agentCapacity > 0)
config.Set("MaxAgents", m_agentCapacity);
if (ScopeID != UUID.Zero)
diff --git a/OpenSim/Framework/Serialization/ArchiveConstants.cs b/OpenSim/Framework/Serialization/ArchiveConstants.cs
index 2c5e0018ec..48f1c4f94f 100644
--- a/OpenSim/Framework/Serialization/ArchiveConstants.cs
+++ b/OpenSim/Framework/Serialization/ArchiveConstants.cs
@@ -52,6 +52,11 @@ namespace OpenSim.Framework.Serialization
///
public const string INVENTORY_PATH = "inventory/";
+ ///
+ /// Path for regions in a multi-region archive
+ ///
+ public const string REGIONS_PATH = "regions/";
+
///
/// Path for the prims file
///
diff --git a/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs b/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs
index 7384e399ce..44c3411752 100644
--- a/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs
+++ b/OpenSim/Framework/Servers/HttpServer/BaseHttpServer.cs
@@ -449,9 +449,7 @@ namespace OpenSim.Framework.Servers.HttpServer
if (TryGetStreamHandler(handlerKey, out requestHandler))
{
if (DebugLevel >= 3)
- m_log.DebugFormat(
- "[BASE HTTP SERVER]: Found stream handler for {0} {1} {2} {3}",
- request.HttpMethod, request.Url.PathAndQuery, requestHandler.Name, requestHandler.Description);
+ LogIncomingToStreamHandler(request, requestHandler);
response.ContentType = requestHandler.ContentType; // Lets do this defaulting before in case handler has varying content type.
@@ -563,9 +561,7 @@ namespace OpenSim.Framework.Servers.HttpServer
if (DoWeHaveALLSDHandler(request.RawUrl))
{
if (DebugLevel >= 3)
- m_log.DebugFormat(
- "[BASE HTTP SERVER]: Found a {0} content type handler for {1} {2}",
- request.ContentType, request.HttpMethod, request.Url.PathAndQuery);
+ LogIncomingToContentTypeHandler(request);
buffer = HandleLLSDRequests(request, response);
}
@@ -573,18 +569,14 @@ namespace OpenSim.Framework.Servers.HttpServer
else if (DoWeHaveAHTTPHandler(request.RawUrl))
{
if (DebugLevel >= 3)
- m_log.DebugFormat(
- "[BASE HTTP SERVER]: Found a {0} content type handler for {1} {2}",
- request.ContentType, request.HttpMethod, request.Url.PathAndQuery);
+ LogIncomingToContentTypeHandler(request);
buffer = HandleHTTPRequest(request, response);
}
else
{
if (DebugLevel >= 3)
- m_log.DebugFormat(
- "[BASE HTTP SERVER]: Assuming a generic XMLRPC request for {0} {1}",
- request.HttpMethod, request.Url.PathAndQuery);
+ LogIncomingToXmlRpcHandler(request);
// generic login request.
buffer = HandleXmlRpcRequests(request, response);
@@ -654,6 +646,58 @@ namespace OpenSim.Framework.Servers.HttpServer
}
}
+ private void LogIncomingToStreamHandler(OSHttpRequest request, IRequestHandler requestHandler)
+ {
+ m_log.DebugFormat(
+ "[BASE HTTP SERVER]: Found stream handler for {0} {1} {2} {3}",
+ request.HttpMethod, request.Url.PathAndQuery, requestHandler.Name, requestHandler.Description);
+
+ if (DebugLevel >= 4)
+ LogIncomingInDetail(request);
+ }
+
+ private void LogIncomingToContentTypeHandler(OSHttpRequest request)
+ {
+ m_log.DebugFormat(
+ "[BASE HTTP SERVER]: Found a {0} content type handler for {1} {2}",
+ request.ContentType, request.HttpMethod, request.Url.PathAndQuery);
+
+ if (DebugLevel >= 4)
+ LogIncomingInDetail(request);
+ }
+
+ private void LogIncomingToXmlRpcHandler(OSHttpRequest request)
+ {
+ m_log.DebugFormat(
+ "[BASE HTTP SERVER]: Assuming a generic XMLRPC request for {0} {1}",
+ request.HttpMethod, request.Url.PathAndQuery);
+
+ if (DebugLevel >= 4)
+ LogIncomingInDetail(request);
+ }
+
+ private void LogIncomingInDetail(OSHttpRequest request)
+ {
+ using (StreamReader reader = new StreamReader(Util.Copy(request.InputStream), Encoding.UTF8))
+ {
+ string output;
+
+ if (DebugLevel == 4)
+ {
+ const int sampleLength = 80;
+ char[] sampleChars = new char[sampleLength];
+ reader.Read(sampleChars, 0, sampleLength);
+ output = string.Format("[BASE HTTP SERVER]: {0}...", new string(sampleChars).Replace("\n", @"\n"));
+ }
+ else
+ {
+ output = string.Format("[BASE HTTP SERVER]: {0}", reader.ReadToEnd());
+ }
+
+ m_log.Debug(output);
+ }
+ }
+
private bool TryGetStreamHandler(string handlerKey, out IRequestHandler streamHandler)
{
string bestMatch = null;
diff --git a/OpenSim/Framework/Servers/MainServer.cs b/OpenSim/Framework/Servers/MainServer.cs
index 8dc0e3a71d..7402c735c1 100644
--- a/OpenSim/Framework/Servers/MainServer.cs
+++ b/OpenSim/Framework/Servers/MainServer.cs
@@ -29,6 +29,7 @@ using System;
using System.Collections.Generic;
using System.Reflection;
using System.Net;
+using System.Text;
using log4net;
using OpenSim.Framework;
using OpenSim.Framework.Console;
@@ -104,6 +105,11 @@ namespace OpenSim.Framework.Servers
public static void RegisterHttpConsoleCommands(ICommandConsole console)
{
+ console.Commands.AddCommand(
+ "Comms", false, "show http-handlers",
+ "show http-handlers",
+ "Show all registered http handlers", HandleShowHttpHandlersCommand);
+
console.Commands.AddCommand(
"Debug", false, "debug http", "debug http []",
"Turn on inbound non-poll http request debugging.",
@@ -111,6 +117,8 @@ namespace OpenSim.Framework.Servers
+ "If level >= 1, then short warnings are logged when receiving bad input data.\n"
+ "If level >= 2, then long warnings are logged when receiving bad input data.\n"
+ "If level >= 3, then short notices about all incoming non-poll HTTP requests are logged.\n"
+ + "If level >= 4, then a sample from the beginning of the incoming data is logged.\n"
+ + "If level >= 5, then the entire incoming data is logged.\n"
+ "If no level is specified then the current level is returned.",
HandleDebugHttpCommand);
}
@@ -136,10 +144,55 @@ namespace OpenSim.Framework.Servers
}
else
{
- MainConsole.Instance.Output("Usage: debug http 0..3");
+ MainConsole.Instance.Output("Usage: debug http 0..5");
}
}
+ private static void HandleShowHttpHandlersCommand(string module, string[] args)
+ {
+ if (args.Length != 2)
+ {
+ MainConsole.Instance.Output("Usage: show http-handlers");
+ return;
+ }
+
+ StringBuilder handlers = new StringBuilder();
+
+ lock (m_Servers)
+ {
+ foreach (BaseHttpServer httpServer in m_Servers.Values)
+ {
+ handlers.AppendFormat(
+ "Registered HTTP Handlers for server at {0}:{1}\n", httpServer.ListenIPAddress, httpServer.Port);
+
+ handlers.AppendFormat("* XMLRPC:\n");
+ foreach (String s in httpServer.GetXmlRpcHandlerKeys())
+ handlers.AppendFormat("\t{0}\n", s);
+
+ handlers.AppendFormat("* HTTP:\n");
+ List poll = httpServer.GetPollServiceHandlerKeys();
+ foreach (String s in httpServer.GetHTTPHandlerKeys())
+ handlers.AppendFormat("\t{0} {1}\n", s, (poll.Contains(s) ? "(poll service)" : string.Empty));
+
+ handlers.AppendFormat("* Agent:\n");
+ foreach (String s in httpServer.GetAgentHandlerKeys())
+ handlers.AppendFormat("\t{0}\n", s);
+
+ handlers.AppendFormat("* LLSD:\n");
+ foreach (String s in httpServer.GetLLSDHandlerKeys())
+ handlers.AppendFormat("\t{0}\n", s);
+
+ handlers.AppendFormat("* StreamHandlers ({0}):\n", httpServer.GetStreamHandlerKeys().Count);
+ foreach (String s in httpServer.GetStreamHandlerKeys())
+ handlers.AppendFormat("\t{0}\n", s);
+
+ handlers.Append("\n");
+ }
+ }
+
+ MainConsole.Instance.Output(handlers.ToString());
+ }
+
///
/// Register an already started HTTP server to the collection of known servers.
///
diff --git a/OpenSim/Framework/Util.cs b/OpenSim/Framework/Util.cs
index 1a383aef80..a26e9308db 100644
--- a/OpenSim/Framework/Util.cs
+++ b/OpenSim/Framework/Util.cs
@@ -1019,6 +1019,38 @@ namespace OpenSim.Framework
}
}
+ ///
+ /// Copy data from one stream to another, leaving the read position of both streams at the beginning.
+ ///
+ ///
+ /// Input stream. Must be seekable.
+ ///
+ ///
+ /// Thrown if the input stream is not seekable.
+ ///
+ public static Stream Copy(Stream inputStream)
+ {
+ if (!inputStream.CanSeek)
+ throw new ArgumentException("Util.Copy(Stream inputStream) must receive an inputStream that can seek");
+
+ const int readSize = 256;
+ byte[] buffer = new byte[readSize];
+ MemoryStream ms = new MemoryStream();
+
+ int count = inputStream.Read(buffer, 0, readSize);
+
+ while (count > 0)
+ {
+ ms.Write(buffer, 0, count);
+ count = inputStream.Read(buffer, 0, readSize);
+ }
+
+ ms.Position = 0;
+ inputStream.Position = 0;
+
+ return ms;
+ }
+
public static XmlRpcResponse XmlRpcCommand(string url, string methodName, params object[] args)
{
return SendXmlRpcCommand(url, methodName, args);
diff --git a/OpenSim/Region/Application/OpenSim.cs b/OpenSim/Region/Application/OpenSim.cs
index 9c7598a776..a9b27459bb 100644
--- a/OpenSim/Region/Application/OpenSim.cs
+++ b/OpenSim/Region/Application/OpenSim.cs
@@ -292,7 +292,7 @@ namespace OpenSim
m_console.Commands.AddCommand("Archiving", false, "save oar",
//"save oar [-v|--version=] [-p|--profile=] []",
- "save oar [-h|--home=] [--noassets] [--publish] [--perm=] []",
+ "save oar [-h|--home=] [--noassets] [--publish] [--perm=] [--all] []",
"Save a region's data to an OAR archive.",
// "-v|--version= generates scene objects as per older versions of the serialization (e.g. -v=0)" + Environment.NewLine
"-h|--home= adds the url of the profile service to the saved user information.\n"
@@ -302,6 +302,7 @@ namespace OpenSim
+ " this is useful if you're making oars generally available that might be reloaded to the same grid from which you published\n"
+ "--perm= stops objects with insufficient permissions from being saved to the OAR.\n"
+ " can contain one or more of these characters: \"C\" = Copy, \"T\" = Transfer\n"
+ + "--all saves all the regions in the simulator, instead of just the current region.\n"
+ "The OAR path must be a filesystem path."
+ " If this is not given then the oar is saved to region.oar in the current directory.",
SaveOar);
@@ -332,10 +333,6 @@ namespace OpenSim
"show circuits",
"Show agent circuit data", HandleShow);
- m_console.Commands.AddCommand("Comms", false, "show http-handlers",
- "show http-handlers",
- "Show all registered http handlers", HandleShow);
-
m_console.Commands.AddCommand("Comms", false, "show pending-objects",
"show pending-objects",
"Show # of objects on the pending queues of all scene viewers", HandleShow);
@@ -1013,33 +1010,6 @@ namespace OpenSim
HandleShowCircuits();
break;
- case "http-handlers":
- System.Text.StringBuilder handlers = new System.Text.StringBuilder("Registered HTTP Handlers:\n");
-
- handlers.AppendFormat("* XMLRPC:\n");
- foreach (String s in HttpServer.GetXmlRpcHandlerKeys())
- handlers.AppendFormat("\t{0}\n", s);
-
- handlers.AppendFormat("* HTTP:\n");
- List poll = HttpServer.GetPollServiceHandlerKeys();
- foreach (String s in HttpServer.GetHTTPHandlerKeys())
- handlers.AppendFormat("\t{0} {1}\n", s, (poll.Contains(s) ? "(poll service)" : string.Empty));
-
- handlers.AppendFormat("* Agent:\n");
- foreach (String s in HttpServer.GetAgentHandlerKeys())
- handlers.AppendFormat("\t{0}\n", s);
-
- handlers.AppendFormat("* LLSD:\n");
- foreach (String s in HttpServer.GetLLSDHandlerKeys())
- handlers.AppendFormat("\t{0}\n", s);
-
- handlers.AppendFormat("* StreamHandlers ({0}):\n", HttpServer.GetStreamHandlerKeys().Count);
- foreach (String s in HttpServer.GetStreamHandlerKeys())
- handlers.AppendFormat("\t{0}\n", s);
-
- MainConsole.Instance.Output(handlers.ToString());
- break;
-
case "modules":
MainConsole.Instance.Output("The currently loaded shared modules are:");
foreach (IRegionModule module in m_moduleLoader.GetLoadedSharedModules)
diff --git a/OpenSim/Region/ClientStack/RegionApplicationBase.cs b/OpenSim/Region/ClientStack/RegionApplicationBase.cs
index 4672f8aa8a..853b72d9e7 100644
--- a/OpenSim/Region/ClientStack/RegionApplicationBase.cs
+++ b/OpenSim/Region/ClientStack/RegionApplicationBase.cs
@@ -76,7 +76,7 @@ namespace OpenSim.Region.ClientStack
protected override void StartupSpecific()
{
- SceneManager = new SceneManager();
+ SceneManager = SceneManager.Instance;
m_clientStackManager = CreateClientStackManager();
Initialize();
diff --git a/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs b/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs
index e1d4d7803c..d1a563cdce 100644
--- a/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs
+++ b/OpenSim/Region/CoreModules/Asset/FlotsamAssetCache.cs
@@ -107,8 +107,6 @@ namespace OpenSim.Region.CoreModules.Asset
private IAssetService m_AssetService;
private List m_Scenes = new List();
- private bool m_DeepScanBeforePurge;
-
public FlotsamAssetCache()
{
m_InvalidChars.AddRange(Path.GetInvalidPathChars());
@@ -170,8 +168,6 @@ namespace OpenSim.Region.CoreModules.Asset
m_CacheDirectoryTierLen = assetConfig.GetInt("CacheDirectoryTierLength", m_CacheDirectoryTierLen);
m_CacheWarnAt = assetConfig.GetInt("CacheWarnAt", m_CacheWarnAt);
-
- m_DeepScanBeforePurge = assetConfig.GetBoolean("DeepScanBeforePurge", m_DeepScanBeforePurge);
}
m_log.InfoFormat("[FLOTSAM ASSET CACHE]: Cache Directory {0}", m_CacheDirectory);
@@ -519,13 +515,10 @@ namespace OpenSim.Region.CoreModules.Asset
// Purge all files last accessed prior to this point
DateTime purgeLine = DateTime.Now - m_FileExpiration;
- // An optional deep scan at this point will ensure assets present in scenes,
- // or referenced by objects in the scene, but not recently accessed
- // are not purged.
- if (m_DeepScanBeforePurge)
- {
- CacheScenes();
- }
+ // An asset cache may contain local non-temporary assets that are not in the asset service. Therefore,
+ // before cleaning up expired files we must scan the objects in the scene to make sure that we retain
+ // such local assets if they have not been recently accessed.
+ TouchAllSceneAssets(false);
foreach (string dir in Directory.GetDirectories(m_CacheDirectory))
{
@@ -718,11 +711,14 @@ namespace OpenSim.Region.CoreModules.Asset
///
/// Iterates through all Scenes, doing a deep scan through assets
- /// to cache all assets present in the scene or referenced by assets
- /// in the scene
+ /// to update the access time of all assets present in the scene or referenced by assets
+ /// in the scene.
///
- ///
- private int CacheScenes()
+ ///
+ /// If true, then assets scanned which are not found in cache are added to the cache.
+ ///
+ /// Number of distinct asset references found in the scene.
+ private int TouchAllSceneAssets(bool storeUncached)
{
UuidGatherer gatherer = new UuidGatherer(m_AssetService);
@@ -745,7 +741,7 @@ namespace OpenSim.Region.CoreModules.Asset
{
File.SetLastAccessTime(filename, DateTime.Now);
}
- else
+ else if (storeUncached)
{
m_AssetService.Get(assetID.ToString());
}
@@ -873,13 +869,14 @@ namespace OpenSim.Region.CoreModules.Asset
break;
-
case "assets":
- m_log.Info("[FLOTSAM ASSET CACHE]: Caching all assets, in all scenes.");
+ m_log.Info("[FLOTSAM ASSET CACHE]: Ensuring assets are cached for all scenes.");
Util.FireAndForget(delegate {
- int assetsCached = CacheScenes();
- m_log.InfoFormat("[FLOTSAM ASSET CACHE]: Completed Scene Caching, {0} assets found.", assetsCached);
+ int assetReferenceTotal = TouchAllSceneAssets(true);
+ m_log.InfoFormat(
+ "[FLOTSAM ASSET CACHE]: Completed check with {0} assets.",
+ assetReferenceTotal);
});
break;
diff --git a/OpenSim/Region/CoreModules/Avatar/Friends/FriendsModule.cs b/OpenSim/Region/CoreModules/Avatar/Friends/FriendsModule.cs
index 24ec435d17..11db18a068 100644
--- a/OpenSim/Region/CoreModules/Avatar/Friends/FriendsModule.cs
+++ b/OpenSim/Region/CoreModules/Avatar/Friends/FriendsModule.cs
@@ -482,9 +482,9 @@ namespace OpenSim.Region.CoreModules.Avatar.Friends
Util.FireAndForget(
delegate
{
- m_log.DebugFormat(
- "[FRIENDS MODULE]: Notifying {0} friends of {1} of online status {2}",
- friendList.Count, agentID, online);
+// m_log.DebugFormat(
+// "[FRIENDS MODULE]: Notifying {0} friends of {1} of online status {2}",
+// friendList.Count, agentID, online);
// Notify about this user status
StatusNotify(friendList, agentID, online);
diff --git a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs
index 486f9d2067..1f62743b31 100644
--- a/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs
+++ b/OpenSim/Region/CoreModules/Framework/EntityTransfer/EntityTransferModule.cs
@@ -327,6 +327,14 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
return;
}
+ // Validate assorted conditions
+ string reason = string.Empty;
+ if (!ValidateGenericConditions(sp, reg, finalDestination, teleportFlags, out reason))
+ {
+ sp.ControllingClient.SendTeleportFailed(reason);
+ return;
+ }
+
//
// This is it
//
@@ -358,6 +366,13 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
}
}
+ // Nothing to validate here
+ protected virtual bool ValidateGenericConditions(ScenePresence sp, GridRegion reg, GridRegion finalDestination, uint teleportFlags, out string reason)
+ {
+ reason = String.Empty;
+ return true;
+ }
+
///
/// Determines whether this instance is within the max transfer distance.
///
@@ -574,7 +589,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
//sp.ControllingClient.SendTeleportProgress(teleportFlags, "Updating agent...");
- if (!UpdateAgent(reg, finalDestination, agent))
+ if (!UpdateAgent(reg, finalDestination, agent, sp))
{
// Region doesn't take it
m_log.WarnFormat(
@@ -701,7 +716,7 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
return success;
}
- protected virtual bool UpdateAgent(GridRegion reg, GridRegion finalDestination, AgentData agent)
+ protected virtual bool UpdateAgent(GridRegion reg, GridRegion finalDestination, AgentData agent, ScenePresence sp)
{
return Scene.SimulationService.UpdateAgent(finalDestination, agent);
}
diff --git a/OpenSim/Region/CoreModules/Framework/EntityTransfer/HGEntityTransferModule.cs b/OpenSim/Region/CoreModules/Framework/EntityTransfer/HGEntityTransferModule.cs
index 3010b59f13..0f1a38190e 100644
--- a/OpenSim/Region/CoreModules/Framework/EntityTransfer/HGEntityTransferModule.cs
+++ b/OpenSim/Region/CoreModules/Framework/EntityTransfer/HGEntityTransferModule.cs
@@ -54,6 +54,47 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
private GatekeeperServiceConnector m_GatekeeperConnector;
+ protected bool m_RestrictAppearanceAbroad;
+ protected string m_AccountName;
+ protected AvatarAppearance m_ExportedAppearance;
+
+ protected AvatarAppearance ExportedAppearance
+ {
+ get
+ {
+ if (m_ExportedAppearance != null)
+ return m_ExportedAppearance;
+
+ string[] parts = m_AccountName.Split();
+ if (parts.Length != 2)
+ {
+ m_log.WarnFormat("[HG ENTITY TRANSFER MODULE]: Wrong user account name format {0}. Specify 'First Last'", m_AccountName);
+ return null;
+ }
+ UserAccount account = Scene.UserAccountService.GetUserAccount(UUID.Zero, parts[0], parts[1]);
+ if (account == null)
+ {
+ m_log.WarnFormat("[HG ENTITY TRANSFER MODULE]: Unknown account {0}", m_AccountName);
+ return null;
+ }
+ m_ExportedAppearance = Scene.AvatarService.GetAppearance(account.PrincipalID);
+ if (m_ExportedAppearance != null)
+ m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: Successfully retrieved appearance for {0}", m_AccountName);
+
+ foreach (AvatarAttachment att in m_ExportedAppearance.GetAttachments())
+ {
+ InventoryItemBase item = new InventoryItemBase(att.ItemID, account.PrincipalID);
+ item = Scene.InventoryService.GetItem(item);
+ if (item != null)
+ m_ExportedAppearance.SetAttachment(att.AttachPoint, att.ItemID, item.AssetID);
+ else
+ m_log.WarnFormat("[HG ENTITY TRANSFER MODULE]: Unable to retrieve item {0} from inventory", att.ItemID);
+ }
+ return m_ExportedAppearance;
+ }
+ }
+
+
#region ISharedRegionModule
public override string Name
@@ -72,8 +113,18 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
{
IConfig transferConfig = source.Configs["EntityTransfer"];
if (transferConfig != null)
+ {
m_levelHGTeleport = transferConfig.GetInt("LevelHGTeleport", 0);
+ m_RestrictAppearanceAbroad = transferConfig.GetBoolean("RestrictAppearanceAbroad", false);
+ if (m_RestrictAppearanceAbroad)
+ {
+ m_AccountName = transferConfig.GetString("AccountForAppearance", string.Empty);
+ if (m_AccountName == string.Empty)
+ m_log.WarnFormat("[HG ENTITY TRANSFER MODULE]: RestrictAppearanceAbroad is on, but no account has been given for avatar appearance!");
+ }
+ }
+
InitialiseCommon(source);
m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: {0} enabled.", Name);
}
@@ -200,6 +251,109 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
TeleportHome(id, client);
}
+ protected override bool ValidateGenericConditions(ScenePresence sp, GridRegion reg, GridRegion finalDestination, uint teleportFlags, out string reason)
+ {
+ reason = "Please wear your grid's allowed appearance before teleporting to another grid";
+ if (!m_RestrictAppearanceAbroad)
+ return true;
+
+ // The rest is only needed for controlling appearance
+
+ int flags = Scene.GridService.GetRegionFlags(Scene.RegionInfo.ScopeID, reg.RegionID);
+ if (flags == -1 /* no region in DB */ || (flags & (int)OpenSim.Data.RegionFlags.Hyperlink) != 0)
+ {
+ // this user is going to another grid
+ if (Scene.UserManagementModule.IsLocalGridUser(sp.UUID))
+ {
+ m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: RestrictAppearanceAbroad is ON. Checking generic appearance");
+
+ // Check wearables
+ for (int i = 0; i < AvatarWearable.MAX_WEARABLES; i++)
+ {
+ for (int j = 0; j < sp.Appearance.Wearables[i].Count; j++)
+ {
+ if (sp.Appearance.Wearables[i] == null)
+ continue;
+
+ if (ExportedAppearance.Wearables[i] == null)
+ {
+ m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: Wearable not allowed to go outside {0}", i);
+ return false;
+ }
+
+ if (sp.Appearance.Wearables[i][j].AssetID != ExportedAppearance.Wearables[i][j].AssetID)
+ {
+ m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: Wearable not allowed to go outside {0}", i);
+ return false;
+ }
+ }
+ }
+
+ // Check attachments
+
+ foreach (AvatarAttachment att in sp.Appearance.GetAttachments())
+ {
+ bool found = false;
+ foreach (AvatarAttachment att2 in ExportedAppearance.GetAttachments())
+ {
+ if (att2.AssetID == att.AssetID)
+ {
+ found = true;
+ break;
+ }
+ }
+ if (!found)
+ {
+ m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: Attachment not allowed to go outside {0}", att.AttachPoint);
+ return false;
+ }
+ }
+ }
+ }
+
+ reason = string.Empty;
+ return true;
+ }
+
+
+ //protected override bool UpdateAgent(GridRegion reg, GridRegion finalDestination, AgentData agentData, ScenePresence sp)
+ //{
+ // int flags = Scene.GridService.GetRegionFlags(Scene.RegionInfo.ScopeID, reg.RegionID);
+ // if (flags == -1 /* no region in DB */ || (flags & (int)OpenSim.Data.RegionFlags.Hyperlink) != 0)
+ // {
+ // // this user is going to another grid
+ // if (m_RestrictAppearanceAbroad && Scene.UserManagementModule.IsLocalGridUser(agentData.AgentID))
+ // {
+ // // We need to strip the agent off its appearance
+ // m_log.DebugFormat("[HG ENTITY TRANSFER MODULE]: RestrictAppearanceAbroad is ON. Sending generic appearance");
+
+ // // Delete existing npc attachments
+ // Scene.AttachmentsModule.DeleteAttachmentsFromScene(sp, false);
+
+ // // XXX: We can't just use IAvatarFactoryModule.SetAppearance() yet since it doesn't transfer attachments
+ // AvatarAppearance newAppearance = new AvatarAppearance(ExportedAppearance, true);
+ // sp.Appearance = newAppearance;
+
+ // // Rez needed npc attachments
+ // Scene.AttachmentsModule.RezAttachments(sp);
+
+
+ // IAvatarFactoryModule module = Scene.RequestModuleInterface();
+ // //module.SendAppearance(sp.UUID);
+ // module.RequestRebake(sp, false);
+
+ // Scene.AttachmentsModule.CopyAttachments(sp, agentData);
+ // agentData.Appearance = sp.Appearance;
+ // }
+ // }
+
+ // foreach (AvatarAttachment a in agentData.Appearance.GetAttachments())
+ // m_log.DebugFormat("[XXX]: {0}-{1}", a.ItemID, a.AssetID);
+
+
+ // return base.UpdateAgent(reg, finalDestination, agentData, sp);
+ //}
+
public override bool TeleportHome(UUID id, IClientAPI client)
{
m_log.DebugFormat(
@@ -375,4 +529,4 @@ namespace OpenSim.Region.CoreModules.Framework.EntityTransfer
return region;
}
}
-}
\ No newline at end of file
+}
diff --git a/OpenSim/Region/CoreModules/Framework/InventoryAccess/HGInventoryAccessModule.cs b/OpenSim/Region/CoreModules/Framework/InventoryAccess/HGInventoryAccessModule.cs
index cf72b58832..a0cad40754 100644
--- a/OpenSim/Region/CoreModules/Framework/InventoryAccess/HGInventoryAccessModule.cs
+++ b/OpenSim/Region/CoreModules/Framework/InventoryAccess/HGInventoryAccessModule.cs
@@ -308,6 +308,8 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess
protected override InventoryItemBase GetItem(UUID agentID, UUID itemID)
{
InventoryItemBase item = base.GetItem(agentID, itemID);
+ if (item == null)
+ return null;
string userAssetServer = string.Empty;
if (IsForeignUser(agentID, out userAssetServer))
diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Asset/LocalAssetServiceConnector.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Asset/LocalAssetServiceConnector.cs
index c78915f970..449c1f1108 100644
--- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Asset/LocalAssetServiceConnector.cs
+++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Asset/LocalAssetServiceConnector.cs
@@ -204,8 +204,11 @@ namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Asset
public byte[] GetData(string id)
{
// m_log.DebugFormat("[LOCAL ASSET SERVICES CONNECTOR]: Requesting data for asset {0}", id);
-
- AssetBase asset = m_Cache.Get(id);
+
+ AssetBase asset = null;
+
+ if (m_Cache != null)
+ asset = m_Cache.Get(id);
if (asset != null)
return asset.Data;
diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Asset/Tests/AssetConnectorTests.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Asset/Tests/AssetConnectorTests.cs
new file mode 100644
index 0000000000..198247317b
--- /dev/null
+++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Asset/Tests/AssetConnectorTests.cs
@@ -0,0 +1,136 @@
+/*
+ * 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.Collections.Generic;
+using System.IO;
+using System.Reflection;
+using System.Threading;
+using log4net.Config;
+using Nini.Config;
+using NUnit.Framework;
+using OpenMetaverse;
+using OpenSim.Framework;
+using OpenSim.Region.Framework.Scenes;
+using OpenSim.Region.CoreModules.ServiceConnectorsOut.Asset;
+using OpenSim.Tests.Common;
+
+namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Asset.Tests
+{
+ [TestFixture]
+ public class AssetConnectorsTests : OpenSimTestCase
+ {
+ [Test]
+ public void TestAddAsset()
+ {
+ TestHelpers.InMethod();
+// TestHelpers.EnableLogging();
+
+ IConfigSource config = new IniConfigSource();
+ config.AddConfig("Modules");
+ config.Configs["Modules"].Set("AssetServices", "LocalAssetServicesConnector");
+ config.AddConfig("AssetService");
+ config.Configs["AssetService"].Set("LocalServiceModule", "OpenSim.Services.AssetService.dll:AssetService");
+ config.Configs["AssetService"].Set("StorageProvider", "OpenSim.Tests.Common.dll");
+
+ LocalAssetServicesConnector lasc = new LocalAssetServicesConnector();
+ lasc.Initialise(config);
+
+ AssetBase a1 = AssetHelpers.CreateNotecardAsset();
+ lasc.Store(a1);
+
+ AssetBase retreivedA1 = lasc.Get(a1.ID);
+ Assert.That(retreivedA1.ID, Is.EqualTo(a1.ID));
+ Assert.That(retreivedA1.Metadata.ID, Is.EqualTo(a1.Metadata.ID));
+ Assert.That(retreivedA1.Data.Length, Is.EqualTo(a1.Data.Length));
+
+ AssetMetadata retrievedA1Metadata = lasc.GetMetadata(a1.ID);
+ Assert.That(retrievedA1Metadata.ID, Is.EqualTo(a1.ID));
+
+ byte[] retrievedA1Data = lasc.GetData(a1.ID);
+ Assert.That(retrievedA1Data.Length, Is.EqualTo(a1.Data.Length));
+
+ // TODO: Add cache and check that this does receive a copy of the asset
+ }
+
+ [Test]
+ public void TestAddTemporaryAsset()
+ {
+ TestHelpers.InMethod();
+// TestHelpers.EnableLogging();
+
+ IConfigSource config = new IniConfigSource();
+ config.AddConfig("Modules");
+ config.Configs["Modules"].Set("AssetServices", "LocalAssetServicesConnector");
+ config.AddConfig("AssetService");
+ config.Configs["AssetService"].Set("LocalServiceModule", "OpenSim.Services.AssetService.dll:AssetService");
+ config.Configs["AssetService"].Set("StorageProvider", "OpenSim.Tests.Common.dll");
+
+ LocalAssetServicesConnector lasc = new LocalAssetServicesConnector();
+ lasc.Initialise(config);
+
+ AssetBase a1 = AssetHelpers.CreateNotecardAsset();
+ a1.Temporary = true;
+
+ lasc.Store(a1);
+
+ Assert.That(lasc.Get(a1.ID), Is.Null);
+ Assert.That(lasc.GetData(a1.ID), Is.Null);
+ Assert.That(lasc.GetMetadata(a1.ID), Is.Null);
+
+ // TODO: Add cache and check that this does receive a copy of the asset
+ }
+
+ [Test]
+ public void TestAddLocalAsset()
+ {
+ TestHelpers.InMethod();
+// TestHelpers.EnableLogging();
+
+ IConfigSource config = new IniConfigSource();
+ config.AddConfig("Modules");
+ config.Configs["Modules"].Set("AssetServices", "LocalAssetServicesConnector");
+ config.AddConfig("AssetService");
+ config.Configs["AssetService"].Set("LocalServiceModule", "OpenSim.Services.AssetService.dll:AssetService");
+ config.Configs["AssetService"].Set("StorageProvider", "OpenSim.Tests.Common.dll");
+
+ LocalAssetServicesConnector lasc = new LocalAssetServicesConnector();
+ lasc.Initialise(config);
+
+ AssetBase a1 = AssetHelpers.CreateNotecardAsset();
+ a1.Local = true;
+
+ lasc.Store(a1);
+
+ Assert.That(lasc.Get(a1.ID), Is.Null);
+ Assert.That(lasc.GetData(a1.ID), Is.Null);
+ Assert.That(lasc.GetMetadata(a1.ID), Is.Null);
+
+ // TODO: Add cache and check that this does receive a copy of the asset
+ }
+ }
+}
\ No newline at end of file
diff --git a/OpenSim/Region/CoreModules/World/Archiver/ArchiveReadRequest.cs b/OpenSim/Region/CoreModules/World/Archiver/ArchiveReadRequest.cs
index 142567bff1..2127f4d7fc 100644
--- a/OpenSim/Region/CoreModules/World/Archiver/ArchiveReadRequest.cs
+++ b/OpenSim/Region/CoreModules/World/Archiver/ArchiveReadRequest.cs
@@ -43,6 +43,7 @@ using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Region.Framework.Scenes.Serialization;
using OpenSim.Services.Interfaces;
+using System.Threading;
namespace OpenSim.Region.CoreModules.World.Archiver
{
@@ -52,7 +53,30 @@ namespace OpenSim.Region.CoreModules.World.Archiver
public class ArchiveReadRequest
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+
+ ///
+ /// Contains data used while dearchiving a single scene.
+ ///
+ private class DearchiveContext
+ {
+ public Scene Scene { get; set; }
+
+ public List SerialisedSceneObjects { get; set; }
+
+ public List SerialisedParcels { get; set; }
+
+ public List SceneObjects { get; set; }
+
+ public DearchiveContext(Scene scene)
+ {
+ Scene = scene;
+ SerialisedSceneObjects = new List();
+ SerialisedParcels = new List();
+ SceneObjects = new List();
+ }
+ }
+
///
/// The maximum major version of OAR that we can read. Minor versions shouldn't need a max number since version
/// bumps here should be compatible.
@@ -62,9 +86,10 @@ namespace OpenSim.Region.CoreModules.World.Archiver
///
/// Has the control file been loaded for this archive?
///
- public bool ControlFileLoaded { get; private set; }
+ public bool ControlFileLoaded { get; private set; }
- protected Scene m_scene;
+ protected string m_loadPath;
+ protected Scene m_rootScene;
protected Stream m_loadStream;
protected Guid m_requestId;
protected string m_errorMessage;
@@ -91,7 +116,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver
{
if (m_UserMan == null)
{
- m_UserMan = m_scene.RequestModuleInterface();
+ m_UserMan = m_rootScene.RequestModuleInterface();
}
return m_UserMan;
}
@@ -104,10 +129,14 @@ namespace OpenSim.Region.CoreModules.World.Archiver
private IGroupsModule m_groupsModule;
+ private IAssetService m_assetService = null;
+
+
public ArchiveReadRequest(Scene scene, string loadPath, bool merge, bool skipAssets, Guid requestId)
{
- m_scene = scene;
+ m_rootScene = scene;
+ m_loadPath = loadPath;
try
{
m_loadStream = new GZipStream(ArchiveHelpers.GetStream(loadPath), CompressionMode.Decompress);
@@ -128,12 +157,14 @@ namespace OpenSim.Region.CoreModules.World.Archiver
// Zero can never be a valid user id
m_validUserUuids[UUID.Zero] = false;
- m_groupsModule = m_scene.RequestModuleInterface();
+ m_groupsModule = m_rootScene.RequestModuleInterface();
+ m_assetService = m_rootScene.AssetService;
}
public ArchiveReadRequest(Scene scene, Stream loadStream, bool merge, bool skipAssets, Guid requestId)
{
- m_scene = scene;
+ m_rootScene = scene;
+ m_loadPath = null;
m_loadStream = loadStream;
m_merge = merge;
m_skipAssets = skipAssets;
@@ -142,33 +173,34 @@ namespace OpenSim.Region.CoreModules.World.Archiver
// Zero can never be a valid user id
m_validUserUuids[UUID.Zero] = false;
- m_groupsModule = m_scene.RequestModuleInterface();
+ m_groupsModule = m_rootScene.RequestModuleInterface();
+ m_assetService = m_rootScene.AssetService;
}
///
/// Dearchive the region embodied in this request.
///
public void DearchiveRegion()
- {
- // The same code can handle dearchiving 0.1 and 0.2 OpenSim Archive versions
- DearchiveRegion0DotStar();
- }
-
- private void DearchiveRegion0DotStar()
{
int successfulAssetRestores = 0;
int failedAssetRestores = 0;
- List serialisedSceneObjects = new List();
- List serialisedParcels = new List();
- string filePath = "NONE";
- TarArchiveReader archive = new TarArchiveReader(m_loadStream);
+ DearchiveScenesInfo dearchivedScenes;
+
+ // We dearchive all the scenes at once, because the files in the TAR archive might be mixed.
+ // Therefore, we have to keep track of the dearchive context of all the scenes.
+ Dictionary sceneContexts = new Dictionary();
+
+ string fullPath = "NONE";
+ TarArchiveReader archive = null;
byte[] data;
TarArchiveReader.TarEntryType entryType;
-
+
try
{
- while ((data = archive.ReadEntry(out filePath, out entryType)) != null)
+ FindAndLoadControlFile(out archive, out dearchivedScenes);
+
+ while ((data = archive.ReadEntry(out fullPath, out entryType)) != null)
{
//m_log.DebugFormat(
// "[ARCHIVER]: Successfully read {0} ({1} bytes)", filePath, data.Length);
@@ -176,9 +208,30 @@ namespace OpenSim.Region.CoreModules.World.Archiver
if (TarArchiveReader.TarEntryType.TYPE_DIRECTORY == entryType)
continue;
+
+ // Find the scene that this file belongs to
+
+ Scene scene;
+ string filePath;
+ if (!dearchivedScenes.GetRegionFromPath(fullPath, out scene, out filePath))
+ continue; // this file belongs to a region that we're not loading
+
+ DearchiveContext sceneContext = null;
+ if (scene != null)
+ {
+ if (!sceneContexts.TryGetValue(scene.RegionInfo.RegionID, out sceneContext))
+ {
+ sceneContext = new DearchiveContext(scene);
+ sceneContexts.Add(scene.RegionInfo.RegionID, sceneContext);
+ }
+ }
+
+
+ // Process the file
+
if (filePath.StartsWith(ArchiveConstants.OBJECTS_PATH))
{
- serialisedSceneObjects.Add(Encoding.UTF8.GetString(data));
+ sceneContext.SerialisedSceneObjects.Add(Encoding.UTF8.GetString(data));
}
else if (filePath.StartsWith(ArchiveConstants.ASSETS_PATH) && !m_skipAssets)
{
@@ -192,19 +245,19 @@ namespace OpenSim.Region.CoreModules.World.Archiver
}
else if (!m_merge && filePath.StartsWith(ArchiveConstants.TERRAINS_PATH))
{
- LoadTerrain(filePath, data);
+ LoadTerrain(scene, filePath, data);
}
else if (!m_merge && filePath.StartsWith(ArchiveConstants.SETTINGS_PATH))
{
- LoadRegionSettings(filePath, data);
+ LoadRegionSettings(scene, filePath, data, dearchivedScenes);
}
else if (!m_merge && filePath.StartsWith(ArchiveConstants.LANDDATA_PATH))
{
- serialisedParcels.Add(Encoding.UTF8.GetString(data));
+ sceneContext.SerialisedParcels.Add(Encoding.UTF8.GetString(data));
}
else if (filePath == ArchiveConstants.CONTROL_FILE_PATH)
{
- LoadControlFile(filePath, data);
+ // Ignore, because we already read the control file
}
}
@@ -212,15 +265,16 @@ namespace OpenSim.Region.CoreModules.World.Archiver
}
catch (Exception e)
{
- m_log.ErrorFormat(
- "[ARCHIVER]: Aborting load with error in archive file {0}. {1}", filePath, e);
+ m_log.Error(
+ String.Format("[ARCHIVER]: Aborting load with error in archive file {0} ", fullPath), e);
m_errorMessage += e.ToString();
- m_scene.EventManager.TriggerOarFileLoaded(m_requestId, m_errorMessage);
+ m_rootScene.EventManager.TriggerOarFileLoaded(m_requestId, new List(), m_errorMessage);
return;
}
finally
{
- archive.Close();
+ if (archive != null)
+ archive.Close();
}
if (!m_skipAssets)
@@ -234,32 +288,143 @@ namespace OpenSim.Region.CoreModules.World.Archiver
}
}
- if (!m_merge)
+ foreach (DearchiveContext sceneContext in sceneContexts.Values)
{
- m_log.Info("[ARCHIVER]: Clearing all existing scene objects");
- m_scene.DeleteAllSceneObjects();
+ m_log.InfoFormat("[ARCHIVER:] Loading region {0}", sceneContext.Scene.RegionInfo.RegionName);
+
+ if (!m_merge)
+ {
+ m_log.Info("[ARCHIVER]: Clearing all existing scene objects");
+ sceneContext.Scene.DeleteAllSceneObjects();
+ }
+
+ try
+ {
+ LoadParcels(sceneContext.Scene, sceneContext.SerialisedParcels);
+ LoadObjects(sceneContext.Scene, sceneContext.SerialisedSceneObjects, sceneContext.SceneObjects);
+
+ // Inform any interested parties that the region has changed. We waited until now so that all
+ // of the region's objects will be loaded when we send this notification.
+ IEstateModule estateModule = sceneContext.Scene.RequestModuleInterface();
+ if (estateModule != null)
+ estateModule.TriggerRegionInfoChange();
+ }
+ catch (Exception e)
+ {
+ m_log.Error("[ARCHIVER]: Error loading parcels or objects ", e);
+ m_errorMessage += e.ToString();
+ m_rootScene.EventManager.TriggerOarFileLoaded(m_requestId, new List(), m_errorMessage);
+ return;
+ }
}
- LoadParcels(serialisedParcels);
- LoadObjects(serialisedSceneObjects);
+ // Start the scripts. We delayed this because we want the OAR to finish loading ASAP, so
+ // that users can enter the scene. If we allow the scripts to start in the loop above
+ // then they significantly increase the time until the OAR finishes loading.
+ Util.FireAndForget(delegate(object o)
+ {
+ Thread.Sleep(15000);
+ m_log.Info("Starting scripts in scene objects");
+
+ foreach (DearchiveContext sceneContext in sceneContexts.Values)
+ {
+ foreach (SceneObjectGroup sceneObject in sceneContext.SceneObjects)
+ {
+ sceneObject.CreateScriptInstances(0, false, sceneContext.Scene.DefaultScriptEngine, 0); // StateSource.RegionStart
+ sceneObject.ResumeScripts();
+ }
+
+ sceneContext.SceneObjects.Clear();
+ }
+ });
m_log.InfoFormat("[ARCHIVER]: Successfully loaded archive");
- m_scene.EventManager.TriggerOarFileLoaded(m_requestId, m_errorMessage);
+ m_rootScene.EventManager.TriggerOarFileLoaded(m_requestId, dearchivedScenes.GetLoadedScenes(), m_errorMessage);
+ }
+
+ ///
+ /// Searches through the files in the archive for the control file, and reads it.
+ /// We must read the control file first, in order to know which regions are available.
+ ///
+ ///
+ /// In most cases the control file *is* first, since that's how we create archives. However,
+ /// it's possible that someone rewrote the archive externally so we can't rely on this fact.
+ ///
+ ///
+ ///
+ private void FindAndLoadControlFile(out TarArchiveReader archive, out DearchiveScenesInfo dearchivedScenes)
+ {
+ archive = new TarArchiveReader(m_loadStream);
+ dearchivedScenes = new DearchiveScenesInfo();
+
+ string filePath;
+ byte[] data;
+ TarArchiveReader.TarEntryType entryType;
+ bool firstFile = true;
+
+ while ((data = archive.ReadEntry(out filePath, out entryType)) != null)
+ {
+ if (TarArchiveReader.TarEntryType.TYPE_DIRECTORY == entryType)
+ continue;
+
+ if (filePath == ArchiveConstants.CONTROL_FILE_PATH)
+ {
+ LoadControlFile(filePath, data, dearchivedScenes);
+
+ // Find which scenes are available in the simulator
+ ArchiveScenesGroup simulatorScenes = new ArchiveScenesGroup();
+ SceneManager.Instance.ForEachScene(delegate(Scene scene2)
+ {
+ simulatorScenes.AddScene(scene2);
+ });
+ simulatorScenes.CalcSceneLocations();
+ dearchivedScenes.SetSimulatorScenes(m_rootScene, simulatorScenes);
+
+ // If the control file wasn't the first file then reset the read pointer
+ if (!firstFile)
+ {
+ m_log.Warn("Control file wasn't the first file in the archive");
+ if (m_loadStream.CanSeek)
+ {
+ m_loadStream.Seek(0, SeekOrigin.Begin);
+ }
+ else if (m_loadPath != null)
+ {
+ archive.Close();
+ archive = null;
+ m_loadStream.Close();
+ m_loadStream = null;
+ m_loadStream = new GZipStream(ArchiveHelpers.GetStream(m_loadPath), CompressionMode.Decompress);
+ archive = new TarArchiveReader(m_loadStream);
+ }
+ else
+ {
+ // There isn't currently a scenario where this happens, but it's best to add a check just in case
+ throw new Exception("Error reading archive: control file wasn't the first file, and the input stream doesn't allow seeking");
+ }
+ }
+
+ return;
+ }
+
+ firstFile = false;
+ }
+
+ throw new Exception("Control file not found");
}
///
/// Load serialized scene objects.
///
- ///
- protected void LoadObjects(List serialisedSceneObjects)
+ protected void LoadObjects(Scene scene, List serialisedSceneObjects, List sceneObjects)
{
// Reload serialized prims
m_log.InfoFormat("[ARCHIVER]: Loading {0} scene objects. Please wait.", serialisedSceneObjects.Count);
- UUID oldTelehubUUID = m_scene.RegionInfo.RegionSettings.TelehubObject;
+ UUID oldTelehubUUID = scene.RegionInfo.RegionSettings.TelehubObject;
- IRegionSerialiserModule serialiser = m_scene.RequestModuleInterface();
+ IRegionSerialiserModule serialiser = scene.RequestModuleInterface();
int sceneObjectsLoadedCount = 0;
foreach (string serialisedSceneObject in serialisedSceneObjects)
@@ -280,7 +445,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver
SceneObjectGroup sceneObject = serialiser.DeserializeGroupFromXml2(serialisedSceneObject);
- bool isTelehub = (sceneObject.UUID == oldTelehubUUID);
+ bool isTelehub = (sceneObject.UUID == oldTelehubUUID) && (oldTelehubUUID != UUID.Zero);
// For now, give all incoming scene objects new uuids. This will allow scenes to be cloned
// on the same region server and multiple examples a single object archive to be imported
@@ -290,8 +455,8 @@ namespace OpenSim.Region.CoreModules.World.Archiver
if (isTelehub)
{
// Change the Telehub Object to the new UUID
- m_scene.RegionInfo.RegionSettings.TelehubObject = sceneObject.UUID;
- m_scene.RegionInfo.RegionSettings.Save();
+ scene.RegionInfo.RegionSettings.TelehubObject = sceneObject.UUID;
+ scene.RegionInfo.RegionSettings.Save();
oldTelehubUUID = UUID.Zero;
}
@@ -301,17 +466,17 @@ namespace OpenSim.Region.CoreModules.World.Archiver
{
if (part.CreatorData == null || part.CreatorData == string.Empty)
{
- if (!ResolveUserUuid(part.CreatorID))
- part.CreatorID = m_scene.RegionInfo.EstateSettings.EstateOwner;
+ if (!ResolveUserUuid(scene, part.CreatorID))
+ part.CreatorID = scene.RegionInfo.EstateSettings.EstateOwner;
}
if (UserManager != null)
UserManager.AddUser(part.CreatorID, part.CreatorData);
- if (!ResolveUserUuid(part.OwnerID))
- part.OwnerID = m_scene.RegionInfo.EstateSettings.EstateOwner;
+ if (!ResolveUserUuid(scene, part.OwnerID))
+ part.OwnerID = scene.RegionInfo.EstateSettings.EstateOwner;
- if (!ResolveUserUuid(part.LastOwnerID))
- part.LastOwnerID = m_scene.RegionInfo.EstateSettings.EstateOwner;
+ if (!ResolveUserUuid(scene, part.LastOwnerID))
+ part.LastOwnerID = scene.RegionInfo.EstateSettings.EstateOwner;
if (!ResolveGroupUuid(part.GroupID))
part.GroupID = UUID.Zero;
@@ -325,14 +490,14 @@ namespace OpenSim.Region.CoreModules.World.Archiver
// being no copy/no mod for everyone
lock (part.TaskInventory)
{
- if (!ResolveUserUuid(part.CreatorID))
- part.CreatorID = m_scene.RegionInfo.EstateSettings.EstateOwner;
+ if (!ResolveUserUuid(scene, part.CreatorID))
+ part.CreatorID = scene.RegionInfo.EstateSettings.EstateOwner;
- if (!ResolveUserUuid(part.OwnerID))
- part.OwnerID = m_scene.RegionInfo.EstateSettings.EstateOwner;
+ if (!ResolveUserUuid(scene, part.OwnerID))
+ part.OwnerID = scene.RegionInfo.EstateSettings.EstateOwner;
- if (!ResolveUserUuid(part.LastOwnerID))
- part.LastOwnerID = m_scene.RegionInfo.EstateSettings.EstateOwner;
+ if (!ResolveUserUuid(scene, part.LastOwnerID))
+ part.LastOwnerID = scene.RegionInfo.EstateSettings.EstateOwner;
// And zap any troublesome sit target information
part.SitTargetOrientation = new Quaternion(0, 0, 0, 1);
@@ -345,15 +510,15 @@ namespace OpenSim.Region.CoreModules.World.Archiver
TaskInventoryDictionary inv = part.TaskInventory;
foreach (KeyValuePair kvp in inv)
{
- if (!ResolveUserUuid(kvp.Value.OwnerID))
+ if (!ResolveUserUuid(scene, kvp.Value.OwnerID))
{
- kvp.Value.OwnerID = m_scene.RegionInfo.EstateSettings.EstateOwner;
+ kvp.Value.OwnerID = scene.RegionInfo.EstateSettings.EstateOwner;
}
if (kvp.Value.CreatorData == null || kvp.Value.CreatorData == string.Empty)
{
- if (!ResolveUserUuid(kvp.Value.CreatorID))
- kvp.Value.CreatorID = m_scene.RegionInfo.EstateSettings.EstateOwner;
+ if (!ResolveUserUuid(scene, kvp.Value.CreatorID))
+ kvp.Value.CreatorID = scene.RegionInfo.EstateSettings.EstateOwner;
}
if (UserManager != null)
@@ -366,10 +531,10 @@ namespace OpenSim.Region.CoreModules.World.Archiver
}
}
- if (m_scene.AddRestoredSceneObject(sceneObject, true, false))
+ if (scene.AddRestoredSceneObject(sceneObject, true, false))
{
sceneObjectsLoadedCount++;
- sceneObject.CreateScriptInstances(0, false, m_scene.DefaultScriptEngine, 0);
+ sceneObject.CreateScriptInstances(0, false, scene.DefaultScriptEngine, 0);
sceneObject.ResumeScripts();
}
}
@@ -384,16 +549,17 @@ namespace OpenSim.Region.CoreModules.World.Archiver
if (oldTelehubUUID != UUID.Zero)
{
m_log.WarnFormat("Telehub object not found: {0}", oldTelehubUUID);
- m_scene.RegionInfo.RegionSettings.TelehubObject = UUID.Zero;
- m_scene.RegionInfo.RegionSettings.ClearSpawnPoints();
+ scene.RegionInfo.RegionSettings.TelehubObject = UUID.Zero;
+ scene.RegionInfo.RegionSettings.ClearSpawnPoints();
}
}
///
/// Load serialized parcels.
///
+ ///
///
- protected void LoadParcels(List serialisedParcels)
+ protected void LoadParcels(Scene scene, List serialisedParcels)
{
// Reload serialized parcels
m_log.InfoFormat("[ARCHIVER]: Loading {0} parcels. Please wait.", serialisedParcels.Count);
@@ -404,8 +570,8 @@ namespace OpenSim.Region.CoreModules.World.Archiver
// Validate User and Group UUID's
- if (!ResolveUserUuid(parcel.OwnerID))
- parcel.OwnerID = m_scene.RegionInfo.EstateSettings.EstateOwner;
+ if (!ResolveUserUuid(scene, parcel.OwnerID))
+ parcel.OwnerID = m_rootScene.RegionInfo.EstateSettings.EstateOwner;
if (!ResolveGroupUuid(parcel.GroupID))
{
@@ -416,7 +582,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver
List accessList = new List();
foreach (LandAccessEntry entry in parcel.ParcelAccessList)
{
- if (ResolveUserUuid(entry.AgentID))
+ if (ResolveUserUuid(scene, entry.AgentID))
accessList.Add(entry);
// else, drop this access rule
}
@@ -432,23 +598,24 @@ namespace OpenSim.Region.CoreModules.World.Archiver
if (!m_merge)
{
bool setupDefaultParcel = (landData.Count == 0);
- m_scene.LandChannel.Clear(setupDefaultParcel);
+ scene.LandChannel.Clear(setupDefaultParcel);
}
- m_scene.EventManager.TriggerIncomingLandDataFromStorage(landData);
+ scene.EventManager.TriggerIncomingLandDataFromStorage(landData);
m_log.InfoFormat("[ARCHIVER]: Restored {0} parcels.", landData.Count);
}
///
/// Look up the given user id to check whether it's one that is valid for this grid.
///
+ ///
///
///
- private bool ResolveUserUuid(UUID uuid)
+ private bool ResolveUserUuid(Scene scene, UUID uuid)
{
if (!m_validUserUuids.ContainsKey(uuid))
{
- UserAccount account = m_scene.UserAccountService.GetUserAccount(m_scene.RegionInfo.ScopeID, uuid);
+ UserAccount account = scene.UserAccountService.GetUserAccount(scene.RegionInfo.ScopeID, uuid);
m_validUserUuids.Add(uuid, account != null);
}
@@ -503,7 +670,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver
string extension = filename.Substring(i);
string uuid = filename.Remove(filename.Length - extension.Length);
- if (m_scene.AssetService.GetMetadata(uuid) != null)
+ if (m_assetService.GetMetadata(uuid) != null)
{
// m_log.DebugFormat("[ARCHIVER]: found existing asset {0}",uuid);
return true;
@@ -523,7 +690,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver
// We're relying on the asset service to do the sensible thing and not store the asset if it already
// exists.
- m_scene.AssetService.Store(asset);
+ m_assetService.Store(asset);
/**
* Create layers on decode for image assets. This is likely to significantly increase the time to load archives so
@@ -551,12 +718,14 @@ namespace OpenSim.Region.CoreModules.World.Archiver
///
/// Load region settings data
///
+ ///
///
///
+ ///
///
/// true if settings were loaded successfully, false otherwise
///
- private bool LoadRegionSettings(string settingsPath, byte[] data)
+ private bool LoadRegionSettings(Scene scene, string settingsPath, byte[] data, DearchiveScenesInfo dearchivedScenes)
{
RegionSettings loadedRegionSettings;
@@ -572,7 +741,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver
return false;
}
- RegionSettings currentRegionSettings = m_scene.RegionInfo.RegionSettings;
+ RegionSettings currentRegionSettings = scene.RegionInfo.RegionSettings;
currentRegionSettings.AgentLimit = loadedRegionSettings.AgentLimit;
currentRegionSettings.AllowDamage = loadedRegionSettings.AllowDamage;
@@ -609,12 +778,14 @@ namespace OpenSim.Region.CoreModules.World.Archiver
foreach (SpawnPoint sp in loadedRegionSettings.SpawnPoints())
currentRegionSettings.AddSpawnPoint(sp);
+ currentRegionSettings.LoadedCreationDateTime = dearchivedScenes.LoadedCreationDateTime;
+ currentRegionSettings.LoadedCreationID = dearchivedScenes.GetOriginalRegionID(scene.RegionInfo.RegionID).ToString();
+
currentRegionSettings.Save();
- m_scene.TriggerEstateSunUpdate();
+ scene.TriggerEstateSunUpdate();
- IEstateModule estateModule = m_scene.RequestModuleInterface();
-
+ IEstateModule estateModule = scene.RequestModuleInterface();
if (estateModule != null)
estateModule.sendRegionHandshakeToAll();
@@ -624,14 +795,15 @@ namespace OpenSim.Region.CoreModules.World.Archiver
///
/// Load terrain data
///
+ ///
///
///
///
/// true if terrain was resolved successfully, false otherwise.
///
- private bool LoadTerrain(string terrainPath, byte[] data)
+ private bool LoadTerrain(Scene scene, string terrainPath, byte[] data)
{
- ITerrainModule terrainModule = m_scene.RequestModuleInterface();
+ ITerrainModule terrainModule = scene.RequestModuleInterface();
MemoryStream ms = new MemoryStream(data);
terrainModule.LoadFromStream(terrainPath, ms);
@@ -647,17 +819,18 @@ namespace OpenSim.Region.CoreModules.World.Archiver
///
///
///
- public void LoadControlFile(string path, byte[] data)
+ ///
+ public DearchiveScenesInfo LoadControlFile(string path, byte[] data, DearchiveScenesInfo dearchivedScenes)
{
XmlNamespaceManager nsmgr = new XmlNamespaceManager(new NameTable());
XmlParserContext context = new XmlParserContext(null, nsmgr, null, XmlSpace.None);
XmlTextReader xtr = new XmlTextReader(Encoding.ASCII.GetString(data), XmlNodeType.Document, context);
- RegionSettings currentRegionSettings = m_scene.RegionInfo.RegionSettings;
+ // Loaded metadata will be empty if no information exists in the archive
+ dearchivedScenes.LoadedCreationDateTime = 0;
+ dearchivedScenes.DefaultOriginalID = "";
- // Loaded metadata will empty if no information exists in the archive
- currentRegionSettings.LoadedCreationDateTime = 0;
- currentRegionSettings.LoadedCreationID = "";
+ bool multiRegion = false;
while (xtr.Read())
{
@@ -683,18 +856,44 @@ namespace OpenSim.Region.CoreModules.World.Archiver
{
int value;
if (Int32.TryParse(xtr.ReadElementContentAsString(), out value))
- currentRegionSettings.LoadedCreationDateTime = value;
+ dearchivedScenes.LoadedCreationDateTime = value;
}
- else if (xtr.Name.ToString() == "id")
+ else if (xtr.Name.ToString() == "row")
{
- currentRegionSettings.LoadedCreationID = xtr.ReadElementContentAsString();
+ multiRegion = true;
+ dearchivedScenes.StartRow();
+ }
+ else if (xtr.Name.ToString() == "region")
+ {
+ dearchivedScenes.StartRegion();
+ }
+ else if (xtr.Name.ToString() == "id")
+ {
+ string id = xtr.ReadElementContentAsString();
+ dearchivedScenes.DefaultOriginalID = id;
+ if (multiRegion)
+ dearchivedScenes.SetRegionOriginalID(id);
+ }
+ else if (xtr.Name.ToString() == "dir")
+ {
+ dearchivedScenes.SetRegionDirectory(xtr.ReadElementContentAsString());
}
}
}
-
- currentRegionSettings.Save();
-
+
+ dearchivedScenes.MultiRegionFormat = multiRegion;
+ if (!multiRegion)
+ {
+ // Add the single scene
+ dearchivedScenes.StartRow();
+ dearchivedScenes.StartRegion();
+ dearchivedScenes.SetRegionOriginalID(dearchivedScenes.DefaultOriginalID);
+ dearchivedScenes.SetRegionDirectory("");
+ }
+
ControlFileLoaded = true;
+
+ return dearchivedScenes;
}
}
-}
\ No newline at end of file
+}
diff --git a/OpenSim/Region/CoreModules/World/Archiver/ArchiveScenesGroup.cs b/OpenSim/Region/CoreModules/World/Archiver/ArchiveScenesGroup.cs
new file mode 100644
index 0000000000..a66ed88ad1
--- /dev/null
+++ b/OpenSim/Region/CoreModules/World/Archiver/ArchiveScenesGroup.cs
@@ -0,0 +1,176 @@
+/*
+ * 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.Collections.Generic;
+using System.Linq;
+using System.Text;
+using OpenSim.Region.Framework.Scenes;
+using OpenMetaverse;
+using System.Drawing;
+
+namespace OpenSim.Region.CoreModules.World.Archiver
+{
+ ///
+ /// A group of regions arranged in a rectangle, possibly with holes.
+ ///
+ ///
+ /// The regions usually (but not necessarily) belong to an archive file, in which case we
+ /// store additional information used to create the archive (e.g., each region's
+ /// directory within the archive).
+ ///
+ public class ArchiveScenesGroup
+ {
+ ///
+ /// All the regions. The outer dictionary contains rows (key: Y coordinate).
+ /// The inner dictionaries contain each row's regions (key: X coordinate).
+ ///
+ public SortedDictionary> Regions { get; set; }
+
+ ///
+ /// The subdirectory where each region is stored in the archive.
+ ///
+ protected Dictionary m_regionDirs;
+
+ ///
+ /// The grid coordinates of the regions' bounding box.
+ ///
+ public Rectangle Rect { get; set; }
+
+
+ public ArchiveScenesGroup()
+ {
+ Regions = new SortedDictionary>();
+ m_regionDirs = new Dictionary();
+ Rect = new Rectangle(0, 0, 0, 0);
+ }
+
+ public void AddScene(Scene scene)
+ {
+ uint x = scene.RegionInfo.RegionLocX;
+ uint y = scene.RegionInfo.RegionLocY;
+
+ SortedDictionary row;
+ if (!Regions.TryGetValue(y, out row))
+ {
+ row = new SortedDictionary();
+ Regions[y] = row;
+ }
+
+ row[x] = scene;
+ }
+
+ ///
+ /// Called after all the scenes have been added. Performs calculations that require
+ /// knowledge of all the scenes.
+ ///
+ public void CalcSceneLocations()
+ {
+ if (Regions.Count == 0)
+ return;
+
+ // Find the bounding rectangle
+
+ uint firstY = Regions.First().Key;
+ uint lastY = Regions.Last().Key;
+
+ uint? firstX = null;
+ uint? lastX = null;
+
+ foreach (SortedDictionary row in Regions.Values)
+ {
+ uint curFirstX = row.First().Key;
+ uint curLastX = row.Last().Key;
+
+ firstX = (firstX == null) ? curFirstX : (firstX < curFirstX) ? firstX : curFirstX;
+ lastX = (lastX == null) ? curLastX : (lastX > curLastX) ? lastX : curLastX;
+ }
+
+ Rect = new Rectangle((int)firstX, (int)firstY, (int)(lastY - firstY + 1), (int)(lastX - firstX + 1));
+
+
+ // Calculate the subdirectory in which each region will be stored in the archive
+
+ m_regionDirs.Clear();
+ ForEachScene(delegate(Scene scene)
+ {
+ // We add the region's coordinates to ensure uniqueness even if multiple regions have the same name
+ string path = string.Format("{0}_{1}_{2}",
+ scene.RegionInfo.RegionLocX - Rect.X + 1,
+ scene.RegionInfo.RegionLocY - Rect.Y + 1,
+ scene.RegionInfo.RegionName.Replace(' ', '_'));
+ m_regionDirs[scene.RegionInfo.RegionID] = path;
+ });
+ }
+
+ ///
+ /// Returns the subdirectory where the region is stored.
+ ///
+ ///
+ ///
+ public string GetRegionDir(UUID regionID)
+ {
+ return m_regionDirs[regionID];
+ }
+
+ ///
+ /// Performs an action on all the scenes in this order: rows from South to North,
+ /// and within each row West to East.
+ ///
+ ///
+ public void ForEachScene(Action action)
+ {
+ foreach (SortedDictionary row in Regions.Values)
+ {
+ foreach (Scene scene in row.Values)
+ {
+ action(scene);
+ }
+ }
+ }
+
+ ///
+ /// Returns the scene at position 'location'.
+ ///
+ /// A location in the grid
+ /// The scene at this location
+ /// Whether the scene was found
+ public bool TryGetScene(Point location, out Scene scene)
+ {
+ SortedDictionary row;
+ if (Regions.TryGetValue((uint)location.Y, out row))
+ {
+ if (row.TryGetValue((uint)location.X, out scene))
+ return true;
+ }
+
+ scene = null;
+ return false;
+ }
+
+ }
+}
diff --git a/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequest.cs b/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequest.cs
new file mode 100644
index 0000000000..d751b1c990
--- /dev/null
+++ b/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequest.cs
@@ -0,0 +1,634 @@
+/*
+ * 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.Collections.Generic;
+using System.IO;
+using System.IO.Compression;
+using System.Reflection;
+using System.Text.RegularExpressions;
+using System.Threading;
+using System.Xml;
+using log4net;
+using OpenMetaverse;
+using OpenSim.Framework;
+using OpenSim.Framework.Serialization;
+using OpenSim.Region.CoreModules.World.Terrain;
+using OpenSim.Region.Framework.Interfaces;
+using OpenSim.Region.Framework.Scenes;
+using Ionic.Zlib;
+using GZipStream = Ionic.Zlib.GZipStream;
+using CompressionMode = Ionic.Zlib.CompressionMode;
+using OpenSim.Framework.Serialization.External;
+
+namespace OpenSim.Region.CoreModules.World.Archiver
+{
+ ///
+ /// Prepare to write out an archive.
+ ///
+ public class ArchiveWriteRequest
+ {
+ private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+
+ ///
+ /// The minimum major version of OAR that we can write.
+ ///
+ public static int MIN_MAJOR_VERSION = 0;
+
+ ///
+ /// The maximum major version of OAR that we can write.
+ ///
+ public static int MAX_MAJOR_VERSION = 1;
+
+ ///
+ /// Whether we're saving a multi-region archive.
+ ///
+ public bool MultiRegionFormat { get; set; }
+
+ ///
+ /// Determine whether this archive will save assets. Default is true.
+ ///
+ public bool SaveAssets { get; set; }
+
+ ///
+ /// Determines which objects will be included in the archive, according to their permissions.
+ /// Default is null, meaning no permission checks.
+ ///
+ public string CheckPermissions { get; set; }
+
+ protected Scene m_rootScene;
+ protected Stream m_saveStream;
+ protected TarArchiveWriter m_archiveWriter;
+ protected Guid m_requestId;
+ protected Dictionary m_options;
+
+ ///
+ /// Constructor
+ ///
+ /// Calling module
+ /// The path to which to save data.
+ /// The id associated with this request
+ ///
+ /// If there was a problem opening a stream for the file specified by the savePath
+ ///
+ public ArchiveWriteRequest(Scene scene, string savePath, Guid requestId) : this(scene, requestId)
+ {
+ try
+ {
+ m_saveStream = new GZipStream(new FileStream(savePath, FileMode.Create), CompressionMode.Compress, CompressionLevel.BestCompression);
+ }
+ catch (EntryPointNotFoundException e)
+ {
+ m_log.ErrorFormat(
+ "[ARCHIVER]: Mismatch between Mono and zlib1g library version when trying to create compression stream."
+ + "If you've manually installed Mono, have you appropriately updated zlib1g as well?");
+ m_log.ErrorFormat("{0} {1}", e.Message, e.StackTrace);
+ }
+ }
+
+ ///
+ /// Constructor.
+ ///
+ /// The root scene to archive
+ /// The stream to which to save data.
+ /// The id associated with this request
+ public ArchiveWriteRequest(Scene scene, Stream saveStream, Guid requestId) : this(scene, requestId)
+ {
+ m_saveStream = saveStream;
+ }
+
+ protected ArchiveWriteRequest(Scene scene, Guid requestId)
+ {
+ m_rootScene = scene;
+ m_requestId = requestId;
+ m_archiveWriter = null;
+
+ MultiRegionFormat = false;
+ SaveAssets = true;
+ CheckPermissions = null;
+ }
+
+ ///
+ /// Archive the region requested.
+ ///
+ /// if there was an io problem with creating the file
+ public void ArchiveRegion(Dictionary options)
+ {
+ m_options = options;
+
+ if (options.ContainsKey("all") && (bool)options["all"])
+ MultiRegionFormat = true;
+
+ if (options.ContainsKey("noassets") && (bool)options["noassets"])
+ SaveAssets = false;
+
+ Object temp;
+ if (options.TryGetValue("checkPermissions", out temp))
+ CheckPermissions = (string)temp;
+
+
+ // Find the regions to archive
+ ArchiveScenesGroup scenesGroup = new ArchiveScenesGroup();
+ if (MultiRegionFormat)
+ {
+ m_log.InfoFormat("[ARCHIVER]: Saving {0} regions", SceneManager.Instance.Scenes.Count);
+ SceneManager.Instance.ForEachScene(delegate(Scene scene)
+ {
+ scenesGroup.AddScene(scene);
+ });
+ }
+ else
+ {
+ scenesGroup.AddScene(m_rootScene);
+ }
+ scenesGroup.CalcSceneLocations();
+
+
+ m_archiveWriter = new TarArchiveWriter(m_saveStream);
+
+ try
+ {
+ // Write out control file. It should be first so that it will be found ASAP when loading the file.
+ m_archiveWriter.WriteFile(ArchiveConstants.CONTROL_FILE_PATH, CreateControlFile(scenesGroup));
+ m_log.InfoFormat("[ARCHIVER]: Added control file to archive.");
+
+ // Archive the regions
+
+ Dictionary assetUuids = new Dictionary();
+
+ scenesGroup.ForEachScene(delegate(Scene scene)
+ {
+ string regionDir = MultiRegionFormat ? scenesGroup.GetRegionDir(scene.RegionInfo.RegionID) : "";
+ ArchiveOneRegion(scene, regionDir, assetUuids);
+ });
+
+ // Archive the assets
+
+ if (SaveAssets)
+ {
+ m_log.DebugFormat("[ARCHIVER]: Saving {0} assets", assetUuids.Count);
+
+ // Asynchronously request all the assets required to perform this archive operation
+ AssetsRequest ar
+ = new AssetsRequest(
+ new AssetsArchiver(m_archiveWriter), assetUuids,
+ m_rootScene.AssetService, m_rootScene.UserAccountService,
+ m_rootScene.RegionInfo.ScopeID, options, ReceivedAllAssets);
+
+ Util.FireAndForget(o => ar.Execute());
+
+ // CloseArchive() will be called from ReceivedAllAssets()
+ }
+ else
+ {
+ m_log.DebugFormat("[ARCHIVER]: Not saving assets since --noassets was specified");
+ CloseArchive(string.Empty);
+ }
+ }
+ catch (Exception e)
+ {
+ CloseArchive(e.Message);
+ throw;
+ }
+ }
+
+
+ private void ArchiveOneRegion(Scene scene, string regionDir, Dictionary assetUuids)
+ {
+ m_log.InfoFormat("[ARCHIVER]: Writing region {0}", scene.RegionInfo.RegionName);
+
+ EntityBase[] entities = scene.GetEntities();
+ List sceneObjects = new List();
+
+ int numObjectsSkippedPermissions = 0;
+
+ // Filter entities so that we only have scene objects.
+ // FIXME: Would be nicer to have this as a proper list in SceneGraph, since lots of methods
+ // end up having to do this
+ IPermissionsModule permissionsModule = scene.RequestModuleInterface();
+ foreach (EntityBase entity in entities)
+ {
+ if (entity is SceneObjectGroup)
+ {
+ SceneObjectGroup sceneObject = (SceneObjectGroup)entity;
+
+ if (!sceneObject.IsDeleted && !sceneObject.IsAttachment)
+ {
+ if (!CanUserArchiveObject(scene.RegionInfo.EstateSettings.EstateOwner, sceneObject, CheckPermissions, permissionsModule))
+ {
+ // The user isn't allowed to copy/transfer this object, so it will not be included in the OAR.
+ ++numObjectsSkippedPermissions;
+ }
+ else
+ {
+ sceneObjects.Add(sceneObject);
+ }
+ }
+ }
+ }
+
+ if (SaveAssets)
+ {
+ UuidGatherer assetGatherer = new UuidGatherer(scene.AssetService);
+ int prevAssets = assetUuids.Count;
+
+ foreach (SceneObjectGroup sceneObject in sceneObjects)
+ {
+ assetGatherer.GatherAssetUuids(sceneObject, assetUuids);
+ }
+
+ m_log.DebugFormat(
+ "[ARCHIVER]: {0} scene objects to serialize requiring save of {1} assets",
+ sceneObjects.Count, assetUuids.Count - prevAssets);
+ }
+
+ if (numObjectsSkippedPermissions > 0)
+ {
+ m_log.DebugFormat(
+ "[ARCHIVER]: {0} scene objects skipped due to lack of permissions",
+ numObjectsSkippedPermissions);
+ }
+
+ // Make sure that we also request terrain texture assets
+ RegionSettings regionSettings = scene.RegionInfo.RegionSettings;
+
+ if (regionSettings.TerrainTexture1 != RegionSettings.DEFAULT_TERRAIN_TEXTURE_1)
+ assetUuids[regionSettings.TerrainTexture1] = AssetType.Texture;
+
+ if (regionSettings.TerrainTexture2 != RegionSettings.DEFAULT_TERRAIN_TEXTURE_2)
+ assetUuids[regionSettings.TerrainTexture2] = AssetType.Texture;
+
+ if (regionSettings.TerrainTexture3 != RegionSettings.DEFAULT_TERRAIN_TEXTURE_3)
+ assetUuids[regionSettings.TerrainTexture3] = AssetType.Texture;
+
+ if (regionSettings.TerrainTexture4 != RegionSettings.DEFAULT_TERRAIN_TEXTURE_4)
+ assetUuids[regionSettings.TerrainTexture4] = AssetType.Texture;
+
+ Save(scene, sceneObjects, regionDir);
+ }
+
+ ///
+ /// Checks whether the user has permission to export an object group to an OAR.
+ ///
+ /// The user
+ /// The object group
+ /// Which permissions to check: "C" = Copy, "T" = Transfer
+ /// The scene's permissions module
+ /// Whether the user is allowed to export the object to an OAR
+ private bool CanUserArchiveObject(UUID user, SceneObjectGroup objGroup, string checkPermissions, IPermissionsModule permissionsModule)
+ {
+ if (checkPermissions == null)
+ return true;
+
+ if (permissionsModule == null)
+ return true; // this shouldn't happen
+
+ // Check whether the user is permitted to export all of the parts in the SOG. If any
+ // part can't be exported then the entire SOG can't be exported.
+
+ bool permitted = true;
+ //int primNumber = 1;
+
+ foreach (SceneObjectPart obj in objGroup.Parts)
+ {
+ uint perm;
+ PermissionClass permissionClass = permissionsModule.GetPermissionClass(user, obj);
+ switch (permissionClass)
+ {
+ case PermissionClass.Owner:
+ perm = obj.BaseMask;
+ break;
+ case PermissionClass.Group:
+ perm = obj.GroupMask | obj.EveryoneMask;
+ break;
+ case PermissionClass.Everyone:
+ default:
+ perm = obj.EveryoneMask;
+ break;
+ }
+
+ bool canCopy = (perm & (uint)PermissionMask.Copy) != 0;
+ bool canTransfer = (perm & (uint)PermissionMask.Transfer) != 0;
+
+ // Special case: if Everyone can copy the object then this implies it can also be
+ // Transferred.
+ // However, if the user is the Owner then we don't check EveryoneMask, because it seems that the mask
+ // always (incorrectly) includes the Copy bit set in this case. But that's a mistake: the viewer
+ // does NOT show that the object has Everyone-Copy permissions, and doesn't allow it to be copied.
+ if (permissionClass != PermissionClass.Owner)
+ canTransfer |= (obj.EveryoneMask & (uint)PermissionMask.Copy) != 0;
+
+ bool partPermitted = true;
+ if (checkPermissions.Contains("C") && !canCopy)
+ partPermitted = false;
+ if (checkPermissions.Contains("T") && !canTransfer)
+ partPermitted = false;
+
+ // If the user is the Creator of the object then it can always be included in the OAR
+ bool creator = (obj.CreatorID.Guid == user.Guid);
+ if (creator)
+ partPermitted = true;
+
+ //string name = (objGroup.PrimCount == 1) ? objGroup.Name : string.Format("{0} ({1}/{2})", obj.Name, primNumber, objGroup.PrimCount);
+ //m_log.DebugFormat("[ARCHIVER]: Object permissions: {0}: Base={1:X4}, Owner={2:X4}, Everyone={3:X4}, permissionClass={4}, checkPermissions={5}, canCopy={6}, canTransfer={7}, creator={8}, permitted={9}",
+ // name, obj.BaseMask, obj.OwnerMask, obj.EveryoneMask,
+ // permissionClass, checkPermissions, canCopy, canTransfer, creator, partPermitted);
+
+ if (!partPermitted)
+ {
+ permitted = false;
+ break;
+ }
+
+ //++primNumber;
+ }
+
+ return permitted;
+ }
+
+ ///
+ /// Create the control file.
+ ///
+ ///
+ public string CreateControlFile(ArchiveScenesGroup scenesGroup)
+ {
+ int majorVersion;
+ int minorVersion;
+
+ if (MultiRegionFormat)
+ {
+ majorVersion = MAX_MAJOR_VERSION;
+ minorVersion = 0;
+ }
+ else
+ {
+ // To support older versions of OpenSim, we continue to create single-region OARs
+ // using the old file format. In the future this format will be discontinued.
+ majorVersion = 0;
+ minorVersion = 8;
+ }
+//
+// if (m_options.ContainsKey("version"))
+// {
+// string[] parts = m_options["version"].ToString().Split('.');
+// if (parts.Length >= 1)
+// {
+// majorVersion = Int32.Parse(parts[0]);
+//
+// if (parts.Length >= 2)
+// minorVersion = Int32.Parse(parts[1]);
+// }
+// }
+//
+// if (majorVersion < MIN_MAJOR_VERSION || majorVersion > MAX_MAJOR_VERSION)
+// {
+// throw new Exception(
+// string.Format(
+// "OAR version number for save must be between {0} and {1}",
+// MIN_MAJOR_VERSION, MAX_MAJOR_VERSION));
+// }
+// else if (majorVersion == MAX_MAJOR_VERSION)
+// {
+// // Force 1.0
+// minorVersion = 0;
+// }
+// else if (majorVersion == MIN_MAJOR_VERSION)
+// {
+// // Force 0.4
+// minorVersion = 4;
+// }
+
+ m_log.InfoFormat("[ARCHIVER]: Creating version {0}.{1} OAR", majorVersion, minorVersion);
+ if (majorVersion == 1)
+ {
+ m_log.WarnFormat("[ARCHIVER]: Please be aware that version 1.0 OARs are not compatible with OpenSim versions prior to 0.7.4. Do not use the --all option if you want to produce a compatible OAR");
+ }
+
+ String s;
+
+ using (StringWriter sw = new StringWriter())
+ {
+ using (XmlTextWriter xtw = new XmlTextWriter(sw))
+ {
+ xtw.Formatting = Formatting.Indented;
+ xtw.WriteStartDocument();
+ xtw.WriteStartElement("archive");
+ xtw.WriteAttributeString("major_version", majorVersion.ToString());
+ xtw.WriteAttributeString("minor_version", minorVersion.ToString());
+
+ xtw.WriteStartElement("creation_info");
+ DateTime now = DateTime.UtcNow;
+ TimeSpan t = now - new DateTime(1970, 1, 1);
+ xtw.WriteElementString("datetime", ((int)t.TotalSeconds).ToString());
+ if (!MultiRegionFormat)
+ xtw.WriteElementString("id", m_rootScene.RegionInfo.RegionID.ToString());
+ xtw.WriteEndElement();
+
+ xtw.WriteElementString("assets_included", SaveAssets.ToString());
+
+ if (MultiRegionFormat)
+ {
+ WriteRegionsManifest(scenesGroup, xtw);
+ }
+ else
+ {
+ xtw.WriteStartElement("region_info");
+ WriteRegionInfo(m_rootScene, xtw);
+ xtw.WriteEndElement();
+ }
+
+ xtw.WriteEndElement();
+
+ xtw.Flush();
+ }
+
+ s = sw.ToString();
+ }
+
+ return s;
+ }
+
+ ///
+ /// Writes the list of regions included in a multi-region OAR.
+ ///
+ private static void WriteRegionsManifest(ArchiveScenesGroup scenesGroup, XmlTextWriter xtw)
+ {
+ xtw.WriteStartElement("regions");
+
+ // Write the regions in order: rows from South to North, then regions from West to East.
+ // The list of regions can have "holes"; we write empty elements in their position.
+
+ for (uint y = (uint)scenesGroup.Rect.Top; y < scenesGroup.Rect.Bottom; ++y)
+ {
+ SortedDictionary row;
+ if (scenesGroup.Regions.TryGetValue(y, out row))
+ {
+ xtw.WriteStartElement("row");
+
+ for (uint x = (uint)scenesGroup.Rect.Left; x < scenesGroup.Rect.Right; ++x)
+ {
+ Scene scene;
+ if (row.TryGetValue(x, out scene))
+ {
+ xtw.WriteStartElement("region");
+ xtw.WriteElementString("id", scene.RegionInfo.RegionID.ToString());
+ xtw.WriteElementString("dir", scenesGroup.GetRegionDir(scene.RegionInfo.RegionID));
+ WriteRegionInfo(scene, xtw);
+ xtw.WriteEndElement();
+ }
+ else
+ {
+ // Write a placeholder for a missing region
+ xtw.WriteElementString("region", "");
+ }
+ }
+
+ xtw.WriteEndElement();
+ }
+ else
+ {
+ // Write a placeholder for a missing row
+ xtw.WriteElementString("row", "");
+ }
+ }
+
+ xtw.WriteEndElement(); // "regions"
+ }
+
+ protected static void WriteRegionInfo(Scene scene, XmlTextWriter xtw)
+ {
+ bool isMegaregion;
+ Vector2 size;
+
+ IRegionCombinerModule rcMod = scene.RequestModuleInterface();
+
+ if (rcMod != null)
+ isMegaregion = rcMod.IsRootForMegaregion(scene.RegionInfo.RegionID);
+ else
+ isMegaregion = false;
+
+ if (isMegaregion)
+ size = rcMod.GetSizeOfMegaregion(scene.RegionInfo.RegionID);
+ else
+ size = new Vector2((float)Constants.RegionSize, (float)Constants.RegionSize);
+
+ xtw.WriteElementString("is_megaregion", isMegaregion.ToString());
+ xtw.WriteElementString("size_in_meters", string.Format("{0},{1}", size.X, size.Y));
+ }
+
+
+ protected void Save(Scene scene, List sceneObjects, string regionDir)
+ {
+ if (regionDir != string.Empty)
+ regionDir = ArchiveConstants.REGIONS_PATH + regionDir + "/";
+
+ m_log.InfoFormat("[ARCHIVER]: Adding region settings to archive.");
+
+ // Write out region settings
+ string settingsPath = String.Format("{0}{1}{2}.xml",
+ regionDir, ArchiveConstants.SETTINGS_PATH, scene.RegionInfo.RegionName);
+ m_archiveWriter.WriteFile(settingsPath, RegionSettingsSerializer.Serialize(scene.RegionInfo.RegionSettings));
+
+ m_log.InfoFormat("[ARCHIVER]: Adding parcel settings to archive.");
+
+ // Write out land data (aka parcel) settings
+ List landObjects = scene.LandChannel.AllParcels();
+ foreach (ILandObject lo in landObjects)
+ {
+ LandData landData = lo.LandData;
+ string landDataPath = String.Format("{0}{1}{2}.xml",
+ regionDir, ArchiveConstants.LANDDATA_PATH, landData.GlobalID.ToString());
+ m_archiveWriter.WriteFile(landDataPath, LandDataSerializer.Serialize(landData, m_options));
+ }
+
+ m_log.InfoFormat("[ARCHIVER]: Adding terrain information to archive.");
+
+ // Write out terrain
+ string terrainPath = String.Format("{0}{1}{2}.r32",
+ regionDir, ArchiveConstants.TERRAINS_PATH, scene.RegionInfo.RegionName);
+
+ MemoryStream ms = new MemoryStream();
+ scene.RequestModuleInterface().SaveToStream(terrainPath, ms);
+ m_archiveWriter.WriteFile(terrainPath, ms.ToArray());
+ ms.Close();
+
+ m_log.InfoFormat("[ARCHIVER]: Adding scene objects to archive.");
+
+ // Write out scene object metadata
+ IRegionSerialiserModule serializer = scene.RequestModuleInterface();
+ foreach (SceneObjectGroup sceneObject in sceneObjects)
+ {
+ //m_log.DebugFormat("[ARCHIVER]: Saving {0} {1}, {2}", entity.Name, entity.UUID, entity.GetType());
+
+ string serializedObject = serializer.SerializeGroupToXml2(sceneObject, m_options);
+ string objectPath = string.Format("{0}{1}", regionDir, ArchiveHelpers.CreateObjectPath(sceneObject));
+ m_archiveWriter.WriteFile(objectPath, serializedObject);
+ }
+ }
+
+ protected void ReceivedAllAssets(
+ ICollection assetsFoundUuids, ICollection assetsNotFoundUuids)
+ {
+ foreach (UUID uuid in assetsNotFoundUuids)
+ {
+ m_log.DebugFormat("[ARCHIVER]: Could not find asset {0}", uuid);
+ }
+
+ // m_log.InfoFormat(
+ // "[ARCHIVER]: Received {0} of {1} assets requested",
+ // assetsFoundUuids.Count, assetsFoundUuids.Count + assetsNotFoundUuids.Count);
+
+ CloseArchive(String.Empty);
+ }
+
+
+ ///
+ /// Closes the archive and notifies that we're done.
+ ///
+ /// The error that occurred, or empty for success
+ protected void CloseArchive(string errorMessage)
+ {
+ try
+ {
+ if (m_archiveWriter != null)
+ m_archiveWriter.Close();
+ m_saveStream.Close();
+ }
+ catch (Exception e)
+ {
+ m_log.Error(string.Format("[ARCHIVER]: Error closing archive: {0} ", e.Message), e);
+ if (errorMessage == string.Empty)
+ errorMessage = e.Message;
+ }
+
+ m_log.InfoFormat("[ARCHIVER]: Finished writing out OAR for {0}", m_rootScene.RegionInfo.RegionName);
+
+ m_rootScene.EventManager.TriggerOarFileSaved(m_requestId, errorMessage);
+ }
+
+ }
+}
diff --git a/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequestExecution.cs b/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequestExecution.cs
deleted file mode 100644
index 0780d86ed5..0000000000
--- a/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequestExecution.cs
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * 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.Collections.Generic;
-using System.IO;
-using System.Reflection;
-using System.Xml;
-using log4net;
-using OpenMetaverse;
-using OpenSim.Framework;
-using OpenSim.Framework.Serialization;
-using OpenSim.Framework.Serialization.External;
-using OpenSim.Region.CoreModules.World.Terrain;
-using OpenSim.Region.Framework.Interfaces;
-using OpenSim.Region.Framework.Scenes;
-
-namespace OpenSim.Region.CoreModules.World.Archiver
-{
- ///
- /// Method called when all the necessary assets for an archive request have been received.
- ///
- public delegate void AssetsRequestCallback(
- ICollection assetsFoundUuids, ICollection assetsNotFoundUuids);
-
- ///
- /// Execute the write of an archive once we have received all the necessary data
- ///
- public class ArchiveWriteRequestExecution
- {
- private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
-
- protected ITerrainModule m_terrainModule;
- protected IRegionSerialiserModule m_serialiser;
- protected List m_sceneObjects;
- protected Scene m_scene;
- protected TarArchiveWriter m_archiveWriter;
- protected Guid m_requestId;
- protected Dictionary m_options;
-
- public ArchiveWriteRequestExecution(
- List sceneObjects,
- ITerrainModule terrainModule,
- IRegionSerialiserModule serialiser,
- Scene scene,
- TarArchiveWriter archiveWriter,
- Guid requestId,
- Dictionary options)
- {
- m_sceneObjects = sceneObjects;
- m_terrainModule = terrainModule;
- m_serialiser = serialiser;
- m_scene = scene;
- m_archiveWriter = archiveWriter;
- m_requestId = requestId;
- m_options = options;
- }
-
- protected internal void ReceivedAllAssets(
- ICollection assetsFoundUuids, ICollection assetsNotFoundUuids)
- {
- try
- {
- Save(assetsFoundUuids, assetsNotFoundUuids);
- }
- finally
- {
- m_archiveWriter.Close();
- }
-
- m_log.InfoFormat("[ARCHIVER]: Finished writing out OAR for {0}", m_scene.RegionInfo.RegionName);
-
- m_scene.EventManager.TriggerOarFileSaved(m_requestId, String.Empty);
- }
-
- protected internal void Save(ICollection assetsFoundUuids, ICollection assetsNotFoundUuids)
- {
- foreach (UUID uuid in assetsNotFoundUuids)
- {
- m_log.DebugFormat("[ARCHIVER]: Could not find asset {0}", uuid);
- }
-
-// m_log.InfoFormat(
-// "[ARCHIVER]: Received {0} of {1} assets requested",
-// assetsFoundUuids.Count, assetsFoundUuids.Count + assetsNotFoundUuids.Count);
-
- m_log.InfoFormat("[ARCHIVER]: Adding region settings to archive.");
-
- // Write out region settings
- string settingsPath
- = String.Format("{0}{1}.xml", ArchiveConstants.SETTINGS_PATH, m_scene.RegionInfo.RegionName);
- m_archiveWriter.WriteFile(settingsPath, RegionSettingsSerializer.Serialize(m_scene.RegionInfo.RegionSettings));
-
- m_log.InfoFormat("[ARCHIVER]: Adding parcel settings to archive.");
-
- // Write out land data (aka parcel) settings
- ListlandObjects = m_scene.LandChannel.AllParcels();
- foreach (ILandObject lo in landObjects)
- {
- LandData landData = lo.LandData;
- string landDataPath = String.Format("{0}{1}.xml", ArchiveConstants.LANDDATA_PATH,
- landData.GlobalID.ToString());
- m_archiveWriter.WriteFile(landDataPath, LandDataSerializer.Serialize(landData, m_options));
- }
-
- m_log.InfoFormat("[ARCHIVER]: Adding terrain information to archive.");
-
- // Write out terrain
- string terrainPath
- = String.Format("{0}{1}.r32", ArchiveConstants.TERRAINS_PATH, m_scene.RegionInfo.RegionName);
-
- MemoryStream ms = new MemoryStream();
- m_terrainModule.SaveToStream(terrainPath, ms);
- m_archiveWriter.WriteFile(terrainPath, ms.ToArray());
- ms.Close();
-
- m_log.InfoFormat("[ARCHIVER]: Adding scene objects to archive.");
-
- // Write out scene object metadata
- foreach (SceneObjectGroup sceneObject in m_sceneObjects)
- {
- //m_log.DebugFormat("[ARCHIVER]: Saving {0} {1}, {2}", entity.Name, entity.UUID, entity.GetType());
-
- string serializedObject = m_serialiser.SerializeGroupToXml2(sceneObject, m_options);
- m_archiveWriter.WriteFile(ArchiveHelpers.CreateObjectPath(sceneObject), serializedObject);
- }
- }
- }
-}
\ No newline at end of file
diff --git a/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequestPreparation.cs b/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequestPreparation.cs
deleted file mode 100644
index 4edaaca594..0000000000
--- a/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequestPreparation.cs
+++ /dev/null
@@ -1,438 +0,0 @@
-/*
- * 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.Collections.Generic;
-using System.IO;
-using System.IO.Compression;
-using System.Reflection;
-using System.Text.RegularExpressions;
-using System.Threading;
-using System.Xml;
-using log4net;
-using OpenMetaverse;
-using OpenSim.Framework;
-using OpenSim.Framework.Serialization;
-using OpenSim.Region.CoreModules.World.Terrain;
-using OpenSim.Region.Framework.Interfaces;
-using OpenSim.Region.Framework.Scenes;
-using Ionic.Zlib;
-using GZipStream = Ionic.Zlib.GZipStream;
-using CompressionMode = Ionic.Zlib.CompressionMode;
-
-namespace OpenSim.Region.CoreModules.World.Archiver
-{
- ///
- /// Prepare to write out an archive.
- ///
- public class ArchiveWriteRequestPreparation
- {
- private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
-
- ///
- /// The minimum major version of OAR that we can write.
- ///
- public static int MIN_MAJOR_VERSION = 0;
-
- ///
- /// The maximum major version of OAR that we can write.
- ///
- public static int MAX_MAJOR_VERSION = 0;
-
- ///
- /// Determine whether this archive will save assets. Default is true.
- ///
- public bool SaveAssets { get; set; }
-
- protected ArchiverModule m_module;
- protected Scene m_scene;
- protected Stream m_saveStream;
- protected Guid m_requestId;
-
- ///
- /// Constructor
- ///
- /// Calling module
- /// The path to which to save data.
- /// The id associated with this request
- ///
- /// If there was a problem opening a stream for the file specified by the savePath
- ///
- public ArchiveWriteRequestPreparation(ArchiverModule module, string savePath, Guid requestId) : this(module, requestId)
- {
- try
- {
- m_saveStream = new GZipStream(new FileStream(savePath, FileMode.Create), CompressionMode.Compress, CompressionLevel.BestCompression);
- }
- catch (EntryPointNotFoundException e)
- {
- m_log.ErrorFormat(
- "[ARCHIVER]: Mismatch between Mono and zlib1g library version when trying to create compression stream."
- + "If you've manually installed Mono, have you appropriately updated zlib1g as well?");
- m_log.ErrorFormat("{0} {1}", e.Message, e.StackTrace);
- }
- }
-
- ///
- /// Constructor.
- ///
- /// Calling module
- /// The stream to which to save data.
- /// The id associated with this request
- public ArchiveWriteRequestPreparation(ArchiverModule module, Stream saveStream, Guid requestId) : this(module, requestId)
- {
- m_saveStream = saveStream;
- }
-
- protected ArchiveWriteRequestPreparation(ArchiverModule module, Guid requestId)
- {
- m_module = module;
-
- // FIXME: This is only here for regression test purposes since they do not supply a module. Need to fix
- // this.
- if (m_module != null)
- m_scene = m_module.Scene;
-
- m_requestId = requestId;
-
- SaveAssets = true;
- }
-
- ///
- /// Archive the region requested.
- ///
- /// if there was an io problem with creating the file
- public void ArchiveRegion(Dictionary options)
- {
- if (options.ContainsKey("noassets") && (bool)options["noassets"])
- SaveAssets = false;
-
- try
- {
- Dictionary assetUuids = new Dictionary();
-
- EntityBase[] entities = m_scene.GetEntities();
- List sceneObjects = new List();
-
- string checkPermissions = null;
- int numObjectsSkippedPermissions = 0;
- Object temp;
- if (options.TryGetValue("checkPermissions", out temp))
- checkPermissions = (string)temp;
-
- // Filter entities so that we only have scene objects.
- // FIXME: Would be nicer to have this as a proper list in SceneGraph, since lots of methods
- // end up having to do this
- foreach (EntityBase entity in entities)
- {
- if (entity is SceneObjectGroup)
- {
- SceneObjectGroup sceneObject = (SceneObjectGroup)entity;
-
- if (!sceneObject.IsDeleted && !sceneObject.IsAttachment)
- {
- if (!CanUserArchiveObject(m_scene.RegionInfo.EstateSettings.EstateOwner, sceneObject, checkPermissions))
- {
- // The user isn't allowed to copy/transfer this object, so it will not be included in the OAR.
- ++numObjectsSkippedPermissions;
- }
- else
- {
- sceneObjects.Add(sceneObject);
- }
- }
- }
- }
-
- if (SaveAssets)
- {
- UuidGatherer assetGatherer = new UuidGatherer(m_scene.AssetService);
-
- foreach (SceneObjectGroup sceneObject in sceneObjects)
- {
- assetGatherer.GatherAssetUuids(sceneObject, assetUuids);
- }
-
- m_log.DebugFormat(
- "[ARCHIVER]: {0} scene objects to serialize requiring save of {1} assets",
- sceneObjects.Count, assetUuids.Count);
- }
- else
- {
- m_log.DebugFormat("[ARCHIVER]: Not saving assets since --noassets was specified");
- }
-
- if (numObjectsSkippedPermissions > 0)
- {
- m_log.DebugFormat(
- "[ARCHIVER]: {0} scene objects skipped due to lack of permissions",
- numObjectsSkippedPermissions);
- }
-
- // Make sure that we also request terrain texture assets
- RegionSettings regionSettings = m_scene.RegionInfo.RegionSettings;
-
- if (regionSettings.TerrainTexture1 != RegionSettings.DEFAULT_TERRAIN_TEXTURE_1)
- assetUuids[regionSettings.TerrainTexture1] = AssetType.Texture;
-
- if (regionSettings.TerrainTexture2 != RegionSettings.DEFAULT_TERRAIN_TEXTURE_2)
- assetUuids[regionSettings.TerrainTexture2] = AssetType.Texture;
-
- if (regionSettings.TerrainTexture3 != RegionSettings.DEFAULT_TERRAIN_TEXTURE_3)
- assetUuids[regionSettings.TerrainTexture3] = AssetType.Texture;
-
- if (regionSettings.TerrainTexture4 != RegionSettings.DEFAULT_TERRAIN_TEXTURE_4)
- assetUuids[regionSettings.TerrainTexture4] = AssetType.Texture;
-
- TarArchiveWriter archiveWriter = new TarArchiveWriter(m_saveStream);
-
- // Asynchronously request all the assets required to perform this archive operation
- ArchiveWriteRequestExecution awre
- = new ArchiveWriteRequestExecution(
- sceneObjects,
- m_scene.RequestModuleInterface(),
- m_scene.RequestModuleInterface(),
- m_scene,
- archiveWriter,
- m_requestId,
- options);
-
- m_log.InfoFormat("[ARCHIVER]: Creating archive file. This may take some time.");
-
- // Write out control file. This has to be done first so that subsequent loaders will see this file first
- // XXX: I know this is a weak way of doing it since external non-OAR aware tar executables will not do this
- archiveWriter.WriteFile(ArchiveConstants.CONTROL_FILE_PATH, CreateControlFile(options));
- m_log.InfoFormat("[ARCHIVER]: Added control file to archive.");
-
- if (SaveAssets)
- {
- AssetsRequest ar
- = new AssetsRequest(
- new AssetsArchiver(archiveWriter), assetUuids,
- m_scene.AssetService, m_scene.UserAccountService,
- m_scene.RegionInfo.ScopeID, options, awre.ReceivedAllAssets);
-
- Util.FireAndForget(o => ar.Execute());
- }
- else
- {
- awre.ReceivedAllAssets(new List(), new List());
- }
- }
- catch (Exception)
- {
- m_saveStream.Close();
- throw;
- }
- }
-
- ///
- /// Checks whether the user has permission to export an object group to an OAR.
- ///
- /// The user
- /// The object group
- /// Which permissions to check: "C" = Copy, "T" = Transfer
- /// Whether the user is allowed to export the object to an OAR
- private bool CanUserArchiveObject(UUID user, SceneObjectGroup objGroup, string checkPermissions)
- {
- if (checkPermissions == null)
- return true;
-
- IPermissionsModule module = m_scene.RequestModuleInterface();
- if (module == null)
- return true; // this shouldn't happen
-
- // Check whether the user is permitted to export all of the parts in the SOG. If any
- // part can't be exported then the entire SOG can't be exported.
-
- bool permitted = true;
- //int primNumber = 1;
-
- foreach (SceneObjectPart obj in objGroup.Parts)
- {
- uint perm;
- PermissionClass permissionClass = module.GetPermissionClass(user, obj);
- switch (permissionClass)
- {
- case PermissionClass.Owner:
- perm = obj.BaseMask;
- break;
- case PermissionClass.Group:
- perm = obj.GroupMask | obj.EveryoneMask;
- break;
- case PermissionClass.Everyone:
- default:
- perm = obj.EveryoneMask;
- break;
- }
-
- bool canCopy = (perm & (uint)PermissionMask.Copy) != 0;
- bool canTransfer = (perm & (uint)PermissionMask.Transfer) != 0;
-
- // Special case: if Everyone can copy the object then this implies it can also be
- // Transferred.
- // However, if the user is the Owner then we don't check EveryoneMask, because it seems that the mask
- // always (incorrectly) includes the Copy bit set in this case. But that's a mistake: the viewer
- // does NOT show that the object has Everyone-Copy permissions, and doesn't allow it to be copied.
- if (permissionClass != PermissionClass.Owner)
- canTransfer |= (obj.EveryoneMask & (uint)PermissionMask.Copy) != 0;
-
- bool partPermitted = true;
- if (checkPermissions.Contains("C") && !canCopy)
- partPermitted = false;
- if (checkPermissions.Contains("T") && !canTransfer)
- partPermitted = false;
-
- // If the user is the Creator of the object then it can always be included in the OAR
- bool creator = (obj.CreatorID.Guid == user.Guid);
- if (creator)
- partPermitted = true;
-
- //string name = (objGroup.PrimCount == 1) ? objGroup.Name : string.Format("{0} ({1}/{2})", obj.Name, primNumber, objGroup.PrimCount);
- //m_log.DebugFormat("[ARCHIVER]: Object permissions: {0}: Base={1:X4}, Owner={2:X4}, Everyone={3:X4}, permissionClass={4}, checkPermissions={5}, canCopy={6}, canTransfer={7}, creator={8}, permitted={9}",
- // name, obj.BaseMask, obj.OwnerMask, obj.EveryoneMask,
- // permissionClass, checkPermissions, canCopy, canTransfer, creator, partPermitted);
-
- if (!partPermitted)
- {
- permitted = false;
- break;
- }
-
- //++primNumber;
- }
-
- return permitted;
- }
-
- ///
- /// Create the control file for the most up to date archive
- ///
- ///
- public string CreateControlFile(Dictionary options)
- {
- int majorVersion = MAX_MAJOR_VERSION, minorVersion = 8;
-//
-// if (options.ContainsKey("version"))
-// {
-// string[] parts = options["version"].ToString().Split('.');
-// if (parts.Length >= 1)
-// {
-// majorVersion = Int32.Parse(parts[0]);
-//
-// if (parts.Length >= 2)
-// minorVersion = Int32.Parse(parts[1]);
-// }
-// }
-//
-// if (majorVersion < MIN_MAJOR_VERSION || majorVersion > MAX_MAJOR_VERSION)
-// {
-// throw new Exception(
-// string.Format(
-// "OAR version number for save must be between {0} and {1}",
-// MIN_MAJOR_VERSION, MAX_MAJOR_VERSION));
-// }
-// else if (majorVersion == MAX_MAJOR_VERSION)
-// {
-// // Force 1.0
-// minorVersion = 0;
-// }
-// else if (majorVersion == MIN_MAJOR_VERSION)
-// {
-// // Force 0.4
-// minorVersion = 4;
-// }
-
- m_log.InfoFormat("[ARCHIVER]: Creating version {0}.{1} OAR", majorVersion, minorVersion);
- //if (majorVersion == 1)
- //{
- // m_log.WarnFormat("[ARCHIVER]: Please be aware that version 1.0 OARs are not compatible with OpenSim 0.7.0.2 and earlier. Please use the --version=0 option if you want to produce a compatible OAR");
- //}
-
- String s;
-
- using (StringWriter sw = new StringWriter())
- {
- using (XmlTextWriter xtw = new XmlTextWriter(sw))
- {
- xtw.Formatting = Formatting.Indented;
- xtw.WriteStartDocument();
- xtw.WriteStartElement("archive");
- xtw.WriteAttributeString("major_version", majorVersion.ToString());
- xtw.WriteAttributeString("minor_version", minorVersion.ToString());
-
- xtw.WriteStartElement("creation_info");
- DateTime now = DateTime.UtcNow;
- TimeSpan t = now - new DateTime(1970, 1, 1);
- xtw.WriteElementString("datetime", ((int)t.TotalSeconds).ToString());
- xtw.WriteElementString("id", UUID.Random().ToString());
- xtw.WriteEndElement();
-
- xtw.WriteStartElement("region_info");
-
- bool isMegaregion;
- Vector2 size;
- IRegionCombinerModule rcMod = null;
-
- // FIXME: This is only here for regression test purposes since they do not supply a module. Need to fix
- // this, possibly by doing control file creation somewhere else.
- if (m_module != null)
- rcMod = m_module.RegionCombinerModule;
-
- if (rcMod != null)
- isMegaregion = rcMod.IsRootForMegaregion(m_scene.RegionInfo.RegionID);
- else
- isMegaregion = false;
-
- if (isMegaregion)
- size = rcMod.GetSizeOfMegaregion(m_scene.RegionInfo.RegionID);
- else
- size = new Vector2((float)Constants.RegionSize, (float)Constants.RegionSize);
-
- xtw.WriteElementString("is_megaregion", isMegaregion.ToString());
- xtw.WriteElementString("size_in_meters", string.Format("{0},{1}", size.X, size.Y));
-
- xtw.WriteEndElement();
-
- xtw.WriteElementString("assets_included", SaveAssets.ToString());
-
- xtw.WriteEndElement();
-
- xtw.Flush();
- }
-
- s = sw.ToString();
- }
-
-// if (m_scene != null)
-// Console.WriteLine(
-// "[ARCHIVE WRITE REQUEST PREPARATION]: Control file for {0} is: {1}", m_scene.RegionInfo.RegionName, s);
-
- return s;
- }
- }
-}
diff --git a/OpenSim/Region/CoreModules/World/Archiver/ArchiverModule.cs b/OpenSim/Region/CoreModules/World/Archiver/ArchiverModule.cs
index bf3b1240a4..2a87dc2a5f 100644
--- a/OpenSim/Region/CoreModules/World/Archiver/ArchiverModule.cs
+++ b/OpenSim/Region/CoreModules/World/Archiver/ArchiverModule.cs
@@ -146,6 +146,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver
ops.Add("noassets", delegate(string v) { options["noassets"] = v != null; });
ops.Add("publish", v => options["wipe-owners"] = v != null);
ops.Add("perm=", delegate(string v) { options["checkPermissions"] = v; });
+ ops.Add("all", delegate(string v) { options["all"] = v != null; });
List mainParams = ops.Parse(cmdparams);
@@ -169,7 +170,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver
m_log.InfoFormat(
"[ARCHIVER]: Writing archive for region {0} to {1}", Scene.RegionInfo.RegionName, savePath);
- new ArchiveWriteRequestPreparation(this, savePath, requestId).ArchiveRegion(options);
+ new ArchiveWriteRequest(Scene, savePath, requestId).ArchiveRegion(options);
}
public void ArchiveRegion(Stream saveStream)
@@ -184,7 +185,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver
public void ArchiveRegion(Stream saveStream, Guid requestId, Dictionary options)
{
- new ArchiveWriteRequestPreparation(this, saveStream, requestId).ArchiveRegion(options);
+ new ArchiveWriteRequest(Scene, saveStream, requestId).ArchiveRegion(options);
}
public void DearchiveRegion(string loadPath)
diff --git a/OpenSim/Region/CoreModules/World/Archiver/AssetsRequest.cs b/OpenSim/Region/CoreModules/World/Archiver/AssetsRequest.cs
index 89e9593ba3..bf58591dcd 100644
--- a/OpenSim/Region/CoreModules/World/Archiver/AssetsRequest.cs
+++ b/OpenSim/Region/CoreModules/World/Archiver/AssetsRequest.cs
@@ -46,6 +46,12 @@ namespace OpenSim.Region.CoreModules.World.Archiver
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+ ///
+ /// Method called when all the necessary assets for an archive request have been received.
+ ///
+ public delegate void AssetsRequestCallback(
+ ICollection assetsFoundUuids, ICollection assetsNotFoundUuids);
+
enum RequestState
{
Initial,
diff --git a/OpenSim/Region/CoreModules/World/Archiver/DearchiveScenesGroup.cs b/OpenSim/Region/CoreModules/World/Archiver/DearchiveScenesGroup.cs
new file mode 100644
index 0000000000..3dcc020188
--- /dev/null
+++ b/OpenSim/Region/CoreModules/World/Archiver/DearchiveScenesGroup.cs
@@ -0,0 +1,232 @@
+/*
+ * 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.Collections.Generic;
+using System.Linq;
+using System.Text;
+using OpenSim.Region.Framework.Scenes;
+using OpenMetaverse;
+using System.Drawing;
+using log4net;
+using System.Reflection;
+using OpenSim.Framework.Serialization;
+
+namespace OpenSim.Region.CoreModules.World.Archiver
+{
+ ///
+ /// The regions included in an OAR file.
+ ///
+ public class DearchiveScenesInfo
+ {
+ private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
+
+ ///
+ /// One region in the archive.
+ ///
+ public class RegionInfo
+ {
+ ///
+ /// The subdirectory in which the region is stored.
+ ///
+ public string Directory { get; set; }
+
+ ///
+ /// The region's coordinates (relative to the South-West corner of the block).
+ ///
+ public Point Location { get; set; }
+
+ ///
+ /// The UUID of the original scene from which this archived region was saved.
+ ///
+ public string OriginalID { get; set; }
+
+ ///
+ /// The scene in the current simulator into which this region is loaded.
+ /// If null then the region doesn't have a corresponding scene, and it won't be loaded.
+ ///
+ public Scene Scene { get; set; }
+ }
+
+ ///
+ /// Whether this archive uses the multi-region format.
+ ///
+ public Boolean MultiRegionFormat { get; set; }
+
+ ///
+ /// Maps (Region directory -> region)
+ ///
+ protected Dictionary m_directory2region = new Dictionary();
+
+ ///
+ /// Maps (UUID of the scene in the simulator where the region will be loaded -> region)
+ ///
+ protected Dictionary m_newId2region = new Dictionary();
+
+ public int LoadedCreationDateTime { get; set; }
+ public string DefaultOriginalID { get; set; }
+
+ // These variables are used while reading the archive control file
+ protected int? m_curY = null;
+ protected int? m_curX = null;
+ protected RegionInfo m_curRegion;
+
+
+ public DearchiveScenesInfo()
+ {
+ MultiRegionFormat = false;
+ }
+
+
+ // The following methods are used while reading the archive control file
+
+ public void StartRow()
+ {
+ m_curY = (m_curY == null) ? 0 : m_curY + 1;
+ m_curX = null;
+ }
+
+ public void StartRegion()
+ {
+ m_curX = (m_curX == null) ? 0 : m_curX + 1;
+ // Note: this doesn't mean we have a real region in this location; this could just be a "hole"
+ }
+
+ public void SetRegionOriginalID(string id)
+ {
+ m_curRegion = new RegionInfo();
+ m_curRegion.Location = new Point((int)m_curX, (int)m_curY);
+ m_curRegion.OriginalID = id;
+ // 'curRegion' will be saved in 'm_directory2region' when SetRegionDir() is called
+ }
+
+ public void SetRegionDirectory(string directory)
+ {
+ m_curRegion.Directory = directory;
+ m_directory2region[directory] = m_curRegion;
+ }
+
+
+ ///
+ /// Sets all the scenes present in the simulator.
+ ///
+ ///
+ /// This method matches regions in the archive to scenes in the simulator according to
+ /// their relative position. We only load regions if there's an existing Scene in the
+ /// grid location where the region should be loaded.
+ ///
+ /// The scene where the Load OAR operation was run
+ /// All the scenes in the simulator
+ public void SetSimulatorScenes(Scene rootScene, ArchiveScenesGroup simulatorScenes)
+ {
+ foreach (RegionInfo archivedRegion in m_directory2region.Values)
+ {
+ Point location = new Point((int)rootScene.RegionInfo.RegionLocX, (int)rootScene.RegionInfo.RegionLocY);
+ location.Offset(archivedRegion.Location);
+
+ Scene scene;
+ if (simulatorScenes.TryGetScene(location, out scene))
+ {
+ archivedRegion.Scene = scene;
+ m_newId2region[scene.RegionInfo.RegionID] = archivedRegion;
+ }
+ else
+ {
+ m_log.WarnFormat("[ARCHIVER]: Not loading archived region {0} because there's no existing region at location {1},{2}",
+ archivedRegion.Directory, location.X, location.Y);
+ }
+ }
+ }
+
+ ///
+ /// Returns the archived region according to the path of a file in the archive.
+ /// Also, converts the full path into a path that is relative to the region's directory.
+ ///
+ /// The path of a file in the archive
+ /// The corresponding Scene, or null if none
+ /// The path relative to the region's directory. (Or the original
+ /// path, if this file doesn't belong to a region.)
+ /// True: use this file; False: skip it
+ public bool GetRegionFromPath(string fullPath, out Scene scene, out string relativePath)
+ {
+ scene = null;
+ relativePath = fullPath;
+
+ if (!MultiRegionFormat)
+ {
+ if (m_newId2region.Count > 0)
+ scene = m_newId2region.First().Value.Scene;
+ return true;
+ }
+
+ if (!fullPath.StartsWith(ArchiveConstants.REGIONS_PATH))
+ return true; // this file doesn't belong to a region
+
+ string[] parts = fullPath.Split(new Char[] { '/' }, 3);
+ if (parts.Length != 3)
+ return false;
+ string regionDirectory = parts[1];
+ relativePath = parts[2];
+
+ RegionInfo region;
+ if (m_directory2region.TryGetValue(regionDirectory, out region))
+ {
+ scene = region.Scene;
+ return (scene != null);
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ ///
+ /// Returns the original UUID of a region (from the simulator where the OAR was saved),
+ /// given the UUID of the scene it was loaded into in the current simulator.
+ ///
+ ///
+ ///
+ public string GetOriginalRegionID(UUID newID)
+ {
+ RegionInfo region;
+ if (m_newId2region.TryGetValue(newID, out region))
+ return region.OriginalID;
+ else
+ return DefaultOriginalID;
+ }
+
+ ///
+ /// Returns the scenes that have been (or will be) loaded.
+ ///
+ ///
+ public List GetLoadedScenes()
+ {
+ return m_newId2region.Keys.ToList();
+ }
+
+ }
+}
diff --git a/OpenSim/Region/CoreModules/World/Archiver/Tests/ArchiverTests.cs b/OpenSim/Region/CoreModules/World/Archiver/Tests/ArchiverTests.cs
index 904110e457..0a309057e7 100644
--- a/OpenSim/Region/CoreModules/World/Archiver/Tests/ArchiverTests.cs
+++ b/OpenSim/Region/CoreModules/World/Archiver/Tests/ArchiverTests.cs
@@ -47,6 +47,7 @@ using ArchiveConstants = OpenSim.Framework.Serialization.ArchiveConstants;
using TarArchiveReader = OpenSim.Framework.Serialization.TarArchiveReader;
using TarArchiveWriter = OpenSim.Framework.Serialization.TarArchiveWriter;
using RegionSettings = OpenSim.Framework.RegionSettings;
+using OpenSim.Region.Framework.Interfaces;
namespace OpenSim.Region.CoreModules.World.Archiver.Tests
{
@@ -56,23 +57,28 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
private Guid m_lastRequestId;
private string m_lastErrorMessage;
+ protected SceneHelpers m_sceneHelpers;
protected TestScene m_scene;
protected ArchiverModule m_archiverModule;
+ protected SerialiserModule m_serialiserModule;
protected TaskInventoryItem m_soundItem;
[SetUp]
public void SetUp()
{
+ new SceneManager();
+
m_archiverModule = new ArchiverModule();
- SerialiserModule serialiserModule = new SerialiserModule();
+ m_serialiserModule = new SerialiserModule();
TerrainModule terrainModule = new TerrainModule();
- m_scene = new SceneHelpers().SetupScene();
- SceneHelpers.SetupSceneModules(m_scene, m_archiverModule, serialiserModule, terrainModule);
+ m_sceneHelpers = new SceneHelpers();
+ m_scene = m_sceneHelpers.SetupScene();
+ SceneHelpers.SetupSceneModules(m_scene, m_archiverModule, m_serialiserModule, terrainModule);
}
-
- private void LoadCompleted(Guid requestId, string errorMessage)
+
+ private void LoadCompleted(Guid requestId, List loadedScenes, string errorMessage)
{
lock (this)
{
@@ -128,26 +134,10 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
TestHelpers.InMethod();
// log4net.Config.XmlConfigurator.Configure();
- SceneObjectPart part1 = CreateSceneObjectPart1();
- SceneObjectGroup sog1 = new SceneObjectGroup(part1);
- m_scene.AddNewSceneObject(sog1, false);
-
- SceneObjectPart part2 = CreateSceneObjectPart2();
-
- AssetNotecard nc = new AssetNotecard();
- nc.BodyText = "Hello World!";
- nc.Encode();
- UUID ncAssetUuid = new UUID("00000000-0000-0000-1000-000000000000");
- UUID ncItemUuid = new UUID("00000000-0000-0000-1100-000000000000");
- AssetBase ncAsset
- = AssetHelpers.CreateAsset(ncAssetUuid, AssetType.Notecard, nc.AssetData, UUID.Zero);
- m_scene.AssetService.Store(ncAsset);
- SceneObjectGroup sog2 = new SceneObjectGroup(part2);
- TaskInventoryItem ncItem
- = new TaskInventoryItem { Name = "ncItem", AssetID = ncAssetUuid, ItemID = ncItemUuid };
- part2.Inventory.AddInventoryItem(ncItem, true);
-
- m_scene.AddNewSceneObject(sog2, false);
+ SceneObjectGroup sog1;
+ SceneObjectGroup sog2;
+ UUID ncAssetUuid;
+ CreateTestObjects(m_scene, out sog1, out sog2, out ncAssetUuid);
MemoryStream archiveWriteStream = new MemoryStream();
m_scene.EventManager.OnOarFileSaved += SaveCompleted;
@@ -186,7 +176,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
Assert.That(filePath, Is.EqualTo(ArchiveConstants.CONTROL_FILE_PATH));
ArchiveReadRequest arr = new ArchiveReadRequest(m_scene, (Stream)null, false, false, Guid.Empty);
- arr.LoadControlFile(filePath, data);
+ arr.LoadControlFile(filePath, data, new DearchiveScenesInfo());
Assert.That(arr.ControlFileLoaded, Is.True);
@@ -211,6 +201,30 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
// TODO: Test presence of more files and contents of files.
}
+ private void CreateTestObjects(Scene scene, out SceneObjectGroup sog1, out SceneObjectGroup sog2, out UUID ncAssetUuid)
+ {
+ SceneObjectPart part1 = CreateSceneObjectPart1();
+ sog1 = new SceneObjectGroup(part1);
+ scene.AddNewSceneObject(sog1, false);
+
+ AssetNotecard nc = new AssetNotecard();
+ nc.BodyText = "Hello World!";
+ nc.Encode();
+ ncAssetUuid = UUID.Random();
+ UUID ncItemUuid = UUID.Random();
+ AssetBase ncAsset
+ = AssetHelpers.CreateAsset(ncAssetUuid, AssetType.Notecard, nc.AssetData, UUID.Zero);
+ m_scene.AssetService.Store(ncAsset);
+
+ TaskInventoryItem ncItem
+ = new TaskInventoryItem { Name = "ncItem", AssetID = ncAssetUuid, ItemID = ncItemUuid };
+ SceneObjectPart part2 = CreateSceneObjectPart2();
+ sog2 = new SceneObjectGroup(part2);
+ part2.Inventory.AddInventoryItem(ncItem, true);
+
+ scene.AddNewSceneObject(sog2, false);
+ }
+
///
/// Test saving an OpenSim Region Archive with the no assets option
///
@@ -270,7 +284,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
Assert.That(filePath, Is.EqualTo(ArchiveConstants.CONTROL_FILE_PATH));
ArchiveReadRequest arr = new ArchiveReadRequest(m_scene, (Stream)null, false, false, Guid.Empty);
- arr.LoadControlFile(filePath, data);
+ arr.LoadControlFile(filePath, data, new DearchiveScenesInfo());
Assert.That(arr.ControlFileLoaded, Is.True);
@@ -307,7 +321,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
tar.WriteFile(
ArchiveConstants.CONTROL_FILE_PATH,
- new ArchiveWriteRequestPreparation(null, (Stream)null, Guid.Empty).CreateControlFile(new Dictionary()));
+ new ArchiveWriteRequest(m_scene, (Stream)null, Guid.Empty).CreateControlFile(new ArchiveScenesGroup()));
SceneObjectGroup sog1 = SceneHelpers.CreateSceneObject(1, ownerId, "obj1-", 0x11);
SceneObjectPart sop2
@@ -362,11 +376,10 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
// Also check that direct entries which will also have a file entry containing that directory doesn't
// upset load
tar.WriteDir(ArchiveConstants.TERRAINS_PATH);
-
+
tar.WriteFile(
ArchiveConstants.CONTROL_FILE_PATH,
- new ArchiveWriteRequestPreparation(null, (Stream)null, Guid.Empty).CreateControlFile(new Dictionary()));
-
+ new ArchiveWriteRequest(m_scene, (Stream)null, Guid.Empty).CreateControlFile(new ArchiveScenesGroup()));
SceneObjectPart part1 = CreateSceneObjectPart1();
part1.SitTargetOrientation = new Quaternion(0.2f, 0.3f, 0.4f, 0.5f);
@@ -389,31 +402,12 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
Assert.That(soundDataResourceName, Is.Not.Null);
byte[] soundData;
- Console.WriteLine("Loading " + soundDataResourceName);
- using (Stream resource = assembly.GetManifestResourceStream(soundDataResourceName))
- {
- using (BinaryReader br = new BinaryReader(resource))
- {
- // FIXME: Use the inspector instead
- soundData = br.ReadBytes(99999999);
- UUID soundUuid = UUID.Parse("00000000-0000-0000-0000-000000000001");
- string soundAssetFileName
- = ArchiveConstants.ASSETS_PATH + soundUuid
- + ArchiveConstants.ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.SoundWAV];
- tar.WriteFile(soundAssetFileName, soundData);
-
- /*
- AssetBase soundAsset = AssetHelpers.CreateAsset(soundUuid, soundData);
- scene.AssetService.Store(soundAsset);
- asset1FileName = ArchiveConstants.ASSETS_PATH + soundUuid + ".wav";
- */
-
- TaskInventoryItem item1
- = new TaskInventoryItem { AssetID = soundUuid, ItemID = soundItemUuid, Name = soundItemName };
- part1.Inventory.AddInventoryItem(item1, true);
- }
- }
-
+ UUID soundUuid;
+ CreateSoundAsset(tar, assembly, soundDataResourceName, out soundData, out soundUuid);
+
+ TaskInventoryItem item1
+ = new TaskInventoryItem { AssetID = soundUuid, ItemID = soundItemUuid, Name = soundItemName };
+ part1.Inventory.AddInventoryItem(item1, true);
m_scene.AddNewSceneObject(object1, false);
string object1FileName = string.Format(
@@ -435,6 +429,34 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
Assert.That(m_lastErrorMessage, Is.Null);
+ TestLoadedRegion(part1, soundItemName, soundData);
+ }
+
+ private static void CreateSoundAsset(TarArchiveWriter tar, Assembly assembly, string soundDataResourceName, out byte[] soundData, out UUID soundUuid)
+ {
+ using (Stream resource = assembly.GetManifestResourceStream(soundDataResourceName))
+ {
+ using (BinaryReader br = new BinaryReader(resource))
+ {
+ // FIXME: Use the inspector instead
+ soundData = br.ReadBytes(99999999);
+ soundUuid = UUID.Parse("00000000-0000-0000-0000-000000000001");
+ string soundAssetFileName
+ = ArchiveConstants.ASSETS_PATH + soundUuid
+ + ArchiveConstants.ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.SoundWAV];
+ tar.WriteFile(soundAssetFileName, soundData);
+
+ /*
+ AssetBase soundAsset = AssetHelpers.CreateAsset(soundUuid, soundData);
+ scene.AssetService.Store(soundAsset);
+ asset1FileName = ArchiveConstants.ASSETS_PATH + soundUuid + ".wav";
+ */
+ }
+ }
+ }
+
+ private void TestLoadedRegion(SceneObjectPart part1, string soundItemName, byte[] soundData)
+ {
SceneObjectPart object1PartLoaded = m_scene.GetSceneObjectPart(part1.Name);
Assert.That(object1PartLoaded, Is.Not.Null, "object1 was not loaded");
@@ -454,9 +476,6 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
Assert.That(loadedSoundAsset.Data, Is.EqualTo(soundData), "saved and loaded sound data do not match");
Assert.Greater(m_scene.LandChannel.AllParcels().Count, 0, "incorrect number of parcels");
-
- // Temporary
- Console.WriteLine("Successfully completed {0}", MethodBase.GetCurrentMethod());
}
///
@@ -516,7 +535,8 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
SerialiserModule serialiserModule = new SerialiserModule();
TerrainModule terrainModule = new TerrainModule();
- TestScene scene2 = new SceneHelpers().SetupScene();
+ m_sceneHelpers = new SceneHelpers();
+ TestScene scene2 = m_sceneHelpers.SetupScene();
SceneHelpers.SetupSceneModules(scene2, archiverModule, serialiserModule, terrainModule);
// Make sure there's a valid owner for the owner we saved (this should have been wiped if the code is
@@ -554,7 +574,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
tar.WriteDir(ArchiveConstants.TERRAINS_PATH);
tar.WriteFile(
ArchiveConstants.CONTROL_FILE_PATH,
- new ArchiveWriteRequestPreparation(null, (Stream)null, Guid.Empty).CreateControlFile(new Dictionary()));
+ new ArchiveWriteRequest(m_scene, (Stream)null, Guid.Empty).CreateControlFile(new ArchiveScenesGroup()));
RegionSettings rs = new RegionSettings();
rs.AgentLimit = 17;
@@ -664,7 +684,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
SerialiserModule serialiserModule = new SerialiserModule();
TerrainModule terrainModule = new TerrainModule();
- Scene scene = new SceneHelpers().SetupScene();
+ Scene scene = m_sceneHelpers.SetupScene();
SceneHelpers.SetupSceneModules(scene, archiverModule, serialiserModule, terrainModule);
m_scene.AddNewSceneObject(new SceneObjectGroup(part2), false);
@@ -700,5 +720,258 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
Assert.That(object2PartMerged.GroupPosition, Is.EqualTo(part2.GroupPosition), "object2 group position not equal after merge");
}
}
+
+ ///
+ /// Test saving a multi-region OAR.
+ ///
+ [Test]
+ public void TestSaveMultiRegionOar()
+ {
+ TestHelpers.InMethod();
+
+ // Create test regions
+
+ int WIDTH = 2;
+ int HEIGHT = 2;
+
+ List scenes = new List();
+
+ // Maps (Directory in OAR file -> scene)
+ Dictionary regionPaths = new Dictionary();
+
+ // Maps (Scene -> expected object paths)
+ Dictionary> expectedPaths = new Dictionary>();
+
+ // List of expected assets
+ List expectedAssets = new List();
+
+ for (uint y = 0; y < HEIGHT; y++)
+ {
+ for (uint x = 0; x < WIDTH; x++)
+ {
+ Scene scene;
+ if (x == 0 && y == 0)
+ {
+ scene = m_scene; // this scene was already created in SetUp()
+ }
+ else
+ {
+ scene = m_sceneHelpers.SetupScene(string.Format("Unit test region {0}", (y * WIDTH) + x + 1), UUID.Random(), 1000 + x, 1000 + y);
+ SceneHelpers.SetupSceneModules(scene, new ArchiverModule(), m_serialiserModule, new TerrainModule());
+ }
+ scenes.Add(scene);
+
+ string dir = String.Format("{0}_{1}_{2}", x + 1, y + 1, scene.RegionInfo.RegionName.Replace(" ", "_"));
+ regionPaths[dir] = scene;
+
+ SceneObjectGroup sog1;
+ SceneObjectGroup sog2;
+ UUID ncAssetUuid;
+
+ CreateTestObjects(scene, out sog1, out sog2, out ncAssetUuid);
+
+ expectedPaths[scene.RegionInfo.RegionID] = new List();
+ expectedPaths[scene.RegionInfo.RegionID].Add(ArchiveHelpers.CreateObjectPath(sog1));
+ expectedPaths[scene.RegionInfo.RegionID].Add(ArchiveHelpers.CreateObjectPath(sog2));
+
+ expectedAssets.Add(ncAssetUuid);
+ }
+ }
+
+
+ // Save OAR
+
+ MemoryStream archiveWriteStream = new MemoryStream();
+ m_scene.EventManager.OnOarFileSaved += SaveCompleted;
+
+ Guid requestId = new Guid("00000000-0000-0000-0000-808080808080");
+
+ Dictionary options = new Dictionary();
+ options.Add("all", true);
+
+ lock (this)
+ {
+ m_archiverModule.ArchiveRegion(archiveWriteStream, requestId, options);
+ Monitor.Wait(this, 60000);
+ }
+
+
+ // Check that the OAR contains the expected data
+
+ Assert.That(m_lastRequestId, Is.EqualTo(requestId));
+
+ byte[] archive = archiveWriteStream.ToArray();
+ MemoryStream archiveReadStream = new MemoryStream(archive);
+ TarArchiveReader tar = new TarArchiveReader(archiveReadStream);
+
+ Dictionary> foundPaths = new Dictionary>();
+ List foundAssets = new List();
+
+ foreach (Scene scene in scenes)
+ {
+ foundPaths[scene.RegionInfo.RegionID] = new List();
+ }
+
+ string filePath;
+ TarArchiveReader.TarEntryType tarEntryType;
+
+ byte[] data = tar.ReadEntry(out filePath, out tarEntryType);
+ Assert.That(filePath, Is.EqualTo(ArchiveConstants.CONTROL_FILE_PATH));
+
+ ArchiveReadRequest arr = new ArchiveReadRequest(m_scene, (Stream)null, false, false, Guid.Empty);
+ arr.LoadControlFile(filePath, data, new DearchiveScenesInfo());
+
+ Assert.That(arr.ControlFileLoaded, Is.True);
+
+ while (tar.ReadEntry(out filePath, out tarEntryType) != null)
+ {
+ if (filePath.StartsWith(ArchiveConstants.ASSETS_PATH))
+ {
+ // Assets are shared, so this file doesn't belong to any specific region.
+ string fileName = filePath.Remove(0, ArchiveConstants.ASSETS_PATH.Length);
+ if (fileName.EndsWith("_notecard.txt"))
+ foundAssets.Add(UUID.Parse(fileName.Substring(0, fileName.Length - "_notecard.txt".Length)));
+ }
+ else
+ {
+ // This file belongs to one of the regions. Find out which one.
+ Assert.IsTrue(filePath.StartsWith(ArchiveConstants.REGIONS_PATH));
+ string[] parts = filePath.Split(new Char[] { '/' }, 3);
+ Assert.AreEqual(3, parts.Length);
+ string regionDirectory = parts[1];
+ string relativePath = parts[2];
+ Scene scene = regionPaths[regionDirectory];
+
+ if (relativePath.StartsWith(ArchiveConstants.OBJECTS_PATH))
+ {
+ foundPaths[scene.RegionInfo.RegionID].Add(relativePath);
+ }
+ }
+ }
+
+ Assert.AreEqual(scenes.Count, foundPaths.Count);
+ foreach (Scene scene in scenes)
+ {
+ Assert.That(foundPaths[scene.RegionInfo.RegionID], Is.EquivalentTo(expectedPaths[scene.RegionInfo.RegionID]));
+ }
+
+ Assert.That(foundAssets, Is.EquivalentTo(expectedAssets));
+ }
+
+ ///
+ /// Test loading a multi-region OAR.
+ ///
+ [Test]
+ public void TestLoadMultiRegionOar()
+ {
+ TestHelpers.InMethod();
+
+ // Create an ArchiveScenesGroup with the regions in the OAR. This is needed to generate the control file.
+
+ int WIDTH = 2;
+ int HEIGHT = 2;
+
+ for (uint y = 0; y < HEIGHT; y++)
+ {
+ for (uint x = 0; x < WIDTH; x++)
+ {
+ Scene scene;
+ if (x == 0 && y == 0)
+ {
+ scene = m_scene; // this scene was already created in SetUp()
+ }
+ else
+ {
+ scene = m_sceneHelpers.SetupScene(string.Format("Unit test region {0}", (y * WIDTH) + x + 1), UUID.Random(), 1000 + x, 1000 + y);
+ SceneHelpers.SetupSceneModules(scene, new ArchiverModule(), m_serialiserModule, new TerrainModule());
+ }
+ }
+ }
+
+ ArchiveScenesGroup scenesGroup = new ArchiveScenesGroup();
+ SceneManager.Instance.ForEachScene(delegate(Scene scene)
+ {
+ scenesGroup.AddScene(scene);
+ });
+ scenesGroup.CalcSceneLocations();
+
+ // Generate the OAR file
+
+ MemoryStream archiveWriteStream = new MemoryStream();
+ TarArchiveWriter tar = new TarArchiveWriter(archiveWriteStream);
+
+ ArchiveWriteRequest writeRequest = new ArchiveWriteRequest(m_scene, (Stream)null, Guid.Empty);
+ writeRequest.MultiRegionFormat = true;
+ tar.WriteFile(
+ ArchiveConstants.CONTROL_FILE_PATH, writeRequest.CreateControlFile(scenesGroup));
+
+ SceneObjectPart part1 = CreateSceneObjectPart1();
+ part1.SitTargetOrientation = new Quaternion(0.2f, 0.3f, 0.4f, 0.5f);
+ part1.SitTargetPosition = new Vector3(1, 2, 3);
+
+ SceneObjectGroup object1 = new SceneObjectGroup(part1);
+
+ // Let's put some inventory items into our object
+ string soundItemName = "sound-item1";
+ UUID soundItemUuid = UUID.Parse("00000000-0000-0000-0000-000000000002");
+ Type type = GetType();
+ Assembly assembly = type.Assembly;
+ string soundDataResourceName = null;
+ string[] names = assembly.GetManifestResourceNames();
+ foreach (string name in names)
+ {
+ if (name.EndsWith(".Resources.test-sound.wav"))
+ soundDataResourceName = name;
+ }
+ Assert.That(soundDataResourceName, Is.Not.Null);
+
+ byte[] soundData;
+ UUID soundUuid;
+ CreateSoundAsset(tar, assembly, soundDataResourceName, out soundData, out soundUuid);
+
+ TaskInventoryItem item1
+ = new TaskInventoryItem { AssetID = soundUuid, ItemID = soundItemUuid, Name = soundItemName };
+ part1.Inventory.AddInventoryItem(item1, true);
+ m_scene.AddNewSceneObject(object1, false);
+
+ string object1FileName = string.Format(
+ "{0}_{1:000}-{2:000}-{3:000}__{4}.xml",
+ part1.Name,
+ Math.Round(part1.GroupPosition.X), Math.Round(part1.GroupPosition.Y), Math.Round(part1.GroupPosition.Z),
+ part1.UUID);
+ string path = "regions/1_1_Unit_test_region/" + ArchiveConstants.OBJECTS_PATH + object1FileName;
+ tar.WriteFile(path, SceneObjectSerializer.ToXml2Format(object1));
+
+ tar.Close();
+
+
+ // Delete the current objects, to test that they're loaded from the OAR and didn't
+ // just remain in the scene.
+ SceneManager.Instance.ForEachScene(delegate(Scene scene)
+ {
+ scene.DeleteAllSceneObjects();
+ });
+
+ // Create a "hole", to test that that the corresponding region isn't loaded from the OAR
+ SceneManager.Instance.CloseScene(SceneManager.Instance.Scenes[1]);
+
+
+ // Check thay the OAR file contains the expected data
+
+ MemoryStream archiveReadStream = new MemoryStream(archiveWriteStream.ToArray());
+
+ lock (this)
+ {
+ m_scene.EventManager.OnOarFileLoaded += LoadCompleted;
+ m_archiverModule.DearchiveRegion(archiveReadStream);
+ }
+
+ Assert.That(m_lastErrorMessage, Is.Null);
+
+ Assert.AreEqual(3, SceneManager.Instance.Scenes.Count);
+
+ TestLoadedRegion(part1, soundItemName, soundData);
+ }
+
}
}
diff --git a/OpenSim/Region/CoreModules/World/Land/PrimCountModule.cs b/OpenSim/Region/CoreModules/World/Land/PrimCountModule.cs
index 102b4d7550..cbb3abe460 100644
--- a/OpenSim/Region/CoreModules/World/Land/PrimCountModule.cs
+++ b/OpenSim/Region/CoreModules/World/Land/PrimCountModule.cs
@@ -69,7 +69,7 @@ namespace OpenSim.Region.CoreModules.World.Land
/// without recounting the whole sim.
///
/// We start out tainted so that the first get call resets the various prim counts.
- ///
+ ///
private bool m_Tainted = true;
private Object m_TaintLock = new Object();
diff --git a/OpenSim/Region/Framework/Interfaces/IEstateModule.cs b/OpenSim/Region/Framework/Interfaces/IEstateModule.cs
index ca2ad94592..292efa4b7e 100644
--- a/OpenSim/Region/Framework/Interfaces/IEstateModule.cs
+++ b/OpenSim/Region/Framework/Interfaces/IEstateModule.cs
@@ -46,6 +46,10 @@ namespace OpenSim.Region.Framework.Interfaces
///
void sendRegionHandshakeToAll();
void TriggerEstateInfoChange();
+
+ ///
+ /// Fires the OnRegionInfoChange event.
+ ///
void TriggerRegionInfoChange();
void setEstateTerrainBaseTexture(int level, UUID texture);
diff --git a/OpenSim/Region/Framework/Interfaces/IJsonStoreModule.cs b/OpenSim/Region/Framework/Interfaces/IJsonStoreModule.cs
index baac6e827f..da39e95638 100644
--- a/OpenSim/Region/Framework/Interfaces/IJsonStoreModule.cs
+++ b/OpenSim/Region/Framework/Interfaces/IJsonStoreModule.cs
@@ -35,7 +35,7 @@ namespace OpenSim.Region.Framework.Interfaces
public interface IJsonStoreModule
{
- bool CreateStore(string value, out UUID result);
+ bool CreateStore(string value, ref UUID result);
bool DestroyStore(UUID storeID);
bool TestPath(UUID storeID, string path, bool useJson);
bool SetValue(UUID storeID, string path, string value, bool useJson);
diff --git a/OpenSim/Region/Framework/Scenes/EventManager.cs b/OpenSim/Region/Framework/Scenes/EventManager.cs
index eee5960a88..e257b57c11 100644
--- a/OpenSim/Region/Framework/Scenes/EventManager.cs
+++ b/OpenSim/Region/Framework/Scenes/EventManager.cs
@@ -47,30 +47,75 @@ namespace OpenSim.Region.Framework.Scenes
public delegate void OnFrameDelegate();
+ ///
+ /// Triggered on each sim frame.
+ ///
+ ///
+ /// This gets triggered in
+ /// Core uses it for things like Sun, Wind & Clouds
+ /// The MRM module also uses it.
+ ///
public event OnFrameDelegate OnFrame;
public delegate void ClientMovement(ScenePresence client);
+ ///
+ /// Trigerred when an agent moves.
+ ///
+ ///
+ /// This gets triggered in
+ /// prior to
+ ///
public event ClientMovement OnClientMovement;
public delegate void OnTerrainTaintedDelegate();
+ ///
+ /// Triggered if the terrain has been edited
+ ///
+ ///
+ /// This gets triggered in
+ /// after it determines that an update has been made.
+ ///
public event OnTerrainTaintedDelegate OnTerrainTainted;
public delegate void OnTerrainTickDelegate();
- public delegate void OnTerrainUpdateDelegate();
-
+ ///
+ /// Triggered if the terrain has been edited
+ ///
+ ///
+ /// This gets triggered in
+ /// but is used by core solely to update the physics engine.
+ ///
public event OnTerrainTickDelegate OnTerrainTick;
+ public delegate void OnTerrainUpdateDelegate();
+
public event OnTerrainUpdateDelegate OnTerrainUpdate;
public delegate void OnBackupDelegate(ISimulationDataService datastore, bool forceBackup);
+ ///
+ /// Triggered when a region is backed up/persisted to storage
+ ///
+ ///
+ /// This gets triggered in
+ /// and is fired before the persistence occurs.
+ ///
public event OnBackupDelegate OnBackup;
public delegate void OnClientConnectCoreDelegate(IClientCore client);
+ ///
+ /// Triggered when a new client connects to the scene.
+ ///
+ ///
+ /// This gets triggered in ,
+ /// which checks if an instance of
+ /// also implements and as such,
+ /// is not triggered by NPCs.
+ ///
public event OnClientConnectCoreDelegate OnClientConnect;
public delegate void OnNewClientDelegate(IClientAPI client);
@@ -91,18 +136,54 @@ namespace OpenSim.Region.Framework.Scenes
public delegate void OnNewPresenceDelegate(ScenePresence presence);
+ ///
+ /// Triggered when a new presence is added to the scene
+ ///
+ ///
+ /// Triggered in which is used by both
+ /// users and NPCs
+ ///
public event OnNewPresenceDelegate OnNewPresence;
public delegate void OnRemovePresenceDelegate(UUID agentId);
+ ///
+ /// Triggered when a presence is removed from the scene
+ ///
+ ///
+ /// Triggered in which is used by both
+ /// users and NPCs
+ ///
public event OnRemovePresenceDelegate OnRemovePresence;
public delegate void OnParcelPrimCountUpdateDelegate();
+ ///
+ /// Triggered whenever the prim count may have been altered, or prior
+ /// to an action that requires the current prim count to be accurate.
+ ///
+ ///
+ /// Triggered by in
+ /// ,
+ /// ,
+ /// ,
+ /// ,
+ /// ,
+ /// ,
+ ///
+ ///
public event OnParcelPrimCountUpdateDelegate OnParcelPrimCountUpdate;
public delegate void OnParcelPrimCountAddDelegate(SceneObjectGroup obj);
+ ///
+ /// Triggered in response to for
+ /// objects that actually contribute to parcel prim count.
+ ///
+ ///
+ /// Triggered by in
+ ///
+ ///
public event OnParcelPrimCountAddDelegate OnParcelPrimCountAdd;
public delegate void OnPluginConsoleDelegate(string[] args);
@@ -123,6 +204,14 @@ namespace OpenSim.Region.Framework.Scenes
public event OnSetRootAgentSceneDelegate OnSetRootAgentScene;
+ ///
+ /// Triggered after parcel properties have been updated.
+ ///
+ ///
+ /// Triggered by in
+ /// ,
+ ///
+ ///
public event ParcelPropertiesUpdateRequest OnParcelPropertiesUpdateRequest;
///
@@ -373,6 +462,20 @@ namespace OpenSim.Region.Framework.Scenes
public event RequestParcelPrimCountUpdate OnRequestParcelPrimCountUpdate;
public delegate void ParcelPrimCountTainted();
+
+ ///
+ /// Triggered when the parcel prim count has been altered.
+ ///
+ ///
+ /// Triggered by in
+ /// ,
+ /// ,
+ /// ,
+ /// ,
+ /// ,
+ /// ,
+ ///
+ ///
public event ParcelPrimCountTainted OnParcelPrimCountTainted;
public event GetScriptRunning OnGetScriptRunning;
@@ -432,7 +535,7 @@ namespace OpenSim.Region.Framework.Scenes
/// the scripts may not have started yet
/// Message is non empty string if there were problems loading the oar file
///
- public delegate void OarFileLoaded(Guid guid, string message);
+ public delegate void OarFileLoaded(Guid guid, List loadedScenes, string message);
public event OarFileLoaded OnOarFileLoaded;
///
@@ -485,6 +588,9 @@ namespace OpenSim.Region.Framework.Scenes
///
///
/// True if the duplicate will immediately be in the scene, false otherwise
+ ///
+ /// Triggered in
+ ///
public event SceneObjectPartCopyDelegate OnSceneObjectPartCopy;
public delegate void SceneObjectPartCopyDelegate(SceneObjectPart copy, SceneObjectPart original, bool userExposed);
@@ -593,8 +699,29 @@ namespace OpenSim.Region.Framework.Scenes
public delegate void LandBuy(Object sender, LandBuyArgs e);
+ ///
+ /// Triggered when an attempt to transfer grid currency occurs
+ ///
+ ///
+ /// Triggered in
+ /// via
+ /// via
+ /// via
+ ///
public event MoneyTransferEvent OnMoneyTransfer;
+
+ ///
+ /// Triggered after after
+ ///
public event LandBuy OnLandBuy;
+
+ ///
+ /// Triggered to allow or prevent a real estate transaction
+ ///
+ ///
+ /// Triggered in
+ ///
+ ///
public event LandBuy OnValidateLandBuy;
public void TriggerOnAttach(uint localID, UUID itemID, UUID avatarID)
@@ -2093,7 +2220,7 @@ namespace OpenSim.Region.Framework.Scenes
return 6;
}
- public void TriggerOarFileLoaded(Guid requestId, string message)
+ public void TriggerOarFileLoaded(Guid requestId, List loadedScenes, string message)
{
OarFileLoaded handlerOarFileLoaded = OnOarFileLoaded;
if (handlerOarFileLoaded != null)
@@ -2102,7 +2229,7 @@ namespace OpenSim.Region.Framework.Scenes
{
try
{
- d(requestId, message);
+ d(requestId, loadedScenes, message);
}
catch (Exception e)
{
diff --git a/OpenSim/Region/Framework/Scenes/SceneManager.cs b/OpenSim/Region/Framework/Scenes/SceneManager.cs
index f1b09cac37..dba3a6133b 100644
--- a/OpenSim/Region/Framework/Scenes/SceneManager.cs
+++ b/OpenSim/Region/Framework/Scenes/SceneManager.cs
@@ -92,7 +92,11 @@ namespace OpenSim.Region.Framework.Scenes
private static SceneManager m_instance = null;
public static SceneManager Instance
{
- get { return m_instance; }
+ get {
+ if (m_instance == null)
+ m_instance = new SceneManager();
+ return m_instance;
+ }
}
private readonly DoubleDictionary m_localScenes = new DoubleDictionary();
diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs
index 18e74c1cbc..f1df6d6668 100644
--- a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs
+++ b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs
@@ -2750,13 +2750,14 @@ namespace OpenSim.Region.Framework.Scenes
m_scene.m_linksetCapacity)
{
m_log.DebugFormat(
- "[SCENE OBJECT GROUP]: Cannot link group with root" +
- " part {0}, {1} ({2} prims) to group with root part" +
- " {3}, {4} ({5} prims) because the new linkset" +
- " would exceed the configured maximum of {6}",
- objectGroup.RootPart.Name, objectGroup.RootPart.UUID,
- objectGroup.PrimCount, RootPart.Name, RootPart.UUID,
- PrimCount, m_scene.m_linksetCapacity);
+ "[SCENE OBJECT GROUP]: Cannot link group with root" +
+ " part {0}, {1} ({2} prims) to group with root part" +
+ " {3}, {4} ({5} prims) because the new linkset" +
+ " would exceed the configured maximum of {6}",
+ objectGroup.RootPart.Name, objectGroup.RootPart.UUID,
+ objectGroup.PrimCount, RootPart.Name, RootPart.UUID,
+ PrimCount, m_scene.m_linksetCapacity);
+
return;
}
diff --git a/OpenSim/Region/OptionalModules/Scripting/JsonStore/JsonStoreModule.cs b/OpenSim/Region/OptionalModules/Scripting/JsonStore/JsonStoreModule.cs
index 311531c8d4..732c28f66d 100644
--- a/OpenSim/Region/OptionalModules/Scripting/JsonStore/JsonStoreModule.cs
+++ b/OpenSim/Region/OptionalModules/Scripting/JsonStore/JsonStoreModule.cs
@@ -175,14 +175,15 @@ namespace OpenSim.Region.OptionalModules.Scripting.JsonStore
///
///
// -----------------------------------------------------------------
- public bool CreateStore(string value, out UUID result)
+ public bool CreateStore(string value, ref UUID result)
{
- result = UUID.Zero;
+ if (result == UUID.Zero)
+ result = UUID.Random();
+
+ JsonStore map = null;
if (! m_enabled) return false;
- UUID uuid = UUID.Random();
- JsonStore map = null;
try
{
@@ -195,9 +196,8 @@ namespace OpenSim.Region.OptionalModules.Scripting.JsonStore
}
lock (m_JsonValueStore)
- m_JsonValueStore.Add(uuid,map);
+ m_JsonValueStore.Add(result,map);
- result = uuid;
return true;
}
@@ -231,7 +231,7 @@ namespace OpenSim.Region.OptionalModules.Scripting.JsonStore
if (! m_JsonValueStore.TryGetValue(storeID,out map))
{
m_log.InfoFormat("[JsonStore] Missing store {0}",storeID);
- return true;
+ return false;
}
}
diff --git a/OpenSim/Region/OptionalModules/Scripting/JsonStore/JsonStoreScriptModule.cs b/OpenSim/Region/OptionalModules/Scripting/JsonStore/JsonStoreScriptModule.cs
index eaba816179..6910d14c67 100644
--- a/OpenSim/Region/OptionalModules/Scripting/JsonStore/JsonStoreScriptModule.cs
+++ b/OpenSim/Region/OptionalModules/Scripting/JsonStore/JsonStoreScriptModule.cs
@@ -227,7 +227,7 @@ namespace OpenSim.Region.OptionalModules.Scripting.JsonStore
protected UUID JsonCreateStore(UUID hostID, UUID scriptID, string value)
{
UUID uuid = UUID.Zero;
- if (! m_store.CreateStore(value, out uuid))
+ if (! m_store.CreateStore(value, ref uuid))
GenerateRuntimeError("Failed to create Json store");
return uuid;
diff --git a/OpenSim/Region/OptionalModules/Scripting/RegionReadyModule/RegionReadyModule.cs b/OpenSim/Region/OptionalModules/Scripting/RegionReadyModule/RegionReadyModule.cs
index fff3a32010..bad75f7819 100644
--- a/OpenSim/Region/OptionalModules/Scripting/RegionReadyModule/RegionReadyModule.cs
+++ b/OpenSim/Region/OptionalModules/Scripting/RegionReadyModule/RegionReadyModule.cs
@@ -181,7 +181,7 @@ namespace OpenSim.Region.OptionalModules.Scripting.RegionReady
}
}
- void OnOarFileLoaded(Guid requestId, string message)
+ void OnOarFileLoaded(Guid requestId, List loadedScenes, string message)
{
m_oarFileLoading = true;
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs
index fa22c78d8a..526dbadd97 100644
--- a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs
@@ -131,8 +131,6 @@ public class BSCharacter : BSPhysObject
BulletSimAPI.SetObjectBuoyancy(Scene.WorldID, LocalID, _buoyancy);
BSBody = new BulletBody(LocalID, BulletSimAPI.GetBodyHandle2(Scene.World.Ptr, LocalID));
- // avatars get all collisions no matter what (makes walking on ground and such work)
- BulletSimAPI.AddToCollisionFlags2(BSBody.Ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
});
return;
@@ -480,11 +478,10 @@ public class BSCharacter : BSPhysObject
// Stop collision events
public override void UnSubscribeEvents() {
_subscribedEventsMs = 0;
- // Avatars get all their collision events
- // Scene.TaintedObject("BSCharacter.UnSubscribeEvents", delegate()
- // {
- // BulletSimAPI.RemoveFromCollisionFlags2(Body.Ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
- // });
+ Scene.TaintedObject("BSCharacter.UnSubscribeEvents", delegate()
+ {
+ BulletSimAPI.RemoveFromCollisionFlags2(BSBody.Ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
+ });
}
// Return 'true' if someone has subscribed to events
public override bool SubscribedEvents() {
@@ -532,20 +529,20 @@ public class BSCharacter : BSPhysObject
// The collision, if it should be reported to the character, is placed in a collection
// that will later be sent to the simulator when SendCollisions() is called.
CollisionEventUpdate collisionCollection = null;
- public override void Collide(uint collidingWith, BSPhysObject collidee, ActorTypes type, Vector3 contactPoint, Vector3 contactNormal, float pentrationDepth)
+ public override bool Collide(uint collidingWith, BSPhysObject collidee, Vector3 contactPoint, Vector3 contactNormal, float pentrationDepth)
{
- // m_log.DebugFormat("{0}: Collide: ms={1}, id={2}, with={3}", LogHeader, _subscribedEventsMs, LocalID, collidingWith);
+ bool ret = false;
// The following makes IsColliding() and IsCollidingGround() work
_collidingStep = Scene.SimulationStep;
- if (collidingWith == BSScene.TERRAIN_ID || collidingWith == BSScene.GROUNDPLANE_ID)
+ if (collidingWith <= Scene.TerrainManager.HighestTerrainID)
{
_collidingGroundStep = Scene.SimulationStep;
}
// DetailLog("{0},BSCharacter.Collison,call,with={1}", LocalID, collidingWith);
// throttle collisions to the rate specified in the subscription
- if (_subscribedEventsMs != 0) {
+ if (SubscribedEvents()) {
int nowTime = Scene.SimulationNowTime;
if (nowTime >= _nextCollisionOkTime) {
_nextCollisionOkTime = nowTime + _subscribedEventsMs;
@@ -553,8 +550,10 @@ public class BSCharacter : BSPhysObject
if (collisionCollection == null)
collisionCollection = new CollisionEventUpdate();
collisionCollection.AddCollider(collidingWith, new ContactPoint(contactPoint, contactNormal, pentrationDepth));
+ ret = true;
}
}
+ return ret;
}
public override void SendCollisions()
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSConstraint.cs b/OpenSim/Region/Physics/BulletSPlugin/BSConstraint.cs
index 2e15ced238..1376a29bbf 100755
--- a/OpenSim/Region/Physics/BulletSPlugin/BSConstraint.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSConstraint.cs
@@ -74,6 +74,17 @@ public abstract class BSConstraint : IDisposable
return ret;
}
+ public virtual bool SetSolverIterations(float cnt)
+ {
+ bool ret = false;
+ if (m_enabled)
+ {
+ BulletSimAPI.SetConstraintNumSolverIterations2(m_constraint.Ptr, cnt);
+ ret = true;
+ }
+ return ret;
+ }
+
public virtual bool CalculateTransforms()
{
bool ret = false;
@@ -96,12 +107,9 @@ public abstract class BSConstraint : IDisposable
ret = CalculateTransforms();
if (ret)
{
- // m_world.scene.PhysicsLogging.Write("{0},BSConstraint.RecomputeConstraintVariables,taint,enabling,A={1},B={2}",
- // BSScene.DetailLogZero, Body1.ID, Body2.ID);
-
// Setting an object's mass to zero (making it static like when it's selected)
// automatically disables the constraints.
- // If enabled, be sure to set the constraint itself to enabled.
+ // If the link is enabled, be sure to set the constraint itself to enabled.
BulletSimAPI.SetConstraintEnable2(m_constraint.Ptr, m_world.scene.NumericBool(true));
}
else
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs
index 8169e990bb..61006f07d6 100644
--- a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs
@@ -80,7 +80,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
// Linear properties
private Vector3 m_linearMotorDirection = Vector3.Zero; // velocity requested by LSL, decayed by time
private Vector3 m_linearMotorDirectionLASTSET = Vector3.Zero; // velocity requested by LSL
- private Vector3 m_dir = Vector3.Zero; // velocity applied to body
+ private Vector3 m_newVelocity = Vector3.Zero; // velocity computed to be applied to body
private Vector3 m_linearFrictionTimescale = Vector3.Zero;
private float m_linearMotorDecayTimescale = 0;
private float m_linearMotorTimescale = 0;
@@ -131,7 +131,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
m_type = Vehicle.TYPE_NONE;
}
- internal void ProcessFloatVehicleParam(Vehicle pParam, float pValue, float timestep)
+ internal void ProcessFloatVehicleParam(Vehicle pParam, float pValue)
{
VDetailLog("{0},ProcessFloatVehicleParam,param={1},val={2}", m_prim.LocalID, pParam, pValue);
switch (pParam)
@@ -230,7 +230,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
}
}//end ProcessFloatVehicleParam
- internal void ProcessVectorVehicleParam(Vehicle pParam, Vector3 pValue, float timestep)
+ internal void ProcessVectorVehicleParam(Vehicle pParam, Vector3 pValue)
{
VDetailLog("{0},ProcessVectorVehicleParam,param={1},val={2}", m_prim.LocalID, pParam, pValue);
switch (pParam)
@@ -299,7 +299,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
}
}//end ProcessVehicleFlags
- internal void ProcessTypeChange(Vehicle pType, float stepSize)
+ internal void ProcessTypeChange(Vehicle pType)
{
VDetailLog("{0},ProcessTypeChange,type={1}", m_prim.LocalID, pType);
// Set Defaults For Type
@@ -478,29 +478,30 @@ namespace OpenSim.Region.Physics.BulletSPlugin
MoveAngular(pTimestep);
LimitRotation(pTimestep);
+ // remember the position so next step we can limit absolute movement effects
+ m_lastPositionVector = m_prim.Position;
+
VDetailLog("{0},BSDynamics.Step,done,pos={1},force={2},velocity={3},angvel={4}",
m_prim.LocalID, m_prim.Position, m_prim.Force, m_prim.Velocity, m_prim.RotationalVelocity);
}// end Step
private void MoveLinear(float pTimestep)
{
- // requested m_linearMotorDirection is significant
- // if (!m_linearMotorDirection.ApproxEquals(Vector3.Zero, 0.01f))
- if (m_linearMotorDirection.LengthSquared() > 0.0001f)
+ // m_linearMotorDirection is the direction we are moving relative to the vehicle coordinates
+ // m_lastLinearVelocityVector is the speed we are moving in that direction
+ if (m_linearMotorDirection.LengthSquared() > 0.001f)
{
Vector3 origDir = m_linearMotorDirection;
Vector3 origVel = m_lastLinearVelocityVector;
// add drive to body
- // Vector3 addAmount = m_linearMotorDirection/(m_linearMotorTimescale/pTimestep);
- Vector3 addAmount = m_linearMotorDirection/(m_linearMotorTimescale);
- // lastLinearVelocityVector is the current body velocity vector?
+ // Vector3 addAmount = m_linearMotorDirection/(m_linearMotorTimescale / pTimestep);
+ Vector3 addAmount = (m_linearMotorDirection - m_lastLinearVelocityVector)/(m_linearMotorTimescale / pTimestep);
+ // lastLinearVelocityVector is the current body velocity vector
// RA: Not sure what the *10 is for. A correction for pTimestep?
// m_lastLinearVelocityVector += (addAmount*10);
m_lastLinearVelocityVector += addAmount;
- // This will work temporarily, but we really need to compare speed on an axis
- // KF: Limit body velocity to applied velocity?
// Limit the velocity vector to less than the last set linear motor direction
if (Math.Abs(m_lastLinearVelocityVector.X) > Math.Abs(m_linearMotorDirectionLASTSET.X))
m_lastLinearVelocityVector.X = m_linearMotorDirectionLASTSET.X;
@@ -509,53 +510,108 @@ namespace OpenSim.Region.Physics.BulletSPlugin
if (Math.Abs(m_lastLinearVelocityVector.Z) > Math.Abs(m_linearMotorDirectionLASTSET.Z))
m_lastLinearVelocityVector.Z = m_linearMotorDirectionLASTSET.Z;
- // decay applied velocity
- Vector3 decayfraction = ((Vector3.One/(m_linearMotorDecayTimescale/pTimestep)));
- m_linearMotorDirection -= m_linearMotorDirection * decayfraction * 0.5f;
-
/*
- Vector3 addAmount = (m_linearMotorDirection - m_lastLinearVelocityVector)/m_linearMotorTimescale;
- m_lastLinearVelocityVector += addAmount;
-
- float decayfraction = (1.0f - 1.0f / m_linearMotorDecayTimescale);
- m_linearMotorDirection *= decayfraction;
-
+ // decay applied velocity
+ Vector3 decayfraction = Vector3.One/(m_linearMotorDecayTimescale / pTimestep);
+ // (RA: do not know where the 0.5f comes from)
+ m_linearMotorDirection -= m_linearMotorDirection * decayfraction * 0.5f;
*/
+ float keepfraction = 1.0f - (1.0f / (m_linearMotorDecayTimescale / pTimestep));
+ m_linearMotorDirection *= keepfraction;
- VDetailLog("{0},MoveLinear,nonZero,origdir={1},origvel={2},add={3},decay={4},dir={5},vel={6}",
- m_prim.LocalID, origDir, origVel, addAmount, decayfraction, m_linearMotorDirection, m_lastLinearVelocityVector);
+ VDetailLog("{0},MoveLinear,nonZero,origdir={1},origvel={2},add={3},notDecay={4},dir={5},vel={6}",
+ m_prim.LocalID, origDir, origVel, addAmount, keepfraction, m_linearMotorDirection, m_lastLinearVelocityVector);
}
else
{
- // if what remains of applied is small, zero it.
- // if (m_lastLinearVelocityVector.ApproxEquals(Vector3.Zero, 0.01f))
- // m_lastLinearVelocityVector = Vector3.Zero;
+ // if what remains of direction is very small, zero it.
m_linearMotorDirection = Vector3.Zero;
m_lastLinearVelocityVector = Vector3.Zero;
+ VDetailLog("{0},MoveLinear,zeroed", m_prim.LocalID);
}
// convert requested object velocity to object relative vector
Quaternion rotq = m_prim.Orientation;
- m_dir = m_lastLinearVelocityVector * rotq;
+ m_newVelocity = m_lastLinearVelocityVector * rotq;
// Add the various forces into m_dir which will be our new direction vector (velocity)
// add Gravity and Buoyancy
- // KF: So far I have found no good method to combine a script-requested
- // .Z velocity and gravity. Therefore only 0g will used script-requested
- // .Z velocity. >0g (m_VehicleBuoyancy < 1) will used modified gravity only.
- Vector3 grav = Vector3.Zero;
// There is some gravity, make a gravity force vector that is applied after object velocity.
// m_VehicleBuoyancy: -1=2g; 0=1g; 1=0g;
- grav.Z = m_prim.Scene.DefaultGravity.Z * m_prim.Mass * (1f - m_VehicleBuoyancy);
+ Vector3 grav = m_prim.Scene.DefaultGravity * (m_prim.Mass * (1f - m_VehicleBuoyancy));
+
+ /*
+ * RA: Not sure why one would do this
// Preserve the current Z velocity
Vector3 vel_now = m_prim.Velocity;
m_dir.Z = vel_now.Z; // Preserve the accumulated falling velocity
+ */
Vector3 pos = m_prim.Position;
- Vector3 posChange = pos;
// Vector3 accel = new Vector3(-(m_dir.X - m_lastLinearVelocityVector.X / 0.1f), -(m_dir.Y - m_lastLinearVelocityVector.Y / 0.1f), m_dir.Z - m_lastLinearVelocityVector.Z / 0.1f);
- double Zchange = Math.Abs(posChange.Z);
+
+ // If below the terrain, move us above the ground a little.
+ float terrainHeight = m_prim.Scene.TerrainManager.GetTerrainHeightAtXYZ(pos);
+ // Taking the rotated size doesn't work here because m_prim.Size is the size of the root prim and not the linkset.
+ // Need to add a m_prim.LinkSet.Size similar to m_prim.LinkSet.Mass.
+ // Vector3 rotatedSize = m_prim.Size * m_prim.Orientation;
+ // if (rotatedSize.Z < terrainHeight)
+ if (pos.Z < terrainHeight)
+ {
+ pos.Z = terrainHeight + 2;
+ m_prim.Position = pos;
+ VDetailLog("{0},MoveLinear,terrainHeight,terrainHeight={1},pos={2}", m_prim.LocalID, terrainHeight, pos);
+ }
+
+ // Check if hovering
+ if ((m_flags & (VehicleFlag.HOVER_WATER_ONLY | VehicleFlag.HOVER_TERRAIN_ONLY | VehicleFlag.HOVER_GLOBAL_HEIGHT)) != 0)
+ {
+ // We should hover, get the target height
+ if ((m_flags & VehicleFlag.HOVER_WATER_ONLY) != 0)
+ {
+ m_VhoverTargetHeight = m_prim.Scene.GetWaterLevelAtXYZ(pos) + m_VhoverHeight;
+ }
+ if ((m_flags & VehicleFlag.HOVER_TERRAIN_ONLY) != 0)
+ {
+ m_VhoverTargetHeight = terrainHeight + m_VhoverHeight;
+ }
+ if ((m_flags & VehicleFlag.HOVER_GLOBAL_HEIGHT) != 0)
+ {
+ m_VhoverTargetHeight = m_VhoverHeight;
+ }
+
+ if ((m_flags & VehicleFlag.HOVER_UP_ONLY) != 0)
+ {
+ // If body is aready heigher, use its height as target height
+ if (pos.Z > m_VhoverTargetHeight) m_VhoverTargetHeight = pos.Z;
+ }
+ if ((m_flags & VehicleFlag.LOCK_HOVER_HEIGHT) != 0)
+ {
+ if ((pos.Z - m_VhoverTargetHeight) > .2 || (pos.Z - m_VhoverTargetHeight) < -.2)
+ {
+ m_prim.Position = pos;
+ }
+ }
+ else
+ {
+ float herr0 = pos.Z - m_VhoverTargetHeight;
+ // Replace Vertical speed with correction figure if significant
+ if (Math.Abs(herr0) > 0.01f)
+ {
+ m_newVelocity.Z = -((herr0 * pTimestep * 50.0f) / m_VhoverTimescale);
+ //KF: m_VhoverEfficiency is not yet implemented
+ }
+ else
+ {
+ m_newVelocity.Z = 0f;
+ }
+ }
+
+ VDetailLog("{0},MoveLinear,hover,pos={1},dir={2},height={3},target={4}", m_prim.LocalID, pos, m_newVelocity, m_VhoverHeight, m_VhoverTargetHeight);
+ }
+
+ Vector3 posChange = pos - m_lastPositionVector;
if (m_BlockingEndPoint != Vector3.Zero)
{
bool changed = false;
@@ -592,125 +648,43 @@ namespace OpenSim.Region.Physics.BulletSPlugin
}
}
- // If below the terrain, move us above the ground a little.
- if (pos.Z < m_prim.Scene.TerrainManager.GetTerrainHeightAtXYZ(pos))
- {
- pos.Z = m_prim.Scene.TerrainManager.GetTerrainHeightAtXYZ(pos) + 2;
- m_prim.Position = pos;
- VDetailLog("{0},MoveLinear,terrainHeight,pos={1}", m_prim.LocalID, pos);
- }
-
- // Check if hovering
- if ((m_flags & (VehicleFlag.HOVER_WATER_ONLY | VehicleFlag.HOVER_TERRAIN_ONLY | VehicleFlag.HOVER_GLOBAL_HEIGHT)) != 0)
- {
- // We should hover, get the target height
- if ((m_flags & VehicleFlag.HOVER_WATER_ONLY) != 0)
- {
- m_VhoverTargetHeight = m_prim.Scene.GetWaterLevel() + m_VhoverHeight;
- }
- if ((m_flags & VehicleFlag.HOVER_TERRAIN_ONLY) != 0)
- {
- m_VhoverTargetHeight = m_prim.Scene.TerrainManager.GetTerrainHeightAtXY(pos.X, pos.Y) + m_VhoverHeight;
- }
- if ((m_flags & VehicleFlag.HOVER_GLOBAL_HEIGHT) != 0)
- {
- m_VhoverTargetHeight = m_VhoverHeight;
- }
-
- if ((m_flags & VehicleFlag.HOVER_UP_ONLY) != 0)
- {
- // If body is aready heigher, use its height as target height
- if (pos.Z > m_VhoverTargetHeight) m_VhoverTargetHeight = pos.Z;
- }
- if ((m_flags & VehicleFlag.LOCK_HOVER_HEIGHT) != 0)
- {
- if ((pos.Z - m_VhoverTargetHeight) > .2 || (pos.Z - m_VhoverTargetHeight) < -.2)
- {
- m_prim.Position = pos;
- }
- }
- else
- {
- float herr0 = pos.Z - m_VhoverTargetHeight;
- // Replace Vertical speed with correction figure if significant
- if (Math.Abs(herr0) > 0.01f)
- {
- m_dir.Z = -((herr0 * pTimestep * 50.0f) / m_VhoverTimescale);
- //KF: m_VhoverEfficiency is not yet implemented
- }
- else
- {
- m_dir.Z = 0f;
- }
- }
-
- VDetailLog("{0},MoveLinear,hover,pos={1},dir={2},height={3},target={4}", m_prim.LocalID, pos, m_dir, m_VhoverHeight, m_VhoverTargetHeight);
-
-// m_VhoverEfficiency = 0f; // 0=boucy, 1=Crit.damped
-// m_VhoverTimescale = 0f; // time to acheive height
-// pTimestep is time since last frame,in secs
- }
-
+ float Zchange = Math.Abs(posChange.Z);
if ((m_flags & (VehicleFlag.LIMIT_MOTOR_UP)) != 0)
{
- //Start Experimental Values
if (Zchange > .3)
- {
grav.Z = (float)(grav.Z * 3);
- }
if (Zchange > .15)
- {
grav.Z = (float)(grav.Z * 2);
- }
if (Zchange > .75)
- {
grav.Z = (float)(grav.Z * 1.5);
- }
if (Zchange > .05)
- {
grav.Z = (float)(grav.Z * 1.25);
- }
if (Zchange > .025)
- {
grav.Z = (float)(grav.Z * 1.125);
- }
- float terraintemp = m_prim.Scene.TerrainManager.GetTerrainHeightAtXYZ(pos);
- float postemp = (pos.Z - terraintemp);
+ float postemp = (pos.Z - terrainHeight);
if (postemp > 2.5f)
- {
grav.Z = (float)(grav.Z * 1.037125);
- }
VDetailLog("{0},MoveLinear,limitMotorUp,grav={1}", m_prim.LocalID, grav);
- //End Experimental Values
}
if ((m_flags & (VehicleFlag.NO_X)) != 0)
- {
- m_dir.X = 0;
- }
+ m_newVelocity.X = 0;
if ((m_flags & (VehicleFlag.NO_Y)) != 0)
- {
- m_dir.Y = 0;
- }
+ m_newVelocity.Y = 0;
if ((m_flags & (VehicleFlag.NO_Z)) != 0)
- {
- m_dir.Z = 0;
- }
-
- m_lastPositionVector = m_prim.Position;
+ m_newVelocity.Z = 0;
// Apply velocity
- m_prim.Velocity = m_dir;
+ m_prim.Velocity = m_newVelocity;
// apply gravity force
// Why is this set here? The physics engine already does gravity.
// m_prim.AddForce(grav, false);
- // m_prim.Force = grav;
// Apply friction
- Vector3 decayamount = Vector3.One / (m_linearFrictionTimescale / pTimestep);
- m_lastLinearVelocityVector -= m_lastLinearVelocityVector * decayamount;
+ Vector3 keepFraction = Vector3.One - (Vector3.One / (m_linearFrictionTimescale / pTimestep));
+ m_lastLinearVelocityVector *= keepFraction;
- VDetailLog("{0},MoveLinear,done,pos={1},vel={2},force={3},decay={4}",
- m_prim.LocalID, m_lastPositionVector, m_dir, grav, decayamount);
+ VDetailLog("{0},MoveLinear,done,lmDir={1},lmVel={2},newVel={3},grav={4},1Mdecay={5}",
+ m_prim.LocalID, m_linearMotorDirection, m_lastLinearVelocityVector, m_newVelocity, grav, keepFraction);
} // end MoveLinear()
@@ -735,17 +709,18 @@ namespace OpenSim.Region.Physics.BulletSPlugin
// There are m_angularMotorApply steps.
Vector3 origAngularVelocity = m_angularMotorVelocity;
// ramp up to new value
- // current velocity += error / (time to get there / step interval)
- // requested speed - last motor speed
+ // current velocity += error / ( time to get there / step interval)
+ // requested speed - last motor speed
m_angularMotorVelocity.X += (m_angularMotorDirection.X - m_angularMotorVelocity.X) / (m_angularMotorTimescale / pTimestep);
m_angularMotorVelocity.Y += (m_angularMotorDirection.Y - m_angularMotorVelocity.Y) / (m_angularMotorTimescale / pTimestep);
m_angularMotorVelocity.Z += (m_angularMotorDirection.Z - m_angularMotorVelocity.Z) / (m_angularMotorTimescale / pTimestep);
- VDetailLog("{0},MoveAngular,angularMotorApply,apply={1},origvel={2},dir={3},vel={4}",
- m_prim.LocalID,m_angularMotorApply,origAngularVelocity, m_angularMotorDirection, m_angularMotorVelocity);
+ VDetailLog("{0},MoveAngular,angularMotorApply,apply={1},angTScale={2},timeStep={3},origvel={4},dir={5},vel={6}",
+ m_prim.LocalID, m_angularMotorApply, m_angularMotorTimescale, pTimestep, origAngularVelocity, m_angularMotorDirection, m_angularMotorVelocity);
- m_angularMotorApply--; // This is done so that if script request rate is less than phys frame rate the expected
- // velocity may still be acheived.
+ // This is done so that if script request rate is less than phys frame rate the expected
+ // velocity may still be acheived.
+ m_angularMotorApply--;
}
else
{
@@ -760,7 +735,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
Vector3 vertattr = Vector3.Zero;
if (m_verticalAttractionTimescale < 300)
{
- float VAservo = 0.2f / (m_verticalAttractionTimescale * pTimestep);
+ float VAservo = 0.2f / (m_verticalAttractionTimescale / pTimestep);
// get present body rotation
Quaternion rotq = m_prim.Orientation;
// make a vector pointing up
@@ -863,16 +838,12 @@ namespace OpenSim.Region.Physics.BulletSPlugin
m_rot.Y = 0;
changed = true;
}
- if ((m_flags & VehicleFlag.LOCK_ROTATION) != 0)
- {
- m_rot.X = 0;
- m_rot.Y = 0;
- changed = true;
- }
if (changed)
+ {
m_prim.Orientation = m_rot;
+ VDetailLog("{0},LimitRotation,done,orig={1},new={2}", m_prim.LocalID, rotq, m_rot);
+ }
- VDetailLog("{0},LimitRotation,done,changed={1},orig={2},new={3}", m_prim.LocalID, changed, rotq, m_rot);
}
// Invoke the detailed logger and output something if it's enabled.
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs b/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs
index 5f6601d631..7e784ebb83 100755
--- a/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs
@@ -202,11 +202,33 @@ public class BSLinkset
return com;
}
+ // The object is going dynamic (physical). Do any setup necessary
+ // for a dynamic linkset.
+ // Only the state of the passed object can be modified. The rest of the linkset
+ // has not yet been fully constructed.
+ // Return 'true' if any properties updated on the passed object.
+ // Called at taint-time!
+ public bool MakeDynamic(BSPhysObject child)
+ {
+ bool ret = false;
+ return ret;
+ }
+
+ // The object is going static (non-physical). Do any setup necessary
+ // for a static linkset.
+ // Return 'true' if any properties updated on the passed object.
+ // Called at taint-time!
+ public bool MakeStatic(BSPhysObject child)
+ {
+ // What is done for each object in BSPrim is what we want.
+ return false;
+ }
+
// When physical properties are changed the linkset needs to recalculate
// its internal properties.
public void Refresh(BSPhysObject requestor)
{
- // If there are no children, there aren't any constraints to recompute
+ // If there are no children, there can't be any constraints to recompute
if (!HasAnyChildren)
return;
@@ -225,11 +247,12 @@ public class BSLinkset
// from a linkset to make sure the constraints know about the new mass and
// geometry.
// Must only be called at taint time!!
- private bool RecomputeLinksetConstraintVariables()
+ private void RecomputeLinksetConstraintVariables()
{
float linksetMass = LinksetMass;
lock (m_linksetActivityLock)
{
+ bool somethingMissing = false;
foreach (BSPhysObject child in m_children)
{
BSConstraint constrain;
@@ -241,16 +264,36 @@ public class BSLinkset
}
else
{
- // Non-fatal error that can happen when children are being added to the linkset but
+ // Non-fatal error that happens when children are being added to the linkset but
// their constraints have not been created yet.
// Caused by the fact that m_children is built at run time but building constraints
// happens at taint time.
- // m_physicsScene.Logger.ErrorFormat("{0} RecomputeLinksetConstraintVariables: constraint not found for root={1}, child={2}",
- // LogHeader, m_linksetRoot.Body.ID, child.Body.ID);
+ somethingMissing = true;
+ break;
}
}
+
+ // If the whole linkset is not here, doesn't make sense to recompute linkset wide values
+ if (!somethingMissing)
+ {
+ // If this is a multiple object linkset, set everybody's center of mass to the set's center of mass
+ OMV.Vector3 centerOfMass = ComputeLinksetCenterOfMass();
+ BulletSimAPI.SetCenterOfMassByPosRot2(LinksetRoot.BSBody.Ptr, centerOfMass, OMV.Quaternion.Identity);
+ foreach (BSPhysObject child in m_children)
+ {
+ BulletSimAPI.SetCenterOfMassByPosRot2(child.BSBody.Ptr, centerOfMass, OMV.Quaternion.Identity);
+ }
+ /*
+ // The root prim takes on the weight of the whole linkset
+ OMV.Vector3 inertia = BulletSimAPI.CalculateLocalInertia2(LinksetRoot.BSShape.Ptr, linksetMass);
+ BulletSimAPI.SetMassProps2(LinksetRoot.BSBody.Ptr, linksetMass, inertia);
+ OMV.Vector3 centerOfMass = ComputeLinksetCenterOfMass();
+ BulletSimAPI.SetCenterOfMassByPosRot2(LinksetRoot.BSBody.Ptr, centerOfMass, OMV.Quaternion.Identity);
+ BulletSimAPI.UpdateInertiaTensor2(LinksetRoot.BSBody.Ptr);
+ */
+ }
}
- return false;
+ return;
}
// I am the root of a linkset and a new child is being added
@@ -296,9 +339,9 @@ public class BSLinkset
DetailLog("{0},RemoveChildFromLinkset,taint,child={1}", m_linksetRoot.LocalID, child.LocalID);
PhysicallyUnlinkAChildFromRoot(rootx, childx);
+ RecomputeLinksetConstraintVariables();
});
- RecomputeLinksetConstraintVariables();
}
else
{
@@ -377,6 +420,10 @@ public class BSLinkset
PhysicsScene.Params.linkConstraintTransMotorMaxVel,
PhysicsScene.Params.linkConstraintTransMotorMaxForce);
constrain.SetCFMAndERP(PhysicsScene.Params.linkConstraintCFM, PhysicsScene.Params.linkConstraintERP);
+ if (PhysicsScene.Params.linkConstraintSolverIterations != 0f)
+ {
+ constrain.SetSolverIterations(PhysicsScene.Params.linkConstraintSolverIterations);
+ }
RecomputeLinksetConstraintVariables();
}
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs
index e411fcb3df..3fe71e1003 100755
--- a/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs
@@ -41,7 +41,7 @@ public abstract class BSPhysObject : PhysicsActor
{
public abstract BSLinkset Linkset { get; set; }
- public abstract void Collide(uint collidingWith, BSPhysObject collidee, ActorTypes type,
+ public abstract bool Collide(uint collidingWith, BSPhysObject collidee,
OMV.Vector3 contactPoint, OMV.Vector3 contactNormal, float pentrationDepth);
public abstract void SendCollisions();
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs
index 6d0af63986..26a581fc77 100644
--- a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs
@@ -136,11 +136,11 @@ public sealed class BSPrim : BSPhysObject
Linkset = new BSLinkset(Scene, this); // a linkset of one
_vehicle = new BSDynamics(Scene, this); // add vehicleness
_mass = CalculateMass();
- // do the actual object creation at taint time
DetailLog("{0},BSPrim.constructor,call", LocalID);
+ // do the actual object creation at taint time
_scene.TaintedObject("BSPrim.create", delegate()
{
- RecreateGeomAndObject();
+ CreateGeomAndObject(true);
// Get the pointer to the physical body for this object.
// At the moment, we're still letting BulletSim manage the creation and destruction
@@ -186,9 +186,10 @@ public sealed class BSPrim : BSPhysObject
_scene.TaintedObject("BSPrim.setSize", delegate()
{
_mass = CalculateMass(); // changing size changes the mass
- BulletSimAPI.SetObjectScaleMass(_scene.WorldID, _localID, _scale, (IsPhysical ? _mass : 0f), IsPhysical);
- DetailLog("{0}: BSPrim.setSize: size={1}, mass={2}, physical={3}", LocalID, _size, _mass, IsPhysical);
- RecreateGeomAndObject();
+ // Since _size changed, the mesh needs to be rebuilt. If rebuilt, all the correct
+ // scale and margins are set.
+ CreateGeomAndObject(true);
+ DetailLog("{0}: BSPrim.setSize: size={1}, scale={2}, mass={3}, physical={4}", LocalID, _size, _scale, _mass, IsPhysical);
});
}
}
@@ -198,7 +199,7 @@ public sealed class BSPrim : BSPhysObject
_scene.TaintedObject("BSPrim.setShape", delegate()
{
_mass = CalculateMass(); // changing the shape changes the mass
- RecreateGeomAndObject();
+ CreateGeomAndObject(false);
});
}
}
@@ -279,7 +280,7 @@ public sealed class BSPrim : BSPhysObject
get {
if (!Linkset.IsRoot(this))
// child prims move around based on their parent. Need to get the latest location
- _position = BulletSimAPI.GetObjectPosition(_scene.WorldID, _localID);
+ _position = BulletSimAPI.GetPosition2(BSBody.Ptr);
// don't do the GetObjectPosition for root elements because this function is called a zillion times
// _position = BulletSimAPI.GetObjectPosition(_scene.WorldID, _localID);
@@ -291,7 +292,7 @@ public sealed class BSPrim : BSPhysObject
_scene.TaintedObject("BSPrim.setPosition", delegate()
{
DetailLog("{0},BSPrim.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation);
- BulletSimAPI.SetObjectTranslation(_scene.WorldID, _localID, _position, _orientation);
+ BulletSimAPI.SetTranslation2(BSBody.Ptr, _position, _orientation);
});
}
}
@@ -302,7 +303,8 @@ public sealed class BSPrim : BSPhysObject
{
get
{
- return Linkset.LinksetMass;
+ // return Linkset.LinksetMass;
+ return _mass;
}
}
@@ -328,7 +330,6 @@ public sealed class BSPrim : BSPhysObject
_scene.TaintedObject("BSPrim.setForce", delegate()
{
DetailLog("{0},BSPrim.setForce,taint,force={1}", LocalID, _force);
- // BulletSimAPI.SetObjectForce(_scene.WorldID, _localID, _force);
BulletSimAPI.SetObjectForce2(BSBody.Ptr, _force);
});
}
@@ -345,7 +346,7 @@ public sealed class BSPrim : BSPhysObject
{
// Done at taint time so we're sure the physics engine is not using the variables
// Vehicle code changes the parameters for this vehicle type.
- _vehicle.ProcessTypeChange(type, Scene.LastSimulatedTimestep);
+ _vehicle.ProcessTypeChange(type);
// Tell the scene about the vehicle so it will get processing each frame.
_scene.VehicleInSceneTypeChanged(this, type);
});
@@ -355,14 +356,14 @@ public sealed class BSPrim : BSPhysObject
{
_scene.TaintedObject("BSPrim.VehicleFloatParam", delegate()
{
- _vehicle.ProcessFloatVehicleParam((Vehicle)param, value, _scene.LastSimulatedTimestep);
+ _vehicle.ProcessFloatVehicleParam((Vehicle)param, value);
});
}
public override void VehicleVectorParam(int param, OMV.Vector3 value)
{
_scene.TaintedObject("BSPrim.VehicleVectorParam", delegate()
{
- _vehicle.ProcessVectorVehicleParam((Vehicle)param, value, _scene.LastSimulatedTimestep);
+ _vehicle.ProcessVectorVehicleParam((Vehicle)param, value);
});
}
public override void VehicleRotationParam(int param, OMV.Quaternion rotation)
@@ -406,7 +407,7 @@ public sealed class BSPrim : BSPhysObject
_scene.TaintedObject("BSPrim.setVelocity", delegate()
{
DetailLog("{0},BSPrim.SetVelocity,taint,vel={1}", LocalID, _velocity);
- BulletSimAPI.SetObjectVelocity(_scene.WorldID, LocalID, _velocity);
+ BulletSimAPI.SetLinearVelocity2(BSBody.Ptr, _velocity);
});
}
}
@@ -430,7 +431,7 @@ public sealed class BSPrim : BSPhysObject
if (!Linkset.IsRoot(this))
{
// Children move around because tied to parent. Get a fresh value.
- _orientation = BulletSimAPI.GetObjectOrientation(_scene.WorldID, LocalID);
+ _orientation = BulletSimAPI.GetOrientation2(BSBody.Ptr);
}
return _orientation;
}
@@ -441,7 +442,7 @@ public sealed class BSPrim : BSPhysObject
{
// _position = BulletSimAPI.GetObjectPosition(_scene.WorldID, _localID);
DetailLog("{0},BSPrim.setOrientation,taint,pos={1},orient={2}", LocalID, _position, _orientation);
- BulletSimAPI.SetObjectTranslation(_scene.WorldID, _localID, _position, _orientation);
+ BulletSimAPI.SetTranslation2(BSBody.Ptr, _position, _orientation);
});
}
}
@@ -483,31 +484,37 @@ public sealed class BSPrim : BSPhysObject
{
// If it's becoming dynamic, it will need hullness
VerifyCorrectPhysicalShape();
+ UpdatePhysicalParameters();
+ }
+ private void UpdatePhysicalParameters()
+ {
+ /*
// Bullet wants static objects to have a mass of zero
float mass = IsStatic ? 0f : _mass;
BulletSimAPI.SetObjectProperties(_scene.WorldID, LocalID, IsStatic, IsSolid, SubscribedEvents(), mass);
- /*
+ */
BulletSimAPI.RemoveObjectFromWorld2(Scene.World.Ptr, BSBody.Ptr);
- // Set up the object physicalness (static or dynamic)
- MakeDynamic();
+ // Set up the object physicalness (does gravity and collisions move this object)
+ MakeDynamic(IsStatic);
- // Make solid or not and arrange for collisions, etc
- MakeSolid();
+ // Make solid or not (do things bounce off or pass through this object)
+ MakeSolid(IsSolid);
- m_currentCollisionFlags = BulletSimAPI.GetCollisionFlags2(BSBody.Ptr);
+ // Arrange for collisions events if the simulator wants them
+ EnableCollisions(SubscribedEvents());
BulletSimAPI.AddObjectToWorld2(Scene.World.Ptr, BSBody.Ptr);
- */
// Recompute any linkset parameters.
// When going from non-physical to physical, this re-enables the constraints that
// had been automatically disabled when the mass was set to zero.
Linkset.Refresh(this);
- DetailLog("{0},BSPrim.SetObjectDynamic,taint,static={1},solid={2},mass={3}, cf={4}", LocalID, IsStatic, IsSolid, mass, m_currentCollisionFlags);
+ DetailLog("{0},BSPrim.UpdatePhysicalParameters,taint,static={1},solid={2},mass={3}, cf={4}",
+ LocalID, IsStatic, IsSolid, _mass, m_currentCollisionFlags);
}
// "Making dynamic" means changing to and from static.
@@ -515,39 +522,84 @@ public sealed class BSPrim : BSPhysObject
// When dynamic, the object can fall and be pushed by others.
// This is independent of its 'solidness' which controls what passes through
// this object and what interacts with it.
- private void MakeDynamic()
+ private void MakeDynamic(bool makeStatic)
{
- if (IsStatic)
+ if (makeStatic)
{
// Become a Bullet 'static' object type
- BulletSimAPI.AddToCollisionFlags2(BSBody.Ptr, CollisionFlags.CF_STATIC_OBJECT);
+ m_currentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(BSBody.Ptr, CollisionFlags.CF_STATIC_OBJECT);
// Stop all movement
BulletSimAPI.ClearAllForces2(BSBody.Ptr);
+ // Center of mass is at the center of the object
+ BulletSimAPI.SetCenterOfMassByPosRot2(Linkset.LinksetRoot.BSBody.Ptr, _position, _orientation);
// Mass is zero which disables a bunch of physics stuff in Bullet
BulletSimAPI.SetMassProps2(BSBody.Ptr, 0f, OMV.Vector3.Zero);
// There is no inertia in a static object
BulletSimAPI.UpdateInertiaTensor2(BSBody.Ptr);
+ // There can be special things needed for implementing linksets
+ Linkset.MakeStatic(this);
// The activation state is 'sleeping' so Bullet will not try to act on it
BulletSimAPI.ForceActivationState2(BSBody.Ptr, ActivationState.ISLAND_SLEEPING);
}
else
{
// Not a Bullet static object
- BulletSimAPI.RemoveFromCollisionFlags2(BSBody.Ptr, CollisionFlags.CF_STATIC_OBJECT);
+ m_currentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(BSBody.Ptr, CollisionFlags.CF_STATIC_OBJECT);
+
+ // Set various physical properties so internal things will get computed correctly as they are set
+ BulletSimAPI.SetFriction2(BSBody.Ptr, Scene.Params.defaultFriction);
+ BulletSimAPI.SetRestitution2(BSBody.Ptr, Scene.Params.defaultRestitution);
+ // per http://www.bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=3382
+ BulletSimAPI.SetInterpolationLinearVelocity2(BSBody.Ptr, OMV.Vector3.Zero);
+ BulletSimAPI.SetInterpolationAngularVelocity2(BSBody.Ptr, OMV.Vector3.Zero);
+ BulletSimAPI.SetInterpolationVelocity2(BSBody.Ptr, OMV.Vector3.Zero, OMV.Vector3.Zero);
+
// A dynamic object has mass
- BulletSimAPI.SetMassProps2(BSBody.Ptr, _mass, OMV.Vector3.Zero);
- // The shape is interesting and has mass and a center of gravity
IntPtr collisionShapePtr = BulletSimAPI.GetCollisionShape2(BSBody.Ptr);
- BulletSimAPI.CalculateLocalInertia2(collisionShapePtr, _mass, OMV.Vector3.Zero);
+ OMV.Vector3 inertia = BulletSimAPI.CalculateLocalInertia2(collisionShapePtr, Linkset.LinksetMass);
+ BulletSimAPI.SetMassProps2(BSBody.Ptr, _mass, inertia);
// Inertia is based on our new mass
BulletSimAPI.UpdateInertiaTensor2(BSBody.Ptr);
+
+ // Various values for simulation limits
+ BulletSimAPI.SetDamping2(BSBody.Ptr, Scene.Params.linearDamping, Scene.Params.angularDamping);
+ BulletSimAPI.SetDeactivationTime2(BSBody.Ptr, Scene.Params.deactivationTime);
+ BulletSimAPI.SetSleepingThresholds2(BSBody.Ptr, Scene.Params.linearSleepingThreshold, Scene.Params.angularSleepingThreshold);
+ BulletSimAPI.SetContactProcessingThreshold2(BSBody.Ptr, Scene.Params.contactProcessingThreshold);
+
+ // There can be special things needed for implementing linksets
+ Linkset.MakeDynamic(this);
+
// Force activation of the object so Bullet will act on it.
BulletSimAPI.Activate2(BSBody.Ptr, true);
}
}
- private void MakeSolid()
+ // "Making solid" means that other object will not pass through this object.
+ private void MakeSolid(bool makeSolid)
{
+ if (makeSolid)
+ {
+ // Easy in Bullet -- just remove the object flag that controls collision response
+ m_currentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(BSBody.Ptr, CollisionFlags.CF_NO_CONTACT_RESPONSE);
+ }
+ else
+ {
+ m_currentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(BSBody.Ptr, CollisionFlags.CF_NO_CONTACT_RESPONSE);
+ }
+ }
+
+ // Turn on or off the flag controlling whether collision events are returned to the simulator.
+ private void EnableCollisions(bool wantsCollisionEvents)
+ {
+ if (wantsCollisionEvents)
+ {
+ m_currentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(BSBody.Ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
+ }
+ else
+ {
+ m_currentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(BSBody.Ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
+ }
}
// prims don't fly
@@ -607,7 +659,7 @@ public sealed class BSPrim : BSPhysObject
_scene.TaintedObject("BSPrim.setRotationalVelocity", delegate()
{
DetailLog("{0},BSPrim.SetRotationalVel,taint,rotvel={1}", LocalID, _rotationalVelocity);
- BulletSimAPI.SetObjectAngularVelocity(_scene.WorldID, LocalID, _rotationalVelocity);
+ BulletSimAPI.SetAngularVelocity2(BSBody.Ptr, _rotationalVelocity);
});
}
}
@@ -624,7 +676,10 @@ public sealed class BSPrim : BSPhysObject
_scene.TaintedObject("BSPrim.setBuoyancy", delegate()
{
DetailLog("{0},BSPrim.SetBuoyancy,taint,buoy={1}", LocalID, _buoyancy);
- BulletSimAPI.SetObjectBuoyancy(_scene.WorldID, _localID, _buoyancy);
+ // Buoyancy is faked by changing the gravity applied to the object
+ float grav = Scene.Params.gravity * (1f - _buoyancy);
+ BulletSimAPI.SetGravity2(BSBody.Ptr, new OMV.Vector3(0f, 0f, grav));
+ // BulletSimAPI.SetObjectBuoyancy(_scene.WorldID, _localID, _buoyancy);
});
}
}
@@ -686,8 +741,8 @@ public sealed class BSPrim : BSPhysObject
}
m_accumulatedForces.Clear();
}
- DetailLog("{0},BSPrim.AddObjectForce,taint,force={1}", LocalID, _force);
- // For unknown reason, "ApplyCentralForce" is really additive.
+ DetailLog("{0},BSPrim.AddObjectForce,taint,force={1}", LocalID, fSum);
+ // For unknown reasons, "ApplyCentralForce" adds this force to the total force on the object.
BulletSimAPI.ApplyCentralForce2(BSBody.Ptr, fSum);
});
}
@@ -1023,36 +1078,44 @@ public sealed class BSPrim : BSPhysObject
}// end CalculateMass
#endregion Mass Calculation
- // Create the geometry information in Bullet for later use
- // The objects needs a hull if it's physical otherwise a mesh is enough
- // No locking here because this is done when we know physics is not simulating
- // if 'forceRebuild' is true, the geometry is rebuilt. Otherwise a previously built version is used
- // Returns 'true' if the geometry was rebuilt
+ // Create the geometry information in Bullet for later use.
+ // The objects needs a hull if it's physical otherwise a mesh is enough.
+ // No locking here because this is done when we know physics is not simulating.
+ // if 'forceRebuild' is true, the geometry is rebuilt. Otherwise a previously built version is used.
+ // Returns 'true' if the geometry was rebuilt.
+ // Called at taint-time!
private bool CreateGeom(bool forceRebuild)
{
- // the mesher thought this was too simple to mesh. Use a native Bullet collision shape.
bool ret = false;
- if (!_scene.NeedsMeshing(_pbs))
+ bool haveShape = false;
+
+ // If the prim attributes are simple, this could be a simple Bullet native shape
+ if ((_pbs.SculptEntry && !Scene.ShouldMeshSculptedPrim)
+ || (_pbs.ProfileBegin == 0 && _pbs.ProfileEnd == 0
+ && _pbs.ProfileHollow == 0
+ && _pbs.PathTwist == 0 && _pbs.PathTwistBegin == 0
+ && _pbs.PathBegin == 0 && _pbs.PathEnd == 0
+ && _pbs.PathTaperX == 0 && _pbs.PathTaperY == 0
+ && _pbs.PathScaleX == 100 && _pbs.PathScaleY == 100
+ && _pbs.PathShearX == 0 && _pbs.PathShearY == 0) )
{
if (_pbs.ProfileShape == ProfileShape.HalfCircle && _pbs.PathCurve == (byte)Extrusion.Curve1)
{
- // if (_size.X == _size.Y && _size.Y == _size.Z && _size.X == _size.Z)
- // {
- // m_log.DebugFormat("{0}: CreateGeom: Defaulting to sphere of size {1}", LogHeader, _size);
- if (forceRebuild || (_shapeType != ShapeData.PhysicsShapeType.SHAPE_SPHERE))
- {
- DetailLog("{0},BSPrim.CreateGeom,sphere (force={1}", LocalID, forceRebuild);
- _shapeType = ShapeData.PhysicsShapeType.SHAPE_SPHERE;
- // Bullet native objects are scaled by the Bullet engine so pass the size in
- _scale = _size;
- // TODO: do we need to check for and destroy a mesh or hull that might have been left from before?
- ret = true;
- }
- // }
+ haveShape = true;
+ if (forceRebuild || (_shapeType != ShapeData.PhysicsShapeType.SHAPE_SPHERE))
+ {
+ DetailLog("{0},BSPrim.CreateGeom,sphere (force={1}", LocalID, forceRebuild);
+ _shapeType = ShapeData.PhysicsShapeType.SHAPE_SPHERE;
+ // Bullet native objects are scaled by the Bullet engine so pass the size in
+ _scale = _size;
+ // TODO: do we need to check for and destroy a mesh or hull that might have been left from before?
+ ret = true;
+ }
}
else
{
// m_log.DebugFormat("{0}: CreateGeom: Defaulting to box. lid={1}, type={2}, size={3}", LogHeader, LocalID, _shapeType, _size);
+ haveShape = true;
if (forceRebuild || (_shapeType != ShapeData.PhysicsShapeType.SHAPE_BOX))
{
DetailLog("{0},BSPrim.CreateGeom,box (force={1})", LocalID, forceRebuild);
@@ -1063,16 +1126,16 @@ public sealed class BSPrim : BSPhysObject
}
}
}
- else
+ // If a simple shape isn't happening, create a mesh and possibly a hull
+ if (!haveShape)
{
if (IsPhysical)
{
if (forceRebuild || _hullKey == 0)
{
// physical objects require a hull for interaction.
- // This will create the mesh if it doesn't already exist
- CreateGeomHull();
- ret = true;
+ // This also creates the mesh if it doesn't already exist
+ ret = CreateGeomHull();
}
}
else
@@ -1080,8 +1143,7 @@ public sealed class BSPrim : BSPhysObject
if (forceRebuild || _meshKey == 0)
{
// Static (non-physical) objects only need a mesh for bumping into
- CreateGeomMesh();
- ret = true;
+ ret = CreateGeomMesh();
}
}
}
@@ -1089,7 +1151,9 @@ public sealed class BSPrim : BSPhysObject
}
// No locking here because this is done when we know physics is not simulating
- private void CreateGeomMesh()
+ // Returns 'true' of a mesh was actually rebuild (we could also have one of these specs).
+ // Called at taint-time!
+ private bool CreateGeomMesh()
{
// level of detail based on size and type of the object
float lod = _scene.MeshLOD;
@@ -1103,7 +1167,7 @@ public sealed class BSPrim : BSPhysObject
// m_log.DebugFormat("{0}: CreateGeomMesh: lID={1}, oldKey={2}, newKey={3}", LogHeader, _localID, _meshKey, newMeshKey);
// if this new shape is the same as last time, don't recreate the mesh
- if (_meshKey == newMeshKey) return;
+ if (_meshKey == newMeshKey) return false;
DetailLog("{0},BSPrim.CreateGeomMesh,create,key={1}", LocalID, newMeshKey);
// Since we're recreating new, get rid of any previously generated shape
@@ -1140,19 +1204,19 @@ public sealed class BSPrim : BSPhysObject
_shapeType = ShapeData.PhysicsShapeType.SHAPE_MESH;
// meshes are already scaled by the meshmerizer
_scale = new OMV.Vector3(1f, 1f, 1f);
- DetailLog("{0},BSPrim.CreateGeomMesh,done", LocalID);
- return;
+ return true;
}
// No locking here because this is done when we know physics is not simulating
- private void CreateGeomHull()
+ // Returns 'true' of a mesh was actually rebuild (we could also have one of these specs).
+ private bool CreateGeomHull()
{
float lod = _pbs.SculptEntry ? _scene.SculptLOD : _scene.MeshLOD;
ulong newHullKey = (ulong)_pbs.GetMeshKey(_size, lod);
// m_log.DebugFormat("{0}: CreateGeomHull: lID={1}, oldKey={2}, newKey={3}", LogHeader, _localID, _hullKey, newHullKey);
// if the hull hasn't changed, don't rebuild it
- if (newHullKey == _hullKey) return;
+ if (newHullKey == _hullKey) return false;
DetailLog("{0},BSPrim.CreateGeomHull,create,oldKey={1},newKey={2}", LocalID, _hullKey, newHullKey);
@@ -1255,7 +1319,7 @@ public sealed class BSPrim : BSPhysObject
// meshes are already scaled by the meshmerizer
_scale = new OMV.Vector3(1f, 1f, 1f);
DetailLog("{0},BSPrim.CreateGeomHull,done", LocalID);
- return;
+ return true;
}
// Callback from convex hull creater with a newly created hull.
@@ -1268,20 +1332,12 @@ public sealed class BSPrim : BSPhysObject
private void VerifyCorrectPhysicalShape()
{
- if (IsStatic)
- {
- // if static, we don't need a hull so, if there is one, rebuild without it
- if (_hullKey != 0)
- {
- RecreateGeomAndObject();
- }
- }
- else
+ if (!IsStatic)
{
// if not static, it will need a hull to efficiently collide with things
if (_hullKey == 0)
{
- RecreateGeomAndObject();
+ CreateGeomAndObject(false);
}
}
@@ -1300,8 +1356,9 @@ public sealed class BSPrim : BSPhysObject
// m_log.DebugFormat("{0}: CreateObject: lID={1}, shape={2}", LogHeader, _localID, shape.Type);
bool ret = BulletSimAPI.CreateObject(_scene.WorldID, shape);
- // the CreateObject() may have recreated the rigid body. Make sure we have the latest.
+ // the CreateObject() may have recreated the rigid body. Make sure we have the latest address.
BSBody = new BulletBody(LocalID, BulletSimAPI.GetBodyHandle2(_scene.World.Ptr, LocalID));
+ BSShape = new BulletShape(BulletSimAPI.GetCollisionShape2(BSBody.Ptr));
return ret;
}
@@ -1325,15 +1382,20 @@ public sealed class BSPrim : BSPhysObject
shape.Static = _isPhysical ? ShapeData.numericFalse : ShapeData.numericTrue;
}
-
// Rebuild the geometry and object.
// This is called when the shape changes so we need to recreate the mesh/hull.
// No locking here because this is done when the physics engine is not simulating
- private void RecreateGeomAndObject()
+ private void CreateGeomAndObject(bool forceRebuild)
{
- // m_log.DebugFormat("{0}: RecreateGeomAndObject. lID={1}", LogHeader, _localID);
- if (CreateGeom(true))
+ // m_log.DebugFormat("{0}: CreateGeomAndObject. lID={1}, force={2}", LogHeader, _localID, forceRebuild);
+ // Create the geometry that will make up the object
+ if (CreateGeom(forceRebuild))
+ {
+ // Create the object and place it into the world
CreateObject();
+ // Make sure the properties are set on the new object
+ UpdatePhysicalParameters();
+ }
return;
}
@@ -1414,12 +1476,14 @@ public sealed class BSPrim : BSPhysObject
DetailLog("{0},BSPrim.UpdateProperties,call,pos={1},orient={2},vel={3},accel={4},rotVel={5}",
LocalID, _position, _orientation, _velocity, _acceleration, _rotationalVelocity);
+ // BulletSimAPI.DumpRigidBody2(Scene.World.Ptr, BSBody.Ptr);
+
base.RequestPhysicsterseUpdate();
}
/*
else
{
- // For debugging, we also report the movement of children
+ // For debugging, we can also report the movement of children
DetailLog("{0},BSPrim.UpdateProperties,child,pos={1},orient={2},vel={3},accel={4},rotVel={5}",
LocalID, entprop.Position, entprop.Rotation, entprop.Velocity,
entprop.Acceleration, entprop.RotationalVelocity);
@@ -1430,15 +1494,15 @@ public sealed class BSPrim : BSPhysObject
// I've collided with something
// Called at taint time from within the Step() function
CollisionEventUpdate collisionCollection;
- public override void Collide(uint collidingWith, BSPhysObject collidee, ActorTypes type, OMV.Vector3 contactPoint, OMV.Vector3 contactNormal, float pentrationDepth)
+ public override bool Collide(uint collidingWith, BSPhysObject collidee, OMV.Vector3 contactPoint, OMV.Vector3 contactNormal, float pentrationDepth)
{
- // m_log.DebugFormat("{0}: Collide: ms={1}, id={2}, with={3}", LogHeader, _subscribedEventsMs, LocalID, collidingWith);
+ bool ret = false;
// The following lines make IsColliding() and IsCollidingGround() work
- _collidingStep = _scene.SimulationStep;
- if (collidingWith == BSScene.TERRAIN_ID || collidingWith == BSScene.GROUNDPLANE_ID)
+ _collidingStep = Scene.SimulationStep;
+ if (collidingWith <= Scene.TerrainManager.HighestTerrainID)
{
- _collidingGroundStep = _scene.SimulationStep;
+ _collidingGroundStep = Scene.SimulationStep;
}
// DetailLog("{0},BSPrim.Collison,call,with={1}", LocalID, collidingWith);
@@ -1446,21 +1510,23 @@ public sealed class BSPrim : BSPhysObject
// prims in the same linkset cannot collide with each other
if (collidee != null && (this.Linkset.LinksetID == collidee.Linkset.LinksetID))
{
- return;
+ return ret;
}
// if someone has subscribed for collision events....
if (SubscribedEvents()) {
// throttle the collisions to the number of milliseconds specified in the subscription
- int nowTime = _scene.SimulationNowTime;
+ int nowTime = Scene.SimulationNowTime;
if (nowTime >= _nextCollisionOkTime) {
_nextCollisionOkTime = nowTime + _subscribedEventsMs;
if (collisionCollection == null)
collisionCollection = new CollisionEventUpdate();
collisionCollection.AddCollider(collidingWith, new ContactPoint(contactPoint, contactNormal, pentrationDepth));
+ ret = true;
}
}
+ return ret;
}
// The scene is telling us it's time to pass our collected collisions into the simulator
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs
index 4a468af3ae..52997dd526 100644
--- a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs
@@ -56,7 +56,6 @@ using OpenMetaverse;
// Do attachments need to be handled separately? Need collision events. Do not collide with VolumeDetect
// Implement LockAngularMotion
// Decide if clearing forces is the right thing to do when setting position (BulletSim::SetObjectTranslation)
-// Does NeedsMeshing() really need to exclude all the different shapes?
// Remove mesh and Hull stuff. Use mesh passed to bullet and use convexdecom from bullet.
// Add PID movement operations. What does ScenePresence.MoveToTarget do?
// Check terrain size. 128 or 127?
@@ -79,7 +78,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters
private HashSet m_objectsWithCollisions = new HashSet();
// Following is a kludge and can be removed when avatar animation updating is
// moved to a better place.
- private HashSet m_avatarsWithCollisions = new HashSet();
+ private HashSet m_avatarsWithCollisions = new HashSet();
// List of all the objects that have vehicle properties and should be called
// to update each physics step.
@@ -111,11 +110,6 @@ public class BSScene : PhysicsScene, IPhysicsParameters
private long m_simulationStep = 0;
public long SimulationStep { get { return m_simulationStep; } }
- // The length of the last timestep we were asked to simulate.
- // This is used by the vehicle code. Since the vehicle code is called
- // once per simulation step, its constants need to be scaled by this.
- public float LastSimulatedTimestep { get; private set; }
-
// A value of the time now so all the collision and update routines do not have to get their own
// Set to 'now' just before all the prims and actors are called for collisions and updates
public int SimulationNowTime { get; private set; }
@@ -132,8 +126,8 @@ public class BSScene : PhysicsScene, IPhysicsParameters
private EntityProperties[] m_updateArray;
private GCHandle m_updateArrayPinnedHandle;
- private bool _meshSculptedPrim = true; // cause scuplted prims to get meshed
- private bool _forceSimplePrimMeshing = false; // if a cube or sphere, let Bullet do internal shapes
+ public bool ShouldMeshSculptedPrim { get; private set; } // cause scuplted prims to get meshed
+ public bool ShouldForceSimplePrimMeshing { get; private set; } // if a cube or sphere, let Bullet do internal shapes
public float PID_D { get; private set; } // derivative
public float PID_P { get; private set; } // proportional
@@ -153,6 +147,11 @@ public class BSScene : PhysicsScene, IPhysicsParameters
{
get { return new Vector3(0f, 0f, Params.gravity); }
}
+ // Just the Z value of the gravity
+ public float DefaultGravityZ
+ {
+ get { return Params.gravity; }
+ }
public float MaximumObjectMass { get; private set; }
@@ -171,8 +170,8 @@ public class BSScene : PhysicsScene, IPhysicsParameters
callback = c;
}
}
+ private Object _taintLock = new Object(); // lock for using the next object
private List _taintedObjects;
- private Object _taintLock = new Object();
// A pointer to an instance if this structure is passed to the C++ code
// Used to pass basic configuration values to the unmanaged code.
@@ -465,12 +464,8 @@ public class BSScene : PhysicsScene, IPhysicsParameters
int collidersCount = 0;
IntPtr collidersPtr;
- LastSimulatedTimestep = timeStep;
-
// prevent simulation until we've been initialized
- if (!m_initialized) return 10.0f;
-
- int simulateStartTime = Util.EnvironmentTickCount();
+ if (!m_initialized) return 5.0f;
// update the prim states while we know the physics engine is not busy
int numTaints = _taintedObjects.Count;
@@ -478,6 +473,7 @@ public class BSScene : PhysicsScene, IPhysicsParameters
// Some of the prims operate with special vehicle properties
ProcessVehicles(timeStep);
+ numTaints += _taintedObjects.Count;
ProcessTaints(); // the vehicles might have added taints
// step the physical world one interval
@@ -506,6 +502,12 @@ public class BSScene : PhysicsScene, IPhysicsParameters
// Get a value for 'now' so all the collision and update routines don't have to get their own
SimulationNowTime = Util.EnvironmentTickCount();
+ // This is a kludge to get avatar movement updates.
+ // ODE sends collisions for avatars even if there are have been no collisions. This updates
+ // avatar animations and stuff.
+ // If you fix avatar animation updates, remove this overhead and let normal collision processing happen.
+ m_objectsWithCollisions = new HashSet(m_avatarsWithCollisions);
+
// If there were collisions, process them by sending the event to the prim.
// Collisions must be processed before updates.
if (collidersCount > 0)
@@ -527,13 +529,6 @@ public class BSScene : PhysicsScene, IPhysicsParameters
bsp.SendCollisions();
m_objectsWithCollisions.Clear();
- // This is a kludge to get avatar movement updated.
- // ODE sends collisions even if there are none and this is used to update
- // avatar animations and stuff.
- foreach (BSPhysObject bpo in m_avatarsWithCollisions)
- bpo.SendCollisions();
- // m_avatarsWithCollisions.Clear();
-
// If any of the objects had updated properties, tell the object it has been changed by the physics engine
if (updatedEntityCount > 0)
{
@@ -544,7 +539,6 @@ public class BSScene : PhysicsScene, IPhysicsParameters
if (PhysObjects.TryGetValue(entprop.ID, out pobj))
{
pobj.UpdateProperties(entprop);
- continue;
}
}
}
@@ -558,18 +552,10 @@ public class BSScene : PhysicsScene, IPhysicsParameters
}
}
- // this is a waste since the outside routine also calcuates the physics simulation
- // period. TODO: There should be a way of computing physics frames from simulator computation.
- // long simulateTotalTime = Util.EnvironmentTickCountSubtract(simulateStartTime);
- // return (timeStep * (float)simulateTotalTime);
-
- // TODO: FIX THIS: fps calculation possibly wrong.
- // This calculation says 1/timeStep is the ideal frame rate. Any time added to
- // that by the physics simulation gives a slower frame rate.
- long totalSimulationTime = Util.EnvironmentTickCountSubtract(simulateStartTime);
- if (totalSimulationTime >= timeStep)
- return 0;
- return 1f / (timeStep + totalSimulationTime);
+ // The physics engine returns the number of milliseconds it simulated this call.
+ // These are summed and normalized to one second and divided by 1000 to give the reported physics FPS.
+ // Since Bullet normally does 5 or 6 substeps, this will normally sum to about 60 FPS.
+ return numSubSteps * m_fixedTimeStep;
}
// Something has collided
@@ -580,28 +566,25 @@ public class BSScene : PhysicsScene, IPhysicsParameters
return; // don't send collisions to the terrain
}
- BSPhysObject collider = PhysObjects[localID];
- // TODO: as of this code, terrain was not in the physical object list.
- // When BSTerrain is created and it will be in the list, we can remove
- // the possibility that it's not there and just fetch the collidee.
- BSPhysObject collidee = null;
+ BSPhysObject collider;
+ if (!PhysObjects.TryGetValue(localID, out collider))
+ {
+ // If the object that is colliding cannot be found, just ignore the collision.
+ return;
+ }
- ActorTypes type = ActorTypes.Prim;
- if (collidingWith <= TerrainManager.HighestTerrainID)
- {
- type = ActorTypes.Ground;
- }
- else
- {
- collidee = PhysObjects[collidingWith];
- if (collidee is BSCharacter)
- type = ActorTypes.Agent;
- }
+ // The terrain is not in the physical object list so 'collidee'
+ // can be null when Collide() is called.
+ BSPhysObject collidee = null;
+ PhysObjects.TryGetValue(collidingWith, out collidee);
// DetailLog("{0},BSScene.SendCollision,collide,id={1},with={2}", DetailLogZero, localID, collidingWith);
- collider.Collide(collidingWith, collidee, type, collidePoint, collideNormal, penetration);
- m_objectsWithCollisions.Add(collider);
+ if (collider.Collide(collidingWith, collidee, collidePoint, collideNormal, penetration))
+ {
+ // If a collision was posted, remember to send it to the simulator
+ m_objectsWithCollisions.Add(collider);
+ }
return;
}
@@ -619,9 +602,9 @@ public class BSScene : PhysicsScene, IPhysicsParameters
public override void SetWaterLevel(float baseheight)
{
m_waterLevel = baseheight;
- // TODO: pass to physics engine so things will float?
}
- public float GetWaterLevel()
+ // Someday....
+ public float GetWaterLevelAtXYZ(Vector3 loc)
{
return m_waterLevel;
}
@@ -659,121 +642,6 @@ public class BSScene : PhysicsScene, IPhysicsParameters
public override bool IsThreaded { get { return false; } }
- ///
- /// Routine to figure out if we need to mesh this prim with our mesher
- ///
- ///
- /// true if the prim needs meshing
- public bool NeedsMeshing(PrimitiveBaseShape pbs)
- {
- // most of this is redundant now as the mesher will return null if it cant mesh a prim
- // but we still need to check for sculptie meshing being enabled so this is the most
- // convenient place to do it for now...
-
- // int iPropertiesNotSupportedDefault = 0;
-
- if (pbs.SculptEntry && !_meshSculptedPrim)
- {
- // Render sculpties as boxes
- return false;
- }
-
- // if it's a standard box or sphere with no cuts, hollows, twist or top shear, return false since Bullet
- // can use an internal representation for the prim
- if (!_forceSimplePrimMeshing)
- {
- if ((pbs.ProfileShape == ProfileShape.Square && pbs.PathCurve == (byte)Extrusion.Straight)
- || (pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte)Extrusion.Curve1
- && pbs.Scale.X == pbs.Scale.Y && pbs.Scale.Y == pbs.Scale.Z))
- {
-
- if (pbs.ProfileBegin == 0 && pbs.ProfileEnd == 0
- && pbs.ProfileHollow == 0
- && pbs.PathTwist == 0 && pbs.PathTwistBegin == 0
- && pbs.PathBegin == 0 && pbs.PathEnd == 0
- && pbs.PathTaperX == 0 && pbs.PathTaperY == 0
- && pbs.PathScaleX == 100 && pbs.PathScaleY == 100
- && pbs.PathShearX == 0 && pbs.PathShearY == 0)
- {
- return false;
- }
- }
- }
-
- /* TODO: verify that the mesher will now do all these shapes
- if (pbs.ProfileHollow != 0)
- iPropertiesNotSupportedDefault++;
-
- if ((pbs.PathBegin != 0) || pbs.PathEnd != 0)
- iPropertiesNotSupportedDefault++;
-
- if ((pbs.PathTwistBegin != 0) || (pbs.PathTwist != 0))
- iPropertiesNotSupportedDefault++;
-
- if ((pbs.ProfileBegin != 0) || pbs.ProfileEnd != 0)
- iPropertiesNotSupportedDefault++;
-
- if ((pbs.PathScaleX != 100) || (pbs.PathScaleY != 100))
- iPropertiesNotSupportedDefault++;
-
- if ((pbs.PathShearX != 0) || (pbs.PathShearY != 0))
- iPropertiesNotSupportedDefault++;
-
- if (pbs.ProfileShape == ProfileShape.Circle && pbs.PathCurve == (byte)Extrusion.Straight)
- iPropertiesNotSupportedDefault++;
-
- if (pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte)Extrusion.Curve1 && (pbs.Scale.X != pbs.Scale.Y || pbs.Scale.Y != pbs.Scale.Z || pbs.Scale.Z != pbs.Scale.X))
- iPropertiesNotSupportedDefault++;
-
- if (pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte) Extrusion.Curve1)
- iPropertiesNotSupportedDefault++;
-
- // test for torus
- if ((pbs.ProfileCurve & 0x07) == (byte)ProfileShape.Square)
- {
- if (pbs.PathCurve == (byte)Extrusion.Curve1)
- {
- iPropertiesNotSupportedDefault++;
- }
- }
- else if ((pbs.ProfileCurve & 0x07) == (byte)ProfileShape.Circle)
- {
- if (pbs.PathCurve == (byte)Extrusion.Straight)
- {
- iPropertiesNotSupportedDefault++;
- }
- // ProfileCurve seems to combine hole shape and profile curve so we need to only compare against the lower 3 bits
- else if (pbs.PathCurve == (byte)Extrusion.Curve1)
- {
- iPropertiesNotSupportedDefault++;
- }
- }
- else if ((pbs.ProfileCurve & 0x07) == (byte)ProfileShape.HalfCircle)
- {
- if (pbs.PathCurve == (byte)Extrusion.Curve1 || pbs.PathCurve == (byte)Extrusion.Curve2)
- {
- iPropertiesNotSupportedDefault++;
- }
- }
- else if ((pbs.ProfileCurve & 0x07) == (byte)ProfileShape.EquilateralTriangle)
- {
- if (pbs.PathCurve == (byte)Extrusion.Straight)
- {
- iPropertiesNotSupportedDefault++;
- }
- else if (pbs.PathCurve == (byte)Extrusion.Curve1)
- {
- iPropertiesNotSupportedDefault++;
- }
- }
- if (iPropertiesNotSupportedDefault == 0)
- {
- return false;
- }
- */
- return true;
- }
-
// Calls to the PhysicsActors can't directly call into the physics engine
// because it might be busy. We delay changes to a known time.
// We rely on C#'s closure to save and restore the context for the delegate.
@@ -782,7 +650,10 @@ public class BSScene : PhysicsScene, IPhysicsParameters
if (!m_initialized) return;
lock (_taintLock)
+ {
_taintedObjects.Add(new TaintCallbackEntry(ident, callback));
+ }
+
return;
}
@@ -919,14 +790,14 @@ public class BSScene : PhysicsScene, IPhysicsParameters
{
new ParameterDefn("MeshSculptedPrim", "Whether to create meshes for sculpties",
ConfigurationParameters.numericTrue,
- (s,cf,p,v) => { s._meshSculptedPrim = cf.GetBoolean(p, s.BoolNumeric(v)); },
- (s) => { return s.NumericBool(s._meshSculptedPrim); },
- (s,p,l,v) => { s._meshSculptedPrim = s.BoolNumeric(v); } ),
+ (s,cf,p,v) => { s.ShouldMeshSculptedPrim = cf.GetBoolean(p, s.BoolNumeric(v)); },
+ (s) => { return s.NumericBool(s.ShouldMeshSculptedPrim); },
+ (s,p,l,v) => { s.ShouldMeshSculptedPrim = s.BoolNumeric(v); } ),
new ParameterDefn("ForceSimplePrimMeshing", "If true, only use primitive meshes for objects",
ConfigurationParameters.numericFalse,
- (s,cf,p,v) => { s._forceSimplePrimMeshing = cf.GetBoolean(p, s.BoolNumeric(v)); },
- (s) => { return s.NumericBool(s._forceSimplePrimMeshing); },
- (s,p,l,v) => { s._forceSimplePrimMeshing = s.BoolNumeric(v); } ),
+ (s,cf,p,v) => { s.ShouldForceSimplePrimMeshing = cf.GetBoolean(p, s.BoolNumeric(v)); },
+ (s) => { return s.NumericBool(s.ShouldForceSimplePrimMeshing); },
+ (s,p,l,v) => { s.ShouldForceSimplePrimMeshing = s.BoolNumeric(v); } ),
new ParameterDefn("MeshLevelOfDetail", "Level of detail to render meshes (32, 16, 8 or 4. 32=most detailed)",
8f,
@@ -1162,8 +1033,8 @@ public class BSScene : PhysicsScene, IPhysicsParameters
(s,cf,p,v) => { s.m_params[0].linkConstraintTransMotorMaxForce = cf.GetFloat(p, v); },
(s) => { return s.m_params[0].linkConstraintTransMotorMaxForce; },
(s,p,l,v) => { s.m_params[0].linkConstraintTransMotorMaxForce = v; } ),
- new ParameterDefn("LinkConstraintCFM", "Amount constraint can be violated. 0=none, 1=all. Default=0",
- 0.0f,
+ new ParameterDefn("LinkConstraintCFM", "Amount constraint can be violated. 0=no violation, 1=infinite. Default=0.1",
+ 0.1f,
(s,cf,p,v) => { s.m_params[0].linkConstraintCFM = cf.GetFloat(p, v); },
(s) => { return s.m_params[0].linkConstraintCFM; },
(s,p,l,v) => { s.m_params[0].linkConstraintCFM = v; } ),
@@ -1172,6 +1043,11 @@ public class BSScene : PhysicsScene, IPhysicsParameters
(s,cf,p,v) => { s.m_params[0].linkConstraintERP = cf.GetFloat(p, v); },
(s) => { return s.m_params[0].linkConstraintERP; },
(s,p,l,v) => { s.m_params[0].linkConstraintERP = v; } ),
+ new ParameterDefn("LinkConstraintSolverIterations", "Number of solver iterations when computing constraint. (0 = Bullet default)",
+ 40,
+ (s,cf,p,v) => { s.m_params[0].linkConstraintSolverIterations = cf.GetFloat(p, v); },
+ (s) => { return s.m_params[0].linkConstraintSolverIterations; },
+ (s,p,l,v) => { s.m_params[0].linkConstraintSolverIterations = v; } ),
new ParameterDefn("DetailedStats", "Frames between outputting detailed phys stats. (0 is off)",
0f,
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs
index 47d7199e2e..d48462e119 100755
--- a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs
@@ -154,27 +154,31 @@ public class BSTerrainManager
// The simulator wants to set a new heightmap for the terrain.
public void SetTerrain(float[] heightMap) {
- if (m_worldOffset != Vector3.Zero && m_parentScene != null)
+ float[] localHeightMap = heightMap;
+ m_physicsScene.TaintedObject("TerrainManager.SetTerrain", delegate()
{
- // If a child of a mega-region, we shouldn't have any terrain allocated for us
- ReleaseGroundPlaneAndTerrain();
- // If doing the mega-prim stuff and we are the child of the zero region,
- // the terrain is added to our parent
- if (m_parentScene is BSScene)
+ if (m_worldOffset != Vector3.Zero && m_parentScene != null)
{
- DetailLog("{0},SetTerrain.ToParent,offset={1},worldMax={2}",
- BSScene.DetailLogZero, m_worldOffset, m_worldMax);
- ((BSScene)m_parentScene).TerrainManager.UpdateOrCreateTerrain(BSScene.CHILDTERRAIN_ID,
- heightMap, m_worldOffset, m_worldOffset+DefaultRegionSize, false);
+ // If a child of a mega-region, we shouldn't have any terrain allocated for us
+ ReleaseGroundPlaneAndTerrain();
+ // If doing the mega-prim stuff and we are the child of the zero region,
+ // the terrain is added to our parent
+ if (m_parentScene is BSScene)
+ {
+ DetailLog("{0},SetTerrain.ToParent,offset={1},worldMax={2}",
+ BSScene.DetailLogZero, m_worldOffset, m_worldMax);
+ ((BSScene)m_parentScene).TerrainManager.UpdateOrCreateTerrain(BSScene.CHILDTERRAIN_ID,
+ localHeightMap, m_worldOffset, m_worldOffset + DefaultRegionSize, true);
+ }
}
- }
- else
- {
- // If not doing the mega-prim thing, just change the terrain
- DetailLog("{0},SetTerrain.Existing", BSScene.DetailLogZero);
+ else
+ {
+ // If not doing the mega-prim thing, just change the terrain
+ DetailLog("{0},SetTerrain.Existing", BSScene.DetailLogZero);
- UpdateOrCreateTerrain(BSScene.TERRAIN_ID, heightMap, m_worldOffset, m_worldOffset+DefaultRegionSize, false);
- }
+ UpdateOrCreateTerrain(BSScene.TERRAIN_ID, localHeightMap, m_worldOffset, m_worldOffset + DefaultRegionSize, true);
+ }
+ });
}
// If called with no mapInfo for the terrain, this will create a new mapInfo and terrain
@@ -319,6 +323,8 @@ public class BSTerrainManager
// Make sure the new shape is processed.
BulletSimAPI.Activate2(mapInfo.terrainBody.Ptr, true);
+
+ m_terrainModified = true;
};
// There is the option to do the changes now (we're already in 'taint time'), or
@@ -357,6 +363,8 @@ public class BSTerrainManager
m_heightMaps.Add(terrainRegionBase, mapInfo);
// Build the terrain
UpdateOrCreateTerrain(newTerrainID, heightMap, minCoords, maxCoords, true);
+
+ m_terrainModified = true;
};
// If already in taint-time, just call Bullet. Otherwise queue the operations for the safe time.
@@ -383,7 +391,7 @@ public class BSTerrainManager
private float lastHeightTX = 999999f;
private float lastHeightTY = 999999f;
private float lastHeight = HEIGHT_INITIAL_LASTHEIGHT;
- public float GetTerrainHeightAtXY(float tX, float tY)
+ private float GetTerrainHeightAtXY(float tX, float tY)
{
// You'd be surprized at the number of times this routine is called
// with the same parameters as last time.
@@ -403,11 +411,18 @@ public class BSTerrainManager
{
float regionX = tX - offsetX;
float regionY = tY - offsetY;
- if (regionX >= mapInfo.sizeX || regionX < 0f) regionX = 0;
- if (regionY >= mapInfo.sizeY || regionY < 0f) regionY = 0;
int mapIndex = (int)regionY * (int)mapInfo.sizeY + (int)regionX;
- ret = mapInfo.heightMap[mapIndex];
- m_terrainModified = false;
+ try
+ {
+ ret = mapInfo.heightMap[mapIndex];
+ }
+ catch
+ {
+ // Sometimes they give us wonky values of X and Y. Give a warning and return something.
+ m_physicsScene.Logger.WarnFormat("{0} Bad request for terrain height. terrainBase={1}, x={2}, y={3}",
+ LogHeader, terrainBaseXY, regionX, regionY);
+ ret = HEIGHT_GETHEIGHT_RET;
+ }
// DetailLog("{0},BSTerrainManager.GetTerrainHeightAtXY,bX={1},baseY={2},szX={3},szY={4},regX={5},regY={6},index={7},ht={8}",
// BSScene.DetailLogZero, offsetX, offsetY, mapInfo.sizeX, mapInfo.sizeY, regionX, regionY, mapIndex, ret);
}
@@ -416,6 +431,7 @@ public class BSTerrainManager
m_physicsScene.Logger.ErrorFormat("{0} GetTerrainHeightAtXY: terrain not found: region={1}, x={2}, y={3}",
LogHeader, m_physicsScene.RegionName, tX, tY);
}
+ m_terrainModified = false;
lastHeight = ret;
return ret;
}
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs b/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs
index e579cf29e0..9221cdb30e 100644
--- a/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs
@@ -213,6 +213,7 @@ public struct ConfigurationParameters
public float linkConstraintTransMotorMaxForce;
public float linkConstraintERP;
public float linkConstraintCFM;
+ public float linkConstraintSolverIterations;
public const float numericTrue = 1f;
public const float numericFalse = 0f;
@@ -395,23 +396,6 @@ public static extern bool DestroyMesh(uint worldID, System.UInt64 meshKey);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool CreateObject(uint worldID, ShapeData shapeData);
-/* Remove old functionality
-[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
-public static extern void CreateLinkset(uint worldID, int objectCount, ShapeData[] shapeDatas);
-
-[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
-public static extern void AddConstraint(uint worldID, uint id1, uint id2,
- Vector3 frame1, Quaternion frame1rot,
- Vector3 frame2, Quaternion frame2rot,
- Vector3 lowLinear, Vector3 hiLinear, Vector3 lowAngular, Vector3 hiAngular);
-
-[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
-public static extern bool RemoveConstraintByID(uint worldID, uint id1);
-
-[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
-public static extern bool RemoveConstraint(uint worldID, uint id1, uint id2);
- */
-
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern Vector3 GetObjectPosition(uint WorldID, uint id);
@@ -538,15 +522,27 @@ public static extern IntPtr BuildHullShape2(IntPtr world, IntPtr meshShape);
public static extern IntPtr BuildNativeShape2(IntPtr world,
float shapeType, float collisionMargin, Vector3 scale);
+[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
+public static extern bool IsNativeShape2(IntPtr shape);
+
+[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
+public static extern IntPtr CreateCompoundShape2(IntPtr sim);
+
+[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
+public static extern void AddChildToCompoundShape2(IntPtr cShape, IntPtr addShape, Vector3 pos, Quaternion rot);
+
+[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
+public static extern void RemoveChildFromCompoundShape2(IntPtr cShape, IntPtr removeShape);
+
+[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
+public static extern IntPtr CreateBodyFromShapeAndInfo2(IntPtr sim, IntPtr shape, IntPtr constructionInfo);
+
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool DeleteCollisionShape2(IntPtr world, IntPtr shape);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern IntPtr CreateBodyFromShape2(IntPtr sim, IntPtr shape, Vector3 pos, Quaternion rot);
-[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
-public static extern IntPtr CreateBodyFromShapeAndInfo2(IntPtr sim, IntPtr shape, IntPtr constructionInfo);
-
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern IntPtr CreateBodyWithDefaultMotionState2(IntPtr shape, Vector3 pos, Quaternion rot);
@@ -561,9 +557,6 @@ public static extern void DestroyObject2(IntPtr sim, IntPtr obj);
// =====================================================================================
// Terrain creation and helper routines
-[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
-public static extern void DumpMapInfo(IntPtr sim, IntPtr manInfo);
-
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern IntPtr CreateHeightMapInfo2(IntPtr sim, uint id, Vector3 minCoords, Vector3 maxCoords,
[MarshalAs(UnmanagedType.LPArray)] float[] heightMap, float collisionMargin);
@@ -1010,7 +1003,7 @@ public static extern void SetLocalScaling2(IntPtr shape, Vector3 scale);
public static extern Vector3 GetLocalScaling2(IntPtr shape);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
-public static extern void CalculateLocalInertia2(IntPtr shape, float mass, Vector3 inertia);
+public static extern Vector3 CalculateLocalInertia2(IntPtr shape, float mass);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern int GetShapeType2(IntPtr shape);
@@ -1026,6 +1019,12 @@ public static extern void SetCollisionFilterMask(IntPtr shape, uint filter, uint
// =====================================================================================
// Debugging
+[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
+public static extern void DumpRigidBody2(IntPtr sim, IntPtr collisionObject);
+
+[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
+public static extern void DumpMapInfo2(IntPtr sim, IntPtr manInfo);
+
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void DumpPhysicsStatistics2(IntPtr sim);
diff --git a/OpenSim/Server/Handlers/Avatar/AvatarServerPostHandler.cs b/OpenSim/Server/Handlers/Avatar/AvatarServerPostHandler.cs
index 393584e932..8cd747ee17 100644
--- a/OpenSim/Server/Handlers/Avatar/AvatarServerPostHandler.cs
+++ b/OpenSim/Server/Handlers/Avatar/AvatarServerPostHandler.cs
@@ -137,6 +137,8 @@ namespace OpenSim.Server.Handlers.Avatar
if (!UUID.TryParse(request["UserID"].ToString(), out user))
return FailureResult();
+ RemoveRequestParamsNotForStorage(request);
+
AvatarData avatar = new AvatarData(request);
if (m_AvatarService.SetAvatar(user, avatar))
return SuccessResult();
@@ -153,11 +155,25 @@ namespace OpenSim.Server.Handlers.Avatar
if (!UUID.TryParse(request["UserID"].ToString(), out user))
return FailureResult();
+ RemoveRequestParamsNotForStorage(request);
+
if (m_AvatarService.ResetAvatar(user))
return SuccessResult();
return FailureResult();
}
+
+ ///
+ /// Remove parameters that were used to invoke the method and should not in themselves be persisted.
+ ///
+ ///
+ private void RemoveRequestParamsNotForStorage(Dictionary request)
+ {
+ request.Remove("VERSIONMAX");
+ request.Remove("VERSIONMIN");
+ request.Remove("METHOD");
+ request.Remove("UserID");
+ }
byte[] SetItems(Dictionary request)
{
@@ -173,6 +189,8 @@ namespace OpenSim.Server.Handlers.Avatar
if (!(request["Names"] is List || request["Values"] is List))
return FailureResult();
+ RemoveRequestParamsNotForStorage(request);
+
List _names = (List)request["Names"];
names = _names.ToArray();
List _values = (List)request["Values"];
diff --git a/OpenSim/Services/AssetService/AssetServiceBase.cs b/OpenSim/Services/AssetService/AssetServiceBase.cs
index 177c56541c..58ab052ebe 100644
--- a/OpenSim/Services/AssetService/AssetServiceBase.cs
+++ b/OpenSim/Services/AssetService/AssetServiceBase.cs
@@ -84,7 +84,7 @@ namespace OpenSim.Services.AssetService
m_Database = LoadPlugin(dllName);
if (m_Database == null)
- throw new Exception("Could not find a storage interface in the given module");
+ throw new Exception(string.Format("Could not find a storage interface in the module {0}", dllName));
m_Database.Initialise(connString);
@@ -96,7 +96,7 @@ namespace OpenSim.Services.AssetService
m_AssetLoader = LoadPlugin(loaderName);
if (m_AssetLoader == null)
- throw new Exception("Asset loader could not be loaded");
+ throw new Exception(string.Format("Asset loader could not be loaded from {0}", loaderName));
}
}
}
diff --git a/OpenSim/Services/HypergridService/HGSuitcaseInventoryService.cs b/OpenSim/Services/HypergridService/HGSuitcaseInventoryService.cs
index 6e4b68c8bd..556a0da49c 100644
--- a/OpenSim/Services/HypergridService/HGSuitcaseInventoryService.cs
+++ b/OpenSim/Services/HypergridService/HGSuitcaseInventoryService.cs
@@ -56,10 +56,12 @@ namespace OpenSim.Services.HypergridService
private string m_HomeURL;
private IUserAccountService m_UserAccountService;
+ private IAvatarService m_AvatarService;
// private UserAccountCache m_Cache;
private ExpiringCache> m_SuitcaseTrees = new ExpiringCache>();
+ private ExpiringCache m_Appearances = new ExpiringCache();
public HGSuitcaseInventoryService(IConfigSource config, string configName)
: base(config, configName)
@@ -77,7 +79,6 @@ namespace OpenSim.Services.HypergridService
IConfig invConfig = config.Configs[m_ConfigName];
if (invConfig != null)
{
- // realm = authConfig.GetString("Realm", realm);
string userAccountsDll = invConfig.GetString("UserAccountsService", string.Empty);
if (userAccountsDll == string.Empty)
throw new Exception("Please specify UserAccountsService in HGInventoryService configuration");
@@ -87,8 +88,14 @@ namespace OpenSim.Services.HypergridService
if (m_UserAccountService == null)
throw new Exception(String.Format("Unable to create UserAccountService from {0}", userAccountsDll));
- // legacy configuration [obsolete]
- m_HomeURL = invConfig.GetString("ProfileServerURI", string.Empty);
+ string avatarDll = invConfig.GetString("AvatarService", string.Empty);
+ if (avatarDll == string.Empty)
+ throw new Exception("Please specify AvatarService in HGInventoryService configuration");
+
+ m_AvatarService = ServerUtils.LoadPlugin(avatarDll, args);
+ if (m_AvatarService == null)
+ throw new Exception(String.Format("Unable to create m_AvatarService from {0}", avatarDll));
+
// Preferred
m_HomeURL = invConfig.GetString("HomeURI", m_HomeURL);
@@ -394,7 +401,7 @@ namespace OpenSim.Services.HypergridService
return null;
}
- if (!IsWithinSuitcaseTree(it.Owner, it.Folder))
+ if (!IsWithinSuitcaseTree(it.Owner, it.Folder) && !IsPartOfAppearance(it.Owner, it.ID))
{
m_log.DebugFormat("[HG SUITCASE INVENTORY SERVICE]: Item {0} (folder {1}) is not within Suitcase",
it.Name, it.Folder);
@@ -549,6 +556,52 @@ namespace OpenSim.Services.HypergridService
else return true;
}
#endregion
+
+ #region Avatar Appearance
+
+ private AvatarAppearance GetAppearance(UUID principalID)
+ {
+ AvatarAppearance a = null;
+ if (m_Appearances.TryGetValue(principalID, out a))
+ return a;
+
+ a = m_AvatarService.GetAppearance(principalID);
+ m_Appearances.AddOrUpdate(principalID, a, 5 * 60); // 5minutes
+ return a;
+ }
+
+ private bool IsPartOfAppearance(UUID principalID, UUID itemID)
+ {
+ AvatarAppearance a = GetAppearance(principalID);
+
+ if (a == null)
+ return false;
+
+ // Check wearables (body parts and clothes)
+ for (int i = 0; i < a.Wearables.Length; i++)
+ {
+ for (int j = 0; j < a.Wearables[i].Count; j++)
+ {
+ if (a.Wearables[i][j].ItemID == itemID)
+ {
+ m_log.DebugFormat("[HG SUITCASE INVENTORY SERVICE]: item {0} is a wearable", itemID);
+ return true;
+ }
+ }
+ }
+
+ // Check attachments
+ if (a.GetAttachmentForItem(itemID) != null)
+ {
+ m_log.DebugFormat("[HG SUITCASE INVENTORY SERVICE]: item {0} is an attachment", itemID);
+ return true;
+ }
+
+ return false;
+ }
+
+ #endregion
+
}
}
diff --git a/bin/OpenSim.ini.example b/bin/OpenSim.ini.example
index f0ebcce0b2..0de40022ff 100644
--- a/bin/OpenSim.ini.example
+++ b/bin/OpenSim.ini.example
@@ -108,10 +108,10 @@
;; This can be overriden in the region config file.
; ClampPrimSize = false
- ;# {LinksetPrims} {} {Max prims an object will hold?} {} 0
- ;; Maximum number of prims allowable in a linkset. Affects creating new linksets. Ignored if less than or equal to zero.
- ;; This can be overriden in the region config file.
- ; LinksetPrims = 0
+ ;# {LinksetPrims} {} {Max prims an object will hold?} {} 0
+ ;; Maximum number of prims allowable in a linkset. Affects creating new linksets. Ignored if less than or equal to zero.
+ ;; This can be overriden in the region config file.
+ ; LinksetPrims = 0
;# {AllowScriptCrossing} {} {Allow scripts to cross into this region} {true false} true
;; Allow scripts to keep running when they cross region boundaries, rather than being restarted. State is reloaded on the destination region.
diff --git a/bin/OpenSimDefaults.ini b/bin/OpenSimDefaults.ini
index f4ea2a5b59..1dc3f15dc3 100644
--- a/bin/OpenSimDefaults.ini
+++ b/bin/OpenSimDefaults.ini
@@ -95,9 +95,9 @@
; This can be overriden in the region config file.
ClampPrimSize = false
- ; Maximum number of prims allowable in a linkset. Affects creating new linksets. Ignored if less than or equal to zero.
- ; This can be overriden in the region config file.
- LinksetPrims = 0
+ ; Maximum number of prims allowable in a linkset. Affects creating new linksets. Ignored if less than or equal to zero.
+ ; This can be overriden in the region config file.
+ LinksetPrims = 0
; Allow scripts to keep running when they cross region boundaries, rather than being restarted. State is reloaded on the destination region.
; This only applies when crossing to a region running in a different simulator.
diff --git a/bin/config-include/FlotsamCache.ini.example b/bin/config-include/FlotsamCache.ini.example
index b9c6d84808..ad74fc14e9 100644
--- a/bin/config-include/FlotsamCache.ini.example
+++ b/bin/config-include/FlotsamCache.ini.example
@@ -54,10 +54,3 @@
; Warning level for cache directory size
;CacheWarnAt = 30000
-
- ; Perform a deep scan of all assets within all regions, looking for all assets
- ; present or referenced. Mark all assets found that are already present in the
- ; cache, and request all assets that are found that are not already cached (this
- ; will cause those assets to be cached)
- ;
- DeepScanBeforePurge = true
diff --git a/bin/config-include/StandaloneHypergrid.ini b/bin/config-include/StandaloneHypergrid.ini
index cc6c587de7..b0ae351c3d 100644
--- a/bin/config-include/StandaloneHypergrid.ini
+++ b/bin/config-include/StandaloneHypergrid.ini
@@ -153,6 +153,7 @@
; For the InventoryServiceInConnector
LocalServiceModule = "OpenSim.Services.HypergridService.dll:HGInventoryService"
UserAccountsService = "OpenSim.Services.UserAccountService.dll:UserAccountService"
+ AvatarService = "OpenSim.Services.AvatarService.dll:AvatarService"
;; The interface that local users get when they are in other grids
;; This restricts/filters the asset operations from the outside
diff --git a/bin/lib32/BulletSim.dll b/bin/lib32/BulletSim.dll
index 0dd508c1da..3b35a9804a 100755
Binary files a/bin/lib32/BulletSim.dll and b/bin/lib32/BulletSim.dll differ
diff --git a/bin/lib32/libBulletSim.so b/bin/lib32/libBulletSim.so
index 747df24288..65eba371a0 100755
Binary files a/bin/lib32/libBulletSim.so and b/bin/lib32/libBulletSim.so differ
diff --git a/bin/lib64/BulletSim.dll b/bin/lib64/BulletSim.dll
index 877ad4cc1d..f01655bd66 100755
Binary files a/bin/lib64/BulletSim.dll and b/bin/lib64/BulletSim.dll differ
diff --git a/bin/lib64/libBulletSim.so b/bin/lib64/libBulletSim.so
index a55e633388..5302e298ee 100755
Binary files a/bin/lib64/libBulletSim.so and b/bin/lib64/libBulletSim.so differ
diff --git a/prebuild.xml b/prebuild.xml
index 416fdc0446..d19dfb63e8 100644
--- a/prebuild.xml
+++ b/prebuild.xml
@@ -3091,6 +3091,7 @@
+