Compare commits

...

79 Commits

Author SHA1 Message Date
Robert Adams
6632eb7c05 BulletSim: Remove calculation and passing of unused collied object type.
Fix collision code to properly sense mega-region children regions as terrain.
When setting an object physical, reset all the physical properties (friction, ...).
2012-09-15 15:31:54 -07:00
Robert Adams
f0a098924e BulletSim: set all linkset objects center of mass to the whole linkset's center of mass 2012-09-15 15:31:49 -07:00
Robert Adams
7c347f4c5c BulletSim: Add calls to linkset class when object going static or dynamic.
Reset center of mass on an object when going dynamic.
2012-09-15 15:31:44 -07:00
Robert Adams
d86cbe6379 BulletSim: remove unused NeedsMeshing() code from BSScene. 2012-09-15 15:31:39 -07:00
Robert Adams
c0fec70b1a BulletSim: Add 'IsNativeShape2' call 2012-09-15 15:31:34 -07:00
Robert Adams
2c5ff93990 BulletSim: Way too many changes in one commit.
Many changes to BSDynamic for readability and commentary.
Linkset hacking for vehicles: don't over mass the root prim.
Add parameter for link constraint solver iterations.
Correct uses of timestep in timescale calculations for vehicles.
Reorganize code/logic for making objects static and dynamic for readability
    and use of API2.
Changed most calls in BSPrim to use API2 calls (the new way).
Avatars do not generate default Bullet collision events but do call up
    to the simulator for every avatar. Reduces overhead.
Objects added to collision list only if they are processing collisions.
    Reduces overhead especially for large numbers of avatars.
Generalize call for water height to GetWaterHeightAtXYZ().
Catch and correct exception getting terrain height when out of bounds.
Correct race condition in Terrain Manager where creation wasn't at taint-time.
Add API calls for constructing compound shapes.
Move NeedsMeshing() logic into object class.
Reorganize logic for object meshing to reduce rebuilding of meshs/hulls.
2012-09-15 15:31:29 -07:00
Justin Clark-Casey (justincc)
1ec84ac8b1 Add basic asset connector tests to check behaviour for normal, local and temporary assets.
Make AssetServiceConnector return more useful data on failure, such as what DLL it was trying to load
Allow LocalAssetServiceConnector.GetData() to work without a cache present, as works for the other lasc Get* methods.
2012-09-15 02:12:26 +01:00
Justin Clark-Casey (justincc)
d5c8c6bc12 For FlotsamAssetCache, always update access times of cached scene assets before looking for files to expire.
This is to resolve a problem where an asset marked as local but not temporary but still used in the scene would be removed.
The timed expiry scan no longer tries to refetch assets from the scene that are not currently in the cache - this is not helpful since it just drags a lot of data into the cache that may never be referenced.
This removes the DeepScanBeforePurge option since setting this to false will introduce the above problem.  This previously had a default of true.
2012-09-15 01:08:15 +01:00
Oren Hurvitz
5dd2569bf7 Added unit tests for multi-region OARs 2012-09-14 20:32:25 +01:00
Justin Clark-Casey (justincc)
d7e6fe488d Rename ArchiveWriteRequestPreparatio nto ArchiveWriteRequest since after the multi-OAR patch there is now only one class that handles this operation.
Adapation of 0004-Renamed-ArchiveWriteRequestPreparation-to-ArchiveWri.patch in http://opensimulator.org/mantis/view.php?id=6105
since that did not directly apply
2012-09-14 20:25:44 +01:00
Oren Hurvitz
ce468215d5 Support multi-region OAR files
Merged ArchiveWriteRequestPreparation.cs and ArchiveWriteRequestExecution.cs. This simplifies the code, and it's faster to write each scene to the archive as it's found rather than all at once at the end.
2012-09-14 20:25:03 +01:00
Justin Clark-Casey (justincc)
6f7825e310 Don't store the unnecessary VERSIONMIN. VERSIONMAX, METHOD or UserID (present as column PrincipalID) parameters in the Avatars table.
These are used to invoke avatar service calls but are not in themselves persistable avatar data.
2012-09-14 00:07:39 +01:00
Diva Canto
dd0a89a0e6 Merge branch 'master' of ssh://opensimulator.org/var/git/opensim 2012-09-13 10:00:52 -07:00
Diva Canto
190f9c258b Restarting to work on HGSuitcaseInventoryService: added the ability for the outside world to retrieve appearance items. Not ACLed yet. 2012-09-13 10:00:29 -07:00
SignpostMarv
46d8405229 Documentation of parcel-related events
Signed-off-by: BlueWall <jamesh@bluewallgroup.com>
2012-09-13 09:59:25 -04:00
SignpostMarv
9598c1af9e correcting a typo that causes c# express to complain about xml comment containing invalid xml
Signed-off-by: BlueWall <jamesh@bluewallgroup.com>
2012-09-13 09:59:25 -04:00
Justin Clark-Casey (justincc)
82487549f7 Merge branch 'master' of ssh://opensimulator.org/var/git/opensim 2012-09-12 23:47:09 +01:00
Justin Clark-Casey (justincc)
cdc3781f42 Fix usage statement on "debug http" console command since max level is now 5 rather than 3 2012-09-12 23:02:25 +01:00
Justin Clark-Casey (justincc)
7df7b86ec5 Fix bug in logging sample input at debug http level 4.
Also converts newlines to "\n" text.
2012-09-12 23:01:07 +01:00
BlueWall
81fda6f947 Revert "Fix prebuild.xml format for reference"
This reverts commit 96f889b20e.
2012-09-11 23:43:37 -04:00
BlueWall
e9322b0bf7 Merge branch 'master' of ssh://opensimulator.org/var/git/opensim 2012-09-11 22:58:21 -04:00
BlueWall
96f889b20e Fix prebuild.xml format for reference
Mono.Data.SQL entry was bad
2012-09-11 22:56:29 -04:00
Justin Clark-Casey (justincc)
224efe7b76 minor: Comment out friends notification log spam for now. 2012-09-12 01:58:01 +01:00
Justin Clark-Casey (justincc)
ebb394bbda Fix indentation and issues where tabs were used instead of spaces in commit 783ee949 2012-09-12 01:45:34 +01:00
SignpostMarv
c17965eec4 mathematically & hypothetically speaking we want to avoid negative values being written 2012-09-12 01:26:22 +01:00
Justin Clark-Casey (justincc)
4ead48f09f Merge branch 'master' of ssh://opensimulator.org/var/git/opensim 2012-09-12 01:24:17 +01:00
SignpostMarv
41f3f2400e Documentation of economy-related EventManager events 2012-09-12 01:23:47 +01:00
Mic Bowman
f06394f195 Allow an incoming identifier to be specified for a JsonStore. 2012-09-11 16:42:07 -07:00
Justin Clark-Casey (justincc)
d53a53d4c5 Make "show http-handlers" command available for ROBUST instances as well as the simulator executable. 2012-09-12 00:10:48 +01:00
Justin Clark-Casey (justincc)
757d9163fa Merge branch 'master' of ssh://opensimulator.org/var/git/opensim 2012-09-12 00:07:56 +01:00
Justin Clark-Casey (justincc)
25111e703f Add levels 4 and 5 to "debug http" console command that will log a sample of incoming request data and the entire incoming data respectively.
See "help debug http" for more details.
2012-09-11 21:48:51 +01:00
SignpostMarv
ad1b9bbba6 documentation (OnSceneObjectPartCopy) 2012-09-11 20:12:45 +01:00
SignpostMarv
97b8739c1d documentation (OnRemovePresence) 2012-09-11 20:12:45 +01:00
SignpostMarv
0760121eb9 documentation (OnNewPresence) 2012-09-11 20:12:44 +01:00
SignpostMarv
0a22e78f59 documentation (OnClientConnect) 2012-09-11 20:12:44 +01:00
SignpostMarv
5e51f16ceb documentation (OnBackup) 2012-09-11 20:12:44 +01:00
SignpostMarv
fd8a7e64b9 documentation (OnTerrainTick) 2012-09-11 20:12:44 +01:00
SignpostMarv
ed5f574356 documentation (OnTerrainTainted) 2012-09-11 20:12:44 +01:00
SignpostMarv
44349f742e documentation (OnClientMovement) 2012-09-11 20:12:43 +01:00
SignpostMarv
e3993eefa5 Documenting some of the events on OpenSim.Region.Framework.Scenes.EventManager (OnFrame) 2012-09-11 20:12:43 +01:00
SignpostMarv
8db5d79f4b passing in the function name to MOD_Api.ConvertFromLSL for more user-friendly error messages 2012-09-10 13:15:32 +01:00
BlueWall
bf56b30024 Adjust namespce of FlotsamCache
Place FloatsamCache in the same namespace as our other core asset caches
2012-09-09 14:35:48 -04:00
BlueWall
8321ecb7a2 Revert "Move addin information to CoreModulePlugin.addin.xml"
This reverts commit 2ec34580ce.

  Moving the attributes for mono addins back to the source file - this also fixes http://opensimulator.org/mantis/view.php?id=6278.
2012-09-09 13:50:44 -04:00
SignpostMarv
783ee949ea implementing per-region configuration of limits on the number of prims one can have in a linkset
Applied with changes - patch was based on a repo different from core

Signed-off-by: Melanie <melanie@t-data.com>
2012-09-09 12:59:25 +01:00
SignpostMarv
e041f09750 refactoring to allow Scene.GetLandData to accept Vector3 as an argument. Note that the prior work on LSL_Vector implicit operators means one does not need to explicitly cast a LSL_Vector to Vector3 2012-09-08 00:44:27 +01:00
Justin Clark-Casey (justincc)
fa3edcf55c Merge branch 'master' of ssh://opensimulator.org/var/git/opensim 2012-09-08 00:17:57 +01:00
SignpostMarv
874bde366a 4096 is used in various places as the maximum height of a region, refactoring to be a constant 2012-09-08 00:14:39 +01:00
Robert Adams
d8df2d6bed BulletSim: update the DLLs and SOs 2012-09-07 16:05:34 -07:00
Robert Adams
126eae7100 BulletSim: Add Bullet body and shape to BSPhysObject and rename
'Body' to 'BSBody' for disambiguation when reading code.
Complete the API2 interface so nearly all methods on bullet
    classes are available to the managed code. The efficient
    single call simulation step is kept in place while all
    other creation/destruction/parameterization can be done
    in the managed code.
2012-09-07 16:05:28 -07:00
Robert Adams
3c097cb7a9 BulletSim: Add some comments (gasp) and log messages. 2012-09-07 16:05:22 -07:00
Robert Adams
189f51233e BulletSim: PhysicsActorType() now returns the correct value rather than 'unknown'. 2012-09-07 16:05:16 -07:00
Robert Adams
76dc29dc37 BulletSim: Modify collision flag calls to return the current flags.
Track current collision flags in BSPrim.
Add BulletSimAPI calls for saving and restoring rigidBodies using
    construction information structure.
2012-09-07 16:05:11 -07:00
SignpostMarv
df3914c7cd renaming to markdown file 2012-09-08 00:02:27 +01:00
SignpostMarv
740eac192b multi-paragraph list item 2012-09-08 00:02:27 +01:00
SignpostMarv
783ecd01f4 headers 2012-09-08 00:02:26 +01:00
SignpostMarv
a0e3e23aa9 removing redundant header; this is the building.txt file for opensim 2012-09-08 00:02:26 +01:00
SignpostMarv
1acee36822 code blocks 2012-09-08 00:02:26 +01:00
SignpostMarv
ac90322f1b markdown-formatted link 2012-09-08 00:02:26 +01:00
SignpostMarv
2ca93cb144 renaming to markdown file 2012-09-08 00:02:26 +01:00
SignpostMarv
4e2805496c header syntax 2012-09-08 00:02:26 +01:00
SignpostMarv
46af3a3d92 casing 2012-09-08 00:02:26 +01:00
BlueWall
717b11856e Move addin attributes to RegionCombinerModule.addin.xml
Will help in automation of setting version info for addin dependencies.
2012-09-07 13:21:06 -04:00
BlueWall
0199abd625 Merge branch 'master' of ssh://opensimulator.org/var/git/opensim 2012-09-07 11:43:22 -04:00
BlueWall
550f6c4ad2 Revert "Move addin information to RegionCombinerModule.addin.xml"
This reverts commit ab446bc692782b75fd27105fc3370a16b4fc17d8.
  Need to look into this
2012-09-07 11:41:47 -04:00
Diva Canto
2b506cffb1 Merge branch 'master' of ssh://opensimulator.org/var/git/opensim 2012-09-07 08:10:03 -07:00
Diva Canto
33469a0d2b Moved the new vars in Robust.HG.ini to the proper section. 2012-09-07 08:09:44 -07:00
BlueWall
1f18ce516f Move addin information to RegionCombinerModule.addin.xml
Move addin information from attributes to RegionCombinerModule.addin.xml to aid in automation of addin dependency tracking.
2012-09-07 11:07:19 -04:00
BlueWall
2ec34580ce Move addin information to CoreModulePlugin.addin.xml
Move the addin information from attributes in the source file to the CoreModulePlugin.xml, which is the standard. This will help us automate addin version dependency information when we make new OpenSim releases.
2012-09-07 10:18:00 -04:00
Justin Clark-Casey (justincc)
288baaecaf Add warning chevrons around the GC.Collect added to Warp3DImageModule in commit 5eb2526
Manually calling GC.Collect() really shouldnt' be necessary and is generally regarded as a bad idea.
A GC should occur anyway pretty shortly afterwards.
However, can leave this in development code for now to see if it does actually make a significant difference rather than simply doing a GC a little earlier.
2012-09-07 00:46:24 +01:00
Justin Clark-Casey (justincc)
d2b00749ef Add missing DynamicTexture.cs file from last commit 2012-09-06 23:14:48 +01:00
Justin Clark-Casey (justincc)
8f02fd926e If reusing dynamic textures, do not reuse small data length textures that fall below current viewer discard level 2 thresholds.
Viewer LL 3.3.4 and before sometimes fail to properly redisplay dynamic textures that have a small data length compared to pixel size when pulled from cache.
This appears to happen when the data length is smaller than the estimate discard level 2 size the viewer uses when making this GetTexture request.
This commit works around this by always regenerating dynamic textures that fall below this threshold rather than reusing them if ReuseDynamicTextures = true
This can be controlled by the [Textures] ReuseDynamicLowDataTextures config setting which defaults to false.
2012-09-06 22:12:05 +01:00
Diva Canto
9f914327c6 Added missing configs, and deleted the [Profile] part on the Hypergrind config. 2012-09-06 12:42:14 -07:00
SignpostMarv
4215877b48 adding utility method for getting SceneObjectGroup from scene
Signed-off-by: BlueWall <jamesh@bluewallgroup.com>
2012-09-06 06:32:56 -04:00
SignpostMarv
d2e79e26d7 adding utility method for getting SceneObjectPart from scene
Signed-off-by: BlueWall <jamesh@bluewallgroup.com>
2012-09-06 06:32:46 -04:00
SignpostMarv
3f6c6eed33 pasting in show uptime code
Signed-off-by: BlueWall <jamesh@bluewallgroup.com>
2012-09-06 06:13:07 -04:00
BlueWall
0a71e3ab39 Add file to .gitignore
Add OpenSim.userprefs which is created by Monodevelop to .gitignore
2012-09-06 05:36:00 -04:00
BlueWall
8fe8fdb0c9 Merge branch 'master' of ssh://opensimulator.org/var/git/opensim 2012-09-06 05:19:15 -04:00
Justin Clark-Casey (justincc)
fbfd28a61b Merge branch 'master' of ssh://opensimulator.org/var/git/opensim 2012-09-06 00:16:40 +01:00
Justin Clark-Casey (justincc)
a0d0c9f751 If the GetTexture capability receives a request for a range of data beyond that of an otherwise valid asset, return HTTP PartialContent rather than RequestedRangeNotSatisfiable.
This is because recent viewers (3.2.1, 3.3.4) and probably earlier ones using the http GetTexture capability will sometimes make such invalid range requests.
This appears to happen if the viewer's estimate of texture sizes at discard levels > 0 (chiefly 2) exceeds the total texture size.
I believe this does not normally happen but can occur for dynamic textures with are large but mainly blank.
If this happens, returning a RequestedRangeNotSatisfiable will cause the viewer to not render the texture at the final resolution.
However, returning a PartialContent (or OK) even with 0 data will allow the viewer to render the final texture.
2012-09-06 00:11:47 +01:00
77 changed files with 3693 additions and 1461 deletions

1
.gitignore vendored
View File

@@ -66,6 +66,7 @@ Examples/*.dll
OpenSim.build
OpenSim.sln
OpenSim.suo
OpenSim.userprefs
Prebuild/Prebuild.build
Prebuild/Prebuild.sln
TestResult.xml

View File

@@ -1,6 +1,4 @@
==== Building OpenSim ====
=== Building on Windows ===
# Building on Windows
Steps:
* runprebuild.bat
@@ -9,16 +7,15 @@ Steps:
* copy OpenSim.ini.example to OpenSim.ini and other appropriate files in bin/config-include
* run OpenSim.exe
=== Building on Linux ===
# Building on Linux
Prereqs:
* Mono >= 2.4.3
* Nant >= 0.85
* On some Linux distributions you may need to install additional packages.
See http://opensimulator.org/wiki/Dependencies for more information.
* May also use xbuild (included in mono distributions)
* May use Monodevelop, a cross-platform IDE
* Mono >= 2.4.3
* Nant >= 0.85
* On some Linux distributions you may need to install additional packages.
See http://opensimulator.org/wiki/Dependencies for more information.
* May also use xbuild (included in mono distributions)
* May use Monodevelop, a cross-platform IDE
From the distribution type:
* ./runprebuild.sh
@@ -27,13 +24,13 @@ From the distribution type:
* copy OpenSim.ini.example to OpenSim.ini and other appropriate files in bin/config-include
* run mono OpenSim.exe
=== Using Monodevelop ===
# Using Monodevelop
From the distribution type:
* ./runprebuild.sh
* type monodevelop OpenSim.sln
=== References ===
# References
Helpful resources:
* http://opensimulator.org/wiki/Build_Instructions

View File

@@ -1369,6 +1369,8 @@ namespace OpenSim.ApplicationPlugins.RemoteController
/// <description>profile url</description></item>
/// <item><term>noassets</term>
/// <description>true if no assets should be saved</description></item>
/// <item><term>all</term>
/// <description>true to save all the regions in the simulator</description></item>
/// <item><term>perm</term>
/// <description>C and/or T</description></item>
/// </list>
@@ -1425,6 +1427,11 @@ namespace OpenSim.ApplicationPlugins.RemoteController
options["checkPermissions"] = (string)requestData["perm"];
}
if ((string)requestData["all"] == "true")
{
options["all"] = (string)requestData["all"];
}
IRegionArchiverModule archiver = scene.RequestModuleInterface<IRegionArchiverModule>();
if (archiver != null)

View File

@@ -163,7 +163,7 @@ namespace OpenSim.Capabilities.Handlers
if (texture == null)
{
//m_log.DebugFormat("[GETTEXTURE]: texture was not in the cache");
// m_log.DebugFormat("[GETTEXTURE]: texture was not in the cache");
// Fetch locally or remotely. Misses return a 404
texture = m_assetService.Get(textureID.ToString());
@@ -197,7 +197,7 @@ namespace OpenSim.Capabilities.Handlers
}
else // it was on the cache
{
//m_log.DebugFormat("[GETTEXTURE]: texture was in the cache");
// m_log.DebugFormat("[GETTEXTURE]: texture was in the cache");
WriteTextureData(httpRequest, httpResponse, texture, format);
return true;
}
@@ -219,12 +219,30 @@ namespace OpenSim.Capabilities.Handlers
int start, end;
if (TryParseRange(range, out start, out end))
{
// Before clamping start make sure we can satisfy it in order to avoid
// sending back the last byte instead of an error status
if (start >= texture.Data.Length)
{
response.StatusCode = (int)System.Net.HttpStatusCode.RequestedRangeNotSatisfiable;
m_log.DebugFormat(
"[GETTEXTURE]: Client requested range for texture {0} starting at {1} but texture has end of {2}",
texture.ID, start, texture.Data.Length);
// Stricly speaking, as per http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html, we should be sending back
// Requested Range Not Satisfiable (416) here. However, it appears that at least recent implementations
// of the Linden Lab viewer (3.2.1 and 3.3.4 and probably earlier), a viewer that has previously
// received a very small texture may attempt to fetch bytes from the server past the
// range of data that it received originally. Whether this happens appears to depend on whether
// the viewer's estimation of how large a request it needs to make for certain discard levels
// (http://wiki.secondlife.com/wiki/Image_System#Discard_Level_and_Mip_Mapping), chiefly discard
// level 2. If this estimate is greater than the total texture size, returning a RequestedRangeNotSatisfiable
// here will cause the viewer to treat the texture as bad and never display the full resolution
// However, if we return PartialContent (or OK) instead, the viewer will display that resolution.
// response.StatusCode = (int)System.Net.HttpStatusCode.RequestedRangeNotSatisfiable;
// response.AddHeader("Content-Range", String.Format("bytes */{0}", texture.Data.Length));
// response.StatusCode = (int)System.Net.HttpStatusCode.OK;
response.StatusCode = (int)System.Net.HttpStatusCode.PartialContent;
response.ContentType = texture.Metadata.ContentType;
}
else
{
@@ -232,12 +250,18 @@ namespace OpenSim.Capabilities.Handlers
start = Utils.Clamp(start, 0, end);
int len = end - start + 1;
//m_log.Debug("Serving " + start + " to " + end + " of " + texture.Data.Length + " bytes for texture " + texture.ID);
// m_log.Debug("Serving " + start + " to " + end + " of " + texture.Data.Length + " bytes for texture " + texture.ID);
// Always return PartialContent, even if the range covered the entire data length
// We were accidentally sending back 404 before in this situation
// https://issues.apache.org/bugzilla/show_bug.cgi?id=51878 supports sending 206 even if the
// entire range is requested, and viewer 3.2.2 (and very probably earlier) seems fine with this.
//
// We also do not want to send back OK even if the whole range was satisfiable since this causes
// HTTP textures on at least Imprudence 1.4.0-beta2 to never display the final texture quality.
// if (end > maxEnd)
// response.StatusCode = (int)System.Net.HttpStatusCode.OK;
// else
response.StatusCode = (int)System.Net.HttpStatusCode.PartialContent;
response.ContentLength = len;

View File

@@ -31,6 +31,7 @@ namespace OpenSim.Framework
public class Constants
{
public const uint RegionSize = 256;
public const uint RegionHeight = 4096;
public const byte TerrainPatchSize = 16;
public const string DefaultTexture = "89556747-24cb-43ed-920b-47caed15465f";

View File

@@ -126,6 +126,7 @@ namespace OpenSim.Framework
private int m_physPrimMax = 0;
private bool m_clampPrimSize = false;
private int m_objectCapacity = 0;
private int m_linksetCapacity = 0;
private int m_agentCapacity = 0;
private string m_regionType = String.Empty;
private RegionLightShareData m_windlight = new RegionLightShareData();
@@ -317,6 +318,11 @@ namespace OpenSim.Framework
get { return m_objectCapacity; }
}
public int LinksetCapacity
{
get { return m_linksetCapacity; }
}
public int AgentCapacity
{
get { return m_agentCapacity; }
@@ -654,6 +660,9 @@ namespace OpenSim.Framework
m_objectCapacity = config.GetInt("MaxPrims", 15000);
allKeys.Remove("MaxPrims");
m_linksetCapacity = config.GetInt("LinksetPrims", 0);
allKeys.Remove("LinksetPrims");
#endregion
@@ -692,24 +701,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_agentCapacity != 0)
if (m_linksetCapacity > 0)
config.Set("LinksetPrims", m_linksetCapacity);
if (m_agentCapacity > 0)
config.Set("MaxAgents", m_agentCapacity);
if (ScopeID != UUID.Zero)
@@ -804,6 +816,9 @@ namespace OpenSim.Framework
configMember.addConfigurationOption("object_capacity", ConfigurationOption.ConfigurationTypes.TYPE_INT32,
"Max objects this sim will hold", m_objectCapacity.ToString(), true);
configMember.addConfigurationOption("linkset_capacity", ConfigurationOption.ConfigurationTypes.TYPE_INT32,
"Max prims an object will hold", m_linksetCapacity.ToString(), true);
configMember.addConfigurationOption("agent_capacity", ConfigurationOption.ConfigurationTypes.TYPE_INT32,
"Max avatars this sim will hold", m_agentCapacity.ToString(), true);
@@ -922,6 +937,9 @@ namespace OpenSim.Framework
case "object_capacity":
m_objectCapacity = (int)configuration_result;
break;
case "linkset_capacity":
m_linksetCapacity = (int)configuration_result;
break;
case "agent_capacity":
m_agentCapacity = (int)configuration_result;
break;
@@ -1052,4 +1070,4 @@ namespace OpenSim.Framework
return kvp;
}
}
}
}

View File

@@ -52,6 +52,11 @@ namespace OpenSim.Framework.Serialization
/// </value>
public const string INVENTORY_PATH = "inventory/";
/// <value>
/// Path for regions in a multi-region archive
/// </value>
public const string REGIONS_PATH = "regions/";
/// <value>
/// Path for the prims file
/// </value>

View File

@@ -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;

View File

@@ -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 [<level>]",
"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<String> 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());
}
/// <summary>
/// Register an already started HTTP server to the collection of known servers.
/// </summary>

View File

@@ -1007,6 +1007,38 @@ namespace OpenSim.Framework
}
}
/// <summary>
/// Copy data from one stream to another, leaving the read position of both streams at the beginning.
/// </summary>
/// <param name='inputStream'>
/// Input stream. Must be seekable.
/// </param>
/// <exception cref='ArgumentException'>
/// Thrown if the input stream is not seekable.
/// </exception>
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);

View File

@@ -292,7 +292,7 @@ namespace OpenSim
m_console.Commands.AddCommand("Archiving", false, "save oar",
//"save oar [-v|--version=<N>] [-p|--profile=<url>] [<OAR path>]",
"save oar [-h|--home=<url>] [--noassets] [--publish] [--perm=<permissions>] [<OAR path>]",
"save oar [-h|--home=<url>] [--noassets] [--publish] [--perm=<permissions>] [--all] [<OAR path>]",
"Save a region's data to an OAR archive.",
// "-v|--version=<N> generates scene objects as per older versions of the serialization (e.g. -v=0)" + Environment.NewLine
"-h|--home=<url> 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=<permissions> stops objects with insufficient permissions from being saved to the OAR.\n"
+ " <permissions> 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<String> 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)

View File

@@ -76,7 +76,7 @@ namespace OpenSim.Region.ClientStack
protected override void StartupSpecific()
{
SceneManager = new SceneManager();
SceneManager = SceneManager.Instance;
m_clientStackManager = CreateClientStackManager();
Initialize();

View File

@@ -52,7 +52,7 @@ using OpenSim.Services.Interfaces;
[assembly: Addin("FlotsamAssetCache", "1.1")]
[assembly: AddinDependency("OpenSim", "0.5")]
namespace Flotsam.RegionModules.AssetCache
namespace OpenSim.Region.CoreModules.Asset
{
[Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule")]
public class FlotsamAssetCache : ISharedRegionModule, IImprovedAssetCache, IAssetService
@@ -107,8 +107,6 @@ namespace Flotsam.RegionModules.AssetCache
private IAssetService m_AssetService;
private List<Scene> m_Scenes = new List<Scene>();
private bool m_DeepScanBeforePurge;
public FlotsamAssetCache()
{
m_InvalidChars.AddRange(Path.GetInvalidPathChars());
@@ -170,8 +168,6 @@ namespace Flotsam.RegionModules.AssetCache
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);
@@ -506,13 +502,10 @@ namespace Flotsam.RegionModules.AssetCache
// 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))
{
@@ -705,11 +698,14 @@ namespace Flotsam.RegionModules.AssetCache
/// <summary>
/// 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.
/// </summary>
/// <returns></returns>
private int CacheScenes()
/// <param name="storeUncached">
/// If true, then assets scanned which are not found in cache are added to the cache.
/// </param>
/// <returns>Number of distinct asset references found in the scene.</returns>
private int TouchAllSceneAssets(bool storeUncached)
{
UuidGatherer gatherer = new UuidGatherer(m_AssetService);
@@ -732,7 +728,7 @@ namespace Flotsam.RegionModules.AssetCache
{
File.SetLastAccessTime(filename, DateTime.Now);
}
else
else if (storeUncached)
{
m_AssetService.Get(assetID.ToString());
}
@@ -860,13 +856,14 @@ namespace Flotsam.RegionModules.AssetCache
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;

View File

@@ -35,7 +35,6 @@ using Nini.Config;
using NUnit.Framework;
using OpenMetaverse;
using OpenMetaverse.Assets;
using Flotsam.RegionModules.AssetCache;
using OpenSim.Framework;
using OpenSim.Region.Framework.Scenes;
using OpenSim.Region.Framework.Scenes.Serialization;

View File

@@ -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);

View File

@@ -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))

View File

@@ -0,0 +1,61 @@
/*
* 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.Drawing;
using OpenSim.Region.Framework.Interfaces;
namespace OpenSim.Region.CoreModules.Scripting.DynamicTexture
{
public class DynamicTexture : IDynamicTexture
{
public string InputCommands { get; private set; }
public Uri InputUri { get; private set; }
public string InputParams { get; private set; }
public byte[] Data { get; private set; }
public Size Size { get; private set; }
public bool IsReuseable { get; private set; }
public DynamicTexture(string inputCommands, string inputParams, byte[] data, Size size, bool isReuseable)
{
InputCommands = inputCommands;
InputParams = inputParams;
Data = data;
Size = size;
IsReuseable = isReuseable;
}
public DynamicTexture(Uri inputUri, string inputParams, byte[] data, Size size, bool isReuseable)
{
InputUri = inputUri;
InputParams = inputParams;
Data = data;
Size = size;
IsReuseable = isReuseable;
}
}
}

View File

@@ -42,7 +42,7 @@ namespace OpenSim.Region.CoreModules.Scripting.DynamicTexture
{
public class DynamicTextureModule : IRegionModule, IDynamicTextureManager
{
//private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private const int ALL_SIDES = -1;
@@ -54,6 +54,17 @@ namespace OpenSim.Region.CoreModules.Scripting.DynamicTexture
/// </summary>
public bool ReuseTextures { get; set; }
/// <summary>
/// If false, then textures which have a low data size are not reused when ReuseTextures = true.
/// </summary>
/// <remarks>
/// LL viewers 3.3.4 and before appear to not fully render textures pulled from the viewer cache if those
/// textures have a relatively high pixel surface but a small data size. Typically, this appears to happen
/// if the data size is smaller than the viewer's discard level 2 size estimate. So if this is setting is
/// false, textures smaller than the calculation in IsSizeReuseable are always regenerated rather than reused
/// to work around this problem.</remarks>
public bool ReuseLowDataTextures { get; set; }
private Dictionary<UUID, Scene> RegisteredScenes = new Dictionary<UUID, Scene>();
private Dictionary<string, IDynamicTextureRender> RenderPlugins =
@@ -83,18 +94,17 @@ namespace OpenSim.Region.CoreModules.Scripting.DynamicTexture
/// <summary>
/// Called by code which actually renders the dynamic texture to supply texture data.
/// </summary>
/// <param name="id"></param>
/// <param name="data"></param>
/// <param name="isReuseable">True if the data generated can be reused for subsequent identical requests</param>
public void ReturnData(UUID id, byte[] data, bool isReuseable)
/// <param name="updaterId"></param>
/// <param name="texture"></param>
public void ReturnData(UUID updaterId, IDynamicTexture texture)
{
DynamicTextureUpdater updater = null;
lock (Updaters)
{
if (Updaters.ContainsKey(id))
if (Updaters.ContainsKey(updaterId))
{
updater = Updaters[id];
updater = Updaters[updaterId];
}
}
@@ -103,11 +113,16 @@ namespace OpenSim.Region.CoreModules.Scripting.DynamicTexture
if (RegisteredScenes.ContainsKey(updater.SimUUID))
{
Scene scene = RegisteredScenes[updater.SimUUID];
UUID newTextureID = updater.DataReceived(data, scene);
UUID newTextureID = updater.DataReceived(texture.Data, scene);
if (ReuseTextures && isReuseable && !updater.BlendWithOldTexture)
if (ReuseTextures
&& !updater.BlendWithOldTexture
&& texture.IsReuseable
&& (ReuseLowDataTextures || IsDataSizeReuseable(texture)))
{
m_reuseableDynamicTextures.Store(
GenerateReusableTextureKey(updater.BodyData, updater.Params), newTextureID);
GenerateReusableTextureKey(texture.InputCommands, texture.InputParams), newTextureID);
}
}
}
@@ -123,6 +138,27 @@ namespace OpenSim.Region.CoreModules.Scripting.DynamicTexture
}
}
/// <summary>
/// Determines whether the texture is reuseable based on its data size.
/// </summary>
/// <remarks>
/// This is a workaround for a viewer bug where very small data size textures relative to their pixel size
/// are not redisplayed properly when pulled from cache. The calculation here is based on the typical discard
/// level of 2, a 'rate' of 0.125 and 4 components (which makes for a factor of 0.5).
/// </remarks>
/// <returns></returns>
private bool IsDataSizeReuseable(IDynamicTexture texture)
{
// Console.WriteLine("{0} {1}", texture.Size.Width, texture.Size.Height);
int discardLevel2DataThreshold = (int)Math.Ceiling((texture.Size.Width >> 2) * (texture.Size.Height >> 2) * 0.5);
// m_log.DebugFormat(
// "[DYNAMIC TEXTURE MODULE]: Discard level 2 threshold {0}, texture data length {1}",
// discardLevel2DataThreshold, texture.Data.Length);
return discardLevel2DataThreshold < texture.Data.Length;
}
public UUID AddDynamicTextureURL(UUID simID, UUID primID, string contentType, string url,
string extraParams, int updateTimer)
{
@@ -249,10 +285,18 @@ namespace OpenSim.Region.CoreModules.Scripting.DynamicTexture
}
}
// m_log.DebugFormat(
// "[DYNAMIC TEXTURE MODULE]: Requesting generation of new dynamic texture for {0} in {1}",
// part.Name, part.ParentGroup.Scene.Name);
RenderPlugins[contentType].AsyncConvertData(updater.UpdaterID, data, extraParams);
}
else
{
// m_log.DebugFormat(
// "[DYNAMIC TEXTURE MODULE]: Reusing cached texture {0} for {1} in {2}",
// objReusableTextureUUID, part.Name, part.ParentGroup.Scene.Name);
// No need to add to updaters as the texture is always the same. Not that this functionality
// apppears to be implemented anyway.
updater.UpdatePart(part, (UUID)objReusableTextureUUID);
@@ -285,7 +329,10 @@ namespace OpenSim.Region.CoreModules.Scripting.DynamicTexture
{
IConfig texturesConfig = config.Configs["Textures"];
if (texturesConfig != null)
{
ReuseTextures = texturesConfig.GetBoolean("ReuseDynamicTextures", false);
ReuseLowDataTextures = texturesConfig.GetBoolean("ReuseDynamicLowDataTextures", false);
}
if (!RegisteredScenes.ContainsKey(scene.RegionInfo.RegionID))
{
@@ -448,8 +495,10 @@ namespace OpenSim.Region.CoreModules.Scripting.DynamicTexture
IJ2KDecoder cacheLayerDecode = scene.RequestModuleInterface<IJ2KDecoder>();
if (cacheLayerDecode != null)
{
cacheLayerDecode.Decode(asset.FullID, asset.Data);
cacheLayerDecode = null;
if (!cacheLayerDecode.Decode(asset.FullID, asset.Data))
m_log.WarnFormat(
"[DYNAMIC TEXTURE MODULE]: Decoding of dynamically generated asset {0} for {1} in {2} failed",
asset.ID, part.Name, part.ParentGroup.Scene.Name);
}
UUID oldID = UpdatePart(part, asset.FullID);

View File

@@ -32,6 +32,7 @@ using System.Net;
using Nini.Config;
using OpenMetaverse;
using OpenMetaverse.Imaging;
using OpenSim.Region.CoreModules.Scripting.DynamicTexture;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.Framework.Scenes;
using log4net;
@@ -73,12 +74,12 @@ namespace OpenSim.Region.CoreModules.Scripting.LoadImageURL
// return false;
// }
public byte[] ConvertUrl(string url, string extraParams)
public IDynamicTexture ConvertUrl(string url, string extraParams)
{
return null;
}
public byte[] ConvertData(string bodyData, string extraParams)
public IDynamicTexture ConvertData(string bodyData, string extraParams)
{
return null;
}
@@ -171,11 +172,11 @@ namespace OpenSim.Region.CoreModules.Scripting.LoadImageURL
private void HttpRequestReturn(IAsyncResult result)
{
RequestState state = (RequestState) result.AsyncState;
WebRequest request = (WebRequest) state.Request;
Stream stream = null;
byte[] imageJ2000 = new byte[0];
Size newSize = new Size(0, 0);
try
{
@@ -188,37 +189,43 @@ namespace OpenSim.Region.CoreModules.Scripting.LoadImageURL
try
{
Bitmap image = new Bitmap(stream);
Size newsize;
// TODO: make this a bit less hard coded
if ((image.Height < 64) && (image.Width < 64))
{
newsize = new Size(32, 32);
newSize.Width = 32;
newSize.Height = 32;
}
else if ((image.Height < 128) && (image.Width < 128))
{
newsize = new Size(64, 64);
newSize.Width = 64;
newSize.Height = 64;
}
else if ((image.Height < 256) && (image.Width < 256))
{
newsize = new Size(128, 128);
newSize.Width = 128;
newSize.Height = 128;
}
else if ((image.Height < 512 && image.Width < 512))
{
newsize = new Size(256, 256);
newSize.Width = 256;
newSize.Height = 256;
}
else if ((image.Height < 1024 && image.Width < 1024))
{
newsize = new Size(512, 512);
newSize.Width = 512;
newSize.Height = 512;
}
else
{
newsize = new Size(1024, 1024);
newSize.Width = 1024;
newSize.Height = 1024;
}
Bitmap resize = new Bitmap(image, newsize);
imageJ2000 = OpenJPEG.EncodeFromImage(resize, true);
using (Bitmap resize = new Bitmap(image, newSize))
{
imageJ2000 = OpenJPEG.EncodeFromImage(resize, true);
}
}
catch (Exception)
{
@@ -233,7 +240,6 @@ namespace OpenSim.Region.CoreModules.Scripting.LoadImageURL
}
catch (WebException)
{
}
finally
{
@@ -243,10 +249,13 @@ namespace OpenSim.Region.CoreModules.Scripting.LoadImageURL
}
}
m_log.DebugFormat("[LOADIMAGEURLMODULE] Returning {0} bytes of image data for request {1}",
m_log.DebugFormat("[LOADIMAGEURLMODULE]: Returning {0} bytes of image data for request {1}",
imageJ2000.Length, state.RequestID);
m_textureManager.ReturnData(state.RequestID, imageJ2000, false);
m_textureManager.ReturnData(
state.RequestID,
new OpenSim.Region.CoreModules.Scripting.DynamicTexture.DynamicTexture(
request.RequestUri, null, imageJ2000, newSize, false));
}
#region Nested type: RequestState

View File

@@ -57,6 +57,7 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender.Tests
m_dtm = new DynamicTextureModule();
m_dtm.ReuseTextures = reuseTextures;
// m_dtm.ReuseLowDataTextures = reuseTextures;
m_vrm = new VectorRenderModule();
@@ -201,6 +202,7 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender.Tests
public void TestRepeatSameDrawReusingTexture()
{
TestHelpers.InMethod();
// TestHelpers.EnableLogging();
string dtText = "PenColour BLACK; MoveTo 40,220; FontSize 32; Text Hello World;";
@@ -228,6 +230,46 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender.Tests
Assert.That(firstDynamicTextureID, Is.EqualTo(so.RootPart.Shape.Textures.GetFace(0).TextureID));
}
/// <summary>
/// Test a low data dynamically generated texture such that it is treated as a low data texture that causes
/// problems for current viewers.
/// </summary>
/// <remarks>
/// As we do not set DynamicTextureModule.ReuseLowDataTextures = true in this test, it should not reuse the
/// texture
/// </remarks>
[Test]
public void TestRepeatSameDrawLowDataTexture()
{
TestHelpers.InMethod();
// TestHelpers.EnableLogging();
string dtText = "PenColour BLACK; MoveTo 40,220; FontSize 32; Text Hello World;";
SetupScene(true);
SceneObjectGroup so = SceneHelpers.AddSceneObject(m_scene);
m_dtm.AddDynamicTextureData(
m_scene.RegionInfo.RegionID,
so.UUID,
m_vrm.GetContentType(),
dtText,
"1024",
0);
UUID firstDynamicTextureID = so.RootPart.Shape.Textures.GetFace(0).TextureID;
m_dtm.AddDynamicTextureData(
m_scene.RegionInfo.RegionID,
so.UUID,
m_vrm.GetContentType(),
dtText,
"1024",
0);
Assert.That(firstDynamicTextureID, Is.Not.EqualTo(so.RootPart.Shape.Textures.GetFace(0).TextureID));
}
[Test]
public void TestRepeatSameDrawDifferentExtraParamsReusingTexture()
{

View File

@@ -35,6 +35,7 @@ using System.Net;
using Nini.Config;
using OpenMetaverse;
using OpenMetaverse.Imaging;
using OpenSim.Region.CoreModules.Scripting.DynamicTexture;
using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.Framework.Scenes;
using log4net;
@@ -46,6 +47,11 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender
{
public class VectorRenderModule : IRegionModule, IDynamicTextureRender
{
// These fields exist for testing purposes, please do not remove.
// private static bool s_flipper;
// private static byte[] s_asset1Data;
// private static byte[] s_asset2Data;
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private Scene m_scene;
@@ -80,20 +86,14 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender
// return lines.Any((str, r) => str.StartsWith("Image"));
// }
public byte[] ConvertUrl(string url, string extraParams)
public IDynamicTexture ConvertUrl(string url, string extraParams)
{
return null;
}
public byte[] ConvertData(string bodyData, string extraParams)
public IDynamicTexture ConvertData(string bodyData, string extraParams)
{
bool reuseable;
return Draw(bodyData, extraParams, out reuseable);
}
private byte[] ConvertData(string bodyData, string extraParams, out bool reuseable)
{
return Draw(bodyData, extraParams, out reuseable);
return Draw(bodyData, extraParams);
}
public bool AsyncConvertUrl(UUID id, string url, string extraParams)
@@ -104,10 +104,7 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender
public bool AsyncConvertData(UUID id, string bodyData, string extraParams)
{
// XXX: This isn't actually being done asynchronously!
bool reuseable;
byte[] data = ConvertData(bodyData, extraParams, out reuseable);
m_textureManager.ReturnData(id, data, reuseable);
m_textureManager.ReturnData(id, ConvertData(bodyData, extraParams));
return true;
}
@@ -161,6 +158,13 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender
{
m_textureManager.RegisterRender(GetContentType(), this);
}
// This code exists for testing purposes, please do not remove.
// s_asset1Data = m_scene.AssetService.Get("00000000-0000-1111-9999-000000000001").Data;
// s_asset1Data = m_scene.AssetService.Get("9f4acf0d-1841-4e15-bdb8-3a12efc9dd8f").Data;
// Terrain dirt - smallest bin/assets file (6004 bytes)
// s_asset2Data = m_scene.AssetService.Get("b8d3965a-ad78-bf43-699b-bff8eca6c975").Data;
}
public void Close()
@@ -179,7 +183,7 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender
#endregion
private byte[] Draw(string data, string extraParams, out bool reuseable)
private IDynamicTexture Draw(string data, string extraParams)
{
// We need to cater for old scripts that didnt use extraParams neatly, they use either an integer size which represents both width and height, or setalpha
// we will now support multiple comma seperated params in the form width:256,height:512,alpha:255
@@ -322,6 +326,7 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender
Bitmap bitmap = null;
Graphics graph = null;
bool reuseable = false;
try
{
@@ -364,6 +369,14 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender
}
byte[] imageJ2000 = new byte[0];
// This code exists for testing purposes, please do not remove.
// if (s_flipper)
// imageJ2000 = s_asset1Data;
// else
// imageJ2000 = s_asset2Data;
//
// s_flipper = !s_flipper;
try
{
@@ -376,7 +389,8 @@ namespace OpenSim.Region.CoreModules.Scripting.VectorRender
e.Message, e.StackTrace);
}
return imageJ2000;
return new OpenSim.Region.CoreModules.Scripting.DynamicTexture.DynamicTexture(
data, extraParams, imageJ2000, new Size(width, height), reuseable);
}
finally
{

View File

@@ -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;

View File

@@ -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
}
}
}

View File

@@ -43,7 +43,7 @@ using OpenSim.Tests.Common;
namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Grid.Tests
{
[TestFixture]
public class GridConnectorsTests
public class GridConnectorsTests : OpenSimTestCase
{
LocalGridServicesConnector m_LocalConnector;
private void SetUp()

View File

@@ -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);
/// <summary>
/// Contains data used while dearchiving a single scene.
/// </summary>
private class DearchiveContext
{
public Scene Scene { get; set; }
public List<string> SerialisedSceneObjects { get; set; }
public List<string> SerialisedParcels { get; set; }
public List<SceneObjectGroup> SceneObjects { get; set; }
public DearchiveContext(Scene scene)
{
Scene = scene;
SerialisedSceneObjects = new List<string>();
SerialisedParcels = new List<string>();
SceneObjects = new List<SceneObjectGroup>();
}
}
/// <summary>
/// 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
/// <summary>
/// Has the control file been loaded for this archive?
/// </summary>
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<IUserManagement>();
m_UserMan = m_rootScene.RequestModuleInterface<IUserManagement>();
}
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<IGroupsModule>();
m_groupsModule = m_rootScene.RequestModuleInterface<IGroupsModule>();
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<IGroupsModule>();
m_groupsModule = m_rootScene.RequestModuleInterface<IGroupsModule>();
m_assetService = m_rootScene.AssetService;
}
/// <summary>
/// Dearchive the region embodied in this request.
/// </summary>
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<string> serialisedSceneObjects = new List<string>();
List<string> serialisedParcels = new List<string>();
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<UUID, DearchiveContext> sceneContexts = new Dictionary<UUID, DearchiveContext>();
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<UUID>(), 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<IEstateModule>();
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<UUID>(), 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);
}
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
/// <param name="archive"></param>
/// <param name="dearchivedScenes"></param>
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");
}
/// <summary>
/// Load serialized scene objects.
/// </summary>
/// <param name="serialisedSceneObjects"></param>
protected void LoadObjects(List<string> serialisedSceneObjects)
protected void LoadObjects(Scene scene, List<string> serialisedSceneObjects, List<SceneObjectGroup> 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>();
IRegionSerialiserModule serialiser = scene.RequestModuleInterface<IRegionSerialiserModule>();
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;
@@ -328,15 +493,15 @@ namespace OpenSim.Region.CoreModules.World.Archiver
TaskInventoryDictionary inv = part.TaskInventory;
foreach (KeyValuePair<UUID, TaskInventoryItem> 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)
@@ -348,10 +513,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();
}
}
@@ -366,16 +531,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();
}
}
/// <summary>
/// Load serialized parcels.
/// </summary>
/// <param name="scene"></param>
/// <param name="serialisedParcels"></param>
protected void LoadParcels(List<string> serialisedParcels)
protected void LoadParcels(Scene scene, List<string> serialisedParcels)
{
// Reload serialized parcels
m_log.InfoFormat("[ARCHIVER]: Loading {0} parcels. Please wait.", serialisedParcels.Count);
@@ -386,8 +552,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))
{
@@ -398,7 +564,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver
List<LandAccessEntry> accessList = new List<LandAccessEntry>();
foreach (LandAccessEntry entry in parcel.ParcelAccessList)
{
if (ResolveUserUuid(entry.AgentID))
if (ResolveUserUuid(scene, entry.AgentID))
accessList.Add(entry);
// else, drop this access rule
}
@@ -414,23 +580,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);
}
/// <summary>
/// Look up the given user id to check whether it's one that is valid for this grid.
/// </summary>
/// <param name="scene"></param>
/// <param name="uuid"></param>
/// <returns></returns>
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);
}
@@ -485,7 +652,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;
@@ -505,7 +672,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
@@ -533,12 +700,14 @@ namespace OpenSim.Region.CoreModules.World.Archiver
/// <summary>
/// Load region settings data
/// </summary>
/// <param name="scene"></param>
/// <param name="settingsPath"></param>
/// <param name="data"></param>
/// <param name="dearchivedScenes"></param>
/// <returns>
/// true if settings were loaded successfully, false otherwise
/// </returns>
private bool LoadRegionSettings(string settingsPath, byte[] data)
private bool LoadRegionSettings(Scene scene, string settingsPath, byte[] data, DearchiveScenesInfo dearchivedScenes)
{
RegionSettings loadedRegionSettings;
@@ -554,7 +723,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;
@@ -591,12 +760,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>();
IEstateModule estateModule = scene.RequestModuleInterface<IEstateModule>();
if (estateModule != null)
estateModule.sendRegionHandshakeToAll();
@@ -606,14 +777,15 @@ namespace OpenSim.Region.CoreModules.World.Archiver
/// <summary>
/// Load terrain data
/// </summary>
/// <param name="scene"></param>
/// <param name="terrainPath"></param>
/// <param name="data"></param>
/// <returns>
/// true if terrain was resolved successfully, false otherwise.
/// </returns>
private bool LoadTerrain(string terrainPath, byte[] data)
private bool LoadTerrain(Scene scene, string terrainPath, byte[] data)
{
ITerrainModule terrainModule = m_scene.RequestModuleInterface<ITerrainModule>();
ITerrainModule terrainModule = scene.RequestModuleInterface<ITerrainModule>();
MemoryStream ms = new MemoryStream(data);
terrainModule.LoadFromStream(terrainPath, ms);
@@ -629,17 +801,18 @@ namespace OpenSim.Region.CoreModules.World.Archiver
/// </summary>
/// <param name="path"></param>
/// <param name="data"></param>
public void LoadControlFile(string path, byte[] data)
/// <param name="dearchivedScenes"></param>
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())
{
@@ -665,18 +838,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;
}
}
}

View File

@@ -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
{
/// <summary>
/// A group of regions arranged in a rectangle, possibly with holes.
/// </summary>
/// <remarks>
/// 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).
/// </remarks>
public class ArchiveScenesGroup
{
/// <summary>
/// All the regions. The outer dictionary contains rows (key: Y coordinate).
/// The inner dictionaries contain each row's regions (key: X coordinate).
/// </summary>
public SortedDictionary<uint, SortedDictionary<uint, Scene>> Regions { get; set; }
/// <summary>
/// The subdirectory where each region is stored in the archive.
/// </summary>
protected Dictionary<UUID, string> m_regionDirs;
/// <summary>
/// The grid coordinates of the regions' bounding box.
/// </summary>
public Rectangle Rect { get; set; }
public ArchiveScenesGroup()
{
Regions = new SortedDictionary<uint, SortedDictionary<uint, Scene>>();
m_regionDirs = new Dictionary<UUID, string>();
Rect = new Rectangle(0, 0, 0, 0);
}
public void AddScene(Scene scene)
{
uint x = scene.RegionInfo.RegionLocX;
uint y = scene.RegionInfo.RegionLocY;
SortedDictionary<uint, Scene> row;
if (!Regions.TryGetValue(y, out row))
{
row = new SortedDictionary<uint, Scene>();
Regions[y] = row;
}
row[x] = scene;
}
/// <summary>
/// Called after all the scenes have been added. Performs calculations that require
/// knowledge of all the scenes.
/// </summary>
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<uint, Scene> 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;
});
}
/// <summary>
/// Returns the subdirectory where the region is stored.
/// </summary>
/// <param name="regionID"></param>
/// <returns></returns>
public string GetRegionDir(UUID regionID)
{
return m_regionDirs[regionID];
}
/// <summary>
/// Performs an action on all the scenes in this order: rows from South to North,
/// and within each row West to East.
/// </summary>
/// <param name="action"></param>
public void ForEachScene(Action<Scene> action)
{
foreach (SortedDictionary<uint, Scene> row in Regions.Values)
{
foreach (Scene scene in row.Values)
{
action(scene);
}
}
}
/// <summary>
/// Returns the scene at position 'location'.
/// </summary>
/// <param name="location">A location in the grid</param>
/// <param name="scene">The scene at this location</param>
/// <returns>Whether the scene was found</returns>
public bool TryGetScene(Point location, out Scene scene)
{
SortedDictionary<uint, Scene> row;
if (Regions.TryGetValue((uint)location.Y, out row))
{
if (row.TryGetValue((uint)location.X, out scene))
return true;
}
scene = null;
return false;
}
}
}

View File

@@ -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
{
/// <summary>
/// Prepare to write out an archive.
/// </summary>
public class ArchiveWriteRequest
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
/// <summary>
/// The minimum major version of OAR that we can write.
/// </summary>
public static int MIN_MAJOR_VERSION = 0;
/// <summary>
/// The maximum major version of OAR that we can write.
/// </summary>
public static int MAX_MAJOR_VERSION = 1;
/// <summary>
/// Whether we're saving a multi-region archive.
/// </summary>
public bool MultiRegionFormat { get; set; }
/// <summary>
/// Determine whether this archive will save assets. Default is true.
/// </summary>
public bool SaveAssets { get; set; }
/// <summary>
/// Determines which objects will be included in the archive, according to their permissions.
/// Default is null, meaning no permission checks.
/// </summary>
public string CheckPermissions { get; set; }
protected Scene m_rootScene;
protected Stream m_saveStream;
protected TarArchiveWriter m_archiveWriter;
protected Guid m_requestId;
protected Dictionary<string, object> m_options;
/// <summary>
/// Constructor
/// </summary>
/// <param name="module">Calling module</param>
/// <param name="savePath">The path to which to save data.</param>
/// <param name="requestId">The id associated with this request</param>
/// <exception cref="System.IO.IOException">
/// If there was a problem opening a stream for the file specified by the savePath
/// </exception>
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);
}
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="scene">The root scene to archive</param>
/// <param name="saveStream">The stream to which to save data.</param>
/// <param name="requestId">The id associated with this request</param>
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;
}
/// <summary>
/// Archive the region requested.
/// </summary>
/// <exception cref="System.IO.IOException">if there was an io problem with creating the file</exception>
public void ArchiveRegion(Dictionary<string, object> 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<UUID, AssetType> assetUuids = new Dictionary<UUID, AssetType>();
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<UUID, AssetType> assetUuids)
{
m_log.InfoFormat("[ARCHIVER]: Writing region {0}", scene.RegionInfo.RegionName);
EntityBase[] entities = scene.GetEntities();
List<SceneObjectGroup> sceneObjects = new List<SceneObjectGroup>();
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<IPermissionsModule>();
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);
}
/// <summary>
/// Checks whether the user has permission to export an object group to an OAR.
/// </summary>
/// <param name="user">The user</param>
/// <param name="objGroup">The object group</param>
/// <param name="checkPermissions">Which permissions to check: "C" = Copy, "T" = Transfer</param>
/// <param name="permissionsModule">The scene's permissions module</param>
/// <returns>Whether the user is allowed to export the object to an OAR</returns>
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;
}
/// <summary>
/// Create the control file.
/// </summary>
/// <returns></returns>
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;
}
/// <summary>
/// Writes the list of regions included in a multi-region OAR.
/// </summary>
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<uint, Scene> 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<IRegionCombinerModule>();
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<SceneObjectGroup> 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<ILandObject> 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<ITerrainModule>().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<IRegionSerialiserModule>();
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<UUID> assetsFoundUuids, ICollection<UUID> 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);
}
/// <summary>
/// Closes the archive and notifies that we're done.
/// </summary>
/// <param name="errorMessage">The error that occurred, or empty for success</param>
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);
}
}
}

View File

@@ -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
{
/// <summary>
/// Method called when all the necessary assets for an archive request have been received.
/// </summary>
public delegate void AssetsRequestCallback(
ICollection<UUID> assetsFoundUuids, ICollection<UUID> assetsNotFoundUuids);
/// <summary>
/// Execute the write of an archive once we have received all the necessary data
/// </summary>
public class ArchiveWriteRequestExecution
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
protected ITerrainModule m_terrainModule;
protected IRegionSerialiserModule m_serialiser;
protected List<SceneObjectGroup> m_sceneObjects;
protected Scene m_scene;
protected TarArchiveWriter m_archiveWriter;
protected Guid m_requestId;
protected Dictionary<string, object> m_options;
public ArchiveWriteRequestExecution(
List<SceneObjectGroup> sceneObjects,
ITerrainModule terrainModule,
IRegionSerialiserModule serialiser,
Scene scene,
TarArchiveWriter archiveWriter,
Guid requestId,
Dictionary<string, object> 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<UUID> assetsFoundUuids, ICollection<UUID> 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<UUID> assetsFoundUuids, ICollection<UUID> 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
List<ILandObject>landObjects = 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);
}
}
}
}

View File

@@ -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
{
/// <summary>
/// Prepare to write out an archive.
/// </summary>
public class ArchiveWriteRequestPreparation
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
/// <summary>
/// The minimum major version of OAR that we can write.
/// </summary>
public static int MIN_MAJOR_VERSION = 0;
/// <summary>
/// The maximum major version of OAR that we can write.
/// </summary>
public static int MAX_MAJOR_VERSION = 0;
/// <summary>
/// Determine whether this archive will save assets. Default is true.
/// </summary>
public bool SaveAssets { get; set; }
protected ArchiverModule m_module;
protected Scene m_scene;
protected Stream m_saveStream;
protected Guid m_requestId;
/// <summary>
/// Constructor
/// </summary>
/// <param name="module">Calling module</param>
/// <param name="savePath">The path to which to save data.</param>
/// <param name="requestId">The id associated with this request</param>
/// <exception cref="System.IO.IOException">
/// If there was a problem opening a stream for the file specified by the savePath
/// </exception>
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);
}
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="module">Calling module</param>
/// <param name="saveStream">The stream to which to save data.</param>
/// <param name="requestId">The id associated with this request</param>
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;
}
/// <summary>
/// Archive the region requested.
/// </summary>
/// <exception cref="System.IO.IOException">if there was an io problem with creating the file</exception>
public void ArchiveRegion(Dictionary<string, object> options)
{
if (options.ContainsKey("noassets") && (bool)options["noassets"])
SaveAssets = false;
try
{
Dictionary<UUID, AssetType> assetUuids = new Dictionary<UUID, AssetType>();
EntityBase[] entities = m_scene.GetEntities();
List<SceneObjectGroup> sceneObjects = new List<SceneObjectGroup>();
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<ITerrainModule>(),
m_scene.RequestModuleInterface<IRegionSerialiserModule>(),
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<UUID>(), new List<UUID>());
}
}
catch (Exception)
{
m_saveStream.Close();
throw;
}
}
/// <summary>
/// Checks whether the user has permission to export an object group to an OAR.
/// </summary>
/// <param name="user">The user</param>
/// <param name="objGroup">The object group</param>
/// <param name="checkPermissions">Which permissions to check: "C" = Copy, "T" = Transfer</param>
/// <returns>Whether the user is allowed to export the object to an OAR</returns>
private bool CanUserArchiveObject(UUID user, SceneObjectGroup objGroup, string checkPermissions)
{
if (checkPermissions == null)
return true;
IPermissionsModule module = m_scene.RequestModuleInterface<IPermissionsModule>();
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;
}
/// <summary>
/// Create the control file for the most up to date archive
/// </summary>
/// <returns></returns>
public string CreateControlFile(Dictionary<string, object> 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;
}
}
}

View File

@@ -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<string> 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<string, object> options)
{
new ArchiveWriteRequestPreparation(this, saveStream, requestId).ArchiveRegion(options);
new ArchiveWriteRequest(Scene, saveStream, requestId).ArchiveRegion(options);
}
public void DearchiveRegion(string loadPath)

View File

@@ -46,6 +46,12 @@ namespace OpenSim.Region.CoreModules.World.Archiver
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
/// <summary>
/// Method called when all the necessary assets for an archive request have been received.
/// </summary>
public delegate void AssetsRequestCallback(
ICollection<UUID> assetsFoundUuids, ICollection<UUID> assetsNotFoundUuids);
enum RequestState
{
Initial,

View File

@@ -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
{
/// <summary>
/// The regions included in an OAR file.
/// </summary>
public class DearchiveScenesInfo
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
/// <summary>
/// One region in the archive.
/// </summary>
public class RegionInfo
{
/// <summary>
/// The subdirectory in which the region is stored.
/// </summary>
public string Directory { get; set; }
/// <summary>
/// The region's coordinates (relative to the South-West corner of the block).
/// </summary>
public Point Location { get; set; }
/// <summary>
/// The UUID of the original scene from which this archived region was saved.
/// </summary>
public string OriginalID { get; set; }
/// <summary>
/// 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.
/// </summary>
public Scene Scene { get; set; }
}
/// <summary>
/// Whether this archive uses the multi-region format.
/// </summary>
public Boolean MultiRegionFormat { get; set; }
/// <summary>
/// Maps (Region directory -> region)
/// </summary>
protected Dictionary<string, RegionInfo> m_directory2region = new Dictionary<string, RegionInfo>();
/// <summary>
/// Maps (UUID of the scene in the simulator where the region will be loaded -> region)
/// </summary>
protected Dictionary<UUID, RegionInfo> m_newId2region = new Dictionary<UUID, RegionInfo>();
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;
}
/// <summary>
/// Sets all the scenes present in the simulator.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
/// <param name="rootScene">The scene where the Load OAR operation was run</param>
/// <param name="simulatorScenes">All the scenes in the simulator</param>
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);
}
}
}
/// <summary>
/// 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.
/// </summary>
/// <param name="fullPath">The path of a file in the archive</param>
/// <param name="scene">The corresponding Scene, or null if none</param>
/// <param name="relativePath">The path relative to the region's directory. (Or the original
/// path, if this file doesn't belong to a region.)</param>
/// <returns>True: use this file; False: skip it</returns>
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;
}
}
/// <summary>
/// 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.
/// </summary>
/// <param name="newID"></param>
/// <returns></returns>
public string GetOriginalRegionID(UUID newID)
{
RegionInfo region;
if (m_newId2region.TryGetValue(newID, out region))
return region.OriginalID;
else
return DefaultOriginalID;
}
/// <summary>
/// Returns the scenes that have been (or will be) loaded.
/// </summary>
/// <returns></returns>
public List<UUID> GetLoadedScenes()
{
return m_newId2region.Keys.ToList();
}
}
}

View File

@@ -47,32 +47,38 @@ 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
{
[TestFixture]
public class ArchiverTests
public class ArchiverTests : OpenSimTestCase
{
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<UUID> 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);
}
/// <summary>
/// Test saving an OpenSim Region Archive with the no assets option
/// </summary>
@@ -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<string, Object>()));
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<string, Object>()));
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());
}
/// <summary>
@@ -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<string, Object>()));
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");
}
}
/// <summary>
/// Test saving a multi-region OAR.
/// </summary>
[Test]
public void TestSaveMultiRegionOar()
{
TestHelpers.InMethod();
// Create test regions
int WIDTH = 2;
int HEIGHT = 2;
List<Scene> scenes = new List<Scene>();
// Maps (Directory in OAR file -> scene)
Dictionary<string, Scene> regionPaths = new Dictionary<string, Scene>();
// Maps (Scene -> expected object paths)
Dictionary<UUID, List<string>> expectedPaths = new Dictionary<UUID, List<string>>();
// List of expected assets
List<UUID> expectedAssets = new List<UUID>();
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<string>();
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<string, Object> options = new Dictionary<string, Object>();
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<UUID, List<string>> foundPaths = new Dictionary<UUID, List<string>>();
List<UUID> foundAssets = new List<UUID>();
foreach (Scene scene in scenes)
{
foundPaths[scene.RegionInfo.RegionID] = new List<string>();
}
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));
}
/// <summary>
/// Test loading a multi-region OAR.
/// </summary>
[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);
}
}
}

View File

@@ -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.
/// <value>
/// </value>
private bool m_Tainted = true;
private Object m_TaintLock = new Object();

View File

@@ -208,6 +208,10 @@ namespace OpenSim.Region.CoreModules.World.Warp3DMap
bitmap = ImageUtils.ResizeImage(origBitmap, viewport.Width, viewport.Height);
}
// XXX: It shouldn't really be necesary to force a GC here as one should occur anyway pretty shortly
// afterwards. It's generally regarded as a bad idea to manually GC. If Warp3D is using lots of memory
// then this may be some issue with the Warp3D code itself, though it's also quite possible that generating
// this map tile simply takes a lot of memory.
GC.Collect();
m_log.Debug("[WARP 3D IMAGE MODULE]: GC.Collect()");

View File

@@ -25,6 +25,8 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Drawing;
using System.IO;
using OpenMetaverse;
@@ -33,7 +35,14 @@ namespace OpenSim.Region.Framework.Interfaces
public interface IDynamicTextureManager
{
void RegisterRender(string handleType, IDynamicTextureRender render);
void ReturnData(UUID id, byte[] data, bool isReuseable);
/// <summary>
/// Used by IDynamicTextureRender implementations to return renders
/// </summary>
/// <param name='id'></param>
/// <param name='data'></param>
/// <param name='isReuseable'></param>
void ReturnData(UUID id, IDynamicTexture texture);
UUID AddDynamicTextureURL(UUID simID, UUID primID, string contentType, string url, string extraParams,
int updateTimer);
@@ -125,11 +134,53 @@ namespace OpenSim.Region.Framework.Interfaces
// /// <param name='extraParams'></param>
// bool AlwaysIdenticalConversion(string bodyData, string extraParams);
byte[] ConvertUrl(string url, string extraParams);
byte[] ConvertData(string bodyData, string extraParams);
IDynamicTexture ConvertUrl(string url, string extraParams);
IDynamicTexture ConvertData(string bodyData, string extraParams);
bool AsyncConvertUrl(UUID id, string url, string extraParams);
bool AsyncConvertData(UUID id, string bodyData, string extraParams);
void GetDrawStringSize(string text, string fontName, int fontSize,
out double xSize, out double ySize);
}
public interface IDynamicTexture
{
/// <summary>
/// Input commands used to generate this data.
/// </summary>
/// <remarks>
/// Null if input commands were not used.
/// </remarks>
string InputCommands { get; }
/// <summary>
/// Uri used to generate this data.
/// </summary>
/// <remarks>
/// Null if a uri was not used.
/// </remarks>
Uri InputUri { get; }
/// <summary>
/// Extra input params used to generate this data.
/// </summary>
string InputParams { get; }
/// <summary>
/// Texture data.
/// </summary>
byte[] Data { get; }
/// <summary>
/// Size of texture.
/// </summary>
Size Size { get; }
/// <summary>
/// Signal whether the texture is reuseable (i.e. whether the same input data will always generate the same
/// texture).
/// </summary>
bool IsReuseable { get; }
}
}

View File

@@ -46,6 +46,11 @@ namespace OpenSim.Region.Framework.Interfaces
/// </summary>
void sendRegionHandshakeToAll();
/// <summary>
/// Fires the OnRegionInfoChange event.
/// </summary>
void TriggerRegionInfoChange();
void setEstateTerrainBaseTexture(int level, UUID texture);
void setEstateTerrainTextureHeights(int corner, float lowValue, float highValue);
}

View File

@@ -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);

View File

@@ -47,26 +47,71 @@ namespace OpenSim.Region.Framework.Scenes
public delegate void OnFrameDelegate();
/// <summary>
/// Triggered on each sim frame.
/// </summary>
/// <remarks>
/// This gets triggered in <see cref="OpenSim.Region.Framework.Scenes.Scene.Update"/>
/// Core uses it for things like Sun, Wind & Clouds
/// The MRM module also uses it.
/// </remarks>
public event OnFrameDelegate OnFrame;
public delegate void ClientMovement(ScenePresence client);
/// <summary>
/// Trigerred when an agent moves.
/// </summary>
/// <remarks>
/// This gets triggered in <see cref="OpenSim.Region.Framework.Scenes.ScenePresence.HandleAgentUpdate"/>
/// prior to <see cref="OpenSim.Region.Framework.Scenes.ScenePresence.TriggerScenePresenceUpdated"/>
/// </remarks>
public event ClientMovement OnClientMovement;
public delegate void OnTerrainTaintedDelegate();
/// <summary>
/// Triggered if the terrain has been edited
/// </summary>
/// <remarks>
/// This gets triggered in <see cref="OpenSim.Region.CoreModules.World.Terrain.CheckForTerrainUpdates"/>
/// after it determines that an update has been made.
/// </remarks>
public event OnTerrainTaintedDelegate OnTerrainTainted;
public delegate void OnTerrainTickDelegate();
/// <summary>
/// Triggered if the terrain has been edited
/// </summary>
/// <remarks>
/// This gets triggered in <see cref="OpenSim.Region.Framework.Scenes.Scene.UpdateTerrain"/>
/// but is used by core solely to update the physics engine.
/// </remarks>
public event OnTerrainTickDelegate OnTerrainTick;
public delegate void OnBackupDelegate(ISimulationDataService datastore, bool forceBackup);
/// <summary>
/// Triggered when a region is backed up/persisted to storage
/// </summary>
/// <remarks>
/// This gets triggered in <see cref="OpenSim.Region.Framework.Scenes.Scene.Backup"/>
/// and is fired before the persistence occurs.
/// </remarks>
public event OnBackupDelegate OnBackup;
public delegate void OnClientConnectCoreDelegate(IClientCore client);
/// <summary>
/// Triggered when a new client connects to the scene.
/// </summary>
/// <remarks>
/// This gets triggered in <see cref="TriggerOnNewClient"/>,
/// which checks if an instance of <see cref="OpenSim.Framework.IClientAPI"/>
/// also implements <see cref="OpenSim.Framework.Client.IClientCore"/> and as such,
/// is not triggered by <see cref="OpenSim.Region.OptionalModules.World.NPC">NPCs</see>.
/// </remarks>
public event OnClientConnectCoreDelegate OnClientConnect;
public delegate void OnNewClientDelegate(IClientAPI client);
@@ -87,18 +132,54 @@ namespace OpenSim.Region.Framework.Scenes
public delegate void OnNewPresenceDelegate(ScenePresence presence);
/// <summary>
/// Triggered when a new presence is added to the scene
/// </summary>
/// <remarks>
/// Triggered in <see cref="OpenSim.Region.Framework.Scenes.Scene.AddNewClient"/> which is used by both
/// <see cref="OpenSim.Framework.PresenceType.User">users</see> and <see cref="OpenSim.Framework.PresenceType.Npc">NPCs</see>
/// </remarks>
public event OnNewPresenceDelegate OnNewPresence;
public delegate void OnRemovePresenceDelegate(UUID agentId);
/// <summary>
/// Triggered when a presence is removed from the scene
/// </summary>
/// <remarks>
/// Triggered in <see cref="OpenSim.Region.Framework.Scenes.Scene.AddNewClient"/> which is used by both
/// <see cref="OpenSim.Framework.PresenceType.User">users</see> and <see cref="OpenSim.Framework.PresenceType.Npc">NPCs</see>
/// </remarks>
public event OnRemovePresenceDelegate OnRemovePresence;
public delegate void OnParcelPrimCountUpdateDelegate();
/// <summary>
/// Triggered whenever the prim count may have been altered, or prior
/// to an action that requires the current prim count to be accurate.
/// </summary>
/// <remarks>
/// Triggered by <see cref="TriggerParcelPrimCountUpdate"/> in
/// <see cref="OpenSim.OpenSimBase.CreateRegion"/>,
/// <see cref="OpenSim.Region.CoreModules.World.Land.LandManagementModule.EventManagerOnRequestParcelPrimCountUpdate"/>,
/// <see cref="OpenSim.Region.CoreModules.World.Land.LandManagementModule.ClientOnParcelObjectOwnerRequest"/>,
/// <see cref="OpenSim.Region.CoreModules.World.Land.LandObject.GetPrimsFree"/>,
/// <see cref="OpenSim.Region.CoreModules.World.Land.LandObject.UpdateLandSold"/>,
/// <see cref="OpenSim.Region.CoreModules.World.Land.LandObject.DeedToGroup"/>,
/// <see cref="OpenSim.Region.CoreModules.World.Land.LandObject.SendLandUpdateToClient"/>
/// </remarks>
public event OnParcelPrimCountUpdateDelegate OnParcelPrimCountUpdate;
public delegate void OnParcelPrimCountAddDelegate(SceneObjectGroup obj);
/// <summary>
/// Triggered in response to <see cref="OnParcelPrimCountUpdate"/> for
/// objects that actually contribute to parcel prim count.
/// </summary>
/// <remarks>
/// Triggered by <see cref="TriggerParcelPrimCountAdd"/> in
/// <see cref="OpenSim.Region.CoreModules.World.Land.LandManagementModule.EventManagerOnParcelPrimCountUpdate"/>
/// </remarks>
public event OnParcelPrimCountAddDelegate OnParcelPrimCountAdd;
public delegate void OnPluginConsoleDelegate(string[] args);
@@ -119,6 +200,14 @@ namespace OpenSim.Region.Framework.Scenes
public event OnSetRootAgentSceneDelegate OnSetRootAgentScene;
/// <summary>
/// Triggered after parcel properties have been updated.
/// </summary>
/// <remarks>
/// Triggered by <see cref="TriggerOnParcelPropertiesUpdateRequest"/> in
/// <see cref="OpenSim.Region.CoreModules.World.Land.LandManagementModule.ClientOnParcelPropertiesUpdateRequest"/>,
/// <see cref="OpenSim.Region.CoreModules.World.Land.LandManagementModule.ProcessPropertiesUpdate"/>
/// </remarks>
public event ParcelPropertiesUpdateRequest OnParcelPropertiesUpdateRequest;
/// <summary>
@@ -369,6 +458,20 @@ namespace OpenSim.Region.Framework.Scenes
public event RequestParcelPrimCountUpdate OnRequestParcelPrimCountUpdate;
public delegate void ParcelPrimCountTainted();
/// <summary>
/// Triggered when the parcel prim count has been altered.
/// </summary>
/// <remarks>
/// Triggered by <see cref="TriggerParcelPrimCountTainted"/> in
/// <see cref="OpenSim.Region.CoreModules.Avatar.Attachments.AttachmentsModule.DetachSingleAttachmentToGround"/>,
/// <see cref="OpenSim.Region.CoreModules.Avatar.Attachments.AttachmentsModule.AttachToAgent"/>,
/// <see cref="Scene.DeleteSceneObject"/>,
/// <see cref="Scene.SelectPrim"/>,
/// <see cref="Scene.DeselectPrim"/>,
/// <see cref="SceneObjectGroup.UpdatePrimFlags"/>,
/// <see cref="SceneObjectGroup.AbsolutePosition"/>
/// </remarks>
public event ParcelPrimCountTainted OnParcelPrimCountTainted;
public event GetScriptRunning OnGetScriptRunning;
@@ -428,7 +531,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
/// </summary>
public delegate void OarFileLoaded(Guid guid, string message);
public delegate void OarFileLoaded(Guid guid, List<UUID> loadedScenes, string message);
public event OarFileLoaded OnOarFileLoaded;
/// <summary>
@@ -481,6 +584,9 @@ namespace OpenSim.Region.Framework.Scenes
/// <param name="copy"></param>
/// <param name="original"></param>
/// <param name="userExposed">True if the duplicate will immediately be in the scene, false otherwise</param>
/// <remarks>
/// Triggered in <see cref="OpenSim.Region.Framework.Scenes.SceneObjectPart.Copy"/>
/// </remarks>
public event SceneObjectPartCopyDelegate OnSceneObjectPartCopy;
public delegate void SceneObjectPartCopyDelegate(SceneObjectPart copy, SceneObjectPart original, bool userExposed);
@@ -589,8 +695,29 @@ namespace OpenSim.Region.Framework.Scenes
public delegate void LandBuy(Object sender, LandBuyArgs e);
/// <summary>
/// Triggered when an attempt to transfer grid currency occurs
/// </summary>
/// <remarks>
/// Triggered in <see cref="OpenSim.Region.Framework.Scenes.Scene.ProcessMoneyTransferRequest"/>
/// via <see cref="OpenSim.Region.Framework.Scenes.Scene.SubscribeToClientGridEvents"/>
/// via <see cref="OpenSim.Region.Framework.Scenes.Scene.SubscribeToClientEvents"/>
/// via <see cref="OpenSim.Region.Framework.Scenes.Scene.AddNewClient"/>
/// </remarks>
public event MoneyTransferEvent OnMoneyTransfer;
/// <summary>
/// Triggered after after <see cref="OnValidateLandBuy"/>
/// </summary>
public event LandBuy OnLandBuy;
/// <summary>
/// Triggered to allow or prevent a real estate transaction
/// </summary>
/// <remarks>
/// Triggered in <see cref="OpenSim.Region.Framework.Scenes.Scene.ProcessParcelBuy"/>
/// <seealso cref="OpenSim.Region.OptionalModules.World.MoneyModule.SampleMoneyModule.ValidateLandBuy"/>
/// </remarks>
public event LandBuy OnValidateLandBuy;
public void TriggerOnAttach(uint localID, UUID itemID, UUID avatarID)
@@ -2068,7 +2195,7 @@ namespace OpenSim.Region.Framework.Scenes
return 6;
}
public void TriggerOarFileLoaded(Guid requestId, string message)
public void TriggerOarFileLoaded(Guid requestId, List<UUID> loadedScenes, string message)
{
OarFileLoaded handlerOarFileLoaded = OnOarFileLoaded;
if (handlerOarFileLoaded != null)
@@ -2077,7 +2204,7 @@ namespace OpenSim.Region.Framework.Scenes
{
try
{
d(requestId, message);
d(requestId, loadedScenes, message);
}
catch (Exception e)
{

View File

@@ -123,6 +123,11 @@ namespace OpenSim.Region.Framework.Scenes
/// </summary>
public float m_maxPhys = 10;
/// <summary>
/// Max prims an object will hold
/// </summary>
public int m_linksetCapacity = 0;
public bool m_clampPrimSize;
public bool m_trustBinaries;
public bool m_allowScriptCrossings;
@@ -772,6 +777,12 @@ namespace OpenSim.Region.Framework.Scenes
m_clampPrimSize = true;
}
m_linksetCapacity = startupConfig.GetInt("LinksetPrims", m_linksetCapacity);
if (RegionInfo.LinksetCapacity > 0)
{
m_linksetCapacity = RegionInfo.LinksetCapacity;
}
m_useTrashOnDelete = startupConfig.GetBoolean("UseTrashOnDelete", m_useTrashOnDelete);
m_trustBinaries = startupConfig.GetBoolean("TrustBinaries", m_trustBinaries);
m_allowScriptCrossings = startupConfig.GetBoolean("AllowScriptCrossing", m_allowScriptCrossings);
@@ -4334,6 +4345,16 @@ namespace OpenSim.Region.Framework.Scenes
return LandChannel.GetLandObject(x, y).LandData;
}
/// <summary>
/// Get LandData by position.
/// </summary>
/// <param name="pos"></param>
/// <returns></returns>
public LandData GetLandData(Vector3 pos)
{
return GetLandData(pos.X, pos.Y);
}
public LandData GetLandData(uint x, uint y)
{
m_log.DebugFormat("[SCENE]: returning land for {0},{1}", x, y);
@@ -4581,6 +4602,18 @@ namespace OpenSim.Region.Framework.Scenes
return m_sceneGraph.GetSceneObjectGroup(name);
}
/// <summary>
/// Attempt to get the SOG via its UUID
/// </summary>
/// <param name="fullID"></param>
/// <param name="sog"></param>
/// <returns></returns>
public bool TryGetSceneObjectGroup(UUID fullID, out SceneObjectGroup sog)
{
sog = GetSceneObjectGroup(fullID);
return sog != null;
}
/// <summary>
/// Get a prim by name from the scene (will return the first
/// found, if there are more than one prim with the same name)
@@ -4612,6 +4645,18 @@ namespace OpenSim.Region.Framework.Scenes
return m_sceneGraph.GetSceneObjectPart(fullID);
}
/// <summary>
/// Attempt to get a prim via its UUID
/// </summary>
/// <param name="fullID"></param>
/// <param name="sop"></param>
/// <returns></returns>
public bool TryGetSceneObjectPart(UUID fullID, out SceneObjectPart sop)
{
sop = GetSceneObjectPart(fullID);
return sop != null;
}
/// <summary>
/// Get a scene object group that contains the prim with the given local id
/// </summary>

View File

@@ -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 List<Scene> m_localScenes = new List<Scene>();

View File

@@ -2014,6 +2014,25 @@ namespace OpenSim.Region.Framework.Scenes
if (objectGroup == this)
return;
// If the configured linkset capacity is greater than zero,
// and the new linkset would have a prim count higher than this
// value, do not link it.
if (m_scene.m_linksetCapacity > 0 &&
(PrimCount + objectGroup.PrimCount) >
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);
return;
}
// 'linkPart' == the root of the group being linked into this group
SceneObjectPart linkPart = objectGroup.m_rootPart;

View File

@@ -447,7 +447,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.Voice.FreeSwitchVoice
// settings allow voice, then whether parcel allows
// voice, if all do retrieve or obtain the parcel
// voice channel
LandData land = scene.GetLandData(avatar.AbsolutePosition.X, avatar.AbsolutePosition.Y);
LandData land = scene.GetLandData(avatar.AbsolutePosition);
//m_log.DebugFormat("[FreeSwitchVoice][PARCELVOICE]: region \"{0}\": Parcel \"{1}\" ({2}): avatar \"{3}\": request: {4}, path: {5}, param: {6}",
// scene.RegionInfo.RegionName, land.Name, land.LocalID, avatarName, request, path, param);

View File

@@ -623,7 +623,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.Voice.VivoxVoice
// settings allow voice, then whether parcel allows
// voice, if all do retrieve or obtain the parcel
// voice channel
LandData land = scene.GetLandData(avatar.AbsolutePosition.X, avatar.AbsolutePosition.Y);
LandData land = scene.GetLandData(avatar.AbsolutePosition);
m_log.DebugFormat("[VivoxVoice][PARCELVOICE]: region \"{0}\": Parcel \"{1}\" ({2}): avatar \"{3}\": request: {4}, path: {5}, param: {6}",
scene.RegionInfo.RegionName, land.Name, land.LocalID, avatarName, request, path, param);

View File

@@ -175,14 +175,15 @@ namespace OpenSim.Region.OptionalModules.Scripting.JsonStore
///
/// </summary>
// -----------------------------------------------------------------
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;
}
}

View File

@@ -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;

View File

@@ -181,7 +181,7 @@ namespace OpenSim.Region.OptionalModules.Scripting.RegionReady
}
}
void OnOarFileLoaded(Guid requestId, string message)
void OnOarFileLoaded(Guid requestId, List<UUID> loadedScenes, string message)
{
m_oarFileLoading = true;

View File

@@ -73,7 +73,8 @@ public class BSCharacter : BSPhysObject
private bool _kinematic;
private float _buoyancy;
public override BulletBody Body { get; set; }
public override BulletBody BSBody { get; set; }
public override BulletShape BSShape { get; set; }
public override BSLinkset Linkset { get; set; }
private int _subscribedEventsMs = 0;
@@ -92,6 +93,7 @@ public class BSCharacter : BSPhysObject
_localID = localID;
_avName = avName;
Scene = parent_scene;
_physicsActorType = (int)ActorTypes.Agent;
_position = pos;
_size = size;
_flying = isFlying;
@@ -128,9 +130,7 @@ public class BSCharacter : BSPhysObject
// Set the buoyancy for flying. This will be refactored when all the settings happen in C#
BulletSimAPI.SetObjectBuoyancy(Scene.WorldID, LocalID, _buoyancy);
Body = 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(Body.Ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
BSBody = new BulletBody(LocalID, BulletSimAPI.GetBodyHandle2(Scene.World.Ptr, LocalID));
});
return;
@@ -440,7 +440,7 @@ public class BSCharacter : BSPhysObject
Scene.TaintedObject("BSCharacter.AddForce", delegate()
{
DetailLog("{0},BSCharacter.setAddForce,taint,addedForce={1}", LocalID, _force);
BulletSimAPI.AddObjectForce2(Body.Ptr, _force);
BulletSimAPI.SetObjectForce2(BSBody.Ptr, _force);
});
}
else
@@ -465,7 +465,7 @@ public class BSCharacter : BSPhysObject
Scene.TaintedObject("BSCharacter.SubscribeEvents", delegate()
{
BulletSimAPI.AddToCollisionFlags2(Body.Ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
BulletSimAPI.AddToCollisionFlags2(BSBody.Ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
});
}
}
@@ -478,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() {
@@ -530,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;
@@ -551,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()

View File

@@ -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,8 +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 the link is enabled, be sure to set the constraint itself to enabled.
BulletSimAPI.SetConstraintEnable2(m_constraint.Ptr, m_world.scene.NumericBool(true));
}
else

View File

@@ -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;
@@ -475,32 +475,33 @@ namespace OpenSim.Region.Physics.BulletSPlugin
frcount = 0;
MoveLinear(pTimestep);
MoveAngular(pTimestep);
// 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,34 +510,29 @@ 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)
@@ -544,18 +540,81 @@ namespace OpenSim.Region.Physics.BulletSPlugin
// 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 +651,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 +712,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 +738,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 +841,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.

View File

@@ -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,15 +247,16 @@ 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;
if (m_physicsScene.Constraints.TryGetConstraint(LinksetRoot.Body, child.Body, out constrain))
if (m_physicsScene.Constraints.TryGetConstraint(LinksetRoot.BSBody, child.BSBody, out constrain))
{
// DetailLog("{0},BSLinkset.RecomputeLinksetConstraintVariables,taint,child={1},mass={2},A={3},B={4}",
// LinksetRoot.LocalID, child.LocalID, linksetMass, constrain.Body1.ID, constrain.Body2.ID);
@@ -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("[BULLETSIM LINKSET] RecomputeLinksetConstraintVariables: constraint not found for root={0}, child={1}",
// 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
{
@@ -327,7 +370,7 @@ public class BSLinkset
DetailLog("{0},PhysicallyLinkAChildToRoot,taint,root={1},child={2},rLoc={3},cLoc={4},midLoc={5}",
rootPrim.LocalID, rootPrim.LocalID, childPrim.LocalID, rootPrim.Position, childPrim.Position, midPoint);
BS6DofConstraint constrain = new BS6DofConstraint(
m_physicsScene.World, rootPrim.Body, childPrim.Body,
m_physicsScene.World, rootPrim.BSBody, childPrim.BSBody,
midPoint,
true,
true
@@ -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();
}
@@ -388,10 +435,10 @@ public class BSLinkset
DetailLog("{0},PhysicallyUnlinkAChildFromRoot,taint,root={1},child={2}", rootPrim.LocalID, rootPrim.LocalID, childPrim.LocalID);
// Find the constraint for this link and get rid of it from the overall collection and from my list
m_physicsScene.Constraints.RemoveAndDestroyConstraint(rootPrim.Body, childPrim.Body);
m_physicsScene.Constraints.RemoveAndDestroyConstraint(rootPrim.BSBody, childPrim.BSBody);
// Make the child refresh its location
BulletSimAPI.PushUpdate2(childPrim.Body.Ptr);
BulletSimAPI.PushUpdate2(childPrim.BSBody.Ptr);
}
// Remove linkage between myself and any possible children I might have
@@ -400,7 +447,7 @@ public class BSLinkset
{
DetailLog("{0},PhysicallyUnlinkAllChildren,taint", rootPrim.LocalID);
m_physicsScene.Constraints.RemoveAndDestroyConstraint(rootPrim.Body);
m_physicsScene.Constraints.RemoveAndDestroyConstraint(rootPrim.BSBody);
}
// Invoke the detailed logger and output something if it's enabled.

View File

@@ -41,14 +41,18 @@ 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();
// Return the object mass without calculating it or side effects
public abstract float MassRaw { get; }
public abstract BulletBody Body { get; set; }
// Reference to the physical body (btCollisionObject) of this object
public abstract BulletBody BSBody { get; set; }
// Reference to the physical shape (btCollisionShape) of this object
public abstract BulletShape BSShape { get; set; }
public abstract void ZeroMotion();
public virtual void StepVehicle(float timeStep) { }

View File

@@ -94,8 +94,10 @@ public sealed class BSPrim : BSPhysObject
private int _nextCollisionOkTime = 0;
long _collidingStep;
long _collidingGroundStep;
CollisionFlags m_currentCollisionFlags = 0;
public override BulletBody Body { get; set; }
public override BulletBody BSBody { get; set; }
public override BulletShape BSShape { get; set; }
private BSDynamics _vehicle;
@@ -113,6 +115,7 @@ public sealed class BSPrim : BSPhysObject
// m_log.DebugFormat("{0}: BSPrim creation of {1}, id={2}", LogHeader, primName, localID);
_localID = localID;
_avName = primName;
_physicsActorType = (int)ActorTypes.Prim;
_scene = parent_scene;
_position = pos;
_size = size;
@@ -133,16 +136,18 @@ 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
// of the object. Someday we'll move that into the C# code.
Body = new BulletBody(LocalID, BulletSimAPI.GetBodyHandle2(_scene.World.Ptr, LocalID));
BSBody = new BulletBody(LocalID, BulletSimAPI.GetBodyHandle2(_scene.World.Ptr, LocalID));
BSShape = new BulletShape(BulletSimAPI.GetCollisionShape2(BSBody.Ptr));
m_currentCollisionFlags = BulletSimAPI.GetCollisionFlags2(BSBody.Ptr);
});
}
@@ -181,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);
});
}
}
@@ -193,7 +199,7 @@ public sealed class BSPrim : BSPhysObject
_scene.TaintedObject("BSPrim.setShape", delegate()
{
_mass = CalculateMass(); // changing the shape changes the mass
RecreateGeomAndObject();
CreateGeomAndObject(false);
});
}
}
@@ -258,10 +264,10 @@ public sealed class BSPrim : BSPhysObject
_rotationalVelocity = OMV.Vector3.Zero;
// Zero some other properties directly into the physics engine
BulletSimAPI.SetVelocity2(Body.Ptr, OMV.Vector3.Zero);
BulletSimAPI.SetAngularVelocity2(Body.Ptr, OMV.Vector3.Zero);
BulletSimAPI.SetInterpolation2(Body.Ptr, OMV.Vector3.Zero, OMV.Vector3.Zero);
BulletSimAPI.ClearForces2(Body.Ptr);
BulletSimAPI.SetLinearVelocity2(BSBody.Ptr, OMV.Vector3.Zero);
BulletSimAPI.SetAngularVelocity2(BSBody.Ptr, OMV.Vector3.Zero);
BulletSimAPI.SetInterpolationVelocity2(BSBody.Ptr, OMV.Vector3.Zero, OMV.Vector3.Zero);
BulletSimAPI.ClearForces2(BSBody.Ptr);
}
public override void LockAngularMotion(OMV.Vector3 axis)
@@ -274,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);
@@ -286,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);
});
}
}
@@ -297,7 +303,8 @@ public sealed class BSPrim : BSPhysObject
{
get
{
return Linkset.LinksetMass;
// return Linkset.LinksetMass;
return _mass;
}
}
@@ -323,8 +330,7 @@ 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(Body.Ptr, _force);
BulletSimAPI.SetObjectForce2(BSBody.Ptr, _force);
});
}
}
@@ -401,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);
});
}
}
@@ -425,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;
}
@@ -436,14 +442,13 @@ 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);
});
}
}
public override int PhysicsActorType {
get { return _physicsActorType; }
set { _physicsActorType = value;
}
set { _physicsActorType = value; }
}
public override bool IsPhysical {
get { return _isPhysical; }
@@ -470,21 +475,131 @@ public sealed class BSPrim : BSPhysObject
// Make gravity work if the object is physical and not selected
// No locking here because only called when it is safe
// There are four flags we're interested in:
// IsStatic: Object does not move, otherwise the object has mass and moves
// isSolid: other objects bounce off of this object
// isVolumeDetect: other objects pass through but can generate collisions
// collisionEvents: whether this object returns collision events
private void SetObjectDynamic()
{
// 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);
// recompute any linkset parameters
// Set up the object physicalness (does gravity and collisions move this object)
MakeDynamic(IsStatic);
// Make solid or not (do things bounce off or pass through this object)
MakeSolid(IsSolid);
// 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);
CollisionFlags cf = BulletSimAPI.GetCollisionFlags2(Body.Ptr);
DetailLog("{0},BSPrim.SetObjectDynamic,taint,static={1},solid={2},mass={3}, cf={4}", LocalID, IsStatic, IsSolid, mass, cf);
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.
// When static, gravity does not effect the object and it is fixed in space.
// 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(bool makeStatic)
{
if (makeStatic)
{
// Become a Bullet 'static' object type
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
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
IntPtr collisionShapePtr = BulletSimAPI.GetCollisionShape2(BSBody.Ptr);
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);
}
}
// "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
@@ -544,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);
});
}
}
@@ -561,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);
});
}
}
@@ -609,7 +727,7 @@ public sealed class BSPrim : BSPhysObject
}
else
{
m_log.WarnFormat("{0}: Got a NaN force applied to a Character", LogHeader);
m_log.WarnFormat("{0}: Got a NaN force applied to a prim. LocalID={1}", LogHeader, LocalID);
return;
}
_scene.TaintedObject("BSPrim.AddForce", delegate()
@@ -623,8 +741,9 @@ public sealed class BSPrim : BSPhysObject
}
m_accumulatedForces.Clear();
}
DetailLog("{0},BSPrim.AddObjectForce,taint,force={1}", LocalID, _force);
BulletSimAPI.AddObjectForce2(Body.Ptr, fSum);
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);
});
}
@@ -644,7 +763,7 @@ public sealed class BSPrim : BSPhysObject
Scene.TaintedObject("BSPrim.SubscribeEvents", delegate()
{
BulletSimAPI.AddToCollisionFlags2(Body.Ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
m_currentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(BSBody.Ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
});
}
}
@@ -652,7 +771,7 @@ public sealed class BSPrim : BSPhysObject
_subscribedEventsMs = 0;
Scene.TaintedObject("BSPrim.UnSubscribeEvents", delegate()
{
BulletSimAPI.RemoveFromCollisionFlags2(Body.Ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
m_currentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(BSBody.Ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
});
}
public override bool SubscribedEvents() {
@@ -959,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);
@@ -999,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
@@ -1016,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();
}
}
}
@@ -1025,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;
@@ -1039,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
@@ -1076,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);
@@ -1191,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.
@@ -1204,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);
}
}
@@ -1236,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.
Body = new BulletBody(LocalID, BulletSimAPI.GetBodyHandle2(_scene.World.Ptr, LocalID));
// 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;
}
@@ -1261,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;
}
@@ -1366,15 +1492,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);
@@ -1382,21 +1508,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

View File

@@ -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<BSPhysObject> m_objectsWithCollisions = new HashSet<BSPhysObject>();
// Following is a kludge and can be removed when avatar animation updating is
// moved to a better place.
private HashSet<BSCharacter> m_avatarsWithCollisions = new HashSet<BSCharacter>();
private HashSet<BSPhysObject> m_avatarsWithCollisions = new HashSet<BSPhysObject>();
// List of all the objects that have vehicle properties and should be called
// to update each physics step.
@@ -132,8 +131,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 +152,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 +175,8 @@ public class BSScene : PhysicsScene, IPhysicsParameters
callback = c;
}
}
private Object _taintLock = new Object(); // lock for using the next object
private List<TaintCallbackEntry> _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.
@@ -478,6 +482,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 +511,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 collisions happen.
m_objectsWithCollisions = new HashSet<BSPhysObject>(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 +538,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 +548,6 @@ public class BSScene : PhysicsScene, IPhysicsParameters
if (PhysObjects.TryGetValue(entprop.ID, out pobj))
{
pobj.UpdateProperties(entprop);
continue;
}
}
}
@@ -580,28 +583,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 +619,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 +659,6 @@ public class BSScene : PhysicsScene, IPhysicsParameters
public override bool IsThreaded { get { return false; } }
/// <summary>
/// Routine to figure out if we need to mesh this prim with our mesher
/// </summary>
/// <param name="pbs"></param>
/// <returns>true if the prim needs meshing</returns>
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 +667,10 @@ public class BSScene : PhysicsScene, IPhysicsParameters
if (!m_initialized) return;
lock (_taintLock)
{
_taintedObjects.Add(new TaintCallbackEntry(ident, callback));
}
return;
}
@@ -919,14 +807,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 +1050,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 +1060,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,

View File

@@ -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,19 +411,27 @@ public class BSTerrainManager
{
float regionX = tX - offsetX;
float regionY = tY - offsetY;
if (regionX > mapInfo.sizeX) regionX = 0;
if (regionY > mapInfo.sizeY) regionY = 0;
int mapIndex = (int)regionY * (int)mapInfo.sizeY + (int)regionX;
ret = mapInfo.heightMap[mapIndex];
m_terrainModified = false;
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);
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);
}
else
{
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;
}

View File

@@ -213,12 +213,26 @@ 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;
}
// Values used by Bullet and BulletSim to control collisions
// The states a bullet collision object can have
public enum ActivationState : uint
{
ACTIVE_TAG = 1,
ISLAND_SLEEPING,
WANTS_DEACTIVATION,
DISABLE_DEACTIVATION,
DISABLE_SIMULATION
}
// Values used by Bullet and BulletSim to control object properties.
// Bullet's "CollisionFlags" has more to do with operations on the
// object (if collisions happen, if gravity effects it, ...).
public enum CollisionFlags : uint
{
CF_STATIC_OBJECT = 1 << 0,
@@ -233,8 +247,75 @@ public enum CollisionFlags : uint
BS_VOLUME_DETECT_OBJECT = 1 << 11,
BS_PHANTOM_OBJECT = 1 << 12,
BS_PHYSICAL_OBJECT = 1 << 13,
BS_TERRAIN_OBJECT = 1 << 14,
BS_NONE = 0,
BS_ALL = 0xFFFFFFFF
};
// Values for collisions groups and masks
public enum CollisionFilterGroups : uint
{
NoneFilter = 0,
DefaultFilter = 1 << 0,
StaticFilter = 1 << 1,
KinematicFilter = 1 << 2,
DebrisFilter = 1 << 3,
SensorTrigger = 1 << 4,
CharacterFilter = 1 << 5,
AllFilter = 0xFFFFFFFF,
// Filter groups defined by BulletSim
GroundPlaneFilter = 1 << 10,
TerrainFilter = 1 << 11,
RaycastFilter = 1 << 12,
SolidFilter = 1 << 13,
};
// For each type, we first clear and then set the collision flags
public enum ClearCollisionFlag : uint
{
Terrain = CollisionFlags.BS_ALL,
Phantom = CollisionFlags.BS_ALL,
VolumeDetect = CollisionFlags.BS_ALL,
PhysicalObject = CollisionFlags.BS_ALL,
StaticObject = CollisionFlags.BS_ALL
}
public enum SetCollisionFlag : uint
{
Terrain = CollisionFlags.CF_STATIC_OBJECT
| CollisionFlags.BS_TERRAIN_OBJECT,
Phantom = CollisionFlags.CF_STATIC_OBJECT
| CollisionFlags.BS_PHANTOM_OBJECT
| CollisionFlags.CF_NO_CONTACT_RESPONSE,
VolumeDetect = CollisionFlags.CF_STATIC_OBJECT
| CollisionFlags.BS_VOLUME_DETECT_OBJECT
| CollisionFlags.CF_NO_CONTACT_RESPONSE,
PhysicalObject = CollisionFlags.BS_PHYSICAL_OBJECT,
StaticObject = CollisionFlags.CF_STATIC_OBJECT,
}
// Collision filters used for different types of objects
public enum SetCollisionFilter : uint
{
Terrain = CollisionFilterGroups.AllFilter,
Phantom = CollisionFilterGroups.GroundPlaneFilter
| CollisionFilterGroups.TerrainFilter,
VolumeDetect = CollisionFilterGroups.AllFilter,
PhysicalObject = CollisionFilterGroups.AllFilter,
StaticObject = CollisionFilterGroups.AllFilter,
}
// Collision masks used for different types of objects
public enum SetCollisionMask : uint
{
Terrain = CollisionFilterGroups.AllFilter,
Phantom = CollisionFilterGroups.GroundPlaneFilter
| CollisionFilterGroups.TerrainFilter,
VolumeDetect = CollisionFilterGroups.AllFilter,
PhysicalObject = CollisionFilterGroups.AllFilter,
StaticObject = CollisionFilterGroups.AllFilter
}
// CFM controls the 'hardness' of the constraint. 0=fixed, 0..1=violatable. Default=0
// ERP controls amount of correction per tick. Usable range=0.1..0.8. Default=0.2.
public enum ConstraintParams : int
@@ -315,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);
@@ -347,6 +411,7 @@ public static extern bool SetObjectVelocity(uint worldID, uint id, Vector3 veloc
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool SetObjectAngularVelocity(uint worldID, uint id, Vector3 angularVelocity);
// Set the current force acting on the object
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool SetObjectForce(uint worldID, uint id, Vector3 force);
@@ -403,6 +468,7 @@ public static extern void SetDebugLogCallback(DebugLogCallback callback);
// The names have a "2" tacked on. This will be removed as the C# code gets rebuilt
// and the old code is removed.
// Functions use while converting from API1 to API2. Can be removed when totally converted.
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern IntPtr GetSimHandle2(uint worldID);
@@ -413,6 +479,7 @@ public static extern IntPtr GetBodyHandleWorldID2(uint worldID, uint id);
public static extern IntPtr GetBodyHandle2(IntPtr world, uint id);
// ===============================================================================
// Initialization and simulation
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern IntPtr Initialize2(Vector3 maxPosition, IntPtr parms,
int maxCollisions, IntPtr collisionArray,
@@ -438,6 +505,7 @@ public static extern int PhysicsStep2(IntPtr world, float timeStep, int maxSubSt
public static extern bool PushUpdate2(IntPtr obj);
// =====================================================================================
// Mesh, hull, shape and body creation helper routines
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern IntPtr CreateMeshShape2(IntPtr world,
int indicesCount, [MarshalAs(UnmanagedType.LPArray)] int[] indices,
@@ -454,6 +522,21 @@ 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);
@@ -464,8 +547,19 @@ public static extern IntPtr CreateBodyFromShape2(IntPtr sim, IntPtr shape, Vecto
public static extern IntPtr CreateBodyWithDefaultMotionState2(IntPtr shape, Vector3 pos, Quaternion rot);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool SetBodyShape2(IntPtr sim, IntPtr obj, IntPtr shape);
public static extern IntPtr AllocateBodyInfo2(IntPtr obj);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void ReleaseBodyInfo2(IntPtr obj);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
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);
@@ -484,6 +578,7 @@ public static extern IntPtr CreateGroundPlaneShape2(uint id, float height, float
public static extern IntPtr CreateTerrainShape2(IntPtr mapInfo);
// =====================================================================================
// Constraint creation and helper routines
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern IntPtr Create6DofConstraint2(IntPtr world, IntPtr obj1, IntPtr obj2,
Vector3 frame1loc, Quaternion frame1rot,
@@ -536,16 +631,108 @@ public static extern bool SetConstraintParam2(IntPtr constrain, ConstraintParams
public static extern bool DestroyConstraint2(IntPtr world, IntPtr constrain);
// =====================================================================================
// btCollisionWorld entries
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void UpdateSingleAabb2(IntPtr world, IntPtr obj);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void UpdateAabbs2(IntPtr world);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool GetForceUpdateAllAabbs2(IntPtr world);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void SetForceUpdateAllAabbs2(IntPtr world, bool force);
// =====================================================================================
// btDynamicsWorld entries
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool AddObjectToWorld2(IntPtr world, IntPtr obj);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool RemoveObjectFromWorld2(IntPtr world, IntPtr obj);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool AddConstraintToWorld2(IntPtr world, IntPtr constrain, bool disableCollisionsBetweenLinkedObjects);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool RemoveConstraintFromWorld2(IntPtr world, IntPtr constrain);
// =====================================================================================
// btCollisionObject entries
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern Vector3 GetAnisotripicFriction2(IntPtr constrain);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern Vector3 SetAnisotripicFriction2(IntPtr constrain, Vector3 frict);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool HasAnisotripicFriction2(IntPtr constrain);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void SetContactProcessingThreshold2(IntPtr obj, float val);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern float GetContactProcessingThreshold2(IntPtr obj);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool IsStaticObject2(IntPtr obj);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool IsKinematicObject2(IntPtr obj);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool IsStaticOrKinematicObject2(IntPtr obj);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool HasContactResponse2(IntPtr obj);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void SetCollisionShape2(IntPtr sim, IntPtr obj, IntPtr shape);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern IntPtr GetCollisionShape2(IntPtr obj);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern int GetActivationState2(IntPtr obj);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void SetActivationState2(IntPtr obj, int state);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void SetDeactivationTime2(IntPtr obj, float dtime);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern float GetDeactivationTime2(IntPtr obj);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void ForceActivationState2(IntPtr obj, ActivationState state);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void Activate2(IntPtr obj, bool forceActivation);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool IsActive2(IntPtr obj);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void SetRestitution2(IntPtr obj, float val);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern float GetRestitution2(IntPtr obj);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void SetFriction2(IntPtr obj, float val);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern float GetFriction2(IntPtr obj);
/* Haven't defined the type 'Transform'
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern Transform GetWorldTransform2(IntPtr obj);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void setWorldTransform2(IntPtr obj, Transform trans);
*/
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern Vector3 GetPosition2(IntPtr obj);
@@ -553,89 +740,288 @@ public static extern Vector3 GetPosition2(IntPtr obj);
public static extern Quaternion GetOrientation2(IntPtr obj);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool SetTranslation2(IntPtr obj, Vector3 position, Quaternion rotation);
public static extern void SetTranslation2(IntPtr obj, Vector3 position, Quaternion rotation);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool SetVelocity2(IntPtr obj, Vector3 velocity);
public static extern IntPtr GetBroadphaseHandle2(IntPtr obj);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool SetAngularVelocity2(IntPtr obj, Vector3 angularVelocity);
public static extern void SetBroadphaseHandle2(IntPtr obj, IntPtr handle);
/*
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern Transform GetInterpolationWorldTransform2(IntPtr obj);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool SetObjectForce2(IntPtr obj, Vector3 force);
public static extern void SetInterpolationWorldTransform2(IntPtr obj, Transform trans);
*/
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool AddObjectForce2(IntPtr obj, Vector3 force);
public static extern void SetInterpolationLinearVelocity2(IntPtr obj, Vector3 vel);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool SetCcdMotionThreshold2(IntPtr obj, float val);
public static extern void SetInterpolationAngularVelocity2(IntPtr obj, Vector3 vel);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool SetCcdSweepSphereRadius2(IntPtr obj, float val);
public static extern void SetInterpolationVelocity2(IntPtr obj, Vector3 linearVel, Vector3 angularVel);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool SetDamping2(IntPtr obj, float lin_damping, float ang_damping);
public static extern float GetHitFraction2(IntPtr obj);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool SetDeactivationTime2(IntPtr obj, float val);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool SetSleepingThresholds2(IntPtr obj, float lin_threshold, float ang_threshold);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool SetContactProcessingThreshold2(IntPtr obj, float val);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool SetFriction2(IntPtr obj, float val);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool SetHitFraction2(IntPtr obj, float val);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool SetRestitution2(IntPtr obj, float val);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool SetLinearVelocity2(IntPtr obj, Vector3 val);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool SetInterpolation2(IntPtr obj, Vector3 lin, Vector3 ang);
public static extern void SetHitFraction2(IntPtr obj, float val);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern CollisionFlags GetCollisionFlags2(IntPtr obj);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern IntPtr SetCollisionFlags2(IntPtr obj, CollisionFlags flags);
public static extern CollisionFlags SetCollisionFlags2(IntPtr obj, CollisionFlags flags);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern IntPtr AddToCollisionFlags2(IntPtr obj, CollisionFlags flags);
public static extern CollisionFlags AddToCollisionFlags2(IntPtr obj, CollisionFlags flags);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern IntPtr RemoveFromCollisionFlags2(IntPtr obj, CollisionFlags flags);
public static extern CollisionFlags RemoveFromCollisionFlags2(IntPtr obj, CollisionFlags flags);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool SetMassProps2(IntPtr obj, float mass, Vector3 inertia);
public static extern float GetCcdMotionThreshold2(IntPtr obj);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool UpdateInertiaTensor2(IntPtr obj);
public static extern void SetCcdMotionThreshold2(IntPtr obj, float val);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool SetGravity2(IntPtr obj, Vector3 val);
public static extern float GetCcdSweepSphereRadius2(IntPtr obj);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern IntPtr ClearForces2(IntPtr obj);
public static extern void SetCcdSweepSphereRadius2(IntPtr obj, float val);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern IntPtr ClearAllForces2(IntPtr obj);
public static extern IntPtr GetUserPointer2(IntPtr obj);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool SetMargin2(IntPtr obj, float val);
public static extern void SetUserPointer2(IntPtr obj, IntPtr val);
// =====================================================================================
// btRigidBody entries
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void ApplyGravity2(IntPtr obj);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool UpdateSingleAabb2(IntPtr world, IntPtr obj);
public static extern void SetGravity2(IntPtr obj, Vector3 val);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool DestroyObject2(IntPtr world, IntPtr obj);
public static extern Vector3 GetGravity2(IntPtr obj);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void SetDamping2(IntPtr obj, float lin_damping, float ang_damping);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern float GetLinearDamping2(IntPtr obj);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern float GetAngularDamping2(IntPtr obj);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern float GetLinearSleepingThreshold2(IntPtr obj);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern float GetAngularSleepingThreshold2(IntPtr obj);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void ApplyDamping2(IntPtr obj, float timeStep);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void SetMassProps2(IntPtr obj, float mass, Vector3 inertia);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern Vector3 GetLinearFactor2(IntPtr obj);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void SetLinearFactor2(IntPtr obj, Vector3 factor);
/*
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void SetCenterOfMassTransform2(IntPtr obj, Transform trans);
*/
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void SetCenterOfMassByPosRot2(IntPtr obj, Vector3 pos, Quaternion rot);
// Add a force to the object as if its mass is one.
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void ApplyCentralForce2(IntPtr obj, Vector3 force);
// Set the force being applied to the object as if its mass is one.
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void SetObjectForce2(IntPtr obj, Vector3 force);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern Vector3 GetTotalForce2(IntPtr obj);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern Vector3 GetTotalTorque2(IntPtr obj);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern Vector3 GetInvInertiaDiagLocal2(IntPtr obj);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void SetInvInertiaDiagLocal2(IntPtr obj, Vector3 inert);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void SetSleepingThresholds2(IntPtr obj, float lin_threshold, float ang_threshold);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void ApplyTorque2(IntPtr obj, Vector3 torque);
// Apply force at the given point. Will add torque to the object.
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void ApplyForce2(IntPtr obj, Vector3 force, Vector3 pos);
// Apply impulse to the object. Same as "ApplycentralForce" but force scaled by object's mass.
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void ApplyCentralImpulse2(IntPtr obj, Vector3 imp);
// Apply impulse to the object's torque. Force is scaled by object's mass.
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void ApplyTorqueImpulse2(IntPtr obj, Vector3 imp);
// Apply impulse at the point given. For is scaled by object's mass and effects both linear and angular forces.
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void ApplyImpulse2(IntPtr obj, Vector3 imp, Vector3 pos);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void ClearForces2(IntPtr obj);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void ClearAllForces2(IntPtr obj);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void UpdateInertiaTensor2(IntPtr obj);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern Vector3 GetCenterOfMassPosition2(IntPtr obj);
/*
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern Transform GetCenterOfMassTransform2(IntPtr obj);
*/
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern Vector3 GetLinearVelocity2(IntPtr obj);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern Vector3 GetAngularVelocity2(IntPtr obj);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void SetLinearVelocity2(IntPtr obj, Vector3 val);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void SetAngularVelocity2(IntPtr obj, Vector3 angularVelocity);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern Vector3 GetVelocityInLocalPoint2(IntPtr obj, Vector3 pos);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void Translate2(IntPtr obj, Vector3 trans);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void UpdateDeactivation2(IntPtr obj, float timeStep);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool WantsSleeping2(IntPtr obj);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void SetAngularFactor2(IntPtr obj, float factor);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void SetAngularFactorV2(IntPtr obj, Vector3 factor);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern Vector3 GetAngularFactor2(IntPtr obj);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool IsInWorld2(IntPtr obj);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void AddConstraintRef2(IntPtr obj, IntPtr constrain);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void RemoveConstraintRef2(IntPtr obj, IntPtr constrain);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern IntPtr GetConstraintRef2(IntPtr obj, int index);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern int GetNumConstraintRefs2(IntPtr obj);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern Vector3 GetDeltaLinearVelocity2(IntPtr obj);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern Vector3 GetDeltaAngularVelocity2(IntPtr obj);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern Vector3 GetPushVelocity2(IntPtr obj);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern Vector3 GetTurnVelocity2(IntPtr obj);
// =====================================================================================
// btCollisionShape entries
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern float GetAngularMotionDisc2(IntPtr shape);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern float GetContactBreakingThreshold2(IntPtr shape, float defaultFactor);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool IsPolyhedral2(IntPtr shape);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool IsConvex2d2(IntPtr shape);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool IsConvex2(IntPtr shape);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool IsNonMoving2(IntPtr shape);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool IsConcave2(IntPtr shape);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool IsCompound2(IntPtr shape);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool IsSoftBody2(IntPtr shape);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern bool IsInfinite2(IntPtr shape);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void SetLocalScaling2(IntPtr shape, Vector3 scale);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern Vector3 GetLocalScaling2(IntPtr shape);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern Vector3 CalculateLocalInertia2(IntPtr shape, float mass);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern int GetShapeType2(IntPtr shape);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void SetMargin2(IntPtr shape, float val);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern float GetMargin2(IntPtr shape);
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void SetCollisionFilterMask(IntPtr shape, uint filter, uint mask);
// =====================================================================================
// Debugging
[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
public static extern void DumpPhysicsStatistics2(IntPtr sim);

View File

@@ -39,11 +39,8 @@ using OpenSim.Framework.Console;
using OpenSim.Region.Physics.Manager;
using Mono.Addins;
[assembly: Addin("RegionCombinerModule", "0.1")]
[assembly: AddinDependency("OpenSim", "0.5")]
namespace OpenSim.Region.RegionCombinerModule
{
[Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule")]
public class RegionCombinerModule : ISharedRegionModule, IRegionCombinerModule
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);

View File

@@ -0,0 +1,14 @@
<Addin id="OpenSim.RegionModules.RegionCombinerModule" version="0.3">
<Runtime>
<Import assembly="OpenSim.Region.RegionCombinerModule.dll"/>
</Runtime>
<Dependencies>
<Addin id="OpenSim" version="0.5" />
</Dependencies>
<Extension path = "/OpenSim/RegionModules">
<RegionModule id="RegionCombinerModule" type="OpenSim.Region.RegionCombinerModule.RegionCombinerModule" />
</Extension>
</Addin>

View File

@@ -1948,7 +1948,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
pos.x > (Constants.RegionSize + 10) || // return FALSE if more than 10 meters into a east-adjacent region.
pos.y < -10.0 || // return FALSE if more than 10 meters into a south-adjacent region.
pos.y > (Constants.RegionSize + 10) || // return FALSE if more than 10 meters into a north-adjacent region.
pos.z > 4096 // return FALSE if altitude than 4096m
pos.z > Constants.RegionHeight // return FALSE if altitude than 4096m
)
)
{
@@ -1959,8 +1959,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
// this could possibly be done in the above else-if block, but we're doing the check here to keep the code easier to read.
Vector3 objectPos = m_host.ParentGroup.RootPart.AbsolutePosition;
LandData here = World.GetLandData((float)objectPos.X, (float)objectPos.Y);
LandData there = World.GetLandData((float)pos.x, (float)pos.y);
LandData here = World.GetLandData(objectPos);
LandData there = World.GetLandData(pos);
// we're only checking prim limits if it's moving to a different parcel under the assumption that if the object got onto the parcel without exceeding the prim limits.
@@ -9770,20 +9770,20 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
switch ((ParcelMediaCommandEnum) aList.Data[i])
{
case ParcelMediaCommandEnum.Url:
list.Add(new LSL_String(World.GetLandData(m_host.AbsolutePosition.X, m_host.AbsolutePosition.Y).MediaURL));
list.Add(new LSL_String(World.GetLandData(m_host.AbsolutePosition).MediaURL));
break;
case ParcelMediaCommandEnum.Desc:
list.Add(new LSL_String(World.GetLandData(m_host.AbsolutePosition.X, m_host.AbsolutePosition.Y).Description));
list.Add(new LSL_String(World.GetLandData(m_host.AbsolutePosition).Description));
break;
case ParcelMediaCommandEnum.Texture:
list.Add(new LSL_String(World.GetLandData(m_host.AbsolutePosition.X, m_host.AbsolutePosition.Y).MediaID.ToString()));
list.Add(new LSL_String(World.GetLandData(m_host.AbsolutePosition).MediaID.ToString()));
break;
case ParcelMediaCommandEnum.Type:
list.Add(new LSL_String(World.GetLandData(m_host.AbsolutePosition.X, m_host.AbsolutePosition.Y).MediaType));
list.Add(new LSL_String(World.GetLandData(m_host.AbsolutePosition).MediaType));
break;
case ParcelMediaCommandEnum.Size:
list.Add(new LSL_String(World.GetLandData(m_host.AbsolutePosition.X, m_host.AbsolutePosition.Y).MediaWidth));
list.Add(new LSL_String(World.GetLandData(m_host.AbsolutePosition.X, m_host.AbsolutePosition.Y).MediaHeight));
list.Add(new LSL_String(World.GetLandData(m_host.AbsolutePosition).MediaWidth));
list.Add(new LSL_String(World.GetLandData(m_host.AbsolutePosition).MediaHeight));
break;
default:
ParcelMediaCommandEnum mediaCommandEnum = ParcelMediaCommandEnum.Url;
@@ -10398,7 +10398,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
public LSL_List llGetParcelDetails(LSL_Vector pos, LSL_List param)
{
m_host.AddScriptLPS(1);
LandData land = World.GetLandData((float)pos.x, (float)pos.y);
LandData land = World.GetLandData(pos);
if (land == null)
{
return new LSL_List(0);

View File

@@ -254,7 +254,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
object[] convertedParms = new object[parms.Length];
for (int i = 0; i < parms.Length; i++)
convertedParms[i] = ConvertFromLSL(parms[i],signature[i]);
convertedParms[i] = ConvertFromLSL(parms[i],signature[i], fname);
// now call the function, the contract with the function is that it will always return
// non-null but don't trust it completely
@@ -294,7 +294,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
/// <summary>
/// </summary>
protected object ConvertFromLSL(object lslparm, Type type)
protected object ConvertFromLSL(object lslparm, Type type, string fname)
{
// ---------- String ----------
if (lslparm is LSL_String)
@@ -374,14 +374,14 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
(LSL_Vector)plist[i]);
}
else
MODError("unknown LSL list element type");
MODError(String.Format("{0}: unknown LSL list element type", fname));
}
return result;
}
}
MODError(String.Format("parameter type mismatch; expecting {0}",type.Name));
MODError(String.Format("{1}: parameter type mismatch; expecting {0}",type.Name, fname));
return null;
}

View File

@@ -2941,7 +2941,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
ScenePresence presence = World.GetScenePresence(avatarId);
if (presence != null)
{
LandData land = World.GetLandData((float)pos.X, (float)pos.Y);
LandData land = World.GetLandData(pos);
if ((land.Flags & (uint)ParcelFlags.AllowDamage) == (uint)ParcelFlags.AllowDamage)
{
float health = presence.Health;

View File

@@ -26,6 +26,7 @@
*/
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Threading;
@@ -71,10 +72,17 @@ namespace OpenSim.Server.Base
//
private string m_pidFile = String.Empty;
/// <summary>
/// Time at which this server was started
/// </summary>
protected DateTime m_startuptime;
// Handle all the automagical stuff
//
public ServicesServerBase(string prompt, string[] args)
{
m_startuptime = DateTime.Now;
// Save raw arguments
//
m_Arguments = args;
@@ -250,6 +258,10 @@ namespace OpenSim.Server.Base
"command-script <script>",
"Run a command script from file", HandleScript);
MainConsole.Instance.Commands.AddCommand("General", false, "show uptime",
"show uptime",
"Show server uptime", HandleShow);
// Allow derived classes to perform initialization that
// needs to be done after the console has opened
@@ -345,5 +357,34 @@ namespace OpenSim.Server.Base
{
}
}
public virtual void HandleShow(string module, string[] cmd)
{
List<string> args = new List<string>(cmd);
args.RemoveAt(0);
string[] showParams = args.ToArray();
switch (showParams[0])
{
case "uptime":
MainConsole.Instance.Output(GetUptimeReport());
break;
}
}
/// <summary>
/// Return a report about the uptime of this server
/// </summary>
/// <returns></returns>
protected string GetUptimeReport()
{
StringBuilder sb = new StringBuilder(String.Format("Time now is {0}\n", DateTime.Now));
sb.Append(String.Format("Server has been running since {0}, {1}\n", m_startuptime.DayOfWeek, m_startuptime));
sb.Append(String.Format("That is an elapsed time of {0}\n", DateTime.Now - m_startuptime));
return sb.ToString();
}
}
}

View File

@@ -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();
}
/// <summary>
/// Remove parameters that were used to invoke the method and should not in themselves be persisted.
/// </summary>
/// <param name='request'></param>
private void RemoveRequestParamsNotForStorage(Dictionary<string, object> request)
{
request.Remove("VERSIONMAX");
request.Remove("VERSIONMIN");
request.Remove("METHOD");
request.Remove("UserID");
}
byte[] SetItems(Dictionary<string, object> request)
{
@@ -173,6 +189,8 @@ namespace OpenSim.Server.Handlers.Avatar
if (!(request["Names"] is List<string> || request["Values"] is List<string>))
return FailureResult();
RemoveRequestParamsNotForStorage(request);
List<string> _names = (List<string>)request["Names"];
names = _names.ToArray();
List<string> _values = (List<string>)request["Values"];

View File

@@ -84,7 +84,7 @@ namespace OpenSim.Services.AssetService
m_Database = LoadPlugin<IAssetDataPlugin>(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<IAssetLoader>(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));
}
}
}

View File

@@ -101,7 +101,7 @@ namespace OpenSim.Services.Connectors.SimianGrid
public string RegisterRegion(UUID scopeID, GridRegion regionInfo)
{
Vector3d minPosition = new Vector3d(regionInfo.RegionLocX, regionInfo.RegionLocY, 0.0);
Vector3d maxPosition = minPosition + new Vector3d(Constants.RegionSize, Constants.RegionSize, 4096.0);
Vector3d maxPosition = minPosition + new Vector3d(Constants.RegionSize, Constants.RegionSize, Constants.RegionHeight);
OSDMap extraData = new OSDMap
{
@@ -286,7 +286,7 @@ namespace OpenSim.Services.Connectors.SimianGrid
List<GridRegion> foundRegions = new List<GridRegion>();
Vector3d minPosition = new Vector3d(xmin, ymin, 0.0);
Vector3d maxPosition = new Vector3d(xmax, ymax, 4096.0);
Vector3d maxPosition = new Vector3d(xmax, ymax, Constants.RegionHeight);
NameValueCollection requestArgs = new NameValueCollection
{

View File

@@ -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<UUID, List<XInventoryFolder>> m_SuitcaseTrees = new ExpiringCache<UUID, List<XInventoryFolder>>();
private ExpiringCache<UUID, AvatarAppearance> m_Appearances = new ExpiringCache<UUID, AvatarAppearance>();
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<IAvatarService>(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,51 @@ 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
}
}

View File

@@ -1,8 +1,6 @@
Welcome to OpenSim!
==================
==== OVERVIEW ====
==================
# Overview
OpenSim is a BSD Licensed Open Source project to develop a functioning
virtual worlds server platform capable of supporting multiple clients
@@ -12,16 +10,12 @@ C#, and can run under Mono or the Microsoft .NET runtimes.
This is considered an alpha release. Some stuff works, a lot doesn't.
If it breaks, you get to keep *both* pieces.
=========================
=== Compiling OpenSim ===
=========================
# Compiling OpenSim
Please see BUILDING.txt if you downloaded a source distribution and
Please see BUILDING.md if you downloaded a source distribution and
need to build OpenSim before running it.
==================================
=== Running OpenSim on Windows ===
==================================
# Running OpenSim on Windows
We recommend that you run OpenSim from a command prompt on Windows in order
to capture any errors.
@@ -33,9 +27,7 @@ To run OpenSim from a command prompt
Now see the "Configuring OpenSim" section
================================
=== Running OpenSim on Linux ===
================================
# Running OpenSim on Linux
You will need Mono >= 2.4.3 to run OpenSim. On some Linux distributions you
may need to install additional packages. See http://opensimulator.org/wiki/Dependencies
@@ -48,14 +40,12 @@ To run OpenSim, from the unpacked distribution type:
Now see the "Configuring OpenSim" section
===========================
=== Configuring OpenSim ===
===========================
# Configuring OpenSim
When OpenSim starts for the first time, you will be prompted with a
series of questions that look something like:
[09-17 03:54:40] DEFAULT REGION CONFIG: Simulator Name [OpenSim Test]:
[09-17 03:54:40] DEFAULT REGION CONFIG: Simulator Name [OpenSim Test]:
For all the options except simulator name, you can safely hit enter to accept
the default if you want to connect using a client on the same machine or over
@@ -72,7 +62,7 @@ in-world. You can also use these details to perform your first login.
Once you are presented with a prompt that looks like:
Region (My region name) #
Region (My region name) #
You have successfully started OpenSim.
@@ -83,9 +73,7 @@ Helpful resources:
* http://opensimulator.org/wiki/Configuration
* http://opensimulator.org/wiki/Configuring_Regions
==================================
=== Connecting to your OpenSim ===
==================================
# Connecting to your OpenSim
By default your sim will be available for login on port 9000. You can login by
adding -loginuri http://127.0.0.1:9000 to the command that starts Second Life
@@ -96,15 +84,11 @@ http://192.168.1.2:9000)
To login, use the avatar details that you gave for your estate ownership or the
one you set up using the "create user" command.
===================
=== Bug reports ===
===================
# Bug reports
In the very likely event of bugs biting you (err, your OpenSim) we
encourage you to see whether the problem has already been reported on
the OpenSim mantis system. You can find the OpenSim mantis system at
http://opensimulator.org/mantis/main_page.php
the [OpenSim mantis system](http://opensimulator.org/mantis/main_page.php).
If your bug has already been reported, you might want to add to the
bug description and supply additional information.
@@ -119,9 +103,7 @@ mantis"). Useful information to include:
mono --debug OpenSim.exe
===================================
=== More Information on OpenSim ===
===================================
# More Information on OpenSim
More extensive information on building, running, and configuring
OpenSim, as well as how to report bugs, and participate in the OpenSim

View File

@@ -107,6 +107,11 @@
;; If a viewer attempts to rez a prim larger than the non-physical or physical prim max, clamp the dimensions to the appropriate maximum
;; 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
;# {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.

View File

@@ -94,6 +94,10 @@
; If a viewer attempts to rez a prim larger than the non-physical or physical prim max, clamp the dimensions to the appropriate maximum
; 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
; 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.
@@ -703,6 +707,13 @@
; Default is false.
ReuseDynamicTextures = false
; If true, then textures generated dynamically that have a low data size relative to their pixel size are not reused
; This is to workaround an apparent LL 3.3.4 and earlier viewer bug where such textures are not redisplayed properly when pulled from the viewer cache.
; Only set this to true if you are sure that all the viewers using your simulator will not suffer from this problem.
; This setting only has an affect is ReuseDynamicTextures = true
; Default is false
ReuseDynamicLowDataTextures = false
[ODEPhysicsSettings]
; ##

View File

@@ -104,6 +104,12 @@ ServiceConnectors = "8003/OpenSim.Server.Handlers.dll:AssetServiceConnector,8003
; Region_Welcome_Area = "DefaultRegion, FallbackRegion"
; (replace spaces with underscore)
;; Allow Hyperlinks to be created at the console
HypergridLinker = true
Gatekeeper = "http://127.0.0.1:8002"
; * This is the configuration for the freeswitch server in grid mode
[FreeswitchService]
LocalServiceModule = "OpenSim.Services.FreeswitchService.dll:FreeswitchService"

View File

@@ -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

View File

@@ -36,8 +36,6 @@
SimulationServiceInConnector = true
MapImageServiceInConnector = true
[Profile]
Module = "BasicProfileModule"
[Messaging]
MessageTransferModule = HGMessageTransferModule
@@ -97,6 +95,10 @@
GridUserService = "OpenSim.Services.UserAccountService.dll:GridUserService"
GridService = "OpenSim.Services.GridService.dll:GridService"
InventoryService = "OpenSim.Services.InventoryService.dll:XInventoryService"
AvatarService = "OpenSim.Services.AvatarService.dll:AvatarService"
;; This switch creates the minimum set of body parts and avatar entries for a viewer 2 to show a default "Ruth" avatar rather than a cloud.
CreateDefaultAvatarEntries = true
[GridUserService]
LocalServiceModule = "OpenSim.Services.UserAccountService.dll:GridUserService"

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -2970,6 +2970,7 @@
<Match path="World/Media/Moap/Tests" pattern="*.cs" recurse="true"/>
<Match path="World/Serialiser/Tests" pattern="*.cs" recurse="true"/>
<Match path="World/Terrain/Tests" pattern="*.cs" recurse="true"/>
<Match path="ServiceConnectorsOut/Asset/Tests" pattern="*.cs" recurse="true"/>
<Match path="ServiceConnectorsOut/Grid/Tests" pattern="*.cs" recurse="true"/>
<Match path="ServiceConnectorsOut/Presence/Tests" pattern="*.cs" recurse="true"/>
</Files>